Libra第一笔转账——硬核方式

最近两周,由脸书发起的旨在颠覆现有金融格局的数字货币Libra项目,一直占据了链圈的风口。之前,网上已有若干篇关于“发起第一笔Libra转账”的教程,介绍如何下载、编译Libra项目的开源代码,并在testnet上完成一笔交易。身为硬核技术宅,那种跟着教程一步步编译代码的事情,显然不能满足我的好奇心。于是,借用几天休假的时间,奉上这样一份绝对的“硬核方法”,供同样不满足于RTFM的硬核宅们把玩。

有多硬?

我用golang重新写了一个全新的Libra区块链的客户端,完成了所有必要的密码学验证过程,并用它完成了转账交易。

上代码和教程:https://github.com/the729/go-libra

不走寻常路的区块链

要说Libra的不寻常,根源恐怕来源于它的宗旨——建立一套全球数十亿人使用的支付平台。它是由一篮子法币100%抵押支持的稳定币,所以Libra是用来花的,不是用来炒的,正所谓“币花不炒”。

币花不炒,导出了Libra很多设计理念,和比特币、以太坊等截然不同的地方:

中心化:Libra设计上就是中心化的,一开始由二十几家大企业共同掌管,一家一票,三分之二通过。未来逐步拓展到一两百家,但也只有组织内的成员有投票权。号称稳定运行五年后要去中心化,但目前还全然没有设计。

节能环保:既然有中心,就不用“电暖器式”的挖矿过程了,大家直接投票即可。线上的投票就是BFT算法。有重要的事要决策时,线下也可以投票,和线上一样三分之二以上通过。

容量大:既然直接投票,就没有了开会频率的限制,会可以一个接一个开,不像比特币10分钟才能出一个块。每次“开会”,大家表决一筐新的交易,达成一致了,就纷纷把这些交易按顺序写入自己的数据库中,签字画押。所以号称是“区块链”,其实通讯协议和存储上几乎就没有“区块”(装交易的“筐”)的概念了,保存的就是一个个按顺序的交易。

面向服务器的技术架构和优化:既然只有那么几十上百个大公司作为中心,目标又是要支撑全世界几十亿人的交易,那么代码的实现上就可以看出很多面向服务器级别的优化。比如,节点采用微服务架构,分为ac(访问流控)、mempool(交易内存池)、vm(Move语言虚拟机)、execution(交易执行单元)、consensus(共识算法(就是开会的代表))、storage(存储)。这些模块之间都采用RPC调用,可以横向扩展。根据Libra技术文档的理论计算,一台中等的服务器和大一些的硬盘(16TB),就可以支撑近期的使用了。不过它仍然是为服务器设计的,显然不适合在树莓派上跑。

Libra数据结构的设计

这里借用一张Libra区块链技术文档中的图。当我第一次看文档时,实话讲这张图完全看不懂。不过当我完成了go-libra的初步开发后,回头看,发现这就是整个Libra数据结构设计的精髓。

如果我们看Bitcoin,数据是按照一个一个区块形成一条单链保存的,每个区块中保存的一组交易,实际上可以看作是对整个区块链数据库状态的“补丁”(即patch或diff)。这样一来,当你想要知道某个账户的状态时,唯一的办法是从创世区块开始,一个一个“补丁”打,一直到目前最新的区块。因此,你必须在本地保存所有区块的数据。

Libra采用的是一种不同的方式,即每个“交易”(暂且对应于比特币的区块)都可以直接验证所有账户在那一刻的数据。换句话说,你可以直接取出并验证某个交易后,任何一个账户的状态,不需要从头开始捋。

这种方式,其实和git使用的是完全一致的思想。(感兴趣者请移步到《全球最强大的区块链项目,竟然不是比特币?》)Libra中的交易,对应着git中的commit。Libra中的账户,对应着git中的文件。通过Libra的每个交易,可以直接取出并验证任何一个账户在那一历史时刻的状态,就像通过git,你可以取出并验证任何一个commit提交时,代码树中任意一个文件的历史状态。

不同的是,Libra的交易只允许单线增加,而git允许commit分叉与合并。

重度MerkleTree使用

在Libra中,三度用到MerkleTree。

如果你还不了解MerkleTree:它是一种不需要拿到全部数据,就可以验证一小部分数据属于全部数据的方法。

比方说老板给秘书签署了1000份文件(设为n),放在一个大文件袋里,袋子上有老板的签名。这时,你找秘书要一个文件,为了验证真实性,你不得不要来整个文件袋,验证签名,然后再取出其中的一个文件。这是O(n)复杂度的验证过程。

如果使用MerkleTree,那么你可以只要你需要的文件,外加10个密码学哈希值,以及老板的签名,就可以验证这份文件确实是在老板签署的文件袋中了。这是O(log n)复杂度的算法,非常高效。

别问为什么不让老板把所有文件一个个都签了。老板若有时间签1000个字,也有精力把那个不懂MerkleTree而让他签到手抽筋的程序员开掉。

Libra中的三处MerkleTree:

所有历史交易按顺序组成的Ledger History树,即上面图中的(1)。每增加一个新交易,就增加一个叶子,同时增加相应的枝杈,一直延申到根。所以说,这棵树会通过增加叶子节点而不断产生新的根节点的。而根节点的哈希值,就是所有一两百家成员开会确定后签字画押的东西。

有了Ledger History树,你就可以验证某一个交易是否发生过。验证了单个交易后,它会含有另一个稀疏MerkleTree的根节点。而这个稀疏MerkleTree的每一个叶子节点,就是交易发生后每一个账户的数据。由于账户是256位的,最多有2^256个账户,一个稠密的MerkleTree就有2^257个Hash值要存,这显然不现实。因此,这里采用的是稀疏的MerkleTree。

第三个是Event Tree。从目前Libra官方的测试服务看,还没有实现整个Event的机制,因此这个树一直是空的。

总结

  • Libra从根本上和比特币等有很大的理念和技术差异。
  • Libra的数据结构更像是git,类似一个带有历史的内容寻址存储(CAS)。历史是交易,内容是账户数据。
  • Libra的设计者和实现者真的很辛苦,我用了几天时间,实现的也只是冰山一角。而真正的难点:Move编译器、虚拟机、CAS数据库,才是设计和开发难度很高的部分。
  • Libra目前状态真的很初级,只是将就着让testnet上线了。一些基础的功能都还没有,例如我们还无法查询一个交易执行的结果成功与否,只能看币到没到。但Libra的开发也很神速,每周三十多个commit。