币付宝Android客户端正式上线!

今天,币付宝正式推出了安卓手机客户端!用户可以随时随地通过手机客户端完成比特币的发款与收款。您可以单独使用网页版币付宝,或结合币付宝手机客户端一起使用——我们已将币付宝的基本功能嵌入手机客户端:

  • 币付宝用户之间发款瞬间到账且操作完全免费
  • 通过邮箱、手机号、比特币地址(含二维码)进行发款
  • 用户自定义头像
  • 联系人列表
  • 简约的操作界面

用户使用手机客户端发款时须设置独立的6位支付密码,此密码仅作为手机端支付使用。为增强应用的安全性,我们建议用户设置解锁图案,并从知名的手机应用市场下载我们的客户端。

快来体验币安卓手机客户端,让币付宝真正成为您的私人钱包!欢迎您将使用体验反馈给我们。

币付宝团队

####下载安卓APK

或扫描以下二维码:

Bifubao's Android App is Here!

We’re excited to announce that our Android app is now available! Now you can use the Bitfoo mobile app send and receive bitcoins anytime, anywhere. You can use the app independently or in conjunction with our web app - all of the your favorite Bitfoo functions are baked right in, including:

  • immediate and free transactions between Bitfoo users
  • send via email, mobile phone, or Bitcoin addresses (via QR codes)
  • Gravatar icons
  • simple, intuitive interface
  • Contact lists
  • And more!

Users will be required to set a six-digit payment password to send bitcoins using the app. For additional security, we encourage users to set an unlock code on their phone, install a virus scanner, and to make sure you download the application from a reputable source.

We hope you enjoy the Bitfoo mobile client! As always, let us know if you have any comments.

The Bitfoo Team

Download Android APK

Or just scan the QR code below:

CCTV visits Bifubao!

Bifubao was featured in a CCTV segment on Bitcoin! CCTV paid a visit to our humble office in the video and we were able to chat with them about what we are building and Bitcoin. Two of our cofounders, Li Xiao Lai and Pan Zhi Biao were featured. You can watch the segment here.

Thank you to CCTV for coming by and visiting our offices. Come back anytime!

*To correct a misstatement in the segment, we aren’t quite adding one thousand users per day just yet (although we certainly wouldn’t mind!).

中央电视台报导币付宝

央视新闻频道《朝闻天下》栏目在2014年3月15日的“比特币之争”节目中报导了币付宝!节目视频中,央视记者来到我们的办公室参观,对比特币以及币付宝进行了了解。币付宝联合创始人之中的两位 - 李笑来先生和潘志彪先生 - 也在节目中现身。您可以在这里.
这里观看该期节目。

感谢央视登门拜访,也欢迎您随时再来!

*在此更正节目中的一条错误信息,币付宝并未达到“每天的用户注册量都在1000人以上” —— 当然我们也希望这一天尽快到来!

Proof of Reserves

#Bifubao’s Proof of Reserves

##What are reserves?
Reserves refers to the funds held by a business. If a business holds funds on behalf of its customers, and its reserve ratio is 100%, this means that it holds 100% of its customer’s funds in reserve. If its reserve ratio is 10%, that means the business only holds 10%, and 90% of the funds are used elsewhere. This is the mechanism used by many banks - depositors give cash to the bank to hold, and the bank lends out a certain percentage while keeping enough in reserve to satisfy withdrawal requests.

##Why does an off-chain wallet need to prove reserves?
In Bifubao’s case, our reserves refer to the bitcoins that Bifubao holds on behalf of our users. Our wallet is off-chain, which means that we hold bitcoins for you, with the data being stored on our database. This has many benefits, but one downside is that it is typically difficult to prove that the platform holds the funds that they say they do. This situation was pronounced in the case of Mt. Gox, and since its demise the bitcoin community has demanded accountability from exchanges and wallets that handle bitcoin deposits in an off-chain manner.

##Method of Proof
The easiest method of proof is to publish a flat list of all user accounts, total deposits, and the platform’s deposit addresses. However, this method exposes a great deal of company information. The Merkle Tree technique makes it difficult for a company to falsify data while protecting privacy (although some information is necessarily revealed).

##How We Implement Proof of Reserves

