【以太坊代码分析系列001-获取合约pure,view数据的流程】

  1. 简单说明

本文基于geth 1.8.19的代码

    1. 测试合约代码

pragma solidity ^0.4.20;

contract Counter {
   
uint count;
   
   
function Counter() public
   
{
        count =
0;
    }
   
   
function increment() public {
       count = count +
1;
    }

   
function getCount() constant returns (uint) {
      
return count;
    }

}

  1. 代码
    1. 调用getCount的rpc代码入口
      1. server.go

rpc/server.go

func (s *Server) serveRequest(ctx context.Context, codec ServerCodec, singleShot bool, options CodecOption) error {
      
var pend sync.WaitGroup

      
defer func() {
             
if err := recover(); err != nil {
                    
const size = 64 << 10
                    
buf := make([]byte, size)
                     buf = buf[:runtime.Stack(buf, false)]
                     log.Error(string(buf))
              }
              s.codecsMu.Lock()
              s.codecs.Remove(codec)
              s.codecsMu.Unlock()
       }()

      
//     ctx, cancel := context.WithCancel(context.Background())
      
ctx, cancel := context.WithCancel(ctx)
      
defer cancel()

      
// if the codec supports notification include a notifier that callbacks can use
       // to send notification to clients. It is tied to the codec/connection. If the
       // connection is closed the notifier will stop and cancels all active subscriptions.
      
if options&OptionSubscriptions == OptionSubscriptions {
              ctx = context.WithValue(ctx, notifierKey{}, newNotifier(codec))
       }
       s.codecsMu.Lock()
      
if atomic.LoadInt32(&s.run) != 1 { // server stopped
             
s.codecsMu.Unlock()
             
return &shutdownError{}
       }
       s.codecs.Add(codec)
       s.codecsMu.Unlock()

      
// test if the server is ordered to stop
      
for atomic.LoadInt32(&s.run) == 1 {
              reqs, batch, err := s.readRequest(codec)
             
if err != nil {
                    
// If a parsing error occurred, send an error
                    
if err.Error() != "EOF" {
                            log.Debug(fmt.Sprintf(
"read error %v\n", err))
                            codec.Write(codec.CreateErrorResponse(nil, err))
                     }
                    
// Error or end of stream, wait for requests and tear down
                    
pend.Wait()
                    
return nil
              }

             
// check if server is ordered to shutdown and return an error
              // telling the client that his request failed.
             
if atomic.LoadInt32(&s.run) != 1 {
                     err = &shutdownError{}
                    
if batch {
                            resps := make([]
interface{}, len(reqs))
                           
for i, r := range reqs {
                                   resps[i] = codec.CreateErrorResponse(&r.id, err)
                            }
                            codec.Write(resps)
                     }
else {
                            codec.Write(codec.CreateErrorResponse(&reqs[
0].id, err))
                     }
                    
return nil
              }
             
// If a single shot request is executing, run and return immediately
              
if singleShot {
                    
if batch {
                            s.execBatch(ctx, codec, reqs)
                     }
else {
                            s.exec(ctx, codec, reqs[
0])
                     }
                    
return nil
              }
             
// For multi-shot connections, start a goroutine to serve and loop back
             
pend.Add(1)
 

// 通过rpc过来的调用view,pure函数的入口走到这里
              go func(reqs []*serverRequest, batch bool) {
                    
defer pend.Done()
                    
if batch {
                            s.execBatch(ctx, codec, reqs)
                     }
else {
                            s.exec(ctx, codec, reqs[
0])
                     }
              }(reqs, batch)
       }
      
return nil
}

 

      1. 执行exec

// exec executes the given request and writes the result back using the codec.
func (s *Server) exec(ctx context.Context, codec ServerCodec, req *serverRequest) {
      
var response interface{}
      
var callback func()
      
if req.err != nil {
              response = codec.CreateErrorResponse(&req.id, req.err)
       }
else {
              response, callback = s.handle(ctx, codec, req)
       }

      
if err := codec.Write(response); err != nil {
              log.Error(fmt.Sprintf(
"%v\n", err))
              codec.Close()
       }

      
// when request was a subscribe request this allows these subscriptions to be actived
      
// callback是支持消息订阅模式的回调(类似mq)

if callback != nil {
              callback()
       }
}

 

      1. 执行handle

// handle executes a request and returns the response from the callback.
func (s *Server) handle(ctx context.Context, codec ServerCodec, req *serverRequest) (interface{}, func()) {
      
if req.err != nil {
             
return codec.CreateErrorResponse(&req.id, req.err), nil
       }

      
if req.isUnsubscribe { // cancel subscription, first param must be the subscription id
             
if len(req.args) >= 1 && req.args[0].Kind() == reflect.String {
                     notifier, supported := NotifierFromContext(ctx)
                    
if !supported { // interface doesn't support subscriptions (e.g. http)
                           
return codec.CreateErrorResponse(&req.id, &callbackError{ErrNotificationsUnsupported.Error()}), nil
                     }

                     subid := ID(req.args[
0].String())
                    
if err := notifier.unsubscribe(subid); err != nil {
                            
return codec.CreateErrorResponse(&req.id, &callbackError{err.Error()}), nil
                     }

                    
return codec.CreateResponse(req.id, true), nil
              }
             
return codec.CreateErrorResponse(&req.id, &invalidParamsError{"Expected subscription id as first argument"}), nil
       }

      
if req.callb.isSubscribe {
              subid, err := s.createSubscription(ctx, codec, req)
             
if err != nil {
                    
return codec.CreateErrorResponse(&req.id, &callbackError{err.Error()}), nil
              }

             
// active the subscription after the sub id was successfully sent to the client
             
activateSub := func() {
                     notifier, _ := NotifierFromContext(ctx)
                     notifier.activate(subid, req.svcname)
              }

             
return codec.CreateResponse(req.id, subid), activateSub
       }

      
// regular RPC call, prepare arguments
      
if len(req.args) != len(req.callb.argTypes) {
              rpcErr := &invalidParamsError{fmt.Sprintf(
"%s%s%s expects %d parameters, got %d",
                     req.svcname,
serviceMethodSeparator, req.callb.method.Name,
                     len(req.callb.argTypes), len(req.args))}
              
return codec.CreateErrorResponse(&req.id, rpcErr), nil
       }

       arguments := []reflect.Value{req.callb.rcvr}
      
if req.callb.hasCtx {
              arguments = append(arguments, reflect.ValueOf(ctx))
       }
      
if len(req.args) > 0 {
              arguments = append(arguments, req.args...)
       }

      
// execute RPC method and return result

// 通过反射调用publicBlock的api
       reply := req.callb.method.Func.Call(arguments)
      
if len(reply) == 0 {
             
return codec.CreateResponse(req.id, nil), nil
       }
      
if req.callb.errPos >= 0 { // test if method returned an error
              
if !reply[req.callb.errPos].IsNil() {
                     e := reply[req.callb.errPos].Interface().(error)
                     res := codec.CreateErrorResponse(&req.id, &callbackError{e.Error()})
                    
return res, nil
              }
       }
      
return codec.CreateResponse(req.id, reply[0].Interface()), nil
}

    1. internal/ethapi/api.go
      1. 执行Call

// Call executes the given transaction on the state for the given block number.
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
//
它不会在状态/区块链中进行更改,并且对执行和检索值很有用。
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
       result, _, _, err := s.doCall(ctx, args, blockNr, vm.Config{},
5*time.Second)
      
return (hexutil.Bytes)(result), err
}

其中

PublicBlockChainAPI的定义如下:

// PublicBlockChainAPI provides an API to access the Ethereum blockchain.
// It offers only methods that operate on public data that is freely available to anyone.

type PublicBlockChainAPI struct {
       b Backend
}

Backend位于ethapi/backend.go中

type Backend interface {
      
// General Ethereum API
      
Downloader() *downloader.Downloader
       ProtocolVersion() int
       SuggestPrice(ctx context.Context) (*big.Int, error)
       ChainDb() ethdb.Database
       EventMux() *event.TypeMux
       AccountManager() *accounts.Manager

      
// BlockChain API
      
SetHead(number uint64)
       HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
       BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error)
       StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error)
       GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
       GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
       GetTd(blockHash common.Hash) *big.Int
       GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmCfg vm.Config) (*vm.EVM,
func() error, error)
       SubscribeChainEvent(ch
chan<- core.ChainEvent) event.Subscription
       SubscribeChainHeadEvent(ch
chan<- core.ChainHeadEvent) event.Subscription
       SubscribeChainSideEvent(ch
chan<- core.ChainSideEvent) event.Subscription

      
// TxPool API
      
SendTx(ctx context.Context, signedTx *types.Transaction) error
       GetPoolTransactions() (types.Transactions, error)
       GetPoolTransaction(txHash common.Hash) *types.Transaction
       GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
       Stats() (pending int, queued int)
       TxPoolContent() (
map[common.Address]types.Transactions, map[common.Address]types.Transactions)
       SubscribeNewTxsEvent(
chan<- core.NewTxsEvent) event.Subscription

       ChainConfig() *params.ChainConfig
       CurrentBlock() *types.Block
}

      1. 执行doCall

func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration) ([]byte, uint64, bool, error) {
      
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())

 
/**
{
    "a87d942c": "getCount()",
    "d09de08a": "increment()"
}

DEBUG[01-07|06:29:01.331|internal/ethapi/api.go:693]             LYH doCall ctx
 runtime="context.Background.WithCancel.WithValue(rpc.notifierKey{}, &rpc.Notifier{codec:(*rpc.jsonCodec)(0xc430d3d630), subMu:sync.Mutex{state:0, sema:0x0}, active:map[rpc.ID]*rpc.Subscription{}, inactive:map[rpc.ID]*rpc.Subscription{}, buffer:map[rpc.ID][]interface {}{}})"
D
EBUG[01-07|06:29:01.331|internal/ethapi/api.go:694]             LYH doCall args
runtime="{From:[219 249 138 34 210 146 213 154 49 29 0 158 177 217 78 183 80 6 50 81]
To:[105 51 38 93 188 121 15 206 224 13 36 32 184 210 177 202 84 25 52 155] Gas:0x0 GasPrice:{neg:false abs:[]} Value:{neg:false abs:[]}
Data:0xa87d942c}"   # Data
里面的数据就是getCount 转为二进制的数值, 这个可以参考 ethapi, https://github.com/paritytech/ethabi

DEBUG[01-07|06:29:01.331|internal/ethapi/api.go:695]             LYH doCall blockNr                       runtime=-1
        */
      
log.Debug("LYH doCall ctx " , "runtime", ctx)
       log.Debug(
"LYH doCall args " , "runtime", args)
       log.Debug(
"LYH doCall blockNr " , "runtime", blockNr)

       state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
      
if state == nil || err != nil {
             
return nil, 0, false, err
       }
      
// Set sender address or use a default if none specified
      
addr := args.From
      
if addr == (common.Address{}) {
             
if wallets := s.b.AccountManager().Wallets(); len(wallets) > 0 {
                    
if accounts := wallets[0].Accounts(); len(accounts) > 0 {
                            addr = accounts[
0].Address
                     }
              }
       }
      
// Set default gas & gas price if none were set
      
gas, gasPrice := uint64(args.Gas), args.GasPrice.ToInt()

// 设置初始gas 2~63 以及gasPrice 1~9

// 因为调用evm的指令时候,需要消耗gas

// 因为这个调用时免费的,所以这个gas最终并没什么用
       if gas == 0 {
              gas = math.
MaxUint64 / 2
      
}
      
if gasPrice.Sign() == 0 {
              gasPrice = new(big.Int).SetUint64(
defaultGasPrice)
       }

      
// Create new call message

// 初始化调用信息,包括 from,to, nonce,以及以太币份额,gasLimit,gasPrice,data
// from , to , nonce , amount ,gasLimit, gasPrice, data , checkNonce 

       msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false)

  

       // Setup context so it may be cancelled the call has completed

       // or, in case of unmetered gas, setup a context with a timeout.

       var cancel context.CancelFunc

       if timeout > 0 {

              ctx, cancel = context.WithTimeout(ctx, timeout)

       } else {

              ctx, cancel = context.WithCancel(ctx)

       }

       // Make sure the context is cancelled when the call has completed

       // this makes sure resources are cleaned up.

       defer cancel()

  

       // Get a new instance of the EVM. // 获取新的EVM

       evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg)

       if err != nil {

              return nil, 0, false, err

       }

       // Wait for the context to be done and cancel the evm. Even if the

       // EVM has finished, cancelling may be done (repeatedly)

       go func() {

              <-ctx.Done()

              evm.Cancel()

       }()

  

       // Setup the gas pool (also for unmetered requests)

       // and apply the message.
// 初始化gas池,也适用于未确定的请求

       gp := new(core.GasPool).AddGas(math.MaxUint64)
// 执行一条事务代码

       res, gas, failed, err := core.ApplyMessage(evm, msg, gp)

       if err := vmError(); err != nil {

              return nil, 0, false, err

       }

       return res, gas, failed, err

}

 

        1. GetEVM获取以太坊EVM环境

代码位于eth/api_backend.go中

func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
       state.SetBalance(msg.From(), math.MaxBig256)
       vmError :=
func() error { return nil }

       context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil)
      
return vm.NewEVM(context, state, b.eth.chainConfig, vmCfg), vmError, nil
}

        1. NewEVM(指定执行的指令解释器版本)

// 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) {
              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
}

        1. NewEVMInterpreter加载解释器

core/vm/interpreter.go,根据配置文件,加载不同的解释器

包括拜赞庭版本,家园版本等,就是不同版本支持不同的指令。

// NewEVMInterpreter returns a new instance of the Interpreter.
func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
      
// We use the STOP instruction whether to see
       // the jump table was initialised. If it was not
       // we'll set the default jump table.
      
