github.com/core-coin/go-core/v2@v2.1.9/cmd/cvm/internal/t8ntool/execution.go (about)

     1  // Copyright 2020 by the Authors
     2  // This file is part of go-core.
     3  //
     4  // go-core 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-core 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-core. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package t8ntool
    18  
    19  import (
    20  	"fmt"
    21  	"math/big"
    22  	"os"
    23  
    24  	"golang.org/x/crypto/sha3"
    25  
    26  	"github.com/core-coin/go-core/v2/xcbdb"
    27  
    28  	"github.com/core-coin/go-core/v2/common"
    29  	"github.com/core-coin/go-core/v2/common/math"
    30  	"github.com/core-coin/go-core/v2/core"
    31  	"github.com/core-coin/go-core/v2/core/rawdb"
    32  	"github.com/core-coin/go-core/v2/core/state"
    33  	"github.com/core-coin/go-core/v2/core/types"
    34  	"github.com/core-coin/go-core/v2/core/vm"
    35  	"github.com/core-coin/go-core/v2/crypto"
    36  	"github.com/core-coin/go-core/v2/log"
    37  	"github.com/core-coin/go-core/v2/params"
    38  	"github.com/core-coin/go-core/v2/rlp"
    39  	"github.com/core-coin/go-core/v2/trie"
    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:"receiptRoot"`
    53  	LogsHash    common.Hash    `json:"logsHash"`
    54  	Bloom       types.Bloom    `json:"logsBloom"        gencodec:"required"`
    55  	Receipts    types.Receipts `json:"receipts"`
    56  	Rejected    []int          `json:"rejected,omitempty"`
    57  }
    58  
    59  type ommer struct {
    60  	Delta   uint64         `json:"delta"`
    61  	Address common.Address `json:"address"`
    62  }
    63  
    64  //go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
    65  type stEnv struct {
    66  	Coinbase    common.Address                      `json:"currentCoinbase"   gencodec:"required"`
    67  	Difficulty  *big.Int                            `json:"currentDifficulty" gencodec:"required"`
    68  	EnergyLimit uint64                              `json:"currentEnergyLimit"   gencodec:"required"`
    69  	Number      uint64                              `json:"currentNumber"     gencodec:"required"`
    70  	Timestamp   uint64                              `json:"currentTimestamp"  gencodec:"required"`
    71  	BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
    72  	Ommers      []ommer                             `json:"ommers,omitempty"`
    73  }
    74  
    75  type stEnvMarshaling struct {
    76  	Coinbase    common.UnprefixedAddress
    77  	Difficulty  *math.HexOrDecimal256
    78  	EnergyLimit math.HexOrDecimal64
    79  	Number      math.HexOrDecimal64
    80  	Timestamp   math.HexOrDecimal64
    81  }
    82  
    83  // Apply applies a set of transactions to a pre-state
    84  func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
    85  	txs types.Transactions, miningReward int64,
    86  	getTracerFn func(txIndex int, txHash common.Hash) (tracer vm.Tracer, err error)) (*state.StateDB, *ExecutionResult, error) {
    87  
    88  	// Capture errors for BLOCKHASH operation, if we haven't been supplied the
    89  	// required blockhashes
    90  	var hashError error
    91  	getHash := func(num uint64) common.Hash {
    92  		if pre.Env.BlockHashes == nil {
    93  			hashError = fmt.Errorf("getHash(%d) invoked, no blockhashes provided", num)
    94  			return common.Hash{}
    95  		}
    96  		h, ok := pre.Env.BlockHashes[math.HexOrDecimal64(num)]
    97  		if !ok {
    98  			hashError = fmt.Errorf("getHash(%d) invoked, blockhash for that block not provided", num)
    99  		}
   100  		return h
   101  	}
   102  	var (
   103  		statedb     = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre)
   104  		signer      = types.MakeSigner(chainConfig.NetworkID)
   105  		energypool  = new(core.EnergyPool)
   106  		blockHash   = common.Hash{0x13, 0x37}
   107  		rejectedTxs []int
   108  		includedTxs types.Transactions
   109  		energyUsed  = uint64(0)
   110  		receipts    = make(types.Receipts, 0)
   111  		txIndex     = 0
   112  	)
   113  	energypool.AddEnergy(pre.Env.EnergyLimit)
   114  	vmContext := vm.BlockContext{
   115  		CanTransfer: core.CanTransfer,
   116  		Transfer:    core.Transfer,
   117  		Coinbase:    pre.Env.Coinbase,
   118  		BlockNumber: new(big.Int).SetUint64(pre.Env.Number),
   119  		Time:        new(big.Int).SetUint64(pre.Env.Timestamp),
   120  		Difficulty:  pre.Env.Difficulty,
   121  		EnergyLimit: pre.Env.EnergyLimit,
   122  		GetHash:     getHash,
   123  	}
   124  
   125  	for i, tx := range txs {
   126  		msg, err := tx.AsMessage(signer)
   127  		if err != nil {
   128  			log.Info("rejected tx", "index", i, "hash", tx.Hash(), "error", err)
   129  			rejectedTxs = append(rejectedTxs, i)
   130  			continue
   131  		}
   132  		tracer, err := getTracerFn(txIndex, tx.Hash())
   133  		if err != nil {
   134  			return nil, nil, err
   135  		}
   136  		vmConfig.Tracer = tracer
   137  		vmConfig.Debug = (tracer != nil)
   138  		statedb.Prepare(tx.Hash(), blockHash, txIndex)
   139  		txContext := core.NewCVMTxContext(msg)
   140  
   141  		cvm := vm.NewCVM(vmContext, txContext, statedb, chainConfig, vmConfig)
   142  		snapshot := statedb.Snapshot()
   143  		// (ret []byte, usedEnergy uint64, failed bool, err error)
   144  		msgResult, err := core.ApplyMessage(cvm, msg, energypool)
   145  		if err != nil {
   146  			statedb.RevertToSnapshot(snapshot)
   147  			log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err)
   148  			rejectedTxs = append(rejectedTxs, i)
   149  			continue
   150  		}
   151  		includedTxs = append(includedTxs, tx)
   152  		if hashError != nil {
   153  			return nil, nil, NewError(ErrorMissingBlockhash, hashError)
   154  		}
   155  		energyUsed += msgResult.UsedEnergy
   156  		// Create a new receipt for the transaction, storing the intermediate root and energy used by the tx
   157  		{
   158  			var root []byte
   159  			statedb.Finalise(true)
   160  
   161  			receipt := types.NewReceipt(root, msgResult.Failed(), energyUsed)
   162  			receipt.TxHash = tx.Hash()
   163  			receipt.EnergyUsed = msgResult.UsedEnergy
   164  			// if the transaction created a contract, store the creation address in the receipt.
   165  			if msg.To() == nil {
   166  				receipt.ContractAddress = crypto.CreateAddress(cvm.TxContext.Origin, tx.Nonce())
   167  			}
   168  			// Set the receipt logs and create a bloom for filtering
   169  			receipt.Logs = statedb.GetLogs(tx.Hash())
   170  			receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
   171  			// These three are non-consensus fields
   172  			//receipt.BlockHash
   173  			//receipt.BlockNumber =
   174  			receipt.TransactionIndex = uint(txIndex)
   175  			receipts = append(receipts, receipt)
   176  		}
   177  		txIndex++
   178  	}
   179  	statedb.IntermediateRoot(true)
   180  	// Add mining reward?
   181  	if miningReward > 0 {
   182  		// Add mining reward. The mining reward may be `0`, which only makes a difference in the cases
   183  		// where
   184  		// - the coinbase suicided, or
   185  		// - there are only 'bad' transactions, which aren't executed. In those cases,
   186  		//   the coinbase gets no txfee, so isn't created, and thus needs to be touched
   187  		var (
   188  			blockReward = big.NewInt(miningReward)
   189  			minerReward = new(big.Int).Set(blockReward)
   190  			perOmmer    = new(big.Int).Div(blockReward, big.NewInt(32))
   191  		)
   192  		for _, ommer := range pre.Env.Ommers {
   193  			// Add 1/32th for each ommer included
   194  			minerReward.Add(minerReward, perOmmer)
   195  			// Add (8-delta)/8
   196  			reward := big.NewInt(8)
   197  			reward.Sub(reward, big.NewInt(0).SetUint64(ommer.Delta))
   198  			reward.Mul(reward, blockReward)
   199  			reward.Div(reward, big.NewInt(8))
   200  			statedb.AddBalance(ommer.Address, reward)
   201  		}
   202  		statedb.AddBalance(pre.Env.Coinbase, minerReward)
   203  	}
   204  	// Commit block
   205  	root, err := statedb.Commit(true)
   206  	if err != nil {
   207  		fmt.Fprintf(os.Stderr, "Could not commit state: %v", err)
   208  		return nil, nil, NewError(ErrorCVM, fmt.Errorf("could not commit state: %v", err))
   209  	}
   210  	execRs := &ExecutionResult{
   211  		StateRoot:   root,
   212  		TxRoot:      types.DeriveSha(includedTxs, new(trie.Trie)),
   213  		ReceiptRoot: types.DeriveSha(receipts, new(trie.Trie)),
   214  		Bloom:       types.CreateBloom(receipts),
   215  		LogsHash:    rlpHash(statedb.Logs()),
   216  		Receipts:    receipts,
   217  		Rejected:    rejectedTxs,
   218  	}
   219  	return statedb, execRs, nil
   220  }
   221  
   222  func MakePreState(db xcbdb.Database, accounts core.GenesisAlloc) *state.StateDB {
   223  	sdb := state.NewDatabase(db)
   224  	statedb, _ := state.New(common.Hash{}, sdb, nil)
   225  	for addr, a := range accounts {
   226  		statedb.SetCode(addr, a.Code)
   227  		statedb.SetNonce(addr, a.Nonce)
   228  		statedb.SetBalance(addr, a.Balance)
   229  		for k, v := range a.Storage {
   230  			statedb.SetState(addr, k, v)
   231  		}
   232  	}
   233  	// Commit and re-open to start with a clean state.
   234  	root, _ := statedb.Commit(false)
   235  	statedb, _ = state.New(root, sdb, nil)
   236  	return statedb
   237  }
   238  
   239  func rlpHash(x interface{}) (h common.Hash) {
   240  	hw := sha3.New256()
   241  	rlp.Encode(hw, x)
   242  	hw.Sum(h[:0])
   243  	return h
   244  }