Our implementation is based on a method using Merkle Trees as proposed by Bitcoin developer Greg Maxwell, and as detailed here, with a few modifications. Using this method, a company would be able to prove to a user that its data was taken into account in calculating the total amount of funds held.

构建的树结构

单个用户看到的结构

##Privacy

###User Information
To protect user identities, we can’t very well directly publish a list of our user’s email addresses or ids along with their bitcoin holders. Instead, we create a hash using each user’s user_id. Combining the user_id with their balance and nonce makes the resulting hash value even harder to trace. The user_ids should be unique and immutable, so as to decrease the odds of two users choosing the same user_id. In pseudocode, this would look something like the following: hash_value = HASH(user_id + nonce + balance). Because we only display a hash digest, users can rest assured that their personal information won’t be exposed as part of this proof. Besides calculating a unique user_id for each user, we additionally calculate a new nonce each time.

###Bifubao Information
This method exposes some of Bifubao’s data. Users can see the total amount of bitcoins on our system and can estimate the total number of users on our system. However, we believe it is worth the tradeoff to verify to our users that we are operating transparently. We are also opening up our source code for the community to inspect.

##Implementation Details
We build up the Merkle Tree by first obtaining the relevant data in our database, and then iteratively running the algorithm to construct the tree to the root node.

###Example Data

User Email/ Mobile Phone Nonce Balance (Satoshi)
panzhibiao@bifubao.com 139853 100047062
support@bifubao.com 982361 88086042
13800138000 093823 3343103669

###User Nodes
The hash of the user node is calculated according to this algorithm:

1
2
3
4
5
hexstr(
first8bytes(
sha256(str(user_id) + sprintf("%06d", nonce) + sprintf("%016lld", balance))
)
)

We concatenate the user_id and the nonce to get the uid in the code below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
typedef struct Node_ {
long long sum;
unsigned char hash[8];
bool operator < (const struct Node_ &right) const {
return memcmp(this->hash, right.hash, 8) < 0 ? true : false;
}
} Node;
// make_user_node
void make_user_node(const char *uid, long long balance, Node *node) {
unsigned char hash[SHA256_DIGEST_LENGTH];
char buf[17] = {0};
node->sum = balance;
sprintf(buf, "%016lld", balance);
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, uid, strlen(uid));
SHA256_Update(&sha256, buf, 16);
SHA256_Final(hash, &sha256);
memcpy(node->hash, hash, 8);
}

After constructing the hashes of all of the user nodes, we sort them according to the hash results.

###Parent Nodes
Two adjacent nodes are added together to make a parent node. If the number of nodes is odd, we construct a padding node.

1
2
3
4
5
hexstr(
first8bytes(
sha256(_8bytes(left.sum + right.sum) + _8bytes(left.hash) + _8bytes(right.hash))
)
)

The balance of the parent node is the sum of the balances of the child nodes.

1
2
3
4
5
6
7
8
9
10
11
12
// make_parent_node
void make_parent_node(const Node *l, const Node *r, Node *p) {
unsigned char hash[SHA256_DIGEST_LENGTH];
unsigned char buf[24]= {0};
p->sum = l->sum + r->sum;
memcpy(buf, (unsigned char *)&(p->sum), 8);
memcpy(buf+8, (unsigned char *)l->hash, 8);
memcpy(buf+16, (unsigned char *)r->hash, 8);
SHA256(buf, 24, hash);
memcpy(p->hash, hash, 8);
}

###Merkle Tree
To build the merkle tree, we recursively use the algorithm above until we get to the root node.

###Cold Storage Addresses
Our proof of reserves would not be complete without proving our control of assets. Below are our cold storage addresses, where you can see how much we hold in reserve. Because we hold some bitcoins in our hot wallet, the number indicated in the sum of the two addresses below will not be equal to the amount calculated above, but you can use the data to be sure that we at least hold a large percentage of user funds.

  1. 1PufBJk2c2HYq5wNap9yjmjSw6G3iD6mr5
  2. 1EQvpVvPVtZrwwrSoXY1mMrdVuCqaiVKEy

Signed Messages
To prove we control these addresses, we’ve signed a message using their corresponding private keys:

