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
|
||||
// transaction itself.
|
||||
// transaction object.
|
||||
type txLookup struct {
|
||||
lookup *rawdb.LegacyTxLookupEntry
|
||||
transaction *types.Transaction
|
||||
|
|
@ -194,18 +194,17 @@ type txLookup struct {
|
|||
|
||||
// txIndexProgress is the struct describing the progress for transaction indexing.
|
||||
type txIndexProgress struct {
|
||||
tail uint64 // the oldest block indexed for transactions
|
||||
head uint64 // the latest block indexed for transactions
|
||||
limit uint64 // the number of blocks required for transaction indexing(0 means the whole chain)
|
||||
head uint64 // the current chain head
|
||||
indexed uint64 // the number of blocks have been indexed
|
||||
limit uint64 // the number of blocks required for transaction indexing(0 means the whole chain)
|
||||
}
|
||||
|
||||
// Error implements Error returning the progress in string format.
|
||||
func (prog txIndexProgress) Error() string {
|
||||
limit := "entire chain"
|
||||
if prog.limit != 0 {
|
||||
limit = fmt.Sprintf("last %d blocks", prog.limit)
|
||||
// done returns an indicator if the transaction indexing is finished.
|
||||
func (prog txIndexProgress) done() bool {
|
||||
if prog.limit == 0 {
|
||||
return prog.indexed == (prog.head + 1) // genesis included
|
||||
}
|
||||
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
|
||||
|
|
@ -2453,18 +2452,17 @@ func (bc *BlockChain) indexBlocks(tail *uint64, head uint64, done chan struct{})
|
|||
|
||||
// reportTxIndexProgress returns the tx indexing progress.
|
||||
func (bc *BlockChain) reportTxIndexProgress(head uint64) txIndexProgress {
|
||||
tail := rawdb.ReadTxIndexTail(bc.db)
|
||||
if tail == nil {
|
||||
return txIndexProgress{
|
||||
tail: 0, // not indexed yet
|
||||
head: 0, // not indexed yet
|
||||
limit: bc.txLookupLimit,
|
||||
}
|
||||
var (
|
||||
indexed uint64
|
||||
tail = rawdb.ReadTxIndexTail(bc.db)
|
||||
)
|
||||
if tail != nil {
|
||||
indexed = head - *tail + 1
|
||||
}
|
||||
return txIndexProgress{
|
||||
tail: *tail,
|
||||
head: head,
|
||||
limit: bc.txLookupLimit,
|
||||
head: head,
|
||||
indexed: indexed,
|
||||
limit: bc.txLookupLimit,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2510,6 +2508,7 @@ func (bc *BlockChain) maintainTxIndex() {
|
|||
// indexer is never triggered.
|
||||
if head := rawdb.ReadHeadBlock(bc.db); head != nil && head.Number().Uint64() != 0 {
|
||||
done = make(chan struct{})
|
||||
lastHead = head.Number().Uint64()
|
||||
go bc.indexBlocks(rawdb.ReadTxIndexTail(bc.db), head.NumberU64(), done)
|
||||
}
|
||||
for {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"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)
|
||||
}
|
||||
|
||||
// GetTransactionLookup retrieves the lookup along with the transaction itself
|
||||
// associate with the given transaction hash. A non-nil error will be returned
|
||||
// if the transaction is not found.
|
||||
// GetTransactionLookup retrieves the lookup along with the transaction
|
||||
// itself associate with the given transaction hash.
|
||||
//
|
||||
// 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) {
|
||||
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
||||
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)
|
||||
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()
|
||||
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{
|
||||
BlockHash: blockHash,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package eth
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
|
|
@ -310,14 +309,24 @@ func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction
|
|||
}
|
||||
|
||||
// GetTransaction retrieves the lookup along with the transaction itself associate
|
||||
// with the given transaction hash. A non-nil error will be returned if the
|
||||
// transaction is not found.
|
||||
func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
|
||||
// with the given transaction hash.
|
||||
//
|
||||
// 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)
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ type Backend interface {
|
|||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||
BlockByHash(ctx context.Context, hash common.Hash) (*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
|
||||
ChainConfig() *params.ChainConfig
|
||||
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
|
||||
// and returns them as a JSON object.
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
// Only mined txes are supported
|
||||
if tx == nil {
|
||||
if !found {
|
||||
return nil, errTxNotFound
|
||||
}
|
||||
// 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
|
||||
}
|
||||
// Try to return an already finalized transaction
|
||||
tx, blockHash, _, index, err := t.r.backend.GetTransaction(ctx, t.hash)
|
||||
if err == nil && tx != nil {
|
||||
found, tx, blockHash, _, index, _ := t.r.backend.GetTransaction(ctx, t.hash)
|
||||
if found {
|
||||
t.tx = tx
|
||||
blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false)
|
||||
t.block = &Block{
|
||||
|
|
|
|||
|
|
@ -1652,8 +1652,8 @@ func (s *TransactionAPI) GetTransactionCount(ctx context.Context, address common
|
|||
// GetTransactionByHash returns the transaction for the given hash
|
||||
func (s *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
||||
// Try to return an already finalized transaction
|
||||
tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
|
||||
if err != nil {
|
||||
found, tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
|
||||
if !found {
|
||||
// No finalized transaction, try to retrieve it from the pool
|
||||
if tx := s.b.GetPoolTransaction(hash); tx != 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.
|
||||
func (s *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
||||
// Retrieve a finalized transaction, or a pooled otherwise
|
||||
tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
|
||||
if err != nil {
|
||||
found, tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
|
||||
if !found {
|
||||
if tx = s.b.GetPoolTransaction(hash); tx == nil {
|
||||
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.
|
||||
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 {
|
||||
// When the transaction doesn't exist or is not indexed yet,
|
||||
// the RPC method should return JSON null as per specification.
|
||||
return nil, err
|
||||
return nil, err // transaction is not fully indexed
|
||||
}
|
||||
if !found {
|
||||
return nil, nil // transaction is not existent or reachable
|
||||
}
|
||||
header, err := s.b.HeaderByHash(ctx, blockHash)
|
||||
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.
|
||||
func (s *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
||||
// Retrieve a finalized transaction, or a pooled otherwise
|
||||
tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tx == nil {
|
||||
found, tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
|
||||
if !found {
|
||||
if tx = s.b.GetPoolTransaction(hash); tx == nil {
|
||||
// Transaction not found anywhere, abort
|
||||
return nil, nil
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return tx.MarshalBinary()
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ type Backend interface {
|
|||
|
||||
// Transaction pool API
|
||||
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)
|
||||
GetPoolTransaction(txHash common.Hash) *types.Transaction
|
||||
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
|
||||
|
|
|
|||
Loading…
Reference in a new issue