core: refactor DeriveReceipts a bit for generalization

This commit is contained in:
Ömer Faruk IRMAK 2025-06-19 18:03:11 +03:00
parent 019a6b4921
commit e8e199749a
7 changed files with 110 additions and 90 deletions

View file

@ -2273,7 +2273,7 @@ func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log {
blobGasPrice = eip4844.CalcBlobFee(bc.chainConfig, b.Header())
}
receipts := rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64())
if err := receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.Time(), b.BaseFee(), blobGasPrice, b.Transactions()); err != nil {
if err := receipts.DeriveFields(bc.chainConfig, b.Header(), blobGasPrice, b.Transactions()); err != nil {
log.Error("Failed to derive block receipts fields", "hash", b.Hash(), "number", b.NumberU64(), "err", err)
}
var logs []*types.Log

View file

@ -238,17 +238,7 @@ func (bc *BlockChain) GetReceiptByIndex(tx *types.Transaction, blockHash common.
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),
})
receipt.DeriveFields(types.MakeDeriveReceiptContext(signer, header, blobGasPrice, tx, ctx.GasUsed, uint(txIndex), ctx.LogIndex))
return receipt, nil
}

View file

@ -453,7 +453,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if block.ExcessBlobGas() != nil {
blobGasPrice = eip4844.CalcBlobFee(cm.config, block.Header())
}
if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.BaseFee(), blobGasPrice, txs); err != nil {
if err := receipts.DeriveFields(config, block.Header(), blobGasPrice, txs); err != nil {
panic(err)
}
@ -563,7 +563,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine
if block.ExcessBlobGas() != nil {
blobGasPrice = eip4844.CalcBlobFee(cm.config, block.Header())
}
if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.BaseFee(), blobGasPrice, txs); err != nil {
if err := receipts.DeriveFields(config, block.Header(), blobGasPrice, txs); err != nil {
panic(err)
}

View file

@ -585,19 +585,15 @@ func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, time uint64,
return nil
}
header := ReadHeader(db, hash, number)
var baseFee *big.Int
if header == nil {
baseFee = big.NewInt(0)
} else {
baseFee = header.BaseFee
return nil
}
// Compute effective blob gas price.
var blobGasPrice *big.Int
if header != nil && header.ExcessBlobGas != nil {
if header.ExcessBlobGas != nil {
blobGasPrice = eip4844.CalcBlobFee(config, header)
}
if err := receipts.DeriveFields(config, hash, number, time, baseFee, blobGasPrice, body.Transactions); err != nil {
if err := receipts.DeriveFields(config, header, blobGasPrice, body.Transactions); err != nil {
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
return nil
}

View file

@ -354,11 +354,13 @@ func TestBlockReceiptStorage(t *testing.T) {
receipt2.Bloom = types.CreateBloom(receipt2)
receipts := []*types.Receipt{receipt1, receipt2}
header := types.Header{Number: big.NewInt(0), Difficulty: big.NewInt(0)}
// Check that no receipt entries are in a pristine database
hash := common.BytesToHash([]byte{0x03, 0x14})
hash := header.Hash()
if rs := ReadReceipts(db, hash, 0, 0, params.TestChainConfig); len(rs) != 0 {
t.Fatalf("non existent receipts returned: %v", rs)
}
WriteHeader(db, &header)
// Insert the body that corresponds to the receipts
WriteBody(db, hash, 0, body)
@ -834,19 +836,21 @@ func TestDeriveLogFields(t *testing.T) {
}
// Derive log metadata fields
number := big.NewInt(1)
hash := common.BytesToHash([]byte{0x03, 0x14})
types.Receipts(receipts).DeriveFields(params.TestChainConfig, hash, number.Uint64(), 12, big.NewInt(0), big.NewInt(0), txs)
header := types.Header{
Number: big.NewInt(1),
Time: 12,
}
types.Receipts(receipts).DeriveFields(params.TestChainConfig, &header, big.NewInt(0), txs)
// Iterate over all the computed fields and check that they're correct
logIndex := uint(0)
for i := range receipts {
for j := range receipts[i].Logs {
if receipts[i].Logs[j].BlockNumber != number.Uint64() {
t.Errorf("receipts[%d].Logs[%d].BlockNumber = %d, want %d", i, j, receipts[i].Logs[j].BlockNumber, number.Uint64())
if receipts[i].Logs[j].BlockNumber != header.Number.Uint64() {
t.Errorf("receipts[%d].Logs[%d].BlockNumber = %d, want %d", i, j, receipts[i].Logs[j].BlockNumber, header.Number.Uint64())
}
if receipts[i].Logs[j].BlockHash != hash {
t.Errorf("receipts[%d].Logs[%d].BlockHash = %s, want %s", i, j, receipts[i].Logs[j].BlockHash.String(), hash.String())
if receipts[i].Logs[j].BlockHash != header.Hash() {
t.Errorf("receipts[%d].Logs[%d].BlockHash = %s, want %s", i, j, receipts[i].Logs[j].BlockHash.String(), header.Hash().String())
}
if receipts[i].Logs[j].BlockTimestamp != 12 {
t.Errorf("receipts[%d].Logs[%d].BlockTimestamp = %d, want %d", i, j, receipts[i].Logs[j].BlockTimestamp, 12)

View file

@ -265,37 +265,74 @@ type DeriveReceiptContext struct {
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
// Receipt fields
GasUsed uint64
LogIndex uint // Number of logs in the block until this receipt
// Tx fields
Hash common.Hash
Nonce uint64
Index uint
Type uint8
From common.Address
To *common.Address
EffectiveGasPrice *big.Int
BlobGas uint64
}
// MakeDeriveReceiptContext builds the context needed to derive a receipt
func MakeDeriveReceiptContext(
signer Signer, header *Header, blobGasPrice *big.Int,
tx *Transaction, gasUsed uint64, txIndex, logIndex uint,
) DeriveReceiptContext {
from, _ := signer.Sender(tx)
return DeriveReceiptContext{
BlockHash: header.Hash(),
BlockNumber: header.Number.Uint64(),
BlockTime: header.Time,
BaseFee: header.BaseFee,
BlobGasPrice: blobGasPrice,
// Receipt fields
GasUsed: gasUsed,
LogIndex: logIndex, // Number of logs in the block until this receipt
// Tx fields
Hash: tx.Hash(),
Nonce: tx.Nonce(),
Index: txIndex,
Type: tx.Type(),
From: from,
To: tx.To(),
EffectiveGasPrice: tx.EffectiveGasPrice(header.BaseFee),
BlobGas: tx.BlobGas(),
}
}
// 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) {
func (r *Receipt) DeriveFields(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.Type = context.Type
r.TxHash = context.Hash
r.GasUsed = context.GasUsed
r.EffectiveGasPrice = context.Tx.inner.effectiveGasPrice(new(big.Int), context.BaseFee)
r.EffectiveGasPrice = context.EffectiveGasPrice
// EIP-4844 blob transaction fields
if context.Tx.Type() == BlobTxType {
r.BlobGasUsed = context.Tx.BlobGas()
if context.Type == BlobTxType {
r.BlobGasUsed = context.BlobGas
r.BlobGasPrice = context.BlobGasPrice
}
// Block location fields
r.BlockHash = context.BlockHash
r.BlockNumber = new(big.Int).SetUint64(context.BlockNumber)
r.TransactionIndex = context.TxIndex
r.TransactionIndex = context.Index
// 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())
if context.To == nil {
r.ContractAddress = crypto.CreateAddress(context.From, context.Nonce)
} else {
r.ContractAddress = common.Address{}
}
@ -306,7 +343,7 @@ func (r *Receipt) DeriveFields(signer Signer, context DeriveReceiptContext) {
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].TxIndex = context.Index
r.Logs[j].Index = logIndex
logIndex++
}
@ -379,10 +416,12 @@ 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, 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)
func (rs Receipts) DeriveFields(config *params.ChainConfig, header *Header, blobGasPrice *big.Int, txs []*Transaction) error {
var (
signer = MakeSigner(config, header.Number, header.Time)
logIndex = uint(0)
)
logIndex := uint(0)
if len(txs) != len(rs) {
return errors.New("transaction and receipt count mismatch")
}
@ -391,17 +430,7 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, blockHash common.Has
if i > 0 {
cumulativeGasUsed = rs[i-1].CumulativeGasUsed
}
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),
})
rs[i].DeriveFields(MakeDeriveReceiptContext(signer, header, blobGasPrice, txs[i], rs[i].CumulativeGasUsed-cumulativeGasUsed, uint(i), logIndex))
logIndex += uint(len(rs[i].Logs))
}
return nil

View file

@ -152,9 +152,11 @@ var (
}),
}
blockNumber = big.NewInt(1)
blockTime = uint64(2)
blockHash = common.BytesToHash([]byte{0x03, 0x14})
header = Header{
Number: big.NewInt(1),
Difficulty: big.NewInt(0),
Time: 2,
}
)
var receiptsOnce sync.Once
@ -173,22 +175,22 @@ func getTestReceipts() Receipts {
Address: common.BytesToAddress([]byte{0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
BlockNumber: header.Number.Uint64(),
TxHash: txs[0].Hash(),
TxIndex: 0,
BlockHash: blockHash,
BlockTimestamp: blockTime,
BlockHash: header.Hash(),
BlockTimestamp: header.Time,
Index: 0,
},
{
Address: common.BytesToAddress([]byte{0x01, 0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
BlockNumber: header.Number.Uint64(),
TxHash: txs[0].Hash(),
TxIndex: 0,
BlockHash: blockHash,
BlockTimestamp: blockTime,
BlockHash: header.Hash(),
BlockTimestamp: header.Time,
Index: 1,
},
},
@ -197,8 +199,8 @@ func getTestReceipts() Receipts {
ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
GasUsed: 1,
EffectiveGasPrice: big.NewInt(11),
BlockHash: blockHash,
BlockNumber: blockNumber,
BlockHash: header.Hash(),
BlockNumber: header.Number,
TransactionIndex: 0,
},
&Receipt{
@ -209,22 +211,22 @@ func getTestReceipts() Receipts {
Address: common.BytesToAddress([]byte{0x22}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
BlockNumber: header.Number.Uint64(),
TxHash: txs[1].Hash(),
TxIndex: 1,
BlockHash: blockHash,
BlockTimestamp: blockTime,
BlockHash: header.Hash(),
BlockTimestamp: header.Time,
Index: 2,
},
{
Address: common.BytesToAddress([]byte{0x02, 0x22}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
BlockNumber: header.Number.Uint64(),
TxHash: txs[1].Hash(),
TxIndex: 1,
BlockHash: blockHash,
BlockTimestamp: blockTime,
BlockHash: header.Hash(),
BlockTimestamp: header.Time,
Index: 3,
},
},
@ -232,8 +234,8 @@ func getTestReceipts() Receipts {
TxHash: txs[1].Hash(),
GasUsed: 2,
EffectiveGasPrice: big.NewInt(22),
BlockHash: blockHash,
BlockNumber: blockNumber,
BlockHash: header.Hash(),
BlockNumber: header.Number,
TransactionIndex: 1,
},
&Receipt{
@ -245,8 +247,8 @@ func getTestReceipts() Receipts {
TxHash: txs[2].Hash(),
GasUsed: 3,
EffectiveGasPrice: big.NewInt(33),
BlockHash: blockHash,
BlockNumber: blockNumber,
BlockHash: header.Hash(),
BlockNumber: header.Number,
TransactionIndex: 2,
},
&Receipt{
@ -258,8 +260,8 @@ func getTestReceipts() Receipts {
TxHash: txs[3].Hash(),
GasUsed: 4,
EffectiveGasPrice: big.NewInt(1044),
BlockHash: blockHash,
BlockNumber: blockNumber,
BlockHash: header.Hash(),
BlockNumber: header.Number,
TransactionIndex: 3,
},
&Receipt{
@ -271,8 +273,8 @@ func getTestReceipts() Receipts {
TxHash: txs[4].Hash(),
GasUsed: 5,
EffectiveGasPrice: big.NewInt(1055),
BlockHash: blockHash,
BlockNumber: blockNumber,
BlockHash: header.Hash(),
BlockNumber: header.Number,
TransactionIndex: 4,
},
&Receipt{
@ -286,8 +288,8 @@ func getTestReceipts() Receipts {
EffectiveGasPrice: big.NewInt(1066),
BlobGasUsed: params.BlobTxBlobGasPerBlob,
BlobGasPrice: big.NewInt(920),
BlockHash: blockHash,
BlockNumber: blockNumber,
BlockHash: header.Hash(),
BlockNumber: header.Number,
TransactionIndex: 5,
},
&Receipt{
@ -301,8 +303,8 @@ func getTestReceipts() Receipts {
EffectiveGasPrice: big.NewInt(1077),
BlobGasUsed: 3 * params.BlobTxBlobGasPerBlob,
BlobGasPrice: big.NewInt(920),
BlockHash: blockHash,
BlockNumber: blockNumber,
BlockHash: header.Hash(),
BlockNumber: header.Number,
TransactionIndex: 6,
},
}
@ -326,11 +328,10 @@ func TestDecodeEmptyTypedReceipt(t *testing.T) {
// Tests that receipt data can be correctly derived from the contextual infos
func TestDeriveFields(t *testing.T) {
// Re-derive receipts.
basefee := big.NewInt(1000)
blobGasPrice := big.NewInt(920)
receipts := getTestReceipts()
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, basefee, blobGasPrice, txs)
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, &header, blobGasPrice, txs)
if err != nil {
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
}