前言:本文主要阐述当前以太坊的具体运作原理,有助于我们理解以太坊背后的各种概念和操作,适合初学者阅读。未来ETH2.0将会发生非常大的变化,具体可以看蓝狐笔记之前的文章《ETH2.0:它会是什么?(一)》和《ETH2.0:它会是什么?(二)》。


接上两篇《以太坊是如何运作的(一)》《以太坊是如何运作的?(二)》


合约创建

 

回顾一下,以太坊有两种类型账户:合约账户和外部账户。当我们说交易是“创建合约”时,我们的意思是说交易的目的是创建一个新的合约账户。

 

为了创建新的合约账户,我们首先使用特殊公式声明新账户地址,然后我们通过如下方式初始化新账户:

 

l 把nonce(随机数)设置为零。

l 如果发送人用交易发送一定量的Ether作为价值,则设置该价值为账户余额。

l 从发送人的余额中扣除发送给新账户的价值。

l 把存储设置为空。

l 把合约的codeHash设置为空字符串的哈希。

 

一旦我们初始化账户,使用跟随交易发送的init代码实际上能够创建账户(有关init代码,可参考“交易和消息”章节)。在执行这个初始化代码时会产生多样的变化。根据合约的构造函数,它可能会升级账户的存储、创建其他合约账户、进行其他消息调用等。

 

当执行初始化合约的代码时,它使用gas。交易不允许使用比剩余gas更多的gas。如果存在这样的情况,执行遇到gas不足的异常,然后退出。如果交易因为gas不足异常而退出,那么,状态会立刻复原到交易之前到那个点。发送人不会收到gas退款,该gas已在之前执行中用完。

 

但是,如果发送人用交易发送任何Ether价值,即使合约创建失败,Ether价值也会被退回。

 

如果初始化代码执行成功,会支付最终的成功创建合约的成本。这是存储成本,支付费用跟所创建合约代码的大小成正比,这里也没有免费午餐。如果没有足够的剩余gas来支付最终的成本,交易再次出现gas不足的异常,并中止。

 

如果一切都顺利,我们目前为止没有异常,则剩余的未使用的gas会退还给交易的最初发送人,由此被改变的状态也允许持续存在。

 

消息调用

 

消息调用的执行跟合约创建的执行类似,不过存在一些差异。

 

消息调用执行并不包括任何init代码,因为没有创建新的账户,但是,如果数据由交易发送人提供,则它可以包含输入数据。一旦执行,消息调用还有额外的组件,组件包含了输出数据,如果接下来的执行需要该数据,它会被使用。

 

跟合约创建一样,如果因为gas不足或交易无效(如堆栈溢出、无效跳转目标或无效指令),消息调用退出,那么,所使用的gas不会退还给最初的调用者。相反,所有剩余的未使用的gas被消耗,并且状态会复原到余额转移之前的点。

 

直到以太坊的最新升级之前,如果没有系统消耗你提供的所有gas,那就无法停止或恢复交易的执行。例如,假设当调用者没有被授权执行某些交易,你授权的合约抛出错误。在之前版本的以太坊,剩余的gas依然会被消耗,且没有任何gas会被退还给发送人。但在拜占庭升级中,它包括了新的“恢复”代码,允许合约停止执行并恢复状态更改,而不是消耗剩余的gas,同时还能返回交易失败的原因。如果因为还原而退出交易,则会把未使用的gas退还给发送人。

 

执行模式

 

到目前为止,我们已经了解到一个交易从开始到结束必须经历的系列步骤。现在,我们来看看交易实际上是如何在VM上执行的。

 

实际处理交易流程的部分协议是以太坊自己的虚拟机,也就是所谓的EVM。EVM是图灵完备的虚拟机。跟其他图灵完备的机器相比,EVM唯一的限制是它跟gas是内在绑定的。也就是说,它的计算量是受gas量内在约束的。

 



资料来源:CMU

 

此外,EVM有基于堆栈的架构。堆栈计算机使用后进先出堆栈来保存临时值。EVM中每个堆栈项的大小是256位,堆栈最大的大小为1024。

 

EVM有内存,其中的item存储为字寻址字节数组。内存不稳定,意味着它不是持久的。

 

EVM还有存储空间。跟内存不同,存储是稳定的,同时它作为系统状态的一部分进行维持。EVM在虚拟ROM中分别存储程序代码,这些代码只能通过特别指令访问。通过这种方式,EVM跟经典的冯·诺依曼架构不同,其中的程序代码存储在内存或存储器中。

 




EVM也有它自己的语言:“EVM字节代码”。当一个程序员编写以太坊智能合约时,一般来说,会使用高阶语言如Solidity。之后,我们可以将其编译为EVM可以理解的EVM字节代码。

 

那么,现在开始执行。

 

在执行特定的计算之前,处理器会确保以下的信息可用且有效:

 

l 系统状态

l 计算用的剩余gas

l 拥有正在执行的代码的账户地址

l 发起该执行的交易发送人的地址

l 导致代码执行的账户地址(可能跟最初发送人不同)

l 产生该执行的交易的gas价格

l 输入该执行的数据

l 作为当前执行的一部分,发送价值(Wei)给此账户

l 要执行的机器代码

l 当前区块的区块头

l 当前消息调用或合约创建堆栈的深度

l 开始执行时,内存和堆栈为空,且程序计数器为零。

 

PC: 0 STACK: [] MEM: [], STORAGE: {}

 

之后,EVM递归执行交易,计算系统状态以及每个循环的机器状态。系统状态只是以太坊的全球状态。机器状态包括:

 

l 可用的gas

l 程序计数器

l 记忆内容

l 内存中的活跃词数

l 堆栈内容

 

从系列的最左侧部分添加或删除堆栈项。

 

在每个循环中,从剩余gas中减少适当的gas量,并且程序计数器递增。

 

在每个循环结束时,有三种可能:

 

1.机器达到异常状态(例如gas不足、无效指令、堆栈项不足、堆栈项因超1024而溢出、JUMP/JUMPI目的地址无效等),因此必须暂停,任何更改都将被丢弃。

2.序列继续处理进入下一个循环

3.机器达到受控停止(执行过程结束)

 

假设执行并没有达到异常状态,也没有达到“受控”或正常停止,则机器会生成结果状态、执行后的剩余gas、累计子状态以及结果输出。

 

我们了解了以太坊最复杂的部分之一。即使你不能完全明白,也没关系。除非你要从事非常深层次的工作,否则你无须理解非常细节的部分。

 

区块如何最终确定

 

最后,我们来看看包含众多交易的区块如何最终确定。

 

当我们说“最终确定”时,它可以意味着两种不同的东西,这取决于区块是新的还是已有的。如果它是新区块,我们指的是挖掘区块要求的过程。如果它是已有区块,那么,我们是指验证区块的过程。在任何一种情况下,有四个要求来实现区块“最终性”。

 

1)验证(或者,如是挖矿,确定)ommers

区块头中的每个ommer区块必须是有效的区块头,并且在当前区块的第六代内。

 

2)验证(或者,如是挖矿,确定)交易

区块上的使用过的gas数必须等于累积的gas,该gas被区块中列出的交易使用过。(回顾一下,当执行交易时,我们一直跟踪区块的gas计数器,它会跟踪区块中所有交易所使用的总gas)。

 

3)申请奖励(仅限挖矿)

受益人地址被授予5 Ether用于区块挖矿。(根据以太坊EIP-649提案,5ETH的奖励将很快会降到3ETH)。此外,对于每个ommer,当前区块的受益人将获得当前区块奖励的额外1/32。最后,ommer区块的受益人也会获得特定数量的奖励(它根据特殊公式来计算,这里不详述。)

 

4)验证(或者,如果挖矿,计算有效的)状态和nonce

确保所有的交易和结果状态改变得到应用,然后,在所有区块奖励已经被应用到最终的交易结果状态后,定义新区块作为状态。通过针对存储在区块头的状态trie来检查该最终状态,以此进行验证。

 

PoW挖矿

 

“区块”章节部分简要介绍了区块难度的概念。赋予区块难度意义的算法叫PoW。以太坊的PoW算法称为“Ethash”(曾经叫做Dagger-Hashimoto)。

 

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部