1
2
3
4
5
6
7
8
# plain text
this address belongs to bifubao.com, 2014-03-04
# signature of 1PufBJk2c2HYq5wNap9yjmjSw6G3iD6mr5
HGcRqoJUq3iINmQ1jCA59KD6Iv0DzcaQxxtkIL9l/+wWo1bREPmh3h35IowYv0DU7lRT54O2wQtQ2rE7AVUxiVk=
# signature of 1EQvpVvPVtZrwwrSoXY1mMrdVuCqaiVKEy
G/AMpYGw6aW2gLHdHwkCh+PIHz6gwybXEostNCSmF8RBzEwAOYUFNBD5oI6XFkLRGvFrs58KRP/7Ok9GATZONW0=
  1. Source Code: https://github.com/bifubao/proof_of_reserves

示例树结构

References

  1. prove-how-(non)-fractional-your-Bitcoin-reserves-are scheme https://iwilcox.me.uk/2014/nofrac-orig]
  2. Proving Your Bitcoin Reserves https://iwilcox.me.uk/2014/proving-bitcoin-reserves]
  3. Pictures courtesy of Zak Wilcox at the above sites.

100%准备金证明

什么是准备金

说白了,准备金就是平台留存的钱。100%准备金率就是用户存100块,平台必须保留100块;10%就是存100块,平台可以只保留10块,另外的90块可以做别的事情,通常银行通过放贷等进行盈利,所以银行需要拼命吸储。

为何Off-Chain钱包需要证明

Off-Chain(链下)机制是用户的币在平台只做登记,币由平台完全控制。On-Chain(链上)机制是用户的币由自己通过私钥管理,平台无法动用。所以,只有Off-Chain才有可能低于100%准备金且需要证明,On-Chain总是保持100%准备金率。

证明机制

最简单的证明方法就是公布所有用户数据,平台的准备金率即:平台储蓄地址 / 用户总余额。该方式很直接,但易伪造。

欲保障逻辑完备性,需要证明:

  1. 没有伪造
    1. 伪造假用户
    2. 伪造用户余额
  2. 没有遗漏
    1. 直接遗漏用户:某个用户在公布的数据里找不到自己
    2. 间接遗漏用户:两个或两个以上用户对应的是同一条数据

先说伪造,伪造假用户的结果是:准备金率下降,打自己耳光;伪造用户余额的结果是:任一用户发现余额与公布的不一致即说明平台造假。还有一种就是仅伪造平台控制的用户数据(自己人的账户),若往多了吹,造成准备金率下降;若往少了说,没意义。但无法防止的情形是:伪造大量的零余额的用户,但这个不影响准备金率。

第二个点是遗漏,直接遗漏也没法弄,一旦某用户发现找不到自己则立即露馅;通常是间接遗漏,防止间接遗漏最直接的方法是公布用户Email地址,但会暴露用户隐私,通常需要设计一个Hash算法,例如:hash_value = HASH(user_id + nonce + balance)user_id这个字段必须每个用户唯一且固定不变,通常是选择email或者手机号码,因为天然具有唯一性且不可伪造。确定HASH算法后,用户的识别由Email地址改为hash_value

证明方法

证明主要过程是构建Merkle Tree,当构建完该树,且根节点的余额与公布的储蓄地址余额相同,即可100%储备。证明算法参考了Proving Your Bitcoin Reserves,少许修改。

构建的树结构

单个用户看到的结构

隐私问题

用户

必须在证明的同时可以保障用户财务隐私不被泄露。

  1. user_id的选取,上文已阐述,不再重复
  2. 每次构建时,用户的Nonce均为随机,即使用户两次余额不发生变化,hash_value依然是不一样的

上述两点可最大限度保护用户财务隐私,虽然别人可能看到你的节点数据,但他不知道你是谁。

平台

对平台来说,敏感的数据是:总储蓄额,总用户数。

总储蓄额必然公开,无需讨论。总用户数若不想那么公开,可以通过一些方法掩饰。通常Merkle Tree是平衡二叉树,根据用户树的高度可以推测用户数量(误差在2倍以内),那么就可以通过构建非常不平衡的二叉树(每次可以是任意形状)来掩饰平衡构建树的真实高度,且不破坏验证机制。例如,平衡二叉树的高度为10,则平台用户数范围是:512~1024之间(2^9=512, 2^10=1024),若构建一个高度为20的非平衡树(2^20=1048576)就可以成功掩饰实际用户数。

