eth/protocols/eth: started work on eth/69, drop receipt blooms

This commit is contained in:
Marius van der Wijden 2024-03-01 16:44:11 +01:00 committed by Felix Lange
parent 1f175347c4
commit e429b91c46
13 changed files with 446 additions and 164 deletions

View file

@ -66,10 +66,10 @@ func (s *Suite) dialAs(key *ecdsa.PrivateKey) (*Conn, error) {
return nil, err return nil, err
} }
conn.caps = []p2p.Cap{ conn.caps = []p2p.Cap{
{Name: "eth", Version: 67},
{Name: "eth", Version: 68}, {Name: "eth", Version: 68},
{Name: "eth", Version: 69},
} }
conn.ourHighestProtoVersion = 68 conn.ourHighestProtoVersion = 69
return &conn, nil return &conn, nil
} }

View file

@ -234,12 +234,14 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
return receipts return receipts
} }
func (bc *BlockChain) GetRawReceiptsByHash(hash common.Hash) types.Receipts { // GetRawReceiptsByHash retrieves the receipts for all transactions in a given block
// without deriving the internal fields and the Bloom.
func (bc *BlockChain) GetRawReceiptsByHash(hash common.Hash) rlp.RawValue {
number := rawdb.ReadHeaderNumber(bc.db, hash) number := rawdb.ReadHeaderNumber(bc.db, hash)
if number == nil { if number == nil {
return nil return nil
} }
return rawdb.ReadRawReceipts(bc.db, hash, *number) return rawdb.ReadReceiptsRLP(bc.db, hash, *number)
} }
// GetUnclesInChain retrieves all the uncles from a given block backwards until // GetUnclesInChain retrieves all the uncles from a given block backwards until

View file

@ -545,8 +545,8 @@ func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawVa
} }
// ReadRawReceipts retrieves all the transaction receipts belonging to a block. // ReadRawReceipts retrieves all the transaction receipts belonging to a block.
// The receipt metadata fields are not guaranteed to be populated, so they // The receipt metadata fields and the Bloom are not guaranteed to be populated,
// should not be used. Use ReadReceipts instead if the metadata is needed. // so they should not be used. Use ReadReceipts instead if the metadata is needed.
func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts { func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts {
// Retrieve the flattened receipt slice // Retrieve the flattened receipt slice
data := ReadReceiptsRLP(db, hash, number) data := ReadReceiptsRLP(db, hash, number)

View file

@ -377,7 +377,11 @@ func TestBlockReceiptStorage(t *testing.T) {
t.Fatalf("receipts returned when body was deleted: %v", rs) t.Fatalf("receipts returned when body was deleted: %v", rs)
} }
// Ensure that receipts without metadata can be returned without the block body too // Ensure that receipts without metadata can be returned without the block body too
if err := checkReceiptsRLP(ReadRawReceipts(db, hash, 0), receipts); err != nil { raw := ReadRawReceipts(db, hash, 0)
for _, r := range raw {
r.Bloom = types.CreateBloom(r)
}
if err := checkReceiptsRLP(raw, receipts); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// Sanity check that body alone without the receipt is a full purge // Sanity check that body alone without the receipt is a full purge

View file

@ -258,7 +258,7 @@ func (r *Receipt) Size() common.StorageSize {
} }
// ReceiptForStorage is a wrapper around a Receipt with RLP serialization // ReceiptForStorage is a wrapper around a Receipt with RLP serialization
// that omits the Bloom field and deserialization that re-computes it. // that omits the Bloom field. The Bloom field is recomputed by DeriveFields.
type ReceiptForStorage Receipt type ReceiptForStorage Receipt
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt // EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
@ -291,7 +291,6 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
} }
r.CumulativeGasUsed = stored.CumulativeGasUsed r.CumulativeGasUsed = stored.CumulativeGasUsed
r.Logs = stored.Logs r.Logs = stored.Logs
r.Bloom = CreateBloom((*Receipt)(r))
return nil return nil
} }
@ -372,6 +371,9 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
rs[i].Logs[j].Index = logIndex rs[i].Logs[j].Index = logIndex
logIndex++ logIndex++
} }
// also derive the Bloom if not derived yet
rs[i].Bloom = CreateBloom(rs[i])
} }
return nil return nil
} }

View file

