本文共 19835 字,大约阅读时间需要 66 分钟。
以一笔交易流程来追踪
原文地址以太坊 tx *types.Transaction
// ApplyTransaction attempts to apply a transaction to the given state database// and uses the input parameters for its environment. It returns the receipt// for the transaction, gas used and an error if the transaction failed,// indicating the block was invalid.func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) { msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) if err != nil { return nil, err } // Create a new context to be used in the EVM environment context := NewEVMContext(msg, header, bc, author) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. vmenv := vm.NewEVM(context, statedb, config, cfg) // Apply the transaction to the current state (included in the env) _, gas, failed, err := ApplyMessage(vmenv, msg, gp) if err != nil { return nil, err } // Update the state with pending changes var root []byte if config.IsByzantium(header.Number) { statedb.Finalise(true) } else { root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() } *usedGas += gas // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx // based on the eip phase, we're passing whether the root touch-delete accounts. receipt := types.NewReceipt(root, failed, *usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = gas // if the transaction created a contract, store the creation address in the receipt. if msg.To() == nil { receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce()) } // Set the receipt logs and create a bloom for filtering receipt.Logs = statedb.GetLogs(tx.Hash()) receipt.Bloom = types.CreateBloom(types.Receipts{ receipt}) receipt.BlockHash = statedb.BlockHash() receipt.BlockNumber = header.Number receipt.TransactionIndex = uint(statedb.TxIndex()) return receipt, err}
quarkchain tx *types.Transaction
// ApplyTransaction apply txfunc ApplyTransaction(config *params.ChainConfig, bc ChainContext, gp *GasPool, statedb *state.StateDB, header types.IHeader, tx *types.Transaction, usedGas *uint64, cfg vm.Config) ([]byte, *types.Receipt, uint64, error) { statedb.SetFullShardKey(tx.EvmTx.ToFullShardKey()) msg, err := tx.EvmTx.AsMessage(types.MakeSigner(tx.EvmTx.NetworkId()), tx.Hash()) if err != nil { return nil, nil, 0, err } context := NewEVMContext(msg, header, bc) vmenv := vm.NewEVM(context, statedb, config, cfg) ret, gas, failed, err := ApplyMessage(vmenv, msg, gp) if err != nil { return nil, nil, 0, err } var root []byte *usedGas += gas // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx // based on the eip phase, we're passing whether the root touch-delete accounts. receipt := types.NewReceipt(root, failed, statedb.GetGasUsed().Uint64()) receipt.TxHash = tx.Hash() receipt.GasUsed = gas // if the transaction created a contract, store the creation address in the receipt. if msg.To() == nil && !msg.IsCrossShard() && !failed { receipt.ContractAddress = account.Recipient(vm.CreateAddress(vmenv.Context.Origin, msg.ToFullShardKey(), tx.EvmTx.Nonce())) } receipt.ContractFullShardKey = tx.EvmTx.ToFullShardKey() // Set the receipt logs and create a bloom for filtering receipt.Logs = statedb.GetLogs(tx.Hash()) receipt.Bloom = types.CreateBloom(types.Receipts{ receipt}) return ret, receipt, gas, err}
以太坊中是从函数提取tx中字段,使用singer获取交易源地址,直接构造返回message对象
// AsMessage returns the transaction as a core.Message.//// AsMessage requires a signer to derive the sender.//// XXX Rename message to something less arbitrary?func (tx *Transaction) AsMessage(s Signer) (Message, error) { msg := Message{ nonce: tx.data.AccountNonce, gasLimit: tx.data.GasLimit, gasPrice: new(big.Int).Set(tx.data.Price), to: tx.data.Recipient, amount: tx.data.Amount, data: tx.data.Payload, checkNonce: true, } var err error msg.from, err = Sender(s, tx) return msg, err}
quarkchain 传入值多了txHash common.Hash toFullShardKey增加
// AsMessage returns the transaction as a core.Message.// AsMessage requires a signer to derive the sender.// XXX Rename message to something less arbitrary?func (tx *EvmTransaction) AsMessage(s Signer, txHash common.Hash) (Message, error) { msgTo := new(common.Address) if tx.data.Recipient != nil { msgTo.SetBytes(tx.data.Recipient.Bytes()) } else { msgTo = nil } toFullShardKey := tx.data.ToFullShardKey.GetValue() msg := Message{ nonce: tx.data.AccountNonce, gasLimit: tx.data.GasLimit, gasPrice: new(big.Int).Set(tx.data.Price), to: msgTo, amount: tx.data.Amount, data: tx.data.Payload, checkNonce: true, fromFullShardKey: tx.data.FromFullShardKey.GetValue(), toFullShardKey: &toFullShardKey, txHash: txHash, isCrossShard: tx.IsCrossShard(), transferTokenID: tx.data.TransferTokenID, gasTokenID: tx.data.GasTokenID, } msgFrom, err := Sender(s, tx) msg.from = msgFrom return msg, err}
函数实际存储数据使用message struct类型
以太坊
// Message is a fully derived transaction and implements core.Message//// NOTE: In a future PR this will be removed.type Message struct { to *common.Address from common.Address nonce uint64 amount *big.Int gasLimit uint64 gasPrice *big.Int data []byte checkNonce bool}
quarkchain增加了字段 涉及分片
// Message is a fully derived transaction and implements core.Message//// NOTE: In a future PR this will be removed.type Message struct { to *common.Address from common.Address nonce uint64 amount *big.Int gasLimit uint64 gasPrice *big.Int data []byte checkNonce bool fromFullShardKey uint32 toFullShardKey *uint32 txHash common.Hash isCrossShard bool transferTokenID uint64 gasTokenID uint64}
构造新evm上下文; vm.context仅使用了msg.From()字段,返回交易源地址
以太坊
// NewEVMContext creates a new context for use in the EVM.func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author *common.Address) vm.Context { // If we don't have an explicit author (i.e. not mining), extract from the header var beneficiary common.Address if author == nil { beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation } else { beneficiary = *author } return vm.Context{ CanTransfer: CanTransfer, Transfer: Transfer, GetHash: GetHashFn(header, chain), Origin: msg.From(), Coinbase: beneficiary, BlockNumber: new(big.Int).Set(header.Number), Time: new(big.Int).SetUint64(header.Time), Difficulty: new(big.Int).Set(header.Difficulty), GasLimit: header.GasLimit, GasPrice: new(big.Int).Set(msg.GasPrice()), }}
quarkchain
func NewEVMContext(msg types.Message, mheader types.IHeader, chain ChainContext) vm.Context { header := mheader.(*types.MinorBlockHeader) return vm.Context{ CanTransfer: CanTransfer, Transfer: Transfer, GetHash: GetHashFn(header, chain), Origin: msg.From(), Coinbase: header.GetCoinbase().Recipient, BlockNumber: new(big.Int).SetUint64(header.NumberU64()), Time: new(big.Int).SetUint64(header.GetTime()), Difficulty: new(big.Int).Set(header.GetDifficulty()), GasLimit: header.GasLimit.Value.Uint64(), GasPrice: new(big.Int).Set(msg.GasPrice()), ToFullShardKey: msg.ToFullShardKey(), GasTokenID: msg.GasTokenID(), TransferTokenID: msg.TransferTokenID(), }}
以太坊
返回一个新EVM,非线程安全,仅能使用一次,返回值为ctx
// NewEVM returns a new EVM. The returned EVM is not thread safe and should// only ever be used *once*.func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { evm := &EVM{ Context: ctx, StateDB: statedb, vmConfig: vmConfig, chainConfig: chainConfig, chainRules: chainConfig.Rules(ctx.BlockNumber), interpreters: make([]Interpreter, 0, 1), } if chainConfig.IsEWASM(ctx.BlockNumber) { // to be implemented by EVM-C and Wagon PRs. // if vmConfig.EWASMInterpreter != "" { // extIntOpts := strings.Split(vmConfig.EWASMInterpreter, ":") // path := extIntOpts[0] // options := []string{} // if len(extIntOpts) > 1 { // options = extIntOpts[1..] // } // evm.interpreters = append(evm.interpreters, NewEVMVCInterpreter(evm, vmConfig, options)) // } else { // evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig)) // } panic("No supported ewasm interpreter yet.") } // vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here // as we always want to have the built-in EVM as the failover option. evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig)) evm.interpreter = evm.interpreters[0] return evm}
quarkchain
// NewEVM returns a new EVM. The returned EVM is not thread safe and should// only ever be used *once*.func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { evm := &EVM{ Context: ctx, StateDB: statedb, vmConfig: vmConfig, chainConfig: chainConfig, chainRules: chainConfig.Rules(ctx.BlockNumber), interpreters: make([]Interpreter, 0, 1), } if chainConfig.IsEWASM(ctx.BlockNumber) { // to be implemented by EVM-C and Wagon PRs. // if vmConfig.EWASMInterpreter != "" { // extIntOpts := strings.Split(vmConfig.EWASMInterpreter, ":") // path := extIntOpts[0] // options := []string{} // if len(extIntOpts) > 1 { // options = extIntOpts[1..] // } // evm.interpreters = append(evm.interpreters, NewEVMVCInterpreter(evm, vmConfig, options)) // } else { // evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig)) // } panic("No supported ewasm interpreter yet.") } // vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here // as we always want to have the built-in EVM as the failover option. evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig)) evm.interpreter = evm.interpreters[0] return evm}
以太坊
msg信息应用到evm状态
返回evm执行所需gas
出错则该交易永不被打包到区块标记error
// ApplyMessage computes the new state by applying the given message// against the old state within the environment.//// ApplyMessage returns the bytes returned by any EVM execution (if it took place),// the gas used (which includes gas refunds) and an error if it failed. An error always// indicates a core error meaning that the message would always fail for that particular// state and would never be accepted within a block.func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool, error) { return NewStateTransition(evm, msg, gp).TransitionDb()}
quarkchain相同
状态转换对象的构建 StateTransition
之后调用 transitionDb()方法
// NewStateTransition initialises and returns a new state transition object.func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition { return &StateTransition{ gp: gp, evm: evm, msg: msg, gasPrice: msg.GasPrice(), value: msg.Value(), data: msg.Data(), state: evm.StateDB, }}
结构体变量 相同
/*The State Transitioning ModelA state transition is a change made when a transaction is applied to the current world stateThe state transitioning model does all the necessary work to work out a valid new state root.1) Nonce handling2) Pre pay gas3) Create a new state object if the recipient is \0*324) Value transfer== If contract creation == 4a) Attempt to run transaction data 4b) If valid, use result as code for the new state object== end ==5) Run Script section6) Derive new state root*/type StateTransition struct { gp *GasPool msg Message gas uint64 gasPrice *big.Int initialGas uint64 value *big.Int data []byte state vm.StateDB evm *vm.EVM}
// TransitionDb will transition the state by applying the current message and// returning the result including the used gas. It returns an error if failed.// An error indicates a consensus issue.func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) { var ( // vm errors do not effect consensus and are therefor // not assigned to err, except for insufficient balance // error. vmerr error gas uint64 msg = st.msg evm = st.evm contractCreation = msg.To() == nil ) if evm.IsApplyXShard { st.preFill() gas = evm.XShardGasUsedStart } else { // Pay intrinsic gas if err = st.preCheck(); err != nil { return } gas, err = IntrinsicGas(st.data, contractCreation, msg.IsCrossShard()) if err != nil { return nil, 0, false, err } } if err = st.useGas(gas); err != nil { return nil, 0, false, err } sender := vm.AccountRef(msg.From()) if msg.IsCrossShard() { st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1) return st.AddCrossShardTxDeposit(gas) } if contractCreation || evm.ContractAddress != nil { ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value, evm.ContractAddress) } else { // Increment the nonce for the next transaction if !st.evm.IsApplyXShard { st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1) } if st.transferFailureByPoSWBalanceCheck() { ret, st.gas, vmerr = nil, 0, vm.ErrPoSWSenderNotAllowed } else { ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value) } } if vmerr != nil { log.Debug("VM returned with error", "err", vmerr) // The only possible consensus-error would be if there wasn't // sufficient balance to make the transfer happen. The first // balance transfer may never fail. if vmerr == vm.ErrInsufficientBalance { return nil, 0, false, vmerr } } st.refundGas(vmerr) st.chargeFee(st.gasUsed()) if vmerr == vm.ErrPoSWSenderNotAllowed { return nil, st.gasUsed(), true, nil } return ret, st.gasUsed(), vmerr != nil, err}
以太坊 非合约创建类交易函数
caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int
源地址 目的地址 payload字段 剩余gas 转账金额
// Call executes the contract associated with the addr with the given input as// parameters. It also handles any necessary value transfer required and takes// the necessary steps to create accounts and reverses the state in case of an// execution error or failed value transfer.func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { if evm.vmConfig.NoRecursion && evm.depth > 0 { return nil, gas, nil } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } // Fail if we're trying to transfer more than the available balance if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, gas, ErrInsufficientBalance } var ( to = AccountRef(addr) snapshot = evm.StateDB.Snapshot() ) if !evm.StateDB.Exist(addr) { precompiles := PrecompiledContractsHomestead if evm.chainRules.IsByzantium { precompiles = PrecompiledContractsByzantium } if evm.chainRules.IsIstanbul { precompiles = PrecompiledContractsIstanbul } if precompiles[addr] == nil && evm.chainRules.IsEIP158 && value.Sign() == 0 { // Calling a non existing account, don't do anything, but ping the tracer if evm.vmConfig.Debug && evm.depth == 0 { evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil) } return nil, gas, nil } evm.StateDB.CreateAccount(addr) } evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. contract := NewContract(caller, to, value, gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) // Even if the account has no code, we need to continue because it might be a precompile start := time.Now() // Capture the tracer start/end events in debug mode if evm.vmConfig.Debug && evm.depth == 0 { evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) defer func() { // Lazy evaluation of the parameters evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) }() } ret, err = run(evm, contract, input, false) // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != errExecutionReverted { contract.UseGas(contract.Gas) } } return ret, contract.Gas, err}
core/evm.go
// CanTransfer checks whether there are enough funds in the address' account to make a transfer.// This does not take the necessary gas in to account to make the transfer valid.func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { return db.GetBalance(addr).Cmp(amount) >= 0}
core/vm/interface.go
该接口有两个实现 判断输入地址是否存在
package state statedb.go(后续分析)
package vm noop.go
// StateDB is an EVM database for full state querying.type StateDB interface { CreateAccount(common.Address) SubBalance(common.Address, *big.Int) AddBalance(common.Address, *big.Int) GetBalance(common.Address) *big.Int GetNonce(common.Address) uint64 SetNonce(common.Address, uint64) GetCodeHash(common.Address) common.Hash GetCode(common.Address) []byte SetCode(common.Address, []byte) GetCodeSize(common.Address) int AddRefund(uint64) SubRefund(uint64) GetRefund() uint64 GetCommittedState(common.Address, common.Hash) common.Hash GetState(common.Address, common.Hash) common.Hash SetState(common.Address, common.Hash, common.Hash) Suicide(common.Address) bool HasSuicided(common.Address) bool // Exist reports whether the given account exists in state. // Notably this should also return true for suicided accounts. Exist(common.Address) bool // Empty returns whether the given account is empty. Empty // is defined according to EIP161 (balance = nonce = code = 0). Empty(common.Address) bool RevertToSnapshot(int) Snapshot() int AddLog(*types.Log) AddPreimage(common.Hash, []byte) ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error}
core/state/statedb.go
// CreateAccount explicitly creates a state object. If a state object with the address// already exists the balance is carried over to the new account.//// CreateAccount is called during the EVM CREATE operation. The situation might arise that// a contract does the following://// 1. sends funds to sha(account ++ (nonce + 1))// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)//// Carrying over the balance ensures that Ether doesn't disappear.func (s *StateDB) CreateAccount(addr common.Address) { newObj, prev := s.createObject(addr) if prev != nil { newObj.setBalance(prev.data.Balance) }}
EVM的stateDB中每个addr与有关stateObject
core/evm.go
// Transfer subtracts amount from sender and adds amount to recipient using the given Dbfunc Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { db.SubBalance(sender, amount) db.AddBalance(recipient, amount)}
之后call函数剩余代码为合约创建与执行
处理输入input字段由EVM执行合约
转载地址:http://mrqnn.baihongyu.com/