diff --git a/core/blockchain.go b/core/blockchain.go index d58aedf086..5f90e9a575 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -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 diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index a4240a3405..cec606b920 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -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 } diff --git a/core/chain_makers.go b/core/chain_makers.go index fe2e25d2d7..aa4639dcb6 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -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) } diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 2386246caf..b43149b8ea 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -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 } diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 6d46239e27..15728f6d06 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -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) diff --git a/core/types/receipt.go b/core/types/receipt.go index 5b6669f274..00f16c0893 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -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 diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index 8f805ff096..257661054f 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -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 ", err) }