mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
core, eth, internal: return tx indexing progress
This commit is contained in:
parent
8b1664dac8
commit
076ba509be
4 changed files with 110 additions and 41 deletions
|
|
@ -185,6 +185,29 @@ func DefaultCacheConfigWithScheme(scheme string) *CacheConfig {
|
||||||
return &config
|
return &config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// txLookup is wrapper over transaction lookup along with the corresponding
|
||||||
|
// transaction itself.
|
||||||
|
type txLookup struct {
|
||||||
|
lookup *rawdb.LegacyTxLookupEntry
|
||||||
|
transaction *types.Transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("index-tail: %d, index-head: %d, limit: %s", prog.tail, prog.head, limit)
|
||||||
|
}
|
||||||
|
|
||||||
// BlockChain represents the canonical chain given a database with a genesis
|
// BlockChain represents the canonical chain given a database with a genesis
|
||||||
// block. The Blockchain manages chain imports, reverts, chain reorganisations.
|
// block. The Blockchain manages chain imports, reverts, chain reorganisations.
|
||||||
//
|
//
|
||||||
|
|
@ -242,15 +265,16 @@ type BlockChain struct {
|
||||||
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
|
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
|
||||||
receiptsCache *lru.Cache[common.Hash, []*types.Receipt]
|
receiptsCache *lru.Cache[common.Hash, []*types.Receipt]
|
||||||
blockCache *lru.Cache[common.Hash, *types.Block]
|
blockCache *lru.Cache[common.Hash, *types.Block]
|
||||||
txLookupCache *lru.Cache[common.Hash, *rawdb.LegacyTxLookupEntry]
|
txLookupCache *lru.Cache[common.Hash, txLookup]
|
||||||
|
|
||||||
// future blocks are blocks added for later processing
|
// future blocks are blocks added for later processing
|
||||||
futureBlocks *lru.Cache[common.Hash, *types.Block]
|
futureBlocks *lru.Cache[common.Hash, *types.Block]
|
||||||
|
|
||||||
wg sync.WaitGroup //
|
wg sync.WaitGroup
|
||||||
quit chan struct{} // shutdown signal, closed in Stop.
|
quit chan struct{} // shutdown signal, closed in Stop.
|
||||||
stopping atomic.Bool // false if chain is running, true when stopped
|
stopping atomic.Bool // false if chain is running, true when stopped
|
||||||
procInterrupt atomic.Bool // interrupt signaler for block processing
|
procInterrupt atomic.Bool // interrupt signaler for block processing
|
||||||
|
txIndexProgCh chan chan txIndexProgress // chan for querying the progress of transaction indexing
|
||||||
|
|
||||||
engine consensus.Engine
|
engine consensus.Engine
|
||||||
validator Validator // Block and state validator interface
|
validator Validator // Block and state validator interface
|
||||||
|
|
@ -297,8 +321,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
|
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
|
||||||
receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit),
|
receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit),
|
||||||
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
|
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
|
||||||
txLookupCache: lru.NewCache[common.Hash, *rawdb.LegacyTxLookupEntry](txLookupCacheLimit),
|
txLookupCache: lru.NewCache[common.Hash, txLookup](txLookupCacheLimit),
|
||||||
futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks),
|
futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks),
|
||||||
|
txIndexProgCh: make(chan chan txIndexProgress),
|
||||||
engine: engine,
|
engine: engine,
|
||||||
vmConfig: vmConfig,
|
vmConfig: vmConfig,
|
||||||
}
|
}
|
||||||
|
|
@ -2425,6 +2450,34 @@ 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return txIndexProgress{
|
||||||
|
tail: *tail,
|
||||||
|
head: head,
|
||||||
|
limit: bc.txLookupLimit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// askTxIndexProgress retrieves the tx indexing progress.
|
||||||
|
func (bc *BlockChain) askTxIndexProgress() (txIndexProgress, error) {
|
||||||
|
ch := make(chan txIndexProgress, 1)
|
||||||
|
select {
|
||||||
|
case bc.txIndexProgCh <- ch:
|
||||||
|
return <-ch, nil
|
||||||
|
case <-bc.quit:
|
||||||
|
return txIndexProgress{}, errors.New("blockchain is closed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// maintainTxIndex is responsible for the construction and deletion of the
|
// maintainTxIndex is responsible for the construction and deletion of the
|
||||||
// transaction index.
|
// transaction index.
|
||||||
//
|
//
|
||||||
|
|
@ -2440,8 +2493,9 @@ func (bc *BlockChain) maintainTxIndex() {
|
||||||
|
|
||||||
// Listening to chain events and manipulate the transaction indexes.
|
// Listening to chain events and manipulate the transaction indexes.
|
||||||
var (
|
var (
|
||||||
done chan struct{} // Non-nil if background unindexing or reindexing routine is active.
|
done chan struct{} // Non-nil if background unindexing or reindexing routine is active.
|
||||||
headCh = make(chan ChainHeadEvent, 1) // Buffered to avoid locking up the event feed
|
lastHead uint64 // The latest announced chain head (whose tx indexes are assumed created)
|
||||||
|
headCh = make(chan ChainHeadEvent, 1) // Buffered to avoid locking up the event feed
|
||||||
)
|
)
|
||||||
sub := bc.SubscribeChainHeadEvent(headCh)
|
sub := bc.SubscribeChainHeadEvent(headCh)
|
||||||
if sub == nil {
|
if sub == nil {
|
||||||
|
|
@ -2464,8 +2518,11 @@ func (bc *BlockChain) maintainTxIndex() {
|
||||||
done = make(chan struct{})
|
done = make(chan struct{})
|
||||||
go bc.indexBlocks(rawdb.ReadTxIndexTail(bc.db), head.Block.NumberU64(), done)
|
go bc.indexBlocks(rawdb.ReadTxIndexTail(bc.db), head.Block.NumberU64(), done)
|
||||||
}
|
}
|
||||||
|
lastHead = head.Block.NumberU64()
|
||||||
case <-done:
|
case <-done:
|
||||||
done = nil
|
done = nil
|
||||||
|
case ch := <-bc.txIndexProgCh:
|
||||||
|
ch <- bc.reportTxIndexProgress(lastHead)
|
||||||
case <-bc.quit:
|
case <-bc.quit:
|
||||||
if done != nil {
|
if done != nil {
|
||||||
log.Info("Waiting background transaction indexer to exit")
|
log.Info("Waiting background transaction indexer to exit")
|
||||||
|
|
|
||||||
|
|
@ -254,20 +254,34 @@ 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 associate with the given transaction
|
// GetTransactionLookup retrieves the lookup along with the transaction itself
|
||||||
// hash from the cache or database.
|
// associate with the given transaction hash. A non-nil error will be returned
|
||||||
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) *rawdb.LegacyTxLookupEntry {
|
// if the transaction is not found.
|
||||||
|
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 lookup, exist := bc.txLookupCache.Get(hash); exist {
|
if item, exist := bc.txLookupCache.Get(hash); exist {
|
||||||
return lookup
|
return item.lookup, item.transaction, nil
|
||||||
}
|
}
|
||||||
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
return 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, progress
|
||||||
}
|
}
|
||||||
lookup := &rawdb.LegacyTxLookupEntry{BlockHash: blockHash, BlockIndex: blockNumber, Index: txIndex}
|
lookup := &rawdb.LegacyTxLookupEntry{
|
||||||
bc.txLookupCache.Add(hash, lookup)
|
BlockHash: blockHash,
|
||||||
return lookup
|
BlockIndex: blockNumber,
|
||||||
|
Index: txIndex,
|
||||||
|
}
|
||||||
|
bc.txLookupCache.Add(hash, txLookup{
|
||||||
|
lookup: lookup,
|
||||||
|
transaction: tx,
|
||||||
|
})
|
||||||
|
return lookup, tx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTd retrieves a block's total difficulty in the canonical chain from the
|
// GetTd retrieves a block's total difficulty in the canonical chain from the
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package eth
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -308,9 +309,15 @@ func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction
|
||||||
return b.eth.txPool.Get(hash)
|
return b.eth.txPool.Get(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
|
||||||
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.eth.ChainDb(), txHash)
|
lookup, tx, err := b.eth.blockchain.GetTransactionLookup(txHash)
|
||||||
return tx, blockHash, blockNumber, index, nil
|
if err != nil {
|
||||||
|
return nil, common.Hash{}, 0, 0, fmt.Errorf("tx is not existent or not indexed, %w", err)
|
||||||
|
}
|
||||||
|
return 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) {
|
||||||
|
|
|
||||||
|
|
@ -1654,22 +1654,17 @@ func (s *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.H
|
||||||
// Try to return an already finalized transaction
|
// Try to return an already finalized transaction
|
||||||
tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
|
tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if tx != nil {
|
header, err := s.b.HeaderByHash(ctx, blockHash)
|
||||||
header, err := s.b.HeaderByHash(ctx, blockHash)
|
if err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return newRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, s.b.ChainConfig()), nil
|
|
||||||
}
|
}
|
||||||
// No finalized transaction, try to retrieve it from the pool
|
return newRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, s.b.ChainConfig()), nil
|
||||||
if tx := s.b.GetPoolTransaction(hash); tx != nil {
|
|
||||||
return NewRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transaction unknown, return as such
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
|
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
|
||||||
|
|
@ -1677,12 +1672,8 @@ func (s *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash commo
|
||||||
// Retrieve a finalized transaction, or a pooled otherwise
|
// Retrieve a finalized transaction, or a pooled otherwise
|
||||||
tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
|
tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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
|
return nil, err
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Serialize to RLP and return
|
// Serialize to RLP and return
|
||||||
|
|
@ -1692,10 +1683,10 @@ 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)
|
tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
|
||||||
if tx == nil || err != nil {
|
if err != nil {
|
||||||
// When the transaction doesn't exist, the RPC method should return JSON null
|
// When the transaction doesn't exist or is not indexed yet,
|
||||||
// as per specification.
|
// the RPC method should return JSON null as per specification.
|
||||||
return nil, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
header, err := s.b.HeaderByHash(ctx, blockHash)
|
header, err := s.b.HeaderByHash(ctx, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue