mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-04 14:08:39 +00:00
internal/ethapi: default block parameter to latest on state methods (#35100)
Make the `Block` parameter optional on the six state-reading methods, defaulting to `latest` when omitted: - `eth_getBalance` - `eth_getCode` - `eth_getStorageAt` - `eth_getTransactionCount` - `eth_getProof` - `eth_getStorageValues` This implements the behavior proposed in https://github.com/ethereum/execution-apis/pull/812. --------- Co-authored-by: Sina M <1591639+s1na@users.noreply.github.com>
This commit is contained in:
parent
80d9ba5d97
commit
6b451a4245
3 changed files with 116 additions and 23 deletions
|
|
@ -328,11 +328,21 @@ func (api *BlockChainAPI) BlockNumber() hexutil.Uint64 {
|
||||||
return hexutil.Uint64(header.Number.Uint64())
|
return hexutil.Uint64(header.Number.Uint64())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// blockNrOrHashOrLatest resolves an optional block selector, defaulting to the
|
||||||
|
// latest block when the parameter was omitted by the caller (nil).
|
||||||
|
func blockNrOrHashOrLatest(blockNrOrHash *rpc.BlockNumberOrHash) rpc.BlockNumberOrHash {
|
||||||
|
if blockNrOrHash != nil {
|
||||||
|
return *blockNrOrHash
|
||||||
|
}
|
||||||
|
return rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
// GetBalance returns the amount of wei for the given address in the state of the
|
// GetBalance returns the amount of wei for the given address in the state of the
|
||||||
// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
|
// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
|
||||||
// block numbers are also allowed.
|
// block numbers are also allowed. When the block parameter is omitted, it
|
||||||
func (api *BlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) {
|
// defaults to the latest block.
|
||||||
state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
func (api *BlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash *rpc.BlockNumberOrHash) (*hexutil.Big, error) {
|
||||||
|
state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHashOrLatest(blockNrOrHash))
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -371,7 +381,8 @@ func (n *proofList) Delete(key []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProof returns the Merkle-proof for a given account and optionally some storage keys.
|
// GetProof returns the Merkle-proof for a given account and optionally some storage keys.
|
||||||
func (api *BlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) {
|
// When the block parameter is omitted, it defaults to the latest block.
|
||||||
|
func (api *BlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash *rpc.BlockNumberOrHash) (*AccountResult, error) {
|
||||||
if len(storageKeys) > maxGetProofKeys {
|
if len(storageKeys) > maxGetProofKeys {
|
||||||
return nil, &invalidParamsError{fmt.Sprintf("too many storage keys requested (max %d, got %d)", maxGetProofKeys, len(storageKeys))}
|
return nil, &invalidParamsError{fmt.Sprintf("too many storage keys requested (max %d, got %d)", maxGetProofKeys, len(storageKeys))}
|
||||||
}
|
}
|
||||||
|
|
@ -388,7 +399,7 @@ func (api *BlockChainAPI) GetProof(ctx context.Context, address common.Address,
|
||||||
return nil, &invalidParamsError{fmt.Sprintf("%v: %q", err, hexKey)}
|
return nil, &invalidParamsError{fmt.Sprintf("%v: %q", err, hexKey)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statedb, header, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
statedb, header, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHashOrLatest(blockNrOrHash))
|
||||||
if statedb == nil || err != nil {
|
if statedb == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -584,8 +595,9 @@ func (api *BlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHas
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCode returns the code stored at the given address in the state for the given block number.
|
// GetCode returns the code stored at the given address in the state for the given block number.
|
||||||
func (api *BlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
|
// When the block parameter is omitted, it defaults to the latest block.
|
||||||
state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
func (api *BlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNrOrHash *rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
|
||||||
|
state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHashOrLatest(blockNrOrHash))
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -595,9 +607,10 @@ func (api *BlockChainAPI) GetCode(ctx context.Context, address common.Address, b
|
||||||
|
|
||||||
// GetStorageAt returns the storage from the state at the given address, key and
|
// GetStorageAt returns the storage from the state at the given address, key and
|
||||||
// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
|
// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
|
||||||
// numbers are also allowed.
|
// numbers are also allowed. When the block parameter is omitted, it defaults to
|
||||||
func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, hexKey string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
|
// the latest block.
|
||||||
state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, hexKey string, blockNrOrHash *rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
|
||||||
|
state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHashOrLatest(blockNrOrHash))
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -610,8 +623,9 @@ func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Addre
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStorageValues returns multiple storage slot values for multiple accounts
|
// GetStorageValues returns multiple storage slot values for multiple accounts
|
||||||
// at the given block.
|
// at the given block. When the block parameter is omitted, it defaults to the
|
||||||
func (api *BlockChainAPI) GetStorageValues(ctx context.Context, requests map[common.Address][]common.Hash, blockNrOrHash rpc.BlockNumberOrHash) (map[common.Address][]hexutil.Bytes, error) {
|
// latest block.
|
||||||
|
func (api *BlockChainAPI) GetStorageValues(ctx context.Context, requests map[common.Address][]common.Hash, blockNrOrHash *rpc.BlockNumberOrHash) (map[common.Address][]hexutil.Bytes, error) {
|
||||||
// Count total slots requested.
|
// Count total slots requested.
|
||||||
var totalSlots int
|
var totalSlots int
|
||||||
for _, keys := range requests {
|
for _, keys := range requests {
|
||||||
|
|
@ -624,7 +638,7 @@ func (api *BlockChainAPI) GetStorageValues(ctx context.Context, requests map[com
|
||||||
return nil, &invalidParamsError{message: "empty request"}
|
return nil, &invalidParamsError{message: "empty request"}
|
||||||
}
|
}
|
||||||
|
|
||||||
state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHashOrLatest(blockNrOrHash))
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1483,10 +1497,12 @@ func (api *TransactionAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Cont
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionCount returns the number of transactions the given address has sent for the given block number
|
// GetTransactionCount returns the number of transactions the given address has sent for the given block number.
|
||||||
func (api *TransactionAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Uint64, error) {
|
// When the block parameter is omitted, it defaults to the latest block.
|
||||||
|
func (api *TransactionAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNrOrHash *rpc.BlockNumberOrHash) (*hexutil.Uint64, error) {
|
||||||
|
bnh := blockNrOrHashOrLatest(blockNrOrHash)
|
||||||
// Ask transaction pool for the nonce which includes pending transactions
|
// Ask transaction pool for the nonce which includes pending transactions
|
||||||
if blockNr, ok := blockNrOrHash.Number(); ok && blockNr == rpc.PendingBlockNumber {
|
if blockNr, ok := bnh.Number(); ok && blockNr == rpc.PendingBlockNumber {
|
||||||
nonce, err := api.b.GetPoolNonce(ctx, address)
|
nonce, err := api.b.GetPoolNonce(ctx, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -1494,7 +1510,7 @@ func (api *TransactionAPI) GetTransactionCount(ctx context.Context, address comm
|
||||||
return (*hexutil.Uint64)(&nonce), nil
|
return (*hexutil.Uint64)(&nonce), nil
|
||||||
}
|
}
|
||||||
// Resolve block number and use its state to ask for the nonce
|
// Resolve block number and use its state to ask for the nonce
|
||||||
state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, bnh)
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4216,7 +4216,7 @@ func TestGetStorageValues(t *testing.T) {
|
||||||
result, err := api.GetStorageValues(context.Background(), map[common.Address][]common.Hash{
|
result, err := api.GetStorageValues(context.Background(), map[common.Address][]common.Hash{
|
||||||
addr1: {slot0, slot1},
|
addr1: {slot0, slot1},
|
||||||
addr2: {slot2},
|
addr2: {slot2},
|
||||||
}, latest)
|
}, &latest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -4236,7 +4236,7 @@ func TestGetStorageValues(t *testing.T) {
|
||||||
// Missing slot returns zero.
|
// Missing slot returns zero.
|
||||||
result, err = api.GetStorageValues(context.Background(), map[common.Address][]common.Hash{
|
result, err = api.GetStorageValues(context.Background(), map[common.Address][]common.Hash{
|
||||||
addr1: {common.HexToHash("0xff")},
|
addr1: {common.HexToHash("0xff")},
|
||||||
}, latest)
|
}, &latest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -4245,7 +4245,7 @@ func TestGetStorageValues(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty request returns error.
|
// Empty request returns error.
|
||||||
_, err = api.GetStorageValues(context.Background(), map[common.Address][]common.Hash{}, latest)
|
_, err = api.GetStorageValues(context.Background(), map[common.Address][]common.Hash{}, &latest)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error for empty request")
|
t.Fatal("expected error for empty request")
|
||||||
}
|
}
|
||||||
|
|
@ -4257,8 +4257,85 @@ func TestGetStorageValues(t *testing.T) {
|
||||||
}
|
}
|
||||||
_, err = api.GetStorageValues(context.Background(), map[common.Address][]common.Hash{
|
_, err = api.GetStorageValues(context.Background(), map[common.Address][]common.Hash{
|
||||||
addr1: tooMany,
|
addr1: tooMany,
|
||||||
}, latest)
|
}, &latest)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error for exceeding slot limit")
|
t.Fatal("expected error for exceeding slot limit")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestStateMethodsDefaultToLatest verifies that the state-reading methods
|
||||||
|
// default the optional block parameter to "latest".
|
||||||
|
func TestStateMethodsDefaultToLatest(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
var (
|
||||||
|
accounts = newAccounts(2)
|
||||||
|
slot = common.HexToHash("0x01")
|
||||||
|
val = common.HexToHash("0x42")
|
||||||
|
code = []byte{0x60, 0x00, 0x60, 0x00}
|
||||||
|
genesis = &core.Genesis{
|
||||||
|
Config: params.MergedTestChainConfig,
|
||||||
|
Alloc: types.GenesisAlloc{
|
||||||
|
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[1].addr: {
|
||||||
|
Balance: big.NewInt(2 * params.Ether),
|
||||||
|
Nonce: 7,
|
||||||
|
Code: code,
|
||||||
|
Storage: map[common.Hash]common.Hash{slot: val},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
acc = accounts[1].addr
|
||||||
|
ctx = context.Background()
|
||||||
|
)
|
||||||
|
backend := newTestBackend(t, 1, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
|
||||||
|
b.SetPoS()
|
||||||
|
})
|
||||||
|
srv := rpc.NewServer()
|
||||||
|
if err := srv.RegisterName("eth", NewBlockChainAPI(backend)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := srv.RegisterName("eth", NewTransactionAPI(backend, new(AddrLocker))); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
client := rpc.DialInProc(srv)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
// call invokes method twice: once omitting the block param and once passing
|
||||||
|
// "latest" explicitly. Both must succeed and return identical results.
|
||||||
|
call := func(name string, dst func() any, explicit []any, omitted []any) {
|
||||||
|
t.Helper()
|
||||||
|
gotOmitted := dst()
|
||||||
|
if err := client.CallContext(ctx, gotOmitted, name, omitted...); err != nil {
|
||||||
|
t.Fatalf("%s with omitted block: unexpected error: %v", name, err)
|
||||||
|
}
|
||||||
|
gotLatest := dst()
|
||||||
|
if err := client.CallContext(ctx, gotLatest, name, explicit...); err != nil {
|
||||||
|
t.Fatalf("%s with explicit latest: unexpected error: %v", name, err)
|
||||||
|
}
|
||||||
|
o, _ := json.Marshal(gotOmitted)
|
||||||
|
l, _ := json.Marshal(gotLatest)
|
||||||
|
if !bytes.Equal(o, l) {
|
||||||
|
t.Errorf("%s: omitted-block result %s != latest result %s", name, o, l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
call("eth_getBalance",
|
||||||
|
func() any { return new(hexutil.Big) },
|
||||||
|
[]any{acc, "latest"}, []any{acc})
|
||||||
|
call("eth_getCode",
|
||||||
|
func() any { return new(hexutil.Bytes) },
|
||||||
|
[]any{acc, "latest"}, []any{acc})
|
||||||
|
call("eth_getTransactionCount",
|
||||||
|
func() any { return new(hexutil.Uint64) },
|
||||||
|
[]any{acc, "latest"}, []any{acc})
|
||||||
|
call("eth_getStorageAt",
|
||||||
|
func() any { return new(hexutil.Bytes) },
|
||||||
|
[]any{acc, slot, "latest"}, []any{acc, slot})
|
||||||
|
call("eth_getProof",
|
||||||
|
func() any { return new(AccountResult) },
|
||||||
|
[]any{acc, []string{slot.Hex()}, "latest"}, []any{acc, []string{slot.Hex()}})
|
||||||
|
call("eth_getStorageValues",
|
||||||
|
func() any { return new(map[common.Address][]hexutil.Bytes) },
|
||||||
|
[]any{map[common.Address][]common.Hash{acc: {slot}}, "latest"},
|
||||||
|
[]any{map[common.Address][]common.Hash{acc: {slot}}})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -566,13 +566,13 @@ web3._extend({
|
||||||
name: 'getProof',
|
name: 'getProof',
|
||||||
call: 'eth_getProof',
|
call: 'eth_getProof',
|
||||||
params: 3,
|
params: 3,
|
||||||
inputFormatter: [web3._extend.formatters.inputAddressFormatter, null, web3._extend.formatters.inputBlockNumberFormatter]
|
inputFormatter: [web3._extend.formatters.inputAddressFormatter, null, web3._extend.formatters.inputDefaultBlockNumberFormatter]
|
||||||
}),
|
}),
|
||||||
new web3._extend.Method({
|
new web3._extend.Method({
|
||||||
name: 'getStorageValues',
|
name: 'getStorageValues',
|
||||||
call: 'eth_getStorageValues',
|
call: 'eth_getStorageValues',
|
||||||
params: 2,
|
params: 2,
|
||||||
inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter]
|
inputFormatter: [null, web3._extend.formatters.inputDefaultBlockNumberFormatter]
|
||||||
}),
|
}),
|
||||||
new web3._extend.Method({
|
new web3._extend.Method({
|
||||||
name: 'createAccessList',
|
name: 'createAccessList',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue