internal/ethapi: support block number or hash on state-related methods (#19491)
This change adds support for EIP-1898.
This commit is contained in:
		
				
					committed by
					
						
						Felix Lange
					
				
			
			
				
	
			
			
			
						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,7 +36,6 @@ 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")
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -44,12 +43,12 @@ var (
 | 
				
			|||||||
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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -104,7 +103,7 @@ 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 {
 | 
				
			||||||
@@ -205,7 +204,7 @@ func (t *Transaction) To(ctx context.Context, args BlockNumberArgs) (*Account, e
 | 
				
			|||||||
	return &Account{
 | 
						return &Account{
 | 
				
			||||||
		backend:       t.backend,
 | 
							backend:       t.backend,
 | 
				
			||||||
		address:       *to,
 | 
							address:       *to,
 | 
				
			||||||
		blockNumber: args.Number(),
 | 
							blockNrOrHash: args.NumberOrLatest(),
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -223,7 +222,7 @@ func (t *Transaction) From(ctx context.Context, args BlockNumberArgs) (*Account,
 | 
				
			|||||||
	return &Account{
 | 
						return &Account{
 | 
				
			||||||
		backend:       t.backend,
 | 
							backend:       t.backend,
 | 
				
			||||||
		address:       from,
 | 
							address:       from,
 | 
				
			||||||
		blockNumber: args.Number(),
 | 
							blockNrOrHash: args.NumberOrLatest(),
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -295,7 +294,7 @@ func (t *Transaction) CreatedContract(ctx context.Context, args BlockNumberArgs)
 | 
				
			|||||||
	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(header.Number.Uint64()), nil
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return hexutil.Uint64(*b.num), 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) {
 | 
				
			||||||
@@ -623,7 +592,7 @@ func (b *Block) Miner(ctx context.Context, args BlockNumberArgs) (*Account, erro
 | 
				
			|||||||
	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,11 +724,7 @@ 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
 | 
				
			||||||
@@ -770,7 +733,7 @@ func (b *Block) Account(ctx context.Context, args struct {
 | 
				
			|||||||
	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)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user