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}) 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/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/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/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/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..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). @@ -99,7 +104,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 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/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 } 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) } 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/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 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