From 019a6b492163a48a811c1abd0d8dee7bf10bd092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20IRMAK?= Date: Tue, 17 Jun 2025 21:35:09 +0300 Subject: [PATCH] core: index more about txs --- core/blockchain.go | 22 ++-- core/rawdb/accessors_indexes.go | 58 +++++++--- core/rawdb/accessors_indexes_test.go | 18 ++- core/rawdb/chain_iterator.go | 15 +-- core/rawdb/chain_iterator_test.go | 10 +- core/txindexer.go | 160 +++++++++++++++++++++------ core/txindexer_test.go | 57 +++++++--- core/types/transaction.go | 7 ++ 8 files changed, 260 insertions(+), 87 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 25b37b5cce..d58aedf086 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -146,7 +146,11 @@ const ( // The following incompatible database changes were added: // * Total difficulty has been removed from both the key-value store and the ancient store. // * The metadata structure of freezer is changed by adding 'flushOffset' - BlockChainVersion uint64 = 9 + // + // - Version 10 + // The following incompatible database changes were added: + // * todo(omer) + BlockChainVersion uint64 = 10 ) // BlockChainConfig contains the configuration of the BlockChain object. @@ -1127,7 +1131,7 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error { if err := batch.Write(); err != nil { log.Crit("Failed to write genesis block", "err", err) } - bc.writeHeadBlock(genesis) + bc.writeHeadBlock(genesis, types.Receipts{}) // Last update all in-memory chain markers bc.genesisBlock = genesis @@ -1185,13 +1189,15 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { // or if they are on a different side chain. // // Note, this function assumes that the `mu` mutex is held! -func (bc *BlockChain) writeHeadBlock(block *types.Block) { +func (bc *BlockChain) writeHeadBlock(block *types.Block, receipts types.Receipts) { // Add the block to the canonical chain number scheme and mark as the head batch := bc.db.NewBatch() rawdb.WriteHeadHeaderHash(batch, block.Hash()) rawdb.WriteHeadFastBlockHash(batch, block.Hash()) rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64()) - rawdb.WriteTxLookupEntriesByBlock(batch, block) + if bc.txIndexer != nil { + bc.txIndexer.indexHead(batch, block, receipts) + } rawdb.WriteHeadBlockHash(batch, block.Hash()) // Flush the whole batch into the disk, exit the node if failed @@ -1536,7 +1542,7 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error { return err } } - bc.writeHeadBlock(block) + bc.writeHeadBlock(block, bc.GetReceiptsByHash(block.Hash())) return nil } @@ -1638,7 +1644,7 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types } // Set new head. - bc.writeHeadBlock(block) + bc.writeHeadBlock(block, receipts) bc.chainFeed.Send(ChainEvent{Header: block.Header()}) if len(logs) > 0 { @@ -2430,7 +2436,7 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error rebirthLogs = nil } // Update the head block - bc.writeHeadBlock(block) + bc.writeHeadBlock(block, bc.GetReceiptsByHash(block.Hash())) } if len(rebirthLogs) > 0 { bc.logsFeed.Send(rebirthLogs) @@ -2505,7 +2511,7 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) { return common.Hash{}, err } } - bc.writeHeadBlock(head) + bc.writeHeadBlock(head, bc.GetReceiptsByHash(head.Hash())) // Emit events logs := bc.collectLogs(head, false) diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go index 3ee85b38ad..28e73b6c89 100644 --- a/core/rawdb/accessors_indexes.go +++ b/core/rawdb/accessors_indexes.go @@ -34,6 +34,10 @@ import ( // DecodeTxLookupEntry decodes the supplied tx lookup data. func DecodeTxLookupEntry(data []byte, db ethdb.Reader) *uint64 { + var index TxIndex + if err := rlp.DecodeBytes(data, &index); err == nil { + return &index.BlockNumber + } // Database v6 tx lookup just stores the block number if len(data) < common.HashLength { number := new(big.Int).SetBytes(data).Uint64() @@ -62,29 +66,53 @@ func ReadTxLookupEntry(db ethdb.Reader, hash common.Hash) *uint64 { return DecodeTxLookupEntry(data, db) } +// ReadTxIndex retrieves the full index data if exists +func ReadTxIndex(db ethdb.Reader, hash common.Hash) *TxIndex { + data, _ := db.Get(txLookupKey(hash)) + var index TxIndex + if err := rlp.DecodeBytes(data, &index); err != nil { + return nil + } + return &index +} + // writeTxLookupEntry stores a positional metadata for a transaction, // enabling hash based transaction and receipt lookups. -func writeTxLookupEntry(db ethdb.KeyValueWriter, hash common.Hash, numberBytes []byte) { - if err := db.Put(txLookupKey(hash), numberBytes); err != nil { +func writeTxLookupEntry(db ethdb.KeyValueWriter, hash common.Hash, bytes []byte) { + if err := db.Put(txLookupKey(hash), bytes); err != nil { log.Crit("Failed to store transaction lookup entry", "err", err) } } -// WriteTxLookupEntries is identical to WriteTxLookupEntry, but it works on -// a list of hashes -func WriteTxLookupEntries(db ethdb.KeyValueWriter, number uint64, hashes []common.Hash) { - numberBytes := new(big.Int).SetUint64(number).Bytes() - for _, hash := range hashes { - writeTxLookupEntry(db, hash, numberBytes) - } +// TxIndex is the collection of data that is stored per transaction for +// speeding up hashed based lookups +type TxIndex struct { + Type uint8 // Transaction Type + Nonce uint64 // Transaction Nonce + BlockNumber uint64 + BlockHash common.Hash + BlockTime uint64 + BaseFee *big.Int + TxIndex uint32 + Sender common.Address + EffectiveGasPrice *big.Int + GasUsed uint64 + LogIndex uint32 + To *common.Address `rlp:"optional"` + BlobGas uint64 `rlp:"optional"` + BlobGasPrice *big.Int `rlp:"optional"` } -// WriteTxLookupEntriesByBlock stores a positional metadata for every transaction from -// a block, enabling hash based transaction and receipt lookups. -func WriteTxLookupEntriesByBlock(db ethdb.KeyValueWriter, block *types.Block) { - numberBytes := block.Number().Bytes() - for _, tx := range block.Transactions() { - writeTxLookupEntry(db, tx.Hash(), numberBytes) +// WriteTxLookupEntries stores a positional metadata for a set of transactions +// enabling hash based transaction and receipt lookups. +func WriteTxLookupEntries(db ethdb.KeyValueWriter, hashes []common.Hash, indexes []TxIndex) { + for index, hash := range hashes { + indexBytes, err := rlp.EncodeToBytes(indexes[index]) + if err != nil { + log.Error("Failed to encode tx index", "error", err) + return + } + writeTxLookupEntry(db, hash, indexBytes) } } diff --git a/core/rawdb/accessors_indexes_test.go b/core/rawdb/accessors_indexes_test.go index f45264b52b..399f206b15 100644 --- a/core/rawdb/accessors_indexes_test.go +++ b/core/rawdb/accessors_indexes_test.go @@ -38,10 +38,26 @@ func TestLookupStorage(t *testing.T) { name string writeTxLookupEntriesByBlock func(ethdb.KeyValueWriter, *types.Block) }{ + { + "DatabaseV10", + func(db ethdb.KeyValueWriter, block *types.Block) { + var ( + txHashes []common.Hash + indexes []TxIndex + ) + for txIndex, tx := range block.Transactions() { + txHashes = append(txHashes, tx.Hash()) + indexes = append(indexes, TxIndex{BlockNumber: block.NumberU64(), TxIndex: uint32(txIndex)}) + } + WriteTxLookupEntries(db, txHashes, indexes) + }, + }, { "DatabaseV6", func(db ethdb.KeyValueWriter, block *types.Block) { - WriteTxLookupEntriesByBlock(db, block) + for _, tx := range block.Transactions() { + db.Put(txLookupKey(tx.Hash()), block.Number().Bytes()) + } }, }, { diff --git a/core/rawdb/chain_iterator.go b/core/rawdb/chain_iterator.go index 35a1ff8874..ed7cc8a716 100644 --- a/core/rawdb/chain_iterator.go +++ b/core/rawdb/chain_iterator.go @@ -17,7 +17,6 @@ package rawdb import ( - "encoding/binary" "time" "github.com/ethereum/go-ethereum/common" @@ -94,12 +93,8 @@ func PruneTransactionIndex(db ethdb.Database, pruneBlock uint64) { if count%10000000 == 0 { log.Info("Pruning tx index", "count", count, "removed", removed) } - if len(v) > 8 { - log.Error("Skipping legacy tx index entry", "hash", txhash) - return false - } - bn := decodeNumber(v) - if bn < pruneBlock { + bn := DecodeTxLookupEntry(v, db) + if *bn < pruneBlock { removed++ return true } @@ -107,9 +102,3 @@ func PruneTransactionIndex(db ethdb.Database, pruneBlock uint64) { }) WriteTxIndexTail(db, pruneBlock) } - -func decodeNumber(b []byte) uint64 { - var numBuffer [8]byte - copy(numBuffer[8-len(b):], b) - return binary.BigEndian.Uint64(numBuffer[:]) -} diff --git a/core/rawdb/chain_iterator_test.go b/core/rawdb/chain_iterator_test.go index ecc35d9479..edcdcda2c6 100644 --- a/core/rawdb/chain_iterator_test.go +++ b/core/rawdb/chain_iterator_test.go @@ -76,7 +76,15 @@ func TestPruneTransactionIndex(t *testing.T) { pruneBlock := lastBlock - 3 for i := uint64(0); i <= lastBlock; i++ { - WriteTxLookupEntriesByBlock(chainDB, blocks[i]) + var ( + txHashes []common.Hash + indexes []TxIndex + ) + for txIndex, tx := range blocks[i].Transactions() { + txHashes = append(txHashes, tx.Hash()) + indexes = append(indexes, TxIndex{BlockNumber: blocks[i].NumberU64(), TxIndex: uint32(txIndex)}) + } + WriteTxLookupEntries(chainDB, txHashes, indexes) } WriteTxIndexTail(chainDB, 0) diff --git a/core/txindexer.go b/core/txindexer.go index 5658d716b9..36ec32d1c7 100644 --- a/core/txindexer.go +++ b/core/txindexer.go @@ -18,16 +18,19 @@ package core import ( "fmt" + "math/big" "runtime" "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/prque" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -67,6 +70,7 @@ type txIndexer struct { // be pruned and not available locally. cutoff uint64 db ethdb.Database + config *params.ChainConfig term chan chan struct{} closed chan struct{} } @@ -78,6 +82,7 @@ func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer { limit: limit, cutoff: cutoff, db: chain.db, + config: chain.chainConfig, term: make(chan chan struct{}), closed: make(chan struct{}), } @@ -337,20 +342,62 @@ func (indexer *txIndexer) close() { } } -type blockTxHashes struct { - number uint64 - hashes []common.Hash +// generateTxIndex generates the data that will be stored alongside the transaction index +func (indexer *txIndexer) generateTxIndex(txs types.Transactions, header *types.Header, receipts types.Receipts) []rawdb.TxIndex { + var ( + logIndex = 0 + prevCumulativeGasUsed = uint64(0) + signer = types.MakeSigner(indexer.config, header.Number, header.Time) + indexes = make([]rawdb.TxIndex, len(txs)) + ) + + var blobGasPrice *big.Int + if header.ExcessBlobGas != nil { + blobGasPrice = eip4844.CalcBlobFee(indexer.config, header) + } + + for blockIndex, tx := range txs { + from, _ := signer.Sender(tx) + indexes[blockIndex] = rawdb.TxIndex{ + Type: tx.Type(), + Nonce: tx.Nonce(), + To: tx.To(), + + BlockNumber: header.Number.Uint64(), + BlockHash: header.Hash(), + BlockTime: header.Time, + BaseFee: header.BaseFee, + TxIndex: uint32(blockIndex), + Sender: from, + EffectiveGasPrice: tx.EffectiveGasPrice(header.BaseFee), + GasUsed: receipts[blockIndex].CumulativeGasUsed - prevCumulativeGasUsed, + LogIndex: uint32(logIndex), + BlobGas: tx.BlobGas(), + BlobGasPrice: blobGasPrice, + } + prevCumulativeGasUsed = receipts[blockIndex].CumulativeGasUsed + logIndex += len(receipts[blockIndex].Logs) + } + return indexes +} + +type blockIndexingContext struct { + number uint64 + txHashes []common.Hash + indexes []rawdb.TxIndex } // iterate iterates over all transactions in the (canon) block // number(s) given, and yields the hashes on a channel. If there is a signal // received from interrupt channel, the iteration will be aborted and result // channel will be closed. -func (indexer *txIndexer) iterate(from uint64, to uint64, reverse bool, interrupt chan struct{}) chan *blockTxHashes { +func (indexer *txIndexer) iterate(from uint64, to uint64, reverse, indexing bool, interrupt chan struct{}) chan *blockIndexingContext { // One thread sequentially reads data from db type numberRlp struct { - number uint64 - rlp rlp.RawValue + number uint64 + headerRLP rlp.RawValue + bodyRLP rlp.RawValue + receiptsRLP rlp.RawValue } if to == from { return nil @@ -360,8 +407,8 @@ func (indexer *txIndexer) iterate(from uint64, to uint64, reverse bool, interrup threads = uint64(cpus) } var ( - rlpCh = make(chan *numberRlp, threads*2) // we send raw rlp over this channel - hashesCh = make(chan *blockTxHashes, threads*2) // send hashes over hashesCh + rlpCh = make(chan *numberRlp, threads*2) // we send raw rlp over this channel + contextsCh = make(chan *blockIndexingContext, threads*2) // send indexing context over contextsCh ) // lookup runs in one instance lookup := func() { @@ -371,10 +418,20 @@ func (indexer *txIndexer) iterate(from uint64, to uint64, reverse bool, interrup } defer close(rlpCh) for n != end { - data := rawdb.ReadCanonicalBodyRLP(indexer.db, n) + blockHash := rawdb.ReadCanonicalHash(indexer.db, n) + rlps := &numberRlp{ + number: n, + bodyRLP: rawdb.ReadBodyRLP(indexer.db, blockHash, n), + } + + if indexing { + // We are indexing, read more to build the index + rlps.headerRLP = rawdb.ReadHeaderRLP(indexer.db, blockHash, n) + rlps.receiptsRLP = rawdb.ReadReceiptsRLP(indexer.db, blockHash, n) + } // Feed the block to the aggregator, or abort on interrupt select { - case rlpCh <- &numberRlp{n, data}: + case rlpCh <- rlps: case <-interrupt: return } @@ -392,26 +449,47 @@ func (indexer *txIndexer) iterate(from uint64, to uint64, reverse bool, interrup defer func() { // Last processor closes the result channel if nThreadsAlive.Add(-1) == 0 { - close(hashesCh) + close(contextsCh) } }() for data := range rlpCh { var body types.Body - if err := rlp.DecodeBytes(data.rlp, &body); err != nil { - log.Warn("Failed to decode block body", "block", data.number, "error", err) + if err := rlp.DecodeBytes(data.bodyRLP, &body); err != nil { + log.Error("Failed to decode block body", "block", data.number, "error", err) return } - var hashes []common.Hash + txHashes := make([]common.Hash, 0, len(body.Transactions)) for _, tx := range body.Transactions { - hashes = append(hashes, tx.Hash()) + txHashes = append(txHashes, tx.Hash()) } - result := &blockTxHashes{ - hashes: hashes, - number: data.number, + + result := &blockIndexingContext{ + number: data.number, + txHashes: txHashes, + } + + if indexing { + var header types.Header + if err := rlp.DecodeBytes(data.headerRLP, &header); err != nil { + log.Error("failed to decode header", "block", data.number, "err", err) + return + } + + var storageReceipts []*types.ReceiptForStorage + if err := rlp.DecodeBytes(data.receiptsRLP, &storageReceipts); err != nil { + log.Error("failed to decode receipts", "block", data.number, "err", err) + return + } + + receipts := make([]*types.Receipt, len(storageReceipts)) + for i, receipt := range storageReceipts { + receipts[i] = (*types.Receipt)(receipt) + } + result.indexes = indexer.generateTxIndex(body.Transactions, &header, receipts) } // Feed the block to the aggregator, or abort on interrupt select { - case hashesCh <- result: + case contextsCh <- result: case <-interrupt: return } @@ -421,7 +499,7 @@ func (indexer *txIndexer) iterate(from uint64, to uint64, reverse bool, interrup for i := 0; i < int(threads); i++ { go process() } - return hashesCh + return contextsCh } // index creates txlookup indices of the specified block range. @@ -438,19 +516,19 @@ func (indexer *txIndexer) index(from uint64, to uint64, interrupt chan struct{}, return } var ( - hashesCh = indexer.iterate(from, to, true, interrupt) - batch = indexer.db.NewBatch() - start = time.Now() - logged = start.Add(-7 * time.Second) + contextsCh = indexer.iterate(from, to, true, true, interrupt) + batch = indexer.db.NewBatch() + start = time.Now() + logged = start.Add(-7 * time.Second) // Since we iterate in reverse, we expect the first number to come // in to be [to-1]. Therefore, setting lastNum to means that the // queue gap-evaluation will work correctly lastNum = to - queue = prque.New[int64, *blockTxHashes](nil) + queue = prque.New[int64, *blockIndexingContext](nil) blocks, txs = 0, 0 // for stats reporting ) - for chanDelivery := range hashesCh { + for chanDelivery := range contextsCh { // Push the delivery into the queue and process contiguous ranges. // Since we iterate in reverse, so lower numbers have lower prio, and // we can use the number directly as prio marker @@ -467,9 +545,9 @@ func (indexer *txIndexer) index(from uint64, to uint64, interrupt chan struct{}, // Next block available, pop it off and index it delivery := queue.PopItem() lastNum = delivery.number - rawdb.WriteTxLookupEntries(batch, delivery.number, delivery.hashes) + rawdb.WriteTxLookupEntries(batch, delivery.txHashes, delivery.indexes) blocks++ - txs += len(delivery.hashes) + txs += len(delivery.txHashes) // If enough data was accumulated in memory or we're at the last block, dump to disk if batch.ValueSize() > ethdb.IdealBatchSize { rawdb.WriteTxIndexTail(batch, lastNum) // Also write the tail here @@ -516,19 +594,19 @@ func (indexer *txIndexer) unindex(from uint64, to uint64, interrupt chan struct{ return } var ( - hashesCh = indexer.iterate(from, to, false, interrupt) - batch = indexer.db.NewBatch() - start = time.Now() - logged = start.Add(-7 * time.Second) + contextsCh = indexer.iterate(from, to, false, false, interrupt) + batch = indexer.db.NewBatch() + start = time.Now() + logged = start.Add(-7 * time.Second) // we expect the first number to come in to be [from]. Therefore, setting // nextNum to from means that the queue gap-evaluation will work correctly nextNum = from - queue = prque.New[int64, *blockTxHashes](nil) + queue = prque.New[int64, *blockIndexingContext](nil) blocks, txs = 0, 0 // for stats reporting ) // Otherwise spin up the concurrent iterator and unindexer - for delivery := range hashesCh { + for delivery := range contextsCh { // Push the delivery into the queue and process contiguous ranges. queue.Push(delivery, -int64(delivery.number)) for !queue.Empty() { @@ -542,8 +620,8 @@ func (indexer *txIndexer) unindex(from uint64, to uint64, interrupt chan struct{ } delivery := queue.PopItem() nextNum = delivery.number + 1 - rawdb.DeleteTxLookupEntries(batch, delivery.hashes) - txs += len(delivery.hashes) + rawdb.DeleteTxLookupEntries(batch, delivery.txHashes) + txs += len(delivery.txHashes) blocks++ // If enough data was accumulated in memory or we're at the last block, dump to disk @@ -583,3 +661,13 @@ func (indexer *txIndexer) unindex(from uint64, to uint64, interrupt chan struct{ logger("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start))) } } + +// indexHead indexes given head block synchronously +func (indexer *txIndexer) indexHead(db ethdb.KeyValueWriter, block *types.Block, receipts types.Receipts) { + indexes := indexer.generateTxIndex(block.Transactions(), block.Header(), receipts) + hashes := make([]common.Hash, len(block.Transactions())) + for i, tx := range block.Transactions() { + hashes[i] = tx.Hash() + } + rawdb.WriteTxLookupEntries(db, hashes, indexes) +} diff --git a/core/txindexer_test.go b/core/txindexer_test.go index 71c78d506b..f294e59267 100644 --- a/core/txindexer_test.go +++ b/core/txindexer_test.go @@ -27,10 +27,14 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -func verifyIndexes(t *testing.T, db ethdb.Database, block *types.Block, exist bool) { - for _, tx := range block.Transactions() { +func verifyIndexes(t *testing.T, db ethdb.Database, block *types.Block, receipts types.Receipts, exist bool) { + signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.Time()) + + for blockIndex, tx := range block.Transactions() { lookup := rawdb.ReadTxLookupEntry(db, tx.Hash()) if exist && lookup == nil { t.Fatalf("missing %d %x", block.NumberU64(), tx.Hash().Hex()) @@ -38,10 +42,35 @@ func verifyIndexes(t *testing.T, db ethdb.Database, block *types.Block, exist bo if !exist && lookup != nil { t.Fatalf("unexpected %d %x", block.NumberU64(), tx.Hash().Hex()) } + + if exist { + txIndex := rawdb.ReadTxIndex(db, tx.Hash()) + require.NotNil(t, txIndex) + assert.Equal(t, tx.Type(), txIndex.Type) + assert.Equal(t, tx.Nonce(), txIndex.Nonce) + assert.Equal(t, block.NumberU64(), txIndex.BlockNumber) + assert.Equal(t, block.Hash(), txIndex.BlockHash) + assert.Equal(t, block.Time(), txIndex.BlockTime) + assert.Equal(t, block.BaseFee(), txIndex.BaseFee) + assert.Equal(t, uint32(blockIndex), txIndex.TxIndex) + + sender, _ := signer.Sender(tx) + assert.Equal(t, txIndex.Sender, sender) + + receipt := receipts[blockIndex] + assert.Equal(t, receipt.EffectiveGasPrice, txIndex.EffectiveGasPrice) + assert.Equal(t, receipt.GasUsed, txIndex.GasUsed) + if len(receipt.Logs) > 0 { + assert.Equal(t, receipt.Logs[0].Index, txIndex.LogIndex) + } + assert.Equal(t, tx.To(), txIndex.To) + assert.Equal(t, tx.BlobGas(), txIndex.BlobGas) + assert.Equal(t, receipt.BlobGasPrice, txIndex.BlobGasPrice) + } } } -func verify(t *testing.T, db ethdb.Database, blocks []*types.Block, expTail uint64) { +func verify(t *testing.T, db ethdb.Database, blocks []*types.Block, receipts []types.Receipts, expTail uint64) { tail := rawdb.ReadTxIndexTail(db) if tail == nil { t.Fatal("Failed to write tx index tail") @@ -50,11 +79,11 @@ func verify(t *testing.T, db ethdb.Database, blocks []*types.Block, expTail uint if *tail != expTail { t.Fatalf("Unexpected tx index tail, want %v, got %d", expTail, *tail) } - for _, b := range blocks { + for i, b := range blocks { if b.Number().Uint64() < *tail { - verifyIndexes(t, db, b, false) + verifyIndexes(t, db, b, receipts[i], false) } else { - verifyIndexes(t, db, b, true) + verifyIndexes(t, db, b, receipts[i], true) } } } @@ -65,7 +94,7 @@ func verifyNoIndex(t *testing.T, db ethdb.Database, blocks []*types.Block) { t.Fatalf("Unexpected tx index tail %d", *tail) } for _, b := range blocks { - verifyIndexes(t, db, b, false) + verifyIndexes(t, db, b, nil, false) } } @@ -121,13 +150,14 @@ func TestTxIndexer(t *testing.T) { // Index the initial blocks from ancient store indexer := &txIndexer{ - limit: 0, - db: db, + limit: 0, + db: db, + config: gspec.Config, } for i, limit := range c.limits { indexer.limit = limit indexer.run(chainHead, make(chan struct{}), make(chan struct{})) - verify(t, db, blocks, c.tails[i]) + verify(t, db, blocks, receipts, c.tails[i]) } db.Close() } @@ -241,8 +271,9 @@ func TestTxIndexerRepair(t *testing.T) { // Index the initial blocks from ancient store indexer := &txIndexer{ - limit: c.limit, - db: db, + limit: c.limit, + db: db, + config: gspec.Config, } indexer.run(chainHead, make(chan struct{}), make(chan struct{})) @@ -252,7 +283,7 @@ func TestTxIndexerRepair(t *testing.T) { if c.expTail == nil { verifyNoIndex(t, db, blocks) } else { - verify(t, db, blocks, *c.expTail) + verify(t, db, blocks, receipts, *c.expTail) } db.Close() } diff --git a/core/types/transaction.go b/core/types/transaction.go index 934feb7353..8a2340ff22 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -404,6 +404,13 @@ func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) i return txTip.Cmp(other) } +// EffectiveGasPrice computes the gas price paid by the transaction, given +// the inclusion block baseFee +func (tx *Transaction) EffectiveGasPrice(baseFee *big.Int) *big.Int { + var dst big.Int + return tx.inner.effectiveGasPrice(&dst, baseFee) +} + // BlobGas returns the blob gas limit of the transaction for blob transactions, 0 otherwise. func (tx *Transaction) BlobGas() uint64 { if blobtx, ok := tx.inner.(*BlobTx); ok {