LoginSignup
1
0

More than 5 years have passed since last update.

Ethereumのトランザクションの流れ(3) バックエンド編 2. transaction実行編

Posted at

Ethereumのトランザクションの流れ(2) バックエンド編 1. transaction受信編に続きトランザクションが実行されるところを追っていきます。
v1.8.15ベースに解説します。

トランザクションの実行

func (self *worker) update() {
    defer self.txsSub.Unsubscribe()
    defer self.chainHeadSub.Unsubscribe()
    defer self.chainSideSub.Unsubscribe()

    for {
        // A real event arrived, process interesting content
        select {
        // Handle ChainHeadEvent
        case <-self.chainHeadCh:
            self.commitNewWork()

        // Handle ChainSideEvent
        case ev := <-self.chainSideCh:
            self.uncleMu.Lock()
            self.possibleUncles[ev.Block.Hash()] = ev.Block
            self.uncleMu.Unlock()

        // Handle NewTxsEvent
        case ev := <-self.txsCh:
            // ここでトランザクションイベントを受け取っている
            // Apply transactions to the pending state if we're not mining.
            //
            // Note all transactions received may not be continuous with transactions
            // already included in the current mining block. These transactions will
            // be automatically eliminated.
            if atomic.LoadInt32(&self.mining) == 0 {
                self.currentMu.Lock()
                txs := make(map[common.Address]types.Transactions)
                for _, tx := range ev.Txs {
                    acc, _ := types.Sender(self.current.signer, tx)
                    txs[acc] = append(txs[acc], tx)
                }
                txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs)
                self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase)
                self.updateSnapshot()
                self.currentMu.Unlock()
            } else {
                // If we're mining, but nothing is being processed, wake on new transactions
                if self.config.Clique != nil && self.config.Clique.Period == 0 {
                    self.commitNewWork()
                }
            }

        // System stopped
        case <-self.txsSub.Err():
            return
        case <-self.chainHeadSub.Err():
            return
        case <-self.chainSideSub.Err():
            return
        }
    }
}


func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce {
    // Initialize a price based heap with the head transactions
    heads := make(TxByPrice, 0, len(txs))
    for from, accTxs := range txs {
        heads = append(heads, accTxs[0])
        // Ensure the sender address is from the signer
        acc, _ := Sender(signer, accTxs[0])
        txs[acc] = accTxs[1:]
        if from != acc {
            delete(txs, from)
        }
    }
    heap.Init(&heads)

    // Assemble and return the transaction set
    return &TransactionsByPriceAndNonce{
        txs:    txs,
        heads:  heads,
        signer: signer,
    }
}

func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address) {
    if env.gasPool == nil {
        env.gasPool = new(core.GasPool).AddGas(env.header.GasLimit)
    }

    var coalescedLogs []*types.Log

    for {
        // If we don't have enough gas for any further transactions then we're done
        if env.gasPool.Gas() < params.TxGas {
            log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
            break
        }
        // Retrieve the next transaction and abort if all done
        tx := txs.Peek()
        if tx == nil {
            break
        }
        // Error may be ignored here. The error has already been checked
        // during transaction acceptance is the transaction pool.
        //
        // We use the eip155 signer regardless of the current hf.
        from, _ := types.Sender(env.signer, tx)
        // Check whether the tx is replay protected. If we're not in the EIP155 hf
        // phase, start ignoring the sender until we do.
        if tx.Protected() && !env.config.IsEIP155(env.header.Number) {
            log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", env.config.EIP155Block)

            txs.Pop()
            continue
        }
        // Start executing the transaction
        env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount)

        // ここで実際のトランザクションの実行を行う
        err, logs := env.commitTransaction(tx, bc, coinbase, env.gasPool)
        switch err {
        case core.ErrGasLimitReached:
            // Pop the current out-of-gas transaction without shifting in the next from the account
            log.Trace("Gas limit exceeded for current block", "sender", from)
            txs.Pop()

        case core.ErrNonceTooLow:
            // New head notification data race between the transaction pool and miner, shift
            log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
            txs.Shift()

        case core.ErrNonceTooHigh:
            // Reorg notification data race between the transaction pool and miner, skip account =
            log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce())
            txs.Pop()

        case nil:
            // エラーがなかったらtxsから取り出す
            // Everything ok, collect the logs and shift in the next transaction from the same account
            coalescedLogs = append(coalescedLogs, logs...)
            env.tcount++
            txs.Shift()

        default:
            // Strange error, discard the transaction and get the next in line (note, the
            // nonce-too-high clause will prevent us from executing in vain).
            log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
            txs.Shift()
        }
    }

    if len(coalescedLogs) > 0 || env.tcount > 0 {
        // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
        // logs by filling in the block hash when the block was mined by the local miner. This can
        // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.
        cpy := make([]*types.Log, len(coalescedLogs))
        for i, l := range coalescedLogs {
            cpy[i] = new(types.Log)
            *cpy[i] = *l
        }
        go func(logs []*types.Log, tcount int) {
            if len(logs) > 0 {
                mux.Post(core.PendingLogsEvent{Logs: logs})
            }
            if tcount > 0 {
                mux.Post(core.PendingStateEvent{})
            }
        }(cpy, env.tcount)
    }
}

func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) {
    snap := env.state.Snapshot()

    receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, &env.header.GasUsed, vm.Config{})
    if err != nil {
        env.state.RevertToSnapshot(snap)
        return err, nil
    }
    env.txs = append(env.txs, tx)
    env.receipts = append(env.receipts, receipt)

    return nil, receipt.Logs
}

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, uint64, error) {
    msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
    if err != nil {
        return nil, 0, 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, 0, 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 wether 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})

    return receipt, gas, err
}

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).Set(header.Time),
        Difficulty:  new(big.Int).Set(header.Difficulty),
        GasLimit:    header.GasLimit,
        GasPrice:    new(big.Int).Set(msg.GasPrice()),
    }
}

func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {
    return db.GetBalance(addr).Cmp(amount) >= 0
}

// Transfer subtracts amount from sender and adds amount to recipient using the given Db
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
    db.SubBalance(sender, amount)
    db.AddBalance(recipient, amount)
}

type EVM struct {
    // Context provides auxiliary blockchain related information
    Context
    // StateDB gives access to the underlying state
    StateDB StateDB
    // Depth is the current call stack
    depth int

    // chainConfig contains information about the current chain
    chainConfig *params.ChainConfig
    // chain rules contains the chain rules for the current epoch
    chainRules params.Rules
    // virtual machine configuration options used to initialise the
    // evm.
    vmConfig Config
    // global (to this context) ethereum virtual machine
    // used throughout the execution of the tx.
    interpreter *Interpreter
    // abort is used to abort the EVM calling operations
    // NOTE: must be set atomically
    abort int32
    // callGasTemp holds the gas available for the current call. This is needed because the
    // available gas is calculated in gasCall* according to the 63/64 rule and later
    // applied in opCall*.
    callGasTemp uint64
}

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),
    }

    evm.interpreter = NewInterpreter(evm, vmConfig)
    return evm
}

type Interpreter struct {
    evm      *EVM
    cfg      Config
    gasTable params.GasTable
    intPool  *intPool

    readOnly   bool   // Whether to throw on stateful modifications
    returnData []byte // Last CALL's return data for subsequent reuse
}

func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
    // 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 &Interpreter{
        evm:      evm,
        cfg:      cfg,
        gasTable: evm.ChainConfig().GasTable(evm.BlockNumber),
        intPool:  newIntPool(),
    }
}

func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool, error) {
    return NewStateTransition(evm, msg, gp).TransitionDb()
}

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

type StateDB struct {
    db   Database
    trie Trie

    // This map holds 'live' objects, which will get modified while processing a state transition.
    stateObjects      map[common.Address]*stateObject
    stateObjectsDirty map[common.Address]struct{}

    // DB error.
    // State objects are used by the consensus core and VM which are
    // unable to deal with database-level errors. Any error that occurs
    // during a database read is memoized here and will eventually be returned
    // by StateDB.Commit.
    dbErr error

    // The refund counter, also used by state transitioning.
    refund uint64

    thash, bhash common.Hash
    txIndex      int
    logs         map[common.Hash][]*types.Log
    logSize      uint

    preimages map[common.Hash][]byte

    // Journal of state modifications. This is the backbone of
    // Snapshot and RevertToSnapshot.
    journal        *journal
    validRevisions []revision
    nextRevisionId int

    lock sync.Mutex
}

func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
    s.Finalise(deleteEmptyObjects)
    return s.trie.Hash()
}

