Skip to content

Commit d1fcb78

Browse files
authored
fix: serve blockhash from state (#1224)
* fix: serve blockhash from state * add test
1 parent c27d825 commit d1fcb78

File tree

3 files changed

+138
-10
lines changed

3 files changed

+138
-10
lines changed

core/state_processor_test.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package core
1919
import (
2020
"crypto/ecdsa"
2121
"encoding/binary"
22+
"hash"
2223
"math/big"
2324
"testing"
2425

@@ -516,9 +517,142 @@ func TestProcessParentBlockHash(t *testing.T) {
516517
})
517518
}
518519

520+
func TestFeynmanBlockhashOpcode(t *testing.T) {
521+
var (
522+
feynmanTime uint64 = 100
523+
524+
// pragma solidity =0.8.24;
525+
//
526+
// contract GetBlockHash {
527+
// function get(uint256 blockNumber) external view returns (bytes32) {
528+
// return blockhash(blockNumber);
529+
// }
530+
// }
531+
GetBlockHashAddress = common.HexToAddress("0x1230000000000000000000000000000000000001")
532+
GetBlockHashCode = common.FromHex("0x6080604052348015600e575f80fd5b50600436106026575f3560e01c80639507d39a14602a575b5f80fd5b60396035366004604b565b4090565b60405190815260200160405180910390f35b5f60208284031215605a575f80fd5b503591905056fea26469706673582212200a417c99978abf73843291e0f284ce65b3ac99baa31abe6a43952308f3161ca864736f6c63430008180033")
533+
)
534+
535+
getBlockHash := func(evm *vm.EVM, statedb *state.StateDB, blockNumber uint64) common.Hash {
536+
// construct contract call calldata
537+
blockNumberBuf := make([]byte, 32)
538+
binary.BigEndian.PutUint64(blockNumberBuf[24:], blockNumber)
539+
calldata := common.Hex2Bytes("9507d39a") // "get(uint256 blockNumber)" selector
540+
calldata = append(calldata, blockNumberBuf...)
541+
542+
msg := types.NewMessage(
543+
params.SystemAddress, // from
544+
&GetBlockHashAddress, // to
545+
0, // nonce
546+
common.Big0, // amount
547+
30_000_000, // gasLimit
548+
common.Big0, // gasPrice
549+
common.Big0, // gasFeeCap
550+
common.Big0, // gasTipCap
551+
calldata, // data
552+
nil, // accessList
553+
false, // isFake
554+
nil, // setCodeAuthorizations
555+
)
556+
557+
evm.Reset(NewEVMTxContext(msg), statedb)
558+
ret, _, _ := evm.Call(vm.AccountRef(msg.From()), *msg.To(), msg.Data(), 30_000_000, common.Big0, nil)
559+
return common.BytesToHash(ret)
560+
}
561+
562+
var (
563+
chainConfig = &params.ChainConfig{
564+
ChainID: big.NewInt(1),
565+
HomesteadBlock: big.NewInt(0),
566+
EIP150Block: big.NewInt(0),
567+
EIP155Block: big.NewInt(0),
568+
EIP158Block: big.NewInt(0),
569+
ByzantiumBlock: big.NewInt(0),
570+
ConstantinopleBlock: big.NewInt(0),
571+
PetersburgBlock: big.NewInt(0),
572+
IstanbulBlock: big.NewInt(0),
573+
MuirGlacierBlock: big.NewInt(0),
574+
BerlinBlock: big.NewInt(0),
575+
LondonBlock: big.NewInt(0),
576+
ShanghaiBlock: big.NewInt(0),
577+
BernoulliBlock: big.NewInt(0),
578+
CurieBlock: big.NewInt(0),
579+
DarwinTime: new(uint64),
580+
DarwinV2Time: new(uint64),
581+
EuclidTime: new(uint64),
582+
EuclidV2Time: new(uint64),
583+
FeynmanTime: &feynmanTime,
584+
Ethash: new(params.EthashConfig),
585+
}
586+
587+
block1Hash = common.Hash{0x01}
588+
block2 = &types.Header{ParentHash: block1Hash, Number: big.NewInt(2), Time: 0, Difficulty: big.NewInt(0)}
589+
block2Hash = block2.Hash()
590+
block3 = &types.Header{ParentHash: block2Hash, Number: big.NewInt(3), Time: feynmanTime, Difficulty: big.NewInt(0)}
591+
592+
coinbase = common.Address{}
593+
)
594+
test := func(statedb *state.StateDB) {
595+
statedb.SetNonce(params.HistoryStorageAddress, 1)
596+
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode)
597+
statedb.SetCode(GetBlockHashAddress, GetBlockHashCode)
598+
statedb.IntermediateRoot(true)
599+
600+
// pre-Feynman
601+
vmContext := NewEVMBlockContext(block2, nil, chainConfig, &coinbase)
602+
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{})
603+
// note: parent hash (block1Hash) is not stored
604+
605+
// query 2 ancestors (block1, block0) of current block (block2)
606+
if have, want := getBlockHash(evm, statedb, 1), getBlockHashPreFeynman(chainConfig.ChainID, 1); have != want {
607+
t.Errorf("want block hash %v, have %v", want, have)
608+
}
609+
if have, want := getBlockHash(evm, statedb, 0), getBlockHashPreFeynman(chainConfig.ChainID, 0); have != want {
610+
t.Errorf("want block hash %v, have %v", want, have)
611+
}
612+
613+
// post-Feynman
614+
vmContext = NewEVMBlockContext(block3, nil, chainConfig, &coinbase)
615+
evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{})
616+
ProcessParentBlockHash(block3.ParentHash, evm, statedb) // store parent hash (block2Hash)
617+
618+
// query 2 ancestors (block2, block1) of current block (block3)
619+
if have, want := getBlockHash(evm, statedb, 2), block2Hash; have != want {
620+
t.Errorf("want block hash %v, have %v", want, have)
621+
}
622+
// querying before the Feynman fork boundary returns the empty hash
623+
if have, want := getBlockHash(evm, statedb, 1), (common.Hash{}); have != want {
624+
t.Errorf("want block hash %v, have %v", want, have)
625+
}
626+
}
627+
t.Run("MPT", func(t *testing.T) {
628+
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
629+
test(statedb)
630+
})
631+
}
632+
519633
func getParentBlockHash(statedb *state.StateDB, number uint64) common.Hash {
520634
ringIndex := number % params.HistoryServeWindow
521635
var key common.Hash
522636
binary.BigEndian.PutUint64(key[24:], ringIndex)
523637
return statedb.GetState(params.HistoryStorageAddress, key)
524638
}
639+
640+
type keccakState interface {
641+
hash.Hash
642+
Read([]byte) (int, error)
643+
}
644+
645+
func getBlockHashPreFeynman(chainId *big.Int, blockNumber uint64) common.Hash {
646+
chainIdBuf := make([]byte, 8)
647+
binary.BigEndian.PutUint64(chainIdBuf, chainId.Uint64())
648+
num64Buf := make([]byte, 8)
649+
binary.BigEndian.PutUint64(num64Buf, blockNumber)
650+
651+
hasher := sha3.NewLegacyKeccak256().(keccakState)
652+
hasher.Write(chainIdBuf)
653+
hasher.Write(num64Buf)
654+
655+
var hasherBuf common.Hash
656+
hasher.Read(hasherBuf[:])
657+
return hasherBuf
658+
}