@ -22,6 +22,7 @@ import (
"math" "math"
"math/big" "math/big"
"reflect" "reflect"
"sync"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -154,148 +155,162 @@ var (
blockNumber = big.NewInt(1) blockNumber = big.NewInt(1)
blockTime = uint64(2) blockTime = uint64(2)
blockHash = common.BytesToHash([]byte{0x03, 0x14}) blockHash = common.BytesToHash([]byte{0x03, 0x14})
// Create the corresponding receipts
receipts = Receipts{
&Receipt{
Status: ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*Log{
{
Address: common.BytesToAddress([]byte{0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[0].Hash(),
TxIndex: 0,
BlockHash: blockHash,
Index: 0,
},
{
Address: common.BytesToAddress([]byte{0x01, 0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[0].Hash(),
TxIndex: 0,
BlockHash: blockHash,
Index: 1,
},
},
// derived fields:
TxHash: txs[0].Hash(),
ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
GasUsed: 1,
EffectiveGasPrice: big.NewInt(11),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 0,
},
&Receipt{
PostState: common.Hash{2}.Bytes(),
CumulativeGasUsed: 3,
Logs: []*Log{
{
Address: common.BytesToAddress([]byte{0x22}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[1].Hash(),
TxIndex: 1,
BlockHash: blockHash,
Index: 2,
},
{
Address: common.BytesToAddress([]byte{0x02, 0x22}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[1].Hash(),
TxIndex: 1,
BlockHash: blockHash,
Index: 3,
},
},
// derived fields:
TxHash: txs[1].Hash(),
GasUsed: 2,
EffectiveGasPrice: big.NewInt(22),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 1,
},
&Receipt{
Type: AccessListTxType,
PostState: common.Hash{3}.Bytes(),
CumulativeGasUsed: 6,
Logs: []*Log{},
// derived fields:
TxHash: txs[2].Hash(),
GasUsed: 3,
EffectiveGasPrice: big.NewInt(33),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 2,
},
&Receipt{
Type: DynamicFeeTxType,
PostState: common.Hash{4}.Bytes(),
CumulativeGasUsed: 10,
Logs: []*Log{},
// derived fields:
TxHash: txs[3].Hash(),
GasUsed: 4,
EffectiveGasPrice: big.NewInt(1044),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 3,
},
&Receipt{
Type: DynamicFeeTxType,
PostState: common.Hash{5}.Bytes(),
CumulativeGasUsed: 15,
Logs: []*Log{},
// derived fields:
TxHash: txs[4].Hash(),
GasUsed: 5,
EffectiveGasPrice: big.NewInt(1055),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 4,
},
&Receipt{
Type: BlobTxType,
PostState: common.Hash{6}.Bytes(),
CumulativeGasUsed: 21,
Logs: []*Log{},
// derived fields:
TxHash: txs[5].Hash(),
GasUsed: 6,
EffectiveGasPrice: big.NewInt(1066),
BlobGasUsed: params.BlobTxBlobGasPerBlob,
BlobGasPrice: big.NewInt(920),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 5,
},
&Receipt{
Type: BlobTxType,
PostState: common.Hash{7}.Bytes(),
CumulativeGasUsed: 28,
Logs: []*Log{},
// derived fields:
TxHash: txs[6].Hash(),
GasUsed: 7,
EffectiveGasPrice: big.NewInt(1077),
BlobGasUsed: 3 * params.BlobTxBlobGasPerBlob,
BlobGasPrice: big.NewInt(920),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 6,
},
}
) )
var receiptsOnce sync.Once
var testReceipts Receipts
func getTestReceipts() Receipts {
// Compute the blooms only once
receiptsOnce.Do(func() {
// Create the corresponding receipts
r := Receipts{
&Receipt{
Status: ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*Log{
{
Address: common.BytesToAddress([]byte{0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[0].Hash(),
TxIndex: 0,
BlockHash: blockHash,
Index: 0,
},
{
Address: common.BytesToAddress([]byte{0x01, 0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[0].Hash(),
TxIndex: 0,
BlockHash: blockHash,
Index: 1,
},
},
// derived fields:
TxHash: txs[0].Hash(),
ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
GasUsed: 1,
EffectiveGasPrice: big.NewInt(11),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 0,
},
&Receipt{
PostState: common.Hash{2}.Bytes(),
CumulativeGasUsed: 3,
Logs: []*Log{
{
Address: common.BytesToAddress([]byte{0x22}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[1].Hash(),
TxIndex: 1,
BlockHash: blockHash,
Index: 2,
},
{
Address: common.BytesToAddress([]byte{0x02, 0x22}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
// derived fields:
BlockNumber: blockNumber.Uint64(),
TxHash: txs[1].Hash(),
TxIndex: 1,
BlockHash: blockHash,
Index: 3,
},
},
// derived fields:
TxHash: txs[1].Hash(),
GasUsed: 2,
EffectiveGasPrice: big.NewInt(22),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 1,
},
&Receipt{
Type: AccessListTxType,
PostState: common.Hash{3}.Bytes(),
CumulativeGasUsed: 6,
Logs: []*Log{},
// derived fields:
TxHash: txs[2].Hash(),
GasUsed: 3,
EffectiveGasPrice: big.NewInt(33),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 2,
},
&Receipt{
Type: DynamicFeeTxType,
PostState: common.Hash{4}.Bytes(),
CumulativeGasUsed: 10,
Logs: []*Log{},
// derived fields:
TxHash: txs[3].Hash(),
GasUsed: 4,
EffectiveGasPrice: big.NewInt(1044),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 3,
},
&Receipt{
Type: DynamicFeeTxType,
PostState: common.Hash{5}.Bytes(),
CumulativeGasUsed: 15,
Logs: []*Log{},
// derived fields:
TxHash: txs[4].Hash(),
GasUsed: 5,
EffectiveGasPrice: big.NewInt(1055),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 4,
},
&Receipt{
Type: BlobTxType,
PostState: common.Hash{6}.Bytes(),
CumulativeGasUsed: 21,
Logs: []*Log{},
// derived fields:
TxHash: txs[5].Hash(),
GasUsed: 6,
EffectiveGasPrice: big.NewInt(1066),
BlobGasUsed: params.BlobTxBlobGasPerBlob,
BlobGasPrice: big.NewInt(920),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 5,
},
&Receipt{
Type: BlobTxType,
PostState: common.Hash{7}.Bytes(),
CumulativeGasUsed: 28,
Logs: []*Log{},
// derived fields:
TxHash: txs[6].Hash(),
GasUsed: 7,
EffectiveGasPrice: big.NewInt(1077),
BlobGasUsed: 3 * params.BlobTxBlobGasPerBlob,
BlobGasPrice: big.NewInt(920),
BlockHash: blockHash,
BlockNumber: blockNumber,
TransactionIndex: 6,
},
}
for _, receipt := range r {
receipt.Bloom = CreateBloom(receipt)
}
testReceipts = r
})
return testReceipts
}
func TestDecodeEmptyTypedReceipt(t *testing.T) { func TestDecodeEmptyTypedReceipt(t *testing.T) {
input := []byte{0x80} input := []byte{0x80}
var r Receipt var r Receipt
@ -310,6 +325,7 @@ func TestDeriveFields(t *testing.T) {
// Re-derive receipts. // Re-derive receipts.
basefee := big.NewInt(1000) basefee := big.NewInt(1000)
blobGasPrice := big.NewInt(920) blobGasPrice := big.NewInt(920)
receipts := getTestReceipts()
derivedReceipts := clearComputedFieldsOnReceipts(receipts) derivedReceipts := clearComputedFieldsOnReceipts(receipts)
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, basefee, blobGasPrice, txs) err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, basefee, blobGasPrice, txs)
if err != nil { if err != nil {
@ -335,6 +351,7 @@ func TestDeriveFields(t *testing.T) {
// Test that we can marshal/unmarshal receipts to/from json without errors. // Test that we can marshal/unmarshal receipts to/from json without errors.
// This also confirms that our test receipts contain all the required fields. // This also confirms that our test receipts contain all the required fields.
func TestReceiptJSON(t *testing.T) { func TestReceiptJSON(t *testing.T) {
receipts := getTestReceipts()
for i := range receipts { for i := range receipts {
b, err := receipts[i].MarshalJSON() b, err := receipts[i].MarshalJSON()
if err != nil { if err != nil {
@ -351,6 +368,7 @@ func TestReceiptJSON(t *testing.T) {
// Test we can still parse receipt without EffectiveGasPrice for backwards compatibility, even // Test we can still parse receipt without EffectiveGasPrice for backwards compatibility, even
// though it is required per the spec. // though it is required per the spec.
func TestEffectiveGasPriceNotRequired(t *testing.T) { func TestEffectiveGasPriceNotRequired(t *testing.T) {
receipts := getTestReceipts()
r := *receipts[0] r := *receipts[0]
r.EffectiveGasPrice = nil r.EffectiveGasPrice = nil
b, err := r.MarshalJSON() b, err := r.MarshalJSON()
@ -511,6 +529,7 @@ func clearComputedFieldsOnReceipt(receipt *Receipt) *Receipt {
cpy.EffectiveGasPrice = big.NewInt(0) cpy.EffectiveGasPrice = big.NewInt(0)
cpy.BlobGasUsed = 0 cpy.BlobGasUsed = 0
cpy.BlobGasPrice = nil cpy.BlobGasPrice = nil
cpy.Bloom = CreateBloom(&cpy)
return &cpy return &cpy
} }

View file

@ -255,7 +255,7 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et
// peer in the download tester. The returned function can be used to retrieve // peer in the download tester. The returned function can be used to retrieve
// batches of block receipts from the particularly requested peer. // batches of block receipts from the particularly requested peer.
func (dlp *downloadTesterPeer) RequestReceipts(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) { func (dlp *downloadTesterPeer) RequestReceipts(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) {
blobs := eth.ServiceGetReceiptsQuery(dlp.chain, hashes) blobs := eth.ServiceGetReceiptsQuery68(dlp.chain, hashes)
receipts := make([][]*types.Receipt, len(blobs)) receipts := make([][]*types.Receipt, len(blobs))
for i, blob := range blobs { for i, blob := range blobs {

View file

@ -175,8 +175,21 @@ var eth68 = map[uint64]msgHandler{
BlockHeadersMsg: handleBlockHeaders, BlockHeadersMsg: handleBlockHeaders,
GetBlockBodiesMsg: handleGetBlockBodies, GetBlockBodiesMsg: handleGetBlockBodies,
BlockBodiesMsg: handleBlockBodies, BlockBodiesMsg: handleBlockBodies,
GetReceiptsMsg: handleGetReceipts, GetReceiptsMsg: handleGetReceipts68,
ReceiptsMsg: handleReceipts, ReceiptsMsg: handleReceipts68,
GetPooledTransactionsMsg: handleGetPooledTransactions,
PooledTransactionsMsg: handlePooledTransactions,
}
var eth69 = map[uint64]msgHandler{
TransactionsMsg: handleTransactions,
NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes,
GetBlockHeadersMsg: handleGetBlockHeaders,
BlockHeadersMsg: handleBlockHeaders,
GetBlockBodiesMsg: handleGetBlockBodies,
BlockBodiesMsg: handleBlockBodies,
GetReceiptsMsg: handleGetReceipts69,
ReceiptsMsg: handleReceipts69,
GetPooledTransactionsMsg: handleGetPooledTransactions, GetPooledTransactionsMsg: handleGetPooledTransactions,
PooledTransactionsMsg: handlePooledTransactions, PooledTransactionsMsg: handlePooledTransactions,
} }
@ -194,7 +207,14 @@ func handleMessage(backend Backend, peer *Peer) error {
} }
defer msg.Discard() defer msg.Discard()
var handlers = eth68 var handlers map[uint64]msgHandler
if peer.version == ETH68 {
handlers = eth68
} else if peer.version == ETH69 {
handlers = eth69
} else {
return fmt.Errorf("unknown eth protocol version: %v", peer.version)
}
// Track the amount of time it takes to serve the request and run the handler // Track the amount of time it takes to serve the request and run the handler
if metrics.Enabled() { if metrics.Enabled() {

View file

@ -699,3 +699,41 @@ func testGetPooledTransaction(t *testing.T, blobTx bool) {
t.Errorf("pooled transaction mismatch: %v", err) t.Errorf("pooled transaction mismatch: %v", err)
} }
} }
func TestTransformReceipts(t *testing.T) {
tests := []struct {
input []types.ReceiptForStorage
txs []*types.Transaction
output []receiptForNetwork
}{
{
input: []types.ReceiptForStorage{{CumulativeGasUsed: 123, Status: 1, Logs: nil}},
txs: []*types.Transaction{types.NewTx(&types.LegacyTx{})},
output: []receiptForNetwork{{GasUsed: 123, Status: 1, Logs: nil}},
},
{
input: []types.ReceiptForStorage{{CumulativeGasUsed: 123, Status: 1, Logs: nil}},
txs: []*types.Transaction{types.NewTx(&types.DynamicFeeTx{})},
output: []receiptForNetwork{{GasUsed: 123, Status: 1, Logs: nil, Type: 2}},
},
{
input: []types.ReceiptForStorage{{CumulativeGasUsed: 123, Status: 1, Logs: nil}},
txs: []*types.Transaction{types.NewTx(&types.AccessListTx{})},
output: []receiptForNetwork{{GasUsed: 123, Status: 1, Logs: nil, Type: 1}},
},
{
input: []types.ReceiptForStorage{{CumulativeGasUsed: 123, Status: 1, Logs: []*types.Log{{Address: common.Address{1}, Topics: []common.Hash{{1}}}}}},
txs: []*types.Transaction{types.NewTx(&types.AccessListTx{})},
output: []receiptForNetwork{{GasUsed: 123, Status: 1, Logs: []*types.Log{{Address: common.Address{1}, Topics: []common.Hash{{1}}}}, Type: 1}},
},
}
for i, test := range tests {
in, _ := rlp.EncodeToBytes(test.input)
have := transformReceipts(in, test.txs)
out, _ := rlp.EncodeToBytes(test.output)
if !bytes.Equal(have, out) {
t.Fatalf("transforming receipt mismatch, test %v: want %v have %v", i, out, have)
}
}
}

View file

@ -17,6 +17,7 @@
package eth package eth
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -243,19 +244,29 @@ func ServiceGetBlockBodiesQuery(chain *core.BlockChain, query GetBlockBodiesRequ
return bodies return bodies
} }
func handleGetReceipts(backend Backend, msg Decoder, peer *Peer) error { func handleGetReceipts68(backend Backend, msg Decoder, peer *Peer) error {
// Decode the block receipts retrieval message // Decode the block receipts retrieval message
var query GetReceiptsPacket var query GetReceiptsPacket
if err := msg.Decode(&query); err != nil { if err := msg.Decode(&query); err != nil {
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
} }
response := ServiceGetReceiptsQuery(backend.Chain(), query.GetReceiptsRequest) response := ServiceGetReceiptsQuery68(backend.Chain(), query.GetReceiptsRequest)
return peer.ReplyReceiptsRLP(query.RequestId, response) return peer.ReplyReceiptsRLP(query.RequestId, response)
} }
// ServiceGetReceiptsQuery assembles the response to a receipt query. It is func handleGetReceipts69(backend Backend, msg Decoder, peer *Peer) error {
// Decode the block receipts retrieval message
var query GetReceiptsPacket
if err := msg.Decode(&query); err != nil {
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
}
response := serviceGetReceiptsQuery69(backend.Chain(), query.GetReceiptsRequest)
return peer.ReplyReceiptsRLP(query.RequestId, response)
}
// ServiceGetReceiptsQuery68 assembles the response to a receipt query. It is
// exposed to allow external packages to test protocol behavior. // exposed to allow external packages to test protocol behavior.
func ServiceGetReceiptsQuery(chain *core.BlockChain, query GetReceiptsRequest) []rlp.RawValue { func ServiceGetReceiptsQuery68(chain *core.BlockChain, query GetReceiptsRequest) []rlp.RawValue {
// Gather state data until the fetch or network limits is reached // Gather state data until the fetch or network limits is reached
var ( var (
bytes int bytes int
@ -284,6 +295,67 @@ func ServiceGetReceiptsQuery(chain *core.BlockChain, query GetReceiptsRequest) [
return receipts return receipts
} }
// serviceGetReceiptsQuery69 assembles the response to a receipt query.
// It does not send the bloom filters for the receipts
func serviceGetReceiptsQuery69(chain *core.BlockChain, query GetReceiptsRequest) []rlp.RawValue {
// Gather state data until the fetch or network limits is reached
var (
bytes int
receipts []rlp.RawValue
)
for lookups, hash := range query {
if bytes >= softResponseLimit || len(receipts) >= maxReceiptsServe ||
lookups >= 2*maxReceiptsServe {
break
}
// Retrieve the requested block's receipts
results := chain.GetRawReceiptsByHash(hash)
if results == nil {
if header := chain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
continue
}
} else {
header := chain.GetHeaderByHash(hash)
if header.ReceiptHash != types.EmptyReceiptsHash {
body := new(types.Body)
if err := rlp.DecodeBytes(chain.GetBodyRLP(hash), &body); err == nil {
results = transformReceipts(results, body.Transactions)
}
}
}
receipts = append(receipts, results)
bytes += len(results)
}
return receipts
}
// transformReceipts takes a slice of rlp-encoded receipts, and transactions,
// and applies the type-encoding on the receipts (for non-legacy receipts).
// e.g. for non-legacy receipts: receipt-data -> {tx-type || receipt-data}
func transformReceipts(blockReceipts []byte, txs []*types.Transaction) []byte {
var (
out bytes.Buffer
enc = rlp.NewEncoderBuffer(&out)
it, _ = rlp.NewListIterator(blockReceipts)
)
outer := enc.List()
for i := 0; it.Next(); i++ {
if txs[i].Type() == types.LegacyTxType {
enc.Write(it.Value())
continue
}
content, _, _ := rlp.SplitList(it.Value())
receiptList := enc.List()
enc.Write([]byte{txs[i].Type()})
enc.Write(content)
enc.ListEnd(receiptList)
}
enc.ListEnd(outer)
enc.Flush()
return out.Bytes()
}
func handleNewBlockhashes(backend Backend, msg Decoder, peer *Peer) error { func handleNewBlockhashes(backend Backend, msg Decoder, peer *Peer) error {
return errors.New("block announcements disallowed") // We dropped support for non-merge networks return errors.New("block announcements disallowed") // We dropped support for non-merge networks
} }
@ -341,7 +413,7 @@ func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error {
}, metadata) }, metadata)
} }
func handleReceipts(backend Backend, msg Decoder, peer *Peer) error { func handleReceipts68(backend Backend, msg Decoder, peer *Peer) error {
// A batch of receipts arrived to one of our previous requests // A batch of receipts arrived to one of our previous requests
res := new(ReceiptsPacket) res := new(ReceiptsPacket)
if err := msg.Decode(res); err != nil { if err := msg.Decode(res); err != nil {
@ -362,6 +434,41 @@ func handleReceipts(backend Backend, msg Decoder, peer *Peer) error {
}, metadata) }, metadata)
} }
func handleReceipts69(backend Backend, msg Decoder, peer *Peer) error {
// A batch of receipts arrived to one of our previous requests
res := new(ReceiptsPacket69)
if err := msg.Decode(res); err != nil {
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
}
var response ReceiptsResponse
// calculate the bloom filter before dispatching
for _, receipts := range res.ReceiptsResponse69 {
var rec []*types.Receipt
for _, receipt := range receipts {
receipt.Bloom = types.CreateBloom((*types.Receipt)(receipt))
rec = append(rec, (*types.Receipt)(receipt))
}
response = append(response, rec)
}
metadata := func() interface{} {
hasher := trie.NewStackTrie(nil)
hashes := make([]common.Hash, len(response))
for i, receipts := range response {
var r []*types.Receipt
for _, receipt := range receipts {
r = append(r, (*types.Receipt)(receipt))
}
hashes[i] = types.DeriveSha(types.Receipts(r), hasher)
}
return hashes
}
return peer.dispatchResponse(&Response{
id: res.RequestId,
code: ReceiptsMsg,
Res: &response,
}, metadata)
}
func handleNewPooledTransactionHashes(backend Backend, msg Decoder, peer *Peer) error { func handleNewPooledTransactionHashes(backend Backend, msg Decoder, peer *Peer) error {
// New transaction announcement arrived, make sure we have // New transaction announcement arrived, make sure we have
// a valid and fresh chain to handle them // a valid and fresh chain to handle them

View file

@ -19,7 +19,6 @@ package eth
import ( import (
"errors" "errors"
"fmt" "fmt"
"math/big"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -43,14 +42,14 @@ func (p *Peer) Handshake(network uint64, head common.Hash, genesis common.Hash,
var status StatusPacket // safe to read after two values have been received from errc var status StatusPacket // safe to read after two values have been received from errc
go func() { go func() {
errc <- p2p.Send(p.rw, StatusMsg, &StatusPacket{ pkt := &StatusPacket{
ProtocolVersion: uint32(p.version), ProtocolVersion: uint32(p.version),
NetworkID: network, NetworkID: network,
TD: new(big.Int), // unknown for post-merge tail=pruned networks
Head: head, Head: head,
Genesis: genesis, Genesis: genesis,
ForkID: forkID, ForkID: forkID,
}) }
errc <- p2p.Send(p.rw, StatusMsg, pkt)
}() }()
go func() { go func() {
errc <- p.readStatus(network, &status, genesis, forkFilter) errc <- p.readStatus(network, &status, genesis, forkFilter)

View file

@ -31,6 +31,7 @@ import (
// Constants to match up protocol versions and messages // Constants to match up protocol versions and messages
const ( const (
ETH68 = 68 ETH68 = 68
ETH69 = 69
) )
// ProtocolName is the official short name of the `eth` protocol used during // ProtocolName is the official short name of the `eth` protocol used during
@ -39,11 +40,11 @@ const ProtocolName = "eth"
// ProtocolVersions are the supported versions of the `eth` protocol (first // ProtocolVersions are the supported versions of the `eth` protocol (first
// is primary). // is primary).
var ProtocolVersions = []uint{ETH68} var ProtocolVersions = []uint{ETH69, ETH68}
// protocolLengths are the number of implemented message corresponding to // protocolLengths are the number of implemented message corresponding to
// different protocol versions. // different protocol versions.
var protocolLengths = map[uint]uint64{ETH68: 17} var protocolLengths = map[uint]uint64{ETH68: 17, ETH69: 17}
// maxMessageSize is the maximum cap on the size of a protocol message. // maxMessageSize is the maximum cap on the size of a protocol message.
const maxMessageSize = 10 * 1024 * 1024 const maxMessageSize = 10 * 1024 * 1024
@ -259,6 +260,16 @@ type ReceiptsPacket struct {
ReceiptsResponse ReceiptsResponse
} }
// ReceiptsResponse is the network packet for block receipts distribution.
type ReceiptsResponse69 [][]*receiptForNetwork
// ReceiptsPacket is the network packet for block receipts distribution with
// request ID wrapping.
type ReceiptsPacket69 struct {
RequestId uint64
ReceiptsResponse69
}
// ReceiptsRLPResponse is used for receipts, when we already have it encoded // ReceiptsRLPResponse is used for receipts, when we already have it encoded
type ReceiptsRLPResponse []rlp.RawValue type ReceiptsRLPResponse []rlp.RawValue

View file

@ -0,0 +1,80 @@
// Copyright 2024 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package eth
import (
"errors"
"io"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
)
type receiptForNetwork types.Receipt
func (r *receiptForNetwork) DecodeRLP(s *rlp.Stream) error {
kind, size, err := s.Kind()
if err != nil {
return err
}
if kind != rlp.List {
return errors.New("invalid receipt")
}
if size == 4 {
var rec = new(struct {
Type byte
Status uint64
GasUsed uint64
Logs []*types.Log
})
if err := s.Decode(rec); err != nil {
return err
}
r.Type = rec.Type
r.Status = rec.Status
r.GasUsed = rec.GasUsed
r.Logs = rec.Logs
} else {
s.Decode(&r)
}
return nil
}
func (r *receiptForNetwork) EncodeRLP(_w io.Writer) error {
data := &types.ReceiptForStorage{Status: r.Status, CumulativeGasUsed: r.GasUsed, Logs: r.Logs}
if r.Type == types.LegacyTxType {
return rlp.Encode(_w, data)
}
w := rlp.NewEncoderBuffer(_w)
outerList := w.List()
w.Write([]byte{r.Type})
if r.Status == types.ReceiptStatusSuccessful {
w.Write([]byte{0x01})
} else {
w.Write([]byte{0x00})
}
w.WriteUint64(r.GasUsed)
logList := w.List()
for _, log := range r.Logs {
if err := log.EncodeRLP(w); err != nil {
return err
}
}
w.ListEnd(logList)
w.ListEnd(outerList)
return w.Flush()
}