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 (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"hash"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"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 {
|
func getParentBlockHash(statedb *state.StateDB, number uint64) common.Hash {
|
||||||
ringIndex := number % params.HistoryServeWindow
|
ringIndex := number % params.HistoryServeWindow
|
||||||
var key common.Hash
|
var key common.Hash
|
||||||
binary.BigEndian.PutUint64(key[24:], ringIndex)
|
binary.BigEndian.PutUint64(key[24:], ringIndex)
|
||||||
return statedb.GetState(params.HistoryStorageAddress, key)
|
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
|
lower = upper - 256
|
||||||
}
|
}
|
||||||
if num64 >= lower && num64 < upper {
|
if num64 >= lower && num64 < upper {
|
||||||
res := interpreter.evm.Context.GetHash(num64)
|
// load block hash from the history storage system contract.
|
||||||
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.
|
|
||||||
ringIndex := num64 % params.HistoryServeWindow
|
ringIndex := num64 % params.HistoryServeWindow
|
||||||
var key common.Hash
|
var key common.Hash
|
||||||
binary.BigEndian.PutUint64(key[24:], ringIndex)
|
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 {
|
} else {
|
||||||
num.Clear()
|
num.Clear()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import (
|
||||||
const (
|
const (
|
||||||
VersionMajor = 5 // Major version component of the current release
|
VersionMajor = 5 // Major version component of the current release
|
||||||
VersionMinor = 8 // Minor 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
|
VersionMeta = "mainnet" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue