diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index e410522ac8bf..d0dd796e0072 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -850,6 +850,10 @@ func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*ty return logs, nil } +func (fb *filterBackend) GetLogsFiltered(ctx context.Context, hash common.Hash, _ func([]*types.Log) []*types.Log) ([]*types.Log, error) { + panic("unimplemented") +} + func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { return nullSubscription() } diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index ed1c71e20216..c22d257e3c4d 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -716,7 +716,7 @@ func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, t // ReadLogs retrieves the logs for all transactions in a block. The log fields // are populated with metadata. In case the receipts or the block body // are not found, a nil is returned. -func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log { +func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64, fn func([]*types.Log) []*types.Log) []*types.Log { // Retrieve the flattened receipt slice data := ReadReceiptsRLP(db, hash, number) if len(data) == 0 { @@ -727,21 +727,36 @@ func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log { log.Error("Invalid receipt array RLP", "hash", hash, "err", err) return nil } - - body := ReadBody(db, hash, number) - if body == nil { - log.Error("Missing body but have receipt", "hash", hash, "number", number) - return nil - } - if err := deriveLogFields(receipts, hash, number, body.Transactions); err != nil { - log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err) - return nil - } logs := make([][]*types.Log, len(receipts)) + id := uint(0) for i, receipt := range receipts { logs[i] = receipt.Logs + for _, log := range logs[i] { + log.TxIndex = uint(i) + log.Index = id + id++ + } + } + var flatLogs []*types.Log + for _, l := range logs { + flatLogs = append(flatLogs, l...) + } + filtered := fn(flatLogs) + if len(filtered) > 0 { + body := ReadBody(db, hash, number) + if body == nil { + log.Error("Missing body but have receipt", "hash", hash, "number", number) + return nil + } + for _, log := range filtered { + log.BlockNumber = number + log.BlockHash = hash + log.TxHash = body.Transactions[log.TxIndex].Hash() + } + return filtered + } else { + return []*types.Log{} } - return logs } // ReadBlock retrieves an entire block corresponding to the hash, assembling it diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 4b173c55eeb0..47ae9ced9c65 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -744,30 +744,26 @@ func TestReadLogs(t *testing.T) { // Insert the receipt slice into the database and check presence WriteReceipts(db, hash, 0, receipts) - logs := ReadLogs(db, hash, 0) + logs := ReadLogs(db, hash, 0, func(logs []*types.Log) []*types.Log { return logs }) if len(logs) == 0 { t.Fatalf("no logs returned") } - if have, want := len(logs), 2; have != want { + if have, want := len(logs), 4; have != want { t.Fatalf("unexpected number of logs returned, have %d want %d", have, want) } - if have, want := len(logs[0]), 2; have != want { - t.Fatalf("unexpected number of logs[0] returned, have %d want %d", have, want) - } - if have, want := len(logs[1]), 2; have != want { - t.Fatalf("unexpected number of logs[1] returned, have %d want %d", have, want) - } // Fill in log fields so we can compare their rlp encoding if err := types.Receipts(receipts).DeriveFields(params.TestChainConfig, hash, 0, body.Transactions); err != nil { t.Fatal(err) } + idx := 0 for i, pr := range receipts { - for j, pl := range pr.Logs { - rlpHave, err := rlp.EncodeToBytes(newFullLogRLP(logs[i][j])) + for _, pl := range pr.Logs { + rlpHave, err := rlp.EncodeToBytes(newFullLogRLP(logs[idx])) if err != nil { t.Fatal(err) } + idx++ rlpWant, err := rlp.EncodeToBytes(newFullLogRLP(pl)) if err != nil { t.Fatal(err) diff --git a/eth/api_backend.go b/eth/api_backend.go index 1af33414cda4..43dd7f186b91 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -181,12 +181,24 @@ func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (type } func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) { + receipts := b.eth.blockchain.GetReceiptsByHash(hash) + if receipts == nil { + return nil, nil + } + logs := make([][]*types.Log, len(receipts)) + for i, receipt := range receipts { + logs[i] = receipt.Logs + } + return logs, nil +} + +func (b *EthAPIBackend) GetLogsFiltered(ctx context.Context, hash common.Hash, filterFn func([]*types.Log) []*types.Log) ([]*types.Log, error) { db := b.eth.ChainDb() number := rawdb.ReadHeaderNumber(db, hash) if number == nil { return nil, errors.New("failed to get block number from hash") } - logs := rawdb.ReadLogs(db, hash, *number) + logs := rawdb.ReadLogs(db, hash, *number, filterFn) if logs == nil { return nil, errors.New("failed to get logs for block") } diff --git a/eth/filters/filter.go b/eth/filters/filter.go index 17635837af8a..43b1eaaf1616 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -36,6 +36,7 @@ type Backend interface { HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) + GetLogsFiltered(ctx context.Context, blockHash common.Hash, fn func([]*types.Log) []*types.Log) ([]*types.Log, error) SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription @@ -244,16 +245,14 @@ func (f *Filter) blockLogs(ctx context.Context, header *types.Header) (logs []*t // checkMatches checks if the receipts belonging to the given header contain any log events that // match the filter criteria. This function is called when the bloom filter signals a potential match. func (f *Filter) checkMatches(ctx context.Context, header *types.Header) (logs []*types.Log, err error) { + filterFn := func(unfiltered []*types.Log) []*types.Log { + return filterLogs(unfiltered, nil, nil, f.addresses, f.topics) + } // Get the logs of the block - logsList, err := f.backend.GetLogs(ctx, header.Hash()) + logs, err = f.backend.GetLogsFiltered(ctx, header.Hash(), filterFn) if err != nil { return nil, err } - var unfiltered []*types.Log - for _, logs := range logsList { - unfiltered = append(unfiltered, logs...) - } - logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics) if len(logs) > 0 { // We have matching logs, check if we need to resolve full logs via the light client if logs[0].TxHash == (common.Hash{}) { @@ -261,7 +260,7 @@ func (f *Filter) checkMatches(ctx context.Context, header *types.Header) (logs [ if err != nil { return nil, err } - unfiltered = unfiltered[:0] + var unfiltered []*types.Log for _, receipt := range receipts { unfiltered = append(unfiltered, receipt.Logs...) } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 1624f49635b3..c7e77d29807d 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -84,6 +84,7 @@ type Backend interface { // Filter API BloomStatus() (uint64, uint64) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) + GetLogsFiltered(ctx context.Context, blockHash common.Hash, _ func([]*types.Log) []*types.Log) ([]*types.Log, error) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription diff --git a/les/api_backend.go b/les/api_backend.go index e12984cb49e3..2bd03edf9e41 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -174,6 +174,10 @@ func (b *LesApiBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*typ return nil, nil } +func (b *LesApiBackend) GetLogsFiltered(ctx context.Context, hash common.Hash, _ func([]*types.Log) []*types.Log) ([]*types.Log, error) { + panic("unimplemented") +} + func (b *LesApiBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int { if number := rawdb.ReadHeaderNumber(b.eth.chainDb, hash); number != nil { return b.eth.blockchain.GetTdOdr(ctx, hash, *number)