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

blockchain: implement EIP-2935 #121

Merged
merged 11 commits into from
Nov 7, 2024
4 changes: 4 additions & 0 deletions api/api_ethereum_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2438,6 +2438,10 @@ func (mc *testChainContext) Engine() consensus.Engine {
return gxhash.NewFaker()
}

func (mc *testChainContext) Config() *params.ChainConfig {
return nil
}

func (mc *testChainContext) GetHeader(common.Hash, uint64) *types.Header {
return mc.header
}
Expand Down
54 changes: 54 additions & 0 deletions blockchain/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ package blockchain

import (
"crypto/ecdsa"
"encoding/binary"
"encoding/json"
"fmt"
"math/big"
Expand Down Expand Up @@ -2205,3 +2206,56 @@ func TestTransientStorageReset(t *testing.T) {
t.Fatalf("Unexpected dirty storage slot")
}
}

func TestProcessParentBlockHash(t *testing.T) {
var (
chainConfig = &params.ChainConfig{
ShanghaiCompatibleBlock: common.Big0, // Shanghai fork is necesasry because `params.HistoryStorageCode` contains `PUSH0(0x5f)` instruction
}
hashA = common.Hash{0x01}
hashB = common.Hash{0x02}
header = &types.Header{ParentHash: hashA, Number: big.NewInt(2), Time: common.Big0, BlockScore: common.Big0}
parent = &types.Header{ParentHash: hashB, Number: big.NewInt(1), Time: common.Big0, BlockScore: common.Big0}
coinbase = common.Address{}
rules = params.Rules{}
db = state.NewDatabase(database.NewMemoryDBManager())
)
test := func(statedb *state.StateDB) {
if err := statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode); err != nil {
t.Error(err)
}
statedb.SetNonce(params.HistoryStorageAddress, 1)
statedb.IntermediateRoot(true)

vmContext := NewEVMBlockContext(header, nil, &coinbase)
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, &vm.Config{})
if err := ProcessParentBlockHash(header, evm, statedb, rules); err != nil {
t.Error(err)
}

vmContext = NewEVMBlockContext(parent, nil, &coinbase)
evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, &vm.Config{})
if err := ProcessParentBlockHash(parent, evm, statedb, rules); err != nil {
t.Error(err)
}

// 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.EmptyRootHashOriginal, db, nil, 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)
}
2 changes: 2 additions & 0 deletions blockchain/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
b := &BlockGen{i: i, parent: parent, chain: blocks, chainReader: blockchain, statedb: stateDB, config: config, engine: engine}
b.header = makeHeader(b.chainReader, parent, stateDB, b.engine)

engine.Initialize(blockchain, b.header, stateDB)

// Execute any user modifications to the block and finalize it
if gen != nil {
gen(i, b)
Expand Down
2 changes: 2 additions & 0 deletions blockchain/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ type ChainContext interface {

// GetHeader returns the hash corresponding to their hash.
GetHeader(common.Hash, uint64) *types.Header

Config() *params.ChainConfig
blukat29 marked this conversation as resolved.
Show resolved Hide resolved
}

// NewEVMBlockContext creates a new context for use in the EVM.
Expand Down
37 changes: 37 additions & 0 deletions blockchain/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/kaiachain/kaia/blockchain/state"
"github.com/kaiachain/kaia/blockchain/types"
"github.com/kaiachain/kaia/blockchain/vm"
"github.com/kaiachain/kaia/common"
"github.com/kaiachain/kaia/consensus"
"github.com/kaiachain/kaia/params"
)
Expand Down Expand Up @@ -74,6 +75,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
processStats ProcessStats
)

p.engine.Initialize(p.bc, header, statedb)

// Extract author from the header
author, _ := p.bc.Engine().Author(header) // Ignore error, we're past header validation

Expand All @@ -99,3 +102,37 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg

return receipts, allLogs, *usedGas, internalTxTraces, processStats, nil
}

// ProcessParentBlockHash stores the parent block hash in the history storage contract
// as per EIP-2935.
func ProcessParentBlockHash(header *types.Header, vmenv *vm.EVM, statedb vm.StateDB, rules params.Rules) error {
blukat29 marked this conversation as resolved.
Show resolved Hide resolved
var (
from = params.SystemAddress
data = header.ParentHash.Bytes()
gasLimit = uint64(30_000_000)
)

intrinsicGas, err := types.IntrinsicGas(data, nil, false, rules)
if err != nil {
return err
}

msg := types.NewMessage(
from,
&params.HistoryStorageAddress,
0,
common.Big0,
gasLimit,
common.Big0,
data,
false,
intrinsicGas,
nil,
)

vmenv.Reset(NewEVMTxContext(msg, header, vmenv.ChainConfig()), statedb)
statedb.AddAddressToAccessList(params.HistoryStorageAddress)
vmenv.Call(vm.AccountRef(from), *msg.To(), msg.Data(), gasLimit, common.Big0)
statedb.Finalise(true, true)
return nil
}
2 changes: 2 additions & 0 deletions blockchain/vm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,6 @@ type StateDB interface {
GetTxHash() common.Hash

GetKey(address common.Address) accountkey.AccountKey

Finalise(bool, bool)
blukat29 marked this conversation as resolved.
Show resolved Hide resolved
}
4 changes: 4 additions & 0 deletions blockchain/vm/runtime/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ func (d *dummyChain) Engine() consensus.Engine {
return nil
}

func (d *dummyChain) Config() *params.ChainConfig {
return nil
}

// GetHeader returns the hash corresponding to their hash.
func (d *dummyChain) GetHeader(h common.Hash, n uint64) *types.Header {
d.counter++
Expand Down
3 changes: 3 additions & 0 deletions consensus/clique/clique.go
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,9 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro

func (c *Clique) InitSnapshot() {}

func (c *Clique) Initialize(chain consensus.ChainReader, header *types.Header, state *state.StateDB) {
}

// Finalize implements consensus.Engine and returns the final block.
func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, receipts []*types.Receipt) (*types.Block, error) {
// No block rewards in PoA, so the state remains as is
Expand Down
3 changes: 3 additions & 0 deletions consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ type Engine interface {
// rules of a particular engine. The changes are executed inline.
Prepare(chain ChainReader, header *types.Header) error

// Initialize runs any pre-transaction state modifications (e.g., EIP-2539)
Initialize(chain ChainReader, header *types.Header, state *state.StateDB)

// Finalize runs any post-transaction state modifications (e.g. block rewards)
// and assembles the final block.
// Note: The block header and state database might be updated to reflect any
Expand Down
3 changes: 3 additions & 0 deletions consensus/gxhash/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,9 @@ func (gxhash *Gxhash) Prepare(chain consensus.ChainReader, header *types.Header)
return nil
}

func (gxhash *Gxhash) Initialize(chain consensus.ChainReader, header *types.Header, state *state.StateDB) {
}

// Finalize implements consensus.Engine, accumulating the block rewards,
// setting the final state and assembling the block.
func (gxhash *Gxhash) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, receipts []*types.Receipt) (*types.Block, error) {
Expand Down
11 changes: 11 additions & 0 deletions consensus/istanbul/backend/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ import (
"time"

lru "github.com/hashicorp/golang-lru"
"github.com/kaiachain/kaia/blockchain"
"github.com/kaiachain/kaia/blockchain/state"
"github.com/kaiachain/kaia/blockchain/system"
"github.com/kaiachain/kaia/blockchain/types"
"github.com/kaiachain/kaia/blockchain/vm"
"github.com/kaiachain/kaia/common"
"github.com/kaiachain/kaia/consensus"
"github.com/kaiachain/kaia/consensus/istanbul"
Expand Down Expand Up @@ -488,6 +490,15 @@ func (sb *backend) Prepare(chain consensus.ChainReader, header *types.Header) er
return nil
}

func (sb *backend) Initialize(chain consensus.ChainReader, header *types.Header, state *state.StateDB) {
// [EIP-2935] stores the parent block hash in the history storage contract
if chain.Config().IsPragueForkEnabled(header.Number) {
context := blockchain.NewEVMBlockContext(header, chain, nil)
vmenv := vm.NewEVM(context, vm.TxContext{}, state, chain.Config(), &vm.Config{})
blockchain.ProcessParentBlockHash(header, vmenv, state, chain.Config().Rules(header.Number))
}
}

// Finalize runs any post-transaction state modifications (e.g. block rewards)
// and assembles the final block.
//
Expand Down
12 changes: 12 additions & 0 deletions consensus/mocks/engine_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions node/cn/state_accessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ func (cn *CN) stateAtTransaction(block *types.Block, txIndex int, reexec uint64)
if err != nil {
return nil, vm.BlockContext{}, vm.TxContext{}, nil, nil, err
}
// If prague hardfork, insert parent block hash in the state as per EIP-2935.
cn.engine.Initialize(cn.blockchain, block.Header(), statedb)
ian0371 marked this conversation as resolved.
Show resolved Hide resolved

if txIndex == 0 && len(block.Transactions()) == 0 {
return nil, vm.BlockContext{}, vm.TxContext{}, statedb, release, nil
}
Expand Down
47 changes: 16 additions & 31 deletions node/cn/tracers/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ func (context *chainContext) GetHeader(hash common.Hash, number uint64) *types.H
return header
}

func (context *chainContext) Config() *params.ChainConfig {
hyunsooda marked this conversation as resolved.
Show resolved Hide resolved
return context.backend.ChainConfig()
}

// chainContext constructs the context reader which is used by the evm for reading
// the necessary chain context.
func newChainContext(ctx context.Context, backend Backend) blockchain.ChainContext {
Expand Down Expand Up @@ -425,32 +429,13 @@ func (api *CommonAPI) traceChain(start, end *types.Block, config *TraceConfig, n
logged = time.Now()
logger.Info("Tracing chain segment", "start", start.NumberU64(), "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin))
}
// Retrieve the parent block and target block for tracing.
block, err := api.blockByNumber(localctx, rpc.BlockNumber(number))
if err != nil {
failed = err
break
}
next, err := api.blockByNumber(localctx, rpc.BlockNumber(number+1))
if err != nil {
failed = err
break
}
// Prepare the statedb for tracing. Don't use the live database for
// tracing to avoid persisting state junks into the database. Switch
// over to `preferDisk` mode only if the memory usage exceeds the
// limit, the trie database will be reconstructed from scratch only
// if the relevant state is available in disk.
var preferDisk bool
if statedb != nil {
s1, s2, s3 := statedb.Database().TrieDB().Size()
preferDisk = s1+s2+s3 > defaultTracechainMemLimit
blukat29 marked this conversation as resolved.
Show resolved Hide resolved
}
statedb, release, err = api.backend.StateAtBlock(localctx, block, reexec, statedb, false, preferDisk)
if err != nil {
failed = err
break
}
_, _, _, statedb, release, err = api.backend.StateAtTransaction(localctx, next, 0, reexec)

// Clean out any pending derefs. 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
Expand Down Expand Up @@ -623,17 +608,12 @@ func (api *CommonAPI) traceBlock(ctx context.Context, block *types.Block, config
if block.NumberU64() == 0 {
return nil, errors.New("genesis is not traceable")
}
// Create the parent state database
parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
if err != nil {
return nil, err
}
reexec := defaultTraceReexec
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}

statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
_, _, _, statedb, release, err := api.backend.StateAtTransaction(ctx, block, 0, reexec)
if err != nil {
return nil, err
}
Expand All @@ -647,7 +627,11 @@ func (api *CommonAPI) traceBlock(ctx context.Context, block *types.Block, config

pend = new(sync.WaitGroup)
jobs = make(chan *txTraceTask, len(txs))

header = block.Header()
blockCtx = blockchain.NewEVMBlockContext(header, newChainContext(ctx, api.backend), nil)
)

threads := runtime.NumCPU()
if threads > len(txs) {
threads = len(txs)
Expand All @@ -667,7 +651,6 @@ func (api *CommonAPI) traceBlock(ctx context.Context, block *types.Block, config
}

txCtx := blockchain.NewEVMTxContext(msg, block.Header(), api.backend.ChainConfig())
blockCtx := blockchain.NewEVMBlockContext(block.Header(), newChainContext(ctx, api.backend), nil)
ian0371 marked this conversation as resolved.
Show resolved Hide resolved
res, err := api.traceTx(ctx, msg, blockCtx, txCtx, task.statedb, config)
if err != nil {
results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()}
Expand Down Expand Up @@ -732,7 +715,7 @@ func (api *CommonAPI) standardTraceBlockToFile(ctx context.Context, block *types
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
_, _, _, statedb, release, err := api.backend.StateAtTransaction(ctx, parent, 0, reexec)
blukat29 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
}
Expand All @@ -751,6 +734,9 @@ func (api *CommonAPI) standardTraceBlockToFile(ctx context.Context, block *types
}
logConfig.Debug = true

header := block.Header()
blockCtx := blockchain.NewEVMBlockContext(header, newChainContext(ctx, api.backend), nil)

// Execute transaction, either tracing all or just the requested one
var (
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
Expand All @@ -765,8 +751,7 @@ func (api *CommonAPI) standardTraceBlockToFile(ctx context.Context, block *types
}

var (
txCtx = blockchain.NewEVMTxContext(msg, block.Header(), api.backend.ChainConfig())
blockCtx = blockchain.NewEVMBlockContext(block.Header(), newChainContext(ctx, api.backend), nil)
txCtx = blockchain.NewEVMTxContext(msg, block.Header(), api.backend.ChainConfig())

vmConf vm.Config
dump *os.File
Expand Down
11 changes: 11 additions & 0 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"fmt"
"math/big"
"time"

"github.com/kaiachain/kaia/common"
)

var TargetGasLimit = GenesisGasLimit // The artificial target
Expand Down Expand Up @@ -198,6 +200,8 @@ const (

// ZeroBaseFee exists for supporting Ethereum compatible data structure.
ZeroBaseFee uint64 = 0

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

const (
Expand All @@ -219,6 +223,13 @@ var (
GenesisBlockScore = big.NewInt(131072) // BlockScore of the Genesis block.
MinimumBlockScore = big.NewInt(131072) // The minimum that the blockscore may ever be.
DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether blockscore should go up or not.

// SystemAddress is where the system-transaction is sent from as per EIP-4788
SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")

// EIP-2935 - Serve historical block hashes from state
HistoryStorageAddress = common.HexToAddress("0x0aae40965e6800cd9b1f4b05ff21581047e3f91e")
HistoryStorageCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500")
)

// Parameters for execution time limit
Expand Down
Loading
Loading