《区块链原理与技术》学习笔记 第五部分
- 5. 以太坊交易
- 5.1 交易内容
- 5.2 交易费用
- 5.3 交易的周期
- 5.4 交易的执行类型
- 6. 以太坊的共识机制
- 6.1 解决以太坊分叉:Ghost协议
- 6.2 新的共识机制:PoS
- 7. 以太坊挖矿难度调整
- 7.1 自适应难度调整
- 7.2 难度炸弹
- 8. 数据结构与存储
- 8.1 区块和叔块
- 8.2 默克尔前缀树(Merkle Patricia Trie)
- 8.3 布隆过滤器(Bloom Filter)
5. 以太坊交易
以太坊中,交易承载了账户转账和合约创建、调用合约等功能。
5.1 交易内容
交易中的数据大体分为基本的交易、交易的执行参数、交易的签名三个部分。
基本交易内容
- From:交易发送者的地址,实际上由签名中的计算得到。
- To:交易接收者的地址。创建合约时设置为0x0000,调用合约时是合约的地址。
- Value:交易的金额。1Ether = 10^18 Wei。
交易的执行参数
- Input Data:交易附带数据,传递创建合约的代码/构造函数、调用合约的函数和参数。
- Nonce:交易发送者的Nonce。
- MaxFeePerGas:发送者为交易愿意付出的最大price
- MaxPriorityFeePerGas:给矿工付出的最大小费
- gasLimit: 交易允许消费的最大gas,解决智能合约不能停机的问题。
交易的签名
- hash:由以上字段生成的哈希值
- r,s,v:由发送者的私钥对交易的哈希做数字签名而成,用于确认转账的合法性。
5.2 交易费用
以太坊中交易的的手续费由Gas机制计算,主要包括以下概念:
- Gas:计算资源消耗的基础单位
- GasLimit:允许消耗的最大Gas值
- GasUsed:执行后消耗的Gas值
- GasPrice:每个Gas的以太币价格
其中交易的Gas由基础的交易Gas加上EVM运行时消耗的Gas值累加得到。当GasUsed超过GasLimit,交易执行失败。
交易的GasUsed*GasPrice就是用户为交易支付的手续费。
5.3 交易的周期
1.发起
- 用户输入from,to,value,data,gasPrice等
- 用户确认发送至以太坊节点
- 以太坊钱包软件为用户补充gasLimit,nonce,使用私钥得到r,s,v,最后将交易序列化后发送到网络。
2.广播
- 节点收到/发起交易后,对交易进行验证
- 节点验证为合法交易,将交易加入节点的交易池中
- 节点根据P2P网络广播的策略向相邻节点继续广播交易
3.打包与执行
- 全节点将交易打包,并将交易按顺序执行
- 所有需要打包的交易执行后,将交易、状态、收据的信息打包到区块中。
- 记账节点获得合法区块后,广播到相邻节点
4.验证与执行
- 没有获得记账权的节点在收到广播的区块后,对区块进行合法性验证,并进行交易的执行。
5.4 交易的执行类型
根据to值的不同,交易的执行分为以下3种:
1.创建合约交易
to为空的交易,交易根据from和nonce值生成合约地址,并执行data中的智能合约代码。EVM会将代码存储到合约地址中。
2.调用合约交易
to为合约账户的交易。EVM获取to地址中的代码,并执行data中的代码。
本质上来说。对合约的调用就是对合约状态的修改。
3.普通转账交易
to为人控制的账户(外部账户),交易直接将以太币从from转到to
6. 以太坊的共识机制
6.1 解决以太坊分叉:Ghost协议
叔块
指不在主链但被主链记录的满足难度的区块。矿工在打包区块时,可以自主选择合法的0-2个叔块。
以太坊中规定,7代以内的有共同祖先的都可以认为是叔父块。
叔父块的接纳和奖励规则
- 对于主链上的区块,每个区块最多接纳2个叔父块,每接纳一个叔父块,主链区块奖励增加1/32。
- 接纳的叔父块应该是主链上祖先的直接子块,且不能重复接纳叔父块。
- 被接纳的叔父块,获得出块奖励为1-(接纳块高度-叔父块高度)/8
- 叔父块中的交易不需要执行。
叔父块可以尽量收缩和统一整个区块链的主链,同时维护矿工的积极性。
6.2 新的共识机制:PoS
权益证明(Proof-of-Stake):持有币的数量和占有币数的时间来决定你获得本次记账权利的概率。持有越多,获得记账概率越大。
相比PoW缩短了达成共识时间,节省能源,但是容易分叉和中心化。
7. 以太坊挖矿难度调整
挖矿难度的公式为:
H=0,D(H)=0 D(H)=max( D 0,P(H ) Hd +x× ζ 2)+ϵH=0,D(H)=0 \\ D(H)=max(D_0,P(H)_{H_d}+x × \zeta_2)+\epsilon H=0,D(H)=0D(H)=max(D0,P(H)Hd+x×ζ2)+ϵ
其中 P ( H ) H d P(H)_{H_d}P(H)Hd是父区块的难度, x × ζ2 x×\zeta_2x×ζ2 用于自适应调节出块难度, ϵ\epsilonϵ表示设定的难度炸弹
基础部分有下界,最小值为 D0= 131072D_0=131072D0=131072
7.1 自适应难度调整
x= ⌊P ( H ) H d 2048⌋, ζ 2=max ( y − ⌊H s−P(H ) Hs 9⌋, − 99 )x= \left\lfloor \frac{P(H)_{H_d}}{2048} \right\rfloor, \ \zeta_2=max \left( y-\left\lfloor \frac{H_s-P(H)_{H_s}}{9} \right\rfloor, -99 \right) x=⌊2048P(H)Hd⌋,ζ2=max(y−⌊9Hs−P(H)Hs⌋,−99)
x为调整的单位, ζ2 \zeta_2ζ2 为调整的系数
如果父区块中包含uncle,y为2,否则为1
Hs H_sHs是本区块的时间戳, P ( H ) H s P(H)_{H_s}P(H)Hs是父区块的时间戳,并规定 Hs> P ( H ) H s H_s > P(H)_{H_s}Hs>P(H)Hs
7.2 难度炸弹
ϵ= ⌊ 2 ⌊ Hi′/ 100 , 000 ⌋ − 2⌋, H i ′=max( H i−30,000,000)\epsilon = \left\lfloor 2^{\lfloor H’_i / 100,000 \rfloor -2} \right\rfloor, \ H’_i=max(H_i-30,000,000) ϵ=⌊2⌊Hi′/100,000⌋−2⌋,Hi′=max(Hi−30,000,000)
ϵ\epsilonϵ 是2的指数函数。每100000块扩大一倍
设置难度炸弹是为了降低迁移到PoS协议时发生fork的风险,引导矿工有意愿迁移到PoS。
8. 数据结构与存储
8.1 区块和叔块
以太坊区块的区块头包括:
- 记录以太坊状态的状态根
- 交易列表、收据列表、父块和叔块的哈希值
- 区块号、难度、矿工地址、时间戳、Nonce值
- gas上限、交易gas之和、工作量证明摘要
- 可变长度字段
以太坊区块体的内容:
- 由交易组成的TX列表
- 有交易执行信息组成的收据列表
- 用于改进以太坊共识过程的叔块列表
世界状态
以太坊中所有账户的状态的汇总,也就是区块头的状态根,通过一个特殊的树状哈希数据结构实现。
收据
对应交易的数据结构,代表交易执行的一些中间状态写入和交易的执行结果等信息。
收据的内容包括:
- 以太坊的智能合约向EVM输出的执行日志
- 智能合约执行的Gas
- 单个交易执行完毕后以太坊的状态根
- 如果交易创建合约,收据中写入新建合约的地址
8.2 默克尔前缀树(Merkle Patricia Trie)
由于以太坊潜在的账户数量巨大,单使用Merkle Tree无法存储数据,且修改成本高,因此以太坊使用了十六叉压缩前缀树,作为地址到账户的索引,然后利用Merkle Tree将每一层节点合并计算节点的哈希值,最后得到根哈希值。
压缩前缀树
普通的字典树(Trie)为每一个字母作为树的节点,这样导致树的高度太高,浪费存储空间;压缩前缀树(Patricia Trie)将共同的前缀部分组成一个子树,逐级向下划分,提高了查找效率。
MPT
在MPT中,“单词”对应账户的哈希值,“指针”对应哈希指针。叶子节点记录(前缀+账户哈希值)的哈希值,父节点的哈希值为(前缀+子分支)的哈希值。
以下图为例:
H1=H(“”+账户1的哈希值),H3=H(d+账户3的哈希值)
H5=H(bc+H1+H2+“”+“”+…+“”)
H6=H(b+H4+…+“”+H3+“+”“)
H_root=H(”“+…+”“+H5+…+”“+H6+””)
状态树
以太坊的状态树记录各个账号的状态,当状态改变后只会影响对应分支的状态,区块存储对应状态树的状态根。
合约存储树
合约账户存储树也使用了MPT来记录存储地址到存储值的映射,映射表的哈希值叫做存储根(storage root)。
由于合约存储空间中的一个单位刚好为256位,所以可以直接当叶子节点
交易树和收据树
这两棵树通过交易/收据在区块中的序号来构建MPT,收据和交易的信息一一对应。
8.3 布隆过滤器(Bloom Filter)
布隆过滤器可以检索一个值是否在一个集合中,以太坊用来对收据的日志进行索引、查找。
在容忍一定错误率的情况下,它的效率很高。
原理:用多个哈希函数将键值映射到一个位图中。对于一个键值,若在同样的哈希函数映射下,没有在位图中出现标记位,则键值一定不在集合中。