1、引言
区块链的本质是一个分布式的数据库。因此不同时刻的用户数据的写入对应着不同的状态。比特币使用UTXO来表示状态的转移,而以太坊使用账来表示状态的转移。
2、账户
以太坊中存在两种账户,分别是外部账户和合约账户。
外部账户EOA:一般自然人分配的账户,被私钥控制且没有任何代码与之关联。
合约账户CA:给智能合约分配的账户,被合约代码控制且有代码与之关联。
账户在源码中的定义如下:
typeAccountstruct{
Nonce uint64
Balance *big.Int
Root common.Hash//merklerootofthestoragetrie
CodeHash[]byte }
Nonce:如果当前账户为EOA,则Nonce为发送的交易序号;如果当前账户为CA,则Nonce为合约创建的序号。
Balance:账户余额,表示账户所对应地址的余额。
Root:存储为状态树的根hash,在EOA中,此字段为空。
CodeHash:账户绑定的EVM code,在EOA中,此字段为空。
图1 此图片来自网络
EOA账户只能发起交易,例如转移以太币或者触发合约代码。CA账户不能发起交易,可以被触发执行合约代码。
外部账户对应转账地址,由用户创建;合约账户在以太坊中由EVM根据合约地址创建(在代码vm文件夹下evm.go文件中)。
3、交易
typeTransactionstruct{
datatxdata
//caches
hashatomic.Value
sizeatomic.Value
fromatomic.Value
}
typetxdatastruct{
AccountNonceuint64
Price *big.Int
GasLimit uint64
Recipient *common.Address
Amount *big.Int
Payload []byte
//Signaturevalues
V*big.Int`json:"v"gencodec:"required"`
R*big.Int`json:"r"gencodec:"required"`
S*big.Int`json:"s"gencodec:"required"`
//ThisisonlyusedwhenmarshalingtoJSON.
Hash*common.Hash`json:"hash"rlp:"-"`
}
AccountNonce:发起者发起的交易总数量
Price:此次交易的gas price
GasLimit:本交易允许消耗的最大gas数
Recipient:交易接收者的地址
Amount:此次交易转移的以太币数量
Payload:其中每个byte对应一个单独虚拟机指令
V签名数据
R签名数据
S签名数据
hash交易的hash
size交易数据的大小
from交易的发起地址
3、区块头
typeHeaderstruct {
ParentHash common.Hash //Hp,上一区块全部内容的hash
UncleHash common.Hash //Ho,本区块的所有叔块列表的hash
Coinbase common.Address //Hc,成功挖出本区块的矿工地址
Root common.Hash //Hr,本区块所有交易的状态tree的根hash
TxHash common.Hash //Ht,本区块所有交易tree的根hash
ReceiptHash common.Hash //He,本区块所有交易收据tree的根hash
Bloom Bloom //Hb,交易收据日志组成的Bloom过滤器
Difficulty *big.Int //Hd,本区块难度级别
Number *big.Int //Hi,区块序号,从创世块0递增
GasLimit uint64 //Hl,每个区块当前的gas limit
GasUsed uint64 //Hg,本区块交易消耗的总gas
Time *big.Int //Hs,本区块创建时的Unix时间戳
Extra []byte //Hx,区块附加数据,<=32字节
MixDigest common.Hash //Hm,256位的hash
Nonce BlockNonce //Hn,64位的hash
}
图2 此图片来自网络
Root(状态hash):整个系统状态的hash,也就是所有账户状态树的根hash。
TxHash(交易列表hash):本区块中所有交易默克尔树的根hash。
ReceiptHash(收据列表hash):收据默克尔树的根hash。
状态树并不存在于链上,而存在于节点的levelDB中。只有它的根hash存在于区块头Root中,每一个区块头里的Root都是区块被挖出确认时的快照。
补充(此内容来自于网络)
三棵树允许轻客户端轻松地进行并核实以下类型的查询答案:
1) 这笔交易被包含在特定的区块中了么?
2) 告诉我这个地址在过去30天中,发出X类型事件的所有实例(例如,一个众筹合约完成了它的目标)
3) 目前我的账户余额是多少?
4) 这个账户是否存在?
5) 假装在这个合约中运行这笔交易,它的输出会是什么?
第一种是由交易树(transaction tree)来处理的;第三和第四种则是由状态树(state tree)负责处理,第二种则由收据树(receipt tree)处理。计算前四个查询任务是相当简单的。服务器简单地找到对象,获取梅克尔分支,并通过分支来回复轻客户端。第五种查询任务同样也是由状态树处理,但它的计算方式会比较复杂。
总结
整个以太坊维持一个所有账户状态的树,这棵树的hash会打包在整个链上;
账户状态树本身的信息不会上链,保存在leveldb中;
交易(狭义的)、消息等相关信息通过交易(广义的)的方式打包在区块上;
交易的执行可大致分为内外两层结构:第一层是虚拟机外,包括执行前将Transaction类型转化成Message,创建虚拟机(EVM)对象,计算一些Gas消耗,以及执行交易完毕后创建收据(Receipt)对象并返回等;第二层是虚拟机内,包括执行转帐,和创建合约并执行合约的指令数组;
合约账户在创建合约时生成,不管是合约还是交易,都会和账户(EOA、CA)相关,所以区块中交易树hash、状态树hash、收据树hash三者能保证链的一致性。