func (s *StateDB) Finalise(deleteEmptyObjects bool) {
    for addr := range s.journal.dirties {
        stateObject, exist := s.stateObjects[addr]
        if !exist {
            // ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2
            // That tx goes out of gas, and although the notion of 'touched' does not exist there, the
            // touch-event will still be recorded in the journal. Since ripeMD is a special snowflake,
            // it will persist in the journal even though the journal is reverted. In this special circumstance,
            // it may exist in `s.journal.dirties` but not in `s.stateObjects`.
            // Thus, we can safely ignore it here
            continue
        }

        if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) {
            s.deleteStateObject(stateObject)
        } else {
            stateObject.updateRoot(s.db)
            s.updateStateObject(stateObject)
        }
        s.stateObjectsDirty[addr] = struct{}{}
    }
    // Invalidate journal because reverting across transactions is not allowed.
    s.clearJournalAndRefund()
}

// GasPool tracks the amount of gas available during execution of the transactions
// in a block. The zero value is a pool with zero gas available.
type GasPool uint64

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
}

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)
    contractCreation := msg.To() == nil

    // Pay intrinsic gas
    gas, err := IntrinsicGas(st.data, contractCreation, homestead)
    if err != nil {
        return nil, 0, false, err
    }
    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)
        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()
    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
}

func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {

    // Depth check execution. Fail if we're trying to execute above the
    // limit.
    if evm.depth > int(params.CallCreateDepth) {
        return nil, common.Address{}, gas, ErrDepth
    }
    if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
        return nil, common.Address{}, gas, ErrInsufficientBalance
    }
    // Ensure there's no existing contract already at the designated address
    nonce := evm.StateDB.GetNonce(caller.Address())
    evm.StateDB.SetNonce(caller.Address(), nonce+1)

    contractAddr = crypto.CreateAddress(caller.Address(), nonce)
    contractHash := evm.StateDB.GetCodeHash(contractAddr)
    if evm.StateDB.GetNonce(contractAddr) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
        return nil, common.Address{}, 0, ErrContractAddressCollision
    }
    // Create a new account on the state
    snapshot := evm.StateDB.Snapshot()
    evm.StateDB.CreateAccount(contractAddr)
    if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
        evm.StateDB.SetNonce(contractAddr, 1)
    }
    evm.Transfer(evm.StateDB, caller.Address(), contractAddr, 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, AccountRef(contractAddr), value, gas)
    contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code)

    if evm.vmConfig.NoRecursion && evm.depth > 0 {
        return nil, contractAddr, gas, nil
    }

    if evm.vmConfig.Debug && evm.depth == 0 {
        evm.vmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value)
    }
    start := time.Now()

    ret, err = run(evm, contract, nil)

    // check whether the max code size has been exceeded
    maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
    // if the contract creation ran successfully and no errors were returned
    // calculate the gas required to store the code. If the code could not
    // be stored due to not enough gas set an error and let it be handled
    // by the error checking condition below.
    if err == nil && !maxCodeSizeExceeded {
        createDataGas := uint64(len(ret)) * params.CreateDataGas
        if contract.UseGas(createDataGas) {
            evm.StateDB.SetCode(contractAddr, ret)
        } else {
            err = ErrCodeStoreOutOfGas
        }
    }

    // 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 maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) {
        evm.StateDB.RevertToSnapshot(snapshot)
        if err != errExecutionReverted {
            contract.UseGas(contract.Gas)
        }
    }
    // Assign err if contract code size exceeds the max while the err is still empty.
    if maxCodeSizeExceeded && err == nil {
        err = errMaxCodeSizeExceeded
    }
    if evm.vmConfig.Debug && evm.depth == 0 {
        evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
    }
    return ret, contractAddr, contract.Gas, err
}

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.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 antything, 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))

    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)

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

func (self *StateDB) CreateAccount(addr common.Address) {
    new, prev := self.createObject(addr)
    if prev != nil {
        new.setBalance(prev.data.Balance)
    }
}

type stateObject struct {
    address  common.Address
    addrHash common.Hash // hash of ethereum address of the account
    data     Account
    db       *StateDB

    // DB error.
    // State objects are used by the consensus core and VM which are
    // unable to deal with database-level errors. Any error that occurs
    // during a database read is memoized here and will eventually be returned
    // by StateDB.Commit.
    dbErr error

    // Write caches.
    trie Trie // storage trie, which becomes non-nil on first access
    code Code // contract bytecode, which gets set when code is loaded

    cachedStorage Storage // Storage entry cache to avoid duplicate reads
    dirtyStorage  Storage // Storage entries that need to be flushed to disk

    // Cache flags.
    // When an object is marked suicided it will be delete from the trie
    // during the "update" phase of the state transition.
    dirtyCode bool // true if the code was updated
    suicided  bool
    deleted   bool
}

func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
    prev = self.getStateObject(addr)
    newobj = newObject(self, addr, Account{})
    newobj.setNonce(0) // sets the object to dirty
    if prev == nil {
        self.journal.append(createObjectChange{account: &addr})
    } else {
        self.journal.append(resetObjectChange{prev: prev})
    }
    self.setStateObject(newobj)
    return newobj, prev
}

func (self *stateObject) setBalance(amount *big.Int) {
    self.data.Balance = amount
}

type Contract struct {
    // CallerAddress is the result of the caller which initialised this
    // contract. However when the "call method" is delegated this value
    // needs to be initialised to that of the caller's caller.
    CallerAddress common.Address
    caller        ContractRef
    self          ContractRef

    jumpdests destinations // result of JUMPDEST analysis.

    Code     []byte
    CodeHash common.Hash
    CodeAddr *common.Address
    Input    []byte

    Gas   uint64
    value *big.Int

    Args []byte

    DelegateCall bool
}

// NewContract returns a new contract environment for the execution of EVM.
func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract {
    c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}

    if parent, ok := caller.(*Contract); ok {
        // Reuse JUMPDEST analysis from parent context if available.
        c.jumpdests = parent.jumpdests
    } else {
        c.jumpdests = make(destinations)
    }

    // Gas should be a pointer so it can safely be reduced through the run
    // This pointer will be off the state transition
    c.Gas = gas
    // ensures a value is set
    c.value = value

    return c
}

func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
    c.Code = code
    c.CodeHash = hash
    c.CodeAddr = addr
}

func run(evm *EVM, contract *Contract, input []byte) ([]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)
        }
    }
    return evm.interpreter.Run(contract, input)
}

var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
    common.BytesToAddress([]byte{1}): &ecrecover{},
    common.BytesToAddress([]byte{2}): &sha256hash{},
    common.BytesToAddress([]byte{3}): &ripemd160hash{},
    common.BytesToAddress([]byte{4}): &dataCopy{},
    common.BytesToAddress([]byte{5}): &bigModExp{},
    common.BytesToAddress([]byte{6}): &bn256Add{},
    common.BytesToAddress([]byte{7}): &bn256ScalarMul{},
    common.BytesToAddress([]byte{8}): &bn256Pairing{},
}

// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
    gas := p.RequiredGas(input)
    if contract.UseGas(gas) {
        return p.Run(input)
    }
    return nil, ErrOutOfGas
}

// ECRECOVER implemented as a native contract.
type ecrecover struct{}

func (c *ecrecover) RequiredGas(input []byte) uint64 {
    return params.EcrecoverGas
}

func (c *ecrecover) Run(input []byte) ([]byte, error) {
    const ecRecoverInputLength = 128

    input = common.RightPadBytes(input, ecRecoverInputLength)
    // "input" is (hash, v, r, s), each 32 bytes
    // but for ecrecover we want (r, s, v)

    r := new(big.Int).SetBytes(input[64:96])
    s := new(big.Int).SetBytes(input[96:128])
    v := input[63] - 27

    // tighter sig s values input homestead only apply to tx sigs
    if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) {
        return nil, nil
    }
    // v needs to be at the end for libsecp256k1
    pubKey, err := crypto.Ecrecover(input[:32], append(input[64:128], v))
    // make sure the public key is a valid one
    if err != nil {
        return nil, nil
    }

    // the first byte of pubkey is bitcoin heritage
    return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32), nil
}

// SHA256 implemented as a native contract.
type sha256hash struct{}

// RequiredGas returns the gas required to execute the pre-compiled contract.
//
// This method does not require any overflow checking as the input size gas costs
// required for anything significant is so high it's impossible to pay for.
func (c *sha256hash) RequiredGas(input []byte) uint64 {
    return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas
}
func (c *sha256hash) Run(input []byte) ([]byte, error) {
    h := sha256.Sum256(input)
    return h[:], nil
}

