Skip to content

Commit

Permalink
BEP-440: Implement EIP-2935: Serve historical block hashes from state
Browse files Browse the repository at this point in the history
  • Loading branch information
buddh0 committed Sep 24, 2024
1 parent 27f67a5 commit 2e1b0dd
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 8 deletions.
9 changes: 8 additions & 1 deletion cmd/evm/internal/t8ntool/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,14 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
}

if pre.Env.BlockHashes != nil && chainConfig.IsPrague(new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) {
var (
prevNumber = pre.Env.Number - 1
prevHash = pre.Env.BlockHashes[math.HexOrDecimal64(prevNumber)]
evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
)
core.ProcessParentBlockHash(prevHash, evm, statedb)
}
for i := 0; txIt.Next(); i++ {
tx, err := txIt.Tx()
if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,12 @@ type CacheConfig struct {
}

// triedbConfig derives the configures for trie database.
func (c *CacheConfig) triedbConfig() *triedb.Config {
func (c *CacheConfig) triedbConfig(isVerkle bool) *triedb.Config {
config := &triedb.Config{
Cache: c.TrieCleanLimit,
Preimages: c.Preimages,
NoTries: c.NoTries,
IsVerkle: isVerkle,
}
if c.StateScheme == rawdb.HashScheme {
config.HashDB = &hashdb.Config{
Expand Down Expand Up @@ -340,7 +341,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
diffLayerChanCache, _ := exlru.New(diffLayerCacheLimit)

// Open trie database with provided config
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig())
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(false))

// Setup the genesis block, commit the provided genesis specification
// to database if the genesis block is not present yet, or load the
Expand Down
2 changes: 2 additions & 0 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,8 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
// Pre-deploy EIP-2935 history contract.
params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode},
},
}
if faucet != nil {
Expand Down
21 changes: 21 additions & 0 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
}
if p.config.IsPrague(block.Number(), block.Time()) {
ProcessParentBlockHash(block.ParentHash(), vmenv, statedb)
}
// Iterate over and process the individual transactions
posa, isPoSA := p.engine.(consensus.PoSA)
commonTxs := make([]*types.Transaction, 0, txNum)
Expand Down Expand Up @@ -254,3 +257,21 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
statedb.Finalise(true)
}

// ProcessParentBlockHash stores the parent block hash in the history storage contract
// as per EIP-2935.
func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb *state.StateDB) {
msg := &Message{
From: params.SystemAddress,
GasLimit: 30_000_000,
GasPrice: common.Big0,
GasFeeCap: common.Big0,
GasTipCap: common.Big0,
To: &params.HistoryStorageAddress,
Data: prevHash.Bytes(),
}
vmenv.Reset(NewEVMTxContext(msg), statedb)
statedb.AddAddressToAccessList(params.HistoryStorageAddress)
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
statedb.Finalise(true)
}
55 changes: 55 additions & 0 deletions core/state_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package core

import (
"crypto/ecdsa"
"encoding/binary"
"errors"
"math/big"
"testing"
Expand All @@ -30,11 +31,14 @@ import (
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb"
"github.com/holiman/uint256"
"golang.org/x/crypto/sha3"
)
Expand Down Expand Up @@ -429,3 +433,54 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
}
return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil))
}

func TestProcessParentBlockHash(t *testing.T) {
var (
chainConfig = params.MergedTestChainConfig
hashA = common.Hash{0x01}
hashB = common.Hash{0x02}
header = &types.Header{ParentHash: hashA, Number: big.NewInt(2), Difficulty: big.NewInt(0)}
parent = &types.Header{ParentHash: hashB, Number: big.NewInt(1), Difficulty: big.NewInt(0)}
coinbase = common.Address{}
)
test := func(statedb *state.StateDB) {
statedb.SetNonce(params.HistoryStorageAddress, 1)
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode)
statedb.IntermediateRoot(true)

vmContext := NewEVMBlockContext(header, nil, &coinbase)
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{})
ProcessParentBlockHash(header.ParentHash, evm, statedb)

vmContext = NewEVMBlockContext(parent, nil, &coinbase)
evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{})
ProcessParentBlockHash(parent.ParentHash, evm, statedb)

// make sure that the state is correct
if have := getParentBlockHash(statedb, 1); have != hashA {
t.Errorf("want parent hash %v, have %v", hashA, have)
}
if have := getParentBlockHash(statedb, 0); have != hashB {
t.Errorf("want parent hash %v, have %v", hashB, have)
}
}
t.Run("MPT", func(t *testing.T) {
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
test(statedb)
})
t.Run("Verkle", func(t *testing.T) {
db := rawdb.NewMemoryDatabase()
cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme)
cacheConfig.SnapshotLimit = 0
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true))
statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabaseWithNodeDB(db, triedb), nil)
test(statedb)
})
}

