From c48e936b7070f57d744f9ed9f7b66bf868bff67d Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Mon, 4 Nov 2024 04:05:44 +0800 Subject: [PATCH 1/7] build: use slices.Clone for copying slice (#30716) --- build/ci.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/ci.go b/build/ci.go index 680bdc9a9c..754d88a86a 100644 --- a/build/ci.go +++ b/build/ci.go @@ -53,6 +53,7 @@ import ( "path" "path/filepath" "runtime" + "slices" "strings" "time" @@ -226,8 +227,7 @@ func doInstall(cmdline []string) { // Do the build! for _, pkg := range packages { - args := make([]string, len(gobuild.Args)) - copy(args, gobuild.Args) + args := slices.Clone(gobuild.Args) args = append(args, "-o", executablePath(path.Base(pkg))) args = append(args, pkg) build.MustRun(&exec.Cmd{Path: gobuild.Path, Args: args, Env: gobuild.Env}) From 6e1fedb12a22a82da5b9c9c327fb0603917a9d26 Mon Sep 17 00:00:00 2001 From: Martin HS Date: Mon, 4 Nov 2024 10:49:23 +0100 Subject: [PATCH 2/7] tests/fuzzers/bls12381: more verbose fuzzing-output (#30724) This PR updates the fuzzing verbosity a bit, in case of mismatches --- tests/fuzzers/bls12381/bls12381_fuzz.go | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/tests/fuzzers/bls12381/bls12381_fuzz.go b/tests/fuzzers/bls12381/bls12381_fuzz.go index 4efc749b6f..74ea6f52a7 100644 --- a/tests/fuzzers/bls12381/bls12381_fuzz.go +++ b/tests/fuzzers/bls12381/bls12381_fuzz.go @@ -257,8 +257,12 @@ func fuzzCrossG1MultiExp(data []byte) int { cp.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{}) // compare result - if !(bytes.Equal(cp.Marshal(), g1.ToBytes(&kp))) { - panic("G1 multi exponentiation mismatch gnark / geth ") + gnarkRes := cp.Marshal() + gethRes := g1.ToBytes(&kp) + if !bytes.Equal(gnarkRes, gethRes) { + msg := fmt.Sprintf("G1 multi exponentiation mismatch gnark/geth.\ngnark: %x\ngeth: %x\ninput: %x\n ", + gnarkRes, gethRes, data) + panic(msg) } return 1 @@ -283,15 +287,18 @@ func getG1Points(input io.Reader) (*bls12381.PointG1, *gnark.G1Affine, *blst.P1A if err != nil { panic(fmt.Sprintf("Could not marshal gnark.G1 -> geth.G1: %v", err)) } - if !bytes.Equal(g1.ToBytes(kp), cpBytes) { - panic("bytes(gnark.G1) != bytes(geth.G1)") + + gnarkRes := g1.ToBytes(kp) + if !bytes.Equal(gnarkRes, cpBytes) { + panic(fmt.Sprintf("bytes(gnark.G1) != bytes(geth.G1)\ngnark.G1: %x\ngeth.G1: %x\n", gnarkRes, cpBytes)) } // marshal gnark point -> blst point scalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(s.Bytes(), 32)) p1 := new(blst.P1Affine).From(scalar) - if !bytes.Equal(p1.Serialize(), cpBytes) { - panic("bytes(blst.G1) != bytes(geth.G1)") + blstRes := p1.Serialize() + if !bytes.Equal(blstRes, cpBytes) { + panic(fmt.Sprintf("bytes(blst.G1) != bytes(geth.G1)\nblst.G1: %x\ngeth.G1: %x\n", blstRes, cpBytes)) } return kp, cp, p1, nil @@ -316,8 +323,10 @@ func getG2Points(input io.Reader) (*bls12381.PointG2, *gnark.G2Affine, *blst.P2A if err != nil { panic(fmt.Sprintf("Could not marshal gnark.G2 -> geth.G2: %v", err)) } - if !bytes.Equal(g2.ToBytes(kp), cpBytes) { - panic("bytes(gnark.G2) != bytes(geth.G2)") + + gnarkRes := g2.ToBytes(kp) + if !bytes.Equal(gnarkRes, cpBytes) { + panic(fmt.Sprintf("bytes(gnark.G2) != bytes(geth.G2)\ngnark.G2: %x\ngeth.G2: %x\n", gnarkRes, cpBytes)) } // marshal gnark point -> blst point From 484f0f4e84c94c02a12a563b1512030cd5b05489 Mon Sep 17 00:00:00 2001 From: piersy Date: Mon, 4 Nov 2024 10:32:41 +0000 Subject: [PATCH 3/7] core/txpool: improve error responses with wrapped errors (#30715) --- core/txpool/txpool.go | 2 +- core/txpool/validation.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 363fa29c02..54ae3be569 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -358,7 +358,7 @@ func (p *TxPool) Add(txs []*types.Transaction, local bool, sync bool) []error { for i, split := range splits { // If the transaction was rejected by all subpools, mark it unsupported if split == -1 { - errs[i] = core.ErrTxTypeNotSupported + errs[i] = fmt.Errorf("%w: received type %d", core.ErrTxTypeNotSupported, txs[i].Type()) continue } // Find which subpool handled it and pull in the corresponding error diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 7fd5f8bc79..5ff92d71c2 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -99,7 +99,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types } // Make sure the transaction is signed properly if _, err := types.Sender(signer, tx); err != nil { - return ErrInvalidSender + return fmt.Errorf("%w: %v", ErrInvalidSender, err) } // Ensure the transaction has more gas than the bare minimum needed to cover // the transaction metadata From 7d6e153fd5ee1ed932e77f9741162f91d7c373e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 4 Nov 2024 12:33:42 +0200 Subject: [PATCH 4/7] eth/catalyst: make engine api test time independent (#30713) This test depends on a 100ms timer, which fails quite often, messing up our pipelines. Hook directly into the internal version of getPayload which has the capacity to wait for the full payload before returning. This might not be absolutely correct from a test perspective, but it beats failing ci. The alternative would be to expose the full build hook into the outside, but it might be a bit overkill for this scenario. --- eth/catalyst/api_test.go | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 7bb31329c6..3ac719c23e 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -208,7 +208,6 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { t.Fatalf("error preparing payload, err=%v", err) } // give the payload some time to be built - time.Sleep(100 * time.Millisecond) payloadID := (&miner.BuildPayloadArgs{ Parent: fcState.HeadBlockHash, Timestamp: blockParams.Timestamp, @@ -217,12 +216,12 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { BeaconRoot: blockParams.BeaconRoot, Version: engine.PayloadV1, }).Id() - execData, err := api.GetPayloadV1(payloadID) + execData, err := api.getPayload(payloadID, true) if err != nil { t.Fatalf("error getting payload, err=%v", err) } - if len(execData.Transactions) != blocks[9].Transactions().Len() { - t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) + if len(execData.ExecutionPayload.Transactions) != blocks[9].Transactions().Len() { + t.Fatalf("invalid number of transactions %d != 1", len(execData.ExecutionPayload.Transactions)) } // Test invalid payloadID var invPayload engine.PayloadID @@ -453,7 +452,6 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) } mcfg := miner.DefaultConfig - mcfg.PendingFeeRecipient = testAddr ethcfg := ðconfig.Config{Genesis: genesis, SyncMode: downloader.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: mcfg} ethservice, err := eth.New(n, ethcfg) if err != nil { @@ -628,7 +626,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { SafeBlockHash: common.Hash{}, FinalizedBlockHash: common.Hash{}, } - payload *engine.ExecutableData + payload *engine.ExecutionPayloadEnvelope resp engine.ForkChoiceResponse err error ) @@ -640,11 +638,10 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status) } // give the payload some time to be built - time.Sleep(50 * time.Millisecond) - if payload, err = api.GetPayloadV1(*resp.PayloadID); err != nil { + if payload, err = api.getPayload(*resp.PayloadID, true); err != nil { t.Fatalf("can't get payload: %v", err) } - if len(payload.Transactions) > 0 { + if len(payload.ExecutionPayload.Transactions) > 0 { break } // No luck this time we need to update the params and try again. @@ -653,7 +650,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { t.Fatalf("payload should not be empty") } } - execResp, err := api.NewPayloadV1(*payload) + execResp, err := api.NewPayloadV1(*payload.ExecutionPayload) if err != nil { t.Fatalf("can't execute payload: %v", err) } @@ -661,14 +658,14 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { t.Fatalf("invalid status: %v", execResp.Status) } fcState = engine.ForkchoiceStateV1{ - HeadBlockHash: payload.BlockHash, - SafeBlockHash: payload.ParentHash, - FinalizedBlockHash: payload.ParentHash, + HeadBlockHash: payload.ExecutionPayload.BlockHash, + SafeBlockHash: payload.ExecutionPayload.ParentHash, + FinalizedBlockHash: payload.ExecutionPayload.ParentHash, } if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { t.Fatalf("Failed to insert block: %v", err) } - if ethservice.BlockChain().CurrentBlock().Number.Uint64() != payload.Number { + if ethservice.BlockChain().CurrentBlock().Number.Uint64() != payload.ExecutionPayload.Number { t.Fatalf("Chain head should be updated") } parent = ethservice.BlockChain().CurrentBlock() @@ -1736,9 +1733,6 @@ func TestWitnessCreationAndConsumption(t *testing.T) { if err != nil { t.Fatalf("error preparing payload, err=%v", err) } - // Give the payload some time to be built - time.Sleep(100 * time.Millisecond) - payloadID := (&miner.BuildPayloadArgs{ Parent: fcState.HeadBlockHash, Timestamp: blockParams.Timestamp, @@ -1748,7 +1742,7 @@ func TestWitnessCreationAndConsumption(t *testing.T) { BeaconRoot: blockParams.BeaconRoot, Version: engine.PayloadV3, }).Id() - envelope, err := api.GetPayloadV3(payloadID) + envelope, err := api.getPayload(payloadID, true) if err != nil { t.Fatalf("error getting payload, err=%v", err) } From 014e2b037f5fcc8b7abccf6f17e6f3beed7216f5 Mon Sep 17 00:00:00 2001 From: Martin HS Date: Mon, 4 Nov 2024 11:39:06 +0100 Subject: [PATCH 5/7] core/vm/runtime: invoke tx-end hook (#30711) When using the `core/vm/runtime` helpers to execute code, callbacks for the tx end were not invoked. This change fixes it by invoking them. --- core/vm/runtime/runtime.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 2ad991ee41..2243e14b65 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -142,13 +142,16 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { // set the receiver's (the executing contract) code for execution. cfg.State.SetCode(address, code) // Call the code with the given configuration. - ret, _, err := vmenv.Call( + ret, leftOverGas, err := vmenv.Call( sender, common.BytesToAddress([]byte("contract")), input, cfg.GasLimit, uint256.MustFromBig(cfg.Value), ) + if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil { + cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err) + } return ret, cfg.State, err } @@ -181,6 +184,9 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { cfg.GasLimit, uint256.MustFromBig(cfg.Value), ) + if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil { + cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err) + } return code, address, leftOverGas, err } @@ -214,5 +220,8 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er cfg.GasLimit, uint256.MustFromBig(cfg.Value), ) + if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil { + cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err) + } return ret, leftOverGas, err } From 06cbc80754c3930828cc605f151554bc7ec36eb9 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Mon, 4 Nov 2024 14:19:50 +0100 Subject: [PATCH 6/7] core, trie: verkle state processor tests (#30672) Tests that are crucial to for verifying the verkle testnet functions properly. --------- Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Co-authored-by: Ignacio Hagopian Co-authored-by: Gary Rong Co-authored-by: Martin HS --- consensus/beacon/consensus.go | 12 +- core/state/access_events.go | 2 +- core/state/statedb.go | 3 +- core/state_processor_test.go | 194 ------ core/verkle_witness_test.go | 1087 +++++++++++++++++++++++++++++++++ trie/utils/verkle.go | 18 +- trie/verkle.go | 50 +- 7 files changed, 1142 insertions(+), 224 deletions(-) create mode 100644 core/verkle_witness_test.go diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 4ee19c7d4d..cdacf354a5 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -398,21 +398,25 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea if parent == nil { return nil, fmt.Errorf("nil parent header for block %d", header.Number) } - preTrie, err := state.Database().OpenTrie(parent.Root) if err != nil { return nil, fmt.Errorf("error opening pre-state tree root: %w", err) } - vktPreTrie, okpre := preTrie.(*trie.VerkleTrie) vktPostTrie, okpost := state.GetTrie().(*trie.VerkleTrie) + + // The witness is only attached iff both parent and current block are + // using verkle tree. if okpre && okpost { if len(keys) > 0 { - verkleProof, stateDiff, err := vktPreTrie.Proof(vktPostTrie, keys, vktPreTrie.FlatdbNodeResolver) + verkleProof, stateDiff, err := vktPreTrie.Proof(vktPostTrie, keys) if err != nil { return nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err) } - block = block.WithWitness(&types.ExecutionWitness{StateDiff: stateDiff, VerkleProof: verkleProof}) + block = block.WithWitness(&types.ExecutionWitness{ + StateDiff: stateDiff, + VerkleProof: verkleProof, + }) } } } diff --git a/core/state/access_events.go b/core/state/access_events.go index 7f67df64eb..b745c383b1 100644 --- a/core/state/access_events.go +++ b/core/state/access_events.go @@ -117,7 +117,7 @@ func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) return gas } -// ContractCreateCPreheck charges access costs before +// ContractCreatePreCheckGas charges access costs before // a contract creation is initiated. It is just reads, because the // address collision is done before the transfer, and so no write // are guaranteed to happen at this point. diff --git a/core/state/statedb.go b/core/state/statedb.go index 775a224af9..d855e5626d 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1068,7 +1068,8 @@ func (s *StateDB) handleDestruction() (map[common.Hash]*accountDelete, []*trieno deletes[addrHash] = op // Short circuit if the origin storage was empty. - if prev.Root == types.EmptyRootHash { + + if prev.Root == types.EmptyRootHash || s.db.TrieDB().IsVerkle() { continue } // Remove storage slots belonging to the account. diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 2a16ef2cfb..f3d2304690 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -18,7 +18,6 @@ package core import ( "crypto/ecdsa" - "encoding/binary" "math" "math/big" "testing" @@ -30,14 +29,11 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" - "github.com/ethereum/go-ethereum/triedb" - "github.com/ethereum/go-verkle" "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) @@ -426,193 +422,3 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr } return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)) } - -var ( - code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`) - intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, true, true, true, true) - // A contract creation that calls EXTCODECOPY in the constructor. Used to ensure that the witness - // will not contain that copied data. - // Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985 - codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`) - intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, true, true, true, true) -) - -func TestProcessVerkle(t *testing.T) { - var ( - config = ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - Ethash: new(params.EthashConfig), - ShanghaiTime: u64(0), - VerkleTime: u64(0), - TerminalTotalDifficulty: common.Big0, - } - signer = types.LatestSigner(config) - testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain - coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") - gspec = &Genesis{ - Config: config, - Alloc: GenesisAlloc{ - coinbase: GenesisAccount{ - Balance: big.NewInt(1000000000000000000), // 1 ether - Nonce: 0, - }, - }, - } - ) - // Verkle trees use the snapshot, which must be enabled before the - // data is saved into the tree+database. - // genesis := gspec.MustCommit(bcdb, triedb) - cacheConfig := DefaultCacheConfigWithScheme("path") - cacheConfig.SnapshotLimit = 0 - blockchain, _ := NewBlockChain(bcdb, cacheConfig, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) - defer blockchain.Stop() - - txCost1 := params.TxGas - txCost2 := params.TxGas - contractCreationCost := intrinsicContractCreationGas + - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */ - 739 /* execution costs */ - codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (tx) */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at pc=0x20) */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */ - params.WitnessChunkReadCost + /* SLOAD in constructor */ - params.WitnessChunkWriteCost + /* SSTORE in constructor */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at PC=0x121) */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */ - params.WitnessChunkReadCost + /* SLOAD in constructor */ - params.WitnessChunkWriteCost + /* SSTORE in constructor */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash for tx creation */ - 15*(params.WitnessChunkReadCost+params.WitnessChunkWriteCost) + /* code chunks #0..#14 */ - 4844 /* execution costs */ - blockGasUsagesExpected := []uint64{ - txCost1*2 + txCost2, - txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas, - } - _, chain, _, proofs, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { - gen.SetPoS() - - // TODO need to check that the tx cost provided is the exact amount used (no remaining left-over) - tx, _ := types.SignTx(types.NewTransaction(uint64(i)*3, common.Address{byte(i), 2, 3}, big.NewInt(999), txCost1, big.NewInt(875000000), nil), signer, testKey) - gen.AddTx(tx) - tx, _ = types.SignTx(types.NewTransaction(uint64(i)*3+1, common.Address{}, big.NewInt(999), txCost1, big.NewInt(875000000), nil), signer, testKey) - gen.AddTx(tx) - tx, _ = types.SignTx(types.NewTransaction(uint64(i)*3+2, common.Address{}, big.NewInt(0), txCost2, big.NewInt(875000000), nil), signer, testKey) - gen.AddTx(tx) - - // Add two contract creations in block #2 - if i == 1 { - tx, _ = types.SignTx(types.NewContractCreation(6, big.NewInt(16), 3000000, big.NewInt(875000000), code), signer, testKey) - gen.AddTx(tx) - - tx, _ = types.SignTx(types.NewContractCreation(7, big.NewInt(0), 3000000, big.NewInt(875000000), codeWithExtCodeCopy), signer, testKey) - gen.AddTx(tx) - } - }) - - // Check proof for both blocks - err := verkle.Verify(proofs[0], gspec.ToBlock().Root().Bytes(), chain[0].Root().Bytes(), statediffs[0]) - if err != nil { - t.Fatal(err) - } - err = verkle.Verify(proofs[1], chain[0].Root().Bytes(), chain[1].Root().Bytes(), statediffs[1]) - if err != nil { - t.Fatal(err) - } - - t.Log("verified verkle proof, inserting blocks into the chain") - - endnum, err := blockchain.InsertChain(chain) - if err != nil { - t.Fatalf("block %d imported with error: %v", endnum, err) - } - - for i := 0; i < 2; i++ { - b := blockchain.GetBlockByNumber(uint64(i) + 1) - if b == nil { - t.Fatalf("expected block %d to be present in chain", i+1) - } - if b.Hash() != chain[i].Hash() { - t.Fatalf("block #%d not found at expected height", b.NumberU64()) - } - if b.GasUsed() != blockGasUsagesExpected[i] { - t.Fatalf("expected block #%d txs to use %d, got %d\n", b.NumberU64(), blockGasUsagesExpected[i], b.GasUsed()) - } - } -} - -func TestProcessParentBlockHash(t *testing.T) { - var ( - chainConfig = params.MergedTestChainConfig - hashA = common.Hash{0x01} - hashB = common.Hash{0x02} - header = &types.Header{ParentHash: hashA, Number: big.NewInt(2), Difficulty: big.NewInt(0)} - parent = &types.Header{ParentHash: hashB, Number: big.NewInt(1), Difficulty: big.NewInt(0)} - coinbase = common.Address{} - ) - test := func(statedb *state.StateDB) { - statedb.SetNonce(params.HistoryStorageAddress, 1) - statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode) - statedb.IntermediateRoot(true) - - vmContext := NewEVMBlockContext(header, nil, &coinbase) - evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{}) - ProcessParentBlockHash(header.ParentHash, evm, statedb) - - vmContext = NewEVMBlockContext(parent, nil, &coinbase) - evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{}) - ProcessParentBlockHash(parent.ParentHash, evm, statedb) - - // make sure that the state is correct - if have := getParentBlockHash(statedb, 1); have != hashA { - t.Errorf("want parent hash %v, have %v", hashA, have) - } - if have := getParentBlockHash(statedb, 0); have != hashB { - t.Errorf("want parent hash %v, have %v", hashB, have) - } - } - t.Run("MPT", func(t *testing.T) { - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) - test(statedb) - }) - t.Run("Verkle", func(t *testing.T) { - db := rawdb.NewMemoryDatabase() - cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme) - cacheConfig.SnapshotLimit = 0 - triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true)) - statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, nil)) - test(statedb) - }) -} - -func getParentBlockHash(statedb *state.StateDB, number uint64) common.Hash { - ringIndex := number % params.HistoryServeWindow - var key common.Hash - binary.BigEndian.PutUint64(key[24:], ringIndex) - return statedb.GetState(params.HistoryStorageAddress, key) -} diff --git a/core/verkle_witness_test.go b/core/verkle_witness_test.go new file mode 100644 index 0000000000..5a4210cdab --- /dev/null +++ b/core/verkle_witness_test.go @@ -0,0 +1,1087 @@ +// Copyright 2024 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 core + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "math/big" + "slices" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/beacon" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie/utils" + "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-verkle" + "github.com/holiman/uint256" +) + +var ( + testVerkleChainConfig = ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + Ethash: new(params.EthashConfig), + ShanghaiTime: u64(0), + VerkleTime: u64(0), + TerminalTotalDifficulty: common.Big0, + // TODO uncomment when proof generation is merged + // ProofInBlocks: true, + } + testKaustinenLikeChainConfig = ¶ms.ChainConfig{ + ChainID: big.NewInt(69420), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + Ethash: new(params.EthashConfig), + ShanghaiTime: u64(0), + VerkleTime: u64(0), + TerminalTotalDifficulty: common.Big0, + } +) + +func TestProcessVerkle(t *testing.T) { + var ( + code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`) + intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, true, true, true, true) + // A contract creation that calls EXTCODECOPY in the constructor. Used to ensure that the witness + // will not contain that copied data. + // Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985 + codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`) + intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, true, true, true, true) + signer = types.LatestSigner(testVerkleChainConfig) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain + coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") + gspec = &Genesis{ + Config: testVerkleChainConfig, + Alloc: GenesisAlloc{ + coinbase: { + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0}, + params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode, Balance: common.Big0}, + params.WithdrawalQueueAddress: {Nonce: 1, Code: params.WithdrawalQueueCode, Balance: common.Big0}, + params.ConsolidationQueueAddress: {Nonce: 1, Code: params.ConsolidationQueueCode, Balance: common.Big0}, + }, + } + ) + // Verkle trees use the snapshot, which must be enabled before the + // data is saved into the tree+database. + // genesis := gspec.MustCommit(bcdb, triedb) + cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme) + cacheConfig.SnapshotLimit = 0 + blockchain, _ := NewBlockChain(bcdb, cacheConfig, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) + defer blockchain.Stop() + + txCost1 := params.TxGas + txCost2 := params.TxGas + contractCreationCost := intrinsicContractCreationGas + + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */ + 739 /* execution costs */ + codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (tx) */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at pc=0x20) */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */ + params.WitnessChunkReadCost + /* SLOAD in constructor */ + params.WitnessChunkWriteCost + /* SSTORE in constructor */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at PC=0x121) */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */ + params.WitnessChunkReadCost + /* SLOAD in constructor */ + params.WitnessChunkWriteCost + /* SSTORE in constructor */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash for tx creation */ + 15*(params.WitnessChunkReadCost+params.WitnessChunkWriteCost) + /* code chunks #0..#14 */ + 4844 /* execution costs */ + blockGasUsagesExpected := []uint64{ + txCost1*2 + txCost2, + txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas, + } + _, chain, _, proofs, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + gen.SetPoS() + + // TODO need to check that the tx cost provided is the exact amount used (no remaining left-over) + tx, _ := types.SignTx(types.NewTransaction(uint64(i)*3, common.Address{byte(i), 2, 3}, big.NewInt(999), txCost1, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + tx, _ = types.SignTx(types.NewTransaction(uint64(i)*3+1, common.Address{}, big.NewInt(999), txCost1, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + tx, _ = types.SignTx(types.NewTransaction(uint64(i)*3+2, common.Address{}, big.NewInt(0), txCost2, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + + // Add two contract creations in block #2 + if i == 1 { + tx, _ = types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 6, + Value: big.NewInt(16), + Gas: 3000000, + GasPrice: big.NewInt(875000000), + Data: code, + }) + gen.AddTx(tx) + + tx, _ = types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 7, + Value: big.NewInt(0), + Gas: 3000000, + GasPrice: big.NewInt(875000000), + Data: codeWithExtCodeCopy, + }) + gen.AddTx(tx) + } + }) + + // Check proof for both blocks + err := verkle.Verify(proofs[0], gspec.ToBlock().Root().Bytes(), chain[0].Root().Bytes(), statediffs[0]) + if err != nil { + t.Fatal(err) + } + err = verkle.Verify(proofs[1], chain[0].Root().Bytes(), chain[1].Root().Bytes(), statediffs[1]) + if err != nil { + t.Fatal(err) + } + + t.Log("verified verkle proof, inserting blocks into the chain") + + endnum, err := blockchain.InsertChain(chain) + if err != nil { + t.Fatalf("block %d imported with error: %v", endnum, err) + } + + for i := 0; i < 2; i++ { + b := blockchain.GetBlockByNumber(uint64(i) + 1) + if b == nil { + t.Fatalf("expected block %d to be present in chain", i+1) + } + if b.Hash() != chain[i].Hash() { + t.Fatalf("block #%d not found at expected height", b.NumberU64()) + } + if b.GasUsed() != blockGasUsagesExpected[i] { + t.Fatalf("expected block #%d txs to use %d, got %d\n", b.NumberU64(), blockGasUsagesExpected[i], b.GasUsed()) + } + } +} + +func TestProcessParentBlockHash(t *testing.T) { + // This test uses blocks where, + // block 1 parent hash is 0x0100.... + // block 2 parent hash is 0x0200.... + // etc + checkBlockHashes := func(statedb *state.StateDB) { + statedb.SetNonce(params.HistoryStorageAddress, 1) + statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode) + // Process n blocks, from 1 .. num + var num = 2 + for i := 1; i <= num; i++ { + header := &types.Header{ParentHash: common.Hash{byte(i)}, Number: big.NewInt(int64(i)), Difficulty: new(big.Int)} + vmContext := NewEVMBlockContext(header, nil, new(common.Address)) + evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, params.MergedTestChainConfig, vm.Config{}) + ProcessParentBlockHash(header.ParentHash, evm, statedb) + } + // Read block hashes for block 0 .. num-1 + for i := 0; i < num; i++ { + have, want := getContractStoredBlockHash(statedb, uint64(i)), common.Hash{byte(i + 1)} + if have != want { + t.Errorf("block %d, have parent hash %v, want %v", i, have, want) + } + } + } + t.Run("MPT", func(t *testing.T) { + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + checkBlockHashes(statedb) + }) + t.Run("Verkle", func(t *testing.T) { + db := rawdb.NewMemoryDatabase() + cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme) + cacheConfig.SnapshotLimit = 0 + triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true)) + statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, nil)) + checkBlockHashes(statedb) + }) +} + +// getContractStoredBlockHash is a utility method which reads the stored parent blockhash for block 'number' +func getContractStoredBlockHash(statedb *state.StateDB, number uint64) common.Hash { + ringIndex := number % params.HistoryServeWindow + var key common.Hash + binary.BigEndian.PutUint64(key[24:], ringIndex) + return statedb.GetState(params.HistoryStorageAddress, key) +} + +// TestProcessVerkleInvalidContractCreation checks for several modes of contract creation failures +func TestProcessVerkleInvalidContractCreation(t *testing.T) { + var ( + account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = verkleTestGenesis(testKaustinenLikeChainConfig) + ) + // slightly modify it to suit the live txs from the testnet + gspec.Alloc[account2] = types.Account{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 1, + } + + // Create two blocks that reproduce what is happening on kaustinen. + // - The first block contains two failing contract creation transactions, that + // write to storage before they revert. + // + // - The second block contains a single failing contract creation transaction, + // that fails right off the bat. + _, chain, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + gen.SetPoS() + + if i == 0 { + for _, rlpData := range []string{ + // SSTORE at slot 41 and reverts + "f8d48084479c2c18830186a08080b8806000602955bda3f9600060ca55600060695523b360006039551983576000601255b0620c2fde2c592ac2600060bc55e0ac6000606455a63e22600060e655eb607e605c5360a2605d5360c7605e53601d605f5360eb606053606b606153608e60625360816063536079606453601e60655360fc60665360b7606753608b60685383021e7ca0cc20c65a97d2e526b8ec0f4266e8b01bdcde43b9aeb59d8bfb44e8eb8119c109a07a8e751813ae1b2ce734960dbc39a4f954917d7822a2c5d1dca18b06c584131f", + // SSTORE at slot 133 and reverts + "02f8db83010f2c01843b9aca0084479c2c18830186a08080b88060006085553fad6000600a55600060565555600060b55506600060cf557f1b8b38183e7bd1bdfaa7123c5a4976e54cce0e42049d841411978fd3595e25c66019527f0538943712953cf08900aae40222a40b2d5a4ac8075ad8cf0870e2be307edbb96039527f9f3174ff85024747041ae7a611acffb987c513c088d90ab288aec080a0cd6ac65ce2cb0a912371f6b5a551ba8caffc22ec55ad4d3cb53de41d05eb77b6a02e0dfe8513dfa6ec7bfd7eda6f5c0dac21b39b982436045e128cec46cfd3f960", + // this one is a simple transfer that succeeds, necessary to get the correct nonce in the other block. + "f8e80184479c2c18830186a094bbbbde4ca27f83fc18aa108170547ff57675936a80b8807ff71f7c15faadb969a76a5f54a81a0117e1e743cb7f24e378eda28442ea4c6eb6604a527fb5409e5718d44e23bfffac926e5ea726067f772772e7e19446acba0c853f62f5606a526020608a536088608b536039608c536004608d5360af608e537f7f7675d9f210e0a61564e6d11e7cd75f5bc9009ac9f6b94a0fc63035441a83021e7ba04a4a172d81ebb02847829b76a387ac09749c8b65668083699abe20c887fb9efca07c5b1a990702ec7b31a5e8e3935cd9a77649f8c25a84131229e24ab61aec6093", + } { + var tx = new(types.Transaction) + if err := tx.UnmarshalBinary(common.Hex2Bytes(rlpData)); err != nil { + t.Fatal(err) + } + gen.AddTx(tx) + } + } else { + var tx = new(types.Transaction) + // immediately reverts + if err := tx.UnmarshalBinary(common.Hex2Bytes("01f8d683010f2c028443ad7d0e830186a08080b880b00e7fa3c849dce891cce5fae8a4c46cbb313d6aec0c0ffe7863e05fb7b22d4807674c6055527ffbfcb0938f3e18f7937aa8fa95d880afebd5c4cec0d85186095832d03c85cf8a60755260ab60955360cf6096536066609753606e60985360fa609953609e609a53608e609b536024609c5360f6609d536072609e5360a4609fc080a08fc6f7101f292ff1fb0de8ac69c2d320fbb23bfe61cf327173786ea5daee6e37a044c42d91838ef06646294bf4f9835588aee66243b16a66a2da37641fae4c045f")); err != nil { + t.Fatal(err) + } + gen.AddTx(tx) + } + }) + + tx1ContractAddress := crypto.CreateAddress(account1, 0) + tx1ContractStem := utils.GetTreeKey(tx1ContractAddress[:], uint256.NewInt(0), 105) + tx1ContractStem = tx1ContractStem[:31] + + tx2ContractAddress := crypto.CreateAddress(account2, 1) + tx2SlotKey := [32]byte{} + tx2SlotKey[31] = 133 + tx2ContractStem := utils.StorageSlotKey(tx2ContractAddress[:], tx2SlotKey[:]) + tx2ContractStem = tx2ContractStem[:31] + + eip2935Stem := utils.GetTreeKey(params.HistoryStorageAddress[:], uint256.NewInt(0), 0) + eip2935Stem = eip2935Stem[:31] + + // Check that the witness contains what we expect: a storage entry for each of the two contract + // creations that failed: one at 133 for the 2nd tx, and one at 105 for the first tx. + for _, stemStateDiff := range statediffs[0] { + // Check that the slot number 133, which is overflowing the account header, + // is present. Note that the offset of the 2nd group (first group after the + // header) is skipping the first 64 values, hence we still have an offset + // of 133, and not 133 - 64. + if bytes.Equal(stemStateDiff.Stem[:], tx2ContractStem[:]) { + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if suffixDiff.Suffix != 133 { + t.Fatalf("invalid suffix diff found for %x in block #1: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + if suffixDiff.CurrentValue != nil { + t.Fatalf("invalid prestate value found for %x in block #1: %v != nil\n", stemStateDiff.Stem, suffixDiff.CurrentValue) + } + if suffixDiff.NewValue != nil { + t.Fatalf("invalid poststate value found for %x in block #1: %v != nil\n", stemStateDiff.Stem, suffixDiff.NewValue) + } + } + } else if bytes.Equal(stemStateDiff.Stem[:], tx1ContractStem) { + // For this contract creation, check that only the accound header and storage slot 41 + // are found in the witness. + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if suffixDiff.Suffix != 105 && suffixDiff.Suffix != 0 && suffixDiff.Suffix != 1 { + t.Fatalf("invalid suffix diff found for %x in block #1: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + } + } else if bytes.Equal(stemStateDiff.Stem[:], eip2935Stem) { + // Check the eip 2935 group of leaves. + // Check that only one leaf was accessed, and is present in the witness. + if len(stemStateDiff.SuffixDiffs) > 1 { + t.Fatalf("invalid suffix diff count found for BLOCKHASH contract: %d != 1", len(stemStateDiff.SuffixDiffs)) + } + // Check that this leaf is the first storage slot + if stemStateDiff.SuffixDiffs[0].Suffix != 64 { + t.Fatalf("invalid suffix diff value found for BLOCKHASH contract: %d != 64", stemStateDiff.SuffixDiffs[0].Suffix) + } + // check that the prestate value is nil and that the poststate value isn't. + if stemStateDiff.SuffixDiffs[0].CurrentValue != nil { + t.Fatalf("non-nil current value in BLOCKHASH contract insert: %x", stemStateDiff.SuffixDiffs[0].CurrentValue) + } + if stemStateDiff.SuffixDiffs[0].NewValue == nil { + t.Fatalf("nil new value in BLOCKHASH contract insert") + } + if *stemStateDiff.SuffixDiffs[0].NewValue != chain[0].Hash() { + t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, chain[0].Hash()) + } + } else { + // For all other entries present in the witness, check that nothing beyond + // the account header was accessed. + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if suffixDiff.Suffix > 2 { + t.Fatalf("invalid suffix diff found for %x in block #1: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + } + } + } + + // Check that no account has a value above 4 in the 2nd block as no storage nor + // code should make it to the witness. + for _, stemStateDiff := range statediffs[1] { + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if bytes.Equal(stemStateDiff.Stem[:], eip2935Stem) { + // BLOCKHASH contract stem + if len(stemStateDiff.SuffixDiffs) > 1 { + t.Fatalf("invalid suffix diff count found for BLOCKHASH contract at block #2: %d != 1", len(stemStateDiff.SuffixDiffs)) + } + if stemStateDiff.SuffixDiffs[0].Suffix != 65 { + t.Fatalf("invalid suffix diff value found for BLOCKHASH contract at block #2: %d != 65", stemStateDiff.SuffixDiffs[0].Suffix) + } + if stemStateDiff.SuffixDiffs[0].NewValue == nil { + t.Fatalf("missing post state value for BLOCKHASH contract at block #2") + } + if *stemStateDiff.SuffixDiffs[0].NewValue != common.HexToHash("0788c2c0f23aa07eb8bf76fe6c1ca9064a4821c1fd0af803913da488a58dba54") { + t.Fatalf("invalid post state value for BLOCKHASH contract at block #2: 0788c2c0f23aa07eb8bf76fe6c1ca9064a4821c1fd0af803913da488a58dba54 != %x", (*stemStateDiff.SuffixDiffs[0].NewValue)[:]) + } + } else if suffixDiff.Suffix > 4 { + t.Fatalf("invalid suffix diff found for %x in block #2: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + } + } +} + +func verkleTestGenesis(config *params.ChainConfig) *Genesis { + var ( + coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") + account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + ) + return &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + coinbase: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account1: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account2: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 3, + }, + params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0}, + params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode, Balance: common.Big0}, + params.WithdrawalQueueAddress: {Nonce: 1, Code: params.WithdrawalQueueCode, Balance: common.Big0}, + params.ConsolidationQueueAddress: {Nonce: 1, Code: params.ConsolidationQueueCode, Balance: common.Big0}, + }, + } +} + +// TestProcessVerkleContractWithEmptyCode checks that the witness contains all valid +// entries, if the initcode returns an empty code. +func TestProcessVerkleContractWithEmptyCode(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + gspec := verkleTestGenesis(&config) + + _, chain, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + gen.SetPoS() + var tx types.Transaction + // a transaction that does some PUSH1n but returns a 0-sized contract + txpayload := common.Hex2Bytes("02f8db83010f2d03843b9aca008444cf6a05830186a08080b8807fdfbbb59f2371a76485ce557fd0de00c298d3ede52a3eab56d35af674eb49ec5860335260826053536001605453604c60555360f3605653606060575360446058536096605953600c605a5360df605b5360f3605c5360fb605d53600c605e53609a605f53607f60605360fe606153603d60625360f4606353604b60645360cac001a0486b6dc55b8a311568b7239a2cae1d77e7446dba71df61eaafd53f73820a138fa010bd48a45e56133ac4c5645142c2ea48950d40eb35050e9510b6bad9e15c5865") + if err := tx.UnmarshalBinary(txpayload); err != nil { + t.Fatal(err) + } + gen.AddTx(&tx) + }) + + eip2935Stem := utils.GetTreeKey(params.HistoryStorageAddress[:], uint256.NewInt(0), 0) + eip2935Stem = eip2935Stem[:31] + + for _, stemStateDiff := range statediffs[0] { + // Handle the case of the history contract: make sure only the correct + // slots are added to the witness. + if bytes.Equal(stemStateDiff.Stem[:], eip2935Stem) { + // BLOCKHASH contract stem + if len(stemStateDiff.SuffixDiffs) > 1 { + t.Fatalf("invalid suffix diff count found for BLOCKHASH contract: %d != 1", len(stemStateDiff.SuffixDiffs)) + } + if stemStateDiff.SuffixDiffs[0].Suffix != 64 { + t.Fatalf("invalid suffix diff value found for BLOCKHASH contract: %d != 64", stemStateDiff.SuffixDiffs[0].Suffix) + } + // check that the "current value" is nil and that the new value isn't. + if stemStateDiff.SuffixDiffs[0].CurrentValue != nil { + t.Fatalf("non-nil current value in BLOCKHASH contract insert: %x", stemStateDiff.SuffixDiffs[0].CurrentValue) + } + if stemStateDiff.SuffixDiffs[0].NewValue == nil { + t.Fatalf("nil new value in BLOCKHASH contract insert") + } + if *stemStateDiff.SuffixDiffs[0].NewValue != chain[0].Hash() { + t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, chain[0].Hash()) + } + } else { + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if suffixDiff.Suffix > 2 { + // if d8898012c484fb48610ecb7963886339207dab004bce968b007b616ffa18e0 shows up, it means that the PUSHn + // in the transaction above added entries into the witness, when they should not have since they are + // part of a contract deployment. + t.Fatalf("invalid suffix diff found for %x in block #1: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + } + } + } +} + +// TestProcessVerkleExtCodeHashOpcode verifies that calling EXTCODEHASH on another +// deployed contract, creates all the right entries in the witness. +func TestProcessVerkleExtCodeHashOpcode(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + gspec = verkleTestGenesis(&config) + ) + dummyContract := []byte{ + byte(vm.PUSH1), 2, + byte(vm.PUSH1), 12, + byte(vm.PUSH1), 0x00, + byte(vm.CODECOPY), + + byte(vm.PUSH1), 2, + byte(vm.PUSH1), 0x00, + byte(vm.RETURN), + + byte(vm.PUSH1), 42, + } + deployer := crypto.PubkeyToAddress(testKey.PublicKey) + dummyContractAddr := crypto.CreateAddress(deployer, 0) + + // contract that calls EXTCODEHASH on the dummy contract + extCodeHashContract := []byte{ + byte(vm.PUSH1), 22, + byte(vm.PUSH1), 12, + byte(vm.PUSH1), 0x00, + byte(vm.CODECOPY), + + byte(vm.PUSH1), 22, + byte(vm.PUSH1), 0x00, + byte(vm.RETURN), + + byte(vm.PUSH20), + 0x3a, 0x22, 0x0f, 0x35, 0x12, 0x52, 0x08, 0x9d, 0x38, 0x5b, 0x29, 0xbe, 0xca, 0x14, 0xe2, 0x7f, 0x20, 0x4c, 0x29, 0x6a, + byte(vm.EXTCODEHASH), + } + extCodeHashContractAddr := crypto.CreateAddress(deployer, 1) + + _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + gen.SetPoS() + + if i == 0 { + // Create dummy contract. + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(0), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: dummyContract, + }) + gen.AddTx(tx) + + // Create contract with EXTCODEHASH opcode. + tx, _ = types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 1, + Value: big.NewInt(0), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: extCodeHashContract}) + gen.AddTx(tx) + } else { + tx, _ := types.SignTx(types.NewTransaction(2, extCodeHashContractAddr, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + } + }) + + contractKeccakTreeKey := utils.CodeHashKey(dummyContractAddr[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[1] { + if bytes.Equal(stemStateDiff.Stem[:], contractKeccakTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + codeHashStateDiff := statediffs[1][stateDiffIdx].SuffixDiffs[0] + // Check location of code hash was accessed + if codeHashStateDiff.Suffix != utils.CodeHashLeafKey { + t.Fatalf("code hash invalid suffix") + } + // check the code hash wasn't present in the prestate, as + // the contract was deployed in this block. + if codeHashStateDiff.CurrentValue == nil { + t.Fatalf("codeHash.CurrentValue must not be empty") + } + // check the poststate value corresponds to the code hash + // of the deployed contract. + expCodeHash := crypto.Keccak256Hash(dummyContract[12:]) + if *codeHashStateDiff.CurrentValue != expCodeHash { + t.Fatalf("codeHash.CurrentValue unexpected code hash") + } + if codeHashStateDiff.NewValue != nil { + t.Fatalf("codeHash.NewValue must be nil") + } +} + +// TestProcessVerkleBalanceOpcode checks that calling balance +// on another contract will add the correct entries to the witness. +func TestProcessVerkleBalanceOpcode(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = verkleTestGenesis(&config) + ) + _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + gen.SetPoS() + txData := slices.Concat( + []byte{byte(vm.PUSH20)}, + common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d").Bytes(), + []byte{byte(vm.BALANCE)}) + + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(0), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: txData}) + gen.AddTx(tx) + }) + + account2BalanceTreeKey := utils.BasicDataKey(account2[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[0] { + if bytes.Equal(stemStateDiff.Stem[:], account2BalanceTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + var zero [32]byte + balanceStateDiff := statediffs[0][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatalf("invalid suffix diff") + } + // check the prestate balance wasn't 0 or missing + if balanceStateDiff.CurrentValue == nil || *balanceStateDiff.CurrentValue == zero { + t.Fatalf("invalid current value %v", *balanceStateDiff.CurrentValue) + } + // check that the poststate witness value for the balance is nil, + // meaning that it didn't get updated. + if balanceStateDiff.NewValue != nil { + t.Fatalf("invalid new value") + } +} + +// TestProcessVerkleSelfDestructInSeparateTx controls the contents of the witness after +// a non-eip6780-compliant selfdestruct occurs. +func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = verkleTestGenesis(&config) + ) + + // runtime code: selfdestruct ( 0x6177843db3138ae69679A54b95cf345ED759450d ) + runtimeCode := slices.Concat( + []byte{byte(vm.PUSH20)}, + account2.Bytes(), + []byte{byte(vm.SELFDESTRUCT)}) + + //The goal of this test is to test SELFDESTRUCT that happens in a contract + // execution which is created in a previous transaction. + selfDestructContract := slices.Concat([]byte{ + byte(vm.PUSH1), byte(len(runtimeCode)), + byte(vm.PUSH1), 12, + byte(vm.PUSH1), 0x00, + byte(vm.CODECOPY), // Codecopy( to-offset: 0, code offset: 12, length: 22 ) + + byte(vm.PUSH1), byte(len(runtimeCode)), + byte(vm.PUSH1), 0x00, + byte(vm.RETURN), // Return ( 0 : len(runtimecode) + }, + runtimeCode) + + deployer := crypto.PubkeyToAddress(testKey.PublicKey) + contract := crypto.CreateAddress(deployer, 0) + + _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + gen.SetPoS() + + if i == 0 { + // Create selfdestruct contract, sending 42 wei. + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(42), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: selfDestructContract, + }) + gen.AddTx(tx) + } else { + // Call it. + tx, _ := types.SignTx(types.NewTransaction(1, contract, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + } + }) + + var zero [32]byte + { // Check self-destructed contract in the witness + selfDestructContractTreeKey := utils.CodeHashKey(contract[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[1] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediffs[1][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatalf("balance invalid suffix") + } + + // The original balance was 42. + var oldBalance [16]byte + oldBalance[15] = 42 + if !bytes.Equal((*balanceStateDiff.CurrentValue)[utils.BasicDataBalanceOffset:], oldBalance[:]) { + t.Fatalf("the pre-state balance before self-destruct must be %x, got %x", oldBalance, *balanceStateDiff.CurrentValue) + } + + // The new balance must be 0. + if !bytes.Equal((*balanceStateDiff.NewValue)[utils.BasicDataBalanceOffset:], zero[utils.BasicDataBalanceOffset:]) { + t.Fatalf("the post-state balance after self-destruct must be 0") + } + } + { // Check self-destructed target in the witness. + selfDestructTargetTreeKey := utils.CodeHashKey(account2[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[1] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructTargetTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediffs[1][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatalf("balance invalid suffix") + } + if balanceStateDiff.CurrentValue == nil { + t.Fatalf("codeHash.CurrentValue must not be empty") + } + if balanceStateDiff.NewValue == nil { + t.Fatalf("codeHash.NewValue must not be empty") + } + preStateBalance := binary.BigEndian.Uint64(balanceStateDiff.CurrentValue[utils.BasicDataBalanceOffset+8:]) + postStateBalance := binary.BigEndian.Uint64(balanceStateDiff.NewValue[utils.BasicDataBalanceOffset+8:]) + if postStateBalance-preStateBalance != 42 { + t.Fatalf("the post-state balance after self-destruct must be 42, got %d-%d=%d", postStateBalance, preStateBalance, postStateBalance-preStateBalance) + } + } +} + +// TestProcessVerkleSelfDestructInSeparateTx controls the contents of the witness after +// a eip6780-compliant selfdestruct occurs. +func TestProcessVerkleSelfDestructInSameTx(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = verkleTestGenesis(&config) + ) + + // The goal of this test is to test SELFDESTRUCT that happens in a contract + // execution which is created in **the same** transaction sending the remaining + // balance to an external (i.e: not itself) account. + + selfDestructContract := slices.Concat( + []byte{byte(vm.PUSH20)}, + account2.Bytes(), + []byte{byte(vm.SELFDESTRUCT)}) + deployer := crypto.PubkeyToAddress(testKey.PublicKey) + contract := crypto.CreateAddress(deployer, 0) + + _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + gen.SetPoS() + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(42), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: selfDestructContract, + }) + gen.AddTx(tx) + }) + + { // Check self-destructed contract in the witness + selfDestructContractTreeKey := utils.CodeHashKey(contract[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[0] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediffs[0][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatalf("balance invalid suffix") + } + + if balanceStateDiff.CurrentValue != nil { + t.Fatalf("the pre-state balance before must be nil, since the contract didn't exist") + } + + if balanceStateDiff.NewValue != nil { + t.Fatalf("the post-state balance after self-destruct must be nil since the contract shouldn't be created at all") + } + } + { // Check self-destructed target in the witness. + selfDestructTargetTreeKey := utils.CodeHashKey(account2[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[0] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructTargetTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediffs[0][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatalf("balance invalid suffix") + } + if balanceStateDiff.CurrentValue == nil { + t.Fatalf("codeHash.CurrentValue must not be empty") + } + if balanceStateDiff.NewValue == nil { + t.Fatalf("codeHash.NewValue must not be empty") + } + preStateBalance := binary.BigEndian.Uint64(balanceStateDiff.CurrentValue[utils.BasicDataBalanceOffset+8:]) + postStateBalance := binary.BigEndian.Uint64(balanceStateDiff.NewValue[utils.BasicDataBalanceOffset+8:]) + if postStateBalance-preStateBalance != 42 { + t.Fatalf("the post-state balance after self-destruct must be 42. got %d", postStateBalance) + } + } +} + +// TestProcessVerkleSelfDestructInSeparateTxWithSelfBeneficiary checks the content of the witness +// if a selfdestruct occurs in a different tx than the one that created it, but the beneficiary +// is the selfdestructed account. +func TestProcessVerkleSelfDestructInSeparateTxWithSelfBeneficiary(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + gspec = verkleTestGenesis(&config) + ) + // The goal of this test is to test SELFDESTRUCT that happens in a contract + // execution which is created in a *previous* transaction sending the remaining + // balance to itself. + selfDestructContract := []byte{ + byte(vm.PUSH1), 2, // PUSH1 2 + byte(vm.PUSH1), 10, // PUSH1 12 + byte(vm.PUSH0), // PUSH0 + byte(vm.CODECOPY), // Codecopy ( to offset 0, code@offset: 10, length: 2) + + byte(vm.PUSH1), 22, + byte(vm.PUSH0), + byte(vm.RETURN), // RETURN( memory[0:2] ) + + // Deployed code + byte(vm.ADDRESS), + byte(vm.SELFDESTRUCT), + } + deployer := crypto.PubkeyToAddress(testKey.PublicKey) + contract := crypto.CreateAddress(deployer, 0) + + _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + gen.SetPoS() + if i == 0 { + // Create self-destruct contract, sending 42 wei. + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(42), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: selfDestructContract, + }) + gen.AddTx(tx) + } else { + // Call it. + tx, _ := types.SignTx(types.NewTransaction(1, contract, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + } + }) + + { + // Check self-destructed contract in the witness. + // The way 6780 is implemented today, it always SubBalance from the self-destructed contract, and AddBalance + // to the beneficiary. In this case both addresses are the same, thus this might be optimizable from a gas + // perspective. But until that happens, we need to honor this "balance reading" adding it to the witness. + + selfDestructContractTreeKey := utils.CodeHashKey(contract[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[1] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatal("no state diff found for stem") + } + + balanceStateDiff := statediffs[1][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatal("balance invalid suffix") + } + + // The original balance was 42. + var oldBalance [16]byte + oldBalance[15] = 42 + if !bytes.Equal((*balanceStateDiff.CurrentValue)[utils.BasicDataBalanceOffset:], oldBalance[:]) { + t.Fatal("the pre-state balance before self-destruct must be 42") + } + + // Note that the SubBalance+AddBalance net effect is a 0 change, so NewValue + // must be nil. + if balanceStateDiff.NewValue != nil { + t.Fatal("the post-state balance after self-destruct must be empty") + } + } +} + +// TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiary checks the content of the witness +// if a selfdestruct occurs in the same tx as the one that created it, but the beneficiary +// is the selfdestructed account. +func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiary(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + gspec = verkleTestGenesis(&config) + deployer = crypto.PubkeyToAddress(testKey.PublicKey) + contract = crypto.CreateAddress(deployer, 0) + ) + + // The goal of this test is to test SELFDESTRUCT that happens while executing + // the init code of a contract creation, that occurs in **the same** transaction. + // The balance is sent to itself. + t.Logf("Contract: %v", contract.String()) + + selfDestructContract := []byte{byte(vm.ADDRESS), byte(vm.SELFDESTRUCT)} + + _, _, _, _, stateDiffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + gen.SetPoS() + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(42), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: selfDestructContract, + }) + gen.AddTx(tx) + }) + stateDiff := stateDiffs[0] // state difference of block 1 + + { // Check self-destructed contract in the witness + selfDestructContractTreeKey := utils.CodeHashKey(contract[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range stateDiff { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatal("no state diff found for stem") + } + balanceStateDiff := stateDiff[stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatal("balance invalid suffix") + } + if balanceStateDiff.CurrentValue != nil { + t.Fatal("the pre-state balance before must be nil, since the contract didn't exist") + } + // Ensure that the value is burnt, and therefore that the balance of the self-destructed + // contract isn't modified (it should remain missing from the state) + if balanceStateDiff.NewValue != nil { + t.Fatal("the post-state balance after self-destruct must be nil since the contract shouldn't be created at all") + } + } +} + +// TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiaryAndPrefundedAccount checks the +// content of the witness if a selfdestruct occurs in the same tx as the one that created it, +// it, but the beneficiary is the selfdestructed account. The difference with the test above, +// is that the created account is prefunded and so the final value should be 0. +func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiaryAndPrefundedAccount(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + gspec = verkleTestGenesis(&config) + deployer = crypto.PubkeyToAddress(testKey.PublicKey) + contract = crypto.CreateAddress(deployer, 0) + ) + // Prefund the account, at an address that the contract will be deployed at, + // before it selfdestrucs. We can therefore check that the account itseld is + // NOT destroyed, which is what the currrent version of the spec requires. + // TODO(gballet) revisit after the spec has been modified. + gspec.Alloc[contract] = types.Account{ + Balance: big.NewInt(100), + } + + selfDestructContract := []byte{byte(vm.ADDRESS), byte(vm.SELFDESTRUCT)} + + _, _, _, _, stateDiffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + gen.SetPoS() + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(42), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: selfDestructContract, + }) + gen.AddTx(tx) + }) + stateDiff := stateDiffs[0] // state difference of block 1 + + { // Check self-destructed contract in the witness + selfDestructContractTreeKey := utils.CodeHashKey(contract[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range stateDiff { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatal("no state diff found for stem") + } + balanceStateDiff := stateDiff[stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatal("balance invalid suffix") + } + expected, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000064") + if balanceStateDiff.CurrentValue == nil || !bytes.Equal(balanceStateDiff.CurrentValue[:], expected) { + t.Fatalf("incorrect prestate balance: %x != %x", *balanceStateDiff.CurrentValue, expected) + } + // Ensure that the value is burnt, and therefore that the balance of the self-destructed + // contract isn't modified (it should remain missing from the state) + expected = make([]byte, 32) + if balanceStateDiff.NewValue == nil { + t.Fatal("incorrect nil poststate balance") + } + if !bytes.Equal(balanceStateDiff.NewValue[:], expected[:]) { + t.Fatalf("incorrect poststate balance: %x != %x", *balanceStateDiff.NewValue, expected[:]) + } + } +} diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go index 12e02de9a4..054fbcf150 100644 --- a/trie/utils/verkle.go +++ b/trie/utils/verkle.go @@ -204,10 +204,10 @@ func CodeChunkKey(address []byte, chunk *uint256.Int) []byte { return GetTreeKey(address, treeIndex, subIndex) } -func StorageIndex(bytes []byte) (*uint256.Int, byte) { +func StorageIndex(storageKey []byte) (*uint256.Int, byte) { // If the storage slot is in the header, we need to add the header offset. var key uint256.Int - key.SetBytes(bytes) + key.SetBytes(storageKey) if key.Cmp(codeStorageDelta) < 0 { // This addition is always safe; it can't ever overflow since pos Date: Mon, 4 Nov 2024 15:10:12 +0100 Subject: [PATCH 7/7] all: fix issues with benchmarks (#30667) This PR fixes some issues with benchmarks - [x] Removes log output from a log-test - [x] Avoids a `nil`-defer in `triedb/pathdb` - [x] Fixes some crashes re tracers - [x] Refactors a very resource-expensive benchmark for blobpol. **NOTE**: this rewrite touches live production code (a little bit), as it makes the validator-function used by the blobpool configurable. - [x] Switch some benches over to use pebble over leveldb - [x] reduce mem overhead in the setup-phase of some tests - [x] Marks some tests with a long setup-phase to be skipped if `-short` is specified (where long is on the order of tens of seconds). Ideally, in my opinion, one should be able to run with `-benchtime 10ms -short` and sanity-check all tests very quickly. - [x] Drops some metrics-bechmark which times the speed of `copy`. --------- Co-authored-by: Sina Mahmoodi --- core/bench_test.go | 26 ++++++++++++++- core/rawdb/accessors_chain_test.go | 6 +++- core/txpool/blobpool/blobpool.go | 20 ++++++++---- core/txpool/blobpool/blobpool_test.go | 29 ++++++++++++++++- core/txpool/blobpool/evictheap_test.go | 22 +++++++++++-- core/txpool/validation.go | 5 +++ .../internal/tracetest/calltrace_test.go | 13 ++------ eth/tracers/tracers_test.go | 4 ++- log/logger_test.go | 3 +- metrics/sample_test.go | 32 +------------------ triedb/pathdb/difflayer_test.go | 7 ++-- 11 files changed, 105 insertions(+), 62 deletions(-) diff --git a/core/bench_test.go b/core/bench_test.go index d0305e268a..6d518e8d3b 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -81,9 +81,15 @@ var ( // value-transfer transaction with n bytes of extra data in each // block. func genValueTx(nbytes int) func(int, *BlockGen) { + // We can reuse the data for all transactions. + // During signing, the method tx.WithSignature(s, sig) + // performs: + // cpy := tx.inner.copy() + // cpy.setSignatureValues(signer.ChainID(), v, r, s) + // After this operation, the data can be reused by the caller. + data := make([]byte, nbytes) return func(i int, gen *BlockGen) { toaddr := common.Address{} - data := make([]byte, nbytes) gas, _ := IntrinsicGas(data, nil, false, false, false, false) signer := gen.Signer() gasPrice := big.NewInt(0) @@ -210,15 +216,27 @@ func BenchmarkChainRead_full_10k(b *testing.B) { benchReadChain(b, true, 10000) } func BenchmarkChainRead_header_100k(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } benchReadChain(b, false, 100000) } func BenchmarkChainRead_full_100k(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } benchReadChain(b, true, 100000) } func BenchmarkChainRead_header_500k(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } benchReadChain(b, false, 500000) } func BenchmarkChainRead_full_500k(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } benchReadChain(b, true, 500000) } func BenchmarkChainWrite_header_10k(b *testing.B) { @@ -234,9 +252,15 @@ func BenchmarkChainWrite_full_100k(b *testing.B) { benchWriteChain(b, true, 100000) } func BenchmarkChainWrite_header_500k(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } benchWriteChain(b, false, 500000) } func BenchmarkChainWrite_full_500k(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } benchWriteChain(b, true, 500000) } diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 0b9dbe1335..0e986f66e0 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -649,11 +649,15 @@ func makeTestBlocks(nblock int, txsPerBlock int) []*types.Block { // makeTestReceipts creates fake receipts for the ancient write benchmark. func makeTestReceipts(n int, nPerBlock int) []types.Receipts { receipts := make([]*types.Receipt, nPerBlock) + var logs []*types.Log + for i := 0; i < 5; i++ { + logs = append(logs, new(types.Log)) + } for i := 0; i < len(receipts); i++ { receipts[i] = &types.Receipt{ Status: types.ReceiptStatusSuccessful, CumulativeGasUsed: 0x888888888, - Logs: make([]*types.Log, 5), + Logs: logs, } } allReceipts := make([]types.Receipts, n) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 76cb6801fa..0352ea9783 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -318,6 +318,10 @@ type BlobPool struct { discoverFeed event.Feed // Event feed to send out new tx events on pool discovery (reorg excluded) insertFeed event.Feed // Event feed to send out new tx events on pool inclusion (reorg included) + // txValidationFn defaults to txpool.ValidateTransaction, but can be + // overridden for testing purposes. + txValidationFn txpool.ValidationFunction + lock sync.RWMutex // Mutex protecting the pool during reorg handling } @@ -329,12 +333,13 @@ func New(config Config, chain BlockChain) *BlobPool { // Create the transaction pool with its initial settings return &BlobPool{ - config: config, - signer: types.LatestSigner(chain.Config()), - chain: chain, - lookup: newLookup(), - index: make(map[common.Address][]*blobTxMeta), - spent: make(map[common.Address]*uint256.Int), + config: config, + signer: types.LatestSigner(chain.Config()), + chain: chain, + lookup: newLookup(), + index: make(map[common.Address][]*blobTxMeta), + spent: make(map[common.Address]*uint256.Int), + txValidationFn: txpool.ValidateTransaction, } } @@ -1090,7 +1095,8 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error { MaxSize: txMaxSize, MinTip: p.gasTip.ToBig(), } - if err := txpool.ValidateTransaction(tx, p.head, p.signer, baseOpts); err != nil { + + if err := p.txValidationFn(tx, p.head, p.signer, baseOpts); err != nil { return err } // Ensure the transaction adheres to the stateful pool filters (nonce, balance) diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 721f7c6c2e..e4441bec5d 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -1449,11 +1449,29 @@ func TestAdd(t *testing.T) { } } +// fakeBilly is a billy.Database implementation which just drops data on the floor. +type fakeBilly struct { + billy.Database + count uint64 +} + +func (f *fakeBilly) Put(data []byte) (uint64, error) { + f.count++ + return f.count, nil +} + +var _ billy.Database = (*fakeBilly)(nil) + // Benchmarks the time it takes to assemble the lazy pending transaction list // from the pool contents. func BenchmarkPoolPending100Mb(b *testing.B) { benchmarkPoolPending(b, 100_000_000) } func BenchmarkPoolPending1GB(b *testing.B) { benchmarkPoolPending(b, 1_000_000_000) } -func BenchmarkPoolPending10GB(b *testing.B) { benchmarkPoolPending(b, 10_000_000_000) } +func BenchmarkPoolPending10GB(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } + benchmarkPoolPending(b, 10_000_000_000) +} func benchmarkPoolPending(b *testing.B, datacap uint64) { // Calculate the maximum number of transaction that would fit into the pool @@ -1477,6 +1495,15 @@ func benchmarkPoolPending(b *testing.B, datacap uint64) { if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { b.Fatalf("failed to create blob pool: %v", err) } + // Make the pool not use disk (just drop everything). This test never reads + // back the data, it just iterates over the pool in-memory items + pool.store = &fakeBilly{pool.store, 0} + // Avoid validation - verifying all blob proofs take significant time + // when the capacity is large. The purpose of this bench is to measure assembling + // the lazies, not the kzg verifications. + pool.txValidationFn = func(tx *types.Transaction, head *types.Header, signer types.Signer, opts *txpool.ValidationOptions) error { + return nil // accept all + } // Fill the pool up with one random transaction from each account with the // same price and everything to maximize the worst case scenario for i := 0; i < int(capacity); i++ { diff --git a/core/txpool/blobpool/evictheap_test.go b/core/txpool/blobpool/evictheap_test.go index 1cf577cb00..b03dd83d69 100644 --- a/core/txpool/blobpool/evictheap_test.go +++ b/core/txpool/blobpool/evictheap_test.go @@ -239,9 +239,25 @@ func BenchmarkPriceHeapOverflow10MB(b *testing.B) { benchmarkPriceHeapOverflow( func BenchmarkPriceHeapOverflow100MB(b *testing.B) { benchmarkPriceHeapOverflow(b, 100*1024*1024) } func BenchmarkPriceHeapOverflow1GB(b *testing.B) { benchmarkPriceHeapOverflow(b, 1024*1024*1024) } func BenchmarkPriceHeapOverflow10GB(b *testing.B) { benchmarkPriceHeapOverflow(b, 10*1024*1024*1024) } -func BenchmarkPriceHeapOverflow25GB(b *testing.B) { benchmarkPriceHeapOverflow(b, 25*1024*1024*1024) } -func BenchmarkPriceHeapOverflow50GB(b *testing.B) { benchmarkPriceHeapOverflow(b, 50*1024*1024*1024) } -func BenchmarkPriceHeapOverflow100GB(b *testing.B) { benchmarkPriceHeapOverflow(b, 100*1024*1024*1024) } + +func BenchmarkPriceHeapOverflow25GB(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } + benchmarkPriceHeapOverflow(b, 25*1024*1024*1024) +} +func BenchmarkPriceHeapOverflow50GB(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } + benchmarkPriceHeapOverflow(b, 50*1024*1024*1024) +} +func BenchmarkPriceHeapOverflow100GB(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } + benchmarkPriceHeapOverflow(b, 100*1024*1024*1024) +} func benchmarkPriceHeapOverflow(b *testing.B, datacap uint64) { // Calculate how many unique transactions we can fit into the provided disk diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 5ff92d71c2..33b383d5cf 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -47,6 +47,11 @@ type ValidationOptions struct { MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool } +// ValidationFunction is an method type which the pools use to perform the tx-validations which do not +// require state access. Production code typically uses ValidateTransaction, whereas testing-code +// might choose to instead use something else, e.g. to always fail or avoid heavy cpu usage. +type ValidationFunction func(tx *types.Transaction, head *types.Header, signer types.Signer, opts *ValidationOptions) error + // ValidateTransaction is a helper method to check whether a transaction is valid // according to the consensus rules, but does not check state-dependent validation // (balance, nonce, etc). diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 5b9c809596..cb635e7127 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -35,7 +35,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/tests" ) @@ -202,7 +201,7 @@ func BenchmarkTracers(b *testing.B) { func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { // Configure a blockchain with the given prestate tx := new(types.Transaction) - if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil { + if err := tx.UnmarshalBinary(common.FromHex(test.Input)); err != nil { b.Fatalf("failed to parse testcase input: %v", err) } signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) @@ -211,15 +210,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { Origin: origin, GasPrice: tx.GasPrice(), } - context := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - Coinbase: test.Context.Miner, - BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), - Time: uint64(test.Context.Time), - Difficulty: (*big.Int)(test.Context.Difficulty), - GasLimit: uint64(test.Context.GasLimit), - } + context := test.Context.toBlockContext(test.Genesis) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 3cce7bffa1..31e14b9112 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -99,11 +99,13 @@ func BenchmarkTransactionTrace(b *testing.B) { for i := 0; i < b.N; i++ { snap := state.StateDB.Snapshot() + tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - _, err = st.TransitionDb() + res, err := st.TransitionDb() if err != nil { b.Fatal(err) } + tracer.OnTxEnd(&types.Receipt{GasUsed: res.UsedGas}, nil) state.StateDB.RevertToSnapshot(snap) if have, want := len(tracer.StructLogs()), 244752; have != want { b.Fatalf("trace wrong, want %d steps, have %d", want, have) diff --git a/log/logger_test.go b/log/logger_test.go index f1a9a93bce..3ec6d2e19c 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -7,7 +7,6 @@ import ( "io" "log/slog" "math/big" - "os" "strings" "testing" "time" @@ -70,7 +69,7 @@ func TestJSONHandler(t *testing.T) { } func BenchmarkTraceLogging(b *testing.B) { - SetDefault(NewLogger(NewTerminalHandler(os.Stderr, true))) + SetDefault(NewLogger(NewTerminalHandler(io.Discard, true))) b.ResetTimer() for i := 0; i < b.N; i++ { Trace("a message", "v", i) diff --git a/metrics/sample_test.go b/metrics/sample_test.go index 4227b43ef7..c855671ae2 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -3,7 +3,6 @@ package metrics import ( "math" "math/rand" - "runtime" "testing" "time" ) @@ -27,6 +26,7 @@ func BenchmarkCompute1000(b *testing.B) { SampleVariance(mean, s) } } + func BenchmarkCompute1000000(b *testing.B) { s := make([]int64, 1000000) var sum int64 @@ -40,28 +40,6 @@ func BenchmarkCompute1000000(b *testing.B) { SampleVariance(mean, s) } } -func BenchmarkCopy1000(b *testing.B) { - s := make([]int64, 1000) - for i := 0; i < len(s); i++ { - s[i] = int64(i) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - sCopy := make([]int64, len(s)) - copy(sCopy, s) - } -} -func BenchmarkCopy1000000(b *testing.B) { - s := make([]int64, 1000000) - for i := 0; i < len(s); i++ { - s[i] = int64(i) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - sCopy := make([]int64, len(s)) - copy(sCopy, s) - } -} func BenchmarkExpDecaySample257(b *testing.B) { benchmarkSample(b, NewExpDecaySample(257, 0.015)) @@ -237,17 +215,9 @@ func TestUniformSampleStatistics(t *testing.T) { } func benchmarkSample(b *testing.B, s Sample) { - var memStats runtime.MemStats - runtime.ReadMemStats(&memStats) - pauseTotalNs := memStats.PauseTotalNs - b.ResetTimer() for i := 0; i < b.N; i++ { s.Update(1) } - b.StopTimer() - runtime.GC() - runtime.ReadMemStats(&memStats) - b.Logf("GC cost: %d ns/op", int(memStats.PauseTotalNs-pauseTotalNs)/b.N) } func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) { diff --git a/triedb/pathdb/difflayer_test.go b/triedb/pathdb/difflayer_test.go index e65f379135..61e8b4e064 100644 --- a/triedb/pathdb/difflayer_test.go +++ b/triedb/pathdb/difflayer_test.go @@ -76,7 +76,7 @@ func benchmarkSearch(b *testing.B, depth int, total int) { nblob = common.CopyBytes(blob) } } - return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), nil) + return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil)) } var layer layer layer = emptyLayer() @@ -118,7 +118,7 @@ func BenchmarkPersist(b *testing.B) { ) nodes[common.Hash{}][string(path)] = node } - return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), nil) + return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil)) } for i := 0; i < b.N; i++ { b.StopTimer() @@ -156,8 +156,7 @@ func BenchmarkJournal(b *testing.B) { ) nodes[common.Hash{}][string(path)] = node } - // TODO(rjl493456442) a non-nil state set is expected. - return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), nil) + return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), new(StateSetWithOrigin)) } var layer layer layer = emptyLayer()