Skip to content

Commit bea4431

Browse files
JukLee0iragzliudan
authored andcommitted
eth/filters: avoid block body retrieval when no matching logs (ethereum#25199)
1 parent 17dafcc commit bea4431

File tree

11 files changed

+345
-98
lines changed

11 files changed

+345
-98
lines changed

accounts/abi/bind/backends/simulated.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,13 @@ func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (typ
555555
return core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash)), nil
556556
}
557557

558+
func (fb *filterBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
559+
if body := fb.bc.GetBody(hash); body != nil {
560+
return body, nil
561+
}
562+
return nil, errors.New("block body not found")
563+
}
564+
558565
func (fb *filterBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
559566
return fb.backend.pendingBlock, fb.backend.pendingReceipts
560567
}

core/rawdb/accessors_chain.go

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,22 @@ import (
2929
"github.com/XinFinOrg/XDPoSChain/rlp"
3030
)
3131

32+
// WriteCanonicalHash stores the hash assigned to a canonical block number.
33+
func WriteCanonicalHash(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
34+
if err := db.Put(headerHashKey(number), hash.Bytes()); err != nil {
35+
log.Crit("Failed to store number to hash mapping", "err", err)
36+
}
37+
}
38+
39+
// WriteHeaderNumber stores the hash->number mapping.
40+
func WriteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
41+
key := headerNumberKey(hash)
42+
enc := encodeBlockNumber(number)
43+
if err := db.Put(key, enc); err != nil {
44+
log.Crit("Failed to store hash to number mapping", "err", err)
45+
}
46+
}
47+
3248
// ReadHeaderNumber returns the header number assigned to a hash.
3349
func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
3450
data, _ := db.Get(headerNumberKey(hash))
@@ -39,6 +55,13 @@ func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
3955
return &number
4056
}
4157

58+
// WriteHeadBlockHash stores the head block's hash.
59+
func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
60+
if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
61+
log.Crit("Failed to store last block's hash", "err", err)
62+
}
63+
}
64+
4265
// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
4366
func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
4467
// First try to look up the data in ancient database. Extra hash
@@ -100,6 +123,27 @@ func WriteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64, body *t
100123
WriteBodyRLP(db, hash, number, data)
101124
}
102125

126+
// WriteHeader stores a block header into the database and also stores the hash-
127+
// to-number mapping.
128+
func WriteHeader(db ethdb.KeyValueWriter, header *types.Header) {
129+
var (
130+
hash = header.Hash()
131+
number = header.Number.Uint64()
132+
)
133+
// Write the hash -> number mapping
134+
WriteHeaderNumber(db, hash, number)
135+
136+
// Write the encoded header
137+
data, err := rlp.EncodeToBytes(header)
138+
if err != nil {
139+
log.Crit("Failed to RLP encode header", "err", err)
140+
}
141+
key := headerKey(number, hash)
142+
if err := db.Put(key, data); err != nil {
143+
log.Crit("Failed to store header", "err", err)
144+
}
145+
}
146+
103147
// ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding.
104148
func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
105149
// First try to look up the data in ancient database. Extra hash
@@ -195,6 +239,12 @@ func WriteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rec
195239
}
196240
}
197241

242+
// WriteBlock serializes a block into the database, header and body separately.
243+
func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
244+
WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
245+
WriteHeader(db, block.Header())
246+
}
247+
198248
// storedReceiptRLP is the storage encoding of a receipt.
199249
// Re-definition in core/types/receipt.go.
200250
type storedReceiptRLP struct {
@@ -248,9 +298,9 @@ func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, t
248298
return nil
249299
}
250300

