forked from forks/go-ethereum
core: reduce load on txindexer from API (#31752)
Fixes https://github.com/ethereum/go-ethereum/issues/31732. This logic was removed in the recent refactoring in the txindexer to handle history cutoff (#31393). It was first introduced in this PR: https://github.com/ethereum/go-ethereum/pull/28908. I have tested it and it works as an alternative to #31745. This PR packs 3 changes to the flow of fetching txs from the API: - It caches the indexer tail after each run is over to avoid hitting the db all the time as was done originally in #28908. - Changes `backend.GetTransaction`. It doesn't return an error anymore when tx indexer is in progress. It shifts the responsibility to the caller to check the progress. The reason is that in most cases we anyway check the txpool for the tx. If it was indeed a pending tx we can avoid the indexer progress check. --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com>
This commit is contained in:
parent
516451dc3a
commit
615d29f7c2
12 changed files with 150 additions and 131 deletions
|
|
@ -270,42 +270,20 @@ func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, max
|
||||||
// GetTransactionLookup retrieves the lookup along with the transaction
|
// GetTransactionLookup retrieves the lookup along with the transaction
|
||||||
// itself associate with the given transaction hash.
|
// itself associate with the given transaction hash.
|
||||||
//
|
//
|
||||||
// An error will be returned if the transaction is not found, and background
|
// A null will be returned if the transaction is not found. This can be due to
|
||||||
// indexing for transactions is still in progress. The transaction might be
|
// the transaction indexer not being finished. The caller must explicitly check
|
||||||
// reachable shortly once it's indexed.
|
// the indexer progress.
|
||||||
//
|
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction) {
|
||||||
// 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) {
|
|
||||||
bc.txLookupLock.RLock()
|
bc.txLookupLock.RLock()
|
||||||
defer bc.txLookupLock.RUnlock()
|
defer bc.txLookupLock.RUnlock()
|
||||||
|
|
||||||
// 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 {
|
||||||
return item.lookup, item.transaction, nil
|
return item.lookup, item.transaction
|
||||||
}
|
}
|
||||||
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
progress, err := bc.TxIndexProgress()
|
return nil, nil
|
||||||
if err != nil {
|
|
||||||
// No error is returned if the transaction indexing progress is unreachable
|
|
||||||
// due to unexpected internal errors. In such cases, it is impossible to
|
|
||||||
// determine whether the transaction does not exist or has simply not been
|
|
||||||
// indexed yet without a progress marker.
|
|
||||||
//
|
|
||||||
// In such scenarios, the transaction is treated as unreachable, though
|
|
||||||
// this is clearly an unintended and unexpected situation.
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
// The transaction indexing is not finished yet, returning an
|
|
||||||
// error to explicitly indicate it.
|
|
||||||
if !progress.Done() {
|
|
||||||
return nil, nil, errors.New("transaction indexing still in progress")
|
|
||||||
}
|
|
||||||
// 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,
|
||||||
|
|
@ -316,7 +294,23 @@ func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLoo
|
||||||
lookup: lookup,
|
lookup: lookup,
|
||||||
transaction: tx,
|
transaction: tx,
|
||||||
})
|
})
|
||||||
return lookup, tx, nil
|
return lookup, tx
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxIndexDone returns true if the transaction indexer has finished indexing.
|
||||||
|
func (bc *BlockChain) TxIndexDone() bool {
|
||||||
|
progress, err := bc.TxIndexProgress()
|
||||||
|
if err != nil {
|
||||||
|
// No error is returned if the transaction indexing progress is unreachable
|
||||||
|
// due to unexpected internal errors. In such cases, it is impossible to
|
||||||
|
// determine whether the transaction does not exist or has simply not been
|
||||||
|
// indexed yet without a progress marker.
|
||||||
|
//
|
||||||
|
// In such scenarios, the transaction is treated as unreachable, though
|
||||||
|
// this is clearly an unintended and unexpected situation.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return progress.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasState checks if state trie is fully present in the database or not.
|
// HasState checks if state trie is fully present in the database or not.
|
||||||
|
|
@ -412,7 +406,7 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
|
||||||
if bc.txIndexer == nil {
|
if bc.txIndexer == nil {
|
||||||
return TxIndexProgress{}, errors.New("tx indexer is not enabled")
|
return TxIndexProgress{}, errors.New("tx indexer is not enabled")
|
||||||
}
|
}
|
||||||
return bc.txIndexer.txIndexProgress()
|
return bc.txIndexer.txIndexProgress(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HistoryPruningCutoff returns the configured history pruning point.
|
// HistoryPruningCutoff returns the configured history pruning point.
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
|
@ -47,11 +47,21 @@ type txIndexer struct {
|
||||||
// and all others shouldn't.
|
// and all others shouldn't.
|
||||||
limit uint64
|
limit uint64
|
||||||
|
|
||||||
|
// The current head of blockchain for transaction indexing. This field
|
||||||
|
// is accessed by both the indexer and the indexing progress queries.
|
||||||
|
head atomic.Uint64
|
||||||
|
|
||||||
|
// The current tail of the indexed transactions, null indicates
|
||||||
|
// that no transactions have been indexed yet.
|
||||||
|
//
|
||||||
|
// This field is accessed by both the indexer and the indexing
|
||||||
|
// progress queries.
|
||||||
|
tail atomic.Pointer[uint64]
|
||||||
|
|
||||||
// cutoff denotes the block number before which the chain segment should
|
// cutoff denotes the block number before which the chain segment should
|
||||||
// be pruned and not available locally.
|
// be pruned and not available locally.
|
||||||
cutoff uint64
|
cutoff uint64
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
progress chan chan TxIndexProgress
|
|
||||||
term chan chan struct{}
|
term chan chan struct{}
|
||||||
closed chan struct{}
|
closed chan struct{}
|
||||||
}
|
}
|
||||||
|
|
@ -63,10 +73,12 @@ func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer {
|
||||||
limit: limit,
|
limit: limit,
|
||||||
cutoff: cutoff,
|
cutoff: cutoff,
|
||||||
db: chain.db,
|
db: chain.db,
|
||||||
progress: make(chan chan TxIndexProgress),
|
|
||||||
term: make(chan chan struct{}),
|
term: make(chan chan struct{}),
|
||||||
closed: make(chan struct{}),
|
closed: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
indexer.head.Store(indexer.resolveHead())
|
||||||
|
indexer.tail.Store(rawdb.ReadTxIndexTail(chain.db))
|
||||||
|
|
||||||
go indexer.loop(chain)
|
go indexer.loop(chain)
|
||||||
|
|
||||||
var msg string
|
var msg string
|
||||||
|
|
@ -154,6 +166,7 @@ func (indexer *txIndexer) repair(head uint64) {
|
||||||
// A crash may occur between the two delete operations,
|
// A crash may occur between the two delete operations,
|
||||||
// potentially leaving dangling indexes in the database.
|
// potentially leaving dangling indexes in the database.
|
||||||
// However, this is considered acceptable.
|
// However, this is considered acceptable.
|
||||||
|
indexer.tail.Store(nil)
|
||||||
rawdb.DeleteTxIndexTail(indexer.db)
|
rawdb.DeleteTxIndexTail(indexer.db)
|
||||||
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
||||||
log.Warn("Purge transaction indexes", "head", head, "tail", *tail)
|
log.Warn("Purge transaction indexes", "head", head, "tail", *tail)
|
||||||
|
|
@ -174,6 +187,7 @@ func (indexer *txIndexer) repair(head uint64) {
|
||||||
// Traversing the database directly within the transaction
|
// Traversing the database directly within the transaction
|
||||||
// index namespace might be slow and expensive, but we
|
// index namespace might be slow and expensive, but we
|
||||||
// have no choice.
|
// have no choice.
|
||||||
|
indexer.tail.Store(nil)
|
||||||
rawdb.DeleteTxIndexTail(indexer.db)
|
rawdb.DeleteTxIndexTail(indexer.db)
|
||||||
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
||||||
log.Warn("Purge transaction indexes", "head", head, "cutoff", indexer.cutoff)
|
log.Warn("Purge transaction indexes", "head", head, "cutoff", indexer.cutoff)
|
||||||
|
|
@ -187,6 +201,7 @@ func (indexer *txIndexer) repair(head uint64) {
|
||||||
// A crash may occur between the two delete operations,
|
// A crash may occur between the two delete operations,
|
||||||
// potentially leaving dangling indexes in the database.
|
// potentially leaving dangling indexes in the database.
|
||||||
// However, this is considered acceptable.
|
// However, this is considered acceptable.
|
||||||
|
indexer.tail.Store(&indexer.cutoff)
|
||||||
rawdb.WriteTxIndexTail(indexer.db, indexer.cutoff)
|
rawdb.WriteTxIndexTail(indexer.db, indexer.cutoff)
|
||||||
rawdb.DeleteAllTxLookupEntries(indexer.db, func(txhash common.Hash, blob []byte) bool {
|
rawdb.DeleteAllTxLookupEntries(indexer.db, func(txhash common.Hash, blob []byte) bool {
|
||||||
n := rawdb.DecodeTxLookupEntry(blob, indexer.db)
|
n := rawdb.DecodeTxLookupEntry(blob, indexer.db)
|
||||||
|
|
@ -218,14 +233,13 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
||||||
var (
|
var (
|
||||||
stop chan struct{} // Non-nil if background routine is active
|
stop chan struct{} // Non-nil if background routine is active
|
||||||
done chan struct{} // Non-nil if background routine is active
|
done chan struct{} // Non-nil if background routine is active
|
||||||
head = indexer.resolveHead() // The latest announced chain head
|
|
||||||
|
|
||||||
headCh = make(chan ChainHeadEvent)
|
headCh = make(chan ChainHeadEvent)
|
||||||
sub = chain.SubscribeChainHeadEvent(headCh)
|
sub = chain.SubscribeChainHeadEvent(headCh)
|
||||||
)
|
)
|
||||||
defer sub.Unsubscribe()
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
// Validate the transaction indexes and repair if necessary
|
// Validate the transaction indexes and repair if necessary
|
||||||
|
head := indexer.head.Load()
|
||||||
indexer.repair(head)
|
indexer.repair(head)
|
||||||
|
|
||||||
// Launch the initial processing if chain is not empty (head != genesis).
|
// Launch the initial processing if chain is not empty (head != genesis).
|
||||||
|
|
@ -238,17 +252,18 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case h := <-headCh:
|
case h := <-headCh:
|
||||||
|
indexer.head.Store(h.Header.Number.Uint64())
|
||||||
if done == nil {
|
if done == nil {
|
||||||
stop = make(chan struct{})
|
stop = make(chan struct{})
|
||||||
done = make(chan struct{})
|
done = make(chan struct{})
|
||||||
go indexer.run(h.Header.Number.Uint64(), stop, done)
|
go indexer.run(h.Header.Number.Uint64(), stop, done)
|
||||||
}
|
}
|
||||||
head = h.Header.Number.Uint64()
|
|
||||||
case <-done:
|
case <-done:
|
||||||
stop = nil
|
stop = nil
|
||||||
done = nil
|
done = nil
|
||||||
case ch := <-indexer.progress:
|
indexer.tail.Store(rawdb.ReadTxIndexTail(indexer.db))
|
||||||
ch <- indexer.report(head)
|
|
||||||
case ch := <-indexer.term:
|
case ch := <-indexer.term:
|
||||||
if stop != nil {
|
if stop != nil {
|
||||||
close(stop)
|
close(stop)
|
||||||
|
|
@ -264,7 +279,7 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// report returns the tx indexing progress.
|
// report returns the tx indexing progress.
|
||||||
func (indexer *txIndexer) report(head uint64) TxIndexProgress {
|
func (indexer *txIndexer) report(head uint64, tail *uint64) TxIndexProgress {
|
||||||
// Special case if the head is even below the cutoff,
|
// Special case if the head is even below the cutoff,
|
||||||
// nothing to index.
|
// nothing to index.
|
||||||
if head < indexer.cutoff {
|
if head < indexer.cutoff {
|
||||||
|
|
@ -284,7 +299,6 @@ func (indexer *txIndexer) report(head uint64) TxIndexProgress {
|
||||||
}
|
}
|
||||||
// Compute how many blocks have been indexed
|
// Compute how many blocks have been indexed
|
||||||
var indexed uint64
|
var indexed uint64
|
||||||
tail := rawdb.ReadTxIndexTail(indexer.db)
|
|
||||||
if tail != nil {
|
if tail != nil {
|
||||||
indexed = head - *tail + 1
|
indexed = head - *tail + 1
|
||||||
}
|
}
|
||||||
|
|
@ -300,16 +314,12 @@ func (indexer *txIndexer) report(head uint64) TxIndexProgress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// txIndexProgress retrieves the tx indexing progress, or an error if the
|
// txIndexProgress retrieves the transaction indexing progress. The reported
|
||||||
// background tx indexer is already stopped.
|
// progress may slightly lag behind the actual indexing state, as the tail is
|
||||||
func (indexer *txIndexer) txIndexProgress() (TxIndexProgress, error) {
|
// only updated at the end of each indexing operation. However, this delay is
|
||||||
ch := make(chan TxIndexProgress, 1)
|
// considered acceptable.
|
||||||
select {
|
func (indexer *txIndexer) txIndexProgress() TxIndexProgress {
|
||||||
case indexer.progress <- ch:
|
return indexer.report(indexer.head.Load(), indexer.tail.Load())
|
||||||
return <-ch, nil
|
|
||||||
case <-indexer.closed:
|
|
||||||
return TxIndexProgress{}, errors.New("indexer is closed")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// close shutdown the indexer. Safe to be called for multiple times.
|
// close shutdown the indexer. Safe to be called for multiple times.
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,6 @@ func TestTxIndexer(t *testing.T) {
|
||||||
indexer := &txIndexer{
|
indexer := &txIndexer{
|
||||||
limit: 0,
|
limit: 0,
|
||||||
db: db,
|
db: db,
|
||||||
progress: make(chan chan TxIndexProgress),
|
|
||||||
}
|
}
|
||||||
for i, limit := range c.limits {
|
for i, limit := range c.limits {
|
||||||
indexer.limit = limit
|
indexer.limit = limit
|
||||||
|
|
@ -243,7 +242,6 @@ func TestTxIndexerRepair(t *testing.T) {
|
||||||
indexer := &txIndexer{
|
indexer := &txIndexer{
|
||||||
limit: c.limit,
|
limit: c.limit,
|
||||||
db: db,
|
db: db,
|
||||||
progress: make(chan chan TxIndexProgress),
|
|
||||||
}
|
}
|
||||||
indexer.run(chainHead, make(chan struct{}), make(chan struct{}))
|
indexer.run(chainHead, make(chan struct{}), make(chan struct{}))
|
||||||
|
|
||||||
|
|
@ -435,12 +433,8 @@ func TestTxIndexerReport(t *testing.T) {
|
||||||
limit: c.limit,
|
limit: c.limit,
|
||||||
cutoff: c.cutoff,
|
cutoff: c.cutoff,
|
||||||
db: db,
|
db: db,
|
||||||
progress: make(chan chan TxIndexProgress),
|
|
||||||
}
|
}
|
||||||
if c.tail != nil {
|
p := indexer.report(c.head, c.tail)
|
||||||
rawdb.WriteTxIndexTail(db, *c.tail)
|
|
||||||
}
|
|
||||||
p := indexer.report(c.head)
|
|
||||||
if p.Indexed != c.expIndexed {
|
if p.Indexed != c.expIndexed {
|
||||||
t.Fatalf("Unexpected indexed: %d, expected: %d", p.Indexed, c.expIndexed)
|
t.Fatalf("Unexpected indexed: %d, expected: %d", p.Indexed, c.expIndexed)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -349,22 +349,20 @@ 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.
|
// with the given transaction hash.
|
||||||
//
|
//
|
||||||
// An error will be returned if the transaction is not found, and background
|
// A null will be returned if the transaction is not found. The transaction is not
|
||||||
// indexing for transactions is still in progress. The error is used to indicate the
|
// existent from the node's perspective. This can be due to the transaction indexer
|
||||||
// scenario explicitly that the transaction might be reachable shortly.
|
// 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) {
|
||||||
// A null will be returned in the transaction is not found and background transaction
|
lookup, tx := b.eth.blockchain.GetTransactionLookup(txHash)
|
||||||
// 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 false, nil, common.Hash{}, 0, 0, err
|
|
||||||
}
|
|
||||||
if lookup == nil || tx == nil {
|
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() bool {
|
||||||
|
return b.eth.blockchain.TxIndexDone()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||||
|
|
@ -391,7 +389,7 @@ func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.S
|
||||||
return b.eth.txPool.SubscribeTransactions(ch, true)
|
return b.eth.txPool.SubscribeTransactions(ch, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) SyncProgress() ethereum.SyncProgress {
|
func (b *EthAPIBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress {
|
||||||
prog := b.eth.Downloader().Progress()
|
prog := b.eth.Downloader().Progress()
|
||||||
if txProg, err := b.eth.blockchain.TxIndexProgress(); err == nil {
|
if txProg, err := b.eth.blockchain.TxIndexProgress(); err == nil {
|
||||||
prog.TxIndexFinishedBlocks = txProg.Indexed
|
prog.TxIndexFinishedBlocks = txProg.Indexed
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,8 @@ 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) (bool, *types.Transaction, common.Hash, uint64, uint64, error)
|
GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64)
|
||||||
|
TxIndexDone() bool
|
||||||
RPCGasCap() uint64
|
RPCGasCap() uint64
|
||||||
ChainConfig() *params.ChainConfig
|
ChainConfig() *params.ChainConfig
|
||||||
Engine() consensus.Engine
|
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
|
// 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) {
|
||||||
found, _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
|
found, _, blockHash, blockNumber, index := api.backend.GetTransaction(hash)
|
||||||
if err != nil {
|
if !found {
|
||||||
|
// Warn in case tx indexer is not done.
|
||||||
|
if !api.backend.TxIndexDone() {
|
||||||
return nil, ethapi.NewTxIndexingError()
|
return nil, ethapi.NewTxIndexingError()
|
||||||
}
|
}
|
||||||
// Only mined txes are supported
|
// Only mined txes are supported
|
||||||
if !found {
|
|
||||||
return nil, errTxNotFound
|
return nil, errTxNotFound
|
||||||
}
|
}
|
||||||
// It shouldn't happen in practice.
|
// It shouldn't happen in practice.
|
||||||
|
|
|
||||||
|
|
@ -116,9 +116,13 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber)
|
||||||
return b.chain.GetBlockByNumber(uint64(number)), nil
|
return b.chain.GetBlockByNumber(uint64(number)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
|
func (b *testBackend) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||||
tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash)
|
tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash)
|
||||||
return tx != nil, tx, hash, blockNumber, index, nil
|
return tx != nil, tx, hash, blockNumber, index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *testBackend) TxIndexDone() bool {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *testBackend) RPCGasCap() uint64 {
|
func (b *testBackend) RPCGasCap() uint64 {
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ type backend interface {
|
||||||
CurrentHeader() *types.Header
|
CurrentHeader() *types.Header
|
||||||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||||
Stats() (pending int, queued int)
|
Stats() (pending int, queued int)
|
||||||
SyncProgress() ethereum.SyncProgress
|
SyncProgress(ctx context.Context) ethereum.SyncProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
// fullNodeBackend encompasses the functionality necessary for a full node
|
// fullNodeBackend encompasses the functionality necessary for a full node
|
||||||
|
|
@ -766,7 +766,7 @@ func (s *Service) reportStats(conn *connWrapper) error {
|
||||||
)
|
)
|
||||||
// check if backend is a full node
|
// check if backend is a full node
|
||||||
if fullBackend, ok := s.backend.(fullNodeBackend); ok {
|
if fullBackend, ok := s.backend.(fullNodeBackend); ok {
|
||||||
sync := fullBackend.SyncProgress()
|
sync := fullBackend.SyncProgress(context.Background())
|
||||||
syncing = !sync.Done()
|
syncing = !sync.Done()
|
||||||
|
|
||||||
price, _ := fullBackend.SuggestGasTipCap(context.Background())
|
price, _ := fullBackend.SuggestGasTipCap(context.Background())
|
||||||
|
|
@ -775,7 +775,7 @@ func (s *Service) reportStats(conn *connWrapper) error {
|
||||||
gasprice += int(basefee.Uint64())
|
gasprice += int(basefee.Uint64())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sync := s.backend.SyncProgress()
|
sync := s.backend.SyncProgress(context.Background())
|
||||||
syncing = !sync.Done()
|
syncing = !sync.Done()
|
||||||
}
|
}
|
||||||
// Assemble the node stats and send it to the server
|
// Assemble the node stats and send it to the server
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ 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
|
||||||
found, tx, blockHash, _, index, _ := t.r.backend.GetTransaction(ctx, t.hash)
|
found, tx, blockHash, _, index := t.r.backend.GetTransaction(t.hash)
|
||||||
if found {
|
if found {
|
||||||
t.tx = tx
|
t.tx = tx
|
||||||
blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false)
|
blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false)
|
||||||
|
|
@ -1530,8 +1530,8 @@ func (s *SyncState) TxIndexRemainingBlocks() hexutil.Uint64 {
|
||||||
// - healingBytecode: number of bytecodes pending
|
// - healingBytecode: number of bytecodes pending
|
||||||
// - txIndexFinishedBlocks: number of blocks whose transactions are indexed
|
// - txIndexFinishedBlocks: number of blocks whose transactions are indexed
|
||||||
// - txIndexRemainingBlocks: number of blocks whose transactions are not indexed yet
|
// - txIndexRemainingBlocks: number of blocks whose transactions are not indexed yet
|
||||||
func (r *Resolver) Syncing() (*SyncState, error) {
|
func (r *Resolver) Syncing(ctx context.Context) (*SyncState, error) {
|
||||||
progress := r.backend.SyncProgress()
|
progress := r.backend.SyncProgress(ctx)
|
||||||
|
|
||||||
// Return not syncing if the synchronisation already completed
|
// Return not syncing if the synchronisation already completed
|
||||||
if progress.Done() {
|
if progress.Done() {
|
||||||
|
|
|
||||||
|
|
@ -144,8 +144,8 @@ func (api *EthereumAPI) BlobBaseFee(ctx context.Context) *hexutil.Big {
|
||||||
// - highestBlock: block number of the highest block header this node has received from peers
|
// - highestBlock: block number of the highest block header this node has received from peers
|
||||||
// - pulledStates: number of state entries processed until now
|
// - pulledStates: number of state entries processed until now
|
||||||
// - knownStates: number of known state entries that still need to be pulled
|
// - knownStates: number of known state entries that still need to be pulled
|
||||||
func (api *EthereumAPI) Syncing() (interface{}, error) {
|
func (api *EthereumAPI) Syncing(ctx context.Context) (interface{}, error) {
|
||||||
progress := api.b.SyncProgress()
|
progress := api.b.SyncProgress(ctx)
|
||||||
|
|
||||||
// Return not syncing if the synchronisation already completed
|
// Return not syncing if the synchronisation already completed
|
||||||
if progress.Done() {
|
if progress.Done() {
|
||||||
|
|
@ -1333,17 +1333,19 @@ func (api *TransactionAPI) GetTransactionCount(ctx context.Context, address comm
|
||||||
// GetTransactionByHash returns the transaction for the given hash
|
// GetTransactionByHash returns the transaction for the given hash
|
||||||
func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
||||||
// Try to return an already finalized transaction
|
// 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 {
|
if !found {
|
||||||
// No finalized transaction, try to retrieve it from the pool
|
// No finalized transaction, try to retrieve it from the pool
|
||||||
if tx := api.b.GetPoolTransaction(hash); tx != nil {
|
if tx := api.b.GetPoolTransaction(hash); tx != nil {
|
||||||
return NewRPCPendingTransaction(tx, api.b.CurrentHeader(), api.b.ChainConfig()), nil
|
return NewRPCPendingTransaction(tx, api.b.CurrentHeader(), api.b.ChainConfig()), nil
|
||||||
}
|
}
|
||||||
if err == nil {
|
// If also not in the pool there is a chance the tx indexer is still in progress.
|
||||||
return nil, nil
|
if !api.b.TxIndexDone() {
|
||||||
}
|
|
||||||
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)
|
header, err := api.b.HeaderByHash(ctx, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -1354,27 +1356,31 @@ func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common
|
||||||
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
|
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
|
||||||
func (api *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
func (api *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
|
||||||
found, tx, _, _, _, err := api.b.GetTransaction(ctx, hash)
|
found, tx, _, _, _ := api.b.GetTransaction(hash)
|
||||||
if !found {
|
if !found {
|
||||||
if tx = api.b.GetPoolTransaction(hash); tx != nil {
|
if tx = api.b.GetPoolTransaction(hash); tx != nil {
|
||||||
return tx.MarshalBinary()
|
return tx.MarshalBinary()
|
||||||
}
|
}
|
||||||
if err == nil {
|
// If also not in the pool there is a chance the tx indexer is still in progress.
|
||||||
return nil, nil
|
if !api.b.TxIndexDone() {
|
||||||
}
|
|
||||||
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()
|
return tx.MarshalBinary()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
|
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
|
||||||
func (api *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
|
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)
|
found, tx, blockHash, blockNumber, index := api.b.GetTransaction(hash)
|
||||||
if err != nil {
|
|
||||||
return nil, NewTxIndexingError() // transaction is not fully indexed
|
|
||||||
}
|
|
||||||
if !found {
|
if !found {
|
||||||
return nil, nil // transaction is not existent or reachable
|
// Make sure indexer is done.
|
||||||
|
if !api.b.TxIndexDone() {
|
||||||
|
return nil, NewTxIndexingError()
|
||||||
|
}
|
||||||
|
// No such tx.
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
header, err := api.b.HeaderByHash(ctx, blockHash)
|
header, err := api.b.HeaderByHash(ctx, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1774,16 +1780,18 @@ 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 (api *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
func (api *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
|
||||||
found, tx, _, _, _, err := api.b.GetTransaction(ctx, hash)
|
found, tx, _, _, _ := api.b.GetTransaction(hash)
|
||||||
if !found {
|
if !found {
|
||||||
if tx = api.b.GetPoolTransaction(hash); tx != nil {
|
if tx = api.b.GetPoolTransaction(hash); tx != nil {
|
||||||
return tx.MarshalBinary()
|
return tx.MarshalBinary()
|
||||||
}
|
}
|
||||||
if err == nil {
|
// If also not in the pool there is a chance the tx indexer is still in progress.
|
||||||
return nil, nil
|
if !api.b.TxIndexDone() {
|
||||||
}
|
|
||||||
return nil, NewTxIndexingError()
|
return nil, NewTxIndexingError()
|
||||||
}
|
}
|
||||||
|
// Transaction is not found in the pool and the indexer is done.
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return tx.MarshalBinary()
|
return tx.MarshalBinary()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -472,7 +472,9 @@ func (b *testBackend) setPendingBlock(block *types.Block) {
|
||||||
b.pending = block
|
b.pending = block
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b testBackend) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} }
|
func (b testBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress {
|
||||||
|
return ethereum.SyncProgress{}
|
||||||
|
}
|
||||||
func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
return big.NewInt(0), nil
|
return big.NewInt(0), nil
|
||||||
}
|
}
|
||||||
|
|
@ -589,9 +591,12 @@ func (b testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) even
|
||||||
func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
func (b testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
|
func (b testBackend) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||||
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.db, txHash)
|
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.db, txHash)
|
||||||
return true, tx, blockHash, blockNumber, index, nil
|
return true, tx, blockHash, blockNumber, index
|
||||||
|
}
|
||||||
|
func (b testBackend) TxIndexDone() bool {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
func (b testBackend) GetPoolTransactions() (types.Transactions, error) { panic("implement me") }
|
func (b testBackend) GetPoolTransactions() (types.Transactions, error) { panic("implement me") }
|
||||||
func (b testBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { panic("implement me") }
|
func (b testBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { panic("implement me") }
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ import (
|
||||||
// both full and light clients) with access to necessary functions.
|
// both full and light clients) with access to necessary functions.
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
// General Ethereum API
|
// General Ethereum API
|
||||||
SyncProgress() ethereum.SyncProgress
|
SyncProgress(ctx context.Context) ethereum.SyncProgress
|
||||||
|
|
||||||
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
||||||
FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error)
|
FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error)
|
||||||
|
|
@ -74,7 +74,8 @@ 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) (bool, *types.Transaction, common.Hash, uint64, uint64, error)
|
GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64)
|
||||||
|
TxIndexDone() bool
|
||||||
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)
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,9 @@ func (b *backendMock) CurrentHeader() *types.Header { return b.current }
|
||||||
func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config }
|
func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config }
|
||||||
|
|
||||||
// Other methods needed to implement Backend interface.
|
// Other methods needed to implement Backend interface.
|
||||||
func (b *backendMock) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} }
|
func (b *backendMock) SyncProgress(ctx context.Context) ethereum.SyncProgress {
|
||||||
|
return ethereum.SyncProgress{}
|
||||||
|
}
|
||||||
func (b *backendMock) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) {
|
func (b *backendMock) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) {
|
||||||
return nil, nil, nil, nil, nil, nil, nil
|
return nil, nil, nil, nil, nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
@ -378,9 +380,10 @@ func (b *backendMock) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) eve
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (b *backendMock) SendTx(ctx context.Context, signedTx *types.Transaction) error { return nil }
|
func (b *backendMock) SendTx(ctx context.Context, signedTx *types.Transaction) error { return nil }
|
||||||
func (b *backendMock) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
|
func (b *backendMock) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||||
return false, nil, [32]byte{}, 0, 0, nil
|
return false, nil, [32]byte{}, 0, 0
|
||||||
}
|
}
|
||||||
|
func (b *backendMock) TxIndexDone() bool { return true }
|
||||||
func (b *backendMock) GetPoolTransactions() (types.Transactions, error) { return nil, nil }
|
func (b *backendMock) GetPoolTransactions() (types.Transactions, error) { return nil, nil }
|
||||||
func (b *backendMock) GetPoolTransaction(txHash common.Hash) *types.Transaction { return nil }
|
func (b *backendMock) GetPoolTransaction(txHash common.Hash) *types.Transaction { return nil }
|
||||||
func (b *backendMock) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
func (b *backendMock) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue