core/state, core: add block-hash to base-state-root mapping for Verkle transition

This commit is contained in:
samuel 2025-04-10 00:20:01 +01:00
parent a8240e46ea
commit ffe1d2406c
8 changed files with 118 additions and 61 deletions

View file

@ -286,7 +286,7 @@ func TestContractLinking(t *testing.T) {
}, },
}, },
// test two contracts can be deployed which don't share deps // test two contracts can be deployed which don't share deps
linkTestCaseInput{ {
map[rune][]rune{ map[rune][]rune{
'a': {'b', 'c', 'd', 'e'}, 'a': {'b', 'c', 'd', 'e'},
'f': {'g', 'h', 'i', 'j'}}, 'f': {'g', 'h', 'i', 'j'}},
@ -296,7 +296,7 @@ func TestContractLinking(t *testing.T) {
}, },
}, },
// test two contracts can be deployed which share deps // test two contracts can be deployed which share deps
linkTestCaseInput{ {
map[rune][]rune{ map[rune][]rune{
'a': {'b', 'c', 'd', 'e'}, 'a': {'b', 'c', 'd', 'e'},
'f': {'g', 'c', 'd', 'h'}}, 'f': {'g', 'c', 'd', 'h'}},
@ -306,7 +306,7 @@ func TestContractLinking(t *testing.T) {
}, },
}, },
// test one contract with overrides for all lib deps // test one contract with overrides for all lib deps
linkTestCaseInput{ {
map[rune][]rune{ map[rune][]rune{
'a': {'b', 'c', 'd', 'e'}}, 'a': {'b', 'c', 'd', 'e'}},
map[rune]struct{}{'b': {}, 'c': {}, 'd': {}, 'e': {}}, map[rune]struct{}{'b': {}, 'c': {}, 'd': {}, 'e': {}},
@ -314,7 +314,7 @@ func TestContractLinking(t *testing.T) {
'a': {}}, 'a': {}},
}, },
// test one contract with overrides for some lib deps // test one contract with overrides for some lib deps
linkTestCaseInput{ {
map[rune][]rune{ map[rune][]rune{
'a': {'b', 'c'}}, 'a': {'b', 'c'}},
map[rune]struct{}{'b': {}, 'c': {}}, map[rune]struct{}{'b': {}, 'c': {}},
@ -322,7 +322,7 @@ func TestContractLinking(t *testing.T) {
'a': {}}, 'a': {}},
}, },
// test deployment of a contract with overrides // test deployment of a contract with overrides
linkTestCaseInput{ {
map[rune][]rune{ map[rune][]rune{
'a': {}}, 'a': {}},
map[rune]struct{}{'a': {}}, map[rune]struct{}{'a': {}},
@ -330,7 +330,7 @@ func TestContractLinking(t *testing.T) {
}, },
// two contracts ('a' and 'f') share some dependencies. contract 'a' is marked as an override. expect that any of // two contracts ('a' and 'f') share some dependencies. contract 'a' is marked as an override. expect that any of
// its depdencies that aren't shared with 'f' are not deployed. // its depdencies that aren't shared with 'f' are not deployed.
linkTestCaseInput{map[rune][]rune{ {map[rune][]rune{
'a': {'b', 'c', 'd', 'e'}, 'a': {'b', 'c', 'd', 'e'},
'f': {'g', 'c', 'd', 'h'}}, 'f': {'g', 'c', 'd', 'h'}},
map[rune]struct{}{'a': {}}, map[rune]struct{}{'a': {}},

View file

@ -1188,6 +1188,13 @@ func (bc *BlockChain) Stop() {
if bc.logger != nil && bc.logger.OnClose != nil { if bc.logger != nil && bc.logger.OnClose != nil {
bc.logger.OnClose() bc.logger.OnClose()
} }
if bc.blockToBaseStateRoot != nil {
if err := bc.blockToBaseStateRoot.Store(bc.db); err != nil {
log.Error("Failed to store verkle transition mappings", "err", err)
}
}
// Close the trie database, release all the held resources as the last step. // Close the trie database, release all the held resources as the last step.
if err := bc.triedb.Close(); err != nil { if err := bc.triedb.Close(); err != nil {
log.Error("Failed to close trie database", "err", err) log.Error("Failed to close trie database", "err", err)
@ -1884,6 +1891,26 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
}() }()
} }
// Handle Verkle transition
if bc.verkleTransitionBlock > 0 && block.NumberU64() >= bc.verkleTransitionBlock {
parentHash := block.ParentHash()
parentHeader := bc.GetHeaderByHash(parentHash)
if parentHeader == nil {
return nil, fmt.Errorf("parent header not found: %s", parentHash.Hex())
}
// Store the mapping from block hash to parent's state root
bc.blockToBaseStateRoot.Add(block.Hash(), parentHeader.Root)
// Set Verkle transition data in state
statedb.SetVerkleTransitionData(block.Hash(), parentHeader.Root)
log.Debug("Processing Verkle transition block",
"number", block.NumberU64(),
"hash", block.Hash(),
"baseStateRoot", parentHeader.Root)
}
// Process block using the parent state as reference point // Process block using the parent state as reference point
pstart := time.Now() pstart := time.Now()
res, err := bc.processor.Process(block, statedb, bc.vmConfig) res, err := bc.processor.Process(block, statedb, bc.vmConfig)

View file

@ -1,6 +1,7 @@
package core package core
import ( import (
"fmt"
"math/big" "math/big"
"testing" "testing"
@ -8,14 +9,24 @@ import (
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "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/params"
"github.com/ethereum/go-ethereum/triedb"
) )
// Define testKey for signing transactions
var testKey, _ = crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")
// TestVerkleTransitionWithReorg tests the mapping during a reorg at the verkle transition // TestVerkleTransitionWithReorg tests the mapping during a reorg at the verkle transition
func TestVerkleTransitionWithReorg(t *testing.T) { func TestVerkleTransitionWithReorg(t *testing.T) {
// Get address from the test key for funding
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
// Configure Verkle transition at block 10 // Configure Verkle transition at block 10
var ( var (
db = rawdb.NewMemoryDatabase() db = rawdb.NewMemoryDatabase()
trieDb = triedb.NewDatabase(db, nil)
gspec = &Genesis{ gspec = &Genesis{
Config: &params.ChainConfig{ Config: &params.ChainConfig{
ChainID: big.NewInt(1337), ChainID: big.NewInt(1337),
@ -27,21 +38,31 @@ func TestVerkleTransitionWithReorg(t *testing.T) {
}, },
Alloc: GenesisAlloc{ Alloc: GenesisAlloc{
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): {Balance: big.NewInt(1000000000000000000)}, common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): {Balance: big.NewInt(1000000000000000000)},
testAddr: {Balance: big.NewInt(1000000000000000000)}, // Add funds to test account
}, },
} }
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db, trieDb)
engine = ethash.NewFaker()
) )
// Create our blockchain instance // Create our blockchain instance using the correct parameter order
blockchain, err := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) blockchain, err := NewBlockChain(
db, // ethdb.Database
nil, // *CacheConfig
gspec, // *Genesis
nil, // *ChainOverrides
engine, // consensus.Engine
vm.Config{}, // vm.Config (not pointer)
nil, // *uint64 (lastAcceptedHash)
)
if err != nil { if err != nil {
t.Fatalf("Failed to create blockchain: %v", err) t.Fatalf("Failed to create blockchain: %v", err)
} }
defer blockchain.Stop() defer blockchain.Stop()
// Create a chain up to the fork block (block 9) // Create a chain up to the fork block (block 9)
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 9, func(i int, gen *BlockGen) { blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 9, func(i int, gen *BlockGen) {
gen.SetCoinbase(common.HexToAddress("0x" + string(i+'0'))) gen.SetCoinbase(common.HexToAddress(fmt.Sprintf("0x%d", i)))
}) })
// Insert blocks up to block 9 // Insert blocks up to block 9
@ -51,12 +72,12 @@ func TestVerkleTransitionWithReorg(t *testing.T) {
// Create two competing forks from block 9 (both are at the Verkle transition height) // Create two competing forks from block 9 (both are at the Verkle transition height)
// Fork 1 with account A1 // Fork 1 with account A1
fork1Block10, _ := GenerateChain(gspec.Config, blocks[len(blocks)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) { fork1Block10, _ := GenerateChain(gspec.Config, blocks[len(blocks)-1], engine, db, 1, func(i int, gen *BlockGen) {
gen.SetCoinbase(common.HexToAddress("0xA1")) gen.SetCoinbase(common.HexToAddress("0xA1"))
}) })
// Fork 2 with account A2 // Fork 2 with account A2
fork2Block10, _ := GenerateChain(gspec.Config, blocks[len(blocks)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) { fork2Block10, _ := GenerateChain(gspec.Config, blocks[len(blocks)-1], engine, db, 1, func(i int, gen *BlockGen) {
gen.SetCoinbase(common.HexToAddress("0xA2")) gen.SetCoinbase(common.HexToAddress("0xA2"))
}) })
@ -69,11 +90,11 @@ func TestVerkleTransitionWithReorg(t *testing.T) {
addr3 := common.HexToAddress("0xA3") addr3 := common.HexToAddress("0xA3")
// Create block 11 on fork 1 (adding account A3) // Create block 11 on fork 1 (adding account A3)
fork1Block11, _ := GenerateChain(gspec.Config, fork1Block10[0], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) { fork1Block11, _ := GenerateChain(gspec.Config, fork1Block10[0], engine, db, 1, func(i int, gen *BlockGen) {
// Add account A3 // Add account A3
tx, _ := types.SignTx( tx, _ := types.SignTx(
types.NewTransaction(0, addr3, big.NewInt(100), 21000, big.NewInt(1), nil), types.NewTransaction(0, addr3, big.NewInt(100), 21000, big.NewInt(1), nil),
types.HomesteadSigner{}, types.LatestSigner(gspec.Config),
testKey, testKey,
) )
gen.AddTx(tx) gen.AddTx(tx)
@ -101,11 +122,11 @@ func TestVerkleTransitionWithReorg(t *testing.T) {
} }
// Create block 11 on fork 2 (also adding account A3) // Create block 11 on fork 2 (also adding account A3)
fork2Block11, _ := GenerateChain(gspec.Config, fork2Block10[0], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) { fork2Block11, _ := GenerateChain(gspec.Config, fork2Block10[0], engine, db, 1, func(i int, gen *BlockGen) {
// Add same account A3 // Add same account A3
tx, _ := types.SignTx( tx, _ := types.SignTx(
types.NewTransaction(0, addr3, big.NewInt(100), 21000, big.NewInt(1), nil), types.NewTransaction(0, addr3, big.NewInt(100), 21000, big.NewInt(1), nil),
types.HomesteadSigner{}, types.LatestSigner(gspec.Config),
testKey, testKey,
) )
gen.AddTx(tx) gen.AddTx(tx)
@ -128,8 +149,8 @@ func TestVerkleTransitionWithReorg(t *testing.T) {
} }
// The base roots should be different // The base roots should be different
if baseRoot1 == baseRoot2 { if baseRoot1 != baseRoot2 {
t.Fatalf("Expected different base roots for different forks") t.Fatalf("Expected same base roots since both forks build on the same parent (block 9)")
} }
// Current chain head should be fork 2 block 11 // Current chain head should be fork 2 block 11
@ -143,8 +164,12 @@ func TestVerkleTransitionWithReorg(t *testing.T) {
t.Fatalf("Failed to get state: %v", err) t.Fatalf("Failed to get state: %v", err)
} }
// Check A3 is present in the state // Check A3 is present in the state (handle uint256.Int vs big.Int)
if balance := state.GetBalance(addr3); balance.Cmp(big.NewInt(100)) != 0 { balance := state.GetBalance(addr3)
expectedBalance := big.NewInt(100)
// Compare as strings to handle both *big.Int and *uint256.Int
if balance.String() != expectedBalance.String() {
t.Fatalf("Expected A3 balance of 100, got %v", balance) t.Fatalf("Expected A3 balance of 100, got %v", balance)
} }
} }

View file

@ -376,10 +376,17 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
// GetState retrieves the value associated with the specific key. // GetState retrieves the value associated with the specific key.
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
stateObject := s.getStateObject(addr) stateObject := s.getStateObject(addr)
var value common.Hash
if stateObject != nil { if stateObject != nil {
return stateObject.GetState(hash) value = stateObject.GetState(hash)
} }
return common.Hash{}
// If in verkle transition and value not found, fallback to base state
if s.verkleTransitionActive && value == (common.Hash{}) {
return s.getStateFromBase(addr, hash)
}
return value
} }
// GetCommittedState retrieves the value associated with the specific key // GetCommittedState retrieves the value associated with the specific key
@ -1457,25 +1464,6 @@ func (s *StateDB) BaseStateRoot() common.Hash {
return s.baseStateRoot return s.baseStateRoot
} }
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
// Try current state first
value := s.GetState(addr, hash) // This is wrong - would cause recursion
// Instead, check how GetState is currently implemented
// Likely something like:
stateObject := s.getStateObject(addr)
if stateObject != nil {
value = stateObject.GetState(hash)
}
// If in verkle transition and value not found, fallback to base state
if s.verkleTransitionActive && value == (common.Hash{}) {
return s.getStateFromBase(addr, hash)
}
return value
}
// Add method to access base state // Add method to access base state
func (s *StateDB) getStateFromBase(addr common.Address, hash common.Hash) common.Hash { func (s *StateDB) getStateFromBase(addr common.Address, hash common.Hash) common.Hash {
// Implementation depends on how Merkle states are accessed // Implementation depends on how Merkle states are accessed

View file

@ -4,7 +4,8 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/triedb" // Added triedb import
) )
func TestBlockToBaseStateRootMapping(t *testing.T) { func TestBlockToBaseStateRootMapping(t *testing.T) {
@ -46,7 +47,7 @@ func TestBlockToBaseStateRootMapping(t *testing.T) {
} }
// Test Store and Load // Test Store and Load
db := memorydb.New() db := rawdb.NewMemoryDatabase()
err := mapping.Store(db) err := mapping.Store(db)
if err != nil { if err != nil {
t.Fatalf("Failed to store mapping: %v", err) t.Fatalf("Failed to store mapping: %v", err)
@ -67,19 +68,29 @@ func TestBlockToBaseStateRootMapping(t *testing.T) {
} }
func TestStateDBVerkleTransition(t *testing.T) { func TestStateDBVerkleTransition(t *testing.T) {
// Create a test state // Create a database backend
db := NewDatabase(memorydb.New()) ethDb := rawdb.NewMemoryDatabase()
baseState, _ := New(common.Hash{}, db, nil)
// Create a trie database first
trieDb := triedb.NewDatabase(ethDb, nil) // Use proper config if needed
// Create state database with trie database
stateDb := NewDatabase(trieDb, nil)
// Create base state
baseState, _ := New(common.Hash{}, stateDb)
// Create base state with account1 // Create base state with account1
addr1 := common.HexToAddress("0x1111111111111111111111111111111111111111") addr1 := common.HexToAddress("0x1111111111111111111111111111111111111111")
key1 := common.HexToHash("0xaaaa") key1 := common.HexToHash("0xaaaa")
value1 := common.HexToHash("0xbbbb") value1 := common.HexToHash("0xbbbb")
baseState.SetState(addr1, key1, value1) baseState.SetState(addr1, key1, value1)
baseRoot, _ := baseState.Commit(false)
// Create new verkle state with account2 // Commit state - check the actual parameters needed in your codebase
statedb, _ := New(common.Hash{}, db, nil) baseRoot, _ := baseState.Commit(0, false, false)
// Create new verkle state
statedb, _ := New(common.Hash{}, stateDb)
statedb.SetVerkleTransitionData(common.HexToHash("0xblock"), baseRoot) statedb.SetVerkleTransitionData(common.HexToHash("0xblock"), baseRoot)
addr2 := common.HexToAddress("0x2222222222222222222222222222222222222222") addr2 := common.HexToAddress("0x2222222222222222222222222222222222222222")

View file

@ -885,7 +885,7 @@ func TestCall(t *testing.T) {
Balance: big.NewInt(params.Ether), Balance: big.NewInt(params.Ether),
Nonce: 1, Nonce: 1,
Storage: map[common.Hash]common.Hash{ Storage: map[common.Hash]common.Hash{
common.Hash{}: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001"), {}: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001"),
}, },
}, },
}, },
@ -3576,7 +3576,7 @@ func TestCreateAccessListWithStateOverrides(t *testing.T) {
Balance: (*hexutil.Big)(big.NewInt(1000000000000000000)), Balance: (*hexutil.Big)(big.NewInt(1000000000000000000)),
Nonce: &nonce, Nonce: &nonce,
State: map[common.Hash]common.Hash{ State: map[common.Hash]common.Hash{
common.Hash{}: common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000002a"), {}: common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000002a"),
}, },
}, },
} }

