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

cmd/evm/internal/t8ntool, core, core/vm, miner, params: implement eip 2935: save historical block hash in state #28878

Closed
wants to merge 4 commits into from
Closed
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
4 changes: 4 additions & 0 deletions cmd/evm/internal/t8ntool/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ type stEnv struct {
ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"`
ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"`
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
ParentHash *common.Hash `json:"parentHash,omitempty"`
}

type stEnvMarshaling struct {
Expand Down Expand Up @@ -190,6 +191,9 @@ 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 chainConfig.IsPrague(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp) {
core.ProcessParentBlockHash(statedb, pre.Env.Number-1, *pre.Env.ParentHash)
}

for i := 0; txIt.Next(); i++ {
tx, err := txIt.Tx()
Expand Down
6 changes: 6 additions & 0 deletions cmd/evm/internal/t8ntool/gen_stenv.go

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

26 changes: 26 additions & 0 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package core

import (
"encoding/binary"
"errors"
"fmt"
"math/big"
Expand Down Expand Up @@ -79,6 +80,14 @@ 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()) {
parent := p.bc.GetBlockByHash(block.ParentHash())
if !p.config.IsPrague(parent.Number(), parent.Time()) {
InsertBlockHashHistoryAtEip2935Fork(statedb, block.NumberU64()-1, block.ParentHash(), p.bc)
} else {
ProcessParentBlockHash(statedb, block.NumberU64()-1, block.ParentHash())
}
}
// Iterate over and process the individual transactions
for i, tx := range block.Transactions() {
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
Expand Down Expand Up @@ -189,3 +198,20 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.Big0)
statedb.Finalise(true)
}

// InsertBlockHashHistoryAtEip2935Fork inserts the block hashes for the 256 ancestors
// of the fork block.
func InsertBlockHashHistoryAtEip2935Fork(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash, chain consensus.ChainHeaderReader) {
ancestor := chain.GetHeader(prevHash, prevNumber)
for i := prevNumber; i > 0 && i >= prevNumber-256; i-- {
ProcessParentBlockHash(statedb, i, ancestor.Hash())
ancestor = chain.GetHeader(ancestor.ParentHash, ancestor.Number.Uint64()-1)
}
}

// ProcessParentBlockHash inserts the parent block hash into the history storage contract.
func ProcessParentBlockHash(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash) {
var key common.Hash
binary.BigEndian.PutUint64(key[24:], prevNumber)
statedb.SetState(params.HistoryStorageAddress, key, prevHash)
}
15 changes: 15 additions & 0 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package vm

import (
"encoding/binary"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -432,13 +434,26 @@ func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
return nil, nil
}

func getBlockHashFromContract(number uint64, statedb StateDB) common.Hash {
var pnum common.Hash
binary.BigEndian.PutUint64(pnum[24:], number)
return statedb.GetState(params.HistoryStorageAddress, pnum)
}

func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
num := scope.Stack.peek()
num64, overflow := num.Uint64WithOverflow()
if overflow {
num.Clear()
return nil, nil
}

evm := interpreter.evm
if evm.chainRules.IsPrague {
num.SetBytes(getBlockHashFromContract(num64, evm.StateDB).Bytes())
return nil, nil
}

var upper, lower uint64
upper = interpreter.evm.Context.BlockNumber.Uint64()
if upper < 257 {
Expand Down
88 changes: 88 additions & 0 deletions core/vm/instructions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package vm

import (
"bytes"
"encoding/binary"
"encoding/json"
"fmt"
"math/big"
Expand Down Expand Up @@ -791,6 +792,93 @@ func TestBlobHash(t *testing.T) {
}
}

func TestBlockHashEip2935(t *testing.T) {
type testcase struct {
name string
idx uint64
expect common.Hash
hashes []common.Hash
}
var (
statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
expect = common.HexToHash("0x1234")
getHash = func(uint64) common.Hash {
return expect
}
newUint64 = func(val uint64) *uint64 { return &val }
blockContext = BlockContext{BlockNumber: big.NewInt(100), GetHash: getHash, Time: 0}
chainConfig = &params.ChainConfig{
ChainID: big.NewInt(1337),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
ArrowGlacierBlock: big.NewInt(0),
GrayGlacierBlock: big.NewInt(0),
ShanghaiTime: newUint64(0),
PragueTime: newUint64(0),
TerminalTotalDifficulty: big.NewInt(0),
TerminalTotalDifficultyPassed: true,
}
env = NewEVM(blockContext, TxContext{}, statedb, chainConfig, Config{})
stack = newstack()
pc = uint64(0)
evmInterpreter = env.interpreter
callBlockHashN = func(n uint64) func(t *testing.T, name string) uint256.Int {
return func(t *testing.T, name string) uint256.Int {
stack.push(uint256.NewInt(n))
opBlockhash(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
if len(stack.data) != 1 {
t.Errorf("Expected one item on stack got %d: ", len(stack.data))
}
return stack.pop()
}
}
callBlockHash10 = callBlockHashN(10)
callBlockHash1024 = callBlockHashN(1024)
)

expected, overflow := uint256.FromBig(new(big.Int).SetBytes(expect.Bytes()))
if overflow {
t.Errorf("invalid overflow")
}

// insert 500 block hashes, starting from genesis.
for i := uint64(0); i < 500; i++ {
var blockNumber common.Hash
binary.BigEndian.PutUint64(blockNumber[24:], i)
statedb.SetState(params.HistoryStorageAddress, blockNumber, expect)
}

// simulate a call to BLOCKHASH at block 100, i.e. less than 256
// blocks after a genesis with eip 2935 activated.
actual := callBlockHash10(t, "legacy")
if actual.Cmp(expected) != 0 {
t.Errorf("%s: expected %x, got %x", "legacy", expected, actual)
}

// now do the same thing much later, after all blocks have
// been inserted.
env.Context.BlockNumber.SetInt64(501)
actual = callBlockHash10(t, "contract")
if actual.Cmp(expected) != 0 {
t.Errorf("%s: expected %x, got %x", "contract", expected, actual)
}

// still at block 501, but now request the hash of a higher block
actual = callBlockHash1024(t, "contract with non-existent block hash")
if !actual.IsZero() {
t.Errorf("%s: expected 0, got %x", "contract with non-existent block hash", actual)
}
}

func TestOpMCopy(t *testing.T) {
// Test cases from https://eips.ethereum.org/EIPS/eip-5656#test-cases
for i, tc := range []struct {
Expand Down
3 changes: 3 additions & 0 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,9 @@ 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) {
core.ProcessParentBlockHash(env.state, header.Number.Uint64()-1, header.ParentHash)
}
return env, nil
}

Expand Down
2 changes: 2 additions & 0 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,6 @@ var (
BeaconRootsStorageAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02")
// SystemAddress is where the system-transaction is sent from as per EIP-4788
SystemAddress common.Address = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")
// HistoryStorageAddress is where the historical block hashes are stored.
HistoryStorageAddress common.Address = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")
)
Loading