用户的hash值(新建一个Nonce字段)通过串联表的各个字段进行计算。为了使图解更加明了,这里我把所有输入都字符串化了,而且只取前四个字符作为结果,例如:
hexstr(
firstfourbytes(
sha256(
concat( str(e-mail), str(balance), hexstr(nonce) )
) ))
所以,第一个用户的hash值计算为:
(在真实的实现中,计算hash的函数,可能会输入balance以及nonce的二进制值,而且还会在每个字段中插入分割符来消除重复,以防止“邪恶的交易所”用同一个结点代表多个用户。这里我没有用分割符,因为我不得不重新画图。)
SHAPE * MERGEFORMAT 接着账号就被当作子叶结点插入到二叉树里面,而且被一些不完整的内部结点连接起来。树可以用任意的形状或者用户所在的位置来构建,然而如果树是平衡的那么任意一个用户都可以粗略地估算到总用户数量。
更里面一层的结点值通过外面一层的结点值计算而来。重申一次,为了简单明了,我对各种计算用到的值进行了字符串化/简化/截取。最左边的一对子结点可以像下面这样计算:
parent.sum = left.sum + right.sum = 2.718282 + 1 = 3.718282
parent.hash = firstfourbytes( sha256( concat( str(parent.sum), hexstr(left.hash), hexstr(right.hash) )))
= firstfourbytes( sha256( "3.718282ab50f32860248b27" ))
= firstfourbytes( 4fdd5968686c6a8f68b10579fbeaefcbde45d4fe8b0023861e937fc95daaeb11 )
在这个微型的例子里,路径—>总账 的公布方法几乎和直接公开用户列表那样,需要提供详尽的数据,但是这种方法更加隐密、可以更加少地公布商业信息。这种路径—>总账 的方式需要公开数据量的增长速度也没有直接公开用户列表那么快。如果Satoshi(余额 3333)要验证交易所的总账,那么交易所只需要抽取树的下面部分内容即可:
实际上,交易所只需要给Satoshi提供他自己的随机临时值,以及那些标粗体的节点即可验证总账;其它节点的数据应该(而且也可以)在检查时,通过用户自己提供或者计算得来,这是非常紧凑简洁的。我的Clojure实现以及Olivier的JS实现在这里:传送门。
为了保证邪恶的交易所不会用相同的一个节点来代表两个余额一样的用户(例如,给他们相同的 路径—>总账 ),每个子叶节点hash值的计算应该包含只属于某个用户的特定输入值。这可能是使每个用户差异最大化的最好选择。如果使用的是用户的ID,那么交易所可能赋予多个用户相同的ID值,这是不理想的。如果使用登陆名的话,邪恶的交易所可能会减少冲突。
为了保证每个用户都不可以从他们自己的 路径—>总账 里面推导出其它节点的信息,每个子叶节点的hash值都应该包括一个临时随机值nonce作为输入,这个随机值只有交易所和某个用户自己知道。否则,用户可能会结合他猜测到的余额以及他知道(或猜测到)的用户唯一值,并把这两个值联合一起,进行数据跟踪。提供了这个临时随机值已经足够了,用户的唯一输入(指用来计算总账的唯一值)可以公开。每次公布资产负债报表时,每个用户的随机临时值nonce都要重新生成一次。
如果hash值不改变的话,邪恶的交易所可能会玩类似“用户配对”的小把戏,把两个相邻的子叶节点进行位置交换。每个子叶节点的hash值应当包括用户的余额,如上所述,或者内部节点的hash应当包括孩子结点的余额(不是总余额)。如果hash值的计算不受这个条件的约束,那么邪恶的交易所会对两个余额相同的用户匹配成相邻的一组,并且给他们提供不同版本的树,例如,假设:
· 左边子叶的hash是: crc32( join("|", name, nonce ) )
· 里面hash的输入 是: crc32( join("|", str(left.sum + right.sum), hexstr(left.hash), hexstr(right.hash) ) )
· 总共只有两个用户:
o 用户 tyler 的 nonce 是“turtle” ,余额是100
o 用户cameron 的 nonce是“canary” 余额是100
邪恶交易所给 tyler 的验证树:
· 根: sum: 100; hash: crc32( join(“|”, str(100 + 0), “01C29E82”, “5A486B26” )) = crc32( “100|01C29E82|5A486B26” ) = F38E7221
o 左 (you): sum: 100; hash: crc32( “tyler|turtle” ) = 01C29E82
o 右: sum: 0; hash: crc32( “cameron|canary” ) = 5A486B26
同时交易所给cameron这样的验证树干:
· 根: sum: 100; hash: crc32( join(“|”, str(0 + 100), “01C29E82”, “5A486B26” )) = crc32( “100|01C29E82|5A486B26” ) = F38E7221
o 左: sum: 0; hash: crc32( “tyler|turtle” ) = 01C29E82
o 右(you): sum: 100; hash: crc32( “cameron|canary” ) = 5A486B26
两个用户都确定了他们在资产负债树里面的余额(两人总共有200),但是交易所只需要证明他的资产值是100就可以了。
每次交易所公布资产负债hashroot树时,用户都想最做最详尽地检查(如检查未公布负债的机会)。同时,用户希望交易所更加频繁地进行披露,以使邪恶的交易所在两次公布资产负债的时间段内,挪用资产变得不实际。邪恶交易所会最小化所需要检查的内容,而且仅仅公布最近的一次hashroot。在实践中,可能这些因素需要结合在一起,找到一个合适的公布间隔,如一天、一周或者一个月。理论上讲,一个负债的交易所应当签署并发布所有历史公布的hashroot,而且会邮件通知每个客户他们自己的 路径—>总账,一个真正好的用户收到邮件后也应该作相应地检查,每次都不会遗漏交易所公布的数据。
邪恶的交易所可能会提供一个唯一的,编织好的路径—>总账给每个用户,唯一一个真实的节点就是用户自己的子叶结点,而剩余的整颗树都未经证实。一个有责任的交易所会通过一个事前约定的途径,公布出hashroot,确保所有用户看到的都是相同的视图。一种交易所可能采取的方法是:每次在公布时,发送一笔转账到某个地址上,这个地上是事先约定使用的,这一笔转账包含了OP_RETURN的output,output携带了已经签名的hashroot。(这个output会被普遍地认为在浪费公共资源,但是这并不十分重要;而且还有其它的方法实现相同的结果。)
Greg Maxwell指出使用合适的技术,去实现“零泄露信息作零知识证明”的协议是可能的,尽管本质上说实现起来会非常复杂。(如果你也在做同样的事情,或许你应该联系Greg,因为他知道怎样在零知识证明外保持ECDSA验证)。
常见关注点及纰漏
目的:对最常见的“难道交易所不能......”进行反驳以及提出并非显而易见但会削弱它的实现细节。
现在:我看到最多的担心就是交易所错误地将隐匿的账户余额清零(而这些账户的所有者不会核查)以及提供负额的虚假账户以掩盖事实;gmaxwell在HN (1,2,3) 和 Reddit的讨论中提出了这个问题.
现有的代码实现Python:
l Nick/ OnTheMargin / ConceptPending / CryptX.io的实现方案 。
JavaScript:
l Olivier Lalonde的实现方案 (同时打包在npmjs.org ),置于一个更大的证明偿付能力的框架中 。Olivier还提出了一个(未完成) 的序列化格式 ,它将使出版商/检查器之间的互操作性变得更容易,并共享测试。
l ConceptPending的实现方案现在还包括在JS客户端验证代码(以及原始的Python)。
Clojure :
l 我的方案是接受accounts.json序列化格式的一部分,和目前仅实现的创造出完整的Merkle树的功能。
C++:
l Bifubao的实现方案 。
l Kraken的实现方案 。
Ruby :
l Jan Han Xie的开源引擎/交易所实现方案peat.io
证明资产的所有权直截了当的做法就是对所有私钥的所有权发表声明。Olivier Lalonde写了一个这样的实行方案。大家会希望这份声明包含着最新的信息,以证明签名是最近作出的及保证签名不是在丢失私钥的冷储存期被提前生成;更理想的是能显示出最后块链高度和HASH值。用户会去验证那个签名,以及验证声明中所说明的总债务可以由所有私钥中可花出去的总币值相等。
大多数交易热钱包可能已经做着一些私钥“消息签名”的事了,所以不管怎样都可以做得更加好一点:证明客户提现时用的私钥(所有私钥的子集)和交易所进行“消息签名”时用的私钥是一样的。
这可能造成一种“签名即服务”的攻击,如果一个邪恶的交易所付费给某个用户,而这个用户(比如说是文克莱沃斯兄弟)拥有大量的币(数量达到了交易所声明的那么多),让这个用户来进行私钥签名。交易所并没有拥有那些币,或者只拥有其中的某一部分。
不管怎样,仍然可能有很多看似很精美(或许不切实际的)协议可以直接地排它性地证明私钥的所有权,也会有证明所有权后马上把私钥归还给别人的情况出现。因为这并不可以说明交易所愿意归还那些币,仅当如果它想归还时候它才会归还——当最近一次的证明结果取得成功后,永远不可能防止交易所隐藏币(比如以文克莱沃斯兄弟做交易为结局)。
最后,这里有一个十分简单的方法给交易所,让交易所既可以也愿意归还所声明的币:一次性把币归还给用户。如果不暂停交易所业务的话,这是做不到的。。但是仍然可以把所有币一次性发送到提前准备好“1/2”的委托付款地址(应该是指多重签名地址),交易所持有其中一个私钥,客户持有另一个私钥。交易所需要等待交易达到一定的确认数量,如果此时用户又把币消费了出去,这就相当于另一种形式的提现;否则的话,这些币最终会打到交易所的热钱包。这可能会和某些交易所强加限制的每日提现数量的做法有冲突(这些做法可能是为了符合一些反洗钱条款),尽管如此,大笔的存入虚拟货币还是可以的。
谁在用这样的准备金证明?Bitcoinity的Comboy鼓励人们叫交易所提供这种证明。他们也提供用他的实时图表进行免费推广的奖金。(以取代现有的“这个地方正在等待一些重大交易”的推广方式)。
Bifubao(币付宝) :这个中国钱包网站(经典的共享钱包网站)的一个创始人在电子邮件中说,他们将在短期内使用Merkle树的方法;可惜我不会说/阅读中文,不能检查他们的钱包,但他们有一些开源的代码 来产生并检查他们的树。他们将用signmessage来证明资产,但只限于冷钱包(至少是个开始)。
Bitalo曾表示,他们的多重签名就意味着你不需要这个证明了;我还没有看过这个东西,但以多重签名为基础的钱包就意味着你拥有两个密钥,钱包网站拥有第三个,而所有的交易都需要三个密钥中的两个才能完成。最直接的实现方式就是让你验证他们是否真的保有你的资产。
BitBargain.co.uk 自2014年3月5日起,BitBargain.co.uk提供了一个扁平化的列表匿名负债声明,并用signmessage来证明相对应的热/冷钱包中的资产控制。 该声明只有卖家知道——这里最大的货币持有者—— 但买家可以询问任何卖家以确认他们的余额是否在负债列表中。BitBargain公开主动阻止用户们不要把货币放在网站上,过后只收取买家(据说是非常少的)一笔保管费。
BitGo似乎在使用像Bitalo一样的三选二系统。
BitPay:jgarzik于2014年3月4日说“作为一个支付系统而不是一个钱包,BitPay尽快地完成支付,力求零储备。BitPay真的不希望长时间持有现金或者比特币。对于美元 ,我们在24小时内完成支付,有时候更快。而对于比特币也是一样。我们最多持有资金几个小时。
Bitstamp 2014-02-25, Bitstamp说“他们正在执行季度的财务审计而且会在官方网站上公布出来。” 2014年03月06日,Bitstamp发了一个声明说,三个月多以前(2013年11月22、23号),一个匿名的第三方公司给他们做过一次审计,没有发现问题。但是既然他们没有公布这个第三方审计公司的名称,用户也不能决定他们是不是要相信这个审计者以及审计报告。如果Firestartr(Bitstamp的合伙人)的声明是通过一个可证明的方式签署,如果你有理由相信Firestartr,这或许有点价值;否则的话,这根本就不意味着什么。
Bittylicious.co.uk 2014年3月4号说“暂时还没有具体的方案,但是如果出现某种最好的方案,我们肯定会考虑的。”他们又补充到,虚拟货币仅以销售者的名义保存在网站上,我们并不是一个一般意义上的钱包网站。
BitX:请从根本上“相信我们”:“我们乐意保证BITX完全妥善保管着所有用户的全部资产。”好吧,焦急的GOX客户曾被客服在2月24日告诉说“你们的币是安全的”,1月8日,个别管理人员也这样说过。
Buttercoin: Benn Hoffman说,“我们也想办法制定出具体细节来保证我们持有的数量与客户的储币(至少是提现)数量相等,而且必须在保证客户隐私的前提下实现。”同时指出对MERKLE方法的关注(“这主要在于弄明白如何整合和有充足的时间去实现”)
Coinbase:决定开展现货外部审计;我们不能忍受与mtgox发邀请给 Roger Ver(比特币天使投资人)一样的行为,我没有理由不相信Andreas Antonopoulos(blockchain首席安全官),但是我也没有理由相信他。如果我是一名Coinbase的用户或者计划成为一名coinbase的用户,这并不能让我放心交易。
Coinfloor.co.uk自称,”Coinfloor的系统在冷钱包内持有100%的资金,并且每笔提现会有3个工作人员经手进行多重认证。我们的规则将会防止任何恶意提现,避免客户资金受到损失。同时也包括防止内外部盗窃以及对私匙的不当操作和强制多态。”这里似乎没有告诉你什么有用的东西(是100%准备金,还是仅持有小部分准备金?尽管使用了“规则”这个词,我认为”防止“意味着“阻止”,而不是保证你可以免受损失。)以上他们所的并不意味着你可以验证交易所的资产和负债,因此我认为这就相当于说“请相信我们(正如GOX说过的那样)”。正是这样的宣称提前引爆了GOX的偿付危机, 也许Coinfloor还会再来一次。
Coinkite宣称可以某种程度上让你验证自己账户上的余额;大概他们可以重复使用地址,然后每个用户有一个地址,让你可以按要求地标记信息。我不是他们的用户,也许不能进行有效的测试,也不能进行深入地了解,请自行阅读他们的使用说明。
Coinsetter表达了对Merkle方法的异议。它们说“我们感觉这个方式是有局限性的。例如这套方法能够辨识欺诈账户,但是它不能将账户余额和实际的资产联系起来“。他们从社区寻求解决和落实的办法。起初我以为他们反对的理由是由于“可替代性”,但是我确信我可能忽略了什么。
CryptX.io has said it will launch with support for the Merkle approach and has provided an implementation in Python.
Cryptx.io说他们将着手支持MERKLE方法,并提供了使用Python语言的实现方案.
FYBSE:它的回应是令人失望的:”如果有任何疑问,请给我们发邮件“。请忽略这个所谓的承诺,正如上述对BITX说过的,焦急的GOX客户曾收到客服在2月24日的答复说“币是安全的”,1月8日,管理人员也这样说过。
Justcoin看起来是有兴趣但是没有表态。
Just-Dice:dooglus说,“通过公布所有账号余额列表,我已经证明了我们的偿付能力“。但是他们似乎对更好保护客户隐私的MERKLE方法也有一定兴趣。
Kraken:jesse回应说他们不认为MERKLE方法的透明性比交易的隐私性更有价值,说他们更喜欢外部审计(我不能确定用什么方法审计)。他还明确地说他们将严格隔离用户资金。也就是说他们不混杂在一起。
peat.io (测试中)2014年3月18日落实MERKLE的债务保全。
Trustedcoin好像使用了三选二系统,与Bitalo/BitGo一样。