View file

@ -432,6 +432,7 @@ type ChainConfig struct {
Ethash *EthashConfig `json:"ethash,omitempty"` Ethash *EthashConfig `json:"ethash,omitempty"`
Clique *CliqueConfig `json:"clique,omitempty"` Clique *CliqueConfig `json:"clique,omitempty"`
BlobScheduleConfig *BlobScheduleConfig `json:"blobSchedule,omitempty"` BlobScheduleConfig *BlobScheduleConfig `json:"blobSchedule,omitempty"`
VerkleBlock *big.Int `json:"verkleBlock,omitempty"`
} }
// EthashConfig is the consensus engine configs for proof-of-work based sealing. // EthashConfig is the consensus engine configs for proof-of-work based sealing.
@ -646,6 +647,11 @@ func (c *ChainConfig) IsOsaka(num *big.Int, time uint64) bool {
return c.IsLondon(num) && isTimestampForked(c.OsakaTime, time) return c.IsLondon(num) && isTimestampForked(c.OsakaTime, time)
} }
// IsVerkleActive returns whether num is either equal to the Verkle block or greater.
func (c *ChainConfig) IsVerkleActive(num uint64) bool {
return c.VerkleBlock != nil && c.VerkleBlock.Uint64() <= num
}
// IsVerkle returns whether time is either equal to the Verkle fork time or greater. // IsVerkle returns whether time is either equal to the Verkle fork time or greater.
func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool { func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool {
return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time) return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time)