以太坊的数据结构
以太坊被描述为为一个交易驱动的状态机,它在某个状态下接受一些输入后,会确定的转移到一个新的状态。具体来说,在一个以太坊的状态下,每个账户上有确定的余额和存储信息,当接收到一组交易,被影响账户上的余额和存储信息会发生变动。从第一个创世块开始,不断的收到交易,由此能进入一连串新的状态。
1.jpg
以太坊每隔一段时间把交易数据和验证信息打包在一个块里,依次串接起来,就成为一个链。块越新,块号(BlockNumber,或叫块高度)便越大,每个块的块头(验证信息)里,保存了前一个块的块头哈希值(ParentHash,父块哈希)。这样区块链里的块就彼此联系了起来。假如我们更改了前间某个块的内容,后面块的父块哈希就和它对应不上,这种块就不被大家认可。这就保证了区块链数据的不可篡改性。
2.jpg
以太坊的块(Block)
每个块包含块头和交易,其中块头的结构如下图所示:
4.jpg
几个关键字段的含义如下:
ParentHash:父块的哈希值
Number:块编号
Timestamp:块产生的时间戳
GasUsed:交易消耗的Gas
GasLimit:Gas限制
Difficulty:POW的难度值
Beneficiary:块打包手续费的受益人,也称矿工
Nonce:一个随机数,使得块头哈希满足POW需求
账户发起交易需要花费一些gas,作为手续费归矿工所占有。在POW模式下,矿工在打包块的时候,需要不停的调整Nonce,使得整个块头的哈希值满足一定的条件(比如头几位都是0)。
StateRoot:状态树的根哈希值
TransactionsRoot:交易树的根哈希值
ReceiptsRoot:收据树的根哈希值
每个矿工在把交易打包成块的时候,会组织三颗树:
交易树,树叶里是交易
收据树,树叶里是交易生成的收据
状态树,树叶里是交易影响到的账户状态
三棵树求取根哈希,可以得到 区块头中的StateRoot,TransactionsRoot,ReceiptsRoot三个字段。这样就建立了交易和区块头字段的映射。当其他用户收到块,根据块里的交易可以计算出收据和状态,计算三个根哈希后和区块头的三个字段进行验证,判断这是否为合法的块。
5.jpg其中交易树和收据树是Merkle树,如上图所示。状态树是Merkle Patricia Tree.
状态(State)
在上文中我们提到,以太坊是基于状态的。多个账户的状态共同组成了以太坊的全局状态。账户分为两种:
外部账户(Externally owned account),被私钥控制且没有任何代码与之关联。一个外部账户可以创建交易,来发送消息给另一个外部账户或合约账户,以此来触发转账交易和智能合约的调用、创建
合约账户(Contract account),被它们的合约代码控制且有代码与之关联。合约账户不可以自己发起一个交易,只能被外部账户调用。
每个账户包含了以下的字段:
Balance:该账户的余额
Nonce:该账户为外部账户时候,表示该账户创建的交易序号,每做一次交易都会加1。该账户为合约账户时候,表示该账户创建的合约序号,每创建一次会加1。
CodeHash:该账户为合约账户时候,表示合约的哈希值,否则为空字符串的哈希
StorageRoot:该账户的存储内容组成Merkle树后求得的根哈希值
多个块的MPT树共享了账户状态,子块状态树和父块状态树的差别在于它指向了在子区块中被改变了的账户。这样节省了总的存储空间,方便了块的回滚操作。
8.jpg交易(Transaction)
外部账户可以创建交易,用自己的私钥进行签名之发送消息给另一个外部账户或合约账户。两个外部账户之间传送的消息即为转账操作。从外部账户到合约账户的消息会激活合约账户的代码,执行各种操作,也就是我们常说的调用智能合约。可以通过向0地址发起交易来创建合约账户。交易包含以下主要字段:
Type:交易的类型,ContractCreation(创建合约)还是MessageCall(调用合约或转账)
Nonce: 发送地址的交易计数
Value: 向目标账户发送的金额
ReceiveAddress:接受方地址
GasPrice:为交易付出的Gas价格
Gas:为交易付出的Gas
Data:交易的附加数据
VRS:交易签名结构体
收据(Receipt)
账户创建交易并向其它节点广播后,会被其它节点执行并放入准备打包的区块。在这个过程中会生成一个收据。收据的主要字段有:
blockHash: 交易所在块的哈希值
blockNumber: 交易在块的序号
transactionHash: 交易的哈希值
transactionIndex: 交易在块中的序号
from: 发送者地址
to: 接受者地址,为空时候表示创建合约
cumulativeGasUsed: 执行完此交易时候,块内消耗的总的gas值
gasUsed:本交易所消耗的gas
contractAddress: 当此交易为创建合约时,表示所创建合约的地址,否则为空
logs: 此交易的日志