func getParentBlockHash(statedb *state.StateDB, number uint64) common.Hash {
ringIndex := number % params.HistoryServeWindow
var key common.Hash
binary.BigEndian.PutUint64(key[24:], ringIndex)
return statedb.GetState(params.HistoryStorageAddress, key)
}
6 changes: 6 additions & 0 deletions eth/state_accessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
if !eth.blockchain.Config().IsFeynman(block.Number(), block.Time()) {
systemcontracts.UpgradeBuildInSystemContract(eth.blockchain.Config(), block.Number(), parent.Time(), block.Time(), statedb)
}
// If prague hardfork, insert parent block hash in the state as per EIP-2935.
if eth.blockchain.Config().IsPrague(block.Number(), block.Time()) {
context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, eth.blockchain.Config(), vm.Config{})
core.ProcessParentBlockHash(block.ParentHash(), vmenv, statedb)
}
if txIndex == 0 && len(block.Transactions()) == 0 {
return nil, vm.BlockContext{}, statedb, release, nil
}
Expand Down
27 changes: 22 additions & 5 deletions eth/tracers/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,16 +402,22 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
failed = err
break
}
// Clean out any pending release functions of trace state. Note this
// step must be done after constructing tracing state, because the
// tracing state of block next depends on the parent state and construction
// may fail if we release too early.
tracker.callReleases()

// upgrade build-in system contract before normal txs if Feynman is not enabled
if !api.backend.ChainConfig().IsFeynman(next.Number(), next.Time()) {
systemcontracts.UpgradeBuildInSystemContract(api.backend.ChainConfig(), next.Number(), block.Time(), next.Time(), statedb)
}
// Insert parent hash in history contract.
if api.backend.ChainConfig().IsPrague(next.Number(), next.Time()) {
context := core.NewEVMBlockContext(next.Header(), api.chainContext(ctx), nil)
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{})
core.ProcessParentBlockHash(next.ParentHash(), vmenv, statedb)
}
// Clean out any pending release functions of trace state. Note this
// step must be done after constructing tracing state, because the
// tracing state of block next depends on the parent state and construction
// may fail if we release too early.
tracker.callReleases()

// Send the block over to the concurrent tracers (if not in the fast-forward phase)
txs := next.Transactions()
Expand Down Expand Up @@ -562,6 +568,9 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
deleteEmptyObjects = chainConfig.IsEIP158(block.Number())
beforeSystemTx = true
)
if chainConfig.IsPrague(block.Number(), block.Time()) {
core.ProcessParentBlockHash(block.ParentHash(), vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{}), statedb)
}
for i, tx := range block.Transactions() {
if err := ctx.Err(); err != nil {
return nil, err
Expand Down Expand Up @@ -664,6 +673,10 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
results = make([]*txTraceResult, len(txs))
beforeSystemTx = true
)
if api.backend.ChainConfig().IsPrague(block.Number(), block.Time()) {
vmenv := vm.NewEVM(blockCtx, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{})
core.ProcessParentBlockHash(block.ParentHash(), vmenv, statedb)
}
for i, tx := range txs {
// upgrade build-in system contract before system txs if Feynman is enabled
if beforeSystemTx {
Expand Down Expand Up @@ -869,6 +882,10 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
chainConfig, canon = overrideConfig(chainConfig, config.Overrides)
}

if chainConfig.IsPrague(block.Number(), block.Time()) {
vmenv := vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{})
core.ProcessParentBlockHash(block.ParentHash(), vmenv, statedb)
}
for i, tx := range block.Transactions() {
// upgrade build-in system contract before system txs if Feynman is enabled
if beforeSystemTx {
Expand Down
5 changes: 5 additions & 0 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,11 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, w.chainConfig, vm.Config{})
core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state)
}
if w.chainConfig.IsPrague(header.Number, header.Time) {
context := core.NewEVMBlockContext(header, w.chain, nil)
vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, w.chainConfig, vm.Config{})
core.ProcessParentBlockHash(header.ParentHash, vmenv, env.state)
}

env.size = uint32(env.header.Size())

Expand Down
6 changes: 6 additions & 0 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ const (

BlobTxTargetBlobGasPerBlock = 3 * BlobTxBlobGasPerBlob // Target consumable blob gas for data blobs per block (for 1559-like pricing)
MaxBlobGasPerBlock = 6 * BlobTxBlobGasPerBlob // Maximum consumable blob gas for data blobs per block

HistoryServeWindow = 8192 // Number of blocks to serve historical block hashes for, EIP-2935.
)

var (
Expand All @@ -214,4 +216,8 @@ var (
BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02")
// SystemAddress is where the system-transaction is sent from as per EIP-4788
SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")
// HistoryStorageAddress is where the historical block hashes are stored.
HistoryStorageAddress = common.HexToAddress("0x0aae40965e6800cd9b1f4b05ff21581047e3f91e")
// HistoryStorageCode is the code with getters for historical block hashes.
HistoryStorageCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500")
)

0 comments on commit 2e1b0dd

Please sign in to comment.