mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
eth, core, internal, graphql: return error only if indexing is not finished
This commit is contained in:
parent
0f3c1d0d48
commit
64196f9657
7 changed files with 72 additions and 53 deletions
|
|
@ -186,7 +186,7 @@ func DefaultCacheConfigWithScheme(scheme string) *CacheConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
// txLookup is wrapper over transaction lookup along with the corresponding
|
// txLookup is wrapper over transaction lookup along with the corresponding
|
||||||
// transaction itself.
|
// transaction object.
|
||||||
type txLookup struct {
|
type txLookup struct {
|
||||||
lookup *rawdb.LegacyTxLookupEntry
|
lookup *rawdb.LegacyTxLookupEntry
|
||||||
transaction *types.Transaction
|
transaction *types.Transaction
|
||||||
|
|
@ -194,18 +194,17 @@ type txLookup struct {
|
||||||
|
|
||||||
// txIndexProgress is the struct describing the progress for transaction indexing.
|
// txIndexProgress is the struct describing the progress for transaction indexing.
|
||||||
type txIndexProgress struct {
|
type txIndexProgress struct {
|
||||||
tail uint64 // the oldest block indexed for transactions
|
head uint64 // the current chain head
|
||||||
head uint64 // the latest block indexed for transactions
|
indexed uint64 // the number of blocks have been indexed
|
||||||
limit uint64 // the number of blocks required for transaction indexing(0 means the whole chain)
|
limit uint64 // the number of blocks required for transaction indexing(0 means the whole chain)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error implements Error returning the progress in string format.
|
// done returns an indicator if the transaction indexing is finished.
|
||||||
func (prog txIndexProgress) Error() string {
|
func (prog txIndexProgress) done() bool {
|
||||||
limit := "entire chain"
|
if prog.limit == 0 {
|
||||||
if prog.limit != 0 {
|
return prog.indexed == (prog.head + 1) // genesis included
|
||||||
limit = fmt.Sprintf("last %d blocks", prog.limit)
|
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("index-tail: %d, index-head: %d, limit: %s", prog.tail, prog.head, limit)
|
return prog.indexed >= prog.limit
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockChain represents the canonical chain given a database with a genesis
|
// BlockChain represents the canonical chain given a database with a genesis
|
||||||
|
|
@ -2453,17 +2452,16 @@ func (bc *BlockChain) indexBlocks(tail *uint64, head uint64, done chan struct{})
|
||||||
|
|
||||||
// reportTxIndexProgress returns the tx indexing progress.
|
// reportTxIndexProgress returns the tx indexing progress.
|
||||||
func (bc *BlockChain) reportTxIndexProgress(head uint64) txIndexProgress {
|
func (bc *BlockChain) reportTxIndexProgress(head uint64) txIndexProgress {
|
||||||
tail := rawdb.ReadTxIndexTail(bc.db)
|
var (
|
||||||
if tail == nil {
|
indexed uint64
|
||||||
return txIndexProgress{
|
tail = rawdb.ReadTxIndexTail(bc.db)
|
||||||
tail: 0, // not indexed yet
|
)
|
||||||
head: 0, // not indexed yet
|
if tail != nil {
|
||||||
limit: bc.txLookupLimit,
|
indexed = head - *tail + 1
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return txIndexProgress{
|
return txIndexProgress{
|
||||||
tail: *tail,
|
|
||||||
head: head,
|
head: head,
|
||||||
|
indexed: indexed,
|
||||||
limit: bc.txLookupLimit,
|
limit: bc.txLookupLimit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2510,6 +2508,7 @@ func (bc *BlockChain) maintainTxIndex() {
|
||||||
// indexer is never triggered.
|
// indexer is never triggered.
|
||||||
if head := rawdb.ReadHeadBlock(bc.db); head != nil && head.Number().Uint64() != 0 {
|
if head := rawdb.ReadHeadBlock(bc.db); head != nil && head.Number().Uint64() != 0 {
|
||||||
done = make(chan struct{})
|
done = make(chan struct{})
|
||||||
|
lastHead = head.Number().Uint64()
|
||||||
go bc.indexBlocks(rawdb.ReadTxIndexTail(bc.db), head.NumberU64(), done)
|
go bc.indexBlocks(rawdb.ReadTxIndexTail(bc.db), head.NumberU64(), done)
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -254,9 +255,16 @@ func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, max
|
||||||
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
|
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionLookup retrieves the lookup along with the transaction itself
|
// GetTransactionLookup retrieves the lookup along with the transaction
|
||||||
// associate with the given transaction hash. A non-nil error will be returned
|
// itself associate with the given transaction hash.
|
||||||
// if the transaction is not found.
|
//
|
||||||
|
// An error will be returned if the transaction is not found, and background
|
||||||
|
// indexing for transactions is still in progress. The transaction might be
|
||||||
|
// reachable shortly once it's indexed.
|
||||||
|
//
|
||||||
|
// A null will be returned in the transaction is not found and background
|
||||||
|
// transaction indexing is already finished. The transaction is not existent
|
||||||
|
// from the node's perspective.
|
||||||
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) {
|
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) {
|
||||||
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
||||||
if item, exist := bc.txLookupCache.Get(hash); exist {
|
if item, exist := bc.txLookupCache.Get(hash); exist {
|
||||||
|
|
@ -264,13 +272,18 @@ func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLoo
|
||||||
}
|
}
|
||||||
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
// The transaction can either be non-existent, or just not indexed
|
|
||||||
// yet. Return the tx indexing progress as well for better UX.
|
|
||||||
progress, err := bc.askTxIndexProgress()
|
progress, err := bc.askTxIndexProgress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
return nil, nil, progress
|
// The transaction indexing is not finished yet, returning an
|
||||||
|
// error to explicitly indicate it.
|
||||||
|
if !progress.done() {
|
||||||
|
return nil, nil, errors.New("transaction is still indexing")
|
||||||
|
}
|
||||||
|
// The transaction is already indexed, the transaction is either
|
||||||
|
// not existent or not in the range of index, returning null.
|
||||||
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
lookup := &rawdb.LegacyTxLookupEntry{
|
lookup := &rawdb.LegacyTxLookupEntry{
|
||||||
BlockHash: blockHash,
|
BlockHash: blockHash,
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ package eth
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -310,14 +309,24 @@ func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransaction retrieves the lookup along with the transaction itself associate
|
// GetTransaction retrieves the lookup along with the transaction itself associate
|
||||||
// with the given transaction hash. A non-nil error will be returned if the
|
// with the given transaction hash.
|
||||||
// transaction is not found.
|
//
|
||||||
func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
|
// An error will be returned if the transaction is not found, and background
|
||||||
|
// indexing for transactions is still in progress. The error is used to indicate the
|
||||||
|
// scenario explicitly that the transaction might be reachable shortly.
|
||||||
|
//
|
||||||
|
// A null will be returned in the transaction is not found and background transaction
|
||||||
|
// indexing is already finished. The transaction is not existent from the perspective
|
||||||
|
// of node.
|
||||||
|
func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
|
||||||
lookup, tx, err := b.eth.blockchain.GetTransactionLookup(txHash)
|
lookup, tx, err := b.eth.blockchain.GetTransactionLookup(txHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, common.Hash{}, 0, 0, fmt.Errorf("tx is not existent or not indexed, %w", err)
|
return false, nil, common.Hash{}, 0, 0, err
|
||||||
}
|
}
|
||||||
return tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index, nil
|
if lookup == nil || tx == nil {
|
||||||
|
return false, nil, common.Hash{}, 0, 0, nil
|
||||||
|
}
|
||||||
|
return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ type Backend interface {
|
||||||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||||
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
|
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
|
||||||
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
|
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
|
||||||
GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
|
GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error)
|
||||||
RPCGasCap() uint64
|
RPCGasCap() uint64
|
||||||
ChainConfig() *params.ChainConfig
|
ChainConfig() *params.ChainConfig
|
||||||
Engine() consensus.Engine
|
Engine() consensus.Engine
|
||||||
|
|
@ -826,12 +826,12 @@ func containsTx(block *types.Block, hash common.Hash) bool {
|
||||||
// TraceTransaction returns the structured logs created during the execution of EVM
|
// TraceTransaction returns the structured logs created during the execution of EVM
|
||||||
// and returns them as a JSON object.
|
// and returns them as a JSON object.
|
||||||
func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
|
func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
|
||||||
tx, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
|
found, _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Only mined txes are supported
|
// Only mined txes are supported
|
||||||
if tx == nil {
|
if !found {
|
||||||
return nil, errTxNotFound
|
return nil, errTxNotFound
|
||||||
}
|
}
|
||||||
// It shouldn't happen in practice.
|
// It shouldn't happen in practice.
|
||||||
|
|
|
||||||
|
|
@ -230,8 +230,8 @@ func (t *Transaction) resolve(ctx context.Context) (*types.Transaction, *Block)
|
||||||
return t.tx, t.block
|
return t.tx, t.block
|
||||||
}
|
}
|
||||||
// Try to return an already finalized transaction
|
// Try to return an already finalized transaction
|
||||||
tx, blockHash, _, index, err := t.r.backend.GetTransaction(ctx, t.hash)
|
found, tx, blockHash, _, index, _ := t.r.backend.GetTransaction(ctx, t.hash)
|
||||||
if err == nil && tx != nil {
|
if found {
|
||||||
t.tx = tx
|
t.tx = tx
|
||||||
blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false)
|
blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false)
|
||||||
t.block = &Block{
|
t.block = &Block{
|
||||||
|
|
|
||||||
|
|
@ -1652,8 +1652,8 @@ func (s *TransactionAPI) GetTransactionCount(ctx context.Context, address common
|
||||||
// GetTransactionByHash returns the transaction for the given hash
|
// GetTransactionByHash returns the transaction for the given hash
|
||||||
func (s *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
func (s *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
||||||
// Try to return an already finalized transaction
|
// Try to return an already finalized transaction
|
||||||
tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
|
found, tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
|
||||||
if err != nil {
|
if !found {
|
||||||
// No finalized transaction, try to retrieve it from the pool
|
// No finalized transaction, try to retrieve it from the pool
|
||||||
if tx := s.b.GetPoolTransaction(hash); tx != nil {
|
if tx := s.b.GetPoolTransaction(hash); tx != nil {
|
||||||
return NewRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig()), nil
|
return NewRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig()), nil
|
||||||
|
|
@ -1670,8 +1670,8 @@ func (s *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.H
|
||||||
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
|
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
|
||||||
func (s *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
func (s *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
||||||
// Retrieve a finalized transaction, or a pooled otherwise
|
// Retrieve a finalized transaction, or a pooled otherwise
|
||||||
tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
|
found, tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
|
||||||
if err != nil {
|
if !found {
|
||||||
if tx = s.b.GetPoolTransaction(hash); tx == nil {
|
if tx = s.b.GetPoolTransaction(hash); tx == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1682,11 +1682,12 @@ func (s *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash commo
|
||||||
|
|
||||||
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
|
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
|
||||||
func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
|
func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
|
||||||
tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
|
found, tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// When the transaction doesn't exist or is not indexed yet,
|
return nil, err // transaction is not fully indexed
|
||||||
// the RPC method should return JSON null as per specification.
|
}
|
||||||
return nil, err
|
if !found {
|
||||||
|
return nil, nil // transaction is not existent or reachable
|
||||||
}
|
}
|
||||||
header, err := s.b.HeaderByHash(ctx, blockHash)
|
header, err := s.b.HeaderByHash(ctx, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -2076,14 +2077,11 @@ func (api *DebugAPI) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.Block
|
||||||
// GetRawTransaction returns the bytes of the transaction for the given hash.
|
// GetRawTransaction returns the bytes of the transaction for the given hash.
|
||||||
func (s *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
func (s *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
||||||
// Retrieve a finalized transaction, or a pooled otherwise
|
// Retrieve a finalized transaction, or a pooled otherwise
|
||||||
tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
|
found, tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
|
||||||
if err != nil {
|
if !found {
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if tx == nil {
|
|
||||||
if tx = s.b.GetPoolTransaction(hash); tx == nil {
|
if tx = s.b.GetPoolTransaction(hash); tx == nil {
|
||||||
// Transaction not found anywhere, abort
|
// Transaction not found anywhere, abort
|
||||||
return nil, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tx.MarshalBinary()
|
return tx.MarshalBinary()
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ type Backend interface {
|
||||||
|
|
||||||
// Transaction pool API
|
// Transaction pool API
|
||||||
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
||||||
GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
|
GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error)
|
||||||
GetPoolTransactions() (types.Transactions, error)
|
GetPoolTransactions() (types.Transactions, error)
|
||||||
GetPoolTransaction(txHash common.Hash) *types.Transaction
|
GetPoolTransaction(txHash common.Hash) *types.Transaction
|
||||||
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
|
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue