Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BEP-440: Implement EIP-2935: Serve historical block hashes from state #2721

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
8 changes: 2 additions & 6 deletions consensus/parlia/parlia.go
Original file line number Diff line number Diff line change
Expand Up @@ -1275,9 +1275,7 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
return errors.New("parent not found")
}

if p.chainConfig.IsFeynman(header.Number, header.Time) {
systemcontracts.UpgradeBuildInSystemContract(p.chainConfig, header.Number, parent.Time, header.Time, state)
}
systemcontracts.HandleBuildInContract(p.chainConfig, header.Number, parent.Time, header.Time, state, false)

if p.chainConfig.IsOnFeynman(header.Number, parent.Time, header.Time) {
err := p.initializeFeynmanContract(state, header, cx, txs, receipts, systemTxs, usedGas, false)
Expand Down Expand Up @@ -1362,9 +1360,7 @@ func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *
return nil, nil, errors.New("parent not found")
}

if p.chainConfig.IsFeynman(header.Number, header.Time) {
systemcontracts.UpgradeBuildInSystemContract(p.chainConfig, header.Number, parent.Time, header.Time, state)
}
systemcontracts.HandleBuildInContract(p.chainConfig, header.Number, parent.Time, header.Time, state, false)

if p.chainConfig.IsOnFeynman(header.Number, parent.Time, header.Time) {
err := p.initializeFeynmanContract(state, header, cx, &txs, &receipts, nil, &header.GasUsed, true)
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
4 changes: 1 addition & 3 deletions core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
misc.ApplyDAOHardFork(statedb)
}

if !config.IsFeynman(b.header.Number, b.header.Time) {
systemcontracts.UpgradeBuildInSystemContract(config, b.header.Number, parent.Time(), b.header.Time, statedb)
}
systemcontracts.HandleBuildInContract(config, b.header.Number, parent.Time(), b.header.Time, statedb, true)

// Execute any user modifications to the block
if gen != nil {
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
27 changes: 23 additions & 4 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
if lastBlock == nil {
return statedb, nil, nil, 0, errors.New("could not get parent block")
}
if !p.config.IsFeynman(block.Number(), block.Time()) {
// Handle upgrade build-in system contract code
systemcontracts.UpgradeBuildInSystemContract(p.config, blockNumber, lastBlock.Time(), block.Time(), statedb)
}
// Handle upgrade build-in system contract code
systemcontracts.HandleBuildInContract(p.config, blockNumber, lastBlock.Time(), block.Time(), statedb, true)

var (
context = NewEVMBlockContext(header, p.bc, nil)
Expand All @@ -92,6 +90,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 +255,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)
}
22 changes: 21 additions & 1 deletion core/systemcontracts/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,27 @@ func init() {
}
}

func UpgradeBuildInSystemContract(config *params.ChainConfig, blockNumber *big.Int, lastBlockTime uint64, blockTime uint64, statedb vm.StateDB) {
func HandleBuildInContract(config *params.ChainConfig, blockNumber *big.Int, lastBlockTime uint64, blockTime uint64, statedb vm.StateDB, atBlockBegin bool) {
if atBlockBegin {
if !config.IsFeynman(blockNumber, lastBlockTime) {
// Handle upgrade build-in system contract code
upgradeBuildInSystemContract(config, blockNumber, lastBlockTime, blockTime, statedb)
}
// TODO: `HandleBuildInContract` not suitable in a dir naming `systemcontracts`?
// HistoryStorageAddress is not a system contract and can't be upgraded
if config.IsOnPrague(blockNumber, lastBlockTime, blockTime) {
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode)
statedb.SetNonce(params.HistoryStorageAddress, 1)
log.Info("Apply upgrade prague", "blockNumber", blockNumber.Int64(), "blockTime", blockTime)
}
} else {
if config.IsFeynman(blockNumber, lastBlockTime) {
upgradeBuildInSystemContract(config, blockNumber, lastBlockTime, blockTime, statedb)
}
}
}

func upgradeBuildInSystemContract(config *params.ChainConfig, blockNumber *big.Int, lastBlockTime uint64, blockTime uint64, statedb vm.StateDB) {
if config == nil || blockNumber == nil || statedb == nil || reflect.ValueOf(statedb).IsNil() {
return
}
Expand Down
4 changes: 2 additions & 2 deletions core/systemcontracts/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func TestUpgradeBuildInSystemContractNilInterface(t *testing.T) {

GenesisHash = params.BSCGenesisHash

UpgradeBuildInSystemContract(config, blockNumber, lastBlockTime, blockTime, statedb)
upgradeBuildInSystemContract(config, blockNumber, lastBlockTime, blockTime, statedb)
}

func TestUpgradeBuildInSystemContractNilValue(t *testing.T) {
Expand All @@ -69,5 +69,5 @@ func TestUpgradeBuildInSystemContractNilValue(t *testing.T) {

GenesisHash = params.BSCGenesisHash

UpgradeBuildInSystemContract(config, blockNumber, lastBlockTime, blockTime, statedb)
upgradeBuildInSystemContract(config, blockNumber, lastBlockTime, blockTime, statedb)
}
13 changes: 8 additions & 5 deletions eth/state_accessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,13 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
return nil, vm.BlockContext{}, nil, nil, err
}
// upgrade build-in system contract before normal txs if Feynman is not enabled
if !eth.blockchain.Config().IsFeynman(block.Number(), block.Time()) {
systemcontracts.UpgradeBuildInSystemContract(eth.blockchain.Config(), block.Number(), parent.Time(), block.Time(), statedb)
systemcontracts.HandleBuildInContract(eth.blockchain.Config(), block.Number(), parent.Time(), block.Time(), statedb, true)

// 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 All @@ -264,9 +269,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
statedb.AddBalance(block.Header().Coinbase, balance)
}

if eth.blockchain.Config().IsFeynman(block.Number(), block.Time()) {
systemcontracts.UpgradeBuildInSystemContract(eth.blockchain.Config(), block.Number(), parent.Time(), block.Time(), statedb)
}
systemcontracts.HandleBuildInContract(eth.blockchain.Config(), block.Number(), parent.Time(), block.Time(), statedb, false)
beforeSystemTx = false
}
}
Expand Down
Loading
Loading