mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
core, eth, ethapi, les, light, rpc: Support Block Number or Hash on state-related RPCs.
This commit is contained in:
parent
cdae1c59ab
commit
fecbf99100
10 changed files with 339 additions and 16 deletions
|
|
@ -1691,6 +1691,11 @@ func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool {
|
||||||
return bc.hc.HasHeader(hash, number)
|
return bc.hc.HasHeader(hash, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCanonicalHash returns the canonical hash for a given block number
|
||||||
|
func (bc *BlockChain) GetCanonicalHash(number uint64) common.Hash {
|
||||||
|
return bc.hc.GetCanonicalHash(number)
|
||||||
|
}
|
||||||
|
|
||||||
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
|
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
|
||||||
// hash, fetching towards the genesis block.
|
// hash, fetching towards the genesis block.
|
||||||
func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
|
func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
|
||||||
|
|
|
||||||
|
|
@ -435,6 +435,10 @@ func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header {
|
||||||
return hc.GetHeader(hash, number)
|
return hc.GetHeader(hash, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hc *HeaderChain) GetCanonicalHash(number uint64) common.Hash {
|
||||||
|
return rawdb.ReadCanonicalHash(hc.chainDb, number)
|
||||||
|
}
|
||||||
|
|
||||||
// CurrentHeader retrieves the current head header of the canonical chain. The
|
// CurrentHeader retrieves the current head header of the canonical chain. The
|
||||||
// header is retrieved from the HeaderChain's internal cache.
|
// header is retrieved from the HeaderChain's internal cache.
|
||||||
func (hc *HeaderChain) CurrentHeader() *types.Header {
|
func (hc *HeaderChain) CurrentHeader() *types.Header {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
|
@ -69,6 +70,27 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNum
|
||||||
return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr)), nil
|
return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *EthAPIBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
|
||||||
|
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||||
|
return b.HeaderByNumber(ctx, blockNr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||||
|
header := b.eth.blockchain.GetHeaderByHash(hash)
|
||||||
|
if header == nil {
|
||||||
|
return nil, fmt.Errorf("header for hash not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||||
|
return nil, fmt.Errorf("hash is not currently canonical")
|
||||||
|
}
|
||||||
|
|
||||||
|
return header, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("specifier neither block nor hash")
|
||||||
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
func (b *EthAPIBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||||
return b.eth.blockchain.GetHeaderByHash(hash), nil
|
return b.eth.blockchain.GetHeaderByHash(hash), nil
|
||||||
}
|
}
|
||||||
|
|
@ -86,6 +108,32 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumb
|
||||||
return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil
|
return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
|
||||||
|
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||||
|
return b.BlockByNumber(ctx, blockNr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||||
|
header := b.eth.blockchain.GetHeaderByHash(hash)
|
||||||
|
if header == nil {
|
||||||
|
return nil, fmt.Errorf("header for hash not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||||
|
return nil, fmt.Errorf("hash is not currently canonical")
|
||||||
|
}
|
||||||
|
|
||||||
|
block := b.eth.blockchain.GetBlock(hash, header.Number.Uint64())
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("block not found with hash at expected number")
|
||||||
|
}
|
||||||
|
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("specifier neither block nor hash")
|
||||||
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
||||||
// Pending state is only known by the miner
|
// Pending state is only known by the miner
|
||||||
if blockNr == rpc.PendingBlockNumber {
|
if blockNr == rpc.PendingBlockNumber {
|
||||||
|
|
@ -101,6 +149,32 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.
|
||||||
return stateDb, header, err
|
return stateDb, header, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
|
||||||
|
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||||
|
return b.StateAndHeaderByNumber(ctx, blockNr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||||
|
header, err := b.HeaderByHash(ctx, hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if header == nil {
|
||||||
|
return nil, nil, fmt.Errorf("header for hash not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||||
|
return nil, nil, fmt.Errorf("hash is not currently canonical")
|
||||||
|
}
|
||||||
|
|
||||||
|
stateDb, err := b.eth.BlockChain().StateAt(header.Root)
|
||||||
|
return stateDb, header, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, fmt.Errorf("specifier neither block nor hash")
|
||||||
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
func (b *EthAPIBackend) GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||||
return b.eth.blockchain.GetBlockByHash(hash), nil
|
return b.eth.blockchain.GetBlockByHash(hash), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -500,8 +500,8 @@ func (s *PublicBlockChainAPI) BlockNumber() hexutil.Uint64 {
|
||||||
// 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.
|
||||||
func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*hexutil.Big, error) {
|
func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) {
|
||||||
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -525,8 +525,8 @@ type StorageResult struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNr rpc.BlockNumber) (*AccountResult, error) {
|
func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) {
|
||||||
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -652,8 +652,8 @@ func (s *PublicBlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, bloc
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
|
||||||
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -664,8 +664,8 @@ func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Addres
|
||||||
// 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.
|
||||||
func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
|
||||||
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -683,10 +683,10 @@ type CallArgs struct {
|
||||||
Data hexutil.Bytes `json:"data"`
|
Data hexutil.Bytes `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) {
|
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) {
|
||||||
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||||
|
|
||||||
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
state, header, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, 0, false, err
|
return nil, 0, false, err
|
||||||
}
|
}
|
||||||
|
|
@ -751,8 +751,8 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
|
||||||
|
|
||||||
// Call executes the given transaction on the state for the given block number.
|
// Call executes the given transaction on the state for the given block number.
|
||||||
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
||||||
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
|
||||||
result, _, _, err := s.doCall(ctx, args, blockNr, 5*time.Second, s.b.RPCGasCap())
|
result, _, _, err := s.doCall(ctx, args, blockNrOrHash, 5*time.Second, s.b.RPCGasCap())
|
||||||
return (hexutil.Bytes)(result), err
|
return (hexutil.Bytes)(result), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -786,7 +786,8 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h
|
||||||
executable := func(gas uint64) bool {
|
executable := func(gas uint64) bool {
|
||||||
args.Gas = hexutil.Uint64(gas)
|
args.Gas = hexutil.Uint64(gas)
|
||||||
|
|
||||||
_, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, 0, gasCap)
|
blockNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||||
|
_, _, failed, err := s.doCall(ctx, args, blockNrOrHash, 0, gasCap)
|
||||||
if err != nil || failed {
|
if err != nil || failed {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -1082,9 +1083,9 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx cont
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*hexutil.Uint64, error) {
|
func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Uint64, error) {
|
||||||
// Ask transaction pool for the nonce which includes pending transactions
|
// Ask transaction pool for the nonce which includes pending transactions
|
||||||
if blockNr == rpc.PendingBlockNumber {
|
if blockNr, ok := blockNrOrHash.Number(); ok && blockNr == rpc.PendingBlockNumber {
|
||||||
nonce, err := s.b.GetPoolNonce(ctx, address)
|
nonce, err := s.b.GetPoolNonce(ctx, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -1092,7 +1093,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, addr
|
||||||
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 := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,9 @@ type Backend interface {
|
||||||
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
|
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
|
||||||
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error)
|
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error)
|
||||||
StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error)
|
StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error)
|
||||||
|
HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error)
|
||||||
|
BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error)
|
||||||
|
StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error)
|
||||||
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
|
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
|
||||||
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
|
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
|
||||||
GetTd(blockHash common.Hash) *big.Int
|
GetTd(blockHash common.Hash) *big.Int
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package les
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
|
@ -63,6 +64,31 @@ func (b *LesApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNum
|
||||||
return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blockNr))
|
return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blockNr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *LesApiBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
|
||||||
|
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||||
|
return b.HeaderByNumber(ctx, blockNr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||||
|
header, err := b.HeaderByHash(ctx, hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if header == nil {
|
||||||
|
return nil, fmt.Errorf("header for hash not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||||
|
return nil, fmt.Errorf("hash is not currently canonical")
|
||||||
|
}
|
||||||
|
|
||||||
|
return header, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("specifier neither block nor hash")
|
||||||
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
func (b *LesApiBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||||
return b.eth.blockchain.GetHeaderByHash(hash), nil
|
return b.eth.blockchain.GetHeaderByHash(hash), nil
|
||||||
}
|
}
|
||||||
|
|
@ -75,6 +101,31 @@ func (b *LesApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumb
|
||||||
return b.GetBlock(ctx, header.Hash())
|
return b.GetBlock(ctx, header.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *LesApiBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
|
||||||
|
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||||
|
return b.BlockByNumber(ctx, blockNr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||||
|
block, err := b.GetBlock(ctx, hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("block not found with hash at expected number")
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(block.NumberU64()) != hash {
|
||||||
|
return nil, fmt.Errorf("hash is not currently canonical")
|
||||||
|
}
|
||||||
|
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("specifier neither block nor hash")
|
||||||
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
||||||
header, err := b.HeaderByNumber(ctx, blockNr)
|
header, err := b.HeaderByNumber(ctx, blockNr)
|
||||||
if header == nil || err != nil {
|
if header == nil || err != nil {
|
||||||
|
|
@ -83,6 +134,27 @@ func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.
|
||||||
return light.NewState(ctx, header, b.eth.odr), header, nil
|
return light.NewState(ctx, header, b.eth.odr), header, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *LesApiBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
|
||||||
|
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||||
|
return b.StateAndHeaderByNumber(ctx, blockNr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||||
|
header := b.eth.blockchain.GetHeaderByHash(hash)
|
||||||
|
if header == nil {
|
||||||
|
return nil, nil, fmt.Errorf("header for hash not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||||
|
return nil, nil, fmt.Errorf("hash is not currently canonical")
|
||||||
|
}
|
||||||
|
|
||||||
|
return light.NewState(ctx, header, b.eth.odr), header, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, fmt.Errorf("specifier neither block nor hash")
|
||||||
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) {
|
func (b *LesApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) {
|
||||||
return b.eth.blockchain.GetBlockByHash(ctx, blockHash)
|
return b.eth.blockchain.GetBlockByHash(ctx, blockHash)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ func errResp(code errCode, format string, v ...interface{}) error {
|
||||||
type BlockChain interface {
|
type BlockChain interface {
|
||||||
Config() *params.ChainConfig
|
Config() *params.ChainConfig
|
||||||
HasHeader(hash common.Hash, number uint64) bool
|
HasHeader(hash common.Hash, number uint64) bool
|
||||||
|
GetCanonicalHash(number uint64) common.Hash
|
||||||
GetHeader(hash common.Hash, number uint64) *types.Header
|
GetHeader(hash common.Hash, number uint64) *types.Header
|
||||||
GetHeaderByHash(hash common.Hash) *types.Header
|
GetHeaderByHash(hash common.Hash) *types.Header
|
||||||
CurrentHeader() *types.Header
|
CurrentHeader() *types.Header
|
||||||
|
|
|
||||||
|
|
@ -429,6 +429,11 @@ func (bc *LightChain) HasHeader(hash common.Hash, number uint64) bool {
|
||||||
return bc.hc.HasHeader(hash, number)
|
return bc.hc.HasHeader(hash, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCanonicalHash returns the canonical hash for a given block number
|
||||||
|
func (bc *LightChain) GetCanonicalHash(number uint64) common.Hash {
|
||||||
|
return bc.hc.GetCanonicalHash(number)
|
||||||
|
}
|
||||||
|
|
||||||
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
|
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
|
||||||
// hash, fetching towards the genesis block.
|
// hash, fetching towards the genesis block.
|
||||||
func (self *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
|
func (self *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
|
||||||
|
|
|
||||||
97
rpc/types.go
97
rpc/types.go
|
|
@ -17,6 +17,7 @@
|
||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
@ -24,6 +25,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
mapset "github.com/deckarep/golang-set"
|
mapset "github.com/deckarep/golang-set"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -163,3 +166,97 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
|
||||||
func (bn BlockNumber) Int64() int64 {
|
func (bn BlockNumber) Int64() int64 {
|
||||||
return (int64)(bn)
|
return (int64)(bn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BlockNumberOrHash struct {
|
||||||
|
BlockNumber *BlockNumber `json:"blockNumber,omitempty"`
|
||||||
|
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
||||||
|
RequireCanonical bool `json:"requireCanonical,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
|
||||||
|
type erased BlockNumberOrHash
|
||||||
|
e := erased{}
|
||||||
|
|
||||||
|
err := json.Unmarshal(data, &e)
|
||||||
|
if err == nil {
|
||||||
|
bnh.BlockNumber = e.BlockNumber
|
||||||
|
bnh.BlockHash = e.BlockHash
|
||||||
|
bnh.RequireCanonical = e.RequireCanonical
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var input string
|
||||||
|
err = json.Unmarshal(data, &input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch input {
|
||||||
|
case "earliest":
|
||||||
|
bn := EarliestBlockNumber
|
||||||
|
bnh.BlockNumber = &bn
|
||||||
|
return nil
|
||||||
|
case "latest":
|
||||||
|
bn := LatestBlockNumber
|
||||||
|
bnh.BlockNumber = &bn
|
||||||
|
return nil
|
||||||
|
case "pending":
|
||||||
|
bn := PendingBlockNumber
|
||||||
|
bnh.BlockNumber = &bn
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
if len(input) == 66 {
|
||||||
|
hash := common.Hash{}
|
||||||
|
err := hash.UnmarshalText([]byte(input))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bnh.BlockHash = &hash
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
blckNum, err := hexutil.DecodeUint64(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if blckNum > math.MaxInt64 {
|
||||||
|
return fmt.Errorf("blocknumber too high")
|
||||||
|
}
|
||||||
|
|
||||||
|
bn := BlockNumber(blckNum)
|
||||||
|
bnh.BlockNumber = &bn
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bnh *BlockNumberOrHash) Number() (BlockNumber, bool) {
|
||||||
|
if bnh.BlockNumber != nil {
|
||||||
|
return *bnh.BlockNumber, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return BlockNumber(0), false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bnh *BlockNumberOrHash) Hash() (common.Hash, bool) {
|
||||||
|
if bnh.BlockHash != nil {
|
||||||
|
return *bnh.BlockHash, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return common.Hash{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func BlockNumberOrHashWithNumber(blockNr BlockNumber) BlockNumberOrHash {
|
||||||
|
return BlockNumberOrHash{
|
||||||
|
BlockNumber: &blockNr,
|
||||||
|
BlockHash: nil,
|
||||||
|
RequireCanonical: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BlockNumberOrHashWithHash(hash common.Hash, canonical bool) BlockNumberOrHash {
|
||||||
|
return BlockNumberOrHash{
|
||||||
|
BlockNumber: nil,
|
||||||
|
BlockHash: &hash,
|
||||||
|
RequireCanonical: canonical,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -64,3 +65,63 @@ func TestBlockNumberJSONUnmarshal(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
mustFail bool
|
||||||
|
expected BlockNumberOrHash
|
||||||
|
}{
|
||||||
|
0: {`"0x"`, true, BlockNumberOrHash{}},
|
||||||
|
1: {`"0x0"`, false, BlockNumberOrHashWithNumber(0)},
|
||||||
|
2: {`"0X1"`, false, BlockNumberOrHashWithNumber(1)},
|
||||||
|
3: {`"0x00"`, true, BlockNumberOrHash{}},
|
||||||
|
4: {`"0x01"`, true, BlockNumberOrHash{}},
|
||||||
|
5: {`"0x1"`, false, BlockNumberOrHashWithNumber(1)},
|
||||||
|
6: {`"0x12"`, false, BlockNumberOrHashWithNumber(18)},
|
||||||
|
7: {`"0x7fffffffffffffff"`, false, BlockNumberOrHashWithNumber(math.MaxInt64)},
|
||||||
|
8: {`"0x8000000000000000"`, true, BlockNumberOrHash{}},
|
||||||
|
9: {"0", true, BlockNumberOrHash{}},
|
||||||
|
10: {`"ff"`, true, BlockNumberOrHash{}},
|
||||||
|
11: {`"pending"`, false, BlockNumberOrHashWithNumber(PendingBlockNumber)},
|
||||||
|
12: {`"latest"`, false, BlockNumberOrHashWithNumber(LatestBlockNumber)},
|
||||||
|
13: {`"earliest"`, false, BlockNumberOrHashWithNumber(EarliestBlockNumber)},
|
||||||
|
14: {`someString`, true, BlockNumberOrHash{}},
|
||||||
|
15: {`""`, true, BlockNumberOrHash{}},
|
||||||
|
16: {``, true, BlockNumberOrHash{}},
|
||||||
|
17: {`"0x0000000000000000000000000000000000000000000000000000000000000000"`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)},
|
||||||
|
18: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)},
|
||||||
|
19: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","canonical":false}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)},
|
||||||
|
20: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","canonical":true}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), true)},
|
||||||
|
21: {`{"blockNumber":"0x1"}`, false, BlockNumberOrHashWithNumber(1)},
|
||||||
|
22: {`{"blockNumber":"pending"}`, false, BlockNumberOrHashWithNumber(PendingBlockNumber)},
|
||||||
|
23: {`{"blockNumber":"latest"}`, false, BlockNumberOrHashWithNumber(LatestBlockNumber)},
|
||||||
|
24: {`{"blockNumber":"earliest"}`, false, BlockNumberOrHashWithNumber(EarliestBlockNumber)},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
var bnh BlockNumberOrHash
|
||||||
|
err := json.Unmarshal([]byte(test.input), &bnh)
|
||||||
|
if test.mustFail && err == nil {
|
||||||
|
t.Errorf("Test %d should fail", i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !test.mustFail && err != nil {
|
||||||
|
t.Errorf("Test %d should pass but got err: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, hashOk := bnh.Hash()
|
||||||
|
expectedHash, expectedHashOk := test.expected.Hash()
|
||||||
|
|
||||||
|
num, numOk := bnh.Number()
|
||||||
|
expectedNum, expectedNumOk := test.expected.Number()
|
||||||
|
|
||||||
|
if bnh.RequireCanonical != test.expected.RequireCanonical ||
|
||||||
|
hash != expectedHash || hashOk != expectedHashOk ||
|
||||||
|
num != expectedNum || numOk != expectedNumOk {
|
||||||
|
t.Errorf("Test %d got unexpected value, want %v, got %v", i, test.expected, bnh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue