diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index ba54964d3e..f68ff43ada 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -275,17 +275,17 @@ func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, max // // from the node's perspective. This can be due to // the transaction indexer not being finished. The caller must explicitly check // the indexer progress. -func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) { +func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction) { bc.txLookupLock.RLock() defer bc.txLookupLock.RUnlock() // Short circuit if the txlookup already in the cache, retrieve otherwise if item, exist := bc.txLookupCache.Get(hash); exist { - return item.lookup, item.transaction, nil + return item.lookup, item.transaction } tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash) if tx == nil { - return nil, nil, nil + return nil, nil } lookup := &rawdb.LegacyTxLookupEntry{ BlockHash: blockHash, @@ -296,11 +296,11 @@ func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLoo lookup: lookup, transaction: tx, }) - return lookup, tx, nil + return lookup, tx } // TxIndexDone returns true if the transaction indexer has finished indexing. -func (bc *BlockChain) TxIndexDone(ctx context.Context) (bool, error) { +func (bc *BlockChain) TxIndexDone(ctx context.Context) bool { progress, err := bc.TxIndexProgress(ctx) if err != nil { // No error is returned if the transaction indexing progress is unreachable @@ -310,12 +310,12 @@ func (bc *BlockChain) TxIndexDone(ctx context.Context) (bool, error) { // // In such scenarios, the transaction is treated as unreachable, though // this is clearly an unintended and unexpected situation. - return true, nil + return true } if !progress.Done() { - return false, nil + return false } - return true, nil + return true } // HasState checks if state trie is fully present in the database or not. diff --git a/eth/api_backend.go b/eth/api_backend.go index 46cd2fb9dd..0372fb6664 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -349,22 +349,21 @@ func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction // GetTransaction 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 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(ctx, txHash) - if err != nil { - return false, nil, common.Hash{}, 0, 0, err - } +// A null will be returned in the transaction is not found. The transaction is not existent +// // from the node's perspective. This can be due to +// the transaction indexer not being finished. The caller must explicitly check +// the indexer progress. +func (b *EthAPIBackend) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) { + lookup, tx := b.eth.blockchain.GetTransactionLookup(txHash) if lookup == nil || tx == nil { - return false, nil, common.Hash{}, 0, 0, nil + return false, nil, common.Hash{}, 0, 0 } - return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index, nil + return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index +} + +// TxIndexDone returns true if the transaction indexer has finished indexing. +func (b *EthAPIBackend) TxIndexDone(ctx context.Context) bool { + return b.eth.blockchain.TxIndexDone(ctx) } func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 3cb86c80e2..3c8e2c9924 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -82,7 +82,8 @@ 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) (bool, *types.Transaction, common.Hash, uint64, uint64, error) + GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) + TxIndexDone(ctx context.Context) bool RPCGasCap() uint64 ChainConfig() *params.ChainConfig Engine() consensus.Engine @@ -858,12 +859,13 @@ 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) { - found, _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) - if err != nil { - return nil, ethapi.NewTxIndexingError() - } - // Only mined txes are supported + found, _, blockHash, blockNumber, index := api.backend.GetTransaction(hash) if !found { + // Warn in case tx indexer is not done. + if !api.backend.TxIndexDone(ctx) { + return nil, ethapi.NewTxIndexingError() + } + // Only mined txes are supported return nil, errTxNotFound } // It shouldn't happen in practice. diff --git a/graphql/graphql.go b/graphql/graphql.go index 0cb1d0cd54..e23e6fcb0e 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -229,7 +229,7 @@ func (t *Transaction) resolve(ctx context.Context) (*types.Transaction, *Block) return t.tx, t.block } // Try to return an already finalized transaction - found, tx, blockHash, _, index, _ := t.r.backend.GetTransaction(ctx, t.hash) + found, tx, blockHash, _, index := t.r.backend.GetTransaction(t.hash) if found { t.tx = tx blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 817d3f5a21..945aa881e8 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1333,16 +1333,18 @@ func (api *TransactionAPI) GetTransactionCount(ctx context.Context, address comm // GetTransactionByHash returns the transaction for the given hash func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) { // Try to return an already finalized transaction - found, tx, blockHash, blockNumber, index, err := api.b.GetTransaction(ctx, hash) + found, tx, blockHash, blockNumber, index := api.b.GetTransaction(hash) if !found { // No finalized transaction, try to retrieve it from the pool if tx := api.b.GetPoolTransaction(hash); tx != nil { return NewRPCPendingTransaction(tx, api.b.CurrentHeader(), api.b.ChainConfig()), nil } - if err == nil { - return nil, nil + // If also not in the pool there is a chance the tx indexer is still in progress. + if !api.b.TxIndexDone(ctx) { + return nil, NewTxIndexingError() } - return nil, NewTxIndexingError() + // If the transaction is not found in the pool and the indexer is done, return nil + return nil, nil } header, err := api.b.HeaderByHash(ctx, blockHash) if err != nil { @@ -1354,27 +1356,31 @@ func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common // GetRawTransactionByHash returns the bytes of the transaction for the given hash. func (api *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { // Retrieve a finalized transaction, or a pooled otherwise - found, tx, _, _, _, err := api.b.GetTransaction(ctx, hash) + found, tx, _, _, _ := api.b.GetTransaction(hash) if !found { if tx = api.b.GetPoolTransaction(hash); tx != nil { return tx.MarshalBinary() } - if err == nil { - return nil, nil + // If also not in the pool there is a chance the tx indexer is still in progress. + if !api.b.TxIndexDone(ctx) { + return nil, NewTxIndexingError() } - return nil, NewTxIndexingError() + // If the transaction is not found in the pool and the indexer is done, return nil + return nil, nil } return tx.MarshalBinary() } // GetTransactionReceipt returns the transaction receipt for the given transaction hash. func (api *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) { - found, tx, blockHash, blockNumber, index, err := api.b.GetTransaction(ctx, hash) - if err != nil { - return nil, NewTxIndexingError() // transaction is not fully indexed - } + found, tx, blockHash, blockNumber, index := api.b.GetTransaction(hash) if !found { - return nil, nil // transaction is not existent or reachable + // Make sure indexer is done. + if !api.b.TxIndexDone(ctx) { + return nil, NewTxIndexingError() + } + // No such tx. + return nil, nil } header, err := api.b.HeaderByHash(ctx, blockHash) if err != nil { @@ -1774,15 +1780,17 @@ func (api *DebugAPI) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.Block // GetRawTransaction returns the bytes of the transaction for the given hash. func (api *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { // Retrieve a finalized transaction, or a pooled otherwise - found, tx, _, _, _, err := api.b.GetTransaction(ctx, hash) + found, tx, _, _, _ := api.b.GetTransaction(hash) if !found { if tx = api.b.GetPoolTransaction(hash); tx != nil { return tx.MarshalBinary() } - if err == nil { - return nil, nil + // If also not in the pool there is a chance the tx indexer is still in progress. + if !api.b.TxIndexDone(ctx) { + return nil, NewTxIndexingError() } - return nil, NewTxIndexingError() + // Transaction is not found in the pool and the indexer is done. + return nil, nil } return tx.MarshalBinary() } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 449eaa2d59..9ac313b2a1 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -74,7 +74,8 @@ type Backend interface { // Transaction pool API SendTx(ctx context.Context, signedTx *types.Transaction) error - GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) + GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) + TxIndexDone(ctx context.Context) bool GetPoolTransactions() (types.Transactions, error) GetPoolTransaction(txHash common.Hash) *types.Transaction GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)