if !cfg.JumpTable[STOP].valid {
              
switch {
             
case evm.ChainConfig().IsConstantinople(evm.BlockNumber):
                     cfg.JumpTable = constantinopleInstructionSet
             
case evm.ChainConfig().IsByzantium(evm.BlockNumber):
                     cfg.JumpTable = byzantiumInstructionSet
             
case evm.ChainConfig().IsHomestead(evm.BlockNumber):
                     cfg.JumpTable = homesteadInstructionSet
             
default:
                     cfg.JumpTable = frontierInstructionSet
              }
       }

      
return &EVMInterpreter{
              evm:      evm,
              cfg:      cfg,
              gasTable: evm.ChainConfig().GasTable(evm.BlockNumber),
       }
}

eg:

比如以下配置了constantinopleBlock,表示支持拜赞庭分支,该分支会多出几个指令,如STATICCALL,RETURNDATASIZE,RETURNDATACOPY具体支持的指令见:

core/vm/jump_table.go 中的 newByzantiumInstructionSet

{

         "nonce": "0xdeadbeefdeadbeef",

         "alloc": {},

         "timestamp": "0x0",

         "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

         "extraData": "",

         "gasLimit": "0xffffffffffffffff",

         "difficulty": "0x0000000000000000000000000000000000000000000000000000000000000001",

         "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",

         "config": {

                   "constantinopleBlock": 0,

                   "chainId": 8888888

         }

}

        1. core/types/transaction.go 中的message定义

type Message struct {
       to         *common.Address
       from       common.Address
       nonce      uint64
       amount     *big.Int
       gasLimit   uint64
       gasPrice   *big.Int
       data       []byte
       checkNonce bool
}

 

func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool) Message {
      
return Message{
              from:       from,
              to:         to,
              nonce:      nonce,
              amount:     amount,
              gasLimit:   gasLimit,
              gasPrice:   gasPrice,
              data:       data,
              checkNonce: checkNonce,
       }
}

    1. core/state_transition.go
      1. 执行ApplyMessage

执行evm指令

// 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()
}

StateTransition数据结构

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
}

      1. 执行TransitionDb

// 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) {
      
if err = st.preCheck(); err != nil {
             
return
      
}
       msg := st.msg
      
// 获取发起者地址
      
sender := vm.AccountRef(msg.From())
       homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber)

      
// 如果msg.To为空,表示为创建合约
      
contractCreation := msg.To() == nil

      
// Pay intrinsic gas
       //
计算调用合约的初始gas,包括创建合约,调用函数,数据长度所花费的gas
      
gas, err := IntrinsicGas(st.data, contractCreation, homestead)
      
if err != nil {
             
return nil, 0, false, err
       }

      
// 扣除已经花费的gas
      
if err = st.useGas(gas); err != nil {
             
return nil, 0, false, err
       }

      
var (
              evm = st.evm
             
// vm errors do not effect consensus and are therefor
              // not assigned to err, except for insufficient balance
              // error.
             
vmerr error
       )

      
// 如果是创建合约
      
if contractCreation {
              ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
       }
else {
             
//如果是调用函数
              // Increment the nonce for the next transaction
             
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)

// to 即为合约地址
              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
              }
       }

      
// 剩余gas返回(返回一半花费的gas给调用者)
      
st.refundGas()

      
// 将gas转给旷工
      
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))

      
return ret, st.gasUsed(), vmerr != nil, err
}

        1. 其中IntrinsicGas

// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error) {
      
// Set the starting gas for the raw transaction
      
var gas uint64
      
if contractCreation && homestead {
             
// 调用创建一次合约的gas 53000
             
gas = params.TxGasContractCreation
      
} else {
             
// 调用非view,pure合约的gas 21000
             
gas = params.TxGas
      
}
      
// Bump the required gas by the amount of transactional data
       //
计算数据长度所对应的gas
      
if len(data) > 0 {
             
// Zero and non-zero bytes are priced differently
             
var nz uint64
             
for _, byt := range data {
                    
if byt != 0 {
                            nz++
                     }
              }
             
// Make sure we don't exceed uint64 for all data combinations
             
if (math.MaxUint64-gas)/params.TxDataNonZeroGas < nz {
                    
return 0, vm.ErrOutOfGas
              }
              gas += nz * params.
TxDataNonZeroGas

             
z := uint64(len(data)) - nz
             
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
                    
return 0, vm.ErrOutOfGas
              }
              gas += z * params.
TxDataZeroGas
      
}
      
return gas, nil
}

        1. refundGas

将一半花费的gas退还给交易的发起者,不知道为什么这么做

func (st *StateTransition) refundGas() {
      
// Apply refund counter, capped to half of the used gas.
      
refund := st.gasUsed() / 2
      
if refund > st.state.GetRefund() {
              refund = st.state.GetRefund()
       }
       st.gas += refund

      
// Return ETH for remaining gas, exchanged at the original rate.
      
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
       st.state.AddBalance(st.msg.From(), remaining)

      
// Also return remaining gas to the block gas counter so it is
       // available for the next transaction.
      
st.gp.AddGas(st.gas)
}

    1. core/vm/evm.go
      1. 执行EVM.Call

// 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) {

      
// NoRecursion disabled Interpreter call, callcode, delegate call and create.
       //
非递归调用会禁用 call callcode delegate call create
      
if evm.vmConfig.NoRecursion && evm.depth > 0 {
             
return nil, gas, nil
       }

      
// Fail if we're trying to execute above the call depth limit
       //
最大调用栈 1024
      
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.ChainConfig().IsByzantium(evm.BlockNumber) {
                     precompiles = PrecompiledContractsByzantium
              }
             
if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && 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
}

        1. CanTransfer

// 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
}

        1. Transfer

// Transfer subtracts amount from sender and adds amount to recipient using the given Db

// todo 需要分析下,是先调用EVM再转账,还是先转账再调用EVM
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
       db.SubBalance(sender, amount)
       db.AddBalance(recipient, amount)
}

      1. 执行run

// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
      
// 获取二进制代码解释器
      
if contract.CodeAddr != nil {
              precompiles := PrecompiledContractsHomestead
             
if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
                     precompiles = PrecompiledContractsByzantium
              }
              
if p := precompiles[*contract.CodeAddr]; p != nil {
                    
return RunPrecompiledContract(p, input, contract)
              }
       }

      
// 执行
      
for _, interpreter := range evm.interpreters {
             
if interpreter.CanRun(contract.Code) {
                    
if evm.interpreter != interpreter {
                           
// Ensure that the interpreter pointer is set back
                            // to its current value upon return.
                           
defer func(i Interpreter) {
                                   evm.interpreter = i
                            }(evm.interpreter)
                            evm.interpreter = interpreter
                     }
                    
return interpreter.Run(contract, input, readOnly)
              }
       }
      
return nil, ErrNoCompatibleInterpreter
}

    1. core/vm/interpreter.go
      1. 执行二进制代码run

// Run loops and evaluates the contract's code with the given input data and returns
// the return byte-slice and an error if one occurred.
//
// It's important to note that any errors returned by the interpreter should be
// considered a revert-and-consume-all-gas operation except for
// errExecutionReverted which means revert-and-keep-gas-left.

// Run loops and evaluates the contract's code with the given input data and returns
// the return byte-slice and an error if one occurred.
//
用给定的入参循环执行合约的代码,并返回返回的字节片段,如果发生错误则返回错误。
// It's important to note that any errors returned by the interpreter should be
// considered a revert-and-consume-all-gas operation. No error specific checks
// should be handled to reduce complexity and errors further down the in.
// 重要的是要注意,解释器返回的任何错误都会消耗全部gas。 为了减少复杂性,没有特别的错误处理流程。

func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
      
if in.intPool == nil {
              in.intPool = poolOfIntPools.get()
             
defer func() {
                     poolOfIntPools.put(in.intPool)
                     in.intPool = nil
              }()
       }
       debug.PrintStack()

       // Increment the call depth which is restricted to 1024
       //
这个应该是嵌套调用深度,最大1024
      
in.evm.depth++
       
defer func() { in.evm.depth-- }()

      
// Make sure the readOnly is only set if we aren't in readOnly yet.
       // This makes also sure that the readOnly flag isn't removed for child calls.
      
if readOnly && !in.readOnly {
              in.readOnly = true
             
defer func() { in.readOnly = false }()
       }

      
// Reset the previous call's return data. It's unimportant to preserve the old buffer
       // as every returning call will return new data anyway.
      
in.returnData = nil

      
// Don't bother with the execution if there's no code.
      
if len(contract.Code) == 0 {
             
return nil, nil
       }

      
var (
              op    OpCode       
// current opcode
             
mem   = NewMemory() // bound memory
             
// 每个本地栈的大小为1024,每个元素256bit
              stack = newstack()  // local stack
              // For optimisation reason we're using uint64 as the program counter.
              // It's theoretically possible to go above 2^64. The YP defines the PC
              // to be uint256. Practically much less so feasible.
             
pc   = uint64(0) // program counter
             
cost uint64
             
// copies used by tracer
             
pcCopy  uint64 // needed for the deferred Tracer
              
gasCopy uint64 // for Tracer to log gas remaining before execution
             
logged  bool   // deferred Tracer should ignore already logged steps
      
)
       contract.Input = input

      
// Reclaim the stack as an int pool when the execution stops
      
defer func() { in.intPool.put(stack.data...) }()

      
if in.cfg.Debug {
             
defer func() {
                    
if err != nil {
                           
if !logged {
                                   in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
                            }
else {
                                   in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
                            }
                     }
              }()
       }
      
// The Interpreter main run loop (contextual). This loop runs until either an
       // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
       // the execution of one of the operations or until the done flag is set by the
       // parent context.
      
// 开始执行指令
       for atomic.LoadInt32(&in.evm.abort) == 0 {
             
if in.cfg.Debug {
                    
// Capture pre-execution values for tracing.
                    
logged, pcCopy, gasCopy = false, pc, contract.Gas
              }

             
// Get the operation from the jump table and validate the stack to ensure there are
              // enough stack items available to perform the operation.
             
// 每条指令定义于 core/vm/opcodes.go
              op = contract.GetOp(pc)

             
// 每条指令对应函数 core/vm/jump_table.go
              /** eg:
              ADD: {
                     execute:       opAdd,
                     gasCost:       constGasFunc(GasFastestStep),
                     validateStack: makeStackFunc(2, 1),
                     valid:         true,
              },
               */

              operation := in.cfg.JumpTable[op]

             
// 校验指令是否合法,比如说不同版本支持不同的指令
              // 其中指令集的初始化在vm/evm.go中 NewEVM
              // func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM
              // evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))

              if !operation.valid {
                    
return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
              }

             
// 校验栈是否能够满足当前操作
              if err := operation.validateStack(stack); err != nil {
                    
return nil, err
              }
             
// If the operation is valid, enforce and write restrictions

              //
在jump_table.go 中,定义 writes 有 CREATE2 SSTORE LOG0 等...
             
if err := in.enforceRestrictions(op, operation, stack); err != nil {
                    
return nil, err
              }

             
var memorySize uint64
             
// calculate the new memory size and expand the memory to fit
              // the operation
             
if operation.memorySize != nil {
                     memSize, overflow := bigUint64(operation.memorySize(stack))
                    
if overflow {
                            
return nil, errGasUintOverflow
                     }
                    
// memory is expanded in words of 32 bytes. Gas
                     // is also calculated in words.
                    
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
                           
return nil, errGasUintOverflow
                     }
              }
             
// consume the gas and return an error if not enough gas is available.
              // cost is explicitly set so that the capture state defer method can get the proper cost
             
// 计算每一个操作花费的gas
              cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize)

             
//  读的时候,由外部初始化gas=2^63,gasPrice=1^9,并扣除gas
             
if err != nil || !contract.UseGas(cost) {
                    
return nil, ErrOutOfGas
              }
             
if memorySize > 0 {
                     mem.Resize(memorySize)
              }

             
if in.cfg.Debug {
                     in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
                     logged = true
              }

             
// execute the operation
             
res, err := operation.execute(&pc, in, contract, mem, stack)
             
// verifyPool is a build flag. Pool verification makes sure the integrity
              // of the integer pool by comparing values to a default value.
             
if verifyPool {
                     verifyIntegerPool(in.intPool)
              }
             
// if the operation clears the return data (e.g. it has returning data)
              // set the last return to the result of the operation.
             
if operation.returns {
                     in.returnData = res
              }

             
switch {
             
case err != nil:
                    
return nil, err
             
case operation.reverts:
                    
return res, errExecutionReverted
             
case operation.halts:
                    
return res, nil
             
case !operation.jumps:
                     pc++
              }
       }
      
return nil, nil
}

  1. 测试代码调用栈信息
    1. getCount代码调用栈信息

DEBUG[01-06|20:21:53.262|eth/downloader/downloader.go:1690]      Recalculated downloader QoS values       rtt=20s confidence=1.000 ttl=1m0s
DEBUG[01-06|20:22:03.191|core/vm/interpreter.go:163]             LYH stack 001 readOnly                   err=false
goroutine 473 [running]:
runtime/debug.Stack(0xf8a880, 0x16, 0x4)
   /usr/local/go/src/runtime/debug/stack.go:24 +0xa7
runtime/debug.PrintStack()
   /usr/local/go/src/runtime/debug/stack.go:16 +0x22