251-
// ReadLogs retrieves the logs for all transactions in a block. The log fields
252-
// are populated with metadata. In case the receipts or the block body
253-
// are not found, a nil is returned.
301+
// ReadLogs retrieves the logs for all transactions in a block. In case
302+
// receipts is not found, a nil is returned.
303+
// Note: ReadLogs does not derive unstored log fields.
254304
func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log {
255305
// Retrieve the flattened receipt slice
256306
data := ReadReceiptsRLP(db, hash, number)
@@ -263,15 +313,6 @@ func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log {
263313
return nil
264314
}
265315

266-
body := ReadBody(db, hash, number)
267-
if body == nil {
268-
log.Error("Missing body but have receipt", "hash", hash, "number", number)
269-
return nil
270-
}
271-
if err := deriveLogFields(receipts, hash, number, body.Transactions); err != nil {
272-
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
273-
return nil
274-
}
275316
logs := make([][]*types.Log, len(receipts))
276317
for i, receipt := range receipts {
277318
logs[i] = receipt.Logs

core/rawdb/accessors_chain_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,6 @@ func TestReadLogs(t *testing.T) {
116116
t.Fatalf("unexpected number of logs[1] returned, have %d want %d", have, want)
117117
}
118118

119-
// Fill in log fields so we can compare their rlp encoding
120-
if err := types.Receipts(receipts).DeriveFields(params.TestChainConfig, hash, 0, body.Transactions); err != nil {
121-
t.Fatal(err)
122-
}
123119
for i, pr := range receipts {
124120
for j, pl := range pr.Logs {
125121
rlpHave, err := rlp.EncodeToBytes(newFullLogRLP(logs[i][j]))

core/rawdb/schema.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,13 @@ import (
2525

2626
// The fields below define the low level database schema prefixing.
2727
var (
28+
headBlockKey = []byte("LastBlock")
2829
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
30+
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
31+
headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
2932
headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian)
30-
blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
3133

34+
blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
3235
blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
3336
)
3437

@@ -50,6 +53,16 @@ func encodeBlockNumber(number uint64) []byte {
5053
return enc
5154
}
5255

56+
// headerKey = headerPrefix + num (uint64 big endian) + hash
57+
func headerKey(number uint64, hash common.Hash) []byte {
58+
return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
59+
}
60+
61+
// headerHashKey = headerPrefix + num (uint64 big endian) + headerHashSuffix
62+
func headerHashKey(number uint64) []byte {
63+
return append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...)
64+
}
65+
5366
// headerNumberKey = headerNumberPrefix + hash
5467
func headerNumberKey(hash common.Hash) []byte {
5568
return append(headerNumberPrefix, hash.Bytes()...)

eth/api_backend.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,17 @@ func (b *EthApiBackend) BlockByHash(ctx context.Context, hash common.Hash) (*typ
159159
return b.eth.blockchain.GetBlockByHash(hash), nil
160160
}
161161

162+
// GetBody returns body of a block. It does not resolve special block numbers.
163+
func (b *EthApiBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
164+
if number < 0 || hash == (common.Hash{}) {
165+
return nil, errors.New("invalid arguments; expect hash and no special block numbers")
166+
}
167+
if body := b.eth.blockchain.GetBody(hash); body != nil {
168+
return body, nil
169+
}
170+
return nil, errors.New("block body not found")
171+
}
172+
162173
func (b *EthApiBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
163174
if blockNr, ok := blockNrOrHash.Number(); ok {
164175
return b.BlockByNumber(ctx, blockNr)

eth/filters/filter.go

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
104104
if header == nil {
105105
return nil, errors.New("unknown block")
106106
}
107-
return f.blockLogs(ctx, header, false)
107+
return f.blockLogs(ctx, header)
108108
}
109109
// Short-cut if all we care about is pending logs
110110
if f.begin == rpc.PendingBlockNumber.Int64() {
@@ -192,7 +192,7 @@ func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, err
192192
if header == nil || err != nil {
193193
return logs, err
194194
}
195-
found, err := f.blockLogs(ctx, header, true)
195+
found, err := f.blockLogs(ctx, header)
196196
if err != nil {
197197
return logs, err
198198
}
@@ -214,7 +214,7 @@ func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, e
214214
if header == nil || err != nil {
215215
return logs, err
216216
}
217-
found, err := f.blockLogs(ctx, header, false)
217+
found, err := f.blockLogs(ctx, header)
218218
if err != nil {
219219
return logs, err
220220
}
@@ -224,46 +224,46 @@ func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, e
224224
}
225225

226226
// blockLogs returns the logs matching the filter criteria within a single block.
227-
func (f *Filter) blockLogs(ctx context.Context, header *types.Header, skipBloom bool) ([]*types.Log, error) {
228-
// Fast track: no filtering criteria
229-
if len(f.addresses) == 0 && len(f.topics) == 0 {
230-
list, err := f.sys.cachedGetLogs(ctx, header.Hash(), header.Number.Uint64())
231-
if err != nil {
232-
return nil, err
233-
}
234-
return flatten(list), nil
235-
} else if skipBloom || bloomFilter(header.Bloom, f.addresses, f.topics) {
227+
func (f *Filter) blockLogs(ctx context.Context, header *types.Header) ([]*types.Log, error) {
228+
if bloomFilter(header.Bloom, f.addresses, f.topics) {
236229
return f.checkMatches(ctx, header)
237230
}
238231
return nil, nil
239232
}
240233

241234
// checkMatches checks if the receipts belonging to the given header contain any log events that
242235
// match the filter criteria. This function is called when the bloom filter signals a potential match.
236+
// skipFilter signals all logs of the given block are requested.
243237
func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*types.Log, error) {
244-
logsList, err := f.sys.cachedGetLogs(ctx, header.Hash(), header.Number.Uint64())
238+
hash := header.Hash()
239+
// Logs in cache are partially filled with context data
240+
// such as tx index, block hash, etc.
241+
// Notably tx hash is NOT filled in because it needs
242+
// access to block body data.
243+
cached, err := f.sys.cachedLogElem(ctx, hash, header.Number.Uint64())
245244
if err != nil {
246245
return nil, err
247246
}
248-
249-
unfiltered := flatten(logsList)
250-
logs := filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
251-
if len(logs) > 0 {
252-
// We have matching logs, check if we need to resolve full logs via the light client
253-
if logs[0].TxHash == (common.Hash{}) {
254-
receipts, err := f.sys.backend.GetReceipts(ctx, header.Hash())
255-
if err != nil {
256-
return nil, err
257-
}
258-
unfiltered = unfiltered[:0]
259-
for _, receipt := range receipts {
260-
unfiltered = append(unfiltered, receipt.Logs...)
261-
}
262-
logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
263-
}
247+
logs := filterLogs(cached.logs, nil, nil, f.addresses, f.topics)
248+
if len(logs) == 0 {
249+
return nil, nil
250+
}
251+
// Most backends will deliver un-derived logs, but check nevertheless.
252+
if len(logs) > 0 && logs[0].TxHash != (common.Hash{}) {
264253
return logs, nil
265254
}
266-
return nil, nil
255+
256+
body, err := f.sys.cachedGetBody(ctx, cached, hash, header.Number.Uint64())
257+
if err != nil {
258+
return nil, err
259+
}
260+
for i, log := range logs {
261+
// Copy log not to modify cache elements
262+
logcopy := *log
263+
logcopy.TxHash = body.Transactions[logcopy.TxIndex].Hash()
264+
logs[i] = &logcopy
265+
}
266+
return logs, nil
267267
}
268268

269269
// pendingLogs returns the logs matching the filter criteria within the pending block.
@@ -353,11 +353,3 @@ func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]commo
353353
}
354354
return true
355355
}
356-
357-
func flatten(list [][]*types.Log) []*types.Log {
358-
var flat []*types.Log
359-
for _, logs := range list {
360-
flat = append(flat, logs...)
361-
}
362-
return flat
363-
}

0 commit comments

Comments
 (0)