2.1 区块链1.0——数字加密货币
以前我们常说的web1.0、web2.0、移动互联网等概念,那么区块链1.0,区块链2.0乃至区块链3.0又是什么呢?区块链1.0是数字加密货币。
上图是数字加密货币的结构,从整体来看,一个区块链系统分为节点后台和前端两大部分,前端包含多种表现形式,比如钱包、命令行接口、区块链浏览器、图形化界面开发工具等。
我们接触得比较多的就是各种各样的钱包,钱包的主要作用是保管账户或者说保管私钥,在交易的时候进行签名,发送交易验证等。
节点后台包含着系统最核心的功能,包括共识机制、邻节点的管理、区块链管理、交易验证、内存管理、脚本引擎等。节点可以安装在各式各样的计算机或设备中,专门用来挖矿的设备叫矿机。
2.2 区块链2.0 可编程区块链
接下来一起看看区块链2.0。区块链2.0是创建去中心化的程序、自治组织和智能合约的一个系统。
和1.0不同的是2.0提供了一个平台,可以在平台上搭建应用。最大的代表是以太坊,提供了各种模块,让用户可以在其基础上搭建应用,这个应用本质上就是智能合约。
除了构建货币体系之外,智能合约可以用来做股票债券期货贷款产权智能财产等功能。智能合约的核心是利用程序算法替代人去执行合同,这些合同需要自动化的资产过程。合约包含三个基本要素:要约、承诺和价值交换。智能合约有效定义了新的应用形式,使得区块链从最初的货币体系拓展到金融以及其他行业的应用。
如图,我们来看看2.0的内部结构,和1.0相比,2.0也是分为前端应用和后台节点两大部分。前端应用大同小异,不同的是,在区块链2.0中有一个智能合约开发工具,这个开发工具是一个基于web的应用,它在智能合约的开发中被广泛使用。
节点后台最大的变化也是加入了智能合约这一模块以及EVM虚拟机。EVM是用来执行智能合约的虚拟机,它类似于Java的虚拟机,可以执行智能合约编译后的字节码。
以太坊目前支持三种编程语言:solidity、serpent、LLL。Solidity比较主流,除了金融类的应用,solidity还被广泛应用在游戏的表现上,比如去年流行的加密猫就是用solidity开发的。
除此以外,以太坊还加入了数据库模块,数据库模块是存储以太坊的事件状态的,以太坊的区块并不存储所有的交易信息,而只存交易的哈希值。关于以太坊的存储结构,我们会在后面的章节中详细介绍。
2.3 区块链3.0 区块链在金融行业之外的各行业的应用场景
上面介绍了区块链1.0和区块链2.0的特征,那么区块链3.0是什么呢?
区块链3.0:区块链3.0是指区块链在金融行业之外的各行业的应用场景。能够满足更加复杂的商业逻辑。区块链3.0被称为互联网技术之后的新一代技术创新,足以推动更大的产业改革。
2.4 什么是以太坊
以太坊的概念首次在2013至2014年间由程序员Vitalik Buterin,受比特币启发后提出,大意为“下一代加密货币与去中心化应用平台”,在2014年通过ICO众筹开始得以发展。
与其它区块链一样,以太坊需要几千人在自己的计算机上运行一个软件,为该网络提供动力。网络中的每个节点(计算机)运行一个叫做以太坊虚拟机(EVM)的软件。将以太坊虚拟机想象成一个操作系统,它能理解并执行通过以太坊特定编程语言编写的软件。由以太坊虚拟机执行的软件/应用程序被称为“智能合约”。
要在这一世界计算机上做任何事都需付费。不过,付的不是美元或英镑等普通货币,而是该网络自带的加密货币,叫做以太币。以太币与比特币大致相同,除了一点,即以太币可以为在以太坊上执行智能合约而付费。
可以换个方式来理解,就像现在的云计算一样,比如我们要做虚拟机需要付费,以太坊可以理解成另外一种形式的云,我们在它上面跑自己的应用或服务业需要用它自己的货币来支付费用。
以太坊不像比特币那样只是一种加密货币,它还存在其它特征,使其成为了一个巨大的分布式计算机。但是,它运行起来极其缓慢——比如今的普通计算机的运行速度缓慢约5至100倍——而且成本很高。那么为什么这个看起来不怎么样的计算机系统能火遍全球呢?
在以太坊上,无论是人还是智能合约都可作为用户。人类用户能做的事,智能合约也能做,而且还远不止如此。以太坊提供了一个内部的图灵完备的脚本语言以供用户来构建任何可以精确定义的智能合约或交易类型。想建立一个全规模的守护程序(Daemon)或天网(Skynet),你可能需要几千个联锁合约并且确定慷慨地喂养它们,一切皆有可能。
2.5智能合约
那什么是智能合约呢?
想象一下,我们俩关于明天的天气打个赌。我赌明天天晴,你赌明天下雨。我们约定输家必须给赢家100美元。我们如何打这个赌,还要确保输家会履行诺言呢?我能想出以下三种不同方法:
最简单的方法是互相信任。如果我们已经是老朋友了,很容易信任对方。我知道你的家庭住址而你知道我的黑历史。然而,如果我们是陌生人的话,那就难办多了。你没理由信任我,我也没理由信任你。
另一个可行的方法是根据我们之间的赌约制定一份法定合同。我们双方会签署一份详细规定了赌约条款的合同——包括关于输家违约的规定。该合同会让我们有向赢家支付赌金的法律义务,却不具实用性。因为如果通过法律途径强迫对方履行合同,其代价高出赌金本身。
我们可以找一个双方都信任的共同朋友,各交100美元在他/她那里保管。第二天,他/她会查看天气情况,将这200美元都交给赢家。这种方式简单明了,除非出现一种情况:要是这位朋友卷款而逃该怎么办呢?
以太坊的智能合约在这种情况下就可以派上用场了。智能合约就像是寻求共同朋友的帮助,不过是被编写成了代码。通过以太坊,我们可以编写一款软件,向两方各收取价值100美元的以太币。第二天打开接入天气应用的API查看天气情况,并将总价值为200美元的以太币转给赢家。因为区块链的特性,智能合约一旦完成,无论如何都无法被编辑或修改。因此,可以肯定的是不管合约中有何规定,无论如何都会被执行。
2.6 智能合约的原理
本质上来说,智能合约也是一条以太坊交易。不管智能合约于何时执行,它都记录了在区块上执行的交易的信息。上图描述了以太坊的交易,它分为几个字段:时间戳、发送方、接收方、账户发送的金额、data。在这里发送方就是调用者,接收方是智能合约的地址,data就是智能合约要调用的内容,也就是俗称的API,DATA字段,赋予了以太坊独特的能力,用于创建记录和执行智能合约。
通常来说以太坊的交易分为三种:
第一种是人类用户之间的互相转账,如下图所示,这个时候 Data字段是没有内容的,发送者和接收者都是人类。
第二种是无接收方的以太币转账。在进行没有接收方的交易时,这就意味着该交易的目的是在网络中利用“数据”项的内容创建一个智能合约。“数据”项中包含软件代码,该代码会像网络中的其它用户一样进行操作。
第三种是用户和智能合约之间的以太币转账。无论用户(或智能合约)何时想要执行智能合约,他/她/它需要与智能合约进行一次交易,将执行指令置于“数据”项中。
2.6.1 以太坊的账户
在以太坊中有两种账号共享地址空间:外部账号和合约账号。外部账号是由公钥和私钥控制的(如人),合约账号是由账号存储的代码所控制。
无论账户类型是什么都存在这四个组成部分:Nonce、Balance、StorageRoot、CodeHash。
外部账号的地址是由公钥决定的,而合约地址是在智能合约被创建的时候决定的(这个地址由创建者的地址和发送方发送过来的交易数字衍生而来,这个数字通常被叫做“nonce”)不管是否账号存有代码(合约账号存储了代码,而外部账号没有),对于EVM来说这两种账号是相等的。每一个账号都有持久化存储一个key和value长度都为256位字的键值对,被称为“storage”。而且,在以太坊中,每个账号都有一个余额(确切的是用“Wei”来作为基本单位),该余额可以被发送方发送过来带有以太币的交易所更改。
2.6.2 交易
交易的本质是一个账户和另一个账户之间的信息交换,它包含了二进制数据(也就是消费数据)和以太数据。如果目标账号包含了代码,这个代码一旦被执行,那么它的消费数据就会作为一个输入数据。如果目标账号是一个0账号(地址为0的账号),交易会生成一个新的合约。
上图中红框的内容就是一个新的合约所编译出来的字符串。
这个合约的地址不为0,但是是来源于发送方,之后这个账号的交易数据会被发送。这个合约消费会被编译为EVM的二进制代码,并执行。这次的执行会被作为这个合约的代码持久化。这就是说:为了创建一个合约,你不需要发送真正的代码到这个合约上,事实上是代码的返回作为合约代码。
2.6.3 Gas
以太坊上的每笔进行一笔交易都会被收取一定数量的Gas.这是为了限制交易的数量,同时对每一笔交易的进行支付额外费用。当EVM执行一个交易,交易发起方就会根据定义的规则消耗对应的Gas。交易的创造者定义了的Gas 价格。所以交易发起方每次需要支付 gas_price * gas 。如果有gas在执行后有剩余,会以同样的方法返回给交易发起方。如果gas在任何时候消耗完,out-of-gas 异常会被抛出,那当前的这边交易所执行的后的状态全部会被回滚到初始状态。
这是一个保护机制,用来保护以太坊不会受到恶意攻击,让整个网络中一些恶意的程序无法进行。这里又可以看出区块链是用经济模型的方式,或者说用经济的方式来限制黑客攻击的。黑客并不是不能用技术手段来进行恶意攻击,但是他要付出的代价成本远大于获得的价值。
2.6.4 以太坊虚拟机的内存结构
以太坊每个账号都有持久化的内存空间叫做存储. 存储是一个key和value长度都为256位的key-value键值对。从一个合约里列举存储是不大可能的。读取存储里的内容是需要一定的代价的,修改storage里的内容代价则会更大。一个合约只能读取或是修改自己的存储内容。
以太坊的第二内存区域叫做主存。系统会为每个消息的调用分配一个新的,被清空的主存空间。主存是线性并且以字节粒度寻址。读的粒度为32字节(256位),写可以是1个字节(8位)或是32个字节(256字节)。当访问一个字(256位)内存时,主存会按照字的大小来扩展。主存扩展时候,消耗Gas也必须要支付,主存的开销会随着其增长而增大(指数增长)。
EVM不是一个基于寄存器,而是基于栈的。所以所有的计算都是在栈中执行。最大的size为1024个元素,每个元素为256位的字。栈的访问限于顶端,按照如下方式:允许拷贝最上面的16个元素中的一个到栈顶或是栈顶和它下面的16个元素中的一个进行交换。所有其他操作会从栈中取出两个(有可能是1个,多个,取决于操作)元素,把操作结果在放回栈中。当然也有可能把栈中元素放入到存储或是主存中,但是不可能在没有移除上层元素的时候,随意访问下层元素。
2.6.5 指令集
为了避免错误的实现而导致的一致性问题,EVM的指令集保留最小集合。所有的指令操作都是基于256位的字。包含有常用的算术,位操作,逻辑操作和比较操作。条件跳转或是非条件跳转都是允许的。而且合约可以访问当前区块的相关属性比如编号和时间戳。
2.6.6 消息调用
合约可以通过消息调用来实现调用其他合约或是发送以太币到非合约账号。消息调用和交易类似,他们都有一个源,一个目标,数据负载,以太币,gas和返回的数据。事实上,每个交易都包含有一个顶层消息调用,这个顶层消息可以依次创建更多的消息调用。
一个合约可以定义内部消息调用需要消耗多少gas,多少gas需要被保留。如果在内部消息调用中出现out-of-gas异常,合约会被通知,会在栈里用一个错误值来标记。这种情况只是这次调用的gas被消耗完。在Solidity,这种情况下调用合约会引起一个人为异常,这种异常会抛出栈的信息。
上面提到,调用合约会被分配到一个新的,并且是清空的主存,并能访问调用的负载。调用负载时被称为calldata的一个独立区域。调用结束后,返回一个存储在调用主存空间里的数据。这个存储空间是被调用者预先分配好的。调用限制的深度为1024.对于更加复杂的操作,我们更倾向于使用循环而不是递归。
2.6.7 代理调用/ 代码调用和库
存在一种特殊的消息调用,叫做代理调用。除了目标地址的代码在调用方的上下文中被执行,而且msg.sender和msg.value不会改变他们的值,其他都和消息调用一样。这就意味着合约可以在运行时动态的加载其他地址的代码。存储,当前地址,余额都和调用合约有关系。只有代码是从被调用方中获取。这就使得我们可以在Solidity中使用库。比如为了实现复杂的数据结构,可重用的代码可以应用于合约存储中。
2.6.8日志
我们可以把数据存储在一个特殊索引的数据结构中。这个结构映射到区块层面的各个地方。为了实现这个事件,在Solidity把这个特性称为日志。合约在被创建出来后是不可以访问日志数据的。但是他们可以从区块链外面有效的访问这些数据。因为日志的部分数据是存储在bloom filters上。我们可以用有效并且安全加密的方式来查询这些数据。即使不用下载整个区块链数据(轻客户端)也能找到这些日志。
2.6.9创建
合约可以通过特殊的指令来创建其他合约。这些创建调用指令和普通的消息调用唯一区别是:负载数据被执行,结果作为代码被存储,调用者在栈里收到了新合约的地址。
2.6.10 自毁
从区块链中移除代码的唯一方法是合约在它的地址上执行了selfdestruct操作。这个账号下剩余的以太币会发送给指定的目标,存储和代码从栈中删除。