其他保护平台隐私方法:

  1. 降低公开频率,例如从每天公开一次降低为每周、每月公开一次
  2. 利用自有资金进出,干扰资金流向跟踪

币付宝的证明机制

我们理念是保护用户隐私的情况下,公开所有平台数据:总储蓄额、总用户数,用户可以下载所有节点数据。构建满平衡二叉树的过程是:构建用户节点 -> 迭代向上构建父节点 -> 至根节点,树构建完毕。若某一层节点数为奇数,则将最后一个节点复制,该节点称为填充节点(padding node)。

示例用户数据

User Email/ Mobile Phone Nonce Balance (Satoshi)
panzhibiao@bifubao.com 139853 100047062
support@bifubao.com 982361 88086042
13800138000 093823 3343103669

用户节点

用户节点hash值的算法:

1
2
3
4
5
hexstr(
first8bytes(
sha256(str(user_id) + sprintf("%06d", nonce) + sprintf("%016lld", balance))
)
)

构建一个用户节点,由于字符串直接拼接,我们把user_idnonce合成为下面函数中的uid示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
typedef struct Node_ {
long long sum;
unsigned char hash[8];
bool operator < (const struct Node_ &right) const {
return memcmp(this->hash, right.hash, 8) < 0 ? true : false;
}
} Node;
// make_user_node
void make_user_node(const char *uid, long long balance, Node *node) {
unsigned char hash[SHA256_DIGEST_LENGTH];
char buf[17] = {0};
node->sum = balance;
sprintf(buf, "%016lld", balance);
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, uid, strlen(uid));
SHA256_Update(&sha256, buf, 16);
SHA256_Final(hash, &sha256);
memcpy(node->hash, hash, 8);
}

用户节点都创建完毕后,按照hash值进行排序。

父节点Hash值计算函数

将两两相邻的节点进行汇总,得到父节点,若当前层的节点数为奇数,则将最后一个节点复制,补充为偶数。

父节点的余额为左子节点与右子节点之和,父节点hash值算法:

1
2
3
4
5
hexstr(
first8bytes(
sha256(_8bytes(left.sum + right.sum) + _8bytes(left.hash) + _8bytes(right.hash))
)
)

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
// make_parent_node
void make_parent_node(const Node *l, const Node *r, Node *p) {
unsigned char hash[SHA256_DIGEST_LENGTH];
unsigned char buf[24]= {0};
p->sum = l->sum + r->sum;
memcpy(buf, (unsigned char *)&(p->sum), 8);
memcpy(buf+8, (unsigned char *)l->hash, 8);
memcpy(buf+16, (unsigned char *)r->hash, 8);
SHA256(buf, 24, hash);
memcpy(p->hash, hash, 8);
}

Merkle Tree

构建Merkle Tree的过程,即向上递归两两合成父节点,至该层节点数为1时停止。

冷钱包地址

  1. 1PufBJk2c2HYq5wNap9yjmjSw6G3iD6mr5
  2. 1EQvpVvPVtZrwwrSoXY1mMrdVuCqaiVKEy

地址签名

1
2
3
4
5
6
7
8
# plain text
this address belongs to bifubao.com, 2014-03-04
# signature of 1PufBJk2c2HYq5wNap9yjmjSw6G3iD6mr5
HGcRqoJUq3iINmQ1jCA59KD6Iv0DzcaQxxtkIL9l/+wWo1bREPmh3h35IowYv0DU7lRT54O2wQtQ2rE7AVUxiVk=
# signature of 1EQvpVvPVtZrwwrSoXY1mMrdVuCqaiVKEy
G/AMpYGw6aW2gLHdHwkCh+PIHz6gwybXEostNCSmF8RBzEwAOYUFNBD5oI6XFkLRGvFrs58KRP/7Ok9GATZONW0=

数据与源码

  1. 构建树源码:https://github.com/bifubao/proof_of_reserves

示例树结构

参考

  1. prove-how-(non)-fractional-your-Bitcoin-reserves-are scheme https://iwilcox.me.uk/2014/nofrac-orig
  2. Proving Your Bitcoin Reserves https://iwilcox.me.uk/2014/proving-bitcoin-reserves
  3. 图片来自:参考2中的网页