2.2 以太坊
2.2.1 什么是以太坊
自2008年比特币出现以来,数字货币的存在已经渐渐为一部分人所接受。人们也积极展开了基于比特币的商业应用的思考与开发。但是随着应用的扩展,人们发现比特币的设计只适合虚拟货币场景,由于存在着非图灵完备性、缺少保存状态的账户概念,以及PoW挖矿机制所带来的资源浪费和效率问题,在很多区块链应用场景下并不适用。人们需要一个新的基于区块链的具有图灵完备性、高效共识机制、支持更多应用场景的智能合约开发平台。以太坊在这种情况下应运而生。
以太坊的目的是对脚本、竞争币和链上元协议(on-chain meta-protocol)等概念进行整合和提高,使得开发者能够创建任意的基于共识的、可扩展的、标准化的、图灵完备的、易于开发和协同的应用。
以太坊是一个通用的全球性区块链,可以管理金融和非金融类型应用的状态。以太坊的新颖在于其神奇的计算机网络,它促成了一种新型的软件应用,真正的去中心化应用。将信任逻辑嵌入小程序里,运行在区块链上。而与比特币相比,以太坊建立了一种新的密码学技术基础框架,在其上开发应用更加容易,并对轻客户端友好,同时允许应用共享一个可行的经济环境和可靠的区块链安全。以太坊在全球范围内激发了商业和社会创新,为前所未有的去中心化应用打开了大门。从长远来看,它所带来的改变将影响全球经济和控制结构。
以太坊是个平台和编程语言,包括数字货币以太币(Ether),以及用来构建和发布分布式应用的以太脚本(EtherScript)。
以太币和著名的数字货币比特币有非常多的相似之处。两者均为数字货币且无法伪造,都以去中心化的方式运行来保证货币供应不被某一方所控制。以太坊的另一半重要特性是提供一个完整的编程语言环境,有时也被叫作以太脚本。我们都知道,编程语言是人类用来控制计算机工作的。因此,用任何编程语言写好的指令对计算机来说都是准确无误没有歧义的。也就是说,计算机如何执行一段代码是没有二义性的。在同样的条件下,一段代码总是会按照既定的步骤执行。这种特性正是人类现行法律与合约所缺失的。因此,有了以太脚本之后,我们就可以制定没有二义性的合约了。
从最底层角度来看,以太坊是一个多层的、基于密码学的开源技术协议。它的不同功能模块通过设计进行了全面的整合,作为一个整体,它是一个创建和部署去中心化应用的综合平台。虽然,以太坊看起来像由多个互相联系的开源项目构成的混合体,但是它的进化一直被明确目标所引导,所以各个组件可以协同地组装在一起。
同时,以太坊也是区块链与智能合约的完美结合,是智能合约的完整解决方案,被设计成了一个通用的去中心化平台,拥有一套完整的、可以扩展其功能的工具,在P2P网络、加密、HttpClient等技术的支持下实现了一个类似于比特币的区块链。它通过工作量证明机制实现共识,由矿工挖矿,通过对新的网络协议的制定实现对区块链的同步等操作。不同于比特币的是,在以太坊上可以任意编写智能合约,通过智能合约实现强大的功能,实现去中心化应用的开发。在以太坊上部署的智能合约运行在以太坊特有的虚拟机上,通过以太坊虚拟机和RPC接口与底层区块链进行交互。
以太坊的总体系统架构如图2-14所示。
图2-14 以太坊总体架构
2.2.2 以太坊技术
1.?以太坊核心概念
(1)以太坊虚拟机
以太坊虚拟机(EVM)是以太坊中智能合约的运行环境。它是以太坊项目中的另一个主要创新。有人说EVM“位于区块链之上”,实际上它是由许多互相连接的计算机组成的。任何人都可以上传程序,并让这些程序自动执行,同时保证现在和所有以前的每个程序的状态总是公共可见的。这些程序运行在区块链上,严格地按照EVM定义的方式继续执行。所以任何人都可以为所有权、交易格式和状态转换函数创建商业逻辑。
(2)账户
以太坊中有两类账户,它们共用同一个地址空间。外部账户,该类账户被公钥-私钥对控制。合约账户,该类账户被存储在账户中的代码控制。外部账户的地址是由公钥决定的,合约账户的地址是在创建合约时由合约创建者的地址和该地址发出过的交易数量计算得到。两类账户的唯一区别是:外部账户没有代码,人们可以通过创建和签名一笔交易从一个外部账户发送消息。每当合约账户收到一条消息,合约内部的代码就会被激活,允许它对内部存储进行读取、写入、发送其他消息和创建合约。
以太坊的账户包含4个部分:①随机数,用于确定每笔交易只能被处理一次的计数器;②账户目前的以太币余额;③账户的合约代码(如果有的话);④账户的存储(默认为空)。
(3)消息
以太坊的消息在某种程度上类似于比特币的交易,但是两者之间存在3点重要的不同。
1)以太坊的消息可以由外部实体或者合约创建,然而比特币的交易只能从外部创建。
2)以太坊消息可以选择包含数据。
3)如果以太坊消息的接收者是合约账户,可以选择进行回应,这意味着以太坊消息也包含函数概念。
(4)交易
以太坊中“交易”是指存储从外部账户发出的消息的签名数据包。交易包含消息的接收者、用于确认发送者的签名、以太币账户余额、要发送的数据和被称为STARTGAS和GASPRICE的两个数值。为了防止代码出现指数型爆炸和无限循环,每笔交易需要对执行代码所引发的计算步骤做出限制。STARTGAS就是通过需要支付的燃料来对计算步骤进行限制,GASPRICE是每一计算步骤需要支付矿工的燃料的价格。
(5)Gas
以太坊上的每笔交易都会被收取一定数量的燃料Gas,设置Gas的目的是限制交易执行所需的工作量,同时为交易的执行支付费用。当EVM执行交易时,Gas将按照特定规则被逐渐消耗。Gas价格由交易创建者设置,发送账户需要预付的交易费用= GASPRICE * Gas amount。如果执行结束还有Gas剩余,这些Gas将被返还给发送账户。无论执行到什么位置,一旦Gas被耗尽就会触发一个out-of-gas异常。同时,当前调用帧所做的所有状态修改都将被回滚。
(6)存储、主存和栈
每个账户都有一块永久的内存区域,被称为存储,其形式为key-value,key和value的长度均为256位。在合约里,不能遍历账户的存储。相对于主存和栈,存储的读操作开销较大,修改存储甚至更多。一个合约只能对它自己的存储进行读写。
第二个内存区被称为主存。合约执行每次消息调用时都有一块新的被清除过的主存。主存可以按字节寻址,但是读写的最小单位为32字节。操作主存的开销随着主存的增长而变大。
EVM不是基于寄存器的,而是基于栈的虚拟机。因此所有的计算都在一个称为栈的区域内执行。栈最大有1024个元素,每个元素有256位。对栈的访问只限于其顶端,允许复制最顶端的16个元素中的一个到栈顶,或者是交换栈顶元素和下面16个元素中的一个。所有其他操作都只能取最顶的一个或几个元素,并把结果压在栈顶。当然可以把栈里的元素放到存储或者主存中。但是无法只访问栈里指定深度的那个元素,在那之前必须把指定深度之上的所有元素都从栈中移除才行。
(7)指令集
EVM的指令集被刻意保持在最小规模,以尽可能避免可能导致共识问题的错误。所有的指令都是针对256位这个基本的数据单位进行的操作,具备常用的算术、位、逻辑和比较操作,也可以进行条件和无条件跳转。此外,合约可以访问当前区块的相关属性,比如它的编号和时间戳。
(8)消息调用
合约可以通过消息调用的方式来调用其他合约,或者发送以太币到非合约账户。消息调用和交易非常类似,它们都有一个源,一个目标,数据负载,以太币,Gas和返回数据。事实上每个交易都可以被认为是一个顶层消息调用,这个消息调用会依次产生更多的消息调用。
一个合约可以决定剩余Gas的分配。比如内部消息调用时使用多少Gas,或者期望保留多少Gas。如果在内部消息调用时发生了out-of-gas异常或者其他异常,合约将会得到通知,一个错误码被压入栈中。这种情况只是内部消息调用的Gas耗尽。在solidity中,这种情况下发起调用的合约默认会触发一个人工异常,这个异常会打印出调用栈。
就像之前说过的,被调用的合约(发起调用的合约也一样)会拥有崭新的主存,并能够访问调用的负载。调用负载被存储在一个单独的被称为calldata的区域。调用执行结束后,返回数据将被存放在调用方预先分配好的一块内存中。调用层数被限制为1024。因此对于更加复杂的操作,我们应该使用循环而不是递归。
(9)代码调用和库
以太坊中存在一种特殊类型的消息调用,被称为callcode。它跟消息调用几乎完全一样,只是加载来自目标地址的代码将在发起调用的合约上下文中运行。这意味着一个合约可以在运行时从另外一个地址动态加载代码。存储,当前地址和余额都指向发起调用的合约,只有代码是从被调用地址获取的。这使得Solidity可以实现“库”。可复用的库代码可以应用在一个合约的存储上,可以用来实现复杂的数据结构,从而使智能合约更加的强大。
2.?以太坊的状态转换
以太坊的状态转换是指在一个交易(TX)发生时,以太坊从一个正确状态(S)转变到下一个正确状态(S’)的转换过程,如图2-15所示。对于交易而言,为了防止代码的指数型爆炸和无限循环,每笔交易需要对执行代码所引发的计算步骤做出限制。STARTGAS就是限制,GASPRICE是每一计算步骤需要支付矿工的费用价格。
图2-15 以太坊状态转换
以太坊的状态转换函数为APPLY(S, TX) -> S’,可以定义如下:
1)检查交易的格式是否正确,签名是否有效,以及随机数是否与发送者账户的随机数匹配。如否,返回错误。
2)计算交易费用fee = STARTGAS * GASPRICE,并从签名中确定发送者的地址。从发送者的账户中减去交易费用和增加发送者的随机数。如果账户余额不足,返回错误。
3)设定初值Gas = STARTGAS,并根据交易中的字节数减去一定量的燃料值。
4)从发送者的账户转移价值到接收者账户。如果接收账户还不存在,创建此账户。如果接收账户是一个合约,运行合约的代码,直到代码运行结束或者燃料用完。
5)如果因为发送者账户没有足够的费用或者代码执行耗尽燃料导致价值转移失败,恢复原来的状态,但是还需要支付交易费用,交易费用加至矿工账户。
6)若代码执行成功,将所有剩余的燃料归还给发送者,消耗掉的燃料作为交易费用发送给矿工。
例如,假设一个合约的代码如下:
if !contract.storage[msg.data[0]]:
contract.storage[msg.data[0]] = msg.data[1]
需要注意的是,在现实中合约代码是用底层以太坊虚拟机(EVM)代码写成的。上面的合约是用我们的高级语言Serpent语言写成的,它可以被编译成EVM代码。假设合约存储器开始时是空的,一个值为10以太、燃料为2000、燃料价格为0.001以太并且两个数据字段值为[2,'CHARLIE']的交易发送后,状态转换函数的处理过程如下:
1)检查交易是否有效,格式是否正确。
2)检查交易发送者是否至少有2000×0.001=2个以太币。如果有,从发送者账户中减去2个以太币。
3)初始设定Gas=2000,假设交易长为170字节,每字节的费用是5,减去850,所以还剩1150。
4)从发送者账户减去10个以太币,为合约账户增加10个以太币。
5)运行代码。在这个合约中,运行代码很简单:它检查合约存储器索引为2处是否已使用,注意到它未被使用,然后将其值置为CHARLIE。假设这消耗了187单位的燃料,于是剩余的燃料为1150-187 = 963。
6)向发送者的账户增加963×0.001=0.963个以太币,返回最终状态。
如果没有合约接收交易,那么所有的交易费用就等于GASPRICE×交易的字节长度,交易的数据就与交易费用无关了。另外,需要注意的是,合约发起的消息可以对它们产生的计算分配燃料限额,如果子计算的燃料用完了,它只恢复到消息发出时的状态。因此,就像交易一样,合约也可以通过对它产生的子计算设置严格的限制,保护它们的计算资源。
3.?以太坊客户端
为了测试各种语言对以太坊的支持,同时使更多的人能够参与以太坊的开发及使用,目前有4种语言编写的以太坊的客户端。它们分别是用Go语言实现的客户端Geth,用C++实现的客户端Eth,用Python语言实现的客户端Pyethapp和用Java实现的客户端EthereumJ。其中,Go语言版是以太坊官方一直维护并推荐使用的客户端。
以太坊包括一个专用的客户端浏览器,使得用户可以运行各种各样的去中心化应用(DApp),发布智能合约。这一浏览器被称为Mist,它易于使用,降低了用户使用门槛,从而使得DApp和智能合约能够被大量用户使用。它的作用等同于浏览器之于互联网或者iTunes之于数字化内容下载。Mist由特殊的安全层、密钥管理、去中心化账户管理和与区块链相关的组件几部分组成。这一切使得Mist成为普通用户运行或者管理区块链去中心化应用不可或缺的工具,普通用户不需要理解技术方面的东西。
从用户体验角度而言,可以在Mist中使用DApp,就像通过常规浏览器与网站进行交互一样。例如,一个纯DApp(例如预测市场Augur)就可以在以太坊Mist浏览器中运行。当然,这些服务也可以通过一个常规浏览器以更加传统的Web 2.0的方式实现。
2.2.3 以太坊智能合约
1.?智能合约
智能合约是由尼克萨博提出的理念,几乎与互联网同龄。但是由于缺少可信的执行环境,智能合约并没有被应用到实际产业中。自从比特币诞生后,人们认识到比特币的底层技术区块链天生可以为智能合约提供可信的执行环境,以太坊首先实现了区块链和智能合约的完整契合。
以太坊是内置有图灵完备编程语言的区块链,通过建立抽象的基础层,使得任何人都能够创建合约和去中心化应用,并在其中设立他们自由定义的所有权规则、交易方式和状态转换函数。建立一个代币的主体框架只需要两行代码就可以实现,诸如货币和信誉系统等其他协议只需要不到20行代码就可以实现。智能合约就像能在以太坊的平台上创建的包含价值而且只有满足某些条件才能打开的加密箱子,并且因为图灵完备性、价值意识(value-awareness)、区块链意识(blockchain-awareness)和记录多状态所增加的功能而比比特币脚本所能提供的智能合约强大得多。
2.?开发语言
以太坊的软件开发语言是其最大特性之一,因为对区块链进行编程是一项首要目标。以太坊具有4种专用语言:Serpent(受Python启发)、Solidity(受JavaScript启发)、Mutan(受Go启发)和LLL(受Lisp启发),都是为面向合约编程而从底层开始设计的语言。
作为以太坊的高级编程语言,Serpent的设计非常类似于Python。它被设计为最大可能地简洁和简单,将低级语言的高效优势与编程风格中的易用性相结合。
Solidity是以太坊的首选语言,它内置了Serpent的所有特性,但是语法类似于JavaScript。Solidity充分利用了现有数以百万程序员已掌握JavaScript这一现状,降低了学习门槛,易于被掌握和使用。
以太坊区块链的另一关键特征是它的“图灵完备性”,这保证了以太坊可以解决所有的计算问题。更加准确地说,它是“半”图灵完备的,因为它是通过对计算量设置上限,它避免了完全图灵完备语言存在的无法停机问题。此外,因为以太坊的语言是为区块链专门设计的,它有账户的概念,使得它在交易的可视化和查询账户状态方面提供了实时性。这是一个受人欢迎的功能,但对比特币而言实现起来具有一定的挑战。在比特币上,由于只有UTXO而没有账户的概念,我们需要导入区块链数据库,解析所有的交易,并为了抽取出在区块链上的某个用户的交易情况而查询交易。而用以太坊,我们则可以在实时的区块链上,根据一个地址情况实时查看当前账户情况和交易状态。
3.?代码执行
以太坊合约的代码是使用低级的基于堆栈的字节码的语言写成的,被称为“以太坊虚拟机代码”或者“EVM代码”。代码由一系列字节构成,每一个字节代表一种操作。一般而言,代码执行是无限循环,程序计数器每增加一(初始值为零)就执行一次操作,直到代码执行完毕或者遇到错误、STOP或者RETURN指令。操作可以访问3种存储数据的空间:
1)堆栈,一种后进先出的数据存储,入栈、出栈的基本单位为32字节。
2)内存,可无限扩展的字节队列。
3)合约的长期存储,一个密钥/数值的存储,其中密钥和数值都是32字节大小。与计算结束即重置的堆栈和内存不同,存储内容将长期保持。
代码可以像访问区块头数据一样访问数值、发送和接收到的消息中的数据,代码还可以返回数据的字节队列作为输出。EVM代码的正式执行模型非常简单。当以太坊虚拟机运行时,它的完整的计算状态可以由元组(block_state,transaction,message,code,memory,stack,pc,gas)来定义,这里block_state是包含所有账户余额和存储的全局状态。每轮执行时,通过调出代码的第pc(程序计数器)个字节,每个指令如何影响元组都有定义。例如,ADD将两个元素出栈并将它们的和入栈,将Gas减1并将pc加1;stack将顶部的两个元素出栈,并将第2个元素插入由第1个元素定义的合约存储位置,同样减少最多200的Gas值,并将pc加1。虽然有许多方法通过即时编译去优化以太坊,但以太坊的基础性的实施可以用几百行代码实现。
2.2.4 以太坊的去中心化应用
1.?什么是DApp
一个DApp是由智能合约和客户端代码构成的。智能合约就像加密的包含价值的箱子。只有当特定条件被满足时它才被打开,它封装了一些逻辑、规则、处理步骤或者双方间的协议。
从架构角度而言,DApp非常类似于传统的Web应用。主要区别是:在传统Web应用中,客户端有JavaScript代码,由用户在自己的浏览器中执行;服务器端的代码由主机运行。但是在一个DApp中,它的智能逻辑运行在区块链上,客户端代码运行在特殊浏览器Mist里面。
2.?应用举例
正如苹果电脑公司的App Store给众多公司和个人提供了极佳的商业机会一样,以太坊这样的平台上一定会出现类似“愤怒的小鸟”这样的成功范例,对于IT导向的创业公司和个人,这是一个广阔的空间。下面举几个例子,看看这个空间中正在孕育的项目。
Augur(www.augur.net),一个正在开发去中心化预测系统。Augur在英文中的意思是“预言家”,用户可以在这个应用上对各种事件打赌并下注,例如希拉里会不会赢得2016年美国的大选;2016年中国的GDP增长会不会超过7%;上证指数2020年之前会不会超10000点,等等。对于参与者而言,如果预测正确,则将获得经济上的回报;而对于社会整体而言,Augur便成了一个群体智慧的收集器,在它上面的下注信息反映了人们对于未来某事件发生可能性的最佳评估。当你打开搜索引擎,输入“×××会不会赢得2020年美国的大选”的时候,可能搜到这样的结果:“Augur:该事件发生的可能性为46.6%”。
Maker(www.makerdao.com),一个正在开发中的金融类去中心化自治组织。Maker是一个去中心化自治组织(Decentralized Autonomous Organization,DAO),它维护一系列用于金融服务的合约软件(即智能合约),其中的一个软件是贷券信贷系统(The Dai Credit System)。贷券信贷系统是一个通过抵押进行借贷的应用,当用户在区块链上拥有众多资产时,一个必然的需求就是,在不出售资产的情况下通过抵押借款获得资金,类似于房屋抵押贷款。
WeiFund,一个正在开发中的去中心化众筹平台。这个众筹平台的好处是资金不需要第三方托管,而是由程序托管,因此能够确保资金100%安全。如果在限定时间内众筹项目的资助超过预定目标,则程序将资金发送给创业者;如果目标未达到,则将资金退回给众筹参与者。
Boardroom,一个正在开发中的DAO管理平台。去中心化自治组织与传统公司一样,也可以有股份的概念,通常就是DAO的代币,这些代币又可以有投票权。与上一个例子中的WeiFund结合,可以发生有趣的事:如果众筹募资的目标达到,则WeiFund可以将资金发送给Boardroom,并将该众筹项目的参与者组成DAO。如此一来,这些参与者便可以通过投票来控制资金的具体使用,而不是简单地将资金一次性发放给创业者,这样便大大避免了来自创业者的道德风险。
Ujo Music,一个音乐版权管理平台,测试版。Ujo Music作为音乐版权管理平台,可以直接在歌曲的创作者与消费者之间建立直接的联系,从而省去了中间商的费用提成。更进一步,歌手甚至可以将自己的作品在WeiFund上进行IPO上市,如此一来,持有歌手代币的投资者就会分得歌手今后歌曲的销售收入,他们是歌手天然的铁杆粉丝团。帮助歌手推广音乐也不仅仅出于经济动机,同时也是出于情感上的支持,试想在周杰伦默默无闻的时候就持有他的代币,该是一件多么令粉丝感到骄傲的事。同时代币也是歌手与粉丝团之间互动的媒介,例如歌手可以让持有代币的粉丝优先购买演唱会的门票,如此,粉丝经济会更加红火。
贷券(Decentralized Autonomous Insured Bond)简称Dai或者Dai Bond,是一种可转让的、彼此等价可互换的“加密债券”,它流通于信贷系统中,使用者无需事先认证,同时又是低风险的。贷券的发行人(借款方)将在以太坊区块链上的以太等数字加密资产作为抵押品来发行贷券,再将这些贷券在市场上卖给贷券持有人以换回流动性好的资产。贷券的持有人之所以买入贷券是为了赚取收益,或者是为了将贷券当作价值稳定的加密货币来流通使用。