// RIPMED160 implemented as a native contract.
type ripemd160hash struct{}

// RequiredGas returns the gas required to execute the pre-compiled contract.
//
// This method does not require any overflow checking as the input size gas costs
// required for anything significant is so high it's impossible to pay for.
func (c *ripemd160hash) RequiredGas(input []byte) uint64 {
    return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas
}
func (c *ripemd160hash) Run(input []byte) ([]byte, error) {
    ripemd := ripemd160.New()
    ripemd.Write(input)
    return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
}

// data copy implemented as a native contract.
type dataCopy struct{}

// RequiredGas returns the gas required to execute the pre-compiled contract.
//
// This method does not require any overflow checking as the input size gas costs
// required for anything significant is so high it's impossible to pay for.
func (c *dataCopy) RequiredGas(input []byte) uint64 {
    return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas
}
func (c *dataCopy) Run(in []byte) ([]byte, error) {
    return in, nil
}

// bigModExp implements a native big integer exponential modular operation.
type bigModExp struct{}

var (
    big1      = big.NewInt(1)
    big4      = big.NewInt(4)
    big8      = big.NewInt(8)
    big16     = big.NewInt(16)
    big32     = big.NewInt(32)
    big64     = big.NewInt(64)
    big96     = big.NewInt(96)
    big480    = big.NewInt(480)
    big1024   = big.NewInt(1024)
    big3072   = big.NewInt(3072)
    big199680 = big.NewInt(199680)
)

// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bigModExp) RequiredGas(input []byte) uint64 {
    var (
        baseLen = new(big.Int).SetBytes(getData(input, 0, 32))
        expLen  = new(big.Int).SetBytes(getData(input, 32, 32))
        modLen  = new(big.Int).SetBytes(getData(input, 64, 32))
    )
    if len(input) > 96 {
        input = input[96:]
    } else {
        input = input[:0]
    }
    // Retrieve the head 32 bytes of exp for the adjusted exponent length
    var expHead *big.Int
    if big.NewInt(int64(len(input))).Cmp(baseLen) <= 0 {
        expHead = new(big.Int)
    } else {
        if expLen.Cmp(big32) > 0 {
            expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), 32))
        } else {
            expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), expLen.Uint64()))
        }
    }
    // Calculate the adjusted exponent length
    var msb int
    if bitlen := expHead.BitLen(); bitlen > 0 {
        msb = bitlen - 1
    }
    adjExpLen := new(big.Int)
    if expLen.Cmp(big32) > 0 {
        adjExpLen.Sub(expLen, big32)
        adjExpLen.Mul(big8, adjExpLen)
    }
    adjExpLen.Add(adjExpLen, big.NewInt(int64(msb)))

    // Calculate the gas cost of the operation
    gas := new(big.Int).Set(math.BigMax(modLen, baseLen))
    switch {
    case gas.Cmp(big64) <= 0:
        gas.Mul(gas, gas)
    case gas.Cmp(big1024) <= 0:
        gas = new(big.Int).Add(
            new(big.Int).Div(new(big.Int).Mul(gas, gas), big4),
            new(big.Int).Sub(new(big.Int).Mul(big96, gas), big3072),
        )
    default:
        gas = new(big.Int).Add(
            new(big.Int).Div(new(big.Int).Mul(gas, gas), big16),
            new(big.Int).Sub(new(big.Int).Mul(big480, gas), big199680),
        )
    }
    gas.Mul(gas, math.BigMax(adjExpLen, big1))
    gas.Div(gas, new(big.Int).SetUint64(params.ModExpQuadCoeffDiv))

    if gas.BitLen() > 64 {
        return math.MaxUint64
    }
    return gas.Uint64()
}

func (c *bigModExp) Run(input []byte) ([]byte, error) {
    var (
        baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
        expLen  = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64()
        modLen  = new(big.Int).SetBytes(getData(input, 64, 32)).Uint64()
    )
    if len(input) > 96 {
        input = input[96:]
    } else {
        input = input[:0]
    }
    // Handle a special case when both the base and mod length is zero
    if baseLen == 0 && modLen == 0 {
        return []byte{}, nil
    }
    // Retrieve the operands and execute the exponentiation
    var (
        base = new(big.Int).SetBytes(getData(input, 0, baseLen))
        exp  = new(big.Int).SetBytes(getData(input, baseLen, expLen))
        mod  = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen))
    )
    if mod.BitLen() == 0 {
        // Modulo 0 is undefined, return zero
        return common.LeftPadBytes([]byte{}, int(modLen)), nil
    }
    return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil
}

// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point,
// returning it, or an error if the point is invalid.
func newCurvePoint(blob []byte) (*bn256.G1, error) {
    p := new(bn256.G1)
    if _, err := p.Unmarshal(blob); err != nil {
        return nil, err
    }
    return p, nil
}

// newTwistPoint unmarshals a binary blob into a bn256 elliptic curve point,
// returning it, or an error if the point is invalid.
func newTwistPoint(blob []byte) (*bn256.G2, error) {
    p := new(bn256.G2)
    if _, err := p.Unmarshal(blob); err != nil {
        return nil, err
    }
    return p, nil
}

// bn256Add implements a native elliptic curve point addition.
type bn256Add struct{}

// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256Add) RequiredGas(input []byte) uint64 {
    return params.Bn256AddGas
}

func (c *bn256Add) Run(input []byte) ([]byte, error) {
    x, err := newCurvePoint(getData(input, 0, 64))
    if err != nil {
        return nil, err
    }
    y, err := newCurvePoint(getData(input, 64, 64))
    if err != nil {
        return nil, err
    }
    res := new(bn256.G1)
    res.Add(x, y)
    return res.Marshal(), nil
}

// bn256ScalarMul implements a native elliptic curve scalar multiplication.
type bn256ScalarMul struct{}

// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256ScalarMul) RequiredGas(input []byte) uint64 {
    return params.Bn256ScalarMulGas
}

func (c *bn256ScalarMul) Run(input []byte) ([]byte, error) {
    p, err := newCurvePoint(getData(input, 0, 64))
    if err != nil {
        return nil, err
    }
    res := new(bn256.G1)
    res.ScalarMult(p, new(big.Int).SetBytes(getData(input, 64, 32)))
    return res.Marshal(), nil
}

var (
    // true32Byte is returned if the bn256 pairing check succeeds.
    true32Byte = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}

    // false32Byte is returned if the bn256 pairing check fails.
    false32Byte = make([]byte, 32)

    // errBadPairingInput is returned if the bn256 pairing input is invalid.
    errBadPairingInput = errors.New("bad elliptic curve pairing size")
)

// bn256Pairing implements a pairing pre-compile for the bn256 curve
type bn256Pairing struct{}

// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256Pairing) RequiredGas(input []byte) uint64 {
    return params.Bn256PairingBaseGas + uint64(len(input)/192)*params.Bn256PairingPerPointGas
}

func (c *bn256Pairing) Run(input []byte) ([]byte, error) {
    // Handle some corner cases cheaply
    if len(input)%192 > 0 {
        return nil, errBadPairingInput
    }
    // Convert the input into a set of coordinates
    var (
        cs []*bn256.G1
        ts []*bn256.G2
    )
    for i := 0; i < len(input); i += 192 {
        c, err := newCurvePoint(input[i : i+64])
        if err != nil {
            return nil, err
        }
        t, err := newTwistPoint(input[i+64 : i+192])
        if err != nil {
            return nil, err
        }
        cs = append(cs, c)
        ts = append(ts, t)
    }
    // Execute the pairing checks and return the results
    if bn256.PairingCheck(cs, ts) {
        return true32Byte, nil
    }
    return false32Byte, nil
}

func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
    r := &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: cumulativeGasUsed}
    if failed {
        r.Status = ReceiptStatusFailed
    } else {
        r.Status = ReceiptStatusSuccessful
    }
    return r
}

type Receipt struct {
    // Consensus fields
    PostState         []byte `json:"root"`
    Status            uint64 `json:"status"`
    CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"`
    Bloom             Bloom  `json:"logsBloom"         gencodec:"required"`
    Logs              []*Log `json:"logs"              gencodec:"required"`

    // Implementation fields (don't reorder!)
    TxHash          common.Hash    `json:"transactionHash" gencodec:"required"`
    ContractAddress common.Address `json:"contractAddress"`
    GasUsed         uint64         `json:"gasUsed" gencodec:"required"`
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0