博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
quarkchain和以太坊go版本交易执行流程简单分析
阅读量:3727 次
发布时间:2019-05-22

本文共 19835 字,大约阅读时间需要 66 分钟。

以一笔交易流程来追踪

原文地址

0.core/state_processor.go执行交易 tx *types.Transaction

以太坊 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}

1.core/types/transaction.go

以太坊中是从函数提取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}

2.core/evm.go

构造新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(), }}

3.core/vm/evm.go

以太坊

返回一个新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}

4.core/state_transition.go

以太坊

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相同

5. core/state_transition.go状态转换

状态转换对象的构建 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}

微信截图_20200227211954.png

6.core/vm/evm.go

以太坊 非合约创建类交易函数

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/

你可能感兴趣的文章
软件工程homework----九宫格记忆网
查看>>
MyBatis笔记_第一部分(狂神老师的笔记)
查看>>
IDEA2020连接mysql8.0时时区问题的解决方法
查看>>
2021-06-09安卓项目图片
查看>>
2021-05-31
查看>>
C语言小游戏——井字棋
查看>>
软件测试基础
查看>>
测试模型
查看>>
杯子测试点
查看>>
测试点
查看>>
测试面试题
查看>>
jmeter链接数据库
查看>>
/opt/Qt5.12.5/Tools/QtCreator/lib/Qt/plugins/platforms/../../lib/libQt5XcbQpa.so.5?
查看>>
Flutter学习第六天:Flutter实现打开第三方应用、一行代码实现夜间模式、字体改变、跟随手指移动,加载网络图片等功能。
查看>>
Java+selenium自动化测试无法登陆http://localhost/phpwind/index.php网站的问题?
查看>>
Flutter学习第十三天,2021最新版超详细Flutter2.0实现百度语音转文字功能,Android和Flutter混合开发?
查看>>
Flutter学习第十五天:2021年最新版超详细Flutter实现Mob+SMSSDK手机验证码登录实现,Android和Flutter混合开发?
查看>>
flutter百度语音唤醒功能出现 {“error“:11,“desc“:“Wakeup engine has no license“,“sub_error“:11002}怎么办?
查看>>
数据结构第二章--栈和队列的考点(输出序列、前缀和后缀、中缀之间转换以及求值,循环队列问题,双端队列),以及实现功能代码?
查看>>
带你实现HarmonyOS的DevEco Studio编译器的安装和简单使用教程,实现创建并运行hello world?
查看>>