前面一章简单的说了区块存储,讲述了区块头、区块体、交易信息的存储,本章来细化下区块、账户、状态、交易、费用等基础结构,加强对其他的了解更快的学习以太坊。
本章目的:
细化以太坊 区块、账户、状态、交易、费用等
准备条件:
从创世区块开始,无尽的交易不断的刷新着系统当前状态,每产生一个区块就对当前状态做一次快照(patricia trie根)存入区块头中。
patricia trie是merkle tree的变体,请自行了解merkle tree,前面文章有简单介绍默克尔树。
区块结构:
区块头、叔块、交易列表三大部分组成区块;
区块结构定义如下:
type Block struct {
header *Header
uncles []*Header
transactions Transactions
// caches
hash atomic.Value
size atomic.Value
// Td is used by package core to store the total difficulty
// of the chain up to and including the block.
td *big.Int
// These fields are used by package eth to track
// inter-peer block relay.
ReceivedAt time.Time
ReceivedFrom interface{}
}
叔块其实就是孤块,因以太坊出块速度很快平均十几秒就会打包生成一个块,所以矿工挖矿的竞争性很高,可能同时产出几个都合法的区块,以太坊为了一些安全性起见,允许竞争块也挂在到主链上,同时给与挖出这些孤块的矿工们少许奖励增加工作的公平性。这些孤块最多允许6个高度,这也是6个区块确认主链说法的来源。
实际的区块信息:
"blocks" : [
{
"blockHeader" : {
"bloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x3535353535353535353535353535353535353535",
"difficulty" : "0x020000",
"extraData" : "",
"gasLimit" : "0x05f5e100",
"gasUsed" : "0x014fa1",
"hash" : "0x39f4659b079e257df8fd7e699528531e97a6b8a442ca0d11200c4a2f7433c483",
"mixHash" : "0x7379f33af4ae2db7e293f808a165135d0b1a99572cc96fb9f7d17ef64a751969",
"nonce" : "0x8e08d7aabeee8773",
"number" : "0x01",
"parentHash" : "0xadbef3bf0b3b7b14f6e7b1a45d240ecc863543a279a86c23f60170e8e7a6bcc3",
"receiptTrie" : "0xb21660268480338c0cd0613358315359b619bd527d5850949c4863cddaec316b",
"stateRoot" : "0xde4ce9b5b2f88ab1680962c64281224b1743bdf94bd6a9e390ea779ff616c1f7",
"timestamp" : "0x03e8",
"transactionsTrie" : "0x56445ba866f3e41851154fb8700dcec8556a178f1833021e030b8a47b494769d",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
"rlp" : "0xf90308f901f9a0adbef3bf0b3b7b14f6e7b1a45d240ecc863543a279a86c23f60170e8e7a6bcc3a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347943535353535353535353535353535353535353535a0de4ce9b5b2f88ab1680962c64281224b1743bdf94bd6a9e390ea779ff616c1f7a056445ba866f3e41851154fb8700dcec8556a178f1833021e030b8a47b494769da0b21660268480338c0cd0613358315359b619bd527d5850949c4863cddaec316bb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018405f5e10083014fa18203e880a07379f33af4ae2db7e293f808a165135d0b1a99572cc96fb9f7d17ef64a751969888e08d7aabeee8773f90108f90105460183030d4094c305c901078781c232a2a521c2af7980f8385ee980b8a430c8d1da000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000001ba021a28cc82b40931239f8653ffa5300e1a506c0ef7fb79a663772cafe6558ab44a075af23441f7f176a2770af41142c77b671391209b15d59144e7a1332179b5e14c0",
"transactions" : [
{
"data" : "0x30c8d1da000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000",
"gasLimit" : "0x030d40",
"gasPrice" : "0x01",
"nonce" : "0x46",
"r" : "0x21a28cc82b40931239f8653ffa5300e1a506c0ef7fb79a663772cafe6558ab44",
"s" : "0x75af23441f7f176a2770af41142c77b671391209b15d59144e7a1332179b5e14",
"to" : "0xc305c901078781c232a2a521c2af7980f8385ee9",
"v" : "0x1b",
"value" : "0x00"
}
],
"uncleHeaders" : [
]
}
]
区块头:
区块头结构定义如下:
type Header struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
Coinbase common.Address `json:"miner" gencodec:"required"`
Root common.Hash `json:"stateRoot" gencodec:"required"`
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
Bloom Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
Number *big.Int `json:"number" gencodec:"required"`
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
Time *big.Int `json:"timestamp" gencodec:"required"`
Extra []byte `json:"extraData" gencodec:"required"`
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
Nonce BlockNonce `json:"nonce" gencodec:"required"`
}
区块头结构解析:
type Header struct {
ParentHash common.Hash //Hp,上一区块全部内容的hash,区块因它而成链!
UncleHash common.Hash //Ho,本区块的ommers(所有叔块)列表的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组合证明出块执行了足够的计算
Nonce BlockNonce //Hn,64位的hash,与MixDigest组合证明出块执行了足够的计算
}
有三个特别的字段保存的是patricia trie的根,
Root(状态hash)、TxHash(交易列表hash)、ReceiptHash(收据列表hash)。
Root树:就是系统状态hash。系统状态就是以太坊整个网络中所有账户的状态,就是world state,它是一个merkle patricia trie结构。这个树(包括所有patricia trie)并不存在于链上,而存在于节点的levelDB中。只有它的根hash存在于区块头Root中,每一个区块头里的Root都是区块被挖出确认时的快照,而world state指现在所有账户的状态。所以它表示的是整个以太坊系统所有账户当前的状态。world state是跨块存在的,另外两棵树只存储本区块的交易和收据。
账户:
以太坊中有两种账户
1、外部拥有账户(EOA),一般指自然人拥有的账户。
2、合约账户(CA),为智能合约分配的账户。
账户结构定义:
// Account is the Ethereum consensus representation of accounts.
// These objects are stored in the main account trie.
type Account struct {
Nonce uint64
Balance *big.Int
Root common.Hash // merkle root of the storage trie
CodeHash []byte
}
账户结构解析:
type Account struct {
Nonce uint64 // 若为EOA是发送的交易序号,如为CA是合约创建的序号。
Balance *big.Int // 这个地址的余额。
//merkle root of the storage trie
Root common.Hash // 账户自身内容RPL编码组成的Merkle Trie的根哈希
CodeHash []byte // 账户绑定的EVM Code,账户一经创建不可修改。
}
以太坊是一个基于交易的状态机。
任意两个账户之间的交易都会引起world state的改变。
两个相邻区块之间的状态差别很小,patricia trie这种数据结构能高效的处理整个系统账户变化的部分。
交易基本定义:【从外部拥有账户】发送的加密签名序列化指令。换句话说交易必须是从EOA发起的才能叫交易,CA之间的通信叫消息也有叫内部交易的,现在是有区别,以后这个区别会不会模糊化不知道。
交易类型有两种:
1、消息调用(Td)
2、合约创建(Ti)
从EOA到EOA的交易仅是转账。
EOA到CA可以激活各种操作。
交易结构定义:
type txdata struct {
AccountNonce uint64 //Tn
Price *big.Int //Tp
GasLimit uint64 //Tg
Recipient *common.Address //Tt
Amount *big.Int //Tv
Payload []byte //Td || Ti
V *big.Int
R *big.Int
S *big.Int
// This is only used when marshaling to JSON.
Hash *common.Hash
}
Tn必须等于发起交易的账户的nonce(账户nonce是该账户发起的第几笔交易的序号,如果是创建合约则代表第几次创建合约的序号)
T p是这笔交易消耗的gas单价Td或Ti,如果交易类型是消息调用则Palload写为Td,表示输入数据,例如消息的参数,假设有一个注册域名的合约服务,则Td就是该服务需要的参数如IP等。如果交易类型是创建合约,则Payload写为Ti,表示一段代码,这段代码用于创建合约账户,这段初始化代码只会被执行一次就丢弃掉,第二次执行的是创建完的合约代码体。
可以看到当接收账户不同时,区别仅仅是Td和Ti的区别。
以太坊网络里任何计算都要支付gas(燃料),
思考为什么不直接用eth做费用?
答案是用两个概念gas和eth区别价值和价格,gas是一种固定衡量的价值,而eth是市场上快速变化的价格,很多EVM(以太坊虚拟机)的操作指令都需要消耗固定的费用就用gas来计价,gas的最小单位是wei,1eth = 1018wei = 109gwei。所以eth和gas之间是有汇率的。
GasPrice:燃料单价
GasLimit:愿意支付的燃料上限
GasLimit × GasPrice = 愿意支付的最大费用
10000 × 100gwei = 1015wei = 0.001eth
这图要说明的是:
一笔交易中,你设置的最大费用如果没有消耗完,多出的会返回给你。如果最大费用不够计算的花费,那么交易会终止、已改变的状态会回滚、但是钱被消耗不会退回了。这些已消耗的费用都奖励给矿工了。
计算都是有费用的,初次之外还有一些东西需要缴费:
费用的三种不同构成:
1)计算操作的固定费用
2)交易(合约创建或消息调用)费用
3)存储(内存、存储账户合约数据)费用
着重说一下存储费用:
存储收费是因为假如你的合约使得状态数据库存储增大,所有节点都会增加存储。
以太币是鼓励尽量保持少量存储的。
但是如果有操作是清除一个存储条目,这个操作的费用不但会被免除,而且由于释放空间还会获得退款。