github.com/ethereum/go-ethereum/core/vm.(*EVMInterpreter).Run(0xc43e1d8000, 0xc4200ca240, 0xc4315f41e8, 0x4, 0x4, 0xc43e1ea000, 0x0, 0x0, 0x0, 0x0, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/vm/interpreter.go:164 +0x109
github.com/ethereum/go-ethereum/core/vm.run(0xc420283500, 0xc4200ca240, 0xc4315f41e8, 0x4, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
  /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/vm/evm.go:64 +0x1f5
github.com/ethereum/go-ethereum/core/vm.(*EVM).Call(0xc420283500, 0x1104ae0, 0xc436519ac0, 0xce0f79bc5d263369, 0xcab1d2b820240de0, 0x9b341954, 0xc4315f41e8, 0x4, 0x4, 0x7ffffffffffface7, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/vm/evm.go:233 +0x525
github.com/ethereum/go-ethereum/core.(*StateTransition).TransitionDb(0xc4202b2a10, 0x1113580, 0xc431669560, 0xc4315f4238, 0xc4202b2a10, 0xc431669560, 0xc431669560, 0xf3d020)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state_transition.go:213 +0x3c4
github.com/ethereum/go-ethereum/core.ApplyMessage(0xc420283500, 0x1113580, 0xc431669560, 0xc4315f4238, 0xc420283500, 0xc4200a28f0, 0xc430d58000, 0x0, 0x0, 0x0, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state_transition.go:132 +0x5b
github.com/ethereum/go-ethereum/internal/ethapi.(*PublicBlockChainAPI).doCall(0xc42003f0c0, 0x110eca0, 0xc4316694a0, 0x9ad592d2228af9db, 0xb74ed9b19e001d31, 0x51320650, 0xc436519940, 0x0, 0x0, 0x0, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/internal/ethapi/api.go:741 +0x608
github.com/ethereum/go-ethereum/internal/ethapi.(*PublicBlockChainAPI).Call(0xc42003f0c0, 0x110ece0, 0xc4316f02a0, 0x9ad592d2228af9db, 0xb74ed9b19e001d31, 0x51320650, 0xc436519940, 0x0, 0x0, 0x0, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/internal/ethapi/api.go:751 +0xcc
reflect.Value.call(0xc420208d00, 0xc42000faa8, 0x13, 0xf77b1c, 0x4, 0xc431669440, 0x4, 0x4, 0x1e4, 0xc431669440, ...)
   /usr/local/go/src/reflect/value.go:447 +0x969
reflect.Value.Call(0xc420208d00, 0xc42000faa8, 0x13, 0xc431669440, 0x4, 0x4, 0x2, 0x2, 0xc430d4cfbc)
   /usr/local/go/src/reflect/value.go:308 +0xa4
github.com/ethereum/go-ethereum/rpc.(*Server).handle(0xc43160dfb0, 0x110ece0, 0xc4316f02a0, 0x11140c0, 0xc430ce5a90, 0xc4316693e0, 0xc430d4cf38, 0x43f3c9, 0xc400000008)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/rpc/server.go:309 +0x676
github.com/ethereum/go-ethereum/rpc.(*Server).exec(0xc43160dfb0, 0x110ece0, 0xc4316f02a0, 0x11140c0, 0xc430ce5a90, 0xc4316693e0)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/rpc/server.go:330 +0x1a6
github.com/ethereum/go-ethereum/rpc.(*Server).serveRequest.func2(0xc4201afd70, 0xc43160dfb0, 0xc4316ee1d0, 0x11140c0, 0xc430ce5a90, 0xc43170a168, 0x1, 0x1, 0xc420055e00)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/rpc/server.go:204 +0xad
created by github.com/ethereum/go-ethereum/rpc.(*Server).serveRequest
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/rpc/server.go:199 +0x28c

    1. increment调用栈信息(修改)

goroutine 53 [running]:
runtime/debug.Stack(0xf8a880, 0x16, 0x4)
   /usr/local/go/src/runtime/debug/stack.go:24 +0xa7
runtime/debug.PrintStack()
   /usr/local/go/src/runtime/debug/stack.go:16 +0x22
github.com/ethereum/go-ethereum/core/vm.(*EVMInterpreter).Run(0xc43abc6000, 0xc430d400c0, 0xc4315f4b48, 0x4, 0x4, 0xc43abd7d00, 0x0, 0x0, 0x0, 0x0, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/vm/interpreter.go:164 +0x109
github.com/ethereum/go-ethereum/core/vm.run(0xc420283500, 0xc430d400c0, 0xc4315f4b48, 0x4, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/vm/evm.go:64 +0x1f5
github.com/ethereum/go-ethereum/core/vm.(*EVM).Call(0xc420283500, 0x1104ae0, 0xc43a36b740, 0xce0f79bc5d263369, 0xcab1d2b820240de0, 0x9b341954, 0xc4315f4b48, 0x4, 0x4, 0x10c78, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/vm/evm.go:233 +0x525
github.com/ethereum/go-ethereum/core.(*StateTransition).TransitionDb(0xc4202b2930, 0x1113580, 0xc4316b8300, 0xc4315f4ce8, 0xc4202b2930, 0xc4316b8300, 0xc4316b8300, 0xf3d020)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state_transition.go:213 +0x3c4
github.com/ethereum/go-ethereum/core.ApplyMessage(0xc420283500, 0x1113580, 0xc4316b8300, 0xc4315f4ce8, 0xb74ed9b19e001d31, 0x51320650, 0xc43ab889e0, 0x9ad592d2228af9db, 0xb74ed9b19e001d31, 0x51320650, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state_transition.go:132 +0x5b
github.com/ethereum/go-ethereum/core.ApplyTransaction(0xc420208380, 0x11068a0, 0xc430cc0000, 0xc43abcd860, 0xc4315f4ce8, 0xc4201ffee0, 0xc430d68d80, 0xc430d633b0, 0xc430d68f50, 0x0, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state_processor.go:99 +0x2a5
github.com/ethereum/go-ethereum/miner.(*worker).commitTransaction(0xc430cf96c0, 0xc430d633b0, 0x9ad592d2228af9db, 0xb74ed9b19e001d31, 0x14342d9e51320650, 0xbf04c83051320650, 0x0, 0x0, 0x0, 0x0)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:695 +0x141
github.com/ethereum/go-ethereum/miner.(*worker).commitTransactions(0xc430cf96c0, 0xc4316a82a0, 0x9ad592d2228af9db, 0xb74ed9b19e001d31, 0x51320650, 0x0, 0x0)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:765 +0x2ba
github.com/ethereum/go-ethereum/miner.(*worker).mainLoop(0xc430cf96c0)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:469 +0x77a
created by github.com/ethereum/go-ethereum/miner.newWorker
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:219 +0x5dc

    1. 创建合约的调用栈

runtime/debug.Stack(0xf8a880, 0x16, 0x4)
   /usr/local/go/src/runtime/debug/stack.go:24 +0xa7
runtime/debug.PrintStack()
   /usr/local/go/src/runtime/debug/stack.go:16 +0x22
github.com/ethereum/go-ethereum/core/vm.(*EVMInterpreter).Run(0xc441740000, 0xc4200ca240, 0x0, 0x0, 0x0, 0x41cb00, 0x0, 0x0, 0x0, 0x0, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/vm/interpreter.go:164 +0x109
github.com/ethereum/go-ethereum/core/vm.run(0xc420283500, 0xc4200ca240, 0x0, 0x0, 0x0, 0x473000, 0x0, 0x0, 0x0, 0x0, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/vm/evm.go:64 +0x1f5
github.com/ethereum/go-ethereum/core/vm.(*EVM).create(0xc420283500, 0x1104ae0, 0xc420313620, 0xc430dabf40, 0x47300c, 0xc430d6d480, 0xce0f79bc5d263369, 0xcab1d2b820240de0, 0x9b341954, 0xb74ed9b19e001d31, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/vm/evm.go:410 +0x65f
github.com/ethereum/go-ethereum/core/vm.(*EVM).Create(0xc420283500, 0x1104ae0, 0xc420313620, 0xc42000ab00, 0xf3, 0xf3, 0x47300c, 0xc430d6d480, 0x0, 0x0, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/vm/evm.go:450 +0x1f3
github.com/ethereum/go-ethereum/core.(*StateTransition).TransitionDb(0xc42035eb60, 0x1113580, 0xc4316b8360, 0xc42028c1f0, 0xc42035eb60, 0xc4316b8360, 0xc4316b8360, 0xf3d020)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state_transition.go:209 +0x750
github.com/ethereum/go-ethereum/core.ApplyMessage(0xc420283500, 0x1113580, 0xc4316b8360, 0xc42028c1f0, 0xb74ed9b19e001d31, 0x51320650, 0xc431600940, 0x0, 0x0, 0x0, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state_transition.go:132 +0x5b
github.com/ethereum/go-ethereum/core.ApplyTransaction(0xc420208380, 0x11068a0, 0xc430cc0000, 0xc441747860, 0xc42028c1f0, 0xc4200a2680, 0xc430d58000, 0xc4202b7a70, 0xc430d581d0, 0x0, ...)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/core/state_processor.go:99 +0x2a5
github.com/ethereum/go-ethereum/miner.(*worker).commitTransaction(0xc430cf96c0, 0xc4202b7a70, 0x0, 0x0, 0xe5b4821700000000, 0x51320650, 0x0, 0x0, 0x0, 0x0)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:695 +0x141
github.com/ethereum/go-ethereum/miner.(*worker).commitTransactions(0xc430cf96c0, 0xc4306bbb30, 0x0, 0x0, 0x0, 0x0, 0x0)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:765 +0x2ba
github.com/ethereum/go-ethereum/miner.(*worker).mainLoop(0xc430cf96c0)
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:469 +0x77a
created by github.com/ethereum/go-ethereum/miner.newWorker
   /home/001_code/002_etherum/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/miner/worker.go:219 +0x5dc

  1. 参考资料

rpc源码分析

https://github.com/ZtesoftCS/go-ethereum-code-analysis/blob/master/rpc%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.md

区块和交易,合约和虚拟机

https://blog.csdn.net/teaspring/article/details/75389151

go-ethereum以太坊源码解析完整版

https://blog.csdn.net/luckydog612/article/details/80534758

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值