forked from cerc-io/plugeth
internal/ethapi: support block number or hash on state-related methods (#19491)
This change adds support for EIP-1898.
This commit is contained in:
parent
62391ddbeb
commit
ad03d9801c
@ -2139,6 +2139,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 {
|
||||||
|
@ -448,6 +448,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 {
|
||||||
|
@ -72,6 +72,23 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
|
|||||||
return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil
|
return b.eth.blockchain.GetHeaderByNumber(uint64(number)), 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, errors.New("header for hash not found")
|
||||||
|
}
|
||||||
|
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||||
|
return nil, errors.New("hash is not currently canonical")
|
||||||
|
}
|
||||||
|
return header, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
@ -93,6 +110,27 @@ func (b *EthAPIBackend) BlockByHash(ctx context.Context, hash common.Hash) (*typ
|
|||||||
return b.eth.blockchain.GetBlockByHash(hash), nil
|
return b.eth.blockchain.GetBlockByHash(hash), 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, errors.New("header for hash not found")
|
||||||
|
}
|
||||||
|
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||||
|
return nil, errors.New("hash is not currently canonical")
|
||||||
|
}
|
||||||
|
block := b.eth.blockchain.GetBlock(hash, header.Number.Uint64())
|
||||||
|
if block == nil {
|
||||||
|
return nil, errors.New("header found, but block body is missing")
|
||||||
|
}
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||||
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
||||||
// Pending state is only known by the miner
|
// Pending state is only known by the miner
|
||||||
if number == rpc.PendingBlockNumber {
|
if number == rpc.PendingBlockNumber {
|
||||||
@ -111,6 +149,27 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B
|
|||||||
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, errors.New("header for hash not found")
|
||||||
|
}
|
||||||
|
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||||
|
return nil, nil, errors.New("hash is not currently canonical")
|
||||||
|
}
|
||||||
|
stateDb, err := b.eth.BlockChain().StateAt(header.Root)
|
||||||
|
return stateDb, header, err
|
||||||
|
}
|
||||||
|
return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||||
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||||
return b.eth.blockchain.GetReceiptsByHash(hash), nil
|
return b.eth.blockchain.GetReceiptsByHash(hash), nil
|
||||||
}
|
}
|
||||||
|
@ -36,20 +36,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errOnlyOnMainChain = errors.New("this operation is only available for blocks on the canonical chain")
|
errBlockInvariant = errors.New("block objects must be instantiated with at least one of num or hash")
|
||||||
errBlockInvariant = errors.New("block objects must be instantiated with at least one of num or hash")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Account represents an Ethereum account at a particular block.
|
// Account represents an Ethereum account at a particular block.
|
||||||
type Account struct {
|
type Account struct {
|
||||||
backend ethapi.Backend
|
backend ethapi.Backend
|
||||||
address common.Address
|
address common.Address
|
||||||
blockNumber rpc.BlockNumber
|
blockNrOrHash rpc.BlockNumberOrHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// getState fetches the StateDB object for an account.
|
// getState fetches the StateDB object for an account.
|
||||||
func (a *Account) getState(ctx context.Context) (*state.StateDB, error) {
|
func (a *Account) getState(ctx context.Context) (*state.StateDB, error) {
|
||||||
state, _, err := a.backend.StateAndHeaderByNumber(ctx, a.blockNumber)
|
state, _, err := a.backend.StateAndHeaderByNumberOrHash(ctx, a.blockNrOrHash)
|
||||||
return state, err
|
return state, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,9 +101,9 @@ func (l *Log) Transaction(ctx context.Context) *Transaction {
|
|||||||
|
|
||||||
func (l *Log) Account(ctx context.Context, args BlockNumberArgs) *Account {
|
func (l *Log) Account(ctx context.Context, args BlockNumberArgs) *Account {
|
||||||
return &Account{
|
return &Account{
|
||||||
backend: l.backend,
|
backend: l.backend,
|
||||||
address: l.log.Address,
|
address: l.log.Address,
|
||||||
blockNumber: args.Number(),
|
blockNrOrHash: args.NumberOrLatest(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,10 +135,10 @@ func (t *Transaction) resolve(ctx context.Context) (*types.Transaction, error) {
|
|||||||
tx, blockHash, _, index := rawdb.ReadTransaction(t.backend.ChainDb(), t.hash)
|
tx, blockHash, _, index := rawdb.ReadTransaction(t.backend.ChainDb(), t.hash)
|
||||||
if tx != nil {
|
if tx != nil {
|
||||||
t.tx = tx
|
t.tx = tx
|
||||||
|
blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false)
|
||||||
t.block = &Block{
|
t.block = &Block{
|
||||||
backend: t.backend,
|
backend: t.backend,
|
||||||
hash: blockHash,
|
numberOrHash: &blockNrOrHash,
|
||||||
canonical: unknown,
|
|
||||||
}
|
}
|
||||||
t.index = index
|
t.index = index
|
||||||
} else {
|
} else {
|
||||||
@ -203,9 +202,9 @@ func (t *Transaction) To(ctx context.Context, args BlockNumberArgs) (*Account, e
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return &Account{
|
return &Account{
|
||||||
backend: t.backend,
|
backend: t.backend,
|
||||||
address: *to,
|
address: *to,
|
||||||
blockNumber: args.Number(),
|
blockNrOrHash: args.NumberOrLatest(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,9 +220,9 @@ func (t *Transaction) From(ctx context.Context, args BlockNumberArgs) (*Account,
|
|||||||
from, _ := types.Sender(signer, tx)
|
from, _ := types.Sender(signer, tx)
|
||||||
|
|
||||||
return &Account{
|
return &Account{
|
||||||
backend: t.backend,
|
backend: t.backend,
|
||||||
address: from,
|
address: from,
|
||||||
blockNumber: args.Number(),
|
blockNrOrHash: args.NumberOrLatest(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,9 +292,9 @@ func (t *Transaction) CreatedContract(ctx context.Context, args BlockNumberArgs)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Account{
|
return &Account{
|
||||||
backend: t.backend,
|
backend: t.backend,
|
||||||
address: receipt.ContractAddress,
|
address: receipt.ContractAddress,
|
||||||
blockNumber: args.Number(),
|
blockNrOrHash: args.NumberOrLatest(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,45 +316,16 @@ func (t *Transaction) Logs(ctx context.Context) (*[]*Log, error) {
|
|||||||
|
|
||||||
type BlockType int
|
type BlockType int
|
||||||
|
|
||||||
const (
|
|
||||||
unknown BlockType = iota
|
|
||||||
isCanonical
|
|
||||||
notCanonical
|
|
||||||
)
|
|
||||||
|
|
||||||
// Block represents an Ethereum block.
|
// Block represents an Ethereum block.
|
||||||
// backend, and either num or hash are mandatory. All other fields are lazily fetched
|
// backend, and numberOrHash are mandatory. All other fields are lazily fetched
|
||||||
// when required.
|
// when required.
|
||||||
type Block struct {
|
type Block struct {
|
||||||
backend ethapi.Backend
|
backend ethapi.Backend
|
||||||
num *rpc.BlockNumber
|
numberOrHash *rpc.BlockNumberOrHash
|
||||||
hash common.Hash
|
hash common.Hash
|
||||||
header *types.Header
|
header *types.Header
|
||||||
block *types.Block
|
block *types.Block
|
||||||
receipts []*types.Receipt
|
receipts []*types.Receipt
|
||||||
canonical BlockType // Indicates if this block is on the main chain or not.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Block) onMainChain(ctx context.Context) error {
|
|
||||||
if b.canonical == unknown {
|
|
||||||
header, err := b.resolveHeader(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
canonHeader, err := b.backend.HeaderByNumber(ctx, rpc.BlockNumber(header.Number.Uint64()))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if header.Hash() == canonHeader.Hash() {
|
|
||||||
b.canonical = isCanonical
|
|
||||||
} else {
|
|
||||||
b.canonical = notCanonical
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if b.canonical != isCanonical {
|
|
||||||
return errOnlyOnMainChain
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve returns the internal Block object representing this block, fetching
|
// resolve returns the internal Block object representing this block, fetching
|
||||||
@ -364,14 +334,17 @@ func (b *Block) resolve(ctx context.Context) (*types.Block, error) {
|
|||||||
if b.block != nil {
|
if b.block != nil {
|
||||||
return b.block, nil
|
return b.block, nil
|
||||||
}
|
}
|
||||||
var err error
|
if b.numberOrHash == nil {
|
||||||
if b.hash != (common.Hash{}) {
|
latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
||||||
b.block, err = b.backend.BlockByHash(ctx, b.hash)
|
b.numberOrHash = &latest
|
||||||
} else {
|
|
||||||
b.block, err = b.backend.BlockByNumber(ctx, *b.num)
|
|
||||||
}
|
}
|
||||||
|
var err error
|
||||||
|
b.block, err = b.backend.BlockByNumberOrHash(ctx, *b.numberOrHash)
|
||||||
if b.block != nil && b.header == nil {
|
if b.block != nil && b.header == nil {
|
||||||
b.header = b.block.Header()
|
b.header = b.block.Header()
|
||||||
|
if hash, ok := b.numberOrHash.Hash(); ok {
|
||||||
|
b.hash = hash
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return b.block, err
|
return b.block, err
|
||||||
}
|
}
|
||||||
@ -380,7 +353,7 @@ func (b *Block) resolve(ctx context.Context) (*types.Block, error) {
|
|||||||
// if necessary. Call this function instead of `resolve` unless you need the
|
// if necessary. Call this function instead of `resolve` unless you need the
|
||||||
// additional data (transactions and uncles).
|
// additional data (transactions and uncles).
|
||||||
func (b *Block) resolveHeader(ctx context.Context) (*types.Header, error) {
|
func (b *Block) resolveHeader(ctx context.Context) (*types.Header, error) {
|
||||||
if b.num == nil && b.hash == (common.Hash{}) {
|
if b.numberOrHash == nil && b.hash == (common.Hash{}) {
|
||||||
return nil, errBlockInvariant
|
return nil, errBlockInvariant
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
@ -388,7 +361,7 @@ func (b *Block) resolveHeader(ctx context.Context) (*types.Header, error) {
|
|||||||
if b.hash != (common.Hash{}) {
|
if b.hash != (common.Hash{}) {
|
||||||
b.header, err = b.backend.HeaderByHash(ctx, b.hash)
|
b.header, err = b.backend.HeaderByHash(ctx, b.hash)
|
||||||
} else {
|
} else {
|
||||||
b.header, err = b.backend.HeaderByNumber(ctx, *b.num)
|
b.header, err = b.backend.HeaderByNumberOrHash(ctx, *b.numberOrHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b.header, err
|
return b.header, err
|
||||||
@ -416,15 +389,12 @@ func (b *Block) resolveReceipts(ctx context.Context) ([]*types.Receipt, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Block) Number(ctx context.Context) (hexutil.Uint64, error) {
|
func (b *Block) Number(ctx context.Context) (hexutil.Uint64, error) {
|
||||||
if b.num == nil || *b.num == rpc.LatestBlockNumber {
|
header, err := b.resolveHeader(ctx)
|
||||||
header, err := b.resolveHeader(ctx)
|
if err != nil {
|
||||||
if err != nil {
|
return 0, err
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
num := rpc.BlockNumber(header.Number.Uint64())
|
|
||||||
b.num = &num
|
|
||||||
}
|
}
|
||||||
return hexutil.Uint64(*b.num), nil
|
|
||||||
|
return hexutil.Uint64(header.Number.Uint64()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Block) Hash(ctx context.Context) (common.Hash, error) {
|
func (b *Block) Hash(ctx context.Context) (common.Hash, error) {
|
||||||
@ -456,26 +426,17 @@ func (b *Block) GasUsed(ctx context.Context) (hexutil.Uint64, error) {
|
|||||||
|
|
||||||
func (b *Block) Parent(ctx context.Context) (*Block, error) {
|
func (b *Block) Parent(ctx context.Context) (*Block, error) {
|
||||||
// If the block header hasn't been fetched, and we'll need it, fetch it.
|
// If the block header hasn't been fetched, and we'll need it, fetch it.
|
||||||
if b.num == nil && b.hash != (common.Hash{}) && b.header == nil {
|
if b.numberOrHash == nil && b.header == nil {
|
||||||
if _, err := b.resolveHeader(ctx); err != nil {
|
if _, err := b.resolveHeader(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if b.header != nil && b.header.Number.Uint64() > 0 {
|
if b.header != nil && b.header.Number.Uint64() > 0 {
|
||||||
num := rpc.BlockNumber(b.header.Number.Uint64() - 1)
|
num := rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(b.header.Number.Uint64() - 1))
|
||||||
return &Block{
|
return &Block{
|
||||||
backend: b.backend,
|
backend: b.backend,
|
||||||
num: &num,
|
numberOrHash: &num,
|
||||||
hash: b.header.ParentHash,
|
hash: b.header.ParentHash,
|
||||||
canonical: unknown,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
if b.num != nil && *b.num != 0 {
|
|
||||||
num := *b.num - 1
|
|
||||||
return &Block{
|
|
||||||
backend: b.backend,
|
|
||||||
num: &num,
|
|
||||||
canonical: isCanonical,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -561,13 +522,11 @@ func (b *Block) Ommers(ctx context.Context) (*[]*Block, error) {
|
|||||||
}
|
}
|
||||||
ret := make([]*Block, 0, len(block.Uncles()))
|
ret := make([]*Block, 0, len(block.Uncles()))
|
||||||
for _, uncle := range block.Uncles() {
|
for _, uncle := range block.Uncles() {
|
||||||
blockNumber := rpc.BlockNumber(uncle.Number.Uint64())
|
blockNumberOrHash := rpc.BlockNumberOrHashWithHash(uncle.Hash(), false)
|
||||||
ret = append(ret, &Block{
|
ret = append(ret, &Block{
|
||||||
backend: b.backend,
|
backend: b.backend,
|
||||||
num: &blockNumber,
|
numberOrHash: &blockNumberOrHash,
|
||||||
hash: uncle.Hash(),
|
header: uncle,
|
||||||
header: uncle,
|
|
||||||
canonical: notCanonical,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return &ret, nil
|
return &ret, nil
|
||||||
@ -603,16 +562,26 @@ func (b *Block) TotalDifficulty(ctx context.Context) (hexutil.Big, error) {
|
|||||||
|
|
||||||
// BlockNumberArgs encapsulates arguments to accessors that specify a block number.
|
// BlockNumberArgs encapsulates arguments to accessors that specify a block number.
|
||||||
type BlockNumberArgs struct {
|
type BlockNumberArgs struct {
|
||||||
|
// TODO: Ideally we could use input unions to allow the query to specify the
|
||||||
|
// block parameter by hash, block number, or tag but input unions aren't part of the
|
||||||
|
// standard GraphQL schema SDL yet, see: https://github.com/graphql/graphql-spec/issues/488
|
||||||
Block *hexutil.Uint64
|
Block *hexutil.Uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number returns the provided block number, or rpc.LatestBlockNumber if none
|
// NumberOr returns the provided block number argument, or the "current" block number or hash if none
|
||||||
// was provided.
|
// was provided.
|
||||||
func (a BlockNumberArgs) Number() rpc.BlockNumber {
|
func (a BlockNumberArgs) NumberOr(current rpc.BlockNumberOrHash) rpc.BlockNumberOrHash {
|
||||||
if a.Block != nil {
|
if a.Block != nil {
|
||||||
return rpc.BlockNumber(*a.Block)
|
blockNr := rpc.BlockNumber(*a.Block)
|
||||||
|
return rpc.BlockNumberOrHashWithNumber(blockNr)
|
||||||
}
|
}
|
||||||
return rpc.LatestBlockNumber
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumberOrLatest returns the provided block number argument, or the "latest" block number if none
|
||||||
|
// was provided.
|
||||||
|
func (a BlockNumberArgs) NumberOrLatest() rpc.BlockNumberOrHash {
|
||||||
|
return a.NumberOr(rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Block) Miner(ctx context.Context, args BlockNumberArgs) (*Account, error) {
|
func (b *Block) Miner(ctx context.Context, args BlockNumberArgs) (*Account, error) {
|
||||||
@ -621,9 +590,9 @@ func (b *Block) Miner(ctx context.Context, args BlockNumberArgs) (*Account, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Account{
|
return &Account{
|
||||||
backend: b.backend,
|
backend: b.backend,
|
||||||
address: header.Coinbase,
|
address: header.Coinbase,
|
||||||
blockNumber: args.Number(),
|
blockNrOrHash: args.NumberOrLatest(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -683,13 +652,11 @@ func (b *Block) OmmerAt(ctx context.Context, args struct{ Index int32 }) (*Block
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
uncle := uncles[args.Index]
|
uncle := uncles[args.Index]
|
||||||
blockNumber := rpc.BlockNumber(uncle.Number.Uint64())
|
blockNumberOrHash := rpc.BlockNumberOrHashWithHash(uncle.Hash(), false)
|
||||||
return &Block{
|
return &Block{
|
||||||
backend: b.backend,
|
backend: b.backend,
|
||||||
num: &blockNumber,
|
numberOrHash: &blockNumberOrHash,
|
||||||
hash: uncle.Hash(),
|
header: uncle,
|
||||||
header: uncle,
|
|
||||||
canonical: notCanonical,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -757,20 +724,16 @@ func (b *Block) Logs(ctx context.Context, args struct{ Filter BlockFilterCriteri
|
|||||||
func (b *Block) Account(ctx context.Context, args struct {
|
func (b *Block) Account(ctx context.Context, args struct {
|
||||||
Address common.Address
|
Address common.Address
|
||||||
}) (*Account, error) {
|
}) (*Account, error) {
|
||||||
err := b.onMainChain(ctx)
|
if b.numberOrHash == nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if b.num == nil {
|
|
||||||
_, err := b.resolveHeader(ctx)
|
_, err := b.resolveHeader(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &Account{
|
return &Account{
|
||||||
backend: b.backend,
|
backend: b.backend,
|
||||||
address: args.Address,
|
address: args.Address,
|
||||||
blockNumber: *b.num,
|
blockNrOrHash: *b.numberOrHash,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -807,17 +770,13 @@ func (c *CallResult) Status() hexutil.Uint64 {
|
|||||||
func (b *Block) Call(ctx context.Context, args struct {
|
func (b *Block) Call(ctx context.Context, args struct {
|
||||||
Data ethapi.CallArgs
|
Data ethapi.CallArgs
|
||||||
}) (*CallResult, error) {
|
}) (*CallResult, error) {
|
||||||
err := b.onMainChain(ctx)
|
if b.numberOrHash == nil {
|
||||||
if err != nil {
|
_, err := b.resolve(ctx)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if b.num == nil {
|
|
||||||
_, err := b.resolveHeader(ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result, gas, failed, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.num, nil, vm.Config{}, 5*time.Second, b.backend.RPCGasCap())
|
result, gas, failed, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, vm.Config{}, 5*time.Second, b.backend.RPCGasCap())
|
||||||
status := hexutil.Uint64(1)
|
status := hexutil.Uint64(1)
|
||||||
if failed {
|
if failed {
|
||||||
status = 0
|
status = 0
|
||||||
@ -832,17 +791,13 @@ func (b *Block) Call(ctx context.Context, args struct {
|
|||||||
func (b *Block) EstimateGas(ctx context.Context, args struct {
|
func (b *Block) EstimateGas(ctx context.Context, args struct {
|
||||||
Data ethapi.CallArgs
|
Data ethapi.CallArgs
|
||||||
}) (hexutil.Uint64, error) {
|
}) (hexutil.Uint64, error) {
|
||||||
err := b.onMainChain(ctx)
|
if b.numberOrHash == nil {
|
||||||
if err != nil {
|
|
||||||
return hexutil.Uint64(0), err
|
|
||||||
}
|
|
||||||
if b.num == nil {
|
|
||||||
_, err := b.resolveHeader(ctx)
|
_, err := b.resolveHeader(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return hexutil.Uint64(0), err
|
return hexutil.Uint64(0), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gas, err := ethapi.DoEstimateGas(ctx, b.backend, args.Data, *b.num, b.backend.RPCGasCap())
|
gas, err := ethapi.DoEstimateGas(ctx, b.backend, args.Data, *b.numberOrHash, b.backend.RPCGasCap())
|
||||||
return gas, err
|
return gas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -875,17 +830,19 @@ func (p *Pending) Transactions(ctx context.Context) (*[]*Transaction, error) {
|
|||||||
func (p *Pending) Account(ctx context.Context, args struct {
|
func (p *Pending) Account(ctx context.Context, args struct {
|
||||||
Address common.Address
|
Address common.Address
|
||||||
}) *Account {
|
}) *Account {
|
||||||
|
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||||
return &Account{
|
return &Account{
|
||||||
backend: p.backend,
|
backend: p.backend,
|
||||||
address: args.Address,
|
address: args.Address,
|
||||||
blockNumber: rpc.PendingBlockNumber,
|
blockNrOrHash: pendingBlockNr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pending) Call(ctx context.Context, args struct {
|
func (p *Pending) Call(ctx context.Context, args struct {
|
||||||
Data ethapi.CallArgs
|
Data ethapi.CallArgs
|
||||||
}) (*CallResult, error) {
|
}) (*CallResult, error) {
|
||||||
result, gas, failed, err := ethapi.DoCall(ctx, p.backend, args.Data, rpc.PendingBlockNumber, nil, vm.Config{}, 5*time.Second, p.backend.RPCGasCap())
|
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||||
|
result, gas, failed, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, vm.Config{}, 5*time.Second, p.backend.RPCGasCap())
|
||||||
status := hexutil.Uint64(1)
|
status := hexutil.Uint64(1)
|
||||||
if failed {
|
if failed {
|
||||||
status = 0
|
status = 0
|
||||||
@ -900,7 +857,8 @@ func (p *Pending) Call(ctx context.Context, args struct {
|
|||||||
func (p *Pending) EstimateGas(ctx context.Context, args struct {
|
func (p *Pending) EstimateGas(ctx context.Context, args struct {
|
||||||
Data ethapi.CallArgs
|
Data ethapi.CallArgs
|
||||||
}) (hexutil.Uint64, error) {
|
}) (hexutil.Uint64, error) {
|
||||||
return ethapi.DoEstimateGas(ctx, p.backend, args.Data, rpc.PendingBlockNumber, p.backend.RPCGasCap())
|
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||||
|
return ethapi.DoEstimateGas(ctx, p.backend, args.Data, pendingBlockNr, p.backend.RPCGasCap())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolver is the top-level object in the GraphQL hierarchy.
|
// Resolver is the top-level object in the GraphQL hierarchy.
|
||||||
@ -914,24 +872,23 @@ func (r *Resolver) Block(ctx context.Context, args struct {
|
|||||||
}) (*Block, error) {
|
}) (*Block, error) {
|
||||||
var block *Block
|
var block *Block
|
||||||
if args.Number != nil {
|
if args.Number != nil {
|
||||||
num := rpc.BlockNumber(uint64(*args.Number))
|
number := rpc.BlockNumber(uint64(*args.Number))
|
||||||
|
numberOrHash := rpc.BlockNumberOrHashWithNumber(number)
|
||||||
block = &Block{
|
block = &Block{
|
||||||
backend: r.backend,
|
backend: r.backend,
|
||||||
num: &num,
|
numberOrHash: &numberOrHash,
|
||||||
canonical: isCanonical,
|
|
||||||
}
|
}
|
||||||
} else if args.Hash != nil {
|
} else if args.Hash != nil {
|
||||||
|
numberOrHash := rpc.BlockNumberOrHashWithHash(*args.Hash, false)
|
||||||
block = &Block{
|
block = &Block{
|
||||||
backend: r.backend,
|
backend: r.backend,
|
||||||
hash: *args.Hash,
|
numberOrHash: &numberOrHash,
|
||||||
canonical: unknown,
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
num := rpc.LatestBlockNumber
|
numberOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
|
||||||
block = &Block{
|
block = &Block{
|
||||||
backend: r.backend,
|
backend: r.backend,
|
||||||
num: &num,
|
numberOrHash: &numberOrHash,
|
||||||
canonical: isCanonical,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Resolve the header, return nil if it doesn't exist.
|
// Resolve the header, return nil if it doesn't exist.
|
||||||
@ -963,11 +920,10 @@ func (r *Resolver) Blocks(ctx context.Context, args struct {
|
|||||||
}
|
}
|
||||||
ret := make([]*Block, 0, to-from+1)
|
ret := make([]*Block, 0, to-from+1)
|
||||||
for i := from; i <= to; i++ {
|
for i := from; i <= to; i++ {
|
||||||
num := i
|
numberOrHash := rpc.BlockNumberOrHashWithNumber(i)
|
||||||
ret = append(ret, &Block{
|
ret = append(ret, &Block{
|
||||||
backend: r.backend,
|
backend: r.backend,
|
||||||
num: &num,
|
numberOrHash: &numberOrHash,
|
||||||
canonical: isCanonical,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
@ -530,8 +530,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
|
||||||
}
|
}
|
||||||
@ -555,8 +555,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
|
||||||
}
|
}
|
||||||
@ -712,8 +712,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
|
||||||
}
|
}
|
||||||
@ -724,8 +724,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
|
||||||
}
|
}
|
||||||
@ -757,10 +757,10 @@ type account struct {
|
|||||||
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
|
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, overrides map[common.Address]account, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) {
|
func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides map[common.Address]account, vmCfg vm.Config, 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 := b.StateAndHeaderByNumber(ctx, blockNr)
|
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, 0, false, err
|
return nil, 0, false, err
|
||||||
}
|
}
|
||||||
@ -874,16 +874,16 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumb
|
|||||||
//
|
//
|
||||||
// Note, this function doesn't make and changes in the state/blockchain and is
|
// Note, this function doesn't make and changes in the state/blockchain and is
|
||||||
// useful to execute and retrieve values.
|
// useful to execute and retrieve values.
|
||||||
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, overrides *map[common.Address]account) (hexutil.Bytes, error) {
|
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]account) (hexutil.Bytes, error) {
|
||||||
var accounts map[common.Address]account
|
var accounts map[common.Address]account
|
||||||
if overrides != nil {
|
if overrides != nil {
|
||||||
accounts = *overrides
|
accounts = *overrides
|
||||||
}
|
}
|
||||||
result, _, _, err := DoCall(ctx, s.b, args, blockNr, accounts, vm.Config{}, 5*time.Second, s.b.RPCGasCap())
|
result, _, _, err := DoCall(ctx, s.b, args, blockNrOrHash, accounts, vm.Config{}, 5*time.Second, s.b.RPCGasCap())
|
||||||
return (hexutil.Bytes)(result), err
|
return (hexutil.Bytes)(result), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, gasCap *big.Int) (hexutil.Uint64, error) {
|
func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, gasCap *big.Int) (hexutil.Uint64, error) {
|
||||||
// Binary search the gas requirement, as it may be higher than the amount used
|
// Binary search the gas requirement, as it may be higher than the amount used
|
||||||
var (
|
var (
|
||||||
lo uint64 = params.TxGas - 1
|
lo uint64 = params.TxGas - 1
|
||||||
@ -894,7 +894,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.Bl
|
|||||||
hi = uint64(*args.Gas)
|
hi = uint64(*args.Gas)
|
||||||
} else {
|
} else {
|
||||||
// Retrieve the block to act as the gas ceiling
|
// Retrieve the block to act as the gas ceiling
|
||||||
block, err := b.BlockByNumber(ctx, blockNr)
|
block, err := b.BlockByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -910,7 +910,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.Bl
|
|||||||
executable := func(gas uint64) bool {
|
executable := func(gas uint64) bool {
|
||||||
args.Gas = (*hexutil.Uint64)(&gas)
|
args.Gas = (*hexutil.Uint64)(&gas)
|
||||||
|
|
||||||
_, _, failed, err := DoCall(ctx, b, args, rpc.PendingBlockNumber, nil, vm.Config{}, 0, gasCap)
|
_, _, failed, err := DoCall(ctx, b, args, blockNrOrHash, nil, vm.Config{}, 0, gasCap)
|
||||||
if err != nil || failed {
|
if err != nil || failed {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -937,7 +937,8 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.Bl
|
|||||||
// EstimateGas returns an estimate of the amount of gas needed to execute the
|
// EstimateGas returns an estimate of the amount of gas needed to execute the
|
||||||
// given transaction against the current pending block.
|
// given transaction against the current pending block.
|
||||||
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) {
|
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) {
|
||||||
return DoEstimateGas(ctx, s.b, args, rpc.PendingBlockNumber, s.b.RPCGasCap())
|
blockNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||||
|
return DoEstimateGas(ctx, s.b, args, blockNrOrHash, s.b.RPCGasCap())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecutionResult groups all structured logs emitted by the EVM
|
// ExecutionResult groups all structured logs emitted by the EVM
|
||||||
@ -1224,9 +1225,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
|
||||||
@ -1234,7 +1235,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
|
||||||
}
|
}
|
||||||
@ -1405,7 +1406,8 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
|
|||||||
Value: args.Value,
|
Value: args.Value,
|
||||||
Data: input,
|
Data: input,
|
||||||
}
|
}
|
||||||
estimated, err := DoEstimateGas(ctx, b, callArgs, rpc.PendingBlockNumber, b.RPCGasCap())
|
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||||
|
estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, b.RPCGasCap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -52,9 +52,12 @@ type Backend interface {
|
|||||||
SetHead(number uint64)
|
SetHead(number uint64)
|
||||||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||||
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
|
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
|
||||||
|
HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error)
|
||||||
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
|
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
|
||||||
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
|
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
|
||||||
|
BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error)
|
||||||
StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error)
|
StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error)
|
||||||
|
StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error)
|
||||||
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
|
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
|
||||||
GetTd(hash common.Hash) *big.Int
|
GetTd(hash common.Hash) *big.Int
|
||||||
GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error)
|
GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error)
|
||||||
|
@ -65,6 +65,26 @@ func (b *LesApiBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
|
|||||||
return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(number))
|
return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, errors.New("header for hash not found")
|
||||||
|
}
|
||||||
|
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||||
|
return nil, errors.New("hash is not currently canonical")
|
||||||
|
}
|
||||||
|
return header, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
@ -81,6 +101,26 @@ func (b *LesApiBackend) BlockByHash(ctx context.Context, hash common.Hash) (*typ
|
|||||||
return b.eth.blockchain.GetBlockByHash(ctx, hash)
|
return b.eth.blockchain.GetBlockByHash(ctx, 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.BlockByHash(ctx, hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if block == nil {
|
||||||
|
return nil, errors.New("header found, but block body is missing")
|
||||||
|
}
|
||||||
|
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(block.NumberU64()) != hash {
|
||||||
|
return nil, errors.New("hash is not currently canonical")
|
||||||
|
}
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||||
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
||||||
header, err := b.HeaderByNumber(ctx, number)
|
header, err := b.HeaderByNumber(ctx, number)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -92,6 +132,23 @@ func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B
|
|||||||
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, errors.New("header for hash not found")
|
||||||
|
}
|
||||||
|
if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
|
||||||
|
return nil, nil, errors.New("hash is not currently canonical")
|
||||||
|
}
|
||||||
|
return light.NewState(ctx, header, b.eth.odr), header, nil
|
||||||
|
}
|
||||||
|
return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||||
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
func (b *LesApiBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||||
if number := rawdb.ReadHeaderNumber(b.eth.chainDb, hash); number != nil {
|
if number := rawdb.ReadHeaderNumber(b.eth.chainDb, hash); number != nil {
|
||||||
return light.GetBlockReceipts(ctx, b.eth.odr, hash, *number)
|
return light.GetBlockReceipts(ctx, b.eth.odr, hash, *number)
|
||||||
|
@ -426,6 +426,11 @@ func (lc *LightChain) HasHeader(hash common.Hash, number uint64) bool {
|
|||||||
return lc.hc.HasHeader(hash, number)
|
return lc.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 (lc *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
|
func (lc *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
|
||||||
|
93
rpc/types.go
93
rpc/types.go
@ -18,10 +18,12 @@ package rpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -105,3 +107,94 @@ 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 {
|
||||||
|
if e.BlockNumber != nil && e.BlockHash != nil {
|
||||||
|
return fmt.Errorf("cannot specify both BlockHash and BlockNumber, choose one or the other")
|
||||||
|
}
|
||||||
|
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,60 @@ 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","requireCanonical":false}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)},
|
||||||
|
20: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","requireCanonical":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)},
|
||||||
|
25: {`{"blockNumber":"0x1", "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`, true, BlockNumberOrHash{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
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 New Issue
Block a user