mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 21:26:42 +00:00
fix: serve blockhash from state (#1224)
* fix: serve blockhash from state * add test
This commit is contained in:
parent
c27d82597b
commit
d1fcb787b1
3 changed files with 138 additions and 10 deletions
|
|
@ -19,6 +19,7 @@ package core
|
|||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
|
|
@ -516,9 +517,142 @@ func TestProcessParentBlockHash(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestFeynmanBlockhashOpcode(t *testing.T) {
|
||||
var (
|
||||
feynmanTime uint64 = 100
|
||||
|
||||
// pragma solidity =0.8.24;
|
||||
//
|
||||
// contract GetBlockHash {
|
||||
// function get(uint256 blockNumber) external view returns (bytes32) {
|
||||
// return blockhash(blockNumber);
|
||||
// }
|
||||
// }
|
||||
GetBlockHashAddress = common.HexToAddress("0x1230000000000000000000000000000000000001")
|
||||
GetBlockHashCode = common.FromHex("0x6080604052348015600e575f80fd5b50600436106026575f3560e01c80639507d39a14602a575b5f80fd5b60396035366004604b565b4090565b60405190815260200160405180910390f35b5f60208284031215605a575f80fd5b503591905056fea26469706673582212200a417c99978abf73843291e0f284ce65b3ac99baa31abe6a43952308f3161ca864736f6c63430008180033")
|
||||
)
|
||||
|
||||
getBlockHash := func(evm *vm.EVM, statedb *state.StateDB, blockNumber uint64) common.Hash {
|
||||
// construct contract call calldata
|
||||
blockNumberBuf := make([]byte, 32)
|
||||
binary.BigEndian.PutUint64(blockNumberBuf[24:], blockNumber)
|
||||
calldata := common.Hex2Bytes("9507d39a") // "get(uint256 blockNumber)" selector
|
||||
calldata = append(calldata, blockNumberBuf...)
|
||||
|
||||
msg := types.NewMessage(
|
||||
params.SystemAddress, // from
|
||||
&GetBlockHashAddress, // to
|
||||
0, // nonce
|
||||
common.Big0, // amount
|
||||
30_000_000, // gasLimit
|
||||
common.Big0, // gasPrice
|
||||
common.Big0, // gasFeeCap
|
||||
common.Big0, // gasTipCap
|
||||
calldata, // data
|
||||
nil, // accessList
|
||||
false, // isFake
|
||||
nil, // setCodeAuthorizations
|
||||
)
|
||||
|
||||
evm.Reset(NewEVMTxContext(msg), statedb)
|
||||
ret, _, _ := evm.Call(vm.AccountRef(msg.From()), *msg.To(), msg.Data(), 30_000_000, common.Big0, nil)
|
||||
return common.BytesToHash(ret)
|
||||
}
|
||||
|
||||
var (
|
||||
chainConfig = ¶ms.ChainConfig{
|
||||
ChainID: big.NewInt(1),
|
||||
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),
|
||||
ShanghaiBlock: big.NewInt(0),
|
||||
BernoulliBlock: big.NewInt(0),
|
||||
CurieBlock: big.NewInt(0),
|
||||
DarwinTime: new(uint64),
|
||||
DarwinV2Time: new(uint64),
|
||||
EuclidTime: new(uint64),
|
||||
EuclidV2Time: new(uint64),
|
||||
FeynmanTime: &feynmanTime,
|
||||
Ethash: new(params.EthashConfig),
|
||||
}
|
||||
|
||||
block1Hash = common.Hash{0x01}
|
||||
block2 = &types.Header{ParentHash: block1Hash, Number: big.NewInt(2), Time: 0, Difficulty: big.NewInt(0)}
|
||||
block2Hash = block2.Hash()
|
||||
block3 = &types.Header{ParentHash: block2Hash, Number: big.NewInt(3), Time: feynmanTime, Difficulty: big.NewInt(0)}
|
||||
|
||||
coinbase = common.Address{}
|
||||
)
|
||||
test := func(statedb *state.StateDB) {
|
||||
statedb.SetNonce(params.HistoryStorageAddress, 1)
|
||||
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode)
|
||||
statedb.SetCode(GetBlockHashAddress, GetBlockHashCode)
|
||||
statedb.IntermediateRoot(true)
|
||||
|
||||
// pre-Feynman
|
||||
vmContext := NewEVMBlockContext(block2, nil, chainConfig, &coinbase)
|
||||
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{})
|
||||
// note: parent hash (block1Hash) is not stored
|
||||
|
||||
// query 2 ancestors (block1, block0) of current block (block2)
|
||||
if have, want := getBlockHash(evm, statedb, 1), getBlockHashPreFeynman(chainConfig.ChainID, 1); have != want {
|
||||
t.Errorf("want block hash %v, have %v", want, have)
|
||||
}
|
||||
if have, want := getBlockHash(evm, statedb, 0), getBlockHashPreFeynman(chainConfig.ChainID, 0); have != want {
|
||||
t.Errorf("want block hash %v, have %v", want, have)
|
||||
}
|
||||
|
||||
// post-Feynman
|
||||
vmContext = NewEVMBlockContext(block3, nil, chainConfig, &coinbase)
|
||||
evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{})
|
||||
ProcessParentBlockHash(block3.ParentHash, evm, statedb) // store parent hash (block2Hash)
|
||||
|
||||
// query 2 ancestors (block2, block1) of current block (block3)
|
||||
if have, want := getBlockHash(evm, statedb, 2), block2Hash; have != want {
|
||||
t.Errorf("want block hash %v, have %v", want, have)
|
||||
}
|
||||
// querying before the Feynman fork boundary returns the empty hash
|
||||
if have, want := getBlockHash(evm, statedb, 1), (common.Hash{}); have != want {
|
||||
t.Errorf("want block hash %v, have %v", want, have)
|
||||
}
|
||||
}
|
||||
t.Run("MPT", func(t *testing.T) {
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), 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)
|
||||
}
|
||||
|
||||
type keccakState interface {
|
||||
hash.Hash
|
||||
Read([]byte) (int, error)
|
||||
}
|
||||
|
||||
func getBlockHashPreFeynman(chainId *big.Int, blockNumber uint64) common.Hash {
|
||||
chainIdBuf := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(chainIdBuf, chainId.Uint64())
|
||||
num64Buf := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(num64Buf, blockNumber)
|
||||
|
||||
hasher := sha3.NewLegacyKeccak256().(keccakState)
|
||||
hasher.Write(chainIdBuf)
|
||||
hasher.Write(num64Buf)
|
||||
|
||||
var hasherBuf common.Hash
|
||||
hasher.Read(hasherBuf[:])
|
||||
return hasherBuf
|
||||
}
|
||||
|
|
|
|||
|
|
@ -485,18 +485,12 @@ func opBlockhashPostFeynman(pc *uint64, interpreter *EVMInterpreter, scope *Scop
|
|||
lower = upper - 256
|
||||
}
|
||||
if num64 >= lower && num64 < upper {
|
||||
res := interpreter.evm.Context.GetHash(num64)
|
||||
if witness := interpreter.evm.StateDB.Witness(); witness != nil {
|
||||
witness.AddBlockHash(num64)
|
||||
}
|
||||
num.SetBytes(res[:])
|
||||
|
||||
// for provability, revm loads block hash from the history storage system contract,
|
||||
// so we need to ensure that the corresponding slot is present in the execution witness.
|
||||
// load block hash from the history storage system contract.
|
||||
ringIndex := num64 % params.HistoryServeWindow
|
||||
var key common.Hash
|
||||
binary.BigEndian.PutUint64(key[24:], ringIndex)
|
||||
interpreter.evm.StateDB.GetState(params.HistoryStorageAddress, key)
|
||||
res := interpreter.evm.StateDB.GetState(params.HistoryStorageAddress, key)
|
||||
num.SetBytes(res[:])
|
||||
} else {
|
||||
num.Clear()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
const (
|
||||
VersionMajor = 5 // Major version component of the current release
|
||||
VersionMinor = 8 // Minor version component of the current release
|
||||
VersionPatch = 70 // Patch version component of the current release
|
||||
VersionPatch = 71 // Patch version component of the current release
|
||||
VersionMeta = "mainnet" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue