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
						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,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) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user