mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-02-26 15:47:21 +00:00
ethapi: reduce some of the wasted effort in GetTransactionReceipt (#32021)
Towards https://github.com/ethereum/go-ethereum/issues/26974 --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com>
This commit is contained in:
parent
7c180f851c
commit
f70aaa8399
17 changed files with 487 additions and 106 deletions
|
|
@ -314,7 +314,7 @@ type BlockChain struct {
|
|||
|
||||
bodyCache *lru.Cache[common.Hash, *types.Body]
|
||||
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
|
||||
receiptsCache *lru.Cache[common.Hash, []*types.Receipt]
|
||||
receiptsCache *lru.Cache[common.Hash, []*types.Receipt] // Receipts cache with all fields derived
|
||||
blockCache *lru.Cache[common.Hash, *types.Block]
|
||||
|
||||
txLookupLock sync.RWMutex
|
||||
|
|
|
|||
|
|
@ -18,9 +18,12 @@ package core
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
|
|
@ -213,6 +216,44 @@ func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*type
|
|||
return
|
||||
}
|
||||
|
||||
// GetCanonicalReceipt allows fetching a receipt for a transaction that was
|
||||
// already looked up on the index. Notably, only receipt in canonical chain
|
||||
// is visible.
|
||||
func (bc *BlockChain) GetCanonicalReceipt(tx *types.Transaction, blockHash common.Hash, blockNumber, txIndex uint64) (*types.Receipt, error) {
|
||||
// The receipt retrieved from the cache contains all previously derived fields
|
||||
if receipts, ok := bc.receiptsCache.Get(blockHash); ok {
|
||||
if int(txIndex) >= len(receipts) {
|
||||
return nil, fmt.Errorf("receipt out of index, length: %d, index: %d", len(receipts), txIndex)
|
||||
}
|
||||
return receipts[int(txIndex)], nil
|
||||
}
|
||||
header := bc.GetHeader(blockHash, blockNumber)
|
||||
if header == nil {
|
||||
return nil, fmt.Errorf("block header is not found, %d, %x", blockNumber, blockHash)
|
||||
}
|
||||
var blobGasPrice *big.Int
|
||||
if header.ExcessBlobGas != nil {
|
||||
blobGasPrice = eip4844.CalcBlobFee(bc.chainConfig, header)
|
||||
}
|
||||
receipt, ctx, err := rawdb.ReadCanonicalRawReceipt(bc.db, blockHash, blockNumber, txIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signer := types.MakeSigner(bc.chainConfig, new(big.Int).SetUint64(blockNumber), header.Time)
|
||||
receipt.DeriveFields(signer, types.DeriveReceiptContext{
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
BlockTime: header.Time,
|
||||
BaseFee: header.BaseFee,
|
||||
BlobGasPrice: blobGasPrice,
|
||||
GasUsed: ctx.GasUsed,
|
||||
LogIndex: ctx.LogIndex,
|
||||
Tx: tx,
|
||||
TxIndex: uint(txIndex),
|
||||
})
|
||||
return receipt, nil
|
||||
}
|
||||
|
||||
// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
|
||||
func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||
if receipts, ok := bc.receiptsCache.Get(hash); ok {
|
||||
|
|
@ -277,13 +318,15 @@ 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
|
||||
// GetCanonicalTransaction retrieves the lookup along with the transaction
|
||||
// itself associate with the given transaction hash.
|
||||
//
|
||||
// A null will be returned if the transaction is not found. 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) {
|
||||
//
|
||||
// Notably, only the transaction in the canonical chain is visible.
|
||||
func (bc *BlockChain) GetCanonicalTransaction(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction) {
|
||||
bc.txLookupLock.RLock()
|
||||
defer bc.txLookupLock.RUnlock()
|
||||
|
||||
|
|
@ -291,7 +334,7 @@ func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLoo
|
|||
if item, exist := bc.txLookupCache.Get(hash); exist {
|
||||
return item.lookup, item.transaction
|
||||
}
|
||||
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
||||
tx, blockHash, blockNumber, txIndex := rawdb.ReadCanonicalTransaction(bc.db, hash)
|
||||
if tx == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,10 +25,12 @@ import (
|
|||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||
|
|
@ -46,6 +48,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// So we can deterministically seed different blockchains
|
||||
|
|
@ -1004,29 +1007,47 @@ func testChainTxReorgs(t *testing.T, scheme string) {
|
|||
|
||||
// removed tx
|
||||
for i, tx := range (types.Transactions{pastDrop, freshDrop}) {
|
||||
if txn, _, _, _ := rawdb.ReadTransaction(db, tx.Hash()); txn != nil {
|
||||
if txn, _, _, _ := rawdb.ReadCanonicalTransaction(db, tx.Hash()); txn != nil {
|
||||
t.Errorf("drop %d: tx %v found while shouldn't have been", i, txn)
|
||||
}
|
||||
if rcpt, _, _, _ := rawdb.ReadReceipt(db, tx.Hash(), blockchain.Config()); rcpt != nil {
|
||||
if rcpt, _, _, _ := rawdb.ReadCanonicalReceipt(db, tx.Hash(), blockchain.Config()); rcpt != nil {
|
||||
t.Errorf("drop %d: receipt %v found while shouldn't have been", i, rcpt)
|
||||
}
|
||||
}
|
||||
// added tx
|
||||
for i, tx := range (types.Transactions{pastAdd, freshAdd, futureAdd}) {
|
||||
if txn, _, _, _ := rawdb.ReadTransaction(db, tx.Hash()); txn == nil {
|
||||
if txn, _, _, _ := rawdb.ReadCanonicalTransaction(db, tx.Hash()); txn == nil {
|
||||
t.Errorf("add %d: expected tx to be found", i)
|
||||
}
|
||||
if rcpt, _, _, _ := rawdb.ReadReceipt(db, tx.Hash(), blockchain.Config()); rcpt == nil {
|
||||
if rcpt, _, _, index := rawdb.ReadCanonicalReceipt(db, tx.Hash(), blockchain.Config()); rcpt == nil {
|
||||
t.Errorf("add %d: expected receipt to be found", i)
|
||||
} else if rawRcpt, ctx, _ := rawdb.ReadCanonicalRawReceipt(db, rcpt.BlockHash, rcpt.BlockNumber.Uint64(), index); rawRcpt == nil {
|
||||
t.Errorf("add %d: expected raw receipt to be found", i)
|
||||
} else {
|
||||
if rcpt.GasUsed != ctx.GasUsed {
|
||||
t.Errorf("add %d, raw gasUsedSoFar doesn't make sense", i)
|
||||
}
|
||||
if len(rcpt.Logs) > 0 && rcpt.Logs[0].Index != ctx.LogIndex {
|
||||
t.Errorf("add %d, raw startingLogIndex doesn't make sense", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
// shared tx
|
||||
for i, tx := range (types.Transactions{postponed, swapped}) {
|
||||
if txn, _, _, _ := rawdb.ReadTransaction(db, tx.Hash()); txn == nil {
|
||||
if txn, _, _, _ := rawdb.ReadCanonicalTransaction(db, tx.Hash()); txn == nil {
|
||||
t.Errorf("share %d: expected tx to be found", i)
|
||||
}
|
||||
if rcpt, _, _, _ := rawdb.ReadReceipt(db, tx.Hash(), blockchain.Config()); rcpt == nil {
|
||||
if rcpt, _, _, index := rawdb.ReadCanonicalReceipt(db, tx.Hash(), blockchain.Config()); rcpt == nil {
|
||||
t.Errorf("share %d: expected receipt to be found", i)
|
||||
} else if rawRcpt, ctx, _ := rawdb.ReadCanonicalRawReceipt(db, rcpt.BlockHash, rcpt.BlockNumber.Uint64(), index); rawRcpt == nil {
|
||||
t.Errorf("add %d: expected raw receipt to be found", i)
|
||||
} else {
|
||||
if rcpt.GasUsed != ctx.GasUsed {
|
||||
t.Errorf("add %d, raw gasUsedSoFar doesn't make sense", i)
|
||||
}
|
||||
if len(rcpt.Logs) > 0 && rcpt.Logs[0].Index != ctx.LogIndex {
|
||||
t.Errorf("add %d, raw startingLogIndex doesn't make sense", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4404,6 +4425,93 @@ func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64,
|
|||
if receipts == nil || len(receipts) != 1 {
|
||||
t.Fatalf("Missed block receipts: %d, cutoff: %d", num, cutoffBlock.NumberU64())
|
||||
}
|
||||
for indx, receipt := range receipts {
|
||||
receiptByLookup, err := chain.GetCanonicalReceipt(body.Transactions[indx], receipt.BlockHash,
|
||||
receipt.BlockNumber.Uint64(), uint64(indx))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, receipt, receiptByLookup)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCanonicalReceipt(t *testing.T) {
|
||||
const chainLength = 64
|
||||
|
||||
// Configure and generate a sample block chain
|
||||
var (
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000000000000)
|
||||
gspec = &Genesis{
|
||||
Config: params.MergedTestChainConfig,
|
||||
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
engine = beacon.New(ethash.NewFaker())
|
||||
codeBin = common.FromHex("0x608060405234801561000f575f5ffd5b507f8ae1c8c6e5f91159d0bc1c4b9a47ce45301753843012cbe641e4456bfc73538b33426040516100419291906100ff565b60405180910390a1610139565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6100778261004e565b9050919050565b6100878161006d565b82525050565b5f819050919050565b61009f8161008d565b82525050565b5f82825260208201905092915050565b7f436f6e7374727563746f72207761732063616c6c6564000000000000000000005f82015250565b5f6100e96016836100a5565b91506100f4826100b5565b602082019050919050565b5f6060820190506101125f83018561007e565b61011f6020830184610096565b8181036040830152610130816100dd565b90509392505050565b603e806101455f395ff3fe60806040525f5ffdfea2646970667358221220e8bc3c31e3ac337eab702e8fdfc1c71894f4df1af4221bcde4a2823360f403fb64736f6c634300081e0033")
|
||||
)
|
||||
_, blocks, receipts := GenerateChainWithGenesis(gspec, engine, chainLength, func(i int, block *BlockGen) {
|
||||
// SPDX-License-Identifier: MIT
|
||||
// pragma solidity ^0.8.0;
|
||||
//
|
||||
// contract ConstructorLogger {
|
||||
// event ConstructorLog(address sender, uint256 timestamp, string message);
|
||||
//
|
||||
// constructor() {
|
||||
// emit ConstructorLog(msg.sender, block.timestamp, "Constructor was called");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// 608060405234801561000f575f5ffd5b507f8ae1c8c6e5f91159d0bc1c4b9a47ce45301753843012cbe641e4456bfc73538b33426040516100419291906100ff565b60405180910390a1610139565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6100778261004e565b9050919050565b6100878161006d565b82525050565b5f819050919050565b61009f8161008d565b82525050565b5f82825260208201905092915050565b7f436f6e7374727563746f72207761732063616c6c6564000000000000000000005f82015250565b5f6100e96016836100a5565b91506100f4826100b5565b602082019050919050565b5f6060820190506101125f83018561007e565b61011f6020830184610096565b8181036040830152610130816100dd565b90509392505050565b603e806101455f395ff3fe60806040525f5ffdfea2646970667358221220e8bc3c31e3ac337eab702e8fdfc1c71894f4df1af4221bcde4a2823360f403fb64736f6c634300081e0033
|
||||
nonce := block.TxNonce(address)
|
||||
tx, err := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 100_000, block.header.BaseFee, codeBin), signer, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
block.AddTx(tx)
|
||||
|
||||
tx2, err := types.SignTx(types.NewContractCreation(nonce+1, big.NewInt(0), 100_000, block.header.BaseFee, codeBin), signer, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
block.AddTx(tx2)
|
||||
|
||||
tx3, err := types.SignTx(types.NewContractCreation(nonce+2, big.NewInt(0), 100_000, block.header.BaseFee, codeBin), signer, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
block.AddTx(tx3)
|
||||
})
|
||||
|
||||
db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||
defer db.Close()
|
||||
options := DefaultConfig().WithStateScheme(rawdb.PathScheme)
|
||||
chain, _ := NewBlockChain(db, gspec, beacon.New(ethash.NewFaker()), options)
|
||||
defer chain.Stop()
|
||||
|
||||
chain.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0)
|
||||
|
||||
for i := 0; i < chainLength; i++ {
|
||||
block := blocks[i]
|
||||
blockReceipts := chain.GetReceiptsByHash(block.Hash())
|
||||
chain.receiptsCache.Purge() // ugly hack
|
||||
for txIndex, tx := range block.Body().Transactions {
|
||||
receipt, err := chain.GetCanonicalReceipt(tx, block.Hash(), block.NumberU64(), uint64(txIndex))
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(receipts[i][txIndex], receipt) {
|
||||
want := spew.Sdump(receipts[i][txIndex])
|
||||
got := spew.Sdump(receipt)
|
||||
t.Fatalf("Receipt is not matched, want %s, got: %s", want, got)
|
||||
}
|
||||
if !reflect.DeepEqual(blockReceipts[txIndex], receipt) {
|
||||
want := spew.Sdump(blockReceipts[txIndex])
|
||||
got := spew.Sdump(receipt)
|
||||
t.Fatalf("Receipt is not matched, want %s, got: %s", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -449,9 +449,10 @@ func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue
|
|||
return data
|
||||
}
|
||||
|
||||
// ReadCanonicalBodyRLP retrieves the block body (transactions and uncles) for the canonical
|
||||
// block at number, in RLP encoding.
|
||||
func ReadCanonicalBodyRLP(db ethdb.Reader, number uint64) rlp.RawValue {
|
||||
// ReadCanonicalBodyRLP retrieves the block body (transactions and uncles) for the
|
||||
// canonical block at number, in RLP encoding. Optionally it takes the block hash
|
||||
// to avoid looking it up
|
||||
func ReadCanonicalBodyRLP(db ethdb.Reader, number uint64, hash *common.Hash) rlp.RawValue {
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
data, _ = reader.Ancient(ChainFreezerBodiesTable, number)
|
||||
|
|
@ -459,10 +460,14 @@ func ReadCanonicalBodyRLP(db ethdb.Reader, number uint64) rlp.RawValue {
|
|||
return nil
|
||||
}
|
||||
// Block is not in ancients, read from leveldb by hash and number.
|
||||
// Note: ReadCanonicalHash cannot be used here because it also
|
||||
// calls ReadAncients internally.
|
||||
hash, _ := db.Get(headerHashKey(number))
|
||||
data, _ = db.Get(blockBodyKey(number, common.BytesToHash(hash)))
|
||||
if hash != nil {
|
||||
data, _ = db.Get(blockBodyKey(number, *hash))
|
||||
} else {
|
||||
// Note: ReadCanonicalHash cannot be used here because it also
|
||||
// calls ReadAncients internally.
|
||||
hashBytes, _ := db.Get(headerHashKey(number))
|
||||
data, _ = db.Get(blockBodyKey(number, common.BytesToHash(hashBytes)))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
|
|
@ -544,6 +549,29 @@ func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawVa
|
|||
return data
|
||||
}
|
||||
|
||||
// ReadCanonicalReceiptsRLP retrieves the receipts RLP for the canonical block at
|
||||
// number, in RLP encoding. Optionally it takes the block hash to avoid looking it up.
|
||||
func ReadCanonicalReceiptsRLP(db ethdb.Reader, number uint64, hash *common.Hash) rlp.RawValue {
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
data, _ = reader.Ancient(ChainFreezerReceiptTable, number)
|
||||
if len(data) > 0 {
|
||||
return nil
|
||||
}
|
||||
// Block is not in ancients, read from leveldb by hash and number.
|
||||
if hash != nil {
|
||||
data, _ = db.Get(blockReceiptsKey(number, *hash))
|
||||
} else {
|
||||
// Note: ReadCanonicalHash cannot be used here because it also
|
||||
// calls ReadAncients internally.
|
||||
hashBytes, _ := db.Get(headerHashKey(number))
|
||||
data, _ = db.Get(blockReceiptsKey(number, common.BytesToHash(hashBytes)))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
// ReadRawReceipts retrieves all the transaction receipts belonging to a block.
|
||||
// The receipt metadata fields and the Bloom are not guaranteed to be populated,
|
||||
// so they should not be used. Use ReadReceipts instead if the metadata is needed.
|
||||
|
|
|
|||
|
|
@ -441,6 +441,9 @@ func TestAncientStorage(t *testing.T) {
|
|||
if blob := ReadReceiptsRLP(db, hash, number); len(blob) > 0 {
|
||||
t.Fatalf("non existent receipts returned")
|
||||
}
|
||||
if blob := ReadCanonicalReceiptsRLP(db, number, &hash); len(blob) > 0 {
|
||||
t.Fatalf("non existent receipts returned")
|
||||
}
|
||||
|
||||
// Write and verify the header in the database
|
||||
WriteAncientBlocks(db, []*types.Block{block}, types.EncodeBlockReceiptLists([]types.Receipts{nil}))
|
||||
|
|
@ -454,6 +457,9 @@ func TestAncientStorage(t *testing.T) {
|
|||
if blob := ReadReceiptsRLP(db, hash, number); len(blob) == 0 {
|
||||
t.Fatalf("no receipts returned")
|
||||
}
|
||||
if blob := ReadCanonicalReceiptsRLP(db, number, &hash); len(blob) == 0 {
|
||||
t.Fatalf("no receipts returned")
|
||||
}
|
||||
|
||||
// Use a fake hash for data retrieval, nothing should be returned.
|
||||
fakeHash := common.BytesToHash([]byte{0x01, 0x02, 0x03})
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -169,9 +170,10 @@ func findTxInBlockBody(blockbody rlp.RawValue, target common.Hash) (*types.Trans
|
|||
return nil, 0, errors.New("transaction not found")
|
||||
}
|
||||
|
||||
// ReadTransaction retrieves a specific transaction from the database, along with
|
||||
// its added positional metadata.
|
||||
func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
|
||||
// ReadCanonicalTransaction retrieves a specific transaction from the database, along
|
||||
// with its added positional metadata. Notably, only the transaction in the canonical
|
||||
// chain is visible.
|
||||
func ReadCanonicalTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
|
||||
blockNumber := ReadTxLookupEntry(db, hash)
|
||||
if blockNumber == nil {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
|
|
@ -180,7 +182,7 @@ func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, com
|
|||
if blockHash == (common.Hash{}) {
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
bodyRLP := ReadBodyRLP(db, blockHash, *blockNumber)
|
||||
bodyRLP := ReadCanonicalBodyRLP(db, *blockNumber, &blockHash)
|
||||
if bodyRLP == nil {
|
||||
log.Error("Transaction referenced missing", "number", *blockNumber, "hash", blockHash)
|
||||
return nil, common.Hash{}, 0, 0
|
||||
|
|
@ -193,9 +195,10 @@ func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, com
|
|||
return tx, blockHash, *blockNumber, txIndex
|
||||
}
|
||||
|
||||
// ReadReceipt retrieves a specific transaction receipt from the database, along with
|
||||
// its added positional metadata.
|
||||
func ReadReceipt(db ethdb.Reader, hash common.Hash, config *params.ChainConfig) (*types.Receipt, common.Hash, uint64, uint64) {
|
||||
// ReadCanonicalReceipt retrieves a specific transaction receipt from the database,
|
||||
// along with its added positional metadata. Notably, only the receipt in the canonical
|
||||
// chain is visible.
|
||||
func ReadCanonicalReceipt(db ethdb.Reader, hash common.Hash, config *params.ChainConfig) (*types.Receipt, common.Hash, uint64, uint64) {
|
||||
// Retrieve the context of the receipt based on the transaction hash
|
||||
blockNumber := ReadTxLookupEntry(db, hash)
|
||||
if blockNumber == nil {
|
||||
|
|
@ -220,7 +223,91 @@ func ReadReceipt(db ethdb.Reader, hash common.Hash, config *params.ChainConfig)
|
|||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
|
||||
// ReadFilterMapRow retrieves a filter map row at the given mapRowIndex
|
||||
// extractReceiptFields takes a raw RLP-encoded receipt blob and extracts
|
||||
// specific fields from it.
|
||||
func extractReceiptFields(receiptRLP rlp.RawValue) (uint64, uint, error) {
|
||||
receiptList, _, err := rlp.SplitList(receiptRLP)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
// Decode the field: receipt status
|
||||
// for receipt before the byzantium fork:
|
||||
// - bytes: post state root
|
||||
// for receipt after the byzantium fork:
|
||||
// - bytes: receipt status flag
|
||||
_, _, rest, err := rlp.Split(receiptList)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
// Decode the field: cumulative gas used (type: uint64)
|
||||
gasUsed, rest, err := rlp.SplitUint64(rest)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
// Decode the field: logs (type: rlp list)
|
||||
logList, _, err := rlp.SplitList(rest)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
logCount, err := rlp.CountValues(logList)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return gasUsed, uint(logCount), nil
|
||||
}
|
||||
|
||||
// RawReceiptContext carries the contextual information that is needed to derive
|
||||
// a complete receipt from a raw one.
|
||||
type RawReceiptContext struct {
|
||||
GasUsed uint64 // Amount of gas used by the associated transaction
|
||||
LogIndex uint // Starting index of the logs within the block
|
||||
}
|
||||
|
||||
// ReadCanonicalRawReceipt reads a raw receipt at the specified position. It also
|
||||
// returns the gas used by the associated transaction and the starting index of
|
||||
// the logs within the block. The main difference with ReadCanonicalReceipt is
|
||||
// that the additional positional fields are not directly included in the receipt.
|
||||
// Notably, only receipts from the canonical chain are visible.
|
||||
func ReadCanonicalRawReceipt(db ethdb.Reader, blockHash common.Hash, blockNumber, txIndex uint64) (*types.Receipt, RawReceiptContext, error) {
|
||||
receiptIt, err := rlp.NewListIterator(ReadCanonicalReceiptsRLP(db, blockNumber, &blockHash))
|
||||
if err != nil {
|
||||
return nil, RawReceiptContext{}, err
|
||||
}
|
||||
var (
|
||||
cumulativeGasUsed uint64
|
||||
logIndex uint
|
||||
)
|
||||
for i := uint64(0); i <= txIndex; i++ {
|
||||
// Unexpected iteration error
|
||||
if receiptIt.Err() != nil {
|
||||
return nil, RawReceiptContext{}, receiptIt.Err()
|
||||
}
|
||||
// Unexpected end of iteration
|
||||
if !receiptIt.Next() {
|
||||
return nil, RawReceiptContext{}, fmt.Errorf("receipt not found, %d, %x, %d", blockNumber, blockHash, txIndex)
|
||||
}
|
||||
if i == txIndex {
|
||||
var stored types.ReceiptForStorage
|
||||
if err := rlp.DecodeBytes(receiptIt.Value(), &stored); err != nil {
|
||||
return nil, RawReceiptContext{}, err
|
||||
}
|
||||
return (*types.Receipt)(&stored), RawReceiptContext{
|
||||
GasUsed: stored.CumulativeGasUsed - cumulativeGasUsed,
|
||||
LogIndex: logIndex,
|
||||
}, nil
|
||||
} else {
|
||||
gas, logs, err := extractReceiptFields(receiptIt.Value())
|
||||
if err != nil {
|
||||
return nil, RawReceiptContext{}, err
|
||||
}
|
||||
cumulativeGasUsed = gas
|
||||
logIndex += logs
|
||||
}
|
||||
}
|
||||
return nil, RawReceiptContext{}, fmt.Errorf("receipt not found, %d, %x, %d", blockNumber, blockHash, txIndex)
|
||||
}
|
||||
|
||||
// ReadFilterMapExtRow retrieves a filter map row at the given mapRowIndex
|
||||
// (see filtermaps.mapRowIndex for the storage index encoding).
|
||||
// Note that zero length rows are not stored in the database and therefore all
|
||||
// non-existent entries are interpreted as empty rows and return no error.
|
||||
|
|
@ -247,7 +334,7 @@ func ReadFilterMapExtRow(db ethdb.KeyValueReader, mapRowIndex uint64, bitLength
|
|||
return nil, err
|
||||
}
|
||||
if len(encRow)%byteLength != 0 {
|
||||
return nil, errors.New("Invalid encoded extended filter row length")
|
||||
return nil, errors.New("invalid encoded extended filter row length")
|
||||
}
|
||||
row := make([]uint32, len(encRow)/byteLength)
|
||||
var b [4]byte
|
||||
|
|
@ -318,7 +405,7 @@ func ReadFilterMapBaseRows(db ethdb.KeyValueReader, mapRowIndex uint64, rowCount
|
|||
return rows, nil
|
||||
}
|
||||
|
||||
// WriteFilterMapRow stores a filter map row at the given mapRowIndex or deletes
|
||||
// WriteFilterMapExtRow stores a filter map row at the given mapRowIndex or deletes
|
||||
// any existing entry if the row is empty.
|
||||
func WriteFilterMapExtRow(db ethdb.KeyValueWriter, mapRowIndex uint64, row []uint32, bitLength uint) {
|
||||
byteLength := int(bitLength) / 8
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package rawdb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
|
|
@ -88,7 +89,7 @@ func TestLookupStorage(t *testing.T) {
|
|||
|
||||
// Check that no transactions entries are in a pristine database
|
||||
for i, tx := range txs {
|
||||
if txn, _, _, _ := ReadTransaction(db, tx.Hash()); txn != nil {
|
||||
if txn, _, _, _ := ReadCanonicalTransaction(db, tx.Hash()); txn != nil {
|
||||
t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn)
|
||||
}
|
||||
}
|
||||
|
|
@ -98,7 +99,7 @@ func TestLookupStorage(t *testing.T) {
|
|||
tc.writeTxLookupEntriesByBlock(db, block)
|
||||
|
||||
for i, tx := range txs {
|
||||
if txn, hash, number, index := ReadTransaction(db, tx.Hash()); txn == nil {
|
||||
if txn, hash, number, index := ReadCanonicalTransaction(db, tx.Hash()); txn == nil {
|
||||
t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash())
|
||||
} else {
|
||||
if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) {
|
||||
|
|
@ -112,7 +113,7 @@ func TestLookupStorage(t *testing.T) {
|
|||
// Delete the transactions and check purge
|
||||
for i, tx := range txs {
|
||||
DeleteTxLookupEntry(db, tx.Hash())
|
||||
if txn, _, _, _ := ReadTransaction(db, tx.Hash()); txn != nil {
|
||||
if txn, _, _, _ := ReadCanonicalTransaction(db, tx.Hash()); txn != nil {
|
||||
t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn)
|
||||
}
|
||||
}
|
||||
|
|
@ -219,3 +220,80 @@ func TestFindTxInBlockBody(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractReceiptFields(t *testing.T) {
|
||||
receiptWithPostState := types.ReceiptForStorage(types.Receipt{
|
||||
Type: types.LegacyTxType,
|
||||
PostState: []byte{0x1, 0x2, 0x3},
|
||||
CumulativeGasUsed: 100,
|
||||
})
|
||||
receiptWithPostStateBlob, _ := rlp.EncodeToBytes(&receiptWithPostState)
|
||||
|
||||
receiptNoLogs := types.ReceiptForStorage(types.Receipt{
|
||||
Type: types.LegacyTxType,
|
||||
Status: types.ReceiptStatusSuccessful,
|
||||
CumulativeGasUsed: 100,
|
||||
})
|
||||
receiptNoLogBlob, _ := rlp.EncodeToBytes(&receiptNoLogs)
|
||||
|
||||
receiptWithLogs := types.ReceiptForStorage(types.Receipt{
|
||||
Type: types.LegacyTxType,
|
||||
Status: types.ReceiptStatusSuccessful,
|
||||
CumulativeGasUsed: 100,
|
||||
Logs: []*types.Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x1}),
|
||||
Topics: []common.Hash{
|
||||
common.BytesToHash([]byte{0x1}),
|
||||
},
|
||||
Data: []byte{0x1},
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x2}),
|
||||
Topics: []common.Hash{
|
||||
common.BytesToHash([]byte{0x2}),
|
||||
},
|
||||
Data: []byte{0x2},
|
||||
},
|
||||
},
|
||||
})
|
||||
receiptWithLogBlob, _ := rlp.EncodeToBytes(&receiptWithLogs)
|
||||
|
||||
invalidReceipt := types.ReceiptForStorage(types.Receipt{
|
||||
Type: types.LegacyTxType,
|
||||
Status: types.ReceiptStatusSuccessful,
|
||||
CumulativeGasUsed: 100,
|
||||
})
|
||||
invalidReceiptBlob, _ := rlp.EncodeToBytes(&invalidReceipt)
|
||||
invalidReceiptBlob[len(invalidReceiptBlob)-1] = 0xf
|
||||
|
||||
var cases = []struct {
|
||||
logs rlp.RawValue
|
||||
expErr error
|
||||
expGasUsed uint64
|
||||
expLogs uint
|
||||
}{
|
||||
{receiptWithPostStateBlob, nil, 100, 0},
|
||||
{receiptNoLogBlob, nil, 100, 0},
|
||||
{receiptWithLogBlob, nil, 100, 2},
|
||||
{invalidReceiptBlob, rlp.ErrExpectedList, 100, 0},
|
||||
}
|
||||
for _, c := range cases {
|
||||
gasUsed, logs, err := extractReceiptFields(c.logs)
|
||||
if c.expErr != nil {
|
||||
if !errors.Is(err, c.expErr) {
|
||||
t.Fatalf("Unexpected error, want: %v, got: %v", c.expErr, err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error %v", err)
|
||||
}
|
||||
if gasUsed != c.expGasUsed {
|
||||
t.Fatalf("Unexpected gas used, want %d, got %d", c.expGasUsed, gasUsed)
|
||||
}
|
||||
if logs != c.expLogs {
|
||||
t.Fatalf("Unexpected logs, want %d, got %d", c.expLogs, logs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool
|
|||
}
|
||||
defer close(rlpCh)
|
||||
for n != end {
|
||||
data := ReadCanonicalBodyRLP(db, n)
|
||||
data := ReadCanonicalBodyRLP(db, n, nil)
|
||||
// Feed the block to the aggregator, or abort on interrupt
|
||||
select {
|
||||
case rlpCh <- &numberRlp{n, data}:
|
||||
|
|
|
|||
|
|
@ -258,6 +258,62 @@ func (r *Receipt) Size() common.StorageSize {
|
|||
return size
|
||||
}
|
||||
|
||||
// DeriveReceiptContext holds the contextual information needed to derive a receipt
|
||||
type DeriveReceiptContext struct {
|
||||
BlockHash common.Hash
|
||||
BlockNumber uint64
|
||||
BlockTime uint64
|
||||
BaseFee *big.Int
|
||||
BlobGasPrice *big.Int
|
||||
GasUsed uint64
|
||||
LogIndex uint // Number of logs in the block until this receipt
|
||||
Tx *Transaction
|
||||
TxIndex uint
|
||||
}
|
||||
|
||||
// DeriveFields fills the receipt with computed fields based on consensus
|
||||
// data and contextual infos like containing block and transactions.
|
||||
func (r *Receipt) DeriveFields(signer Signer, context DeriveReceiptContext) {
|
||||
// The transaction type and hash can be retrieved from the transaction itself
|
||||
r.Type = context.Tx.Type()
|
||||
r.TxHash = context.Tx.Hash()
|
||||
r.GasUsed = context.GasUsed
|
||||
r.EffectiveGasPrice = context.Tx.inner.effectiveGasPrice(new(big.Int), context.BaseFee)
|
||||
|
||||
// EIP-4844 blob transaction fields
|
||||
if context.Tx.Type() == BlobTxType {
|
||||
r.BlobGasUsed = context.Tx.BlobGas()
|
||||
r.BlobGasPrice = context.BlobGasPrice
|
||||
}
|
||||
|
||||
// Block location fields
|
||||
r.BlockHash = context.BlockHash
|
||||
r.BlockNumber = new(big.Int).SetUint64(context.BlockNumber)
|
||||
r.TransactionIndex = context.TxIndex
|
||||
|
||||
// The contract address can be derived from the transaction itself
|
||||
if context.Tx.To() == nil {
|
||||
// Deriving the signer is expensive, only do if it's actually needed
|
||||
from, _ := Sender(signer, context.Tx)
|
||||
r.ContractAddress = crypto.CreateAddress(from, context.Tx.Nonce())
|
||||
} else {
|
||||
r.ContractAddress = common.Address{}
|
||||
}
|
||||
// The derived log fields can simply be set from the block and transaction
|
||||
logIndex := context.LogIndex
|
||||
for j := 0; j < len(r.Logs); j++ {
|
||||
r.Logs[j].BlockNumber = context.BlockNumber
|
||||
r.Logs[j].BlockHash = context.BlockHash
|
||||
r.Logs[j].BlockTimestamp = context.BlockTime
|
||||
r.Logs[j].TxHash = r.TxHash
|
||||
r.Logs[j].TxIndex = context.TxIndex
|
||||
r.Logs[j].Index = logIndex
|
||||
logIndex++
|
||||
}
|
||||
// Also derive the Bloom if not derived yet
|
||||
r.Bloom = CreateBloom(r)
|
||||
}
|
||||
|
||||
// ReceiptForStorage is a wrapper around a Receipt with RLP serialization
|
||||
// that omits the Bloom field. The Bloom field is recomputed by DeriveFields.
|
||||
type ReceiptForStorage Receipt
|
||||
|
|
@ -323,58 +379,30 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
|
|||
|
||||
// DeriveFields fills the receipts with their computed fields based on consensus
|
||||
// data and contextual infos like containing block and transactions.
|
||||
func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, time uint64, baseFee *big.Int, blobGasPrice *big.Int, txs []*Transaction) error {
|
||||
signer := MakeSigner(config, new(big.Int).SetUint64(number), time)
|
||||
func (rs Receipts) DeriveFields(config *params.ChainConfig, blockHash common.Hash, blockNumber uint64, blockTime uint64, baseFee *big.Int, blobGasPrice *big.Int, txs []*Transaction) error {
|
||||
signer := MakeSigner(config, new(big.Int).SetUint64(blockNumber), blockTime)
|
||||
|
||||
logIndex := uint(0)
|
||||
if len(txs) != len(rs) {
|
||||
return errors.New("transaction and receipt count mismatch")
|
||||
}
|
||||
for i := 0; i < len(rs); i++ {
|
||||
// The transaction type and hash can be retrieved from the transaction itself
|
||||
rs[i].Type = txs[i].Type()
|
||||
rs[i].TxHash = txs[i].Hash()
|
||||
rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), baseFee)
|
||||
|
||||
// EIP-4844 blob transaction fields
|
||||
if txs[i].Type() == BlobTxType {
|
||||
rs[i].BlobGasUsed = txs[i].BlobGas()
|
||||
rs[i].BlobGasPrice = blobGasPrice
|
||||
var cumulativeGasUsed uint64
|
||||
if i > 0 {
|
||||
cumulativeGasUsed = rs[i-1].CumulativeGasUsed
|
||||
}
|
||||
|
||||
// block location fields
|
||||
rs[i].BlockHash = hash
|
||||
rs[i].BlockNumber = new(big.Int).SetUint64(number)
|
||||
rs[i].TransactionIndex = uint(i)
|
||||
|
||||
// The contract address can be derived from the transaction itself
|
||||
if txs[i].To() == nil {
|
||||
// Deriving the signer is expensive, only do if it's actually needed
|
||||
from, _ := Sender(signer, txs[i])
|
||||
rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
|
||||
} else {
|
||||
rs[i].ContractAddress = common.Address{}
|
||||
}
|
||||
|
||||
// The used gas can be calculated based on previous r
|
||||
if i == 0 {
|
||||
rs[i].GasUsed = rs[i].CumulativeGasUsed
|
||||
} else {
|
||||
rs[i].GasUsed = rs[i].CumulativeGasUsed - rs[i-1].CumulativeGasUsed
|
||||
}
|
||||
|
||||
// The derived log fields can simply be set from the block and transaction
|
||||
for j := 0; j < len(rs[i].Logs); j++ {
|
||||
rs[i].Logs[j].BlockNumber = number
|
||||
rs[i].Logs[j].BlockHash = hash
|
||||
rs[i].Logs[j].BlockTimestamp = time
|
||||
rs[i].Logs[j].TxHash = rs[i].TxHash
|
||||
rs[i].Logs[j].TxIndex = uint(i)
|
||||
rs[i].Logs[j].Index = logIndex
|
||||
logIndex++
|
||||
}
|
||||
// also derive the Bloom if not derived yet
|
||||
rs[i].Bloom = CreateBloom(rs[i])
|
||||
rs[i].DeriveFields(signer, DeriveReceiptContext{
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
BlockTime: blockTime,
|
||||
BaseFee: baseFee,
|
||||
BlobGasPrice: blobGasPrice,
|
||||
GasUsed: rs[i].CumulativeGasUsed - cumulativeGasUsed,
|
||||
LogIndex: logIndex,
|
||||
Tx: txs[i],
|
||||
TxIndex: uint(i),
|
||||
})
|
||||
logIndex += uint(len(rs[i].Logs))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -282,6 +282,10 @@ func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (type
|
|||
return b.eth.blockchain.GetReceiptsByHash(hash), nil
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) GetCanonicalReceipt(tx *types.Transaction, blockHash common.Hash, blockNumber, blockIndex uint64) (*types.Receipt, error) {
|
||||
return b.eth.blockchain.GetCanonicalReceipt(tx, blockHash, blockNumber, blockIndex)
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
|
||||
return rawdb.ReadLogs(b.eth.chainDb, hash, number), nil
|
||||
}
|
||||
|
|
@ -354,14 +358,16 @@ func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction
|
|||
return b.eth.txPool.Get(hash)
|
||||
}
|
||||
|
||||
// GetTransaction retrieves the lookup along with the transaction itself associate
|
||||
// with the given transaction hash.
|
||||
// GetCanonicalTransaction retrieves the lookup along with the transaction itself
|
||||
// associate with the given transaction hash.
|
||||
//
|
||||
// A null will be returned if 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)
|
||||
//
|
||||
// Notably, only the transaction in the canonical chain is visible.
|
||||
func (b *EthAPIBackend) GetCanonicalTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||
lookup, tx := b.eth.blockchain.GetCanonicalTransaction(txHash)
|
||||
if lookup == nil || tx == nil {
|
||||
return false, nil, common.Hash{}, 0, 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,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(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64)
|
||||
GetCanonicalTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64)
|
||||
TxIndexDone() bool
|
||||
RPCGasCap() uint64
|
||||
ChainConfig() *params.ChainConfig
|
||||
|
|
@ -863,7 +863,7 @@ 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 := api.backend.GetTransaction(hash)
|
||||
found, _, blockHash, blockNumber, index := api.backend.GetCanonicalTransaction(hash)
|
||||
if !found {
|
||||
// Warn in case tx indexer is not done.
|
||||
if !api.backend.TxIndexDone() {
|
||||
|
|
|
|||
|
|
@ -117,8 +117,8 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber)
|
|||
return b.chain.GetBlockByNumber(uint64(number)), nil
|
||||
}
|
||||
|
||||
func (b *testBackend) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||
tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash)
|
||||
func (b *testBackend) GetCanonicalTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||
tx, hash, blockNumber, index := rawdb.ReadCanonicalTransaction(b.chaindb, txHash)
|
||||
return tx != nil, tx, hash, blockNumber, index
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(t.hash)
|
||||
found, tx, blockHash, _, index := t.r.backend.GetCanonicalTransaction(t.hash)
|
||||
if found {
|
||||
t.tx = tx
|
||||
blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false)
|
||||
|
|
|
|||
|
|
@ -1334,7 +1334,7 @@ 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 := api.b.GetTransaction(hash)
|
||||
found, tx, blockHash, blockNumber, index := api.b.GetCanonicalTransaction(hash)
|
||||
if !found {
|
||||
// No finalized transaction, try to retrieve it from the pool
|
||||
if tx := api.b.GetPoolTransaction(hash); tx != nil {
|
||||
|
|
@ -1357,7 +1357,7 @@ 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, _, _, _ := api.b.GetTransaction(hash)
|
||||
found, tx, _, _, _ := api.b.GetCanonicalTransaction(hash)
|
||||
if !found {
|
||||
if tx = api.b.GetPoolTransaction(hash); tx != nil {
|
||||
return tx.MarshalBinary()
|
||||
|
|
@ -1374,7 +1374,7 @@ func (api *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash com
|
|||
|
||||
// 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 := api.b.GetTransaction(hash)
|
||||
found, tx, blockHash, blockNumber, index := api.b.GetCanonicalTransaction(hash)
|
||||
if !found {
|
||||
// Make sure indexer is done.
|
||||
if !api.b.TxIndexDone() {
|
||||
|
|
@ -1383,22 +1383,12 @@ func (api *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash commo
|
|||
// No such tx.
|
||||
return nil, nil
|
||||
}
|
||||
header, err := api.b.HeaderByHash(ctx, blockHash)
|
||||
receipt, err := api.b.GetCanonicalReceipt(tx, blockHash, blockNumber, index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
receipts, err := api.b.GetReceipts(ctx, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if uint64(len(receipts)) <= index {
|
||||
return nil, nil
|
||||
}
|
||||
receipt := receipts[index]
|
||||
|
||||
// Derive the sender.
|
||||
signer := types.MakeSigner(api.b.ChainConfig(), header.Number, header.Time)
|
||||
return marshalReceipt(receipt, blockHash, blockNumber, signer, tx, int(index)), nil
|
||||
return marshalReceipt(receipt, blockHash, blockNumber, api.signer, tx, int(index)), nil
|
||||
}
|
||||
|
||||
// marshalReceipt marshals a transaction receipt into a JSON object.
|
||||
|
|
@ -1781,7 +1771,7 @@ 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, _, _, _ := api.b.GetTransaction(hash)
|
||||
found, tx, _, _, _ := api.b.GetCanonicalTransaction(hash)
|
||||
if !found {
|
||||
if tx = api.b.GetPoolTransaction(hash); tx != nil {
|
||||
return tx.MarshalBinary()
|
||||
|
|
|
|||
|
|
@ -586,9 +586,12 @@ func (b testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) even
|
|||
func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||
panic("implement me")
|
||||
}
|
||||
func (b testBackend) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.db, txHash)
|
||||
return true, tx, blockHash, blockNumber, index
|
||||
func (b testBackend) GetCanonicalTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||
tx, blockHash, blockNumber, index := rawdb.ReadCanonicalTransaction(b.db, txHash)
|
||||
return tx != nil, tx, blockHash, blockNumber, index
|
||||
}
|
||||
func (b testBackend) GetCanonicalReceipt(tx *types.Transaction, blockHash common.Hash, blockNumber, blockIndex uint64) (*types.Receipt, error) {
|
||||
return b.chain.GetCanonicalReceipt(tx, blockHash, blockNumber, blockIndex)
|
||||
}
|
||||
func (b testBackend) TxIndexDone() bool {
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -68,13 +68,14 @@ type Backend interface {
|
|||
StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error)
|
||||
Pending() (*types.Block, types.Receipts, *state.StateDB)
|
||||
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
|
||||
GetCanonicalReceipt(tx *types.Transaction, blockHash common.Hash, blockNumber, blockIndex uint64) (*types.Receipt, error)
|
||||
GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM
|
||||
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
|
||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||
|
||||
// Transaction pool API
|
||||
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
||||
GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64)
|
||||
GetCanonicalTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64)
|
||||
TxIndexDone() bool
|
||||
GetPoolTransactions() (types.Transactions, error)
|
||||
GetPoolTransaction(txHash common.Hash) *types.Transaction
|
||||
|
|
|
|||
|
|
@ -369,6 +369,9 @@ func (b *backendMock) Pending() (*types.Block, types.Receipts, *state.StateDB) {
|
|||
func (b *backendMock) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (b *backendMock) GetCanonicalReceipt(tx *types.Transaction, blockHash common.Hash, blockNumber, blockIndex uint64) (*types.Receipt, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (b *backendMock) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -380,7 +383,7 @@ func (b *backendMock) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) eve
|
|||
return nil
|
||||
}
|
||||
func (b *backendMock) SendTx(ctx context.Context, signedTx *types.Transaction) error { return nil }
|
||||
func (b *backendMock) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||
func (b *backendMock) GetCanonicalTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||
return false, nil, [32]byte{}, 0, 0
|
||||
}
|
||||
func (b *backendMock) TxIndexDone() bool { return true }
|
||||
|
|
|
|||
Loading…
Reference in a new issue