From e13a318e0b3b97e8161d40708000d5025d066bac Mon Sep 17 00:00:00 2001 From: sudeep Date: Thu, 7 Jul 2022 17:17:00 +0530 Subject: [PATCH] evm t8n to use ExecuteBlockEphemerally api (#4642) * evm t8n tool to use ExecuteBlockEphemerally api (#4512) * fix to set V, R, S in legacy transaction * fix to dump post-execution alloc for evm t8n * close tx in evm t8n * populate current difficulty and gas used in output result - update the ExecutionResult to include corresponding info (like Difficulty/GasUsed) * initial attempt at migrating 'evm t8n' to use ExecuteBlockEphemerally * using ExecutionResult in ExecuteBlockEphemerally * bypass validations and integrate with EphemeralExecResult * fixing output of 'evm t8n' - remaining bits are "stateRoot" in results.txt and "balance" field for one account in alloc.txt (for testdata=1) * get ExecuteBlockEphemerally to accept getTracer lambda * fix build failure * test cases for evm t8n * more test cases for evm t8n * fix stateRoot computation in evm t8n * remove reward argument, as EBE itself takes care of it * final cleanups for migration to using ExecuteBlockEphemerally * change EBEforBSC to match EBE * fix linter issues * manually revert an unwanted diff * avoid calculating ReceiptHash twice * linter check * minor correction * remove unnecessary logic in EBEforBsc * fix integration tests * fix build --- accounts/abi/bind/backends/simulated.go | 5 +- cmd/evm/internal/t8ntool/execution.go | 288 +++-------------- cmd/evm/internal/t8ntool/flags.go | 7 +- cmd/evm/internal/t8ntool/gen_stenv.go | 65 ++-- cmd/evm/internal/t8ntool/transition.go | 268 ++++++++++++++-- cmd/evm/main.go | 3 +- cmd/evm/t8n_test.go | 247 ++++++++++++++ cmd/evm/testdata/1/exp.json | 45 +++ cmd/evm/testdata/10/alloc.json | 23 ++ cmd/evm/testdata/10/env.json | 12 + cmd/evm/testdata/10/exp.json | 79 +++++ cmd/evm/testdata/10/readme.md | 79 +++++ cmd/evm/testdata/10/txs.json | 70 ++++ cmd/evm/testdata/11/alloc.json | 25 ++ cmd/evm/testdata/11/env.json | 12 + cmd/evm/testdata/11/readme.md | 13 + cmd/evm/testdata/11/txs.json | 14 + cmd/evm/testdata/12/alloc.json | 11 + cmd/evm/testdata/12/env.json | 10 + cmd/evm/testdata/12/exp.json | 26 ++ cmd/evm/testdata/12/readme.md | 40 +++ cmd/evm/testdata/12/txs.json | 20 ++ cmd/evm/testdata/19/alloc.json | 12 + cmd/evm/testdata/19/env.json | 9 + cmd/evm/testdata/19/exp_arrowglacier.json | 24 ++ cmd/evm/testdata/19/exp_london.json | 24 ++ cmd/evm/testdata/19/readme.md | 9 + cmd/evm/testdata/19/txs.json | 1 + cmd/evm/testdata/3/exp.json | 39 +++ cmd/evm/testdata/5/exp.json | 23 ++ cmd/evm/testdata/7/exp.json | 375 ++++++++++++++++++++++ cmd/evm/testdata/8/exp.json | 68 ++++ cmd/evm/testdata/9/alloc.json | 28 +- cmd/evm/testdata/9/env.json | 15 +- cmd/evm/testdata/9/exp.json | 54 ++++ cmd/evm/testdata/9/readme.md | 75 +++++ cmd/evm/testdata/9/txs.json | 49 ++- cmd/integration/commands/state_stages.go | 3 +- cmd/rpcdaemon/commands/eth_receipts.go | 3 +- cmd/rpcdaemon22/commands/eth_receipts.go | 3 +- cmd/state/commands/erigon2.go | 2 +- cmd/state/commands/erigon22.go | 3 +- cmd/state/commands/history2.go | 2 +- cmd/state/commands/history22.go | 2 +- cmd/state/commands/opcode_tracer.go | 2 +- cmd/state/commands/state_recon.go | 3 +- cmd/state/commands/state_recon_1.go | 3 +- consensus/parlia/parlia.go | 2 +- core/blockchain.go | 209 ++++++++---- core/chain_makers.go | 4 +- core/evm.go | 4 +- core/state_processor.go | 58 +--- core/vm/logger.go | 75 +++++ eth/stagedsync/stage_execute.go | 17 +- eth/stagedsync/stage_mining_exec.go | 2 +- go.mod | 7 +- go.sum | 17 +- internal/cmdtest/test_cmd.go | 300 +++++++++++++++++ tests/state_test_util.go | 3 +- turbo/transactions/tracing.go | 3 +- 60 files changed, 2390 insertions(+), 504 deletions(-) create mode 100644 cmd/evm/t8n_test.go create mode 100644 cmd/evm/testdata/1/exp.json create mode 100644 cmd/evm/testdata/10/alloc.json create mode 100644 cmd/evm/testdata/10/env.json create mode 100644 cmd/evm/testdata/10/exp.json create mode 100644 cmd/evm/testdata/10/readme.md create mode 100644 cmd/evm/testdata/10/txs.json create mode 100644 cmd/evm/testdata/11/alloc.json create mode 100644 cmd/evm/testdata/11/env.json create mode 100644 cmd/evm/testdata/11/readme.md create mode 100644 cmd/evm/testdata/11/txs.json create mode 100644 cmd/evm/testdata/12/alloc.json create mode 100644 cmd/evm/testdata/12/env.json create mode 100644 cmd/evm/testdata/12/exp.json create mode 100644 cmd/evm/testdata/12/readme.md create mode 100644 cmd/evm/testdata/12/txs.json create mode 100644 cmd/evm/testdata/19/alloc.json create mode 100644 cmd/evm/testdata/19/env.json create mode 100644 cmd/evm/testdata/19/exp_arrowglacier.json create mode 100644 cmd/evm/testdata/19/exp_london.json create mode 100644 cmd/evm/testdata/19/readme.md create mode 100644 cmd/evm/testdata/19/txs.json create mode 100644 cmd/evm/testdata/3/exp.json create mode 100644 cmd/evm/testdata/5/exp.json create mode 100644 cmd/evm/testdata/7/exp.json create mode 100644 cmd/evm/testdata/8/exp.json create mode 100644 cmd/evm/testdata/9/exp.json create mode 100644 cmd/evm/testdata/9/readme.md create mode 100644 internal/cmdtest/test_cmd.go diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index a04bcb9149d..2c5753f68d5 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -667,7 +667,8 @@ func (b *SimulatedBackend) callContract(_ context.Context, call ethereum.CallMsg msg := callMsg{call} txContext := core.NewEVMTxContext(msg) - evmContext := core.NewEVMBlockContext(block.Header(), b.getHeader, b.m.Engine, nil, b.contractHasTEVM) + header := block.Header() + evmContext := core.NewEVMBlockContext(header, core.GetHashFn(header, b.getHeader), b.m.Engine, nil, b.contractHasTEVM) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. vmEnv := vm.NewEVM(evmContext, txContext, statedb, b.m.ChainConfig, vm.Config{}) @@ -696,7 +697,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx types.Transac b.pendingState.Prepare(tx.Hash(), common.Hash{}, len(b.pendingBlock.Transactions())) //fmt.Printf("==== Start producing block %d, header: %d\n", b.pendingBlock.NumberU64(), b.pendingHeader.Number.Uint64()) if _, _, err := core.ApplyTransaction( - b.m.ChainConfig, b.getHeader, b.m.Engine, + b.m.ChainConfig, core.GetHashFn(b.pendingHeader, b.getHeader), b.m.Engine, &b.pendingHeader.Coinbase, b.gasPool, b.pendingState, state.NewNoopWriter(), b.pendingHeader, tx, diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 1ef1457c2c7..0bd33bc8a43 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -17,29 +17,19 @@ package t8ntool import ( - "context" "encoding/binary" - "fmt" "math/big" "github.com/holiman/uint256" "github.com/ledgerwatch/erigon-lib/kv" - "github.com/ledgerwatch/erigon-lib/kv/memdb" - "github.com/ledgerwatch/log/v3" - "golang.org/x/crypto/sha3" "github.com/ledgerwatch/erigon/common" "github.com/ledgerwatch/erigon/common/math" - "github.com/ledgerwatch/erigon/consensus/misc" + "github.com/ledgerwatch/erigon/consensus/ethash" "github.com/ledgerwatch/erigon/core" "github.com/ledgerwatch/erigon/core/state" - "github.com/ledgerwatch/erigon/core/systemcontracts" "github.com/ledgerwatch/erigon/core/types" - "github.com/ledgerwatch/erigon/core/vm" - "github.com/ledgerwatch/erigon/crypto" "github.com/ledgerwatch/erigon/params" - "github.com/ledgerwatch/erigon/rlp" - "github.com/ledgerwatch/erigon/turbo/trie" ) type Prestate struct { @@ -47,18 +37,6 @@ type Prestate struct { 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 []*rejectedTx `json:"rejected,omitempty"` -} - type ommer struct { Delta uint64 `json:"delta"` Address common.Address `json:"address"` @@ -66,226 +44,36 @@ type ommer struct { //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"` - BaseFee *big.Int `json:"currentBaseFee,omitempty"` - Random *common.Hash `json:"currentRandom,omitempty"` -} - -type rejectedTx struct { - Index int `json:"index"` - Err string `json:"error"` + Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` + Difficulty *big.Int `json:"currentDifficulty"` + Random *big.Int `json:"currentRandom"` + ParentDifficulty *big.Int `json:"parentDifficulty"` + GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` + Number uint64 `json:"currentNumber" gencodec:"required"` + Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` + ParentTimestamp uint64 `json:"parentTimestamp,omitempty"` + BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` + Ommers []ommer `json:"ommers,omitempty"` + BaseFee *big.Int `json:"currentBaseFee,omitempty"` + ParentUncleHash common.Hash `json:"parentUncleHash"` } type stEnvMarshaling struct { - Coinbase common.UnprefixedAddress - Difficulty *math.HexOrDecimal256 - GasLimit math.HexOrDecimal64 - Number math.HexOrDecimal64 - Timestamp math.HexOrDecimal64 - BaseFee *math.HexOrDecimal256 -} - -// 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.Tracer, err error)) (kv.RwDB, *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 - } - db := memdb.New() - - tx, err := db.BeginRw(context.Background()) - if err != nil { - return nil, nil, err - } - defer tx.Rollback() - - var ( - rules0 = chainConfig.Rules(0) - rules1 = chainConfig.Rules(1) - rules = chainConfig.Rules(pre.Env.Number) - ibs = MakePreState(rules0, tx, pre.Pre) - signer = types.MakeSigner(chainConfig, pre.Env.Number) - gaspool = new(core.GasPool) - blockHash = common.Hash{0x13, 0x37} - rejectedTxs []*rejectedTx - includedTxs types.Transactions - gasUsed = uint64(0) - receipts = make(types.Receipts, 0) - txIndex = 0 - ) - gaspool.AddGas(pre.Env.GasLimit) - - difficulty := new(big.Int) - if pre.Env.Random == nil { - difficulty = pre.Env.Difficulty - } else { - // We are on POS hence difficulty opcode is now supplant with RANDOM - random := pre.Env.Random.Bytes() - difficulty.SetBytes(random) - } - vmContext := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - Coinbase: pre.Env.Coinbase, - BlockNumber: pre.Env.Number, - ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil }, - Time: pre.Env.Timestamp, - Difficulty: difficulty, - GasLimit: pre.Env.GasLimit, - GetHash: getHash, - } - // If currentBaseFee is defined, add it to the vmContext. - if pre.Env.BaseFee != nil { - vmContext.BaseFee = new(uint256.Int) - overflow := vmContext.BaseFee.SetFromBig(pre.Env.BaseFee) - if overflow { - return nil, nil, fmt.Errorf("pre.Env.BaseFee higher than 2^256-1") - } - } - // 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(ibs) - } - systemcontracts.UpgradeBuildInSystemContract(chainConfig, new(big.Int).SetUint64(pre.Env.Number), ibs) - - for i, txn := range txs { - msg, err := txn.AsMessage(*signer, pre.Env.BaseFee, rules) - if err != nil { - log.Warn("rejected txn", "index", i, "hash", txn.Hash(), "err", err) - rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) - continue - } - tracer, err := getTracerFn(txIndex, txn.Hash()) - if err != nil { - return nil, nil, err - } - vmConfig.Tracer = tracer - vmConfig.Debug = (tracer != nil) - ibs.Prepare(txn.Hash(), blockHash, txIndex) - txContext := core.NewEVMTxContext(msg) - snapshot := ibs.Snapshot() - evm := vm.NewEVM(vmContext, txContext, ibs, chainConfig, vmConfig) - - // (ret []byte, usedGas uint64, failed bool, err error) - msgResult, err := core.ApplyMessage(evm, msg, gaspool, true /* refunds */, false /* gasBailout */) - if err != nil { - ibs.RevertToSnapshot(snapshot) - log.Info("rejected txn", "index", i, "hash", txn.Hash(), "from", msg.From(), "err", err) - rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) - continue - } - includedTxs = append(includedTxs, txn) - if hashError != nil { - return nil, nil, NewError(ErrorMissingBlockhash, hashError) - } - gasUsed += msgResult.UsedGas - - // Receipt: - { - // Create a new receipt for the transaction, storing the intermediate root and - // gas used by the txn. - receipt := &types.Receipt{Type: txn.Type(), CumulativeGasUsed: gasUsed} - if msgResult.Failed() { - receipt.Status = types.ReceiptStatusFailed - } else { - receipt.Status = types.ReceiptStatusSuccessful - } - receipt.TxHash = txn.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, txn.GetNonce()) - } - - // Set the receipt logs and create a bloom for filtering - receipt.Logs = ibs.GetLogs(txn.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++ - } - // 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 = uint256.NewInt(uint64(miningReward)) - minerReward = uint256.NewInt(0).Set(blockReward) - perOmmer = uint256.NewInt(0).Div(blockReward, uint256.NewInt(32)) - ) - for _, ommer := range pre.Env.Ommers { - // Add 1/32th for each ommer included - minerReward.Add(minerReward, perOmmer) - // Add (8-delta)/8 - reward := uint256.NewInt(8) - reward.Sub(reward, uint256.NewInt(ommer.Delta)) - reward.Mul(reward, blockReward) - reward.Div(reward, uint256.NewInt(8)) - ibs.AddBalance(ommer.Address, reward) - } - ibs.AddBalance(pre.Env.Coinbase, minerReward) - } - - // Commit block - var root common.Hash - if err = ibs.FinalizeTx(rules1, state.NewPlainStateWriter(tx, tx, 1)); err != nil { - return nil, nil, err - } - root, err = trie.CalcRoot("", tx) - if err != nil { - return nil, nil, err - } - if err = tx.Commit(); err != nil { - return nil, nil, err - } - - execRs := &ExecutionResult{ - StateRoot: root, - TxRoot: types.DeriveSha(includedTxs), - ReceiptRoot: types.DeriveSha(receipts), - Bloom: types.CreateBloom(receipts), - LogsHash: rlpHash(ibs.Logs()), - Receipts: receipts, - Rejected: rejectedTxs, - } - return db, execRs, nil + Coinbase common.UnprefixedAddress + Difficulty *math.HexOrDecimal256 + Random *math.HexOrDecimal256 + ParentDifficulty *math.HexOrDecimal256 + GasLimit math.HexOrDecimal64 + Number math.HexOrDecimal64 + Timestamp math.HexOrDecimal64 + ParentTimestamp math.HexOrDecimal64 + BaseFee *math.HexOrDecimal256 } -func MakePreState(chainRules *params.Rules, tx kv.RwTx, accounts core.GenesisAlloc) *state.IntraBlockState { +func MakePreState(chainRules *params.Rules, tx kv.RwTx, accounts core.GenesisAlloc) (*state.PlainStateReader, *state.PlainStateWriter) { var blockNr uint64 = 0 - r, _ := state.NewPlainStateReader(tx), state.NewPlainStateWriter(tx, tx, blockNr) - statedb := state.New(r) + stateReader, stateWriter := state.NewPlainStateReader(tx), state.NewPlainStateWriter(tx, tx, blockNr) + statedb := state.New(stateReader) //ibs for addr, a := range accounts { statedb.SetCode(addr, a.Code) statedb.SetNonce(addr, a.Nonce) @@ -299,7 +87,6 @@ func MakePreState(chainRules *params.Rules, tx kv.RwTx, accounts core.GenesisAll if len(a.Code) > 0 || len(a.Storage) > 0 { statedb.SetIncarnation(addr, state.FirstContractIncarnation) - var b [8]byte binary.BigEndian.PutUint64(b[:], state.FirstContractIncarnation) tx.Put(kv.IncarnationMap, addr[:], b[:]) @@ -312,12 +99,25 @@ func MakePreState(chainRules *params.Rules, tx kv.RwTx, accounts core.GenesisAll if err := statedb.CommitBlock(chainRules, state.NewPlainStateWriter(tx, tx, blockNr+1)); err != nil { panic(err) } - return statedb + return stateReader, stateWriter } -func rlpHash(x interface{}) (h common.Hash) { - hw := sha3.NewLegacyKeccak256() - rlp.Encode(hw, x) //nolint:errcheck - hw.Sum(h[:0]) - return h +// calcDifficulty is based on ethash.CalcDifficulty. This method is used in case +// the caller does not provide an explicit difficulty, but instead provides only +// parent timestamp + difficulty. +// Note: this method only works for ethash engine. +func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime uint64, + parentDifficulty *big.Int, parentUncleHash common.Hash) *big.Int { + uncleHash := parentUncleHash + if uncleHash == (common.Hash{}) { + uncleHash = types.EmptyUncleHash + } + parent := &types.Header{ + ParentHash: common.Hash{}, + UncleHash: uncleHash, + Difficulty: parentDifficulty, + Number: new(big.Int).SetUint64(number - 1), + Time: parentTime, + } + return ethash.CalcDifficulty(config, currentTime, parent.Time, parent.Difficulty, number-1, parent.UncleHash) } diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go index 7a5da94d6f8..4a918b048fc 100644 --- a/cmd/evm/internal/t8ntool/flags.go +++ b/cmd/evm/internal/t8ntool/flags.go @@ -83,11 +83,6 @@ var ( Usage: "`stdin` or file name of where to find the transactions to apply.", Value: "txs.json", } - RewardFlag = cli.Int64Flag{ - Name: "state.reward", - Usage: "Mining reward. Set to -1 to disable", - Value: 0, - } ChainIDFlag = cli.Int64Flag{ Name: "state.chainid", Usage: "ChainID to use", @@ -103,7 +98,7 @@ var ( "\n\tSyntax (+ExtraEip)", strings.Join(tests.AvailableForks(), "\n\t "), strings.Join(vm.ActivateableEips(), ", ")), - Value: "Istanbul", + Value: "ArrowGlacier", } VerbosityFlag = cli.IntFlag{ Name: "verbosity", diff --git a/cmd/evm/internal/t8ntool/gen_stenv.go b/cmd/evm/internal/t8ntool/gen_stenv.go index 88dfc4d3cb2..677948e5927 100644 --- a/cmd/evm/internal/t8ntool/gen_stenv.go +++ b/cmd/evm/internal/t8ntool/gen_stenv.go @@ -16,41 +16,50 @@ var _ = (*stEnvMarshaling)(nil) // MarshalJSON marshals as JSON. func (s stEnv) MarshalJSON() ([]byte, error) { type stEnv struct { - Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` - GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` - Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` - Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` - BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` - Ommers []ommer `json:"ommers,omitempty"` - BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` - Random *common.Hash `json:"currentRandom,omitempty"` + Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` + Random *math.HexOrDecimal256 `json:"currentRandom"` + ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` + GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` + Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` + Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` + ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` + BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` + Ommers []ommer `json:"ommers,omitempty"` + BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` + ParentUncleHash common.Hash `json:"parentUncleHash"` } var enc stEnv enc.Coinbase = common.UnprefixedAddress(s.Coinbase) enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty) + enc.Random = (*math.HexOrDecimal256)(s.Random) + enc.ParentDifficulty = (*math.HexOrDecimal256)(s.ParentDifficulty) enc.GasLimit = math.HexOrDecimal64(s.GasLimit) enc.Number = math.HexOrDecimal64(s.Number) enc.Timestamp = math.HexOrDecimal64(s.Timestamp) + enc.ParentTimestamp = math.HexOrDecimal64(s.ParentTimestamp) enc.BlockHashes = s.BlockHashes enc.Ommers = s.Ommers enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee) - enc.Random = s.Random + enc.ParentUncleHash = s.ParentUncleHash return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. func (s *stEnv) UnmarshalJSON(input []byte) error { type stEnv struct { - Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` - GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` - Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` - Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` - BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` - Ommers []ommer `json:"ommers,omitempty"` - BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` - Random *common.Hash `json:"currentRandom,omitempty"` + Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` + Random *math.HexOrDecimal256 `json:"currentRandom"` + ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` + GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` + Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` + Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` + ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` + BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` + Ommers []ommer `json:"ommers,omitempty"` + BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` + ParentUncleHash *common.Hash `json:"parentUncleHash"` } var dec stEnv if err := json.Unmarshal(input, &dec); err != nil { @@ -60,10 +69,15 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'currentCoinbase' for stEnv") } s.Coinbase = common.Address(*dec.Coinbase) - if dec.Difficulty == nil { - return errors.New("missing required field 'currentDifficulty' for stEnv") + if dec.Difficulty != nil { + s.Difficulty = (*big.Int)(dec.Difficulty) + } + if dec.Random != nil { + s.Random = (*big.Int)(dec.Random) + } + if dec.ParentDifficulty != nil { + s.ParentDifficulty = (*big.Int)(dec.ParentDifficulty) } - s.Difficulty = (*big.Int)(dec.Difficulty) if dec.GasLimit == nil { return errors.New("missing required field 'currentGasLimit' for stEnv") } @@ -76,6 +90,9 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'currentTimestamp' for stEnv") } s.Timestamp = uint64(*dec.Timestamp) + if dec.ParentTimestamp != nil { + s.ParentTimestamp = uint64(*dec.ParentTimestamp) + } if dec.BlockHashes != nil { s.BlockHashes = dec.BlockHashes } @@ -85,8 +102,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { if dec.BaseFee != nil { s.BaseFee = (*big.Int)(dec.BaseFee) } - if dec.Random != nil { - s.Random = dec.Random + if dec.ParentUncleHash != nil { + s.ParentUncleHash = *dec.ParentUncleHash } return nil } diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index a6547a7a3d6..230efb89be4 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -17,6 +17,7 @@ package t8ntool import ( + "context" "crypto/ecdsa" "encoding/json" "errors" @@ -27,9 +28,13 @@ import ( "path/filepath" "github.com/holiman/uint256" + "github.com/ledgerwatch/erigon-lib/kv" + "github.com/ledgerwatch/erigon-lib/kv/memdb" "github.com/ledgerwatch/erigon/cmd/rpcdaemon/commands" "github.com/ledgerwatch/erigon/common" "github.com/ledgerwatch/erigon/common/hexutil" + "github.com/ledgerwatch/erigon/common/math" + "github.com/ledgerwatch/erigon/consensus/ethash" "github.com/ledgerwatch/erigon/core" "github.com/ledgerwatch/erigon/core/state" "github.com/ledgerwatch/erigon/core/types" @@ -38,6 +43,7 @@ import ( "github.com/ledgerwatch/erigon/params" "github.com/ledgerwatch/erigon/rlp" "github.com/ledgerwatch/erigon/tests" + "github.com/ledgerwatch/erigon/turbo/trie" "github.com/ledgerwatch/log/v3" "github.com/urfave/cli" @@ -67,10 +73,15 @@ func (n *NumberedError) Error() string { return fmt.Sprintf("ERROR(%d): %v", n.errorCode, n.err.Error()) } -func (n *NumberedError) Code() int { +func (n *NumberedError) ExitCode() int { return n.errorCode } +// compile-time conformance test +var ( + _ cli.ExitCoder = (*NumberedError)(nil) +) + type input struct { Alloc core.GenesisAlloc `json:"alloc,omitempty"` Env *stEnv `json:"env,omitempty"` @@ -79,16 +90,8 @@ type input struct { func Main(ctx *cli.Context) error { log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StderrHandler)) - /* - // Configure the go-ethereum logger - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name))) - log.Root().SetHandler(glogger) - */ - var ( err error - tracer vm.Tracer baseDir = "" ) var getTracer func(txIndex int, txHash common.Hash) (vm.Tracer, error) @@ -162,6 +165,7 @@ func Main(ctx *cli.Context) error { return NewError(ErrorJson, fmt.Errorf("failed unmarshaling alloc-file: %v", err)) } } + prestate.Pre = inputData.Alloc // Set the block environment @@ -181,8 +185,8 @@ func Main(ctx *cli.Context) error { prestate.Env = *inputData.Env vmConfig := vm.Config{ - Tracer: tracer, - Debug: (tracer != nil), + Tracer: nil, + Debug: ctx.Bool(TraceFlag.Name), } // Construct the chainconfig var chainConfig *params.ChainConfig @@ -216,25 +220,90 @@ func Main(ctx *cli.Context) error { return NewError(ErrorJson, fmt.Errorf("failed signing transactions: %v", err)) } + eip1559 := chainConfig.IsLondon(prestate.Env.Number) // Sanity check, to not `panic` in state_transition - if chainConfig.IsLondon(prestate.Env.Number) { + if eip1559 { if prestate.Env.BaseFee == nil { return NewError(ErrorVMConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section")) } } - // Run the test and aggregate the result - _, result, err1 := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer) - if err1 != nil { - return err1 + // Sanity check, to not `panic` in state_transition + if prestate.Env.Random != nil && !eip1559 { + return NewError(ErrorVMConfig, errors.New("can only apply RANDOM on top of London chainrules")) + } + if env := prestate.Env; env.Difficulty == nil { + // If difficulty was not provided by caller, we need to calculate it. + switch { + case env.ParentDifficulty == nil: + return NewError(ErrorVMConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty")) + case env.Number == 0: + return NewError(ErrorVMConfig, errors.New("currentDifficulty needs to be provided for block number 0")) + case env.Timestamp <= env.ParentTimestamp: + return NewError(ErrorVMConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)", + env.Timestamp, env.ParentTimestamp)) + } + prestate.Env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp, + env.ParentTimestamp, env.ParentDifficulty, env.ParentUncleHash) + } + + // manufacture block from above inputs + header := NewHeader(prestate.Env, chainConfig.IsLondon(prestate.Env.Number)) + + var ommerHeaders = make([]*types.Header, len(prestate.Env.Ommers)) + header.Number.Add(header.Number, big.NewInt(int64(len(prestate.Env.Ommers)))) + for i, ommer := range prestate.Env.Ommers { + var ommerN big.Int + ommerN.SetUint64(header.Number.Uint64() - ommer.Delta) + ommerHeaders[i] = &types.Header{Coinbase: ommer.Address, Number: &ommerN} + } + block := types.NewBlock(header, txs, ommerHeaders, nil) + + var hashError error + getHash := func(num uint64) common.Hash { + if prestate.Env.BlockHashes == nil { + hashError = fmt.Errorf("getHash(%d) invoked, no blockhashes provided", num) + return common.Hash{} + } + h, ok := prestate.Env.BlockHashes[math.HexOrDecimal64(num)] + if !ok { + hashError = fmt.Errorf("getHash(%d) invoked, blockhash for that block not provided", num) + } + return h + } + db := memdb.New() + + tx, err := db.BeginRw(context.Background()) + if err != nil { + return err + } + + reader, writer := MakePreState(chainConfig.Rules(0), tx, prestate.Pre) + engine := ethash.NewFaker() + + result, err := core.ExecuteBlockEphemerally(chainConfig, &vmConfig, getHash, engine, block, reader, writer, nil, nil, nil, true, getTracer) + + if hashError != nil { + return NewError(ErrorMissingBlockhash, fmt.Errorf("blockhash error: %v", err)) } + + if err != nil { + return fmt.Errorf("error on EBE: %w", err) + } + + // state root calculation + root, err := CalculateStateRoot(tx) + if err != nil { + return err + } + result.StateRoot = *root + + // Dump the execution result body, _ := rlp.EncodeToBytes(txs) - // Dump the excution result collector := make(Alloc) - // TODO: Where DumpToCollector is declared? - //state.DumpToCollector(collector, false, false, false, nil, -1) + dumper := state.NewDumper(tx, prestate.Env.Number) + dumper.DumpToCollector(collector, false, false, common.Address{}, 0) return dispatchOutput(ctx, baseDir, result, collector, body) - } // txWithKey is a helper-struct, to allow us to use the types.Transaction along with @@ -261,8 +330,7 @@ func (t *txWithKey) UnmarshalJSON(input []byte) error { return err } } - gasPrice, value := uint256.NewInt(0), uint256.NewInt(0) - var overflow bool + // Now, read the transaction itself var txJson commands.RPCTransaction @@ -270,22 +338,104 @@ func (t *txWithKey) UnmarshalJSON(input []byte) error { return err } + // assemble transaction + tx, err := getTransaction(txJson) + if err != nil { + return err + } + t.tx = tx + return nil +} + +func getTransaction(txJson commands.RPCTransaction) (types.Transaction, error) { + gasPrice, value := uint256.NewInt(0), uint256.NewInt(0) + var overflow bool + var chainId *uint256.Int + if txJson.Value != nil { value, overflow = uint256.FromBig((*big.Int)(txJson.Value)) if overflow { - return fmt.Errorf("value field caused an overflow (uint256)") + return nil, fmt.Errorf("value field caused an overflow (uint256)") } } if txJson.GasPrice != nil { gasPrice, overflow = uint256.FromBig((*big.Int)(txJson.GasPrice)) if overflow { - return fmt.Errorf("gasPrice field caused an overflow (uint256)") + return nil, fmt.Errorf("gasPrice field caused an overflow (uint256)") } } - // assemble transaction - t.tx = types.NewTransaction(uint64(txJson.Nonce), *txJson.To, value, uint64(txJson.Gas), gasPrice, txJson.Input) - return nil + + if txJson.ChainID != nil { + chainId, overflow = uint256.FromBig((*big.Int)(txJson.ChainID)) + if overflow { + return nil, fmt.Errorf("chainId field caused an overflow (uint256)") + } + } + + switch txJson.Type { + case types.LegacyTxType, types.AccessListTxType: + var toAddr common.Address = common.Address{} + if txJson.To != nil { + toAddr = *txJson.To + } + legacyTx := types.NewTransaction(uint64(txJson.Nonce), toAddr, value, uint64(txJson.Gas), gasPrice, txJson.Input) + legacyTx.V.SetFromBig(txJson.V.ToInt()) + legacyTx.S.SetFromBig(txJson.S.ToInt()) + legacyTx.R.SetFromBig(txJson.R.ToInt()) + + if txJson.Type == types.AccessListTxType { + accessListTx := types.AccessListTx{ + LegacyTx: *legacyTx, + ChainID: chainId, + AccessList: *txJson.Accesses, + } + + return &accessListTx, nil + } else { + return legacyTx, nil + } + + case types.DynamicFeeTxType: + var tip *uint256.Int + var feeCap *uint256.Int + if txJson.Tip != nil { + tip, overflow = uint256.FromBig((*big.Int)(txJson.Tip)) + if overflow { + return nil, fmt.Errorf("maxPriorityFeePerGas field caused an overflow (uint256)") + } + } + + if txJson.FeeCap != nil { + feeCap, overflow = uint256.FromBig((*big.Int)(txJson.FeeCap)) + if overflow { + return nil, fmt.Errorf("maxFeePerGas field caused an overflow (uint256)") + } + } + + dynamicFeeTx := types.DynamicFeeTransaction{ + CommonTx: types.CommonTx{ + ChainID: chainId, + Nonce: uint64(txJson.Nonce), + To: txJson.To, + Value: value, + Gas: uint64(txJson.Gas), + Data: txJson.Input, + }, + Tip: tip, + FeeCap: feeCap, + AccessList: *txJson.Accesses, + } + + dynamicFeeTx.V.SetFromBig(txJson.V.ToInt()) + dynamicFeeTx.S.SetFromBig(txJson.S.ToInt()) + dynamicFeeTx.R.SetFromBig(txJson.R.ToInt()) + + return &dynamicFeeTx, nil + + default: + return nil, nil + } } // signUnsignedTransactions converts the input txs to canonical transactions. @@ -358,7 +508,7 @@ func saveFile(baseDir, filename string, data interface{}) error { // dispatchOutput writes the output data to either stderr or stdout, or to the specified // files -func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes) error { +func dispatchOutput(ctx *cli.Context, baseDir string, result *core.EphemeralExecResult, alloc Alloc, body hexutil.Bytes) error { stdOutObject := make(map[string]interface{}) stdErrObject := make(map[string]interface{}) dispatch := func(baseDir, fName, name string, obj interface{}) error { @@ -401,3 +551,65 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a } return nil } + +func NewHeader(env stEnv, Eip1559 bool) *types.Header { + var header types.Header + header.UncleHash = env.ParentUncleHash + header.Coinbase = env.Coinbase + header.Difficulty = env.Difficulty + header.Number = big.NewInt(int64(env.Number)) + header.GasLimit = env.GasLimit + header.Time = env.Timestamp + header.BaseFee = env.BaseFee + header.Eip1559 = Eip1559 + + return &header +} + +func CalculateStateRoot(tx kv.RwTx) (*common.Hash, error) { + // Generate hashed state + c, err := tx.RwCursor(kv.PlainState) + if err != nil { + return nil, err + } + h := common.NewHasher() + defer common.ReturnHasherToPool(h) + for k, v, err := c.First(); k != nil; k, v, err = c.Next() { + if err != nil { + return nil, fmt.Errorf("interate over plain state: %w", err) + } + var newK []byte + if len(k) == common.AddressLength { + newK = make([]byte, common.HashLength) + } else { + newK = make([]byte, common.HashLength*2+common.IncarnationLength) + } + h.Sha.Reset() + //nolint:errcheck + h.Sha.Write(k[:common.AddressLength]) + //nolint:errcheck + h.Sha.Read(newK[:common.HashLength]) + if len(k) > common.AddressLength { + copy(newK[common.HashLength:], k[common.AddressLength:common.AddressLength+common.IncarnationLength]) + h.Sha.Reset() + //nolint:errcheck + h.Sha.Write(k[common.AddressLength+common.IncarnationLength:]) + //nolint:errcheck + h.Sha.Read(newK[common.HashLength+common.IncarnationLength:]) + if err = tx.Put(kv.HashedStorage, newK, common.CopyBytes(v)); err != nil { + return nil, fmt.Errorf("insert hashed key: %w", err) + } + } else { + if err = tx.Put(kv.HashedAccounts, newK, common.CopyBytes(v)); err != nil { + return nil, fmt.Errorf("insert hashed key: %w", err) + } + } + } + c.Close() + root, err := trie.CalcRoot("", tx) + if err != nil { + return nil, err + } + + return &root, nil +} diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 451c0edbd8d..8d6bb1ef5f6 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -148,7 +148,6 @@ var stateTransitionCommand = cli.Command{ t8ntool.InputTxsFlag, t8ntool.ForknameFlag, t8ntool.ChainIDFlag, - t8ntool.RewardFlag, t8ntool.VerbosityFlag, }, } @@ -192,7 +191,7 @@ func main() { if err := app.Run(os.Args); err != nil { code := 1 if ec, ok := err.(*t8ntool.NumberedError); ok { - code = ec.Code() + code = ec.ExitCode() } fmt.Fprintln(os.Stderr, err) os.Exit(code) diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go new file mode 100644 index 00000000000..a2868e080b8 --- /dev/null +++ b/cmd/evm/t8n_test.go @@ -0,0 +1,247 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "reflect" + "strings" + "testing" + + "github.com/docker/docker/pkg/reexec" + "github.com/ledgerwatch/erigon/internal/cmdtest" +) + +func TestMain(m *testing.M) { + // Run the app if we've been exec'd as "ethkey-test" in runEthkey. + reexec.Register("evm-test", func() { + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Exit(0) + }) + // check if we have been reexec'd + if reexec.Init() { + return + } + os.Exit(m.Run()) +} + +type testT8n struct { + *cmdtest.TestCmd +} + +type t8nInput struct { + inAlloc string + inTxs string + inEnv string + stFork string +} + +func (args *t8nInput) get(base string) []string { + var out []string + if opt := args.inAlloc; opt != "" { + out = append(out, "--input.alloc") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + if opt := args.inTxs; opt != "" { + out = append(out, "--input.txs") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + if opt := args.inEnv; opt != "" { + out = append(out, "--input.env") + out = append(out, fmt.Sprintf("%v/%v", base, opt)) + } + if opt := args.stFork; opt != "" { + out = append(out, "--state.fork", opt) + } + return out +} + +type t8nOutput struct { + alloc bool + result bool + body bool +} + +func (args *t8nOutput) get() (out []string) { + if args.body { + out = append(out, "--output.body", "stdout") + } else { + out = append(out, "--output.body", "") // empty means ignore + } + if args.result { + out = append(out, "--output.result", "stdout") + } else { + out = append(out, "--output.result", "") + } + if args.alloc { + out = append(out, "--output.alloc", "stdout") + } else { + out = append(out, "--output.alloc", "") + } + return out +} + +func TestT8n(t *testing.T) { + tt := new(testT8n) + tt.TestCmd = cmdtest.NewTestCmd(t, tt) + for i, tc := range []struct { + base string + input t8nInput + output t8nOutput + expExitCode int + expOut string + }{ + { // Test exit (3) on bad config + base: "./testdata/1", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "Frontier+1346", + }, + output: t8nOutput{alloc: true, result: true}, + expExitCode: 3, + }, + { + base: "./testdata/1", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "Byzantium", + }, + output: t8nOutput{alloc: true, result: true}, + expOut: "exp.json", + }, + { // blockhash test + base: "./testdata/3", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "Berlin", + }, + output: t8nOutput{alloc: true, result: true}, + expOut: "exp.json", + }, + { // missing blockhash test + base: "./testdata/4", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "Berlin", + }, + expExitCode: 4, + }, + { // Uncle test + base: "./testdata/5", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "Byzantium", + }, + output: t8nOutput{alloc: true, result: true}, + expOut: "exp.json", + }, + { // Dao-transition check + base: "./testdata/7", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "HomesteadToDaoAt5", + }, + expOut: "exp.json", + output: t8nOutput{alloc: true, result: true}, + }, + { // transactions with access list + base: "./testdata/8", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "Berlin", + }, + expOut: "exp.json", + output: t8nOutput{alloc: true, result: true}, + }, + { // EIP-1559 + base: "./testdata/9", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "London", + }, + expOut: "exp.json", + output: t8nOutput{alloc: true, result: true}, + }, + { // EIP-1559 + base: "./testdata/10", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "London", + }, + expOut: "exp.json", + output: t8nOutput{alloc: true, result: true}, + }, + { // missing base fees + base: "./testdata/11", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "London", + }, + expExitCode: 3, + }, + { // EIP-1559 & gasCap + base: "./testdata/12", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "London", + }, + expOut: "exp.json", + output: t8nOutput{alloc: true, result: true}, + }, + { // Difficulty calculation on London + base: "./testdata/19", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "London", + }, + expOut: "exp_london.json", + output: t8nOutput{alloc: true, result: true}, + }, + { // Difficulty calculation on arrow glacier + base: "./testdata/19", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "ArrowGlacier", + }, + expOut: "exp_arrowglacier.json", + output: t8nOutput{alloc: true, result: true}, + }, + } { + + args := []string{"t8n"} + args = append(args, tc.output.get()...) + args = append(args, tc.input.get(tc.base)...) + var qArgs []string // quoted args for debugging purposes + for _, arg := range args { + if len(arg) == 0 { + qArgs = append(qArgs, `""`) + } else { + qArgs = append(qArgs, arg) + } + } + tt.Logf("args: %v\n", strings.Join(qArgs, " ")) + tt.Run("evm-test", args...) + // Compare the expected output, if provided + if tc.expOut != "" { + want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut)) + if err != nil { + t.Fatalf("test %d: could not read expected output: %v", i, err) + } + have := tt.Output() + ok, err := cmpJson(have, want) + switch { + case err != nil: + t.Fatalf("test %d, json parsing failed: %v", i, err) + case !ok: + t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want)) + } + } + tt.WaitExit() + if have, want := tt.ExitStatus(), tc.expExitCode; have != want { + t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want) + } + } +} + +// cmpJson compares the JSON in two byte slices. +func cmpJson(a, b []byte) (bool, error) { + var j, j2 interface{} + if err := json.Unmarshal(a, &j); err != nil { + return false, err + } + if err := json.Unmarshal(b, &j2); err != nil { + return false, err + } + + return reflect.DeepEqual(j2, j), nil +} diff --git a/cmd/evm/testdata/1/exp.json b/cmd/evm/testdata/1/exp.json new file mode 100644 index 00000000000..d8094e7aa67 --- /dev/null +++ b/cmd/evm/testdata/1/exp.json @@ -0,0 +1,45 @@ +{ + "alloc": { + "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192": { + "balance": "0xfeed1a9d", + "nonce": "0x1" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x5ffd4878be161d74", + "nonce": "0xac" + }, + "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x29a2241af62ca410" + } + }, + "result": { + "stateRoot": "0xe72f10cef9b1d32a16e2f5a8d64b25dacde99efcdea460387db527486582c3f7", + "txRoot": "0xc4761fd7b87ff2364c7c60b6c5c8d02e522e815328aaea3f20e3b7b7ef52c42d", + "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": [ + { + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0x5208", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x5208", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", + "transactionIndex": "0x0" + } + ], + "rejected": [ + { + "index": 1, + "error": "nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1" + } + ], + "currentDifficulty": "0x20000", + "gasUsed": "0x5208" + } +} diff --git a/cmd/evm/testdata/10/alloc.json b/cmd/evm/testdata/10/alloc.json new file mode 100644 index 00000000000..6e98e7513c4 --- /dev/null +++ b/cmd/evm/testdata/10/alloc.json @@ -0,0 +1,23 @@ +{ + "0x1111111111111111111111111111111111111111" : { + "balance" : "0x010000000000", + "code" : "0xfe", + "nonce" : "0x01", + "storage" : { + } + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "0x010000000000", + "code" : "0x", + "nonce" : "0x01", + "storage" : { + } + }, + "0xd02d72e067e77158444ef2020ff2d325f929b363" : { + "balance" : "0x01000000000000", + "code" : "0x", + "nonce" : "0x01", + "storage" : { + } + } +} \ No newline at end of file diff --git a/cmd/evm/testdata/10/env.json b/cmd/evm/testdata/10/env.json new file mode 100644 index 00000000000..3a82d46a774 --- /dev/null +++ b/cmd/evm/testdata/10/env.json @@ -0,0 +1,12 @@ +{ + "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "0x020000", + "currentNumber" : "0x01", + "currentTimestamp" : "0x079e", + "previousHash" : "0xcb23ee65a163121f640673b41788ee94633941405f95009999b502eedfbbfd4f", + "currentGasLimit" : "0x40000000", + "currentBaseFee" : "0x036b", + "blockHashes" : { + "0" : "0xcb23ee65a163121f640673b41788ee94633941405f95009999b502eedfbbfd4f" + } +} \ No newline at end of file diff --git a/cmd/evm/testdata/10/exp.json b/cmd/evm/testdata/10/exp.json new file mode 100644 index 00000000000..5ab98860c77 --- /dev/null +++ b/cmd/evm/testdata/10/exp.json @@ -0,0 +1,79 @@ +{ + "alloc": { + "0x1111111111111111111111111111111111111111": { + "code": "0xfe", + "balance": "0x10000000000", + "nonce": "0x1" + }, + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "balance": "0x1bc16d674ec80000" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x10000000000", + "nonce": "0x1" + }, + "0xd02d72e067e77158444ef2020ff2d325f929b363": { + "balance": "0xff5beffffc95", + "nonce": "0x4" + } + }, + "result": { + "stateRoot": "0x4b7b4d5dd6316b58407468a5d3cf0a18e42d3833911d3fccd80eb49273024ffa", + "txRoot": "0xda925f2306a52fa24c15d5cd212d736ee016415fd8dd0c45fd368de7917d64bb", + "receiptsRoot": "0x439a25f7fc424c10fb1f89800e4aa1df74156b137239d9ac3eaa7c911c353cd5", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": [ + { + "type": "0x2", + "root": "0x", + "status": "0x0", + "cumulativeGasUsed": "0x10000001", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0x88980f6efcc5358d9c359663e7b9414722d430497637340ea056b076bc206701", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x10000001", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", + "transactionIndex": "0x0" + }, + { + "type": "0x2", + "root": "0x", + "status": "0x0", + "cumulativeGasUsed": "0x20000001", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0xd7bf3886f4e2aef74d525ae072c680f3846f550254401b67cbfda4a233757582", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x10000000", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", + "transactionIndex": "0x1" + }, + { + "type": "0x2", + "root": "0x", + "status": "0x0", + "cumulativeGasUsed": "0x30000001", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0x50308296760f01f1eeec7500e9e73cad67469249b1f59e9a9f55e6625a4923db", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x10000000", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1", + "transactionIndex": "0x2" + } + ], + "rejected": [ + { + "index": 3, + "error": "gas limit reached" + } + ], + "currentDifficulty": "0x20000", + "gasUsed": "0x30000001" + } +} diff --git a/cmd/evm/testdata/10/readme.md b/cmd/evm/testdata/10/readme.md new file mode 100644 index 00000000000..c34be80bb71 --- /dev/null +++ b/cmd/evm/testdata/10/readme.md @@ -0,0 +1,79 @@ +## EIP-1559 testing + +This test contains testcases for EIP-1559, which were reported by Ori as misbehaving. + +``` +[user@work evm]$ dir=./testdata/10 && ./evm t8n --state.fork=London --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --output.alloc=stdout --output.result=stdout 2>&1 +INFO [05-09|22:11:59.436] rejected tx index=3 hash=db07bf..ede1e8 from=0xd02d72E067e77158444ef2020Ff2d325f929B363 error="gas limit reached" +``` +Output: +```json +{ + "alloc": { + "0x1111111111111111111111111111111111111111": { + "code": "0xfe", + "balance": "0x10000000000", + "nonce": "0x1" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x10000000000", + "nonce": "0x1" + }, + "0xd02d72e067e77158444ef2020ff2d325f929b363": { + "balance": "0xff5beffffc95", + "nonce": "0x4" + } + }, + "result": { + "stateRoot": "0xf91a7ec08e4bfea88719aab34deabb000c86902360532b52afa9599d41f2bb8b", + "txRoot": "0xda925f2306a52fa24c15d5cd212d736ee016415fd8dd0c45fd368de7917d64bb", + "receiptRoot": "0x439a25f7fc424c10fb1f89800e4aa1df74156b137239d9ac3eaa7c911c353cd5", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": [ + { + "type": "0x2", + "root": "0x", + "status": "0x0", + "cumulativeGasUsed": "0x10000001", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0x88980f6efcc5358d9c359663e7b9414722d430497637340ea056b076bc206701", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x10000001", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionIndex": "0x0" + }, + { + "type": "0x2", + "root": "0x", + "status": "0x0", + "cumulativeGasUsed": "0x20000001", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0xd7bf3886f4e2aef74d525ae072c680f3846f550254401b67cbfda4a233757582", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x10000000", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionIndex": "0x1" + }, + { + "type": "0x2", + "root": "0x", + "status": "0x0", + "cumulativeGasUsed": "0x30000001", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0x50308296760f01f1eeec7500e9e73cad67469249b1f59e9a9f55e6625a4923db", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x10000000", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionIndex": "0x2" + } + ], + "rejected": [ + 3 + ] + } +} +``` diff --git a/cmd/evm/testdata/10/txs.json b/cmd/evm/testdata/10/txs.json new file mode 100644 index 00000000000..f7c9baa26da --- /dev/null +++ b/cmd/evm/testdata/10/txs.json @@ -0,0 +1,70 @@ +[ + { + "input" : "0x", + "gas" : "0x10000001", + "nonce" : "0x1", + "to" : "0x1111111111111111111111111111111111111111", + "value" : "0x0", + "v" : "0x0", + "r" : "0x7a45f00bcde9036b026cdf1628b023cd8a31a95c62b5e4dbbee2fa7debe668fb", + "s" : "0x3cc9d6f2cd00a045b0263f2d6dad7d60938d5d13d061af4969f95928aa934d4a", + "secretKey" : "0x41f6e321b31e72173f8ff2e292359e1862f24fba42fe6f97efaf641980eff298", + "chainId" : "0x1", + "type" : "0x2", + "maxFeePerGas" : "0xfa0", + "maxPriorityFeePerGas" : "0x0", + "accessList" : [ + ] + }, + { + "input" : "0x", + "gas" : "0x10000000", + "nonce" : "0x2", + "to" : "0x1111111111111111111111111111111111111111", + "value" : "0x0", + "v" : "0x0", + "r" : "0x4c564b94b0281a8210eeec2dd1fe2e16ff1c1903a8c3a1078d735d7f8208b2af", + "s" : "0x56432b2593e6de95db1cb997b7385217aca03f1615327e231734446b39f266d", + "secretKey" : "0x41f6e321b31e72173f8ff2e292359e1862f24fba42fe6f97efaf641980eff298", + "chainId" : "0x1", + "type" : "0x2", + "maxFeePerGas" : "0xfa0", + "maxPriorityFeePerGas" : "0x0", + "accessList" : [ + ] + }, + { + "input" : "0x", + "gas" : "0x10000000", + "nonce" : "0x3", + "to" : "0x1111111111111111111111111111111111111111", + "value" : "0x0", + "v" : "0x0", + "r" : "0x2ed2ef52f924f59d4a21e1f2a50d3b1109303ce5e32334a7ece9b46f4fbc2a57", + "s" : "0x2980257129cbd3da987226f323d50ba3975a834d165e0681f991b75615605c44", + "secretKey" : "0x41f6e321b31e72173f8ff2e292359e1862f24fba42fe6f97efaf641980eff298", + "chainId" : "0x1", + "type" : "0x2", + "maxFeePerGas" : "0xfa0", + "maxPriorityFeePerGas" : "0x0", + "accessList" : [ + ] + }, + { + "input" : "0x", + "gas" : "0x10000000", + "nonce" : "0x4", + "to" : "0x1111111111111111111111111111111111111111", + "value" : "0x0", + "v" : "0x0", + "r" : "0x5df7d7f8f8e15b36fc9f189cacb625040fad10398d08fc90812595922a2c49b2", + "s" : "0x565fc1803f77a84d754ffe3c5363ab54a8d93a06ea1bb9d4c73c73a282b35917", + "secretKey" : "0x41f6e321b31e72173f8ff2e292359e1862f24fba42fe6f97efaf641980eff298", + "chainId" : "0x1", + "type" : "0x2", + "maxFeePerGas" : "0xfa0", + "maxPriorityFeePerGas" : "0x0", + "accessList" : [ + ] + } +] \ No newline at end of file diff --git a/cmd/evm/testdata/11/alloc.json b/cmd/evm/testdata/11/alloc.json new file mode 100644 index 00000000000..86938230fa7 --- /dev/null +++ b/cmd/evm/testdata/11/alloc.json @@ -0,0 +1,25 @@ +{ + "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "0x0de0b6b3a7640000", + "code" : "0x61ffff5060046000f3", + "nonce" : "0x01", + "storage" : { + } + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "0x0de0b6b3a7640000", + "code" : "0x", + "nonce" : "0x00", + "storage" : { + "0x00" : "0x00" + } + }, + "0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "0x00", + "code" : "0x6001600055", + "nonce" : "0x00", + "storage" : { + } + } +} + diff --git a/cmd/evm/testdata/11/env.json b/cmd/evm/testdata/11/env.json new file mode 100644 index 00000000000..37dedf09475 --- /dev/null +++ b/cmd/evm/testdata/11/env.json @@ -0,0 +1,12 @@ +{ + "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "0x020000", + "currentNumber" : "0x01", + "currentTimestamp" : "0x03e8", + "previousHash" : "0xfda4419b3660e99f37e536dae1ab081c180136bb38c837a93e93d9aab58553b2", + "currentGasLimit" : "0x0f4240", + "blockHashes" : { + "0" : "0xfda4419b3660e99f37e536dae1ab081c180136bb38c837a93e93d9aab58553b2" + } +} + diff --git a/cmd/evm/testdata/11/readme.md b/cmd/evm/testdata/11/readme.md new file mode 100644 index 00000000000..d499f8e99fa --- /dev/null +++ b/cmd/evm/testdata/11/readme.md @@ -0,0 +1,13 @@ +## Test missing basefee + +In this test, the `currentBaseFee` is missing from the env portion. +On a live blockchain, the basefee is present in the header, and verified as part of header validation. + +In `evm t8n`, we don't have blocks, so it needs to be added in the `env`instead. + +When it's missing, an error is expected. + +``` +dir=./testdata/11 && ./evm t8n --state.fork=London --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --output.alloc=stdout --output.result=stdout 2>&1>/dev/null +ERROR(3): EIP-1559 config but missing 'currentBaseFee' in env section +``` \ No newline at end of file diff --git a/cmd/evm/testdata/11/txs.json b/cmd/evm/testdata/11/txs.json new file mode 100644 index 00000000000..c54b0a1f5b4 --- /dev/null +++ b/cmd/evm/testdata/11/txs.json @@ -0,0 +1,14 @@ +[ + { + "input" : "0x38600060013960015160005560006000f3", + "gas" : "0x61a80", + "gasPrice" : "0x1", + "nonce" : "0x0", + "value" : "0x186a0", + "v" : "0x1c", + "r" : "0x2e1391fd903387f1cc2b51df083805fb4bbb0d4710a2cdf4a044d191ff7be63e", + "s" : "0x7f10a933c42ab74927db02b1db009e923d9d2ab24ac24d63c399f2fe5d9c9b22", + "secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + } +] + diff --git a/cmd/evm/testdata/12/alloc.json b/cmd/evm/testdata/12/alloc.json new file mode 100644 index 00000000000..3ed96894fbc --- /dev/null +++ b/cmd/evm/testdata/12/alloc.json @@ -0,0 +1,11 @@ +{ + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "84000000", + "code" : "0x", + "nonce" : "0x00", + "storage" : { + "0x00" : "0x00" + } + } +} + diff --git a/cmd/evm/testdata/12/env.json b/cmd/evm/testdata/12/env.json new file mode 100644 index 00000000000..8ae5465369c --- /dev/null +++ b/cmd/evm/testdata/12/env.json @@ -0,0 +1,10 @@ +{ + "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "0x020000", + "currentNumber" : "0x01", + "currentTimestamp" : "0x03e8", + "previousHash" : "0xfda4419b3660e99f37e536dae1ab081c180136bb38c837a93e93d9aab58553b2", + "currentGasLimit" : "0x0f4240", + "currentBaseFee" : "0x20" +} + diff --git a/cmd/evm/testdata/12/exp.json b/cmd/evm/testdata/12/exp.json new file mode 100644 index 00000000000..0589c867585 --- /dev/null +++ b/cmd/evm/testdata/12/exp.json @@ -0,0 +1,26 @@ +{ + "alloc": { + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "balance": "0x1bc16d674ec80000" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x501bd00" + } + }, + "result": { + "stateRoot": "0x9fd6c7f520a9e9a160c19d65b929161415bc4e86ea75e7c9cac4fe8f776cf453", + "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": null, + "rejected": [ + { + "index": 0, + "error": "insufficient funds for gas * price + value: address 0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B have 84000000 want 84000032" + } + ], + "currentDifficulty": "0x20000", + "gasUsed": "0x0" + } +} diff --git a/cmd/evm/testdata/12/readme.md b/cmd/evm/testdata/12/readme.md new file mode 100644 index 00000000000..b0177ecc24b --- /dev/null +++ b/cmd/evm/testdata/12/readme.md @@ -0,0 +1,40 @@ +## Test 1559 balance + gasCap + +This test contains an EIP-1559 consensus issue which happened on Ropsten, where +`geth` did not properly account for the value transfer while doing the check on `max_fee_per_gas * gas_limit`. + +Before the issue was fixed, this invocation allowed the transaction to pass into a block: +``` +dir=./testdata/12 && ./evm t8n --state.fork=London --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --output.alloc=stdout --output.result=stdout +``` + +With the fix applied, the result is: +``` +dir=./testdata/12 && ./evm t8n --state.fork=London --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --output.alloc=stdout --output.result=stdout +INFO [07-21|19:03:50.276] rejected tx index=0 hash=ccc996..d83435 from=0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B error="insufficient funds for gas * price + value: address 0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B have 84000000 want 84000032" +INFO [07-21|19:03:50.276] Trie dumping started root=e05f81..6597a5 +INFO [07-21|19:03:50.276] Trie dumping complete accounts=1 elapsed="39.549Āµs" +{ + "alloc": { + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x501bd00" + } + }, + "result": { + "stateRoot": "0xe05f81f8244a76503ceec6f88abfcd03047a612a1001217f37d30984536597a5", + "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": [], + "rejected": [ + { + "index": 0, + "error": "insufficient funds for gas * price + value: address 0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B have 84000000 want 84000032" + } + ] + } +} +``` + +The transaction is rejected. \ No newline at end of file diff --git a/cmd/evm/testdata/12/txs.json b/cmd/evm/testdata/12/txs.json new file mode 100644 index 00000000000..cd683f271c7 --- /dev/null +++ b/cmd/evm/testdata/12/txs.json @@ -0,0 +1,20 @@ +[ + { + "input" : "0x", + "gas" : "0x5208", + "nonce" : "0x0", + "to" : "0x1111111111111111111111111111111111111111", + "value" : "0x20", + "v" : "0x0", + "r" : "0x0", + "s" : "0x0", + "secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "chainId" : "0x1", + "type" : "0x2", + "maxFeePerGas" : "0xfa0", + "maxPriorityFeePerGas" : "0x20", + "accessList" : [ + ] + } +] + diff --git a/cmd/evm/testdata/19/alloc.json b/cmd/evm/testdata/19/alloc.json new file mode 100644 index 00000000000..cef1a25ff01 --- /dev/null +++ b/cmd/evm/testdata/19/alloc.json @@ -0,0 +1,12 @@ +{ + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x5ffd4878be161d74", + "code": "0x", + "nonce": "0xac", + "storage": {} + }, + "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192":{ + "balance": "0xfeedbead", + "nonce" : "0x00" + } +} \ No newline at end of file diff --git a/cmd/evm/testdata/19/env.json b/cmd/evm/testdata/19/env.json new file mode 100644 index 00000000000..0c64392aff5 --- /dev/null +++ b/cmd/evm/testdata/19/env.json @@ -0,0 +1,9 @@ +{ + "currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentGasLimit": "0x750a163df65e8a", + "currentBaseFee": "0x500", + "currentNumber": "13000000", + "currentTimestamp": "100015", + "parentTimestamp" : "99999", + "parentDifficulty" : "0x2000000000000" +} diff --git a/cmd/evm/testdata/19/exp_arrowglacier.json b/cmd/evm/testdata/19/exp_arrowglacier.json new file mode 100644 index 00000000000..85506a07ae9 --- /dev/null +++ b/cmd/evm/testdata/19/exp_arrowglacier.json @@ -0,0 +1,24 @@ +{ + "alloc": { + "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192": { + "balance": "0xfeedbead" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x5ffd4878be161d74", + "nonce": "0xac" + }, + "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x1bc16d674ec80000" + } + }, + "result": { + "stateRoot": "0x374cbd5c614cb6ef173024d1c0d4e0313dafc2d7fc8f4399cf4bd1b60fc7c2ca", + "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": null, + "currentDifficulty": "0x2000000200000", + "gasUsed": "0x0" + } +} diff --git a/cmd/evm/testdata/19/exp_london.json b/cmd/evm/testdata/19/exp_london.json new file mode 100644 index 00000000000..10e11aedd5b --- /dev/null +++ b/cmd/evm/testdata/19/exp_london.json @@ -0,0 +1,24 @@ +{ + "alloc": { + "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192": { + "balance": "0xfeedbead" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x5ffd4878be161d74", + "nonce": "0xac" + }, + "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x1bc16d674ec80000" + } + }, + "result": { + "stateRoot": "0x374cbd5c614cb6ef173024d1c0d4e0313dafc2d7fc8f4399cf4bd1b60fc7c2ca", + "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": null, + "currentDifficulty": "0x2000080000000", + "gasUsed": "0x0" + } +} diff --git a/cmd/evm/testdata/19/readme.md b/cmd/evm/testdata/19/readme.md new file mode 100644 index 00000000000..5fae183f488 --- /dev/null +++ b/cmd/evm/testdata/19/readme.md @@ -0,0 +1,9 @@ +## Difficulty calculation + +This test shows how the `evm t8n` can be used to calculate the (ethash) difficulty, if none is provided by the caller, +this time on `ArrowGlacier` (Eip 4345). + +Calculating it (with an empty set of txs) using `ArrowGlacier` rules (and no provided unclehash for the parent block): +``` +[user@work evm]$ ./evm t8n --input.alloc=./testdata/14/alloc.json --input.txs=./testdata/14/txs.json --input.env=./testdata/14/env.json --output.result=stdout --state.fork=ArrowGlacier +``` \ No newline at end of file diff --git a/cmd/evm/testdata/19/txs.json b/cmd/evm/testdata/19/txs.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/cmd/evm/testdata/19/txs.json @@ -0,0 +1 @@ +[] diff --git a/cmd/evm/testdata/3/exp.json b/cmd/evm/testdata/3/exp.json new file mode 100644 index 00000000000..5b8b7c84ebc --- /dev/null +++ b/cmd/evm/testdata/3/exp.json @@ -0,0 +1,39 @@ +{ + "alloc": { + "0x095e7baea6a6c7c4c2dfeb977efac326af552d87": { + "code": "0x600140", + "balance": "0xde0b6b3a76586a0" + }, + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "balance": "0x1bc16d674ec8521f" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xde0b6b3a7622741", + "nonce": "0x1" + } + }, + "result": { + "stateRoot": "0x5aeefb3e8fe1d722455ff4b4ee76793af2c654f7f5120b79a8427d696ed01558", + "txRoot": "0x75e61774a2ff58cbe32653420256c7f44bc715715a423b0b746d5c622979af6b", + "receiptsRoot": "0xd0d26df80374a327c025d405ebadc752b1bbd089d864801ae78ab704bcad8086", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": [ + { + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0x521f", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x521f", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x5", + "transactionIndex": "0x0" + } + ], + "currentDifficulty": "0x20000", + "gasUsed": "0x521f" + } +} diff --git a/cmd/evm/testdata/5/exp.json b/cmd/evm/testdata/5/exp.json new file mode 100644 index 00000000000..1815d430588 --- /dev/null +++ b/cmd/evm/testdata/5/exp.json @@ -0,0 +1,23 @@ +{ + "alloc": { + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": { + "balance": "0x2c3c465ca58ec000" + }, + "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": { + "balance": "0x246ddf9797668000" + }, + "0xcccccccccccccccccccccccccccccccccccccccc": { + "balance": "0x1f399b1438a10000" + } + }, + "result": { + "stateRoot": "0x5069e6c86aeba39397685cf7914a7505a78059be8c5f4d1348050ce78b348e99", + "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": null, + "currentDifficulty": "0x20000", + "gasUsed": "0x0" + } +} diff --git a/cmd/evm/testdata/7/exp.json b/cmd/evm/testdata/7/exp.json new file mode 100644 index 00000000000..e5b41ef0d21 --- /dev/null +++ b/cmd/evm/testdata/7/exp.json @@ -0,0 +1,375 @@ +{ + "alloc": { + "0x005f5cee7a43331d5a3d3eec71305925a62f34b6": { + "balance": "0x0" + }, + "0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9": { + "balance": "0x0" + }, + "0x057b56736d32b86616a10f619859c6cd6f59092a": { + "balance": "0x0" + }, + "0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936": { + "balance": "0x0" + }, + "0x0737a6b837f97f46ebade41b9bc3e1c509c85c53": { + "balance": "0x0" + }, + "0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a": { + "balance": "0x0" + }, + "0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d": { + "balance": "0x0" + }, + "0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00": { + "balance": "0x0" + }, + "0x12e626b0eebfe86a56d633b9864e389b45dcb260": { + "balance": "0x0" + }, + "0x1591fc0f688c81fbeb17f5426a162a7024d430c2": { + "balance": "0x0" + }, + "0x17802f43a0137c506ba92291391a8a8f207f487d": { + "balance": "0x0" + }, + "0x1975bd06d486162d5dc297798dfc41edd5d160a7": { + "balance": "0x0" + }, + "0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b": { + "balance": "0x0" + }, + "0x1cba23d343a983e9b5cfd19496b9a9701ada385f": { + "balance": "0x0" + }, + "0x200450f06520bdd6c527622a273333384d870efb": { + "balance": "0x0" + }, + "0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241": { + "balance": "0x0" + }, + "0x23b75c2f6791eef49c69684db4c6c1f93bf49a50": { + "balance": "0x0" + }, + "0x24c4d950dfd4dd1902bbed3508144a54542bba94": { + "balance": "0x0" + }, + "0x253488078a4edf4d6f42f113d1e62836a942cf1a": { + "balance": "0x0" + }, + "0x27b137a85656544b1ccb5a0f2e561a5703c6a68f": { + "balance": "0x0" + }, + "0x2a5ed960395e2a49b1c758cef4aa15213cfd874c": { + "balance": "0x0" + }, + "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2": { + "balance": "0x0" + }, + "0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f": { + "balance": "0x0" + }, + "0x304a554a310c7e546dfe434669c62820b7d83490": { + "balance": "0x0" + }, + "0x319f70bab6845585f412ec7724b744fec6095c85": { + "balance": "0x0" + }, + "0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b": { + "balance": "0x0" + }, + "0x3ba4d81db016dc2890c81f3acec2454bff5aada5": { + "balance": "0x0" + }, + "0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5": { + "balance": "0x0" + }, + "0x40b803a9abce16f50f36a77ba41180eb90023925": { + "balance": "0x0" + }, + "0x440c59b325d2997a134c2c7c60a8c61611212bad": { + "balance": "0x0" + }, + "0x4486a3d68fac6967006d7a517b889fd3f98c102b": { + "balance": "0x0" + }, + "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a": { + "balance": "0x0" + }, + "0x47e7aa56d6bdf3f36be34619660de61275420af8": { + "balance": "0x0" + }, + "0x4863226780fe7c0356454236d3b1c8792785748d": { + "balance": "0x0" + }, + "0x492ea3bb0f3315521c31f273e565b868fc090f17": { + "balance": "0x0" + }, + "0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c": { + "balance": "0x0" + }, + "0x4deb0033bb26bc534b197e61d19e0733e5679784": { + "balance": "0x0" + }, + "0x4fa802324e929786dbda3b8820dc7834e9134a2a": { + "balance": "0x0" + }, + "0x4fd6ace747f06ece9c49699c7cabc62d02211f75": { + "balance": "0x0" + }, + "0x51e0ddd9998364a2eb38588679f0d2c42653e4a6": { + "balance": "0x0" + }, + "0x52c5317c848ba20c7504cb2c8052abd1fde29d03": { + "balance": "0x0" + }, + "0x542a9515200d14b68e934e9830d91645a980dd7a": { + "balance": "0x0" + }, + "0x5524c55fb03cf21f549444ccbecb664d0acad706": { + "balance": "0x0" + }, + "0x579a80d909f346fbfb1189493f521d7f48d52238": { + "balance": "0x0" + }, + "0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb": { + "balance": "0x0" + }, + "0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5": { + "balance": "0x0" + }, + "0x5c8536898fbb74fc7445814902fd08422eac56d0": { + "balance": "0x0" + }, + "0x5d2b2e6fcbe3b11d26b525e085ff818dae332479": { + "balance": "0x0" + }, + "0x5dc28b15dffed94048d73806ce4b7a4612a1d48f": { + "balance": "0x0" + }, + "0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c": { + "balance": "0x0" + }, + "0x6131c42fa982e56929107413a9d526fd99405560": { + "balance": "0x0" + }, + "0x6231b6d0d5e77fe001c2a460bd9584fee60d409b": { + "balance": "0x0" + }, + "0x627a0a960c079c21c34f7612d5d230e01b4ad4c7": { + "balance": "0x0" + }, + "0x63ed5a272de2f6d968408b4acb9024f4cc208ebf": { + "balance": "0x0" + }, + "0x6966ab0d485353095148a2155858910e0965b6f9": { + "balance": "0x0" + }, + "0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb": { + "balance": "0x0" + }, + "0x6d87578288b6cb5549d5076a207456a1f6a63dc0": { + "balance": "0x0" + }, + "0x6f6704e5a10332af6672e50b3d9754dc460dfa4d": { + "balance": "0x0" + }, + "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97": { + "balance": "0x0" + }, + "0x779543a0491a837ca36ce8c635d6154e3c4911a6": { + "balance": "0x0" + }, + "0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6": { + "balance": "0x0" + }, + "0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4": { + "balance": "0x0" + }, + "0x807640a13483f8ac783c557fcdf27be11ea4ac7a": { + "balance": "0x0" + }, + "0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd": { + "balance": "0x0" + }, + "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106": { + "balance": "0x0" + }, + "0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915": { + "balance": "0x0" + }, + "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192": { + "balance": "0xfeedbead" + }, + "0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6": { + "balance": "0x0" + }, + "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79": { + "balance": "0x0" + }, + "0x97f43a37f595ab5dd318fb46e7a155eae057317a": { + "balance": "0x0" + }, + "0x9aa008f65de0b923a2a4f02012ad034a5e2e2192": { + "balance": "0x0" + }, + "0x9c15b54878ba618f494b38f0ae7443db6af648ba": { + "balance": "0x0" + }, + "0x9c50426be05db97f5d64fc54bf89eff947f0a321": { + "balance": "0x0" + }, + "0x9da397b9e80755301a3b32173283a91c0ef6c87e": { + "balance": "0x0" + }, + "0x9ea779f907f0b315b364b0cfc39a0fde5b02a416": { + "balance": "0x0" + }, + "0x9f27daea7aca0aa0446220b98d028715e3bc803d": { + "balance": "0x0" + }, + "0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339": { + "balance": "0x0" + }, + "0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7": { + "balance": "0x0" + }, + "0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6": { + "balance": "0x0" + }, + "0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90": { + "balance": "0x0" + }, + "0xa82f360a8d3455c5c41366975bde739c37bfeb8a": { + "balance": "0x0" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x5ffd4878be161d74", + "nonce": "0xac" + }, + "0xac1ecab32727358dba8962a0f3b261731aad9723": { + "balance": "0x0" + }, + "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6": { + "balance": "0x0" + }, + "0xacd87e28b0c9d1254e868b81cba4cc20d9a32225": { + "balance": "0x0" + }, + "0xadf80daec7ba8dcf15392f1ac611fff65d94f880": { + "balance": "0x0" + }, + "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c": { + "balance": "0x0" + }, + "0xb136707642a4ea12fb4bae820f03d2562ebff487": { + "balance": "0x0" + }, + "0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e": { + "balance": "0x0" + }, + "0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425": { + "balance": "0x0" + }, + "0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab": { + "balance": "0x0" + }, + "0xb9637156d330c0d605a791f1c31ba5890582fe1c": { + "balance": "0x0" + }, + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413": { + "balance": "0x0" + }, + "0xbc07118b9ac290e4622f5e77a0853539789effbe": { + "balance": "0x0" + }, + "0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76": { + "balance": "0x0" + }, + "0xbe8539bfe837b67d1282b2b1d61c3f723966f049": { + "balance": "0x0" + }, + "0xbf4ed7b27f1d666546e30d74d50d173d20bca754": { + "balance": "0x0" + }, + "0xc4bbd073882dd2add2424cf47d35213405b01324": { + "balance": "0x0" + }, + "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x4563918244f40000" + }, + "0xca544e5c4687d109611d0f8f928b53a25af72448": { + "balance": "0x0" + }, + "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7": { + "balance": "0x0" + }, + "0xcc34673c6c40e791051898567a1222daf90be287": { + "balance": "0x0" + }, + "0xceaeb481747ca6c540a000c1f3641f8cef161fa7": { + "balance": "0x0" + }, + "0xd131637d5275fd1a68a3200f4ad25c71a2a9522e": { + "balance": "0x0" + }, + "0xd164b088bd9108b60d0ca3751da4bceb207b0782": { + "balance": "0x0" + }, + "0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091": { + "balance": "0x0" + }, + "0xd343b217de44030afaa275f54d31a9317c7f441e": { + "balance": "0x0" + }, + "0xd4fe7bc31cedb7bfb8a345f31e668033056b2728": { + "balance": "0x0" + }, + "0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b": { + "balance": "0x0" + }, + "0xda2fef9e4a3230988ff17df2165440f37e8b1708": { + "balance": "0x0" + }, + "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940": { + "balance": "0x0" + }, + "0xe308bd1ac5fda103967359b2712dd89deffb7973": { + "balance": "0x0" + }, + "0xe4ae1efdfc53b73893af49113d8694a057b9c0d1": { + "balance": "0x0" + }, + "0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5": { + "balance": "0x0" + }, + "0xecd135fa4f61a655311e86238c92adcd779555d2": { + "balance": "0x0" + }, + "0xf0b1aa0eb660754448a7937c022e30aa692fe0c5": { + "balance": "0x0" + }, + "0xf1385fb24aad0cd7432824085e42aff90886fef5": { + "balance": "0x0" + }, + "0xf14c14075d6c4ed84b86798af0956deef67365b5": { + "balance": "0x0" + }, + "0xf4c64518ea10f995918a454158c6b61407ea345c": { + "balance": "0x0" + }, + "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f": { + "balance": "0x0" + } + }, + "result": { + "stateRoot": "0xd320ae476350b8107b9b78d45d73f539cc363e7e588d8c794666515d852f6e81", + "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": null, + "currentDifficulty": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000", + "gasUsed": "0x0" + } +} diff --git a/cmd/evm/testdata/8/exp.json b/cmd/evm/testdata/8/exp.json new file mode 100644 index 00000000000..2d44c071be7 --- /dev/null +++ b/cmd/evm/testdata/8/exp.json @@ -0,0 +1,68 @@ +{ + "alloc": { + "0x000000000000000000000000000000000000aaaa": { + "code": "0x5854505854", + "balance": "0x7", + "nonce": "0x1" + }, + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "balance": "0x1bc16d674ec94832" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xeb7ca", + "nonce": "0x3" + } + }, + "result": { + "stateRoot": "0xb78515d83d9ad63ae2740f09f21bb6b44e9041e18b606a3ed35dd6cfd338c0bb", + "txRoot": "0xe42c488908c04b9f7d4d39614ed4093a33ff16353299672e1770b786c28a5e6f", + "receiptsRoot": "0xb207f384195fb6fb7ee7105ba963cc19e1614ce0e75809999289c6c82e7a8d97", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": [ + { + "type": "0x1", + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0x7aae", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0x26c8c6e23fa3b246f44fba53e7b5fcb55f01f1e075f2de3db9b982afd4bd3901", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x7aae", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1000000", + "transactionIndex": "0x0" + }, + { + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0xdd24", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0x26ea003b1188334eced68a720dbe89886cd6a477cccdf924cf1d392e2281c01b", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x6276", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1000000", + "transactionIndex": "0x1" + }, + { + "type": "0x1", + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0x14832", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0x6997569ed85f1d810bc61d969cbbae12f34ce88d314ff5ef2629bc741466fca6", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x6b0e", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1000000", + "transactionIndex": "0x2" + } + ], + "currentDifficulty": "0x20000", + "gasUsed": "0x14832" + } +} diff --git a/cmd/evm/testdata/9/alloc.json b/cmd/evm/testdata/9/alloc.json index 430e4242732..c14e38e8451 100644 --- a/cmd/evm/testdata/9/alloc.json +++ b/cmd/evm/testdata/9/alloc.json @@ -1,19 +1,11 @@ { - "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { - "balance": "0x100000000000000000", - "nonce": "0x00" - }, - "0x00000000000000000000000000000000b0b0face": { - "code":"0x40600052", - "storage":{}, - "balance":"0x0", - "nonce": - "0x0" - }, - "0x000000000000000000000000000000ca1100f022": { - "code":"0x60806040527f248f18b25d9b5856c092f62a7d329b239f4a0a77e6ee6c58637f56745b9803f3446040518082815260200191505060405180910390a100fea265627a7a72315820eea50cf12e938601a56dcdef0ab1446f14ba25367299eb81834af54e1672f5d864736f6c63430005110032", - "storage":{}, - "balance":"0x0", - "nonce":"0x0" - } - } \ No newline at end of file + "0x000000000000000000000000000000000000aaaa": { + "balance": "0x03", + "code": "0x58585454", + "nonce": "0x1" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x100000000000000", + "nonce": "0x00" + } +} diff --git a/cmd/evm/testdata/9/env.json b/cmd/evm/testdata/9/env.json index 479d8a3f47d..05f35191fd8 100644 --- a/cmd/evm/testdata/9/env.json +++ b/cmd/evm/testdata/9/env.json @@ -1,8 +1,9 @@ { - "currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - "currentDifficulty": "0x20000", - "currentGasLimit": "0x1000000000", - "currentNumber": "0x1000000", - "currentTimestamp": "0x04", - "currentRandom": "0x1000000000000000000000000000000000000000000000000000000000000001" - } \ No newline at end of file + "currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty": "0x20000", + "currentGasTarget": "0x1000000000", + "currentGasLimit": "0x750a163df65e8a", + "currentBaseFee": "0x3B9ACA00", + "currentNumber": "0x1000000", + "currentTimestamp": "0x04" +} diff --git a/cmd/evm/testdata/9/exp.json b/cmd/evm/testdata/9/exp.json new file mode 100644 index 00000000000..53a1bfd4d91 --- /dev/null +++ b/cmd/evm/testdata/9/exp.json @@ -0,0 +1,54 @@ +{ + "alloc": { + "0x000000000000000000000000000000000000aaaa": { + "code": "0x58585454", + "balance": "0x3", + "nonce": "0x1" + }, + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "balance": "0x1bc1c9185ca6f6e0" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xff745ee8832120", + "nonce": "0x2" + } + }, + "result": { + "stateRoot": "0x8e0c14cca1717d764e5cd25569bdf079758d704bb8ba56a3827997842f135ad8", + "txRoot": "0xbe6c599aefbec1cfe31dbdeca4b4dd0315bf5fca0f78e10c8f869c40a42feb0d", + "receiptsRoot": "0x5fdadbccc0b40ed39f6c7aacafb08a71c468f28793027552d9d99b1aeb19d406", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": [ + { + "type": "0x2", + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0x6b70", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0xb4821e4a9122a6f9baecad99351bee6ec54fe8c3f6a737b2e6478f4963536819", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x6b70", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1000000", + "transactionIndex": "0x0" + }, + { + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0xcde4", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0xa9c6c6a848b9c9a0d8bbb4df5f30394983632817dbccc738e839c8e174fa4036", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x6274", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": "0x1000000", + "transactionIndex": "0x1" + } + ], + "currentDifficulty": "0x20000", + "gasUsed": "0xcde4" + } +} diff --git a/cmd/evm/testdata/9/readme.md b/cmd/evm/testdata/9/readme.md new file mode 100644 index 00000000000..88f0f12aaaa --- /dev/null +++ b/cmd/evm/testdata/9/readme.md @@ -0,0 +1,75 @@ +## EIP-1559 testing + +This test contains testcases for EIP-1559, which uses an new transaction type and has a new block parameter. + +### Prestate + +The alloc portion contains one contract (`0x000000000000000000000000000000000000aaaa`), containing the +following code: `0x58585454`: `PC; PC; SLOAD; SLOAD`. + +Essentialy, this contract does `SLOAD(0)` and `SLOAD(1)`. + +The alloc also contains some funds on `0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b`. + +## Transactions + +There are two transactions, each invokes the contract above. + +1. EIP-1559 ACL-transaction, which contains the `0x0` slot for `0xaaaa` +2. Legacy transaction + +## Execution + +Running it yields: +``` +$ dir=./testdata/9 && ./evm t8n --state.fork=London --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --trace && cat trace-* | grep SLOAD +{"pc":2,"op":84,"gas":"0x48c28","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x0","0x1"],"returnStack":null,"returnD +ata":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +{"pc":3,"op":84,"gas":"0x483f4","gasCost":"0x64","memory":"0x","memSize":0,"stack":["0x0","0x0"],"returnStack":null,"returnDa +ta":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +{"pc":2,"op":84,"gas":"0x49cf4","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x0","0x1"],"returnStack":null,"returnD +ata":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +{"pc":3,"op":84,"gas":"0x494c0","gasCost":"0x834","memory":"0x","memSize":0,"stack":["0x0","0x0"],"returnStack":null,"returnD +ata":"0x","depth":1,"refund":0,"opName":"SLOAD","error":""} +``` + +We can also get the post-alloc: +``` +$ dir=./testdata/9 && ./evm t8n --state.fork=London --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --output.alloc=stdout +{ + "alloc": { + "0x000000000000000000000000000000000000aaaa": { + "code": "0x58585454", + "balance": "0x3", + "nonce": "0x1" + }, + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "balance": "0xbfc02677a000" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0xff104fcfea7800", + "nonce": "0x2" + } + } +} +``` + +If we try to execute it on older rules: +``` +dir=./testdata/9 && ./evm t8n --state.fork=Berlin --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --output.alloc=stdout +ERROR(10): Failed signing transactions: ERROR(10): Tx 0: failed to sign tx: transaction type not supported +``` + +It fails, due to the `evm t8n` cannot sign them in with the given signer. We can bypass that, however, +by feeding it presigned transactions, located in `txs_signed.json`. + +``` +dir=./testdata/9 && ./evm t8n --state.fork=Berlin --input.alloc=$dir/alloc.json --input.txs=$dir/txs_signed.json --input.env=$dir/env.json +INFO [05-07|12:28:42.072] rejected tx index=0 hash=b4821e..536819 error="transaction type not supported" +INFO [05-07|12:28:42.072] rejected tx index=1 hash=a9c6c6..fa4036 from=0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B error="nonce too high: address 0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B, tx: 1 state: 0" +INFO [05-07|12:28:42.073] Wrote file file=alloc.json +INFO [05-07|12:28:42.073] Wrote file file=result.json +``` + +Number `0` is not applicable, and therefore number `1` has wrong nonce, and both are rejected. + diff --git a/cmd/evm/testdata/9/txs.json b/cmd/evm/testdata/9/txs.json index 7f15b0b2215..740abce079d 100644 --- a/cmd/evm/testdata/9/txs.json +++ b/cmd/evm/testdata/9/txs.json @@ -1,14 +1,37 @@ [ - { - "gasPrice":"0x80", - "nonce":"0x0", - "to":"0x000000000000000000000000000000ca1100f022", - "input": "", - "gas":"0x1312d00", - "value": "0x0", - "v": "0x0", - "r": "0x0", - "s": "0x0", - "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" - } -] \ No newline at end of file + { + "gas": "0x4ef00", + "maxPriorityFeePerGas": "0x2", + "maxFeePerGas": "0x12A05F200", + "chainId": "0x1", + "input": "0x", + "nonce": "0x0", + "to": "0x000000000000000000000000000000000000aaaa", + "value": "0x0", + "type" : "0x2", + "accessList": [ + {"address": "0x000000000000000000000000000000000000aaaa", + "storageKeys": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ] + } + ], + "v": "0x0", + "r": "0x0", + "s": "0x0", + "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + }, + { + "gas": "0x4ef00", + "gasPrice": "0x12A05F200", + "chainId": "0x1", + "input": "0x", + "nonce": "0x1", + "to": "0x000000000000000000000000000000000000aaaa", + "value": "0x0", + "v": "0x0", + "r": "0x0", + "s": "0x0", + "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + } +] diff --git a/cmd/integration/commands/state_stages.go b/cmd/integration/commands/state_stages.go index 1edb32fcbe7..449eaebf1fc 100644 --- a/cmd/integration/commands/state_stages.go +++ b/cmd/integration/commands/state_stages.go @@ -17,7 +17,6 @@ import ( "github.com/ledgerwatch/erigon/common/changeset" "github.com/ledgerwatch/erigon/common/dbutils" "github.com/ledgerwatch/erigon/common/debugprint" - "github.com/ledgerwatch/erigon/core" "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/core/state" "github.com/ledgerwatch/erigon/core/types" @@ -219,7 +218,7 @@ func syncBySmallSteps(db kv.RwDB, miningConfig params.MiningConfig, ctx context. } encoder := json.NewEncoder(w) encoder.SetIndent(" ", " ") - for _, l := range core.FormatLogs(vmConfig.Tracer.(*vm.StructLogger).StructLogs()) { + for _, l := range vm.FormatLogs(vmConfig.Tracer.(*vm.StructLogger).StructLogs()) { if err2 := encoder.Encode(l); err2 != nil { panic(err2) } diff --git a/cmd/rpcdaemon/commands/eth_receipts.go b/cmd/rpcdaemon/commands/eth_receipts.go index 8b41227542a..5ebc6fc0f9c 100644 --- a/cmd/rpcdaemon/commands/eth_receipts.go +++ b/cmd/rpcdaemon/commands/eth_receipts.go @@ -59,7 +59,8 @@ func (api *BaseAPI) getReceipts(ctx context.Context, tx kv.Tx, chainConfig *para for i, txn := range block.Transactions() { ibs.Prepare(txn.Hash(), block.Hash(), i) - receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, ethashFaker, nil, gp, ibs, noopWriter, block.Header(), txn, usedGas, vm.Config{}, contractHasTEVM) + header := block.Header() + receipt, _, err := core.ApplyTransaction(chainConfig, core.GetHashFn(header, getHeader), ethashFaker, nil, gp, ibs, noopWriter, header, txn, usedGas, vm.Config{}, contractHasTEVM) if err != nil { return nil, err } diff --git a/cmd/rpcdaemon22/commands/eth_receipts.go b/cmd/rpcdaemon22/commands/eth_receipts.go index bdd74f1017d..a1c313a9547 100644 --- a/cmd/rpcdaemon22/commands/eth_receipts.go +++ b/cmd/rpcdaemon22/commands/eth_receipts.go @@ -56,7 +56,8 @@ func (api *BaseAPI) getReceipts(ctx context.Context, tx kv.Tx, chainConfig *para for i, txn := range block.Transactions() { ibs.Prepare(txn.Hash(), block.Hash(), i) - receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, ethashFaker, nil, gp, ibs, noopWriter, block.Header(), txn, usedGas, vm.Config{}, contractHasTEVM) + header := block.Header() + receipt, _, err := core.ApplyTransaction(chainConfig, core.GetHashFn(header, getHeader), ethashFaker, nil, gp, ibs, noopWriter, header, txn, usedGas, vm.Config{}, contractHasTEVM) if err != nil { return nil, err } diff --git a/cmd/state/commands/erigon2.go b/cmd/state/commands/erigon2.go index 934f79cac63..370433ecaa3 100644 --- a/cmd/state/commands/erigon2.go +++ b/cmd/state/commands/erigon2.go @@ -412,7 +412,7 @@ func processBlock(trace bool, txNumStart uint64, rw *ReaderWrapper, ww *WriterWr daoBlock = false } ibs.Prepare(tx.Hash(), block.Hash(), i) - receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, ww, header, tx, usedGas, vmConfig, nil) + receipt, _, err := core.ApplyTransaction(chainConfig, core.GetHashFn(header, getHeader), engine, nil, gp, ibs, ww, header, tx, usedGas, vmConfig, nil) if err != nil { return 0, nil, fmt.Errorf("could not apply tx %d [%x] failed: %w", i, tx.Hash(), err) } diff --git a/cmd/state/commands/erigon22.go b/cmd/state/commands/erigon22.go index de83339c87a..0bcc8538c13 100644 --- a/cmd/state/commands/erigon22.go +++ b/cmd/state/commands/erigon22.go @@ -305,6 +305,7 @@ func processBlock22(startTxNum uint64, trace bool, txNumStart uint64, rw *Reader txNum++ // Pre-block transaction ww.w.SetTxNum(txNum) + getHashFn := core.GetHashFn(header, getHeader) for i, tx := range block.Transactions() { if txNum >= startTxNum { @@ -312,7 +313,7 @@ func processBlock22(startTxNum uint64, trace bool, txNumStart uint64, rw *Reader ibs.Prepare(tx.Hash(), block.Hash(), i) ct := NewCallTracer() vmConfig.Tracer = ct - receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, ww, header, tx, usedGas, vmConfig, nil) + receipt, _, err := core.ApplyTransaction(chainConfig, getHashFn, engine, nil, gp, ibs, ww, header, tx, usedGas, vmConfig, nil) if err != nil { return 0, nil, fmt.Errorf("could not apply tx %d [%x] failed: %w", i, tx.Hash(), err) } diff --git a/cmd/state/commands/history2.go b/cmd/state/commands/history2.go index 63332bcb0b0..0aa8b07ffee 100644 --- a/cmd/state/commands/history2.go +++ b/cmd/state/commands/history2.go @@ -157,7 +157,7 @@ func runHistory2(trace bool, blockNum, txNumStart uint64, hw *HistoryWrapper, ww daoBlock = false } ibs.Prepare(tx.Hash(), block.Hash(), i) - receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, ww, header, tx, usedGas, vmConfig, nil) + receipt, _, err := core.ApplyTransaction(chainConfig, core.GetHashFn(header, getHeader), engine, nil, gp, ibs, ww, header, tx, usedGas, vmConfig, nil) if err != nil { return 0, nil, fmt.Errorf("could not apply tx %d [%x] failed: %w", i, tx.Hash(), err) } diff --git a/cmd/state/commands/history22.go b/cmd/state/commands/history22.go index fd9d10f2613..a7ecf4d8ad9 100644 --- a/cmd/state/commands/history22.go +++ b/cmd/state/commands/history22.go @@ -245,7 +245,7 @@ func runHistory22(trace bool, blockNum, txNumStart uint64, hw *state.HistoryRead hw.SetTxNum(txNum) ibs := state.New(hw) ibs.Prepare(tx.Hash(), block.Hash(), i) - receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, ww, header, tx, usedGas, vmConfig, nil) + receipt, _, err := core.ApplyTransaction(chainConfig, core.GetHashFn(header, getHeader), engine, nil, gp, ibs, ww, header, tx, usedGas, vmConfig, nil) if err != nil { return 0, nil, fmt.Errorf("could not apply tx %d [%x] failed: %w", i, tx.Hash(), err) } diff --git a/cmd/state/commands/opcode_tracer.go b/cmd/state/commands/opcode_tracer.go index fb6e9777180..ee6a5d86102 100644 --- a/cmd/state/commands/opcode_tracer.go +++ b/cmd/state/commands/opcode_tracer.go @@ -684,7 +684,7 @@ func runBlock(engine consensus.Engine, ibs *state.IntraBlockState, txnWriter sta rules := chainConfig.Rules(block.NumberU64()) for i, tx := range block.Transactions() { ibs.Prepare(tx.Hash(), block.Hash(), i) - receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig, contractHasTEVM) + receipt, _, err := core.ApplyTransaction(chainConfig, core.GetHashFn(header, getHeader), engine, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig, contractHasTEVM) if err != nil { return nil, fmt.Errorf("could not apply tx %d [%x] failed: %w", i, tx.Hash(), err) } diff --git a/cmd/state/commands/state_recon.go b/cmd/state/commands/state_recon.go index 1e6b3568438..231fcca9499 100644 --- a/cmd/state/commands/state_recon.go +++ b/cmd/state/commands/state_recon.go @@ -152,7 +152,8 @@ func (rw *ReconWorker) runTxTask(txTask state.TxTask) { vmConfig := vm.Config{NoReceipts: true, SkipAnalysis: core.SkipAnalysis(rw.chainConfig, txTask.BlockNum)} contractHasTEVM := func(contractHash common.Hash) (bool, error) { return false, nil } ibs.Prepare(txHash, txTask.BlockHash, txTask.TxIndex) - _, _, err = core.ApplyTransaction(rw.chainConfig, rw.getHeader, rw.engine, nil, gp, ibs, noop, txTask.Header, txTask.Tx, usedGas, vmConfig, contractHasTEVM) + getHashFn := core.GetHashFn(txTask.Header, rw.getHeader) + _, _, err = core.ApplyTransaction(rw.chainConfig, getHashFn, rw.engine, nil, gp, ibs, noop, txTask.Header, txTask.Tx, usedGas, vmConfig, contractHasTEVM) if err != nil { panic(fmt.Errorf("could not apply tx %d [%x] failed: %w", txTask.TxIndex, txHash, err)) } diff --git a/cmd/state/commands/state_recon_1.go b/cmd/state/commands/state_recon_1.go index fd968542373..b149406a892 100644 --- a/cmd/state/commands/state_recon_1.go +++ b/cmd/state/commands/state_recon_1.go @@ -150,7 +150,8 @@ func (rw *ReconWorker1) runTxTask(txTask state.TxTask) { contractHasTEVM := func(contractHash common.Hash) (bool, error) { return false, nil } ibs.Prepare(txHash, txTask.BlockHash, txTask.TxIndex) vmConfig.SkipAnalysis = core.SkipAnalysis(rw.chainConfig, txTask.BlockNum) - blockContext := core.NewEVMBlockContext(txTask.Header, rw.getHeader, rw.engine, nil /* author */, contractHasTEVM) + getHashFn := core.GetHashFn(txTask.Header, rw.getHeader) + blockContext := core.NewEVMBlockContext(txTask.Header, getHashFn, rw.engine, nil /* author */, contractHasTEVM) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, ibs, rw.chainConfig, vmConfig) msg, err := txTask.Tx.AsMessage(*types.MakeSigner(rw.chainConfig, txTask.BlockNum), txTask.Header.BaseFee, rules) if err != nil { diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 30fd185239a..731fd618121 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -1218,7 +1218,7 @@ func (p *Parlia) systemCall(from, contract common.Address, data []byte, ibs *sta ) vmConfig := vm.Config{NoReceipts: true} // Create a new context to be used in the EVM environment - blockContext := core.NewEVMBlockContext(header, nil, p, &from, nil) + blockContext := core.NewEVMBlockContext(header, core.GetHashFn(header, nil), p, &from, nil) evm := vm.NewEVM(blockContext, core.NewEVMTxContext(msg), ibs, chainConfig, vmConfig) ret, leftOverGas, err := evm.Call( vm.AccountRef(msg.From()), diff --git a/core/blockchain.go b/core/blockchain.go index b102ae22c15..6b845347df9 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -18,16 +18,17 @@ package core import ( - "encoding/json" "fmt" - "os" "time" "github.com/ledgerwatch/erigon/core/systemcontracts" + "github.com/ledgerwatch/erigon/rlp" + "golang.org/x/crypto/sha3" "golang.org/x/exp/slices" metrics2 "github.com/VictoriaMetrics/metrics" "github.com/ledgerwatch/erigon/common" + "github.com/ledgerwatch/erigon/common/math" "github.com/ledgerwatch/erigon/common/u256" "github.com/ledgerwatch/erigon/consensus" "github.com/ledgerwatch/erigon/consensus/misc" @@ -48,12 +49,30 @@ const ( TriesInMemory = 128 ) -// ExecuteBlockEphemerally runs a block from provided stateReader and -// writes the result to the provided stateWriter +type RejectedTx struct { + Index int `json:"index" gencodec:"required"` + Err string `json:"error" gencodec:"required"` +} + +type RejectedTxs []*RejectedTx + +type EphemeralExecResult struct { + StateRoot common.Hash `json:"stateRoot"` + TxRoot common.Hash `json:"txRoot"` + ReceiptRoot common.Hash `json:"receiptsRoot"` + LogsHash common.Hash `json:"logsHash"` + Bloom types.Bloom `json:"logsBloom" gencodec:"required"` + Receipts types.Receipts `json:"receipts"` + Rejected RejectedTxs `json:"rejected,omitempty"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` + GasUsed math.HexOrDecimal64 `json:"gasUsed"` + ReceiptForStorage *types.ReceiptForStorage `json:"-"` +} + func ExecuteBlockEphemerallyForBSC( chainConfig *params.ChainConfig, vmConfig *vm.Config, - getHeader func(hash common.Hash, number uint64) *types.Header, + blockHashFunc func(n uint64) common.Hash, engine consensus.Engine, block *types.Block, stateReader state.StateReader, @@ -61,16 +80,23 @@ func ExecuteBlockEphemerallyForBSC( epochReader consensus.EpochReader, chainReader consensus.ChainHeaderReader, contractHasTEVM func(codeHash common.Hash) (bool, error), -) (types.Receipts, error) { + statelessExec bool, // for usage of this API via cli tools wherein some of the validations need to be relaxed. + getTracer func(txIndex int, txHash common.Hash) (vm.Tracer, error), +) (*EphemeralExecResult, error) { defer blockExecutionTimer.UpdateDuration(time.Now()) block.Uncles() ibs := state.New(stateReader) header := block.Header() - var receipts types.Receipts usedGas := new(uint64) gp := new(GasPool) gp.AddGas(block.GasLimit()) + var ( + rejectedTxs []*RejectedTx + includedTxs types.Transactions + receipts types.Receipts + ) + if !vmConfig.ReadOnly { if err := InitializeBlockExecution(engine, chainReader, epochReader, block.Header(), block.Transactions(), block.Uncles(), chainConfig, ibs); err != nil { return nil, err @@ -95,35 +121,37 @@ func ExecuteBlockEphemerallyForBSC( ibs.Prepare(tx.Hash(), block.Hash(), i) writeTrace := false if vmConfig.Debug && vmConfig.Tracer == nil { - vmConfig.Tracer = vm.NewStructLogger(&vm.LogConfig{}) + tracer, err := getTracer(i, tx.Hash()) + if err != nil { + return nil, fmt.Errorf("could not obtain tracer: %w", err) + } + vmConfig.Tracer = tracer writeTrace = true } - receipt, _, err := ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, noop, header, tx, usedGas, *vmConfig, contractHasTEVM) + receipt, _, err := ApplyTransaction(chainConfig, blockHashFunc, engine, nil, gp, ibs, noop, header, tx, usedGas, *vmConfig, contractHasTEVM) if writeTrace { - w, err1 := os.Create(fmt.Sprintf("txtrace_%x.txt", tx.Hash())) - if err1 != nil { - panic(err1) - } - encoder := json.NewEncoder(w) - logs := FormatLogs(vmConfig.Tracer.(*vm.StructLogger).StructLogs()) - if err2 := encoder.Encode(logs); err2 != nil { - panic(err2) - } - if err2 := w.Close(); err2 != nil { - panic(err2) + if ftracer, ok := vmConfig.Tracer.(vm.FlushableTracer); ok { + ftracer.Flush(tx) } + vmConfig.Tracer = nil } if err != nil { - return nil, fmt.Errorf("could not apply tx %d from block %d [%v]: %w", i, block.NumberU64(), tx.Hash().Hex(), err) - } - if !vmConfig.NoReceipts { - receipts = append(receipts, receipt) + if !statelessExec { + return nil, fmt.Errorf("could not apply tx %d from block %d [%v]: %w", i, block.NumberU64(), tx.Hash().Hex(), err) + } + rejectedTxs = append(rejectedTxs, &RejectedTx{i, err.Error()}) + } else { + includedTxs = append(includedTxs, tx) + if !vmConfig.NoReceipts { + receipts = append(receipts, receipt) + } } } var newBlock *types.Block + var receiptSha common.Hash if !vmConfig.ReadOnly { // We're doing this hack for BSC to avoid changing consensus interfaces a lot. BSC modifies txs and receipts by appending // system transactions, and they increase used gas and write cumulative gas to system receipts, that's why we need @@ -148,21 +176,26 @@ func ExecuteBlockEphemerallyForBSC( if !vmConfig.NoReceipts { receipts = outReceipts } + receiptSha = newBlock.ReceiptHash() } else { newBlock = block + receiptSha = types.DeriveSha(receipts) } if chainConfig.IsByzantium(header.Number.Uint64()) && !vmConfig.NoReceipts { - if newBlock.ReceiptHash() != block.ReceiptHash() { + if !statelessExec && receiptSha != block.ReceiptHash() { return nil, fmt.Errorf("mismatched receipt headers for block %d (%s != %s)", block.NumberU64(), newBlock.ReceiptHash().Hex(), block.Header().ReceiptHash.Hex()) } } - if newBlock.GasUsed() != header.GasUsed { + if !statelessExec && newBlock.GasUsed() != header.GasUsed { return nil, fmt.Errorf("gas used by execution: %d, in header: %d", *usedGas, header.GasUsed) } + + var bloom types.Bloom if !vmConfig.NoReceipts { - if newBlock.Bloom() != header.Bloom { - return nil, fmt.Errorf("bloom computed by execution: %x, in header: %x", newBlock.Bloom(), header.Bloom) + bloom = newBlock.Bloom() + if !statelessExec && bloom != header.Bloom { + return nil, fmt.Errorf("bloom computed by execution: %x, in header: %x", bloom, header.Bloom) } } @@ -172,7 +205,17 @@ func ExecuteBlockEphemerallyForBSC( return nil, fmt.Errorf("writing changesets for block %d failed: %w", header.Number.Uint64(), err) } - return receipts, nil + execRs := &EphemeralExecResult{ + TxRoot: types.DeriveSha(includedTxs), + ReceiptRoot: receiptSha, + Bloom: bloom, + Receipts: receipts, + Difficulty: (*math.HexOrDecimal256)(block.Header().Difficulty), + GasUsed: math.HexOrDecimal64(*usedGas), + Rejected: rejectedTxs, + } + + return execRs, nil } // ExecuteBlockEphemerally runs a block from provided stateReader and @@ -180,7 +223,7 @@ func ExecuteBlockEphemerallyForBSC( func ExecuteBlockEphemerally( chainConfig *params.ChainConfig, vmConfig *vm.Config, - getHeader func(hash common.Hash, number uint64) *types.Header, + blockHashFunc func(n uint64) common.Hash, engine consensus.Engine, block *types.Block, stateReader state.StateReader, @@ -188,19 +231,28 @@ func ExecuteBlockEphemerally( epochReader consensus.EpochReader, chainReader consensus.ChainHeaderReader, contractHasTEVM func(codeHash common.Hash) (bool, error), -) (types.Receipts, *types.ReceiptForStorage, error) { + statelessExec bool, // for usage of this API via cli tools wherein some of the validations need to be relaxed. + getTracer func(txIndex int, txHash common.Hash) (vm.Tracer, error), +) (*EphemeralExecResult, error) { + defer blockExecutionTimer.UpdateDuration(time.Now()) block.Uncles() ibs := state.New(stateReader) header := block.Header() - var receipts types.Receipts + usedGas := new(uint64) gp := new(GasPool) gp.AddGas(block.GasLimit()) + var ( + rejectedTxs []*RejectedTx + includedTxs types.Transactions + receipts types.Receipts + ) + if !vmConfig.ReadOnly { if err := InitializeBlockExecution(engine, chainReader, epochReader, block.Header(), block.Transactions(), block.Uncles(), chainConfig, ibs); err != nil { - return nil, nil, err + return nil, err } } @@ -213,54 +265,55 @@ func ExecuteBlockEphemerally( ibs.Prepare(tx.Hash(), block.Hash(), i) writeTrace := false if vmConfig.Debug && vmConfig.Tracer == nil { - vmConfig.Tracer = vm.NewStructLogger(&vm.LogConfig{}) + tracer, err := getTracer(i, tx.Hash()) + if err != nil { + return nil, fmt.Errorf("could not obtain tracer: %w", err) + } + vmConfig.Tracer = tracer writeTrace = true } - receipt, _, err := ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, noop, header, tx, usedGas, *vmConfig, contractHasTEVM) + receipt, _, err := ApplyTransaction(chainConfig, blockHashFunc, engine, nil, gp, ibs, noop, header, tx, usedGas, *vmConfig, contractHasTEVM) if writeTrace { - w, err1 := os.Create(fmt.Sprintf("txtrace_%x.txt", tx.Hash())) - if err1 != nil { - panic(err1) - } - encoder := json.NewEncoder(w) - logs := FormatLogs(vmConfig.Tracer.(*vm.StructLogger).StructLogs()) - if err2 := encoder.Encode(logs); err2 != nil { - panic(err2) - } - if err2 := w.Close(); err2 != nil { - panic(err2) + if ftracer, ok := vmConfig.Tracer.(vm.FlushableTracer); ok { + ftracer.Flush(tx) } + vmConfig.Tracer = nil } if err != nil { - return nil, nil, fmt.Errorf("could not apply tx %d from block %d [%v]: %w", i, block.NumberU64(), tx.Hash().Hex(), err) - } - if !vmConfig.NoReceipts { - receipts = append(receipts, receipt) + if !statelessExec { + return nil, fmt.Errorf("could not apply tx %d from block %d [%v]: %w", i, block.NumberU64(), tx.Hash().Hex(), err) + } + rejectedTxs = append(rejectedTxs, &RejectedTx{i, err.Error()}) + } else { + includedTxs = append(includedTxs, tx) + if !vmConfig.NoReceipts { + receipts = append(receipts, receipt) + } } } - if chainConfig.IsByzantium(header.Number.Uint64()) && !vmConfig.NoReceipts { - receiptSha := types.DeriveSha(receipts) - if receiptSha != block.ReceiptHash() { - return nil, nil, fmt.Errorf("mismatched receipt headers for block %d", block.NumberU64()) - } + receiptSha := types.DeriveSha(receipts) + if !statelessExec && chainConfig.IsByzantium(header.Number.Uint64()) && !vmConfig.NoReceipts && receiptSha != block.ReceiptHash() { + return nil, fmt.Errorf("mismatched receipt headers for block %d", block.NumberU64()) } - if *usedGas != header.GasUsed { - return nil, nil, fmt.Errorf("gas used by execution: %d, in header: %d", *usedGas, header.GasUsed) + if !statelessExec && *usedGas != header.GasUsed { + return nil, fmt.Errorf("gas used by execution: %d, in header: %d", *usedGas, header.GasUsed) } + + var bloom types.Bloom if !vmConfig.NoReceipts { - bloom := types.CreateBloom(receipts) - if bloom != header.Bloom { - return nil, nil, fmt.Errorf("bloom computed by execution: %x, in header: %x", bloom, header.Bloom) + bloom = types.CreateBloom(receipts) + if !statelessExec && bloom != header.Bloom { + return nil, fmt.Errorf("bloom computed by execution: %x, in header: %x", bloom, header.Bloom) } } if !vmConfig.ReadOnly { txs := block.Transactions() if _, err := FinalizeBlockExecution(engine, stateReader, block.Header(), txs, block.Uncles(), stateWriter, chainConfig, ibs, receipts, epochReader, chainReader, false); err != nil { - return nil, nil, err + return nil, err } } @@ -287,7 +340,26 @@ func ExecuteBlockEphemerally( } } - return receipts, stateSyncReceipt, nil + execRs := &EphemeralExecResult{ + TxRoot: types.DeriveSha(includedTxs), + ReceiptRoot: receiptSha, + Bloom: bloom, + LogsHash: rlpHash(blockLogs), + Receipts: receipts, + Difficulty: (*math.HexOrDecimal256)(header.Difficulty), + GasUsed: math.HexOrDecimal64(*usedGas), + Rejected: rejectedTxs, + ReceiptForStorage: stateSyncReceipt, + } + + return execRs, nil +} + +func rlpHash(x interface{}) (h common.Hash) { + hw := sha3.NewLegacyKeccak256() + rlp.Encode(hw, x) //nolint:errcheck + hw.Sum(h[:0]) + return h } func SysCallContract(contract common.Address, data []byte, chainConfig params.ChainConfig, ibs *state.IntraBlockState, header *types.Header, engine consensus.Engine) (result []byte, err error) { @@ -308,19 +380,16 @@ func SysCallContract(contract common.Address, data []byte, chainConfig params.Ch vmConfig := vm.Config{NoReceipts: true} // Create a new context to be used in the EVM environment isBor := chainConfig.Bor != nil + var txContext vm.TxContext var author *common.Address if isBor { author = &header.Coinbase - } else { - author = &state.SystemAddress - } - blockContext := NewEVMBlockContext(header, nil, engine, author, nil) - var txContext vm.TxContext - if isBor { txContext = vm.TxContext{} } else { + author = &state.SystemAddress txContext = NewEVMTxContext(msg) } + blockContext := NewEVMBlockContext(header, GetHashFn(header, nil), engine, author, nil) evm := vm.NewEVM(blockContext, txContext, ibs, &chainConfig, vmConfig) if isBor { ret, _, err := evm.Call( @@ -364,7 +433,7 @@ func CallContract(contract common.Address, data []byte, chainConfig params.Chain return nil, fmt.Errorf("SysCallContract: %w ", err) } vmConfig := vm.Config{NoReceipts: true} - _, result, err = ApplyTransaction(&chainConfig, nil, engine, &state.SystemAddress, gp, ibs, noop, header, tx, &gasUsed, vmConfig, nil) + _, result, err = ApplyTransaction(&chainConfig, GetHashFn(header, nil), engine, &state.SystemAddress, gp, ibs, noop, header, tx, &gasUsed, vmConfig, nil) if err != nil { return result, fmt.Errorf("SysCallContract: %w ", err) } @@ -392,7 +461,7 @@ func FinalizeBlockExecution(engine consensus.Engine, stateReader state.StateRead _, _, err = engine.Finalize(cc, header, ibs, txs, uncles, receipts, e, headerReader, syscall) } if err != nil { - return + return nil, err } var originalSystemAcc *accounts.Account diff --git a/core/chain_makers.go b/core/chain_makers.go index 24a8e4266df..17248745e79 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -112,7 +112,7 @@ func (b *BlockGen) AddTxWithChain(getHeader func(hash common.Hash, number uint64 } b.ibs.Prepare(tx.Hash(), common.Hash{}, len(b.txs)) contractHasTEVM := func(_ common.Hash) (bool, error) { return false, nil } - receipt, _, err := ApplyTransaction(b.config, getHeader, engine, &b.header.Coinbase, b.gasPool, b.ibs, state.NewNoopWriter(), b.header, tx, &b.header.GasUsed, vm.Config{}, contractHasTEVM) + receipt, _, err := ApplyTransaction(b.config, GetHashFn(b.header, getHeader), engine, &b.header.Coinbase, b.gasPool, b.ibs, state.NewNoopWriter(), b.header, tx, &b.header.GasUsed, vm.Config{}, contractHasTEVM) if err != nil { panic(err) } @@ -126,7 +126,7 @@ func (b *BlockGen) AddFailedTxWithChain(getHeader func(hash common.Hash, number } b.ibs.Prepare(tx.Hash(), common.Hash{}, len(b.txs)) contractHasTEVM := func(common.Hash) (bool, error) { return false, nil } - receipt, _, err := ApplyTransaction(b.config, getHeader, engine, &b.header.Coinbase, b.gasPool, b.ibs, state.NewNoopWriter(), b.header, tx, &b.header.GasUsed, vm.Config{}, contractHasTEVM) + receipt, _, err := ApplyTransaction(b.config, GetHashFn(b.header, getHeader), engine, &b.header.Coinbase, b.gasPool, b.ibs, state.NewNoopWriter(), b.header, tx, &b.header.GasUsed, vm.Config{}, contractHasTEVM) _ = err // accept failed transactions b.txs = append(b.txs, tx) b.receipts = append(b.receipts, receipt) diff --git a/core/evm.go b/core/evm.go index c2fa1471cd5..b89f8e5acb4 100644 --- a/core/evm.go +++ b/core/evm.go @@ -30,7 +30,7 @@ import ( ) // NewEVMBlockContext creates a new context for use in the EVM. -func NewEVMBlockContext(header *types.Header, getHeader func(hash common.Hash, number uint64) *types.Header, engine consensus.Engine, author *common.Address, contractHasTEVM func(contractHash common.Hash) (bool, error)) vm.BlockContext { +func NewEVMBlockContext(header *types.Header, blockHashFunc func(n uint64) common.Hash, engine consensus.Engine, author *common.Address, contractHasTEVM func(contractHash common.Hash) (bool, error)) vm.BlockContext { // If we don't have an explicit author (i.e. not mining), extract from the header var beneficiary common.Address if author == nil { @@ -71,7 +71,7 @@ func NewEVMBlockContext(header *types.Header, getHeader func(hash common.Hash, n return vm.BlockContext{ CanTransfer: CanTransfer, Transfer: transferFunc, - GetHash: GetHashFn(header, getHeader), + GetHash: blockHashFunc, Coinbase: beneficiary, BlockNumber: header.Number.Uint64(), Time: header.Time, diff --git a/core/state_processor.go b/core/state_processor.go index 066378ab5d9..c203685bb9e 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -17,10 +17,7 @@ package core import ( - "fmt" - "github.com/ledgerwatch/erigon/common" - "github.com/ledgerwatch/erigon/common/math" "github.com/ledgerwatch/erigon/consensus" "github.com/ledgerwatch/erigon/core/state" "github.com/ledgerwatch/erigon/core/types" @@ -29,57 +26,6 @@ import ( "github.com/ledgerwatch/erigon/params" ) -// StructLogRes stores a structured log emitted by the EVM while replaying a -// transaction in debug mode -type StructLogRes struct { - Pc uint64 `json:"pc"` - Op string `json:"op"` - Gas uint64 `json:"gas"` - GasCost uint64 `json:"gasCost"` - Depth int `json:"depth"` - Error error `json:"error,omitempty"` - Stack *[]string `json:"stack,omitempty"` - Memory *[]string `json:"memory,omitempty"` - Storage *map[string]string `json:"storage,omitempty"` -} - -// FormatLogs formats EVM returned structured logs for json output -func FormatLogs(logs []vm.StructLog) []StructLogRes { - formatted := make([]StructLogRes, len(logs)) - for index, trace := range logs { - formatted[index] = StructLogRes{ - Pc: trace.Pc, - Op: trace.Op.String(), - Gas: trace.Gas, - GasCost: trace.GasCost, - Depth: trace.Depth, - Error: trace.Err, - } - if trace.Stack != nil { - stack := make([]string, len(trace.Stack)) - for i, stackValue := range trace.Stack { - stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32)) - } - formatted[index].Stack = &stack - } - if trace.Memory != nil { - memory := make([]string, 0, (len(trace.Memory)+31)/32) - for i := 0; i+32 <= len(trace.Memory); i += 32 { - memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32])) - } - formatted[index].Memory = &memory - } - if trace.Storage != nil { - storage := make(map[string]string) - for i, storageValue := range trace.Storage { - storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue) - } - formatted[index].Storage = &storage - } - } - return formatted -} - // applyTransaction attempts to apply a transaction to the given state database // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, @@ -140,7 +86,7 @@ func applyTransaction(config *params.ChainConfig, gp *GasPool, statedb *state.In // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. -func ApplyTransaction(config *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, engine consensus.Engine, author *common.Address, gp *GasPool, ibs *state.IntraBlockState, stateWriter state.StateWriter, header *types.Header, tx types.Transaction, usedGas *uint64, cfg vm.Config, contractHasTEVM func(contractHash common.Hash) (bool, error)) (*types.Receipt, []byte, error) { +func ApplyTransaction(config *params.ChainConfig, blockHashFunc func(n uint64) common.Hash, engine consensus.Engine, author *common.Address, gp *GasPool, ibs *state.IntraBlockState, stateWriter state.StateWriter, header *types.Header, tx types.Transaction, usedGas *uint64, cfg vm.Config, contractHasTEVM func(contractHash common.Hash) (bool, error)) (*types.Receipt, []byte, error) { // Create a new context to be used in the EVM environment // Add addresses to access list if applicable @@ -152,7 +98,7 @@ func ApplyTransaction(config *params.ChainConfig, getHeader func(hash common.Has if tx.IsStarkNet() { vmenv = &vm.CVMAdapter{Cvm: vm.NewCVM(ibs)} } else { - blockContext := NewEVMBlockContext(header, getHeader, engine, author, contractHasTEVM) + blockContext := NewEVMBlockContext(header, blockHashFunc, engine, author, contractHasTEVM) vmenv = vm.NewEVM(blockContext, vm.TxContext{}, ibs, config, cfg) } diff --git a/core/vm/logger.go b/core/vm/logger.go index 1d50dca6c1b..b9d511b8858 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -18,10 +18,12 @@ package vm import ( "encoding/hex" + "encoding/json" "errors" "fmt" "io" "math/big" + "os" "strings" "time" @@ -128,6 +130,27 @@ type Tracer interface { CaptureAccountWrite(account common.Address) error } +// FlushableTracer is a Tracer extension whose accumulated traces has to be +// flushed once the tracing is completed. +type FlushableTracer interface { + Tracer + Flush(tx types.Transaction) +} + +// StructLogRes stores a structured log emitted by the EVM while replaying a +// transaction in debug mode +type StructLogRes struct { + Pc uint64 `json:"pc"` + Op string `json:"op"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` + Depth int `json:"depth"` + Error error `json:"error,omitempty"` + Stack *[]string `json:"stack,omitempty"` + Memory *[]string `json:"memory,omitempty"` + Storage *map[string]string `json:"storage,omitempty"` +} + // StructLogger is an EVM state logger and implements Tracer. // // StructLogger can capture state based on the given Log configuration and also keeps @@ -261,6 +284,58 @@ func (l *StructLogger) Error() error { return l.err } // Output returns the VM return value captured by the trace. func (l *StructLogger) Output() []byte { return l.output } +func (l *StructLogger) Flush(tx types.Transaction) { + w, err1 := os.Create(fmt.Sprintf("txtrace_%x.txt", tx.Hash())) + if err1 != nil { + panic(err1) + } + encoder := json.NewEncoder(w) + logs := FormatLogs(l.StructLogs()) + if err2 := encoder.Encode(logs); err2 != nil { + panic(err2) + } + if err2 := w.Close(); err2 != nil { + panic(err2) + } +} + +// FormatLogs formats EVM returned structured logs for json output +func FormatLogs(logs []StructLog) []StructLogRes { + formatted := make([]StructLogRes, len(logs)) + for index, trace := range logs { + formatted[index] = StructLogRes{ + Pc: trace.Pc, + Op: trace.Op.String(), + Gas: trace.Gas, + GasCost: trace.GasCost, + Depth: trace.Depth, + Error: trace.Err, + } + if trace.Stack != nil { + stack := make([]string, len(trace.Stack)) + for i, stackValue := range trace.Stack { + stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32)) + } + formatted[index].Stack = &stack + } + if trace.Memory != nil { + memory := make([]string, 0, (len(trace.Memory)+31)/32) + for i := 0; i+32 <= len(trace.Memory); i += 32 { + memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32])) + } + formatted[index].Memory = &memory + } + if trace.Storage != nil { + storage := make(map[string]string) + for i, storageValue := range trace.Storage { + storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue) + } + formatted[index].Storage = &storage + } + } + return formatted +} + // WriteTrace writes a formatted trace to the given writer func WriteTrace(writer io.Writer, logs []StructLog) { for _, log := range logs { diff --git a/eth/stagedsync/stage_execute.go b/eth/stagedsync/stage_execute.go index f40a126d865..0bb8ace725d 100644 --- a/eth/stagedsync/stage_execute.go +++ b/eth/stagedsync/stage_execute.go @@ -16,6 +16,7 @@ import ( "github.com/ledgerwatch/erigon-lib/etl" "github.com/ledgerwatch/erigon-lib/kv" commonold "github.com/ledgerwatch/erigon/common" + ecom "github.com/ledgerwatch/erigon/common" "github.com/ledgerwatch/erigon/common/changeset" "github.com/ledgerwatch/erigon/common/dbutils" "github.com/ledgerwatch/erigon/common/math" @@ -121,21 +122,30 @@ func executeBlock( return h } + getTracer := func(txIndex int, txHash ecom.Hash) (vm.Tracer, error) { + return vm.NewStructLogger(&vm.LogConfig{}), nil + } + callTracer := calltracer.NewCallTracer(contractHasTEVM) vmConfig.Debug = true vmConfig.Tracer = callTracer var receipts types.Receipts var stateSyncReceipt *types.ReceiptForStorage - _, isPoSa := effectiveEngine.(consensus.PoSA) + var execRs *core.EphemeralExecResult + _, isPoSa := cfg.engine.(consensus.PoSA) + getHashFn := core.GetHashFn(block.Header(), getHeader) + if isPoSa { - receipts, err = core.ExecuteBlockEphemerallyForBSC(cfg.chainConfig, &vmConfig, getHeader, effectiveEngine, block, stateReader, stateWriter, epochReader{tx: tx}, chainReader{config: cfg.chainConfig, tx: tx, blockReader: cfg.blockReader}, contractHasTEVM) + execRs, err = core.ExecuteBlockEphemerallyForBSC(cfg.chainConfig, &vmConfig, getHashFn, cfg.engine, block, stateReader, stateWriter, epochReader{tx: tx}, chainReader{config: cfg.chainConfig, tx: tx, blockReader: cfg.blockReader}, contractHasTEVM, false, getTracer) } else { - receipts, stateSyncReceipt, err = core.ExecuteBlockEphemerally(cfg.chainConfig, &vmConfig, getHeader, effectiveEngine, block, stateReader, stateWriter, epochReader{tx: tx}, chainReader{config: cfg.chainConfig, tx: tx, blockReader: cfg.blockReader}, contractHasTEVM) + execRs, err = core.ExecuteBlockEphemerally(cfg.chainConfig, &vmConfig, getHashFn, cfg.engine, block, stateReader, stateWriter, epochReader{tx: tx}, chainReader{config: cfg.chainConfig, tx: tx, blockReader: cfg.blockReader}, contractHasTEVM, false, getTracer) } if err != nil { return err } + receipts = execRs.Receipts + stateSyncReceipt = execRs.ReceiptForStorage if writeReceipts { if err = rawdb.AppendReceipts(tx, blockNum, receipts); err != nil { @@ -147,7 +157,6 @@ func executeBlock( return err } } - } if cfg.changeSetHook != nil { diff --git a/eth/stagedsync/stage_mining_exec.go b/eth/stagedsync/stage_mining_exec.go index 3afa9922767..a1742548088 100644 --- a/eth/stagedsync/stage_mining_exec.go +++ b/eth/stagedsync/stage_mining_exec.go @@ -180,7 +180,7 @@ func addTransactionsToMiningBlock(logPrefix string, current *MiningBlock, chainC var miningCommitTx = func(txn types.Transaction, coinbase common.Address, vmConfig *vm.Config, chainConfig params.ChainConfig, ibs *state.IntraBlockState, current *MiningBlock) ([]*types.Log, error) { snap := ibs.Snapshot() - receipt, _, err := core.ApplyTransaction(&chainConfig, getHeader, engine, &coinbase, gasPool, ibs, noop, header, txn, &header.GasUsed, *vmConfig, contractHasTEVM) + receipt, _, err := core.ApplyTransaction(&chainConfig, core.GetHashFn(header, getHeader), engine, &coinbase, gasPool, ibs, noop, header, txn, &header.GasUsed, *vmConfig, contractHasTEVM) if err != nil { ibs.RevertToSnapshot(snap) return nil, err diff --git a/go.mod b/go.mod index 92826f92b17..7087daba894 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,8 @@ require ( github.com/edsrzf/mmap-go v1.1.0 github.com/emicklei/dot v1.0.0 github.com/emirpasic/gods v1.18.1 - github.com/fjl/gencodec v0.0.0-20191126094850-e283372f291f + github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c + github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/goccy/go-json v0.9.7 github.com/gofrs/flock v0.8.1 github.com/golang-jwt/jwt/v4 v4.4.1 @@ -92,13 +93,13 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect + github.com/docker/docker v20.10.17+incompatible github.com/dustin/go-humanize v1.0.0 // indirect github.com/flanglet/kanzi-go v1.9.1-0.20211212184056-72dda96261ee // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect github.com/go-kit/kit v0.10.0 // indirect github.com/go-logfmt/logfmt v0.5.0 // indirect - github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect @@ -161,3 +162,5 @@ require ( modernc.org/strutil v1.1.1 // indirect modernc.org/token v1.0.0 // indirect ) + +require gotest.tools/v3 v3.3.0 // indirect diff --git a/go.sum b/go.sum index 939cfee5aaf..525743beec3 100644 --- a/go.sum +++ b/go.sum @@ -165,6 +165,8 @@ github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16/II7vuEo/nHjodOg0p7+OiDpjX5t1E= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= +github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48 h1:iZOop7pqsg+56twTopWgwCGxdB5SI2yDO8Ti7eTRliQ= github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= @@ -191,8 +193,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fjl/gencodec v0.0.0-20191126094850-e283372f291f h1:Y/gg/utVetS+WS6htAKCTDralkm/8hLIIUAtLFdbdQ8= -github.com/fjl/gencodec v0.0.0-20191126094850-e283372f291f/go.mod h1:q+7Z5oyy8cvKF3TakcuihvQvBHFTnXjB+7UP1e2Q+1o= +github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c h1:CndMRAH4JIwxbW8KYq6Q+cGWcGHz0FjGR3QqcInWcW0= +github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= github.com/flanglet/kanzi-go v1.9.1-0.20211212184056-72dda96261ee h1:CaVlPeoz5kJQ+cAOV+ZDdlr3J2FmKyNkGu9LY+x7cDM= github.com/flanglet/kanzi-go v1.9.1-0.20211212184056-72dda96261ee/go.mod h1:/sUSVgDcbjsisuW42GPDgaMqvJ0McZERNICnD7b1nRA= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -208,12 +210,10 @@ github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILD github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= @@ -291,7 +291,6 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -359,7 +358,6 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= @@ -383,7 +381,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758 h1:0D5M2HQSGD3PYPwICLl+/9oulQauOuETfgFvhBDffs0= -github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/ledgerwatch/erigon-lib v0.0.0-20220707065230-95e361fa1ed7 h1:ytnJHsVttH1NleI45f6FbP7HaratpDx4IPCK/D/aZwI= @@ -446,7 +443,6 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= @@ -586,6 +582,7 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3 github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -796,12 +793,12 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -882,6 +879,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= +gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/cmdtest/test_cmd.go b/internal/cmdtest/test_cmd.go new file mode 100644 index 00000000000..b837c9c399c --- /dev/null +++ b/internal/cmdtest/test_cmd.go @@ -0,0 +1,300 @@ +// Copyright 2017 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 . + +package cmdtest + +import ( + "bufio" + "bytes" + "fmt" + "io" + "os" + "os/exec" + "regexp" + "strings" + "sync" + "sync/atomic" + "syscall" + "testing" + "text/template" + "time" + + "github.com/docker/docker/pkg/reexec" +) + +func NewTestCmd(t *testing.T, data interface{}) *TestCmd { + return &TestCmd{T: t, Data: data} +} + +type TestCmd struct { + // For total convenience, all testing methods are available. + *testing.T + + Func template.FuncMap + Data interface{} + Cleanup func() + + cmd *exec.Cmd + stdout *bufio.Reader + stdin io.WriteCloser + stderr *testlogger + // Err will contain the process exit error or interrupt signal error + Err error +} + +var id int32 + +// Run exec's the current binary using name as argv[0] which will trigger the +// reexec init function for that name (e.g. "geth-test" in cmd/geth/run_test.go) +func (tt *TestCmd) Run(name string, args ...string) { + id := atomic.AddInt32(&id, 1) + tt.stderr = &testlogger{t: tt.T, name: fmt.Sprintf("%d", id)} + tt.cmd = &exec.Cmd{ + Path: reexec.Self(), + Args: append([]string{name}, args...), + Stderr: tt.stderr, + } + stdout, err := tt.cmd.StdoutPipe() + if err != nil { + tt.Fatal(err) + } + tt.stdout = bufio.NewReader(stdout) + if tt.stdin, err = tt.cmd.StdinPipe(); err != nil { + tt.Fatal(err) + } + if err := tt.cmd.Start(); err != nil { + tt.Fatal(err) + } +} + +// InputLine writes the given text to the child's stdin. +// This method can also be called from an expect template, e.g.: +// +// geth.expect(`Passphrase: {{.InputLine "password"}}`) +func (tt *TestCmd) InputLine(s string) string { + io.WriteString(tt.stdin, s+"\n") + return "" +} + +func (tt *TestCmd) SetTemplateFunc(name string, fn interface{}) { + if tt.Func == nil { + tt.Func = make(map[string]interface{}) + } + tt.Func[name] = fn +} + +// Expect runs its argument as a template, then expects the +// child process to output the result of the template within 5s. +// +// If the template starts with a newline, the newline is removed +// before matching. +func (tt *TestCmd) Expect(tplsource string) { + // Generate the expected output by running the template. + tpl := template.Must(template.New("").Funcs(tt.Func).Parse(tplsource)) + wantbuf := new(bytes.Buffer) + if err := tpl.Execute(wantbuf, tt.Data); err != nil { + panic(err) + } + // Trim exactly one newline at the beginning. This makes tests look + // much nicer because all expect strings are at column 0. + want := bytes.TrimPrefix(wantbuf.Bytes(), []byte("\n")) + if err := tt.matchExactOutput(want); err != nil { + tt.Fatal(err) + } + tt.Logf("Matched stdout text:\n%s", want) +} + +// Output reads all output from stdout, and returns the data. +func (tt *TestCmd) Output() []byte { + var buf []byte + tt.withKillTimeout(func() { buf, _ = io.ReadAll(tt.stdout) }) + return buf +} + +func (tt *TestCmd) matchExactOutput(want []byte) error { + buf := make([]byte, len(want)) + n := 0 + tt.withKillTimeout(func() { n, _ = io.ReadFull(tt.stdout, buf) }) + buf = buf[:n] + if n < len(want) || !bytes.Equal(buf, want) { + // Grab any additional buffered output in case of mismatch + // because it might help with debugging. + buf = append(buf, make([]byte, tt.stdout.Buffered())...) + tt.stdout.Read(buf[n:]) + // Find the mismatch position. + for i := 0; i < n; i++ { + if want[i] != buf[i] { + return fmt.Errorf("output mismatch at ā—Š:\n---------------- (stdout text)\n%sā—Š%s\n---------------- (expected text)\n%s", + buf[:i], buf[i:n], want) + } + } + if n < len(want) { + return fmt.Errorf("not enough output, got until ā—Š:\n---------------- (stdout text)\n%s\n---------------- (expected text)\n%sā—Š%s", + buf, want[:n], want[n:]) + } + } + return nil +} + +// ExpectRegexp expects the child process to output text matching the +// given regular expression within 5s. +// +// Note that an arbitrary amount of output may be consumed by the +// regular expression. This usually means that expect cannot be used +// after ExpectRegexp. +func (tt *TestCmd) ExpectRegexp(regex string) (*regexp.Regexp, []string) { + regex = strings.TrimPrefix(regex, "\n") + var ( + re = regexp.MustCompile(regex) + rtee = &runeTee{in: tt.stdout} + matches []int + ) + tt.withKillTimeout(func() { matches = re.FindReaderSubmatchIndex(rtee) }) + output := rtee.buf.Bytes() + if matches == nil { + tt.Fatalf("Output did not match:\n---------------- (stdout text)\n%s\n---------------- (regular expression)\n%s", + output, regex) + return re, nil + } + tt.Logf("Matched stdout text:\n%s", output) + var submatches []string + for i := 0; i < len(matches); i += 2 { + submatch := string(output[matches[i]:matches[i+1]]) + submatches = append(submatches, submatch) + } + return re, submatches +} + +// ExpectExit expects the child process to exit within 5s without +// printing any additional text on stdout. +func (tt *TestCmd) ExpectExit() { + var output []byte + tt.withKillTimeout(func() { + output, _ = io.ReadAll(tt.stdout) + }) + tt.WaitExit() + if tt.Cleanup != nil { + tt.Cleanup() + } + if len(output) > 0 { + tt.Errorf("Unmatched stdout text:\n%s", output) + } +} + +func (tt *TestCmd) WaitExit() { + tt.Err = tt.cmd.Wait() +} + +func (tt *TestCmd) Interrupt() { + tt.Err = tt.cmd.Process.Signal(os.Interrupt) +} + +// ExitStatus exposes the process' OS exit code +// It will only return a valid value after the process has finished. +func (tt *TestCmd) ExitStatus() int { + if tt.Err != nil { + exitErr := tt.Err.(*exec.ExitError) + if exitErr != nil { + if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { + return status.ExitStatus() + } + } + } + return 0 +} + +// StderrText returns any stderr output written so far. +// The returned text holds all log lines after ExpectExit has +// returned. +func (tt *TestCmd) StderrText() string { + tt.stderr.mu.Lock() + defer tt.stderr.mu.Unlock() + return tt.stderr.buf.String() +} + +func (tt *TestCmd) CloseStdin() { + tt.stdin.Close() +} + +func (tt *TestCmd) Kill() { + tt.cmd.Process.Kill() + if tt.Cleanup != nil { + tt.Cleanup() + } +} + +func (tt *TestCmd) withKillTimeout(fn func()) { + timeout := time.AfterFunc(5*time.Second, func() { + tt.Log("killing the child process (timeout)") + tt.Kill() + }) + defer timeout.Stop() + fn() +} + +// testlogger logs all written lines via t.Log and also +// collects them for later inspection. +type testlogger struct { + t *testing.T + mu sync.Mutex + buf bytes.Buffer + name string +} + +func (tl *testlogger) Write(b []byte) (n int, err error) { + lines := bytes.Split(b, []byte("\n")) + for _, line := range lines { + if len(line) > 0 { + tl.t.Logf("(stderr:%v) %s", tl.name, line) + } + } + tl.mu.Lock() + tl.buf.Write(b) + tl.mu.Unlock() + return len(b), err +} + +// runeTee collects text read through it into buf. +type runeTee struct { + in interface { + io.Reader + io.ByteReader + io.RuneReader + } + buf bytes.Buffer +} + +func (rtee *runeTee) Read(b []byte) (n int, err error) { + n, err = rtee.in.Read(b) + rtee.buf.Write(b[:n]) + return n, err +} + +func (rtee *runeTee) ReadRune() (r rune, size int, err error) { + r, size, err = rtee.in.ReadRune() + if err == nil { + rtee.buf.WriteRune(r) + } + return r, size, err +} + +func (rtee *runeTee) ReadByte() (b byte, err error) { + b, err = rtee.in.ReadByte() + if err == nil { + rtee.buf.WriteByte(b) + } + return b, err +} diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 4989ab2af97..51e3a94079c 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -190,7 +190,8 @@ func (t *StateTest) RunNoVerify(rules *params.Rules, tx kv.RwTx, subtest StateSu // Prepare the EVM. txContext := core.NewEVMTxContext(msg) contractHasTEVM := func(common.Hash) (bool, error) { return false, nil } - context := core.NewEVMBlockContext(block.Header(), nil, nil, &t.json.Env.Coinbase, contractHasTEVM) + header := block.Header() + context := core.NewEVMBlockContext(header, core.GetHashFn(header, nil), nil, &t.json.Env.Coinbase, contractHasTEVM) context.GetHash = vmTestBlockHash if baseFee != nil { context.BaseFee = new(uint256.Int) diff --git a/turbo/transactions/tracing.go b/turbo/transactions/tracing.go index 3c3a2b3b557..d0d569c93fc 100644 --- a/turbo/transactions/tracing.go +++ b/turbo/transactions/tracing.go @@ -42,7 +42,8 @@ func ComputeTxEnv(ctx context.Context, block *types.Block, cfg *params.ChainConf // Recompute transactions up to the target index. signer := types.MakeSigner(cfg, block.NumberU64()) - BlockContext := core.NewEVMBlockContext(block.Header(), getHeader, engine, nil, contractHasTEVM) + header := block.Header() + BlockContext := core.NewEVMBlockContext(header, core.GetHashFn(header, getHeader), engine, nil, contractHasTEVM) vmenv := vm.NewEVM(BlockContext, vm.TxContext{}, statedb, cfg, vm.Config{}) rules := vmenv.ChainRules() for idx, tx := range block.Transactions() {