4.1世界状态(state of world) :世界状态( state)是在地址( 160 位的标识符)和账户状态(序列化为 RLP 的数据结构,详见附录 B)的映射。虽然世界状态没有直接储存在区块链上,但会假定在实施过程中会将这个映射维护在一个修改过的 Merkle Patricia 树上(即 trie,详见附录 D)。这个 trie 需要一个简单的后端数据库去维护字节数组到字节数组的映射;我们称这个后端数据库为状态数据库。它有一系列的好处: 第一,这个结构的根节点是基于密码学依赖于所有内部数据的,它的哈希可以作为整个系统状态的一个安全标识;第二,作为一个不变的数据结构,我们可以通过简单地改变根节点哈希来召回任何一个先前的状态(在根节点哈希已知的条件下)。因为我们在区块链中储存了所以这样的根节点哈希值,所以我们可以很容易地恢复到特定的历史状态。
账户的状态(state of world)包含以下四个字段:
nonce: 账户发出的交易数量或者由这个账户所创建的合约数量(当这个账户有相关的EVM代码时)。表示状态中地址的nonce值。
balance:表示账户a拥有多少Wei
storageRoot:保存了账户的存储内容的Merkle Patricia树的根节点的256位哈希值,这个树中保存的是256位整数键值的Keccak256位哈希值到256位整数值的RLP编码的映射。这个哈希定义为
codeHash:与账户相关的EVM代码的哈希值,这个是以太坊部署智能合约的哈希值。当这个地址接收到一个消息调用时,这些代码会被调用,创建后不可更改(也就是说合约创建后不可修改)。状态数据库中包含所有这样的代码片段哈希,以便后续使用。这个哈希可以定义为,然后用b表示代码,则有KEC(b) =
因为我通常希望所指的并不是Trie的根哈希,而是其中所保存的键值对集合,所以我做了一个更方便的定义:
Trie中的键值对集合函数被定义为适用于基础函数中所有元素的变换:
其中:
需要说明的是不应算作这个账户的“物理”成员”,不会参与之后的RLP序列化
如果codeHash字段是一个空字符串的Keccak-256哈希,也就是说,那么这个节点则表示一个简单账户,就是说这个账户没有与EVM相关的合约代码,这个账户没有部署合约,也就是“非合约”账户
因此我们可以定义一个世界状态函数:
其中
函数和函数一起用来提供一个世界状态的简短标识(哈希)。我们假定:
其中, 是账户有效性验证函数
如果一个账户没有代码,它将是empty,且nonce和balance均为0:
即使所谓预编译合约也可能处于empty状态。这是因为它们的账户状态并不总是包含可以表述它们的行为的代码。
当一个账户不存在或为empty时,就表示它dead(死了):
4.2交易
交易(符号,T)是个单一的加密学签名的指令,通常由以太坊系统之外的操作者创建。我们假设外部的操作者是人,软件工具则用于构建和散播。这里的交易有两种类型:一种表现为消息调用,另一种则通过EVM代码创建新的账户(称为“合约创建”)。这两种类型的交易都有一些共同的字段:
nonce: 由交易发送者发出的交易的数量,由表示。
gasPrice: 为执行这个交易所需要进行的计算步骤消耗的每单位gas的价格,以Wei为单位,由表示。
gasLimit: 用于执行这个教育的最大gas数量。这个值必须在交易开始前设置,且设定后不能再增加,由表示。
to :160位的消息调用接收者地址;对与合约创建交易,用表示的唯一成员。此字段由表示
value:转移到接收者账户的Wei的数量;对于合约创建,则代表给新建合约地址的初始捐款。由表示。
v,r,s: 与交易签名相符的若干数值,用于确定交易的发送者,由表示(详见附录F)
此外,合约创建还包括以下字段:
init:不限制大小的字节数组,用来指定初始化程序的EVM代码,由表示。init是EVM代码片段;它将返回body,这是这个账户每次接收到消息调用时会执行的代码(通过一个交易或者代码的内部执行)。init仅会在合约创建时被执行一次,然后就会被丢弃。
与此相对,一个消息调用交易包括:
data:一个不限制大小的字节数组,用来指定消息调用的输入数据,由表示。
附录F详细描述了将交易映射到发送者的函数S。这种映射通过SECP-256k1曲线的ECDSA算法实现,使用交易哈希(除去后3个签名字段)作为数据来进行签名。目前我们先简单地使用S(T)表示指定交易T 的发送者。
在这里,我们假设除了任意长度的字节数组和以外,所有变量都是作为整数来进行RLP编码的、
(16)
其中,
地址哈希稍微有些不同:它是一个02字节的地址哈希值,或者当创建合约时(将等于)是RLP空字节序列,所以是的成员:
4.3区块
在以太坊中,区块由以下部分组成的:一些相关信息片段组成的集合(成为block header,即区块头);组成区块的交易T和其他一些区块头U(这是一些父区块与当前区块的爷爷辈区块相同的区块,这样的区块成为)。区块头包含的信息如下:
parentHash:父区块头的Keccak256位哈希,由表示。
ommersHash:当前区块的ommers列表的Keccak256位哈希,由表示。
beneficiary:成功挖到这个区块所得到的所有交易费的160位接收地址,由表示。
stateRoot:所有交易被执行完且区块定稿后的状态树(state trie)根节点的Keccak256位哈希
transactionsRoot:由当前区块中所包含的所有交易所组成的树结构(transaction trie)根节点的Keccak256位哈希,由表示。
receiptsRoot:由当前区块中所包含的所有交易的收据所组成的树结构(transaction trie)根节点的Keccak256位哈希。由表示。
logsBloom: 由当前区块中所有交易的收据数据中的可索引信息(产生日志的地址和日志主题)组成的Bloom过滤器,由表示。
difficulty:当前区块难度水平的纯量值,它可以根据前一个区块的难度水平和时间戳计算得到,由表示。
number:当前区块的祖先的数量,由表示。创世区块的这个数量为0.
gasLimit:目前每个区块的gas开支上限,由表示。
gasUsed:当前区块的所有交易所用掉的gas之和,由表示。
timestamp:当前区块初始化时的Unix时间戳,由表示。
extraData:与当前区块相关的任意字节数据,但必须在32字节以内,由表示。
minHash:一个256位的哈希值,用来与nonce一起证明当前区块已经承载了足够的计算量。
nonce:一个64位的值,用来与minHash一起证明当前区块已经承载了足够量的计算量,由表示。
区块的另外两个组成部分是ommer区块头(与以上格式相同)列表和一系列的交易。我们可以表示一个区块:
4.3.1交易收据
为了能使交易信息对零知识证明、索引和搜索都是有用的,我们将每个交易执行过程中一些特定信息编码为交易收据。我们以表示第个交易的收据,并把收据信息保存在一个以索引为键的树(index_keyed_trie)中,树的根节点用保存到区块头中。
交易数据R是一个包含四个条目的元组:包含交易数据的区块中当交易发生后的累积gas使用量;交易过程中创建的日志集合;由这些日志信息所构成的Bloom过滤器和交易的状态代码:
函数是将交易收据转换为RLP序列化字节数组的预处理函数:
其中, 代替了先前协议版本中的交易前状态根(the pre-transaction state root)。我们要求状态代码是一个整数。
我们要求累积的gas使用量是一个正整数,且日志的Bloom是一个2048位(256字节)数据的哈希:
是一系列的日志项()。一个日志项O是一个记录了日志产生者的地址;一系列32字节的日志主题和一些字节数据所组成的元组:
我们定义Bloom过滤器函数M将一个日志项精简为一个256字节哈希:
其中() 是一个特别的Bloom过滤器,它通过设置2048位中的3位数值来给定一个随机的字节序列。这是通过取得对一个字节序列中的前三对字节的Keccak-256哈希值的低11位数据实现的:
...
Homestread难度值参数被用来影响出块时间的动态平衡,它已经通过Buterin[2015]提出的EIP-2实现了。在Homestead版本中,难度符号会越来越快地使难度值缓慢增长(每10万个区块),从而增加区块时间差别,也为向权益证明(proof-of-stake)的切换增加了时间压力。这个效果就是所谓的“难度炸弹”("difficulty bomb")或("冰河时期")(“ice age”)在由Schoedon and Buterin[2017]提出的EIP-649中进行了解释,并用来推迟早先在EIP-2中的实现。也在EIP-100通过使用(即上面公式中的矫正参数)和分母9进行了修改,用来达到Buterin[2016]提出的对包含叔区块(uncle blocks)在内的平均出块时间的调整效果。最终,在拜占庭版本中,伴随EIP-649,我们通过伪造一个区块号来延迟冰河时期的来临。这是通过用实际区块号减去300万来获得的。换句话说,就是减少和区块的时间间隔,来为权益证明的开发争取更多的时间并防止网络被“冻结”。