github.com/calmw/ethereum@v0.1.1/cmd/evm/internal/t8ntool/execution.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package t8ntool
    18  
    19  import (
    20  	"fmt"
    21  	"math/big"
    22  	"os"
    23  
    24  	"github.com/calmw/ethereum/common"
    25  	"github.com/calmw/ethereum/common/math"
    26  	"github.com/calmw/ethereum/consensus/ethash"
    27  	"github.com/calmw/ethereum/consensus/misc"
    28  	"github.com/calmw/ethereum/core"
    29  	"github.com/calmw/ethereum/core/rawdb"
    30  	"github.com/calmw/ethereum/core/state"
    31  	"github.com/calmw/ethereum/core/types"
    32  	"github.com/calmw/ethereum/core/vm"
    33  	"github.com/calmw/ethereum/crypto"
    34  	"github.com/calmw/ethereum/ethdb"
    35  	"github.com/calmw/ethereum/log"
    36  	"github.com/calmw/ethereum/params"
    37  	"github.com/calmw/ethereum/rlp"
    38  	"github.com/calmw/ethereum/trie"
    39  	"golang.org/x/crypto/sha3"
    40  )
    41  
    42  type Prestate struct {
    43  	Env stEnv             `json:"env"`
    44  	Pre core.GenesisAlloc `json:"pre"`
    45  }
    46  
    47  // ExecutionResult contains the execution status after running a state test, any
    48  // error that might have occurred and a dump of the final state if requested.
    49  type ExecutionResult struct {
    50  	StateRoot       common.Hash           `json:"stateRoot"`
    51  	TxRoot          common.Hash           `json:"txRoot"`
    52  	ReceiptRoot     common.Hash           `json:"receiptsRoot"`
    53  	LogsHash        common.Hash           `json:"logsHash"`
    54  	Bloom           types.Bloom           `json:"logsBloom"        gencodec:"required"`
    55  	Receipts        types.Receipts        `json:"receipts"`
    56  	Rejected        []*rejectedTx         `json:"rejected,omitempty"`
    57  	Difficulty      *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
    58  	GasUsed         math.HexOrDecimal64   `json:"gasUsed"`
    59  	BaseFee         *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
    60  	WithdrawalsRoot *common.Hash          `json:"withdrawalsRoot,omitempty"`
    61  }
    62  
    63  type ommer struct {
    64  	Delta   uint64         `json:"delta"`
    65  	Address common.Address `json:"address"`
    66  }
    67  
    68  //go:generate go run github.com/fjl/gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
    69  type stEnv struct {
    70  	Coinbase         common.Address                      `json:"currentCoinbase"   gencodec:"required"`
    71  	Difficulty       *big.Int                            `json:"currentDifficulty"`
    72  	Random           *big.Int                            `json:"currentRandom"`
    73  	ParentDifficulty *big.Int                            `json:"parentDifficulty"`
    74  	ParentBaseFee    *big.Int                            `json:"parentBaseFee,omitempty"`
    75  	ParentGasUsed    uint64                              `json:"parentGasUsed,omitempty"`
    76  	ParentGasLimit   uint64                              `json:"parentGasLimit,omitempty"`
    77  	GasLimit         uint64                              `json:"currentGasLimit"   gencodec:"required"`
    78  	Number           uint64                              `json:"currentNumber"     gencodec:"required"`
    79  	Timestamp        uint64                              `json:"currentTimestamp"  gencodec:"required"`
    80  	ParentTimestamp  uint64                              `json:"parentTimestamp,omitempty"`
    81  	BlockHashes      map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
    82  	Ommers           []ommer                             `json:"ommers,omitempty"`
    83  	Withdrawals      []*types.Withdrawal                 `json:"withdrawals,omitempty"`
    84  	BaseFee          *big.Int                            `json:"currentBaseFee,omitempty"`
    85  	ParentUncleHash  common.Hash                         `json:"parentUncleHash"`
    86  }
    87  
    88  type stEnvMarshaling struct {
    89  	Coinbase         common.UnprefixedAddress
    90  	Difficulty       *math.HexOrDecimal256
    91  	Random           *math.HexOrDecimal256
    92  	ParentDifficulty *math.HexOrDecimal256
    93  	ParentBaseFee    *math.HexOrDecimal256
    94  	ParentGasUsed    math.HexOrDecimal64
    95  	ParentGasLimit   math.HexOrDecimal64
    96  	GasLimit         math.HexOrDecimal64
    97  	Number           math.HexOrDecimal64
    98  	Timestamp        math.HexOrDecimal64
    99  	ParentTimestamp  math.HexOrDecimal64
   100  	BaseFee          *math.HexOrDecimal256
   101  }
   102  
   103  type rejectedTx struct {
   104  	Index int    `json:"index"`
   105  	Err   string `json:"error"`
   106  }
   107  
   108  // Apply applies a set of transactions to a pre-state
   109  func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
   110  	txs types.Transactions, miningReward int64,
   111  	getTracerFn func(txIndex int, txHash common.Hash) (tracer vm.EVMLogger, err error)) (*state.StateDB, *ExecutionResult, error) {
   112  	// Capture errors for BLOCKHASH operation, if we haven't been supplied the
   113  	// required blockhashes
   114  	var hashError error
   115  	getHash := func(num uint64) common.Hash {
   116  		if pre.Env.BlockHashes == nil {
   117  			hashError = fmt.Errorf("getHash(%d) invoked, no blockhashes provided", num)
   118  			return common.Hash{}
   119  		}
   120  		h, ok := pre.Env.BlockHashes[math.HexOrDecimal64(num)]
   121  		if !ok {
   122  			hashError = fmt.Errorf("getHash(%d) invoked, blockhash for that block not provided", num)
   123  		}
   124  		return h
   125  	}
   126  	var (
   127  		statedb     = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre)
   128  		signer      = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp)
   129  		gaspool     = new(core.GasPool)
   130  		blockHash   = common.Hash{0x13, 0x37}
   131  		rejectedTxs []*rejectedTx
   132  		includedTxs types.Transactions
   133  		gasUsed     = uint64(0)
   134  		receipts    = make(types.Receipts, 0)
   135  		txIndex     = 0
   136  	)
   137  	gaspool.AddGas(pre.Env.GasLimit)
   138  	vmContext := vm.BlockContext{
   139  		CanTransfer: core.CanTransfer,
   140  		Transfer:    core.Transfer,
   141  		Coinbase:    pre.Env.Coinbase,
   142  		BlockNumber: new(big.Int).SetUint64(pre.Env.Number),
   143  		Time:        pre.Env.Timestamp,
   144  		Difficulty:  pre.Env.Difficulty,
   145  		GasLimit:    pre.Env.GasLimit,
   146  		GetHash:     getHash,
   147  	}
   148  	// If currentBaseFee is defined, add it to the vmContext.
   149  	if pre.Env.BaseFee != nil {
   150  		vmContext.BaseFee = new(big.Int).Set(pre.Env.BaseFee)
   151  	}
   152  	// If random is defined, add it to the vmContext.
   153  	if pre.Env.Random != nil {
   154  		rnd := common.BigToHash(pre.Env.Random)
   155  		vmContext.Random = &rnd
   156  	}
   157  	// If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's
   158  	// done in StateProcessor.Process(block, ...), right before transactions are applied.
   159  	if chainConfig.DAOForkSupport &&
   160  		chainConfig.DAOForkBlock != nil &&
   161  		chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 {
   162  		misc.ApplyDAOHardFork(statedb)
   163  	}
   164  
   165  	for i, tx := range txs {
   166  		msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee)
   167  		if err != nil {
   168  			log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", err)
   169  			rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
   170  			continue
   171  		}
   172  		tracer, err := getTracerFn(txIndex, tx.Hash())
   173  		if err != nil {
   174  			return nil, nil, err
   175  		}
   176  		vmConfig.Tracer = tracer
   177  		statedb.SetTxContext(tx.Hash(), txIndex)
   178  
   179  		var (
   180  			txContext = core.NewEVMTxContext(msg)
   181  			snapshot  = statedb.Snapshot()
   182  			prevGas   = gaspool.Gas()
   183  		)
   184  		evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
   185  
   186  		// (ret []byte, usedGas uint64, failed bool, err error)
   187  		msgResult, err := core.ApplyMessage(evm, msg, gaspool)
   188  		if err != nil {
   189  			statedb.RevertToSnapshot(snapshot)
   190  			log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
   191  			rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
   192  			gaspool.SetGas(prevGas)
   193  			continue
   194  		}
   195  		includedTxs = append(includedTxs, tx)
   196  		if hashError != nil {
   197  			return nil, nil, NewError(ErrorMissingBlockhash, hashError)
   198  		}
   199  		gasUsed += msgResult.UsedGas
   200  
   201  		// Receipt:
   202  		{
   203  			var root []byte
   204  			if chainConfig.IsByzantium(vmContext.BlockNumber) {
   205  				statedb.Finalise(true)
   206  			} else {
   207  				root = statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)).Bytes()
   208  			}
   209  
   210  			// Create a new receipt for the transaction, storing the intermediate root and
   211  			// gas used by the tx.
   212  			receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: gasUsed}
   213  			if msgResult.Failed() {
   214  				receipt.Status = types.ReceiptStatusFailed
   215  			} else {
   216  				receipt.Status = types.ReceiptStatusSuccessful
   217  			}
   218  			receipt.TxHash = tx.Hash()
   219  			receipt.GasUsed = msgResult.UsedGas
   220  
   221  			// If the transaction created a contract, store the creation address in the receipt.
   222  			if msg.To == nil {
   223  				receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
   224  			}
   225  
   226  			// Set the receipt logs and create the bloom filter.
   227  			receipt.Logs = statedb.GetLogs(tx.Hash(), vmContext.BlockNumber.Uint64(), blockHash)
   228  			receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
   229  			// These three are non-consensus fields:
   230  			//receipt.BlockHash
   231  			//receipt.BlockNumber
   232  			receipt.TransactionIndex = uint(txIndex)
   233  			receipts = append(receipts, receipt)
   234  		}
   235  
   236  		txIndex++
   237  	}
   238  	statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber))
   239  	// Add mining reward? (-1 means rewards are disabled)
   240  	if miningReward >= 0 {
   241  		// Add mining reward. The mining reward may be `0`, which only makes a difference in the cases
   242  		// where
   243  		// - the coinbase suicided, or
   244  		// - there are only 'bad' transactions, which aren't executed. In those cases,
   245  		//   the coinbase gets no txfee, so isn't created, and thus needs to be touched
   246  		var (
   247  			blockReward = big.NewInt(miningReward)
   248  			minerReward = new(big.Int).Set(blockReward)
   249  			perOmmer    = new(big.Int).Div(blockReward, big.NewInt(32))
   250  		)
   251  		for _, ommer := range pre.Env.Ommers {
   252  			// Add 1/32th for each ommer included
   253  			minerReward.Add(minerReward, perOmmer)
   254  			// Add (8-delta)/8
   255  			reward := big.NewInt(8)
   256  			reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta))
   257  			reward.Mul(reward, blockReward)
   258  			reward.Div(reward, big.NewInt(8))
   259  			statedb.AddBalance(ommer.Address, reward)
   260  		}
   261  		statedb.AddBalance(pre.Env.Coinbase, minerReward)
   262  	}
   263  	// Apply withdrawals
   264  	for _, w := range pre.Env.Withdrawals {
   265  		// Amount is in gwei, turn into wei
   266  		amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei))
   267  		statedb.AddBalance(w.Address, amount)
   268  	}
   269  	// Commit block
   270  	root, err := statedb.Commit(chainConfig.IsEIP158(vmContext.BlockNumber))
   271  	if err != nil {
   272  		fmt.Fprintf(os.Stderr, "Could not commit state: %v", err)
   273  		return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err))
   274  	}
   275  	execRs := &ExecutionResult{
   276  		StateRoot:   root,
   277  		TxRoot:      types.DeriveSha(includedTxs, trie.NewStackTrie(nil)),
   278  		ReceiptRoot: types.DeriveSha(receipts, trie.NewStackTrie(nil)),
   279  		Bloom:       types.CreateBloom(receipts),
   280  		LogsHash:    rlpHash(statedb.Logs()),
   281  		Receipts:    receipts,
   282  		Rejected:    rejectedTxs,
   283  		Difficulty:  (*math.HexOrDecimal256)(vmContext.Difficulty),
   284  		GasUsed:     (math.HexOrDecimal64)(gasUsed),
   285  		BaseFee:     (*math.HexOrDecimal256)(vmContext.BaseFee),
   286  	}
   287  	if pre.Env.Withdrawals != nil {
   288  		h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil))
   289  		execRs.WithdrawalsRoot = &h
   290  	}
   291  	return statedb, execRs, nil
   292  }
   293  
   294  func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
   295  	sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
   296  	statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
   297  	for addr, a := range accounts {
   298  		statedb.SetCode(addr, a.Code)
   299  		statedb.SetNonce(addr, a.Nonce)
   300  		statedb.SetBalance(addr, a.Balance)
   301  		for k, v := range a.Storage {
   302  			statedb.SetState(addr, k, v)
   303  		}
   304  	}
   305  	// Commit and re-open to start with a clean state.
   306  	root, _ := statedb.Commit(false)
   307  	statedb, _ = state.New(root, sdb, nil)
   308  	return statedb
   309  }
   310  
   311  func rlpHash(x interface{}) (h common.Hash) {
   312  	hw := sha3.NewLegacyKeccak256()
   313  	rlp.Encode(hw, x)
   314  	hw.Sum(h[:0])
   315  	return h
   316  }
   317  
   318  // calcDifficulty is based on ethash.CalcDifficulty. This method is used in case
   319  // the caller does not provide an explicit difficulty, but instead provides only
   320  // parent timestamp + difficulty.
   321  // Note: this method only works for ethash engine.
   322  func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime uint64,
   323  	parentDifficulty *big.Int, parentUncleHash common.Hash) *big.Int {
   324  	uncleHash := parentUncleHash
   325  	if uncleHash == (common.Hash{}) {
   326  		uncleHash = types.EmptyUncleHash
   327  	}
   328  	parent := &types.Header{
   329  		ParentHash: common.Hash{},
   330  		UncleHash:  uncleHash,
   331  		Difficulty: parentDifficulty,
   332  		Number:     new(big.Int).SetUint64(number - 1),
   333  		Time:       parentTime,
   334  	}
   335  	return ethash.CalcDifficulty(config, currentTime, parent)
   336  }