本文主要是介绍以太坊go-ethereum源码研读(一)从Process函数相关自定义类型和结构体开始,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
- 写在前面
- 一些自定义类型
- 一些结构体
- Receipt
- Log
- Header
- ChainConfig
- BlockChain
- Transaction
- Block
- StateProcessor
- BlockContext
- evm
- Message
- StateDB
- accessList
- Engine
- 相关函数
- Process
- NewEVMBlockContext
- NewEVM
- NewEVMInterpreter
- AsMessage
- Prepare
- Finalize
写在前面
现在自己对其中一些代码的理解还不够,等我逐渐深入学习后回回来再修改的。
其中对于一些代码的理解参考了以太坊黄皮书的内容。
链接: https://ethereum.github.io/yellowpaper/paper.pdf
一些自定义类型
类型名 | 位置 | 定义 |
---|---|---|
Hash | common/types.go | 32byte |
Address | common/types.go | 20byte |
Bloom | core/types/bloom9.go | 256byte(filter) |
GasPool | core/gaspool.go | uint64 |
一些结构体
Receipt
位于core\types\receipt.go
为了方便索引、搜索交易和对交易的零知识证明,将交易执行中的某些信息进行编码形成了Receipt
参考黄皮书4.3.1
名字 | 定义 |
---|---|
Type | 交易类型 |
PostState | StateDB的MPT树根,相当于当前Block事务执行后所有账户状态 |
Status | 当前交易的状态 |
CumulativeGasUsed | 累积的Gas使用量 |
Bloom | 布隆过滤器,用来快速验证给定Log是否为这个事务生成 |
Logs | 交易执行过程中生成的log集合 |
TxHash | 交易hash值 |
ContractAddress | 交易对应智能合约地址 |
GasUsed | 使用的Gas量 |
BlockHash | 区块hash |
BlockNumber | 区块号 |
TransactionIndex | 交易索引 |
Log
位于core\types\log.go
以太坊中定义了event和log机制,用于表示一个合约的日志。
名字 | 定义 |
---|---|
Address | 对应事件的合约地址 |
Topics | 用于检索日志时使用 |
Data | 由合约提供的ABI编码的内容 |
BlockNumber | 区块号 |
TxHash | 交易hash |
TxIndex | 该Log对应的交易在区块中的索引 |
BlockHash | 该Log对应的交易所在的区块hash |
Index | 该Log在区块中的索引 |
Removed | 当发生了链重组导致log被恢复,该字段为真。故如果通过过滤器查询log时,要多注意该字段。 |
Header
位于core\types\block.go
表示区块头
名字 | 定义 |
---|---|
ParentHash | 父区块的hash值 |
UncleHash | 叔区块RLP编码hash |
Coinbase | 矿工地址 |
Root | 世界状态的根hash |
TxHash | 交易信息的根hash |
ReceiptHash | 收据信息的根hash |
Bloom | 布隆过滤器 |
Difficulty | 挖矿的难度系数 |
Number | 区块序号 |
GasLimit | 区块内Gas消耗上限 |
GasUsed | 区块交易完成后消耗Gas总量 |
Time | 区块生成的时间(貌似并不太精准) |
Extra | 区块创建者(矿工)记录的信息 |
MixDigest | hashimotoFull函数生成后的digest生成的hash值,可用于结合nonce进行工作量证明 |
Nonce | Pow枚举猜测的值 |
BaseFee | EIP-1559新增的区块头可选项,允许协议强制执行最低费用,而不会激励矿工和交易方在链下交易,形成链外市场。 |
ChainConfig
位于params\config.go
代表区块链的配置
名字 | 定义 |
---|---|
DAOForkBlock | DAO硬分叉的区块号 |
DAOForkSupport | 当前节点是否支持DAO硬分叉 |
BlockChain
位于core\blockchain.go
待补充
Transaction
位于core\types\transaction.go
其中txdata是一个接口,定义如下,位于相同位置
位于core\types\transaction.go
名字 | 定义 |
---|---|
inner | 交易相关共识内容 |
time | 交易时间 |
hash | 交易hash值 |
size | 交易大小 |
from | 交易发起方 |
Block
位于core\types\block.go
表示区块
名字 | 定义 |
---|---|
ParentHash | 父区块的hash值 |
UncleHash | 叔区块RLP编码hash |
Coinbase | 矿工地址 |
Root | 世界状态的根hash |
TxHash | 交易信息的根hash |
ReceiptHash | 收据信息的根hash |
Bloom | 布隆过滤器 |
Difficulty | 挖矿的难度系数 |
StateProcessor
位于core\state_processor.go
名字 | 定义 |
---|---|
config | 区块配置 |
bc | 区块链 |
engine | 用于区块奖励的共识引擎 |
BlockContext
位于core\vm\evm.go
表示区块上下文,很多属性和前面是重复的。
名字 | 定义 |
---|---|
CoinBase | 矿工地址 |
GasLimit | Gas的限制量 |
BlockNumber | 区块号 |
Time | 时间 |
Difficulty | 难度 |
BaseFee | 协议执行最低费用 |
evm
位于core\vm\evm.go
待补充
名字 | 定义 |
---|---|
interpreter | 解释编译程序 |
Message
位于core\types\transaction.go
派生的事务(待补充)
名字 | 定义 |
---|---|
nonce | 即为交易中的nonce,用来交易排序,交易校验以避免双花 |
gasLimit | 当前消息gas最大限制 |
gasPrice | 油价 |
gasFeeCap | 用户所能支付给矿工的最大单价限额 |
gasTipCap | 小费,即在网络拥堵的情况下支付给矿工的小费,这个也意味着矿工有优先选择权。支付该费用,则优先打包区块 |
StateDB
位于core\state\statedb.go
StateDB结构用于存储所有的与Merkle trie相关的存储, 包括一些循环state结构
accessList
位于core\state\access_list.go
每个事务的访问列表,在某些形式的 EVM 执行过程中会触及的账户和合约存储位置的列表
Engine
位于consensus\consensus.go
engine是一个算法无关的用作共识层面的引擎
相关函数
Process
位于core\state_processor.go
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {var (receipts types.ReceiptsusedGas = new(uint64)header = block.Header()blockHash = block.Hash()blockNumber = block.Number()allLogs []*types.Loggp = new(GasPool).AddGas(block.GasLimit()))// Mutate the block and state according to any hard-fork specsif p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {misc.ApplyDAOHardFork(statedb)}blockContext := NewEVMBlockContext(header, p.bc, nil)vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)// Iterate over and process the individual transactionsfor i, tx := range block.Transactions() {msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee)if err != nil {return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)}statedb.Prepare(tx.Hash(), i)receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv)if err != nil {return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)}receipts = append(receipts, receipt)allLogs = append(allLogs, receipt.Logs...)}// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles())return receipts, allLogs, *usedGas, nil
}
首先该函数判断当前区块是否为DAO硬分叉的区块,若是则调用ApplyDAOHardFork
函数(待补充)。
然后调用NewEVMBlockContext
函数为当前区块创立一个运行上下文。
随后调用NewEVM
函数建立一个以太坊虚拟机,准备编译执行程序。
然后枚举区块中的交易并把它转换为消息格式,随后调用Prepare
函数设置当前状态的交易hash和序号,随后调用applyTransaction
在虚拟机中执行交易相关指令(见以太坊go-ethereum源码研读(二)进一步分析),得到返回的收据后,加入到列表中,并获取其中的日志加入到列表中,最后调用共识引擎的Finalize
函数计算区块奖励并加入到最终状态中。
NewEVMBlockContext
位于core\evm.go
根据区块头信息,建立并返回一个BlockContext区块上下文信息结构体
func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address) vm.BlockContext {var (beneficiary common.AddressbaseFee *big.Int)// If we don't have an explicit author (i.e. not mining), extract from the headerif author == nil {beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation} else {beneficiary = *author}if header.BaseFee != nil {baseFee = new(big.Int).Set(header.BaseFee)}return vm.BlockContext{CanTransfer: CanTransfer,Transfer: Transfer,GetHash: GetHashFn(header, chain),Coinbase: beneficiary,BlockNumber: new(big.Int).Set(header.Number),Time: new(big.Int).SetUint64(header.Time),Difficulty: new(big.Int).Set(header.Difficulty),BaseFee: baseFee,GasLimit: header.GasLimit,}
}
NewEVM
位于core/vm/evm.go
根据之前的上下文信息,以及其他的配置和内容建立虚拟机,同时调用NewEVMInterpreter
函数建立对应解释器。
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
// only ever be used *once*.
func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM {evm := &EVM{Context: blockCtx,TxContext: txCtx,StateDB: statedb,Config: config,chainConfig: chainConfig,chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil),}evm.interpreter = NewEVMInterpreter(evm, config)return evm
}
NewEVMInterpreter
根据采取的不同链规则不同来建立对应EVM解释器
// NewEVMInterpreter returns a new instance of the Interpreter.
func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {// If jump table was not initialised we set the default one.if cfg.JumpTable == nil {switch {case evm.chainRules.IsLondon:cfg.JumpTable = &londonInstructionSetcase evm.chainRules.IsBerlin:cfg.JumpTable = &berlinInstructionSetcase evm.chainRules.IsIstanbul:cfg.JumpTable = &istanbulInstructionSetcase evm.chainRules.IsConstantinople:cfg.JumpTable = &constantinopleInstructionSetcase evm.chainRules.IsByzantium:cfg.JumpTable = &byzantiumInstructionSetcase evm.chainRules.IsEIP158:cfg.JumpTable = &spuriousDragonInstructionSetcase evm.chainRules.IsEIP150:cfg.JumpTable = &tangerineWhistleInstructionSetcase evm.chainRules.IsHomestead:cfg.JumpTable = &homesteadInstructionSetdefault:cfg.JumpTable = &frontierInstructionSet}for i, eip := range cfg.ExtraEips {copy := *cfg.JumpTableif err := EnableEIP(eip, ©); err != nil {// Disable it, so caller can check if it's activated or notcfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...)log.Error("EIP activation failed", "eip", eip, "error", err)}cfg.JumpTable = ©}}return &EVMInterpreter{evm: evm,cfg: cfg,}
}
AsMessage
位于core\types\transaction.go
将交易返回为消息格式
// AsMessage returns the transaction as a core.Message.
func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) {msg := Message{nonce: tx.Nonce(),gasLimit: tx.Gas(),gasPrice: new(big.Int).Set(tx.GasPrice()),gasFeeCap: new(big.Int).Set(tx.GasFeeCap()),gasTipCap: new(big.Int).Set(tx.GasTipCap()),to: tx.To(),amount: tx.Value(),data: tx.Data(),accessList: tx.AccessList(),isFake: false,}// If baseFee provided, set gasPrice to effectiveGasPrice.if baseFee != nil {msg.gasPrice = math.BigMin(msg.gasPrice.Add(msg.gasTipCap, baseFee), msg.gasFeeCap)}var err errormsg.from, err = Sender(s, tx)return msg, err
}
Prepare
位于core\state\statedb.go
在EVM需要生成新的状态时调用此函数来设置当前交易的hash和序号
// Prepare sets the current transaction hash and index which are
// used when the EVM emits new state logs.
func (s *StateDB) Prepare(thash common.Hash, ti int) {s.thash = thashs.txIndex = tis.accessList = newAccessList()
}
Finalize
// Finalize implements consensus.Engine, accumulating the block and uncle rewards,
// setting the final state on the header
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {// Accumulate any block and uncle rewards and commit the final state rootaccumulateRewards(chain.Config(), state, header, uncles)header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
}
位于consensus\ethash\consensus.go
该函数累积区块和叔块的奖励并设置在头部的最终状态中。
这篇关于以太坊go-ethereum源码研读(一)从Process函数相关自定义类型和结构体开始的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!