| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- // Copyright 2020 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- package t8ntool
- import (
- "fmt"
- "math/big"
- "os"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/math"
- "github.com/ethereum/go-ethereum/consensus/misc"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie"
- "golang.org/x/crypto/sha3"
- )
- type Prestate struct {
- Env stEnv `json:"env"`
- Pre core.GenesisAlloc `json:"pre"`
- }
- // ExecutionResult contains the execution status after running a state test, any
- // error that might have occurred and a dump of the final state if requested.
- type ExecutionResult struct {
- StateRoot common.Hash `json:"stateRoot"`
- TxRoot common.Hash `json:"txRoot"`
- ReceiptRoot common.Hash `json:"receiptRoot"`
- LogsHash common.Hash `json:"logsHash"`
- Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
- Receipts types.Receipts `json:"receipts"`
- Rejected []int `json:"rejected,omitempty"`
- }
- type ommer struct {
- Delta uint64 `json:"delta"`
- Address common.Address `json:"address"`
- }
- //go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
- type stEnv struct {
- Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
- Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"`
- GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
- Number uint64 `json:"currentNumber" gencodec:"required"`
- Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
- BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
- Ommers []ommer `json:"ommers,omitempty"`
- }
- type stEnvMarshaling struct {
- Coinbase common.UnprefixedAddress
- Difficulty *math.HexOrDecimal256
- GasLimit math.HexOrDecimal64
- Number math.HexOrDecimal64
- Timestamp math.HexOrDecimal64
- }
- // Apply applies a set of transactions to a pre-state
- func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
- txs types.Transactions, miningReward int64,
- getTracerFn func(txIndex int, txHash common.Hash) (tracer vm.EVMLogger, err error)) (*state.StateDB, *ExecutionResult, error) {
- // Capture errors for BLOCKHASH operation, if we haven't been supplied the
- // required blockhashes
- var hashError error
- getHash := func(num uint64) common.Hash {
- if pre.Env.BlockHashes == nil {
- hashError = fmt.Errorf("getHash(%d) invoked, no blockhashes provided", num)
- return common.Hash{}
- }
- h, ok := pre.Env.BlockHashes[math.HexOrDecimal64(num)]
- if !ok {
- hashError = fmt.Errorf("getHash(%d) invoked, blockhash for that block not provided", num)
- }
- return h
- }
- var (
- statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre)
- signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number))
- gaspool = new(core.GasPool)
- blockHash = common.Hash{0x13, 0x37}
- rejectedTxs []int
- includedTxs types.Transactions
- gasUsed = uint64(0)
- receipts = make(types.Receipts, 0)
- txIndex = 0
- )
- gaspool.AddGas(pre.Env.GasLimit)
- vmContext := vm.BlockContext{
- CanTransfer: core.CanTransfer,
- Transfer: core.Transfer,
- Coinbase: pre.Env.Coinbase,
- BlockNumber: new(big.Int).SetUint64(pre.Env.Number),
- Time: new(big.Int).SetUint64(pre.Env.Timestamp),
- Difficulty: pre.Env.Difficulty,
- GasLimit: pre.Env.GasLimit,
- GetHash: getHash,
- }
- // If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's
- // done in StateProcessor.Process(block, ...), right before transactions are applied.
- if chainConfig.DAOForkSupport &&
- chainConfig.DAOForkBlock != nil &&
- chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 {
- misc.ApplyDAOHardFork(statedb)
- }
- for i, tx := range txs {
- msg, err := tx.AsMessage(signer)
- if err != nil {
- log.Info("rejected tx", "index", i, "hash", tx.Hash(), "error", err)
- rejectedTxs = append(rejectedTxs, i)
- continue
- }
- tracer, err := getTracerFn(txIndex, tx.Hash())
- if err != nil {
- return nil, nil, err
- }
- vmConfig.Tracer = tracer
- vmConfig.Debug = (tracer != nil)
- statedb.Prepare(tx.Hash(), blockHash, txIndex)
- txContext := core.NewEVMTxContext(msg)
- snapshot := statedb.Snapshot()
- evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
- // (ret []byte, usedGas uint64, failed bool, err error)
- msgResult, err := core.ApplyMessage(evm, msg, gaspool)
- if err != nil {
- statedb.RevertToSnapshot(snapshot)
- log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err)
- rejectedTxs = append(rejectedTxs, i)
- continue
- }
- includedTxs = append(includedTxs, tx)
- if hashError != nil {
- return nil, nil, NewError(ErrorMissingBlockhash, hashError)
- }
- gasUsed += msgResult.UsedGas
- // Receipt:
- {
- var root []byte
- if chainConfig.IsByzantium(vmContext.BlockNumber) {
- statedb.Finalise(true)
- } else {
- root = statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)).Bytes()
- }
- // Create a new receipt for the transaction, storing the intermediate root and
- // gas used by the tx.
- receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: gasUsed}
- if msgResult.Failed() {
- receipt.Status = types.ReceiptStatusFailed
- } else {
- receipt.Status = types.ReceiptStatusSuccessful
- }
- receipt.TxHash = tx.Hash()
- receipt.GasUsed = msgResult.UsedGas
- // If the transaction created a contract, store the creation address in the receipt.
- if msg.To() == nil {
- receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
- }
- // Set the receipt logs and create the bloom filter.
- receipt.Logs = statedb.GetLogs(tx.Hash())
- receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
- // These three are non-consensus fields:
- //receipt.BlockHash
- //receipt.BlockNumber
- receipt.TransactionIndex = uint(txIndex)
- receipts = append(receipts, receipt)
- }
- txIndex++
- }
- statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber))
- // Add mining reward?
- if miningReward > 0 {
- // Add mining reward. The mining reward may be `0`, which only makes a difference in the cases
- // where
- // - the coinbase suicided, or
- // - there are only 'bad' transactions, which aren't executed. In those cases,
- // the coinbase gets no txfee, so isn't created, and thus needs to be touched
- var (
- blockReward = big.NewInt(miningReward)
- minerReward = new(big.Int).Set(blockReward)
- perOmmer = new(big.Int).Div(blockReward, big.NewInt(32))
- )
- for _, ommer := range pre.Env.Ommers {
- // Add 1/32th for each ommer included
- minerReward.Add(minerReward, perOmmer)
- // Add (8-delta)/8
- reward := big.NewInt(8)
- reward.Sub(reward, big.NewInt(0).SetUint64(ommer.Delta))
- reward.Mul(reward, blockReward)
- reward.Div(reward, big.NewInt(8))
- statedb.AddBalance(ommer.Address, reward)
- }
- statedb.AddBalance(pre.Env.Coinbase, minerReward)
- }
- // Commit block
- root, _, err := statedb.Commit(chainConfig.IsEIP158(vmContext.BlockNumber))
- if err != nil {
- fmt.Fprintf(os.Stderr, "Could not commit state: %v", err)
- return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err))
- }
- execRs := &ExecutionResult{
- StateRoot: root,
- TxRoot: types.DeriveSha(includedTxs, trie.NewStackTrie(nil)),
- ReceiptRoot: types.DeriveSha(receipts, trie.NewStackTrie(nil)),
- Bloom: types.CreateBloom(receipts),
- LogsHash: rlpHash(statedb.Logs()),
- Receipts: receipts,
- Rejected: rejectedTxs,
- }
- return statedb, execRs, nil
- }
- func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
- sdb := state.NewDatabase(db)
- statedb, _ := state.New(common.Hash{}, sdb, nil)
- for addr, a := range accounts {
- statedb.SetCode(addr, a.Code)
- statedb.SetNonce(addr, a.Nonce)
- statedb.SetBalance(addr, a.Balance)
- for k, v := range a.Storage {
- statedb.SetState(addr, k, v)
- }
- }
- // Commit and re-open to start with a clean state.
- root, _, _ := statedb.Commit(false)
- statedb, _ = state.New(root, sdb, nil)
- return statedb
- }
- func rlpHash(x interface{}) (h common.Hash) {
- hw := sha3.NewLegacyKeccak256()
- rlp.Encode(hw, x)
- hw.Sum(h[:0])
- return h
- }
|