diff --git a/core/bintrie_witness_test.go b/core/bintrie_witness_test.go
new file mode 100644
index 0000000000..7704ba41fb
--- /dev/null
+++ b/core/bintrie_witness_test.go
@@ -0,0 +1,237 @@
+// 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 (
+ "encoding/binary"
+ "fmt"
+ "math/big"
+ "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/tracing"
+ "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/triedb"
+)
+
+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,
+ EnableVerkleAtGenesis: true,
+ BlobScheduleConfig: ¶ms.BlobScheduleConfig{
+ Verkle: params.DefaultPragueBlobConfig,
+ },
+ }
+)
+
+func TestProcessVerkle(t *testing.T) {
+ var (
+ code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`)
+ intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, 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, 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)
+ options := DefaultConfig().WithStateScheme(rawdb.PathScheme)
+ options.SnapshotLimit = 0
+ blockchain, _ := NewBlockChain(bcdb, gspec, beacon.New(ethash.NewFaker()), options)
+ 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 */
+ uint64(4844) /* execution costs */
+ blockGasUsagesExpected := []uint64{
+ txCost1*2 + txCost2,
+ txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas,
+ }
+ _, chain, _ := GenerateChainWithGenesis(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)
+ }
+ })
+
+ for i, b := range chain {
+ fmt.Printf("%d %x\n", i, b.Root())
+ }
+ endnum, err := blockchain.InsertChain(chain)
+ if err != nil {
+ t.Fatalf("block %d imported with error: %v", endnum, err)
+ }
+
+ for i := range 2 {
+ 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, isVerkle bool) {
+ statedb.SetNonce(params.HistoryStorageAddress, 1, tracing.NonceChangeUnspecified)
+ statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode, tracing.CodeChangeUnspecified)
+ // 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)}
+ chainConfig := params.MergedTestChainConfig
+ if isVerkle {
+ chainConfig = testVerkleChainConfig
+ }
+ vmContext := NewEVMBlockContext(header, nil, new(common.Address))
+ evm := vm.NewEVM(vmContext, statedb, chainConfig, vm.Config{})
+ ProcessParentBlockHash(header.ParentHash, evm)
+ }
+ // Read block hashes for block 0 .. num-1
+ for i := 0; i < num; i++ {
+ have, want := getContractStoredBlockHash(statedb, uint64(i), isVerkle), common.Hash{byte(i + 1)}
+ if have != want {
+ t.Errorf("block %d, verkle=%v, have parent hash %v, want %v", i, isVerkle, have, want)
+ }
+ }
+ }
+ t.Run("MPT", func(t *testing.T) {
+ statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
+ checkBlockHashes(statedb, false)
+ })
+ t.Run("Verkle", func(t *testing.T) {
+ db := rawdb.NewMemoryDatabase()
+ cacheConfig := DefaultConfig().WithStateScheme(rawdb.PathScheme)
+ cacheConfig.SnapshotLimit = 0
+ triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true))
+ statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, nil))
+ checkBlockHashes(statedb, true)
+ })
+}
+
+// getContractStoredBlockHash is a utility method which reads the stored parent blockhash for block 'number'
+func getContractStoredBlockHash(statedb *state.StateDB, number uint64, isVerkle bool) common.Hash {
+ ringIndex := number % params.HistoryServeWindow
+ var key common.Hash
+ binary.BigEndian.PutUint64(key[24:], ringIndex)
+ if isVerkle {
+ return statedb.GetState(params.HistoryStorageAddress, key)
+ }
+ return statedb.GetState(params.HistoryStorageAddress, key)
+}
diff --git a/core/chain_makers.go b/core/chain_makers.go
index f2c1fc2fbd..a1e07becba 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -32,7 +32,6 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/triedb"
- "github.com/ethereum/go-verkle"
"github.com/holiman/uint256"
)
@@ -490,117 +489,6 @@ func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int,
return db, blocks, receipts
}
-func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, trdb *triedb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts, []*verkle.VerkleProof, []verkle.StateDiff) {
- if config == nil {
- config = params.TestChainConfig
- }
- proofs := make([]*verkle.VerkleProof, 0, n)
- keyvals := make([]verkle.StateDiff, 0, n)
- cm := newChainMaker(parent, config, engine)
-
- genblock := func(i int, parent *types.Block, triedb *triedb.Database, statedb *state.StateDB) (*types.Block, types.Receipts) {
- b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine}
- b.header = cm.makeHeader(parent, statedb, b.engine)
-
- // TODO uncomment when proof generation is merged
- // Save pre state for proof generation
- // preState := statedb.Copy()
-
- // EIP-2935 / 7709
- blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
- blockContext.Random = &common.Hash{} // enable post-merge instruction set
- evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{})
- ProcessParentBlockHash(b.header.ParentHash, evm)
-
- // Execute any user modifications to the block.
- if gen != nil {
- gen(i, b)
- }
-
- requests := b.collectRequests(false)
- if requests != nil {
- reqHash := types.CalcRequestsHash(requests)
- b.header.RequestsHash = &reqHash
- }
-
- body := &types.Body{
- Transactions: b.txs,
- Uncles: b.uncles,
- Withdrawals: b.withdrawals,
- }
- block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, body, b.receipts)
- if err != nil {
- panic(err)
- }
-
- // Write state changes to DB.
- root, err := statedb.Commit(b.header.Number.Uint64(), config.IsEIP158(b.header.Number), config.IsCancun(b.header.Number, b.header.Time))
- if err != nil {
- panic(fmt.Sprintf("state write error: %v", err))
- }
- if err = triedb.Commit(root, false); err != nil {
- panic(fmt.Sprintf("trie write error: %v", err))
- }
-
- proofs = append(proofs, block.ExecutionWitness().VerkleProof)
- keyvals = append(keyvals, block.ExecutionWitness().StateDiff)
-
- return block, b.receipts
- }
-
- sdb := state.NewDatabase(trdb, nil)
-
- for i := 0; i < n; i++ {
- statedb, err := state.New(parent.Root(), sdb)
- if err != nil {
- panic(err)
- }
- block, receipts := genblock(i, parent, trdb, statedb)
-
- // Post-process the receipts.
- // Here we assign the final block hash and other info into the receipt.
- // In order for DeriveFields to work, the transaction and receipt lists need to be
- // of equal length. If AddUncheckedTx or AddUncheckedReceipt are used, there will be
- // extra ones, so we just trim the lists here.
- receiptsCount := len(receipts)
- txs := block.Transactions()
- if len(receipts) > len(txs) {
- receipts = receipts[:len(txs)]
- } else if len(receipts) < len(txs) {
- txs = txs[:len(receipts)]
- }
- var blobGasPrice *big.Int
- if block.ExcessBlobGas() != nil {
- blobGasPrice = eip4844.CalcBlobFee(cm.config, block.Header())
- }
- if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.BaseFee(), blobGasPrice, txs); err != nil {
- panic(err)
- }
-
- // Re-expand to ensure all receipts are returned.
- receipts = receipts[:receiptsCount]
-
- // Advance the chain.
- cm.add(block, receipts)
- parent = block
- }
- return cm.chain, cm.receipts, proofs, keyvals
-}
-
-func GenerateVerkleChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (common.Hash, ethdb.Database, []*types.Block, []types.Receipts, []*verkle.VerkleProof, []verkle.StateDiff) {
- db := rawdb.NewMemoryDatabase()
- cacheConfig := DefaultConfig().WithStateScheme(rawdb.PathScheme)
- cacheConfig.SnapshotLimit = 0
- triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true))
- defer triedb.Close()
- genesisBlock, err := genesis.Commit(db, triedb)
- if err != nil {
- panic(err)
- }
- blocks, receipts, proofs, keyvals := GenerateVerkleChain(genesis.Config, genesisBlock, engine, db, triedb, n, gen)
- return genesisBlock.Hash(), db, blocks, receipts, proofs, keyvals
-}
-
func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header {
time := parent.Time() + 10 // block time is fixed at 10 seconds
parentHeader := parent.Header()
diff --git a/core/state/database.go b/core/state/database.go
index e3e26259a1..ae177d964f 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -241,7 +241,7 @@ func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) {
if db.triedb.IsVerkle() {
ts := overlay.LoadTransitionState(db.TrieDB().Disk(), root, db.triedb.IsVerkle())
if ts.InTransition() {
- panic("transition isn't supported yet")
+ panic("state tree transition isn't supported yet")
}
if ts.Transitioned() {
// Use BinaryTrie instead of VerkleTrie when IsVerkle is set
diff --git a/core/verkle_witness_test.go b/core/verkle_witness_test.go
deleted file mode 100644
index 9495e325ca..0000000000
--- a/core/verkle_witness_test.go
+++ /dev/null
@@ -1,1107 +0,0 @@
-// 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"
- "fmt"
- "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/tracing"
- "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,
- EnableVerkleAtGenesis: true,
- BlobScheduleConfig: ¶ms.BlobScheduleConfig{
- Verkle: params.DefaultPragueBlobConfig,
- },
- // 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,
- EnableVerkleAtGenesis: true,
- BlobScheduleConfig: ¶ms.BlobScheduleConfig{
- Verkle: params.DefaultPragueBlobConfig,
- },
- }
-)
-
-func TestProcessVerkle(t *testing.T) {
- var (
- code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`)
- intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, 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, 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)
- options := DefaultConfig().WithStateScheme(rawdb.PathScheme)
- options.SnapshotLimit = 0
- blockchain, _ := NewBlockChain(bcdb, gspec, beacon.New(ethash.NewFaker()), options)
- 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 */
- uint64(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")
-
- for i, b := range chain {
- fmt.Printf("%d %x\n", i, b.Root())
- }
- endnum, err := blockchain.InsertChain(chain)
- if err != nil {
- t.Fatalf("block %d imported with error: %v", endnum, err)
- }
-
- for i := range 2 {
- 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, isVerkle bool) {
- statedb.SetNonce(params.HistoryStorageAddress, 1, tracing.NonceChangeUnspecified)
- statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode, tracing.CodeChangeUnspecified)
- // 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)}
- chainConfig := params.MergedTestChainConfig
- if isVerkle {
- chainConfig = testVerkleChainConfig
- }
- vmContext := NewEVMBlockContext(header, nil, new(common.Address))
- evm := vm.NewEVM(vmContext, statedb, chainConfig, vm.Config{})
- ProcessParentBlockHash(header.ParentHash, evm)
- }
- // Read block hashes for block 0 .. num-1
- for i := 0; i < num; i++ {
- have, want := getContractStoredBlockHash(statedb, uint64(i), isVerkle), common.Hash{byte(i + 1)}
- if have != want {
- t.Errorf("block %d, verkle=%v, have parent hash %v, want %v", i, isVerkle, have, want)
- }
- }
- }
- t.Run("MPT", func(t *testing.T) {
- statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
- checkBlockHashes(statedb, false)
- })
- t.Run("Verkle", func(t *testing.T) {
- db := rawdb.NewMemoryDatabase()
- cacheConfig := DefaultConfig().WithStateScheme(rawdb.PathScheme)
- cacheConfig.SnapshotLimit = 0
- triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true))
- statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, nil))
- checkBlockHashes(statedb, true)
- })
-}
-
-// getContractStoredBlockHash is a utility method which reads the stored parent blockhash for block 'number'
-func getContractStoredBlockHash(statedb *state.StateDB, number uint64, isVerkle bool) common.Hash {
- ringIndex := number % params.HistoryServeWindow
- var key common.Hash
- binary.BigEndian.PutUint64(key[24:], ringIndex)
- if isVerkle {
- return statedb.GetState(params.HistoryStorageAddress, key)
- }
- 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.
- genesisH, _, 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 account 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 != genesisH {
- t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, genesisH)
- }
- } 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 != chain[0].Hash() {
- t.Fatalf("invalid post state value for BLOCKHASH contract at block #2: %x != %x", chain[0].Hash(), (*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 = new(big.Int).SetUint64(69421)
- gspec := verkleTestGenesis(&config)
-
- genesisH, _, _, _, _, 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 != genesisH {
- t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, genesisH)
- }
- } 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 = new(big.Int).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 = new(big.Int).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 = new(big.Int).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)
- }
- }
-}
-
-// TestProcessVerkleSelfDestructInSameTx 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 = new(big.Int).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 = new(big.Int).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 = new(big.Int).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 = new(big.Int).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 current 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/bintrie/internal_node.go b/trie/bintrie/internal_node.go
index 125ea77a20..3fc22df6f1 100644
--- a/trie/bintrie/internal_node.go
+++ b/trie/bintrie/internal_node.go
@@ -93,6 +93,9 @@ func (bt *InternalNode) Get(key []byte, resolver NodeResolverFn) ([]byte, error)
if err != nil {
return nil, fmt.Errorf("get error: %w", err)
}
+ if values == nil {
+ return nil, nil
+ }
return values[key[31]], nil
}
diff --git a/trie/bintrie/stem_node.go b/trie/bintrie/stem_node.go
index 4a12157477..60856b42ce 100644
--- a/trie/bintrie/stem_node.go
+++ b/trie/bintrie/stem_node.go
@@ -141,7 +141,10 @@ func (bt *StemNode) CollectNodes(path []byte, flush NodeFlushFn) error {
}
// GetValuesAtStem retrieves the group of values located at the given stem key.
-func (bt *StemNode) GetValuesAtStem(_ []byte, _ NodeResolverFn) ([][]byte, error) {
+func (bt *StemNode) GetValuesAtStem(stem []byte, _ NodeResolverFn) ([][]byte, error) {
+ if !bytes.Equal(bt.Stem, stem) {
+ return nil, nil
+ }
return bt.Values[:], nil
}
diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go
index 9fc65de277..c9de393bb2 100644
--- a/triedb/pathdb/database.go
+++ b/triedb/pathdb/database.go
@@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/trie/bintrie"
"github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-verkle"
)
@@ -109,6 +110,18 @@ func verkleNodeHasher(blob []byte) (common.Hash, error) {
return n.Commit().Bytes(), nil
}
+// binaryNodeHasher computes the hash of the given verkle node.
+func binaryNodeHasher(blob []byte) (common.Hash, error) {
+ if len(blob) == 0 {
+ return types.EmptyVerkleHash, nil
+ }
+ n, err := bintrie.DeserializeNode(blob, 0)
+ if err != nil {
+ return common.Hash{}, err
+ }
+ return n.Hash(), nil
+}
+
// Database is a multiple-layered structure for maintaining in-memory states
// along with its dirty trie nodes. It consists of one persistent base layer
// backed by a key-value store, on top of which arbitrarily many in-memory diff
@@ -163,7 +176,7 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database {
// compress the shared key prefix.
if isVerkle {
db.diskdb = rawdb.NewTable(diskdb, string(rawdb.VerklePrefix))
- db.hasher = verkleNodeHasher
+ db.hasher = binaryNodeHasher
}
// Construct the layer tree by resolving the in-disk singleton state
// and in-memory layer journal.