Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove unnecessary receipt derivation logic #29670

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
6 changes: 6 additions & 0 deletions core/blockchain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*type
return
}

// GetReceiptByHash retrieves the receipt by transaction hash
func (bc *BlockChain) GetReceiptByHash(txHash common.Hash) *types.Receipt {
receipt, _, _, _ := rawdb.ReadReceipt(bc.db, txHash, bc.chainConfig)
return receipt
}

// 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 {
Expand Down
61 changes: 53 additions & 8 deletions core/rawdb/accessors_indexes.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
Expand Down Expand Up @@ -120,9 +121,9 @@ func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, com

// 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) {
func ReadReceipt(db ethdb.Reader, txHash 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)
blockNumber := ReadTxLookupEntry(db, txHash)
if blockNumber == nil {
return nil, common.Hash{}, 0, 0
}
Expand All @@ -134,14 +135,58 @@ func ReadReceipt(db ethdb.Reader, hash common.Hash, config *params.ChainConfig)
if blockHeader == nil {
return nil, common.Hash{}, 0, 0
}
// Read all the receipts from the block and return the one with the matching hash
receipts := ReadReceipts(db, blockHash, *blockNumber, blockHeader.Time, config)
for receiptIndex, receipt := range receipts {
if receipt.TxHash == hash {
return receipt, blockHash, *blockNumber, uint64(receiptIndex)
blockBody := ReadBody(db, blockHash, *blockNumber)
if blockBody == nil {
log.Error("Missing body but have receipt", "blockHash", blockHash, "number", blockNumber)
return nil, common.Hash{}, 0, 0
}

// Find a match tx and derive receipt fields
for txIndex, tx := range blockBody.Transactions {
if tx.Hash() != txHash {
continue
}

// Read raw receipts only if hash matches
receipts := ReadRawReceipts(db, blockHash, *blockNumber)
if receipts == nil {
return nil, common.Hash{}, 0, 0
}
if len(blockBody.Transactions) != len(receipts) {
log.Error("Transaction and receipt count mismatch", "txs", len(blockBody.Transactions), "receipts", len(receipts))
return nil, common.Hash{}, 0, 0
}

targetReceipt := receipts[txIndex]
signer := types.MakeSigner(config, new(big.Int).SetUint64(*blockNumber), blockHeader.Time)

// Compute effective blob gas price.
var blobGasPrice *big.Int
if blockHeader.ExcessBlobGas != nil {
blobGasPrice = eip4844.CalcBlobFee(*blockHeader.ExcessBlobGas)
}

var gasUsed uint64
if txIndex == 0 {
gasUsed = targetReceipt.CumulativeGasUsed
} else {
gasUsed = targetReceipt.CumulativeGasUsed - receipts[txIndex-1].CumulativeGasUsed
}

// Calculate the staring log index from previous logs
logIndex := uint(0)
for i := 0; i < txIndex; i++ {
logIndex += uint(len(receipts[i].Logs))
}

if err := targetReceipt.DeriveField(signer, blockHash, *blockNumber, blockHeader.BaseFee, blobGasPrice, uint(txIndex), gasUsed, logIndex, blockBody.Transactions[txIndex]); err != nil {
log.Error("Failed to derive the receipt fields", "txHash", txHash, "err", err)
return nil, common.Hash{}, 0, 0
}
return targetReceipt, blockHash, *blockNumber, uint64(txIndex)
}
log.Error("Receipt not found", "number", *blockNumber, "hash", blockHash, "txhash", hash)

log.Error("Receipt not found", "number", *blockNumber, "blockHash", blockHash, "txHash", txHash)
return nil, common.Hash{}, 0, 0
}

Expand Down
84 changes: 47 additions & 37 deletions core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,47 @@ func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
return r
}

// DeriveField fills the receipt with their computed fields based on consensus
// data and contextual infos like containing block and transactions.
func (r *Receipt) DeriveField(signer Signer, hash common.Hash, number uint64, baseFee *big.Int, blobGasPrice *big.Int, txIndex uint, gasUsed uint64, firstLogIndex uint, tx *Transaction) error {
// The transaction type and hash can be retrieved from the transaction itself
r.Type = tx.Type()
r.TxHash = tx.Hash()
r.EffectiveGasPrice = tx.inner.effectiveGasPrice(new(big.Int), baseFee)

// EIP-4844 blob transaction fields
if tx.Type() == BlobTxType {
r.BlobGasUsed = tx.BlobGas()
r.BlobGasPrice = blobGasPrice
}

// block location fields
r.BlockHash = hash
r.BlockNumber = new(big.Int).SetUint64(number)
r.TransactionIndex = txIndex

// The contract address can be derived from the transaction itself
if tx.To() == nil {
// Deriving the signer is expensive, only do if it's actually needed
from, _ := Sender(signer, tx)
r.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
} else {
r.ContractAddress = common.Address{}
}

r.GasUsed = gasUsed

// The derived log fields can simply be set from the block and transaction
for i := 0; i < len(r.Logs); i++ {
r.Logs[i].BlockNumber = number
r.Logs[i].BlockHash = hash
r.Logs[i].TxHash = r.TxHash
r.Logs[i].TxIndex = txIndex
r.Logs[i].Index = firstLogIndex + uint(i)
}
return nil
}

// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
// into an RLP stream. If no post state is present, byzantium fork is assumed.
func (r *Receipt) EncodeRLP(w io.Writer) error {
Expand Down Expand Up @@ -331,47 +372,16 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
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
}

// 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
var gasUsed uint64
if i == 0 {
rs[i].GasUsed = rs[i].CumulativeGasUsed
gasUsed = rs[i].CumulativeGasUsed
} else {
rs[i].GasUsed = rs[i].CumulativeGasUsed - rs[i-1].CumulativeGasUsed
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].TxHash = rs[i].TxHash
rs[i].Logs[j].TxIndex = uint(i)
rs[i].Logs[j].Index = logIndex
logIndex++
if err := rs[i].DeriveField(signer, hash, number, baseFee, blobGasPrice, uint(i), gasUsed, logIndex, txs[i]); err != nil {
return err
}
logIndex += uint(len(rs[i].Logs))
}
return nil
}
40 changes: 39 additions & 1 deletion core/types/receipt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,45 @@ func TestDecodeEmptyTypedReceipt(t *testing.T) {
}
}

// Tests that receipt data can be correctly derived from the contextual infos
// Tests that a single receipt data can be correctly derived from the contextual infos
func TestDeriveField(t *testing.T) {
// Re-derive receipts one by one.
basefee := big.NewInt(1000)
blobGasPrice := big.NewInt(920)
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
logIndex := uint(0)
for i := 0; i < len(derivedReceipts); i++ {
signer := MakeSigner(params.TestChainConfig, new(big.Int).SetUint64(blockNumber.Uint64()), blockTime)
var gasUsed uint64
if i == 0 {
gasUsed = derivedReceipts[i].CumulativeGasUsed
} else {
gasUsed = derivedReceipts[i].CumulativeGasUsed - derivedReceipts[i-1].CumulativeGasUsed
}
err := derivedReceipts[i].DeriveField(signer, blockHash, blockNumber.Uint64(), basefee, blobGasPrice, uint(i), gasUsed, logIndex, txs[i])
if err != nil {
t.Fatalf("DeriveField(...) = %v, want <nil>", err)
}
logIndex += uint(len(derivedReceipts[i].Logs))

// Check diff of a receipt against a derivedReceipt.
r1, err := json.MarshalIndent(receipts[i], "", " ")
if err != nil {
t.Fatal("error marshaling an input receipt:", err)
}

r2, err := json.MarshalIndent(derivedReceipts[i], "", " ")
if err != nil {
t.Fatal("error marshaling a derived receipt:", err)
}
d := diff.Diff(string(r1), string(r2))
if d != "" {
t.Fatal("a receipt differ:", d)
}
}
}

// Tests that multiple receipt data can be correctly derived from the contextual infos
func TestDeriveFields(t *testing.T) {
// Re-derive receipts.
basefee := big.NewInt(1000)
Expand Down
4 changes: 4 additions & 0 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockN
return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
}

func (b *EthAPIBackend) GetReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
return b.eth.blockchain.GetReceiptByHash(txHash), nil
}

func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
return b.eth.blockchain.GetReceiptsByHash(hash), nil
}
Expand Down
5 changes: 5 additions & 0 deletions eth/filters/filter_system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ func (b *testBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.
return nil, errors.New("block body not found")
}

func (b *testBackend) GetReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
receipt, _, _, _ := rawdb.ReadReceipt(b.db, txHash, params.TestChainConfig)
return receipt, nil
}

func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
if number := rawdb.ReadHeaderNumber(b.db, hash); number != nil {
if header := rawdb.ReadHeader(b.db, hash, *number); header != nil {
Expand Down
4 changes: 4 additions & 0 deletions eth/gasprice/gasprice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber)
return b.chain.GetBlockByNumber(uint64(number)), nil
}

func (b *testBackend) GetReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
return b.chain.GetReceiptByHash(txHash), nil
}

func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
return b.chain.GetReceiptsByHash(hash), nil
}
Expand Down
5 changes: 2 additions & 3 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1666,14 +1666,13 @@ func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.
if err != nil {
return nil, err
}
receipts, err := s.b.GetReceipts(ctx, blockHash)
receipt, err := s.b.GetReceipt(ctx, hash)
if err != nil {
return nil, err
}
if uint64(len(receipts)) <= index {
if receipt == nil {
return nil, nil
}
receipt := receipts[index]

// Derive the sender.
signer := types.MakeSigner(s.b.ChainConfig(), header.Number, header.Time)
Expand Down
4 changes: 4 additions & 0 deletions internal/ethapi/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,10 @@ func (b testBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOr
panic("only implemented for number")
}
func (b testBackend) Pending() (*types.Block, types.Receipts, *state.StateDB) { panic("implement me") }
func (b testBackend) GetReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
receipt, _, _, _ := rawdb.ReadReceipt(b.db, txHash, b.chain.Config())
return receipt, nil
}
func (b testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
header, err := b.HeaderByHash(ctx, hash)
if header == nil || err != nil {
Expand Down
1 change: 1 addition & 0 deletions internal/ethapi/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type Backend interface {
StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error)
StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error)
Pending() (*types.Block, types.Receipts, *state.StateDB)
GetReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error)
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
GetTd(ctx context.Context, hash common.Hash) *big.Int
GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM
Expand Down
3 changes: 3 additions & 0 deletions internal/ethapi/transaction_args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,9 @@ func (b *backendMock) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrO
return nil, nil, nil
}
func (b *backendMock) Pending() (*types.Block, types.Receipts, *state.StateDB) { return nil, nil, nil }
func (b *backendMock) GetReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
return nil, nil
}
func (b *backendMock) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
return nil, nil
}
Expand Down