From df2b3cd2bd6781098a4bba12fba104a5e3a63f36 Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Thu, 6 Oct 2022 05:43:55 -0700 Subject: [PATCH] eth/filters: fix for eth_getLogs failing with finalized- and safe tag (#25922) Prior to this change, f.begin (and possibly end) stay negative, leading to strange results later in the code. With this change, filters using "safe" and "finalized" block produce results consistent w/ the overall behavior of this RPC method. Co-authored-by: Martin Holst Swende --- accounts/abi/bind/backends/simulated.go | 23 +++++- eth/filters/filter.go | 38 +++++++-- eth/filters/filter_system_test.go | 15 +++- eth/filters/filter_test.go | 103 +++++++++++++----------- 4 files changed, 120 insertions(+), 59 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 21c94d7e1..277850e3b 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -844,11 +844,28 @@ func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db } func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") } -func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) { - if block == rpc.LatestBlockNumber { +func (fb *filterBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { + switch number { + case rpc.PendingBlockNumber: + if block := fb.backend.pendingBlock; block != nil { + return block.Header(), nil + } + return nil, nil + case rpc.LatestBlockNumber: return fb.bc.CurrentHeader(), nil + case rpc.FinalizedBlockNumber: + if block := fb.bc.CurrentFinalizedBlock(); block != nil { + return block.Header(), nil + } + return nil, errors.New("finalized block not found") + case rpc.SafeBlockNumber: + if block := fb.bc.CurrentSafeBlock(); block != nil { + return block.Header(), nil + } + return nil, errors.New("safe block not found") + default: + return fb.bc.GetHeaderByNumber(uint64(number.Int64())), nil } - return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil } func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { diff --git a/eth/filters/filter.go b/eth/filters/filter.go index 0a70c9ece..fbbb4a7aa 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -119,20 +119,44 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { return nil, nil } var ( - head = header.Number.Uint64() - end = uint64(f.end) + err error + head = header.Number.Int64() pending = f.end == rpc.PendingBlockNumber.Int64() ) - if f.begin == rpc.LatestBlockNumber.Int64() { - f.begin = int64(head) + resolveSpecial := func(number int64) (int64, error) { + var hdr *types.Header + switch number { + case rpc.LatestBlockNumber.Int64(): + return head, nil + case rpc.PendingBlockNumber.Int64(): + // we should return head here since we've already captured + // that we need to get the pending logs in the pending boolean above + return head, nil + case rpc.FinalizedBlockNumber.Int64(): + hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.FinalizedBlockNumber) + if hdr == nil { + return 0, errors.New("finalized header not found") + } + case rpc.SafeBlockNumber.Int64(): + hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.SafeBlockNumber) + if hdr == nil { + return 0, errors.New("safe header not found") + } + default: + return number, nil + } + return hdr.Number.Int64(), nil } - if f.end == rpc.LatestBlockNumber.Int64() || f.end == rpc.PendingBlockNumber.Int64() { - end = head + if f.begin, err = resolveSpecial(f.begin); err != nil { + return nil, err + } + if f.end, err = resolveSpecial(f.end); err != nil { + return nil, err } // Gather all indexed logs, and finish with non indexed ones var ( logs []*types.Log - err error + end = uint64(f.end) size, sections = f.sys.backend.BloomStatus() ) if indexed := sections * size; indexed > uint64(f.begin) { diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 73a4ab2d4..4386f0e5b 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -18,6 +18,7 @@ package filters import ( "context" + "errors" "fmt" "math/big" "math/rand" @@ -58,14 +59,24 @@ func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumbe hash common.Hash num uint64 ) - if blockNr == rpc.LatestBlockNumber { + switch blockNr { + case rpc.LatestBlockNumber: hash = rawdb.ReadHeadBlockHash(b.db) number := rawdb.ReadHeaderNumber(b.db, hash) if number == nil { return nil, nil } num = *number - } else { + case rpc.FinalizedBlockNumber: + hash = rawdb.ReadFinalizedBlockHash(b.db) + number := rawdb.ReadHeaderNumber(b.db, hash) + if number == nil { + return nil, nil + } + num = *number + case rpc.SafeBlockNumber: + return nil, errors.New("safe block not found") + default: num = uint64(blockNr) hash = rawdb.ReadCanonicalHash(b.db, num) } diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index 39ed46cec..2a4dfd90a 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -19,6 +19,7 @@ package filters import ( "context" "math/big" + "reflect" "testing" "github.com/ethereum/go-ethereum/common" @@ -170,58 +171,66 @@ func TestFilters(t *testing.T) { rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), receipts[i]) } - filter := sys.NewRangeFilter(0, -1, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}}) + // Set block 998 as Finalized (-3) + rawdb.WriteFinalizedBlockHash(db, chain[998].Hash()) + filter := sys.NewRangeFilter(0, -1, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}}) logs, _ := filter.Logs(context.Background()) if len(logs) != 4 { t.Error("expected 4 log, got", len(logs)) } - filter = sys.NewRangeFilter(900, 999, []common.Address{addr}, [][]common.Hash{{hash3}}) - logs, _ = filter.Logs(context.Background()) - if len(logs) != 1 { - t.Error("expected 1 log, got", len(logs)) - } - if len(logs) > 0 && logs[0].Topics[0] != hash3 { - t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) - } - - filter = sys.NewRangeFilter(990, -1, []common.Address{addr}, [][]common.Hash{{hash3}}) - logs, _ = filter.Logs(context.Background()) - if len(logs) != 1 { - t.Error("expected 1 log, got", len(logs)) - } - if len(logs) > 0 && logs[0].Topics[0] != hash3 { - t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) - } - - filter = sys.NewRangeFilter(1, 10, nil, [][]common.Hash{{hash1, hash2}}) - - logs, _ = filter.Logs(context.Background()) - if len(logs) != 2 { - t.Error("expected 2 log, got", len(logs)) - } - - failHash := common.BytesToHash([]byte("fail")) - filter = sys.NewRangeFilter(0, -1, nil, [][]common.Hash{{failHash}}) - - logs, _ = filter.Logs(context.Background()) - if len(logs) != 0 { - t.Error("expected 0 log, got", len(logs)) - } - - failAddr := common.BytesToAddress([]byte("failmenow")) - filter = sys.NewRangeFilter(0, -1, []common.Address{failAddr}, nil) - - logs, _ = filter.Logs(context.Background()) - if len(logs) != 0 { - t.Error("expected 0 log, got", len(logs)) - } - - filter = sys.NewRangeFilter(0, -1, nil, [][]common.Hash{{failHash}, {hash1}}) - - logs, _ = filter.Logs(context.Background()) - if len(logs) != 0 { - t.Error("expected 0 log, got", len(logs)) + for i, tc := range []struct { + f *Filter + wantHashes []common.Hash + }{ + { + sys.NewRangeFilter(900, 999, []common.Address{addr}, [][]common.Hash{{hash3}}), + []common.Hash{hash3}, + }, { + sys.NewRangeFilter(990, -1, []common.Address{addr}, [][]common.Hash{{hash3}}), + []common.Hash{hash3}, + }, { + sys.NewRangeFilter(1, 10, nil, [][]common.Hash{{hash1, hash2}}), + []common.Hash{hash1, hash2}, + }, { + sys.NewRangeFilter(0, -1, nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}}), + nil, + }, { + sys.NewRangeFilter(0, -1, []common.Address{common.BytesToAddress([]byte("failmenow"))}, nil), + nil, + }, { + sys.NewRangeFilter(0, -1, nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}, {hash1}}), + nil, + }, { + sys.NewRangeFilter(-1, -1, nil, nil), []common.Hash{hash4}, + }, { + sys.NewRangeFilter(-3, -1, nil, nil), []common.Hash{hash3, hash4}, + }, { + sys.NewRangeFilter(-3, -3, nil, nil), []common.Hash{hash3}, + }, { + sys.NewRangeFilter(-1, -3, nil, nil), nil, + }, { + sys.NewRangeFilter(-4, -1, nil, nil), nil, + }, { + sys.NewRangeFilter(-4, -4, nil, nil), nil, + }, { + sys.NewRangeFilter(-1, -4, nil, nil), nil, + }, + } { + logs, _ := tc.f.Logs(context.Background()) + var haveHashes []common.Hash + for _, l := range logs { + haveHashes = append(haveHashes, l.Topics[0]) + } + if have, want := len(haveHashes), len(tc.wantHashes); have != want { + t.Fatalf("test %d, have %d logs, want %d", i, have, want) + } + if len(haveHashes) == 0 { + continue + } + if !reflect.DeepEqual(tc.wantHashes, haveHashes) { + t.Fatalf("test %d, have %v want %v", i, haveHashes, tc.wantHashes) + } } }