Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

all: derive log fields only after filtering #23655

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
39 changes: 27 additions & 12 deletions core/rawdb/accessors_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps replace fn with filterFn, and document it.
Also, is it required to be func([]*types.Log) []*types.Log, or could it be simplified (without any meaningful loss of performance) into func(*types.Log) bool ?
If so, I think that would be cleaner.
Then you could also define allLogsFilter := func(*types.Log) bool { return true } , maybe, if that's common. Or, let nil mean "no filter", which IMO is even nicer from api perspective

// Retrieve the flattened receipt slice
data := ReadReceiptsRLP(db, hash, number)
if len(data) == 0 {
Expand All @@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This clause can be de-indented, if you check the return-case first.

if len(filtered) == 0{
  return nil
}

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
Expand Down
16 changes: 6 additions & 10 deletions core/rawdb/accessors_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 13 additions & 1 deletion eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
13 changes: 6 additions & 7 deletions eth/filters/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -244,24 +245,22 @@ 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{}) {
receipts, err := f.backend.GetReceipts(ctx, header.Hash())
if err != nil {
return nil, err
}
unfiltered = unfiltered[:0]
var unfiltered []*types.Log
for _, receipt := range receipts {
unfiltered = append(unfiltered, receipt.Logs...)
}
Expand Down
1 change: 1 addition & 0 deletions internal/ethapi/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions les/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down