core/{.,state,vm},miner,ethr/tracers: implement 7709

Co-authored-by: Ignacio Hagopian <jsign.uy@gmail.com>

Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
This commit is contained in:
Guillaume Ballet 2025-01-10 12:18:18 +01:00
parent 033de2a05b
commit dd5ff823bd
8 changed files with 39 additions and 18 deletions

View file

@ -384,7 +384,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase) blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
blockContext.Random = &common.Hash{} // enable post-merge instruction set blockContext.Random = &common.Hash{} // enable post-merge instruction set
evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{}) evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{})
ProcessParentBlockHash(b.header.ParentHash, evm) ProcessParentBlockHash(b.header.ParentHash, evm, false)
} }
// Execute any user modifications to the block // Execute any user modifications to the block
@ -492,7 +492,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine
// EIP-2935 // EIP-2935
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase) blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{}) evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{})
ProcessParentBlockHash(b.header.ParentHash, evm) ProcessParentBlockHash(b.header.ParentHash, evm, false)
} }
// Execute any user modifications to the block. // Execute any user modifications to the block.

View file

@ -157,6 +157,10 @@ func (s *hookedStateDB) Witness() *stateless.Witness {
return s.inner.Witness() return s.inner.Witness()
} }
func (s *hookedStateDB) AccessEvents() *AccessEvents {
return s.inner.AccessEvents()
}
func (s *hookedStateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { func (s *hookedStateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int {
prev := s.inner.SubBalance(addr, amount, reason) prev := s.inner.SubBalance(addr, amount, reason)
if s.hooks.OnBalanceChange != nil && !amount.IsZero() { if s.hooks.OnBalanceChange != nil && !amount.IsZero() {

View file

@ -17,6 +17,7 @@
package core package core
import ( import (
"encoding/binary"
"fmt" "fmt"
"math/big" "math/big"
@ -86,7 +87,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
ProcessBeaconBlockRoot(*beaconRoot, evm) ProcessBeaconBlockRoot(*beaconRoot, evm)
} }
if p.config.IsPrague(block.Number(), block.Time()) { if p.config.IsPrague(block.Number(), block.Time()) {
ProcessParentBlockHash(block.ParentHash(), evm) ProcessParentBlockHash(block.ParentHash(), evm, p.config.IsVerkle(block.Number(), block.Time()))
} }
// Iterate over and process the individual transactions // Iterate over and process the individual transactions
@ -234,14 +235,24 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
} }
// ProcessParentBlockHash stores the parent block hash in the history storage contract // ProcessParentBlockHash stores the parent block hash in the history storage contract
// as per EIP-2935. // as per EIP-2935/7709.
func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) { func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM, is7709 bool) {
if tracer := evm.Config.Tracer; tracer != nil { if tracer := evm.Config.Tracer; tracer != nil {
onSystemCallStart(tracer, evm.GetVMContext()) onSystemCallStart(tracer, evm.GetVMContext())
if tracer.OnSystemCallEnd != nil { if tracer.OnSystemCallEnd != nil {
defer tracer.OnSystemCallEnd() defer tracer.OnSystemCallEnd()
} }
} }
if is7709 {
// currently 8192 for kautinen7, to be changed to 8 (and made a constant) for kaustinen8
ringIndex := (evm.Context.BlockNumber.Uint64() - 1) % params.HistoryServeWindow
var key common.Hash
binary.BigEndian.PutUint64(key[24:], ringIndex)
evm.StateDB.SetState(params.SystemAddress, key, prevHash)
evm.StateDB.AccessEvents().SlotGas(params.SystemAddress, key, true)
evm.StateDB.Finalise(false)
return
}
msg := &Message{ msg := &Message{
From: params.SystemAddress, From: params.SystemAddress,
GasLimit: 30_000_000, GasLimit: 30_000_000,

View file

@ -217,7 +217,7 @@ func TestProcessParentBlockHash(t *testing.T) {
// block 1 parent hash is 0x0100.... // block 1 parent hash is 0x0100....
// block 2 parent hash is 0x0200.... // block 2 parent hash is 0x0200....
// etc // etc
checkBlockHashes := func(statedb *state.StateDB) { checkBlockHashes := func(statedb *state.StateDB, isVerkle bool) {
statedb.SetNonce(params.HistoryStorageAddress, 1) statedb.SetNonce(params.HistoryStorageAddress, 1)
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode) statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode)
// Process n blocks, from 1 .. num // Process n blocks, from 1 .. num
@ -226,19 +226,19 @@ func TestProcessParentBlockHash(t *testing.T) {
header := &types.Header{ParentHash: common.Hash{byte(i)}, Number: big.NewInt(int64(i)), Difficulty: new(big.Int)} header := &types.Header{ParentHash: common.Hash{byte(i)}, Number: big.NewInt(int64(i)), Difficulty: new(big.Int)}
vmContext := NewEVMBlockContext(header, nil, new(common.Address)) vmContext := NewEVMBlockContext(header, nil, new(common.Address))
evm := vm.NewEVM(vmContext, statedb, params.MergedTestChainConfig, vm.Config{}) evm := vm.NewEVM(vmContext, statedb, params.MergedTestChainConfig, vm.Config{})
ProcessParentBlockHash(header.ParentHash, evm) ProcessParentBlockHash(header.ParentHash, evm, isVerkle)
} }
// Read block hashes for block 0 .. num-1 // Read block hashes for block 0 .. num-1
for i := 0; i < num; i++ { for i := 0; i < num; i++ {
have, want := getContractStoredBlockHash(statedb, uint64(i)), common.Hash{byte(i + 1)} have, want := getContractStoredBlockHash(statedb, uint64(i), isVerkle), common.Hash{byte(i + 1)}
if have != want { if have != want {
t.Errorf("block %d, have parent hash %v, want %v", i, 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) { t.Run("MPT", func(t *testing.T) {
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
checkBlockHashes(statedb) checkBlockHashes(statedb, false)
}) })
t.Run("Verkle", func(t *testing.T) { t.Run("Verkle", func(t *testing.T) {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
@ -246,15 +246,18 @@ func TestProcessParentBlockHash(t *testing.T) {
cacheConfig.SnapshotLimit = 0 cacheConfig.SnapshotLimit = 0
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true)) triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true))
statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, nil)) statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, nil))
checkBlockHashes(statedb) checkBlockHashes(statedb, true)
}) })
} }
// getContractStoredBlockHash is a utility method which reads the stored parent blockhash for block 'number' // getContractStoredBlockHash is a utility method which reads the stored parent blockhash for block 'number'
func getContractStoredBlockHash(statedb *state.StateDB, number uint64) common.Hash { func getContractStoredBlockHash(statedb *state.StateDB, number uint64, isVerkle bool) common.Hash {
ringIndex := number % params.HistoryServeWindow ringIndex := number % params.HistoryServeWindow
var key common.Hash var key common.Hash
binary.BigEndian.PutUint64(key[24:], ringIndex) binary.BigEndian.PutUint64(key[24:], ringIndex)
if isVerkle {
return statedb.GetState(params.SystemAddress, key)
}
return statedb.GetState(params.HistoryStorageAddress, key) return statedb.GetState(params.HistoryStorageAddress, key)
} }

View file

@ -20,6 +20,7 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
@ -98,6 +99,8 @@ type StateDB interface {
Witness() *stateless.Witness Witness() *stateless.Witness
AccessEvents() *state.AccessEvents
// Finalise must be invoked at the end of a transaction // Finalise must be invoked at the end of a transaction
Finalise(bool) Finalise(bool)
} }

View file

@ -242,7 +242,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
} }
// If prague hardfork, insert parent block hash in the state as per EIP-2935. // If prague hardfork, insert parent block hash in the state as per EIP-2935.
if eth.blockchain.Config().IsPrague(block.Number(), block.Time()) { if eth.blockchain.Config().IsPrague(block.Number(), block.Time()) {
core.ProcessParentBlockHash(block.ParentHash(), evm) core.ProcessParentBlockHash(block.ParentHash(), evm, eth.blockchain.Config().IsVerkle(block.Number(), block.Time()))
} }
if txIndex == 0 && len(block.Transactions()) == 0 { if txIndex == 0 && len(block.Transactions()) == 0 {
return nil, vm.BlockContext{}, statedb, release, nil return nil, vm.BlockContext{}, statedb, release, nil

View file

@ -386,7 +386,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
} }
// Insert parent hash in history contract. // Insert parent hash in history contract.
if api.backend.ChainConfig().IsPrague(next.Number(), next.Time()) { if api.backend.ChainConfig().IsPrague(next.Number(), next.Time()) {
core.ProcessParentBlockHash(next.ParentHash(), evm) core.ProcessParentBlockHash(next.ParentHash(), evm, api.backend.ChainConfig().IsVerkle(next.Number(), next.Time()))
} }
// Clean out any pending release functions of trace state. Note this // Clean out any pending release functions of trace state. Note this
// step must be done after constructing tracing state, because the // step must be done after constructing tracing state, because the
@ -541,7 +541,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
core.ProcessBeaconBlockRoot(*beaconRoot, evm) core.ProcessBeaconBlockRoot(*beaconRoot, evm)
} }
if chainConfig.IsPrague(block.Number(), block.Time()) { if chainConfig.IsPrague(block.Number(), block.Time()) {
core.ProcessParentBlockHash(block.ParentHash(), evm) core.ProcessParentBlockHash(block.ParentHash(), evm, api.backend.ChainConfig().IsVerkle(block.Number(), block.Time()))
} }
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
if err := ctx.Err(); err != nil { if err := ctx.Err(); err != nil {
@ -605,7 +605,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
core.ProcessBeaconBlockRoot(*beaconRoot, evm) core.ProcessBeaconBlockRoot(*beaconRoot, evm)
} }
if api.backend.ChainConfig().IsPrague(block.Number(), block.Time()) { if api.backend.ChainConfig().IsPrague(block.Number(), block.Time()) {
core.ProcessParentBlockHash(block.ParentHash(), evm) core.ProcessParentBlockHash(block.ParentHash(), evm, api.backend.ChainConfig().IsVerkle(block.Number(), block.Time()))
} }
// JS tracers have high overhead. In this case run a parallel // JS tracers have high overhead. In this case run a parallel
@ -782,7 +782,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
core.ProcessBeaconBlockRoot(*beaconRoot, evm) core.ProcessBeaconBlockRoot(*beaconRoot, evm)
} }
if chainConfig.IsPrague(block.Number(), block.Time()) { if chainConfig.IsPrague(block.Number(), block.Time()) {
core.ProcessParentBlockHash(block.ParentHash(), evm) core.ProcessParentBlockHash(block.ParentHash(), evm, api.backend.ChainConfig().IsVerkle(block.Number(), block.Time()))
} }
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
// Prepare the transaction for un-traced execution // Prepare the transaction for un-traced execution

View file

@ -231,7 +231,7 @@ func (miner *Miner) prepareWork(genParams *generateParams, witness bool) (*envir
core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, env.evm) core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, env.evm)
} }
if miner.chainConfig.IsPrague(header.Number, header.Time) { if miner.chainConfig.IsPrague(header.Number, header.Time) {
core.ProcessParentBlockHash(header.ParentHash, env.evm) core.ProcessParentBlockHash(header.ParentHash, env.evm, false)
} }
return env, nil return env, nil
} }