《以太坊到底是如何工作》读书笔记
以太坊的简单定义
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 这个算法。