core/vm/instructions.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -485,18 +485,12 @@ func opBlockhashPostFeynman(pc *uint64, interpreter *EVMInterpreter, scope *Scop
485485
lower = upper - 256
486486
}
487487
if num64 >= lower && num64 < upper {
488-
res := interpreter.evm.Context.GetHash(num64)
489-
if witness := interpreter.evm.StateDB.Witness(); witness != nil {
490-
witness.AddBlockHash(num64)
491-
}
492-
num.SetBytes(res[:])
493-
494-
// for provability, revm loads block hash from the history storage system contract,
495-
// so we need to ensure that the corresponding slot is present in the execution witness.
488+
// load block hash from the history storage system contract.
496489
ringIndex := num64 % params.HistoryServeWindow
497490
var key common.Hash
498491
binary.BigEndian.PutUint64(key[24:], ringIndex)
499-
interpreter.evm.StateDB.GetState(params.HistoryStorageAddress, key)
492+
res := interpreter.evm.StateDB.GetState(params.HistoryStorageAddress, key)
493+
num.SetBytes(res[:])
500494
} else {
501495
num.Clear()
502496
}

params/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
const (
2525
VersionMajor = 5 // Major version component of the current release
2626
VersionMinor = 8 // Minor version component of the current release
27-
VersionPatch = 70 // Patch version component of the current release
27+
VersionPatch = 71 // Patch version component of the current release
2828
VersionMeta = "mainnet" // Version metadata to append to the version string
2929
)
3030

0 commit comments

Comments
 (0)