《以太坊到底是如何工作》

以太坊的简单定义

transactional singleton machine with shared-state 事务性状态共享的单例机器

实际上就是逻辑上唯一,但物理上由多个节点维护的共识中的 world computer。这台机器的状态是由事务变迁驱动的:

幽灵协议

“GHOST” = “Greedy Heaviest Observed Subtree”

简而言之,就是只在拥有最大计算量的路径上进行计算(这个协议是从比特币那里来的吗?)。

账户与事务

外部账户由私钥控制,内部账户由代码控制。

外部账户可以主动发起事务,内部账户只有收到事务以后才能发起内部事务。

账户的状态构成

一个账户的状态总是由四个组件构成:

nonce:如果这是个外部账户,则这个数字代表了这个账户地址发出的事务数。如果这是个合约账户,则这个数字代表了这个账户创造的合约数量。这两种情况下,nonce 都不是随机数。

balance:这个地址拥有的 Wei 数量。一个以太币有个 1e+18 Wei。

storageRoot:默认为空。Merkle Patricia 树的根。

codeHash:对于内部账户,就是 EVM 代码的散列值(意味着代码存在别出)。对于外部账户,这是空字符串的散列值。

以太坊里的默克尔树

以太坊的默克尔帕特里夏树的叶子节点是把地址映射到账户(状态)。

可以看出来状态树的叶子节点就是状态,特别地,状态里还有另一颗默克尔树的根。

同样的多叉 trie 树还用来存储事务和收据(receipts),一共有三种 trie:状态 trie、事务 trie 和收据 trie。

区块头的实际结构

可以看到区块头里还是有自己的 nonce:

完整节点与轻节点

完整节点要下载整条链(从创世区块到当前头区块),执行里面包含的所有事务,否则无以挖矿。

轻节点如果不用执行每条事务或者查询历史信息,只要下载头链即可。

轻节点使用“Merkle Proof”的证明方式来验证一片数据:

步骤如下:

1 要验证的数据 chunk 和它的散列值。
2 默克尔树的根
3 branch(从数据 chunk 到根的所有伙伴散列值,所以这幅图里所有深绿色的节点,都要加入验证)

问题是,这样需要单独下载 branch 到底提升了多少性能呢?这种部分知识证明到底解决了什么查找问题?

gas 与支付

在每个事务里,发送者设置 gas limit 和 gas price,实际花掉的以太币是这两个值的乘积。这两个值和区块/矿工眼里的 gas limt 和 gas price还不太一样。

假设一个矿工设置了如下的两个参数值:

最终扣减账户内以太币的过程则是:

以太币不够,那么状态会回滚,而且在系统中多了一条事务执行失败记录,被用掉的以太币不会被返还:

事务执行花掉的钱最终要付给“beneficiary”地址,实际上就是矿工地址。

操作要付费,存储也要按照每三十二字节对齐一一付费。

为什么要付费呢?因为这个网络操作很贵,要支付矿工的基本维护费用(同中本聪分析的理性逐利一致),而且付费机制可以防止恶意程序拖垮全网。

事务的内容

以太坊是事务性状态机,逻辑上只有一个。

事务是由外部账户生成的密码学签名的指令片,经序列化后提交给区块链。

有两种事务:消息调用和合约创建。

所有事务都包含以下内容:

nonce:这个发送方地址名下创建的事务数。
gasPrice:sender 愿意付的 gas 价格。
gasLimit:发送方愿意支付的 gas 数量上限。
to:接收方地址。
value:要发给接收方的价值。对于合约事务,传送的价值将被存储为合约的初始余额。
v,r,s:用来生成标识这个发送者的签名。
init:生成合约的程序。只被运行一次,它的返回值就是合约本身。猜测应该就是 ABI 和二进制代码。
data:只为了消息调用而存在的参数值。也就是调用的输入数据。

内部事务和外部事物可以说是如出一辙,是不由外部账户生成,也不会被序列化,只存在于以太坊执行环境里的虚拟对象。内部事务也不含 gasLimit,这就要求最初的外部事务的 gasLimit 必须能够覆盖掉所有的衍生 sub-execution。sub-executions 出现不够 gas 的情况,会 revert 掉它的子 sub-execution,而不会 revert 掉 parent-execution(为什么?)。

Ommers 叔叔区块

以太坊的出块时间大约 15 s。

叔叔区块的奖励是为了奖励那些产生叔叔区块的矿工。

区块头信息

区块头包含以下信息:

parentHash:父区块的头的散列-所以区块散列实际上是区块头的散列。
ommersHash:当前区块的ommers 列表的散列。
beneficiary:生成这个区块的受益人的收款地址。
stateRoot:state trie 的根。
transactionRoot:transaction trie 的根。
receiptsRoot:receipt trie 的根。
logsBloom:用布隆过滤器来有效存 log 的地方(数据大头)。
difficulty:当前区块的难度级别
number:当前区块的序号。创世区块是0,每个区块加一。
gasLimit:当前区块的 gasLimit。
gasUsed:这个区块使用掉的 gas 总数。
timestamp:这个区块开始奠基(inception)的时间戳。
extraData:区块附言。
mixHash:与 nonce结合说明计算量的散列值。
nonce:与mixHash结合说明计算量的散列值(为什么不是随机值?)。

事务收据

区块头里存储的日志信息包含事务收据。每个 transaction 都有一个收据。收据包括:

块号(区块 id1?)
块散列值(区块 id2?)
事务散列值(事务 id?)
当前事务消耗掉的 gas
执行完这个事务后,这个区块累计使用的 gas 值。
执行事务生成的 log
其他(是什么?)

区块难度

Hd 就是难度。所以在这里 n 并不是数学游戏的输入,而是 nonce 的目标?

事务执行

计算初始 gas 的过程见原文。

执行过程的每一步,都会生成 log,也会生成一个逐渐减少的 refund balance。

事务执行后,以太坊得到了唯一的确定性状态。

合约创建

先创建一个 nonce 为空,codeHash 为空的 account,然后执行 init,把生成的合约代码和账户关联起来。

消息调用

消息调用类似合约创建。

执行模型

很复杂,还是看原文

PoW 的原理

还是ETHASH 算法,还是很复杂。但重点是 m 和 n 分别是 mixhash 和 nonce,这俩联合起来才能 match 这个算法。