虽然以太坊白皮书的中文版已经面世几个月了,但由于其中涉及了很多技术细节,阅读白皮书要求读者本身掌握一定的计算机编程知识,这样的门槛不可避免地将一部分读者拒之门外。介于此,本文将先从非技术的角度,带读者一起走进以太坊的世界,之后再举两个例子,对以太坊中的合约的运行进行解析,对于不想细究技术原理的读者,此处可跳过 。
背景
比特币开创了去中心化密码货币的先河,五年多的时间充分检验了区块链技术的可行性和安全性。比特币的区块链事实上是一套分布式的数据库,如果再在其中加进一个符号——比特币,并规定一套协议使得这个符号可以在数据库上安全地转移,并且无需信任第三方,这些特征的组合完美地构造了一个货币传输体系——比特币网络。然而比特币作为一次密码货币的伟大尝试,她并不完美,其中,协议的扩展性是一项不足,例如比特币网络里只有一种符号——比特币,用户无法自定义另外的符号,这些符号可以是代表公司的股票,或者是债务凭证等,这就损失了一些功能。另外,比特币协议里使用了一套基于堆栈的脚本语言,这语言虽然具有一定灵活性,使得像多重签名这样的功能得以实现,然而却不足以构建更高级的应用,例如去中心化交易所等。
为了解决这个问题,社区的Mastercoin团队和Countparty团队都试图在比特币之上创建另外一套协议,同时也创建一种新币,这些币可以统称为Metacoin。具体的方式是通过在比特币的交易里添加额外的信息,再用另外的客户端来解析这些信息,他们把Meatcoin这种方式自比“在TCP/IP协议之上构建HTTP协议”。但问题来了,首先,这种方式的效率不高,例如在利用Metacoin系统构建的去中心化交易所里,当用户发出指令要买入某种资产时候,挟带这条指令的那笔比特币交易必须要被加入到区块之后,指令才能生效。高效的做法应该是这条指令直接保存在矿工的内存里,如果买入指令跟已有的卖单匹配,矿工则撮合交易,并写入下一个区块。而现在的情况是,比特币矿工根本不知道交易中嵌入的额外信息是什么意思,这样看起来“TCP/IP协议”和“HTTP协议”之间其实并不怎么配合,正如以太坊白皮书里所说,这倒更像是在SMTP协议上建立HTTP协议。以太坊的白皮书还指出,Metacoin不可能实现完全安全的轻钱包,也就是说客户端需要保留Metacoin创世区块后的所有比特币区块,这对用户来说是一件多么头大的事情啊!
以太坊是什么?
以太坊从设计上就是为了解决比特币扩展性不足的问题。总的来说,以太坊是一个平台,她上面提供各种模块让用户来搭建应用,如果将搭建应用比作造房子,那么以太坊就提供了墙面、屋顶、地板等模块,用户只需像搭积木一样把房子搭起来,因此在以太坊上建立应用的成本和速度都大大改善。具体来说,以太坊通过一套图灵完备的脚本语言(Ethereum Virtual Machine code,简称EVM语言)来建立应用,它类似于汇编语言,我们知道,直接用汇编语言编程是非常痛苦的,但以太坊里的编程并不需要直接使用EVM语言,而是类似C语言、Python、Lisp等高级语言,再通过编译器转成EVM语言。
上面所说的平台之上的应用,其实就是合约,这是以太坊的核心。合约是一个活在以太坊系统里的自动代理人,他有一个自己的以太币地址,当用户向合约的地址里发送一笔交易后,该合约就被激活,然后根据交易中的额外信息,合约会运行自身的代码,最后返回一个结果,这个结果可能是从合约的地址发出另外一笔交易。需要指出的是,以太坊中的交易,不单只是发送以太币而已,它还可以嵌入相当多的额外信息。如果一笔交易是发送给合约的,那么这些信息就非常重要,因为合约将根据这些信息来完成自身的业务逻辑。
合约所能提供的业务,几乎是无穷无尽的,它的边界就是你的想象力,因为图灵完备的语言提供了完整的自由度,让用户搭建各种应用。白皮书举了几个例子,如储蓄账户、用户自定义的子货币等,这里再列举几个笔者想到的例子:1)去中心化交易所,2)各种金融衍生合约,3)彩票,4)基于预测未来事件的打赌,5)投票/仲裁系统,6)去中心化自治组织(DAO),例如基金。这些合约之间可以互动,例如4)打赌合约可以访问5)仲裁合约,而仲裁合约可能是一个基于POS的投票系统,投票的结果作为外部事件的结果返回给打赌合约,作为最后的裁定。
合约示例
对于希望了解如何建立合约、以及合约如何运转的读者,以下两个例子应该能提供一些帮助,但最好结合白皮书中的交易格式、费用组成、数据存储结构等知识进行理解。以下的例子采用类似C的语言编写。
Name Registratio
[1] if tx.value < block.basefee * 200:
[2] sto
[3] if contract.storage[tx.data[0]] or tx.data[0] < 100:
[4] sto
[5] contract.storage[tx.data[0]] = tx.data[1]
第1行:如果手续费不够,退出。
第3行:contract.storage[tx.data[0]] 是为了检查tx.data[0]所代表的名称是不是已经被注册了,例如www.bitfarm.io。因为所有contract.storage的项的初始值都是0,所以如果contract.storage[tx.data[0]]不为0,说明之前已经注册过了,而如果if从句返回一个非0的值,则执行stop。tx.data[0]<100是检查需要注册的名称翻译成数字时,是否小于100,这里100是一个估计值,它需要比这个合约编译后生成的机器代码的字节数大才行,以免注册的名称把合约本身覆盖了,因为合约本身编译后的存储也是在contract.storage里面 。这个合约不会超过100个字节,但对于更多字节数的合约则可能需要使用500来作为保护值,这个值估计需要保守一点。
第5行:把tx.data[1]的值写进contract.storage[tx.data[0]],这里tx.data[1]可能是一个ip地址,来对www.bitfarm.io进行DNS注册。
Sub-currency
第1行:如果手续费不够,退出。
第3行:因为所有的contract.storage的项的初始值都是0,所以跳过3-12行,来到第13行。
第14行:MYCREATOR是contract的创建者自己可以定义的,可能是他自己的某个Ether的地址,这样,他就给自己分配了10^18个子货币,例如这个子货币是他公司的股票。
第15行:把contract.storage[1000]的值从0改为1。至此,合约的初始化完毕。
第3行:现在合约的创建者可以给其他人发送子货币(股票)了。因为已经初始化了,contract.storage[1000] = 1,所以执行第4行。
第4行:定义局部变量from,并赋值tx.sender给它。这里,tx.sender是合约创始人的地址MYCREATOR。
第5行:定义局部变量to,并赋值tx.data[0]给它。tx.data[0]是发送交易激活合约时,发送者输入的子货币接收地址。
第6行:定义局部变量value,并赋值tx.data[1]给它。tx.data[1]是发送交易激活合约时,发送者输入的子货币发送量。
第7行:检查to是不是小于1000,防止覆盖合约本身的代码。具体细节请见上一个例子。
第9行:检查发送者拥有的子货币数量是否小于要发送的子货币数量。
第11、12行:把子货币的发送量从发送者的账户减掉,加到接受者的账户。
总结
比特币网络事实上是一套分布式的数据库,而以太坊则更进一步,她可以看作是一台分布式的计算机:区块链是计算机的ROM,合约是程序,而以太坊的矿工们则负责计算,担任CPU的角色。这台计算机不是、也不可能是免费使用的,不然任何人都可以往里面存储各种垃圾信息和执行各种鸡毛蒜皮的计算,白皮书有说明,使用它至少需要支付计算费和存储费,当然还有其它一些费用。
这台分布式计算机,配以图灵完备的语言,再加上你的聪明才智,将怎样撬动密码货币、分布式应用领域?我们拭目以待。
大时代来临,你准备好了吗?