From d85f796356846f40d3e00ec6d03c1a3e3410a3b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Mon, 17 Mar 2025 18:59:04 +0100 Subject: [PATCH] eth/filters: implement log filter using new log index (#31080) This PR is #2 of a 3-part series that implements the new log index intended to replace core/bloombits. Based on https://github.com/ethereum/go-ethereum/pull/31079 Replaces https://github.com/ethereum/go-ethereum/pull/30370 This part replaces the old bloombits based log search logic in `eth/filters` to use the new `core/filtermaps` logic. FilterMaps data structure explanation: https://gist.github.com/zsfelfoldi/a60795f9da7ae6422f28c7a34e02a07e Log index generator code overview: https://gist.github.com/zsfelfoldi/97105dff0b1a4f5ed557924a24b9b9e7 Search pattern matcher code overview: https://gist.github.com/zsfelfoldi/5981735641c956afb18065e84f8aff34 Note that the possibility of a tree hashing scheme and remote proof protocol are mentioned in the documents above but they are not exactly specified yet. These specs are WIP and will be finalized after the local log indexer/filter code is finalized and merged. --------- Co-authored-by: Felix Lange --- cmd/geth/chaincmd.go | 3 + cmd/geth/main.go | 3 + cmd/utils/flags.go | 26 ++ common/range.go | 115 +++++ common/range_test.go | 36 ++ core/filtermaps/checkpoints_holesky.json | 37 +- core/filtermaps/checkpoints_mainnet.json | 516 ++++++++++++----------- core/filtermaps/checkpoints_sepolia.json | 127 +++--- core/filtermaps/filtermaps.go | 116 ++--- core/filtermaps/indexer.go | 144 ++++--- core/filtermaps/indexer_test.go | 52 ++- core/filtermaps/map_renderer.go | 270 ++++++------ core/filtermaps/matcher.go | 293 ++++++------- core/filtermaps/matcher_backend.go | 56 +-- core/rawdb/accessors_indexes.go | 21 +- eth/api_backend.go | 5 + eth/backend.go | 74 ++++ eth/ethconfig/config.go | 8 +- eth/filters/filter.go | 394 +++++++++++------ eth/filters/filter_system.go | 5 +- eth/filters/filter_system_test.go | 88 ++-- eth/filters/filter_test.go | 196 ++++++++- internal/ethapi/api_test.go | 6 +- internal/ethapi/backend.go | 6 +- internal/ethapi/transaction_args_test.go | 10 +- 25 files changed, 1593 insertions(+), 1014 deletions(-) create mode 100644 common/range.go create mode 100644 common/range_test.go diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index c30927d9ff..95239bd640 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -100,6 +100,9 @@ if one is set. Otherwise it prints the genesis from the datadir.`, utils.VMTraceFlag, utils.VMTraceJsonConfigFlag, utils.TransactionHistoryFlag, + utils.LogHistoryFlag, + utils.LogNoHistoryFlag, + utils.LogExportCheckpointsFlag, utils.StateHistoryFlag, }, utils.DatabaseFlags), Description: ` diff --git a/cmd/geth/main.go b/cmd/geth/main.go index fd3f4e1bab..2995d87624 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -87,6 +87,9 @@ var ( utils.TxLookupLimitFlag, // deprecated utils.TransactionHistoryFlag, utils.ChainHistoryFlag, + utils.LogHistoryFlag, + utils.LogNoHistoryFlag, + utils.LogExportCheckpointsFlag, utils.StateHistoryFlag, utils.LightServeFlag, // deprecated utils.LightIngressFlag, // deprecated diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 06eabbf313..2e3bb6aead 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -278,6 +278,23 @@ var ( Value: ethconfig.Defaults.HistoryMode.String(), Category: flags.StateCategory, } + LogHistoryFlag = &cli.Uint64Flag{ + Name: "history.logs", + Usage: "Number of recent blocks to maintain log search index for (default = about one year, 0 = entire chain)", + Value: ethconfig.Defaults.LogHistory, + Category: flags.StateCategory, + } + LogNoHistoryFlag = &cli.BoolFlag{ + Name: "history.logs.disable", + Usage: "Do not maintain log search index", + Category: flags.StateCategory, + } + LogExportCheckpointsFlag = &cli.StringFlag{ + Name: "history.logs.export", + Usage: "Export checkpoints to file in go source file format", + Category: flags.StateCategory, + Value: "", + } // Beacon client light sync settings BeaconApiFlag = &cli.StringSliceFlag{ Name: "beacon.api", @@ -1636,6 +1653,15 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.StateScheme = rawdb.HashScheme log.Warn("Forcing hash state-scheme for archive mode") } + if ctx.IsSet(LogHistoryFlag.Name) { + cfg.LogHistory = ctx.Uint64(LogHistoryFlag.Name) + } + if ctx.IsSet(LogNoHistoryFlag.Name) { + cfg.LogNoHistory = true + } + if ctx.IsSet(LogExportCheckpointsFlag.Name) { + cfg.LogExportCheckpoints = ctx.String(LogExportCheckpointsFlag.Name) + } if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheTrieFlag.Name) { cfg.TrieCleanCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheTrieFlag.Name) / 100 } diff --git a/common/range.go b/common/range.go new file mode 100644 index 0000000000..c3a26ea7f5 --- /dev/null +++ b/common/range.go @@ -0,0 +1,115 @@ +// Copyright 2025 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 . + +package common + +import ( + "iter" +) + +// Range represents a range of integers. +type Range[T uint32 | uint64] struct { + first, afterLast T +} + +// NewRange creates a new range based of first element and number of elements. +func NewRange[T uint32 | uint64](first, count T) Range[T] { + return Range[T]{first, first + count} +} + +// First returns the first element of the range. +func (r Range[T]) First() T { + return r.first +} + +// Last returns the last element of the range. This panics for empty ranges. +func (r Range[T]) Last() T { + if r.first == r.afterLast { + panic("last item of zero length range is not allowed") + } + return r.afterLast - 1 +} + +// AfterLast returns the first element after the range. This allows obtaining +// information about the end part of zero length ranges. +func (r Range[T]) AfterLast() T { + return r.afterLast +} + +// Count returns the number of elements in the range. +func (r Range[T]) Count() T { + return r.afterLast - r.first +} + +// IsEmpty returns true if the range is empty. +func (r Range[T]) IsEmpty() bool { + return r.first == r.afterLast +} + +// Includes returns true if the given element is inside the range. +func (r Range[T]) Includes(v T) bool { + return v >= r.first && v < r.afterLast +} + +// SetFirst updates the first element of the list. +func (r *Range[T]) SetFirst(v T) { + r.first = v + if r.afterLast < r.first { + r.afterLast = r.first + } +} + +// SetAfterLast updates the end of the range by specifying the first element +// after the range. This allows setting zero length ranges. +func (r *Range[T]) SetAfterLast(v T) { + r.afterLast = v + if r.afterLast < r.first { + r.first = r.afterLast + } +} + +// SetLast updates last element of the range. +func (r *Range[T]) SetLast(v T) { + r.SetAfterLast(v + 1) +} + +// Intersection returns the intersection of two ranges. +func (r Range[T]) Intersection(q Range[T]) Range[T] { + i := Range[T]{first: max(r.first, q.first), afterLast: min(r.afterLast, q.afterLast)} + if i.first > i.afterLast { + return Range[T]{} + } + return i +} + +// Union returns the union of two ranges. Panics for gapped ranges. +func (r Range[T]) Union(q Range[T]) Range[T] { + if max(r.first, q.first) > min(r.afterLast, q.afterLast) { + panic("cannot create union; gap between ranges") + } + return Range[T]{first: min(r.first, q.first), afterLast: max(r.afterLast, q.afterLast)} +} + +// Iter iterates all integers in the range. +func (r Range[T]) Iter() iter.Seq[T] { + return func(yield func(T) bool) { + for i := r.first; i < r.afterLast; i++ { + if !yield(i) { + break + } + } + } +} diff --git a/common/range_test.go b/common/range_test.go new file mode 100644 index 0000000000..878b6d66c8 --- /dev/null +++ b/common/range_test.go @@ -0,0 +1,36 @@ +// Copyright 2025 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 . + +package common + +import ( + "slices" + "testing" +) + +func TestRangeIter(t *testing.T) { + r := NewRange[uint32](1, 7) + values := slices.Collect(r.Iter()) + if !slices.Equal(values, []uint32{1, 2, 3, 4, 5, 6, 7}) { + t.Fatalf("wrong iter values: %v", values) + } + + empty := NewRange[uint32](1, 0) + values = slices.Collect(empty.Iter()) + if !slices.Equal(values, []uint32{}) { + t.Fatalf("wrong iter values: %v", values) + } +} diff --git a/core/filtermaps/checkpoints_holesky.json b/core/filtermaps/checkpoints_holesky.json index c30856db4f..b8f6be9caf 100644 --- a/core/filtermaps/checkpoints_holesky.json +++ b/core/filtermaps/checkpoints_holesky.json @@ -1,20 +1,21 @@ [ -{"blockNumber": 814411, "blockId": "0xf763e96fc3920359c5f706803024b78e83796a3a8563bb5a83c3ddd7cbfde287", "firstIndex": 67107637}, -{"blockNumber": 914278, "blockId": "0x0678cf8d53c0d6d27896df657d98cc73bc63ca468b6295068003938ef9b0f927", "firstIndex": 134217671}, -{"blockNumber": 1048874, "blockId": "0x3620c3d52a40ff4d9fc58c3104cfa2f327f55592caf6a2394c207a5e00b4f740", "firstIndex": 201326382}, -{"blockNumber": 1144441, "blockId": "0x438fb42850f5a0d8e1666de598a4d0106b62da0f7448c62fe029b8cbad35d08d", "firstIndex": 268435440}, -{"blockNumber": 1230411, "blockId": "0xf0ee07e60a93910723b259473a253dd9cf674e8b78c4f153b32ad7032efffeeb", "firstIndex": 335543079}, -{"blockNumber": 1309112, "blockId": "0xc1646e5ef4b4343880a85b1a4111e3321d609a1225e9cebbe10d1c7abf99e58d", "firstIndex": 402653100}, -{"blockNumber": 1380522, "blockId": "0x1617cae91989d97ac6335c4217aa6cc7f7f4c2837e20b3b5211d98d6f9e97e44", "firstIndex": 469761917}, -{"blockNumber": 1476962, "blockId": "0xd978455d2618d093dfc685d7f43f61be6dae0fa8a9cb915ae459aa6e0a5525f0", "firstIndex": 536870773}, -{"blockNumber": 1533518, "blockId": "0xe7d39d71bd9d5f1f3157c35e0329531a7950a19e3042407e38948b89b5384f78", "firstIndex": 603979664}, -{"blockNumber": 1613787, "blockId": "0xa793168d135c075732a618ec367faaed5f359ffa81898c73cb4ec54ec2caa696", "firstIndex": 671088003}, -{"blockNumber": 1719099, "blockId": "0xc4394c71a8a24efe64c5ff2afcdd1594f3708524e6084aa7dadd862bd704ab03", "firstIndex": 738196914}, -{"blockNumber": 1973165, "blockId": "0xee3a9e959a437c707a3036736ec8d42a9261ac6100972c26f65eedcde315a81d", "firstIndex": 805306333}, -{"blockNumber": 2274844, "blockId": "0x76e2d33653ed9282c63ad09d721e1f2e29064aa9c26202e20fc4cc73e8dfe5f6", "firstIndex": 872415141}, -{"blockNumber": 2530503, "blockId": "0x59f4e45345f8b8f848be5004fe75c4a28f651864256c3aa9b2da63369432b718", "firstIndex": 939523693}, -{"blockNumber": 2781903, "blockId": "0xc981e91c6fb69c5e8146ead738fcfc561831f11d7786d39c7fa533966fc37675", "firstIndex": 1006632906}, -{"blockNumber": 3101713, "blockId": "0xc7baa577c91d8439e3fc79002d2113d07ca54a4724bf2f1f5af937b7ba8e1f32", "firstIndex": 1073741382}, -{"blockNumber": 3221770, "blockId": "0xa6b8240b7883fcc71aa5001b5ba66c889975c5217e14c16edebdd6f6e23a9424", "firstIndex": 1140850360} +{"blockNumber": 814410, "blockId": "0x6c38f0d4ff2c23ae187f581cebb0963c0ec2dbf051b289de1582c369c98a3244", "firstIndex": 67107349}, +{"blockNumber": 914268, "blockId": "0xeff161573a11eb6e2ab930674a5245b2c5ffc5e9e63093503344ec6fa60578a3", "firstIndex": 134216064}, +{"blockNumber": 1048866, "blockId": "0xebd3b95415dad9ab7f2b1d25e48c791e299063c09427bbde54217029c3e215ad", "firstIndex": 201325914}, +{"blockNumber": 1144433, "blockId": "0x0c895438ef12a2c835b4372c209e40a499ba2b18ed0e658846813784de391392", "firstIndex": 268434935}, +{"blockNumber": 1230406, "blockId": "0xd3512e7241efc9853e46b1ad565403d302f62457b6dd84917c31ff375fabf487", "firstIndex": 335544096}, +{"blockNumber": 1309104, "blockId": "0xcf108c6e002e7a5657bf7587adde03edf5f20d03e95b66a194eb8080daaf4f97", "firstIndex": 402653143}, +{"blockNumber": 1380499, "blockId": "0x984b0f8b6bf06f1240bbca8c1df9bef3880bedafd5b5c2a55cbc2f64c9efb974", "firstIndex": 469759822}, +{"blockNumber": 1476950, "blockId": "0x8323b528bb8d80a96b172de92452be0f45078a9ca311cb4be6675f089e25a426", "firstIndex": 536870335}, +{"blockNumber": 1533506, "blockId": "0x737dc416070aa3b522a0c2ab813a70c1820f062c718f27597394f309f0004106", "firstIndex": 603979618}, +{"blockNumber": 1613764, "blockId": "0x9d6a5a505afdda86b1ebcc2b10cb687a1f89c2e2b74315945a3ddc6a0b3c9cd6", "firstIndex": 671088253}, +{"blockNumber": 1719073, "blockId": "0x17bf6a51d9c55a908c3e9e652f80b2aa464b049c697abf3bd5a537e461aff0b9", "firstIndex": 738197366}, +{"blockNumber": 1973133, "blockId": "0x14b288d70e688de3334d09283670301aacfed21c193da8a56fd63767f8177705", "firstIndex": 805305624}, +{"blockNumber": 2274761, "blockId": "0xf02790b273b04219e001d2fcc93e8e47708cb228364e96ad754bc8050d08a9aa", "firstIndex": 872414871}, +{"blockNumber": 2530470, "blockId": "0x157ea98b1a7e66dd3f045da8e25219800671f9a000211d3d94006efd33d89a80", "firstIndex": 939523234}, +{"blockNumber": 2781706, "blockId": "0xa723663a584e280700d2302eba03d1b3b6549333db70c6152576d33e2911f594", "firstIndex": 1006632936}, +{"blockNumber": 3101669, "blockId": "0xa6e66a18fed64d33178c7088f273c404be597662c34f6e6884cf2e24f3ea4ace", "firstIndex": 1073741353}, +{"blockNumber": 3221725, "blockId": "0xe771f897dece48b1583cc1d1d10de8015da57407eb1fdf239fdbe46eaab85143", "firstIndex": 1140850137}, +{"blockNumber": 3357164, "blockId": "0x6252d0aa54c79623b0680069c88d7b5c47983f0d5c4845b6c811b8d9b5e8ff3c", "firstIndex": 1207959453}, +{"blockNumber": 3447019, "blockId": "0xeb7d585e1e063f3cc05ed399fbf6c2df63c271f62f030acb804e9fb1e74b6dc1", "firstIndex": 1275067542} ] - diff --git a/core/filtermaps/checkpoints_mainnet.json b/core/filtermaps/checkpoints_mainnet.json index d2b031a474..ca30280e03 100644 --- a/core/filtermaps/checkpoints_mainnet.json +++ b/core/filtermaps/checkpoints_mainnet.json @@ -1,256 +1,264 @@ [ -{"blockNumber": 4166218, "blockId": "0xdd767e0426256179125551e8e40f33565a96d1c94076c7746fa79d767ed4ad65", "firstIndex": 67108680}, -{"blockNumber": 4514014, "blockId": "0x33a0879bdabea4a7a3f2b424388cbcbf2fbd519bddadf13752a259049c78e95d", "firstIndex": 134217343}, -{"blockNumber": 4817415, "blockId": "0x4f0e8c7dd04fbe0985b9394575b19f13ea66a2a628fa5b08178ce4b138c6db80", "firstIndex": 201326352}, -{"blockNumber": 5087733, "blockId": "0xc84cd5e9cda999c919803c7a53a23bb77a18827fbde401d3463f1e9e52536424", "firstIndex": 268435343}, -{"blockNumber": 5306107, "blockId": "0x13f028b5fc055d23f55a92a2eeecfbcfbda8a08e4cd519ce451ba2e70428f5f9", "firstIndex": 335544094}, -{"blockNumber": 5509918, "blockId": "0x1ed770a58a7b4d4a828b7bb44c8820a674d562b23a6a0139981abe4c489d4dad", "firstIndex": 402652853}, -{"blockNumber": 5670390, "blockId": "0x3923ee6a62e6cc5132afdadf1851ae4e73148e6fbe0a8319cafd2a120c98efa3", "firstIndex": 469761897}, -{"blockNumber": 5826139, "blockId": "0xe61bc6ef03c333805f26319e1688f82553f98aa5e902b200e0621a3371b69050", "firstIndex": 536870853}, -{"blockNumber": 5953029, "blockId": "0x43d710b1b7243b848400975048ccefdfaba091c692c7f01c619d988886cc160f", "firstIndex": 603979580}, -{"blockNumber": 6102846, "blockId": "0xa100b2018f6545cc689656b4b846677b138955b7efd30e850cd14c246430ba18", "firstIndex": 671088291}, -{"blockNumber": 6276718, "blockId": "0xb832ac448b06c104ba50faefd58b0b94d53c0fba5cb268086adad4db99c2f35f", "firstIndex": 738197399}, -{"blockNumber": 6448696, "blockId": "0x48e8ae6f729ad6c76b6cf632bd52a6df7886ed55be09d43c5004fcc1463e533b", "firstIndex": 805305988}, -{"blockNumber": 6655974, "blockId": "0xac395971a6ffc30f807848f68b97b2834f8ea13478a7615860b6a69e3d0823ca", "firstIndex": 872415033}, -{"blockNumber": 6873949, "blockId": "0xc522ddb1113b1e9a87b2bdcb11ce78756beba6454a890122f121a032b5769354", "firstIndex": 939523784}, -{"blockNumber": 7080953, "blockId": "0x3606de577d80120d1edbb64bad7fa6795e788bae342866a98cc58ce2f7575045", "firstIndex": 1006632796}, -{"blockNumber": 7267002, "blockId": "0xad770882a69d216e955e34fef84851e56c0de82deacd6187a7a41f6170cd6c6d", "firstIndex": 1073741045}, -{"blockNumber": 7466708, "blockId": "0x17a48817b3a65aba333a5b56f3ff2e86fbcc19e184b046a5305a5182fdd8eb8a", "firstIndex": 1140850680}, -{"blockNumber": 7661807, "blockId": "0xa74731ee775fbd3f4d9313c68562737dd7c8d2c9eb968791d8abe167e16ddc96", "firstIndex": 1207959112}, -{"blockNumber": 7834556, "blockId": "0xe4b4812448075508cb05a0e3257f91b49509dc78cd963676a633864db6e78956", "firstIndex": 1275068095}, -{"blockNumber": 7990068, "blockId": "0x07bd4ca38abb4584a6209e04035646aa545ebbb6c948d438d4c25bfd9cb205fa", "firstIndex": 1342176620}, -{"blockNumber": 8143032, "blockId": "0x0e3149e9637290b044ee693b8fcb66e23d22db3ad0bdda32962138ba18e59f3f", "firstIndex": 1409285949}, -{"blockNumber": 8297660, "blockId": "0x34cd24f80247f7dfaf316b2e637f4b62f72ecc90703014fb25cb98ad044fc2c0", "firstIndex": 1476394911}, -{"blockNumber": 8465137, "blockId": "0x4452fa296498248d7f10c9dc6ec1e4ae7503aa07f491e6d38b21aea5d2c658a8", "firstIndex": 1543503744}, -{"blockNumber": 8655820, "blockId": "0x7bdb9008b30be420f7152cc294ac6e5328eed5b4abd954a34105de3da24f3cc6", "firstIndex": 1610612619}, -{"blockNumber": 8807187, "blockId": "0xde03e3bfddc722c019f0b59bc55efabcd5ab68c6711f4c08d0390a56f396590d", "firstIndex": 1677721589}, -{"blockNumber": 8911171, "blockId": "0xe44f342de74ab05a2a994f8841bdf88f720b9dc260177ba4030d0f7077901324", "firstIndex": 1744830310}, -{"blockNumber": 8960320, "blockId": "0x79764f9ff6e0fe4848eda1805687872021076e4e603112861af84181395ac559", "firstIndex": 1811938893}, -{"blockNumber": 9085994, "blockId": "0x24a101d1c8a63367a0953d10dc79c3b587a93bd7fd382084708adefce0b8363f", "firstIndex": 1879047965}, -{"blockNumber": 9230924, "blockId": "0xb176a98d3acd855cbb75265fb6be955a8d51abc771e021e13275d5b3ecb07eeb", "firstIndex": 1946156668}, -{"blockNumber": 9390535, "blockId": "0x640f5e2d511a5141878d57ae7a619f19b72a2bd3ef019cf0a22d74d93d9acf07", "firstIndex": 2013265733}, -{"blockNumber": 9515674, "blockId": "0xff4a7b6b21aeaeb6e1a75ecd22b1f34c058a0ce1477ce90a8ce78165fd1d0941", "firstIndex": 2080374553}, -{"blockNumber": 9659426, "blockId": "0xc351455249343b41e9171e183612b68c3c895271c62bd2c53d9e3ab1aa865aa1", "firstIndex": 2147483567}, -{"blockNumber": 9794018, "blockId": "0xde98035b4b7f9449c256239b65c7ff2c0330de44dee190106d0a96fb6f683238", "firstIndex": 2214592213}, -{"blockNumber": 9923840, "blockId": "0x881da313a1e2b6fab58a1d6fa65b5dacfdc9d68a3112a647104955b5233f84e3", "firstIndex": 2281701302}, -{"blockNumber": 10042435, "blockId": "0x451f6459640a6f54e2a535cc3a49cfc469861da3ddc101840ab3aef9e17fa424", "firstIndex": 2348810174}, -{"blockNumber": 10168883, "blockId": "0x5d16ff5adf0df1e4dc810da60af37399ef733be7870f21112b8c2cfff4995dd9", "firstIndex": 2415918783}, -{"blockNumber": 10289554, "blockId": "0x85d5690f15a787c43b9a49e8dd6e324f0b3e0c9796d07c0cfb128e5c168f5488", "firstIndex": 2483027930}, -{"blockNumber": 10386676, "blockId": "0x20f675ea72db448024a8a0b8e3ec180cac37a5910575bc32f8d9f5cdfe3c2649", "firstIndex": 2550136212}, -{"blockNumber": 10479675, "blockId": "0x014abb07acf2330cc78800ca1f564928f2daccca4b389bf5c59f4b840d843ec0", "firstIndex": 2617245218}, -{"blockNumber": 10562661, "blockId": "0xd437607a3f81ce8b7c605e167ce5e52bf8a3e02cdc646997bd0ccc57a50ad7d1", "firstIndex": 2684354520}, -{"blockNumber": 10641508, "blockId": "0x2e8ab6470c29f90ac23dcfc58310f0208f5d0e752a0c7982a77a223eca104082", "firstIndex": 2751462730}, -{"blockNumber": 10717156, "blockId": "0x8820447b6429dd12be603c1c130be532e9db065bb4bc6b2a9d4551794d63789a", "firstIndex": 2818571831}, -{"blockNumber": 10784549, "blockId": "0xc557daab80a7cdc963d62aa881faf3ab1baceff8e027046bcd203e432e0983b3", "firstIndex": 2885680800}, -{"blockNumber": 10848651, "blockId": "0xede1b0de5db6685a6f589096ceb8fccb08d3ff60e8b00a93caa4a775b48e07fc", "firstIndex": 2952789740}, -{"blockNumber": 10909166, "blockId": "0x989db675899d13323006a4d6174557e3c5501c672afd60d8bd902fc98d37e92e", "firstIndex": 3019897599}, -{"blockNumber": 10972902, "blockId": "0x5484050cc2c7d774bc5cd6af1c2ef8c19d1de12dabe25867c9b365924ea10434", "firstIndex": 3087007422}, -{"blockNumber": 11036597, "blockId": "0x1e3686e19056587c385262d5b0a07b3ec04e804c2d59e9aaca1e5876e78f69ae", "firstIndex": 3154116231}, -{"blockNumber": 11102520, "blockId": "0x339cf302fe813cce3bb9318b860dfa8be7f688413f38a6ea1987a1b84d742b4b", "firstIndex": 3221224863}, -{"blockNumber": 11168162, "blockId": "0xc0fa21ea090627610bcac4732dff702633f310cabafc42bc500d3d4805198fe0", "firstIndex": 3288334273}, -{"blockNumber": 11233707, "blockId": "0x491c37a479b8cf22eaa3654ae34c5ddc4627df8c58ca8a6979159e1710428576", "firstIndex": 3355442691}, -{"blockNumber": 11300526, "blockId": "0xb7366d2a24df99002cffe0c9a00959c93ef0dcfc3fd17389e2020bf5caa788eb", "firstIndex": 3422551480}, -{"blockNumber": 11367621, "blockId": "0xce53df5080c5b5238bb7717dfbfd88c2f574cfbb3d91f92b57171a00e9776cd2", "firstIndex": 3489660710}, -{"blockNumber": 11431881, "blockId": "0x2a08ff9c4f6fd152166213d902f0870822429f01d5f90e384ac54a3eac0ceb3a", "firstIndex": 3556768626}, -{"blockNumber": 11497107, "blockId": "0x1f99c6b65f2b1cb06ed1786c6a0274ff1b9eacab6cb729fcd386f10ebbd88123", "firstIndex": 3623878389}, -{"blockNumber": 11560104, "blockId": "0xebe6924817bbdfe52af49667da1376bae5a2994b375d4b996e8ff2683744e37a", "firstIndex": 3690986640}, -{"blockNumber": 11625129, "blockId": "0xbe6eee325329ee2fe632d8576864c29dd1c79bab891dc0a22d5b2ac87618d26e", "firstIndex": 3758095773}, -{"blockNumber": 11690397, "blockId": "0xc28bf55f858ddf5b82d1ceb3b5258b90a9ca34df8863a1c652c4d359f5748fdf", "firstIndex": 3825204492}, -{"blockNumber": 11755087, "blockId": "0x0c10cde6ce1bbe24dc57347fe4aaebc17b7d8e8d7d97e3db573133477f494740", "firstIndex": 3892314051}, -{"blockNumber": 11819674, "blockId": "0x36b694a1776c94e4c6ae4a410931b2086de47a83e437517040e3290ce9afff67", "firstIndex": 3959422445}, -{"blockNumber": 11883358, "blockId": "0x21f447aca9ddf94ed71df9fa3648a12acc2ba603f89f24c4784936864c41945f", "firstIndex": 4026531743}, -{"blockNumber": 11948524, "blockId": "0x71a52b6cce80d3a552b0daa18beb952facf81a89bc7ca769d08ac297f317507a", "firstIndex": 4093640009}, -{"blockNumber": 12013168, "blockId": "0x9a7fb369b8d8cd0edd0d890d636096f20c63abb7eb5798ad1e578cac599e3db8", "firstIndex": 4160748475}, -{"blockNumber": 12078711, "blockId": "0x5de09329413b0c2f58d926f225197552a335ba3d5544d7bdb45e7574f78c9b8d", "firstIndex": 4227858275}, -{"blockNumber": 12143640, "blockId": "0xbeafc0e1e0586f5a95f00f2a796d7df122c79c187aa2d917129297f24b8306bd", "firstIndex": 4294967145}, -{"blockNumber": 12208005, "blockId": "0x052487095cdd4a604808e6c14e30fb68b3fa546d35585b315f287219d38ef77c", "firstIndex": 4362075289}, -{"blockNumber": 12272465, "blockId": "0x82c8a50413bd67a0d6f53b085adcd9ae8c25ecc07ed766fa80297a8dcae63b29", "firstIndex": 4429184610}, -{"blockNumber": 12329418, "blockId": "0x294c147e48d32c217ff3f27a3c8c989f15eee57a911408ec4c28d4f13a36bb3b", "firstIndex": 4496292968}, -{"blockNumber": 12382388, "blockId": "0x8c2555965ff735690d2d94ececc48df4700e079c7b21b8e601a30d4e99bc4b5b", "firstIndex": 4563401809}, -{"blockNumber": 12437052, "blockId": "0x2e38362031f36a0f3394da619dcc03be03c19700594cbd1df84c2c476a87de63", "firstIndex": 4630511012}, -{"blockNumber": 12490026, "blockId": "0x122749c02a55c9c2a1e69068f54b6c1d25419eb743e3553aba91acf1daeadc35", "firstIndex": 4697619920}, -{"blockNumber": 12541747, "blockId": "0xfb9f12aa2902da798ac05fab425434f8c7ce98050d67d416dbb32f98c21f66f7", "firstIndex": 4764728267}, -{"blockNumber": 12597413, "blockId": "0x9a7a399c2904ac8d0fec580550525e7e1a73d8f65f739bf7c05d86e389d0d3f7", "firstIndex": 4831837757}, -{"blockNumber": 12651950, "blockId": "0xb78dcb572cdafb9c4e2f3863ef518a3b2df0cd4f76faa26a423b2ca0c1cde734", "firstIndex": 4898946491}, -{"blockNumber": 12706472, "blockId": "0xfd21f41ec6b0c39287d7d48c134d1212a261c53d65db99739994b003150bbad1", "firstIndex": 4966054796}, -{"blockNumber": 12762929, "blockId": "0xc94d994bc40b2ae7dc23cf2b92cc01e84915f090bb57c0d9a67584bd564d3916", "firstIndex": 5033164307}, -{"blockNumber": 12816689, "blockId": "0x7770c72f22cbf6ccf7ab85d203088f7ede89632cf0042c690102f926a90bd09d", "firstIndex": 5100273412}, -{"blockNumber": 12872408, "blockId": "0x2e008b8c952d828875d777f7912f472af96ffc977f2ceae884006682cab6b8ed", "firstIndex": 5167381625}, -{"blockNumber": 12929718, "blockId": "0x85eb0ed3c5910c6a01b65ef0a5b76c59c2cdb5094e6e27eb87c751d77bcc2c88", "firstIndex": 5234491305}, -{"blockNumber": 12988757, "blockId": "0xdf12045bea73af18d4e71f8be8e334160f78b85f96a3535a4056409d8b61355a", "firstIndex": 5301600237}, -{"blockNumber": 13049172, "blockId": "0xf07608d97a101cd9a95fee9d9062a15bcb333263e555f8cfa31da037e0468f30", "firstIndex": 5368709080}, -{"blockNumber": 13108936, "blockId": "0x42739341db582d2f39b91ec9e8cc758777ca3f6ff9f25cd98883619fd5f026a7", "firstIndex": 5435817013}, -{"blockNumber": 13175495, "blockId": "0x564f25eacb229350b7c648b5828169e7a0344ae62e866206828e2cfad8947f10", "firstIndex": 5502926476}, -{"blockNumber": 13237721, "blockId": "0x0973425abec0fa6319701b46e07c2373b0580e3adbed6900aad27d5bf26dcb95", "firstIndex": 5570035419}, -{"blockNumber": 13298771, "blockId": "0xf3a16fec5be808c9f7782fb578dc8cef7f8e2110f7289bd03c0cc13977dd1518", "firstIndex": 5637143840}, -{"blockNumber": 13361281, "blockId": "0x3c0b6364201ca9221b61af3de27a3a87e111870b8c7efc43a6d8496e98c68690", "firstIndex": 5704253046}, -{"blockNumber": 13421819, "blockId": "0x2f472e57997b95558b99e3e5e7e0e8d4dbf8b71c081aac6536c9ff5925dac2ce", "firstIndex": 5771361231}, -{"blockNumber": 13480620, "blockId": "0xc4d689e87464a0c83c661c8e3a0614c370631de857f7e385b161dfe8bacd3e71", "firstIndex": 5838469468}, -{"blockNumber": 13535793, "blockId": "0xe7674bacc8edce9fb3efd59b92c97da48fe7ace1de314b4a67d7d032fc3bb680", "firstIndex": 5905578026}, -{"blockNumber": 13590588, "blockId": "0x6a3e86bdce7dd7d8792e1af9156edd8c3ffee7c20fed97001f58a9a2699f6594", "firstIndex": 5972687757}, -{"blockNumber": 13646707, "blockId": "0xab404a5d3709cf571b04e9493f37116eeb5dd2bc9dc10c48387c1e0199013d69", "firstIndex": 6039797165}, -{"blockNumber": 13703025, "blockId": "0x20e2fde15b8fe56f5dd7ab0f324c552038167ed44864bf3978e531ae68d6d138", "firstIndex": 6106905803}, -{"blockNumber": 13761024, "blockId": "0x2ae49275e13e780f1d29aea8507b2a708ff7bfe977efac93e050273b8b3a8164", "firstIndex": 6174015107}, -{"blockNumber": 13819468, "blockId": "0xb9d19cb31dedb1128b11cad9ffd6e58c70fe7ba65ba68f1ac63668ac5160ad85", "firstIndex": 6241124350}, -{"blockNumber": 13877932, "blockId": "0x80b1ff0bb069a8479360a15eaa84ba30da02cfacadc564837f4b1c90478addb8", "firstIndex": 6308232256}, -{"blockNumber": 13935384, "blockId": "0xe1f5469a559a6114dd469af61b118b9d9551a69bbd49a4e88f2a2d724830c871", "firstIndex": 6375341632}, -{"blockNumber": 13994042, "blockId": "0x25188fb75f2328c870ade7c38ef42ff5fddef9c4e364eebe4c5d8d9cc3ecabab", "firstIndex": 6442449799}, -{"blockNumber": 14051123, "blockId": "0xf4ef2bce9ee9222bdcf6b3a0c204676d9345e211e10c983e523930274e041ef1", "firstIndex": 6509559107}, -{"blockNumber": 14109189, "blockId": "0x80b730c28f75d8cb5ec2fb736341cd87cb4ecb2c9c614e0a4ecc0f9812675d50", "firstIndex": 6576667347}, -{"blockNumber": 14166822, "blockId": "0xf662a24b91684fa8ac462b31071f406de8d6183dba46d30d690f4407bc6af36f", "firstIndex": 6643777079}, -{"blockNumber": 14222488, "blockId": "0x7333e324c96b12f11a38d1fc2ddb4860e018b90f5dc10f3dbe19f7679bb95535", "firstIndex": 6710885890}, -{"blockNumber": 14277180, "blockId": "0x4373c1000e8e10179657689e2f0e42f88bd1601ecb4a5d83970d10287f6654cc", "firstIndex": 6777994595}, -{"blockNumber": 14331080, "blockId": "0x9c708a750a3f284ec0ee950110b36fd488cb1ec24cd0c2ea72c19551ec5c42a5", "firstIndex": 6845103719}, -{"blockNumber": 14384243, "blockId": "0x34ce7503b76335aa18dec880b0cefd388a29e0fcff6f2e1ddda8fb8c0ac1daf0", "firstIndex": 6912212376}, -{"blockNumber": 14437670, "blockId": "0x79842efd3e406b41f51935fe2e6ad20a7dd5a9db2280ebd7f602ed93da1e3c24", "firstIndex": 6979320543}, -{"blockNumber": 14489204, "blockId": "0xcd12addf0afdc229e9fe3bd0a34677a3826c5e78d4baf715f8ed36b736d6627a", "firstIndex": 7046430591}, -{"blockNumber": 14541688, "blockId": "0x55f617abf208a73fc467e8cb5feead586b671dbb0f6281570b3c44b8eabb2b9e", "firstIndex": 7113538755}, -{"blockNumber": 14594551, "blockId": "0xc7211bf772e93c8c2f945fcb6098b47c3455604cb8b94a505cb5cb720914c369", "firstIndex": 7180646025}, -{"blockNumber": 14645065, "blockId": "0x6d5b0326f4b22e2b0196986a514f23ec6e9a62f70f53300a22b21ff661a6ef7e", "firstIndex": 7247756883}, -{"blockNumber": 14695926, "blockId": "0x0a77272250e43b4bb46c02eb76944881a3c6b00a21bb9086a8229199bd62d97a", "firstIndex": 7314865843}, -{"blockNumber": 14746330, "blockId": "0xd677fdbaf8efb1bfdc138ac6b2bd5d0e890a29acb1f52f40169181ad517b0d31", "firstIndex": 7381974956}, -{"blockNumber": 14798546, "blockId": "0xbb277e8623acd2ce2340cf32f6c0ddab70fd95d862287f68a3c37250a70619cd", "firstIndex": 7449082890}, -{"blockNumber": 14848230, "blockId": "0x587b39f11bdaa2091291c7c3947e88df2e91e7997f2375dfd43b6e310a538582", "firstIndex": 7516192636}, -{"blockNumber": 14897646, "blockId": "0xf5b5c9d0c024ca0c0f0c6171871f609687f4ccb064ededbd61176cf23a9011e8", "firstIndex": 7583299602}, -{"blockNumber": 14950782, "blockId": "0x50549486afaf92a4c3520012b325e914ef77a82e4d6530a71f9b1cca31bfae18", "firstIndex": 7650409868}, -{"blockNumber": 15004101, "blockId": "0x7edac55dea3ee4308db60b9bc0524836226fe301e085b3ce39105bd145ba7fc3", "firstIndex": 7717517503}, -{"blockNumber": 15056903, "blockId": "0xb4cfd02d435718598179cdba3f5c11eb8653fe97ec8d89c60673e3e07b8dfc94", "firstIndex": 7784627997}, -{"blockNumber": 15108302, "blockId": "0x53c77a7de4515e9e93467a76f04cc401834bcdd64e9dfa03cf6d2844a6930293", "firstIndex": 7851736988}, -{"blockNumber": 15159526, "blockId": "0x1a31ad84b423254d7ff24e7eca54048ed8cc13cec5eb7289bf3f98ed4de9f724", "firstIndex": 7918844431}, -{"blockNumber": 15211013, "blockId": "0xe5d491e1d6cc5322454143b915c106be1bf28114a41b054ba5e5cfe0abecafba", "firstIndex": 7985953942}, -{"blockNumber": 15264389, "blockId": "0xd9939bb9e58e95d2672c1148b4ec5730204527d3f3fc98ca03a67dc85cf3d710", "firstIndex": 8053063187}, -{"blockNumber": 15315862, "blockId": "0x7254f99c4bb05235d5b437984c9132164e33182d4ce11a3847999da5c28b4092", "firstIndex": 8120172147}, -{"blockNumber": 15364726, "blockId": "0x11b57547579d9009679e327f57e308fe86856391805bc3c86e7b39daae890f52", "firstIndex": 8187281042}, -{"blockNumber": 15412886, "blockId": "0xbe3602b1dbef9015a3ec7968ac7652edf4424934b6bf7b713b99d8556f1d9444", "firstIndex": 8254390023}, -{"blockNumber": 15462792, "blockId": "0x3348ca4e14ac8d3c6ac6df676deaf3e3b5e0a11b599f73bd9739b74ebd693efe", "firstIndex": 8321499024}, -{"blockNumber": 15509914, "blockId": "0xbc98fd6b71438d5a169f9373172fea799fa3d22a8e6fe648d35e1070f2261113", "firstIndex": 8388606521}, -{"blockNumber": 15558748, "blockId": "0x5fa2cf499276ae74a5b8618990e71ed11a063619afe25c01b46e6252eba14c19", "firstIndex": 8455716577}, -{"blockNumber": 15604217, "blockId": "0x78a608e13d2eb3c5fed81a19b829ede88071cf01ea9ff58112a7472435f97c30", "firstIndex": 8522825668}, -{"blockNumber": 15651869, "blockId": "0xd465d861d925d1475440782ff16c2b3361ba3c8e169d7cc90eb8dfc0f31b0aac", "firstIndex": 8589934080}, -{"blockNumber": 15700968, "blockId": "0x71e3def131271e02c06ca945d14a995703a48faac1334a9e2e2321edd0b504d0", "firstIndex": 8657043390}, -{"blockNumber": 15762986, "blockId": "0x9b1b51dca2eae29162ca66968a77b45175f134b44aea3defadcb924f83e0b944", "firstIndex": 8724151376}, -{"blockNumber": 15814455, "blockId": "0x3c04a509cb6304d3df4bef57e0119d9e615ab737ec0b4a7deada6e5f57d9f873", "firstIndex": 8791260562}, -{"blockNumber": 15865639, "blockId": "0x9e9e26148c774518ecf362c0e7c65a5c1b054a8a3e4e36036c70e273fac6147c", "firstIndex": 8858368894}, -{"blockNumber": 15920564, "blockId": "0x9efe1d4dbfd9aa891ac0cffd3e1422a27ba2ea4add211b6900a2242cdb0f0ca0", "firstIndex": 8925477950}, -{"blockNumber": 15974371, "blockId": "0xc63ccef7bc35a0b431a411f99fe581b322d00cfc6422d078696808a5658a32ac", "firstIndex": 8992587107}, -{"blockNumber": 16032913, "blockId": "0x3e60957224964669a8646914e3166553b9f4256d5be160b17995d838af3ef137", "firstIndex": 9059696632}, -{"blockNumber": 16091057, "blockId": "0x12b346047bb49063ab6d9e737775924cf05c52114202ddb1a2bdaf9caabbfe0c", "firstIndex": 9126804912}, -{"blockNumber": 16150977, "blockId": "0x49318a32ff0ce979c4061c1c34db2a94fb06e7669c93742b75aff14a134fa598", "firstIndex": 9193913896}, -{"blockNumber": 16207432, "blockId": "0xf7870865edf81be4389a0be01468da959de703df0d431610814d16ed480176e4", "firstIndex": 9261019778}, -{"blockNumber": 16262582, "blockId": "0x25818e0f4d54af6c44ef7b23add34409a47de3ab1c905889478f3ec8ad173ec3", "firstIndex": 9328131320}, -{"blockNumber": 16319695, "blockId": "0x25de4b1c18cc503f5d12b4fa9072d33a11fa503a3dbeb9ab3d016b57c1e5cd4d", "firstIndex": 9395240790}, -{"blockNumber": 16373605, "blockId": "0x3794a5e0d2aa10baf1e6a5ec623d6089fdd39799eff633017d8df5144526939f", "firstIndex": 9462349509}, -{"blockNumber": 16423494, "blockId": "0xe0217d947ba3865dfc9288e0c890b0996457bb9d18467bd125e86bbb0052b57f", "firstIndex": 9529458033}, -{"blockNumber": 16474853, "blockId": "0xd454f033d190f22f9e56f0209ea1eeb3b6257805d5d88650d2759eb4d24821b7", "firstIndex": 9596567055}, -{"blockNumber": 16525689, "blockId": "0x8a23cbbf3e258e13f5a1ada434366796cb4a3e5b1062455582fb2bc3ab991541", "firstIndex": 9663674943}, -{"blockNumber": 16574203, "blockId": "0xc1a5b7d26e8222bd2d56ef4108f75d69f7c116707d348950834e00962241a4f8", "firstIndex": 9730785112}, -{"blockNumber": 16622622, "blockId": "0x3ddb3ef7a4309bd788258fb0d62613c89a0b4de715f4e12f6017a194d19d6481", "firstIndex": 9797893665}, -{"blockNumber": 16672585, "blockId": "0x8aa5e9f72b261f9e2a9eb768483d1bbd84d3a88fdb1346f6a9a7f262fd28ba41", "firstIndex": 9865002893}, -{"blockNumber": 16720124, "blockId": "0x2128f8baf264166e37554d5c31a06de58d9ccfb663117358251da548a23a060f", "firstIndex": 9932111275}, -{"blockNumber": 16769162, "blockId": "0x6b3e849482d3222032740ad6b8f98e24636c82682a6a3572b1ef76dfebc66821", "firstIndex": 9999217824}, -{"blockNumber": 16818311, "blockId": "0xe45f57381978a2bfc85bd20af1c41e2b630412642ac4f606b477f05f030ef5d9", "firstIndex": 10066328668}, -{"blockNumber": 16869531, "blockId": "0xa154555266d24dc1f4885af5fafcf8cab3de788998cf69e1d28f56aa13a40c43", "firstIndex": 10133437302}, -{"blockNumber": 16921611, "blockId": "0xf1f829b4ab5eec6e243916dd530993fa11eef5510fd730e8d09ead6b380355a1", "firstIndex": 10200547185}, -{"blockNumber": 16974870, "blockId": "0x1a33202b95926ae4cb8e6e99d8d150f3c50d817b3a316452bdf428c971dabde5", "firstIndex": 10267655914}, -{"blockNumber": 17031277, "blockId": "0x706c9dd0dc81e7ac29d2ea0f826e6b8a1dcb5adb1b904ff6e43260729c9fd0a7", "firstIndex": 10334764934}, -{"blockNumber": 17086330, "blockId": "0x085a80cafe96b520105b9a1f8e7a2bbc9474da24da7e6344ca7c4d32db822f92", "firstIndex": 10401871892}, -{"blockNumber": 17141311, "blockId": "0x33ec6513dfa515bc5f6356476b4eb075a8064181d6aaf6aa1a1e18887e342f74", "firstIndex": 10468982364}, -{"blockNumber": 17190907, "blockId": "0x6f41273d3bf30d3347e7eb68872a49b3ac947f314543478be7a28a55e5c41a3c", "firstIndex": 10536090817}, -{"blockNumber": 17237199, "blockId": "0x9a87a14a128c0345a366940f821a14f16719de628658ac0628e410a72d723e90", "firstIndex": 10603200178}, -{"blockNumber": 17287181, "blockId": "0x9c6e78adcf562ac63c103e3e5a02f025023079aca79bdd6ef18f7bd2a6271c29", "firstIndex": 10670309183}, -{"blockNumber": 17338652, "blockId": "0x1b747da97b2397a293602af57514dab4ca1010bb6c601ff05cb2012dd1124ebb", "firstIndex": 10737418023}, -{"blockNumber": 17389337, "blockId": "0xbc3c0ca1e5989605b9b59c94b418562eb17ccbce30e45ac8531cf0b3867a6b2c", "firstIndex": 10804522857}, -{"blockNumber": 17442261, "blockId": "0x1ec341be1cbd09f559bfa3d3e39a341d8e21052eeb7880931d43d086651733b7", "firstIndex": 10871635535}, -{"blockNumber": 17497787, "blockId": "0x6069880d486f2548599df1e14e12752d3eb9bc99843a98cd6631c22be1b58554", "firstIndex": 10938744657}, -{"blockNumber": 17554322, "blockId": "0x69b2564bc00b1f310f6b416912869d7530d7864bf7d70d55c7ace554f129b989", "firstIndex": 11005852829}, -{"blockNumber": 17608492, "blockId": "0x7d590653d5fa52c0d3ee453a77d2088504f57adcef35cd57c567afb554608457", "firstIndex": 11072961972}, -{"blockNumber": 17664272, "blockId": "0xdc16159d3500cdc7410873102f41fc55de2a8a41e3779c4b70e6224a541e2b9e", "firstIndex": 11140070967}, -{"blockNumber": 17715101, "blockId": "0x655e33c4e81182464ea0b0e1fdbc53ce53902431db5107326b816091a4564652", "firstIndex": 11207179487}, -{"blockNumber": 17764042, "blockId": "0x54439184f31cd83ba06b48b6dbfdd744ae7246355be1327b44744058711d05c0", "firstIndex": 11274287303}, -{"blockNumber": 17814383, "blockId": "0xfb453bc951360c76fb09bb1b9a3e39d23ececa0adb93368cc3f41f0457845089", "firstIndex": 11341397984}, -{"blockNumber": 17864648, "blockId": "0x32a68823ef4ec0cbab2fe50c97e3f462b575e8b117da40d00c710b4c66ee1d6d", "firstIndex": 11408505657}, -{"blockNumber": 17913366, "blockId": "0x04b944aab8a4ff91b77c2191817cf051766100c227616a3746af53407e740124", "firstIndex": 11475614351}, -{"blockNumber": 17961690, "blockId": "0x08bee7cc0b764106ca01dd5370b617879487ffb423688c96e948dce125990f45", "firstIndex": 11542723488}, -{"blockNumber": 18011048, "blockId": "0x94c39d3a64f3e9a91b1d98554cd29e1390e30fa61cfa4e909c503eee2fd9f165", "firstIndex": 11609833142}, -{"blockNumber": 18061209, "blockId": "0x2ee9ade68955c030488c8a30537bdf948355f7dd5ae64942b5bfce1be6650e19", "firstIndex": 11676941316}, -{"blockNumber": 18111692, "blockId": "0xd6c4fd0c1cc20ed5e7960bb5043e9e5e9c66a4d2ec5709ac9797fff678435640", "firstIndex": 11744050346}, -{"blockNumber": 18166212, "blockId": "0x3262588c2ef79a3b3f6a3db6435202d22f5667cd48c136b0797404901525c9ff", "firstIndex": 11811159686}, -{"blockNumber": 18218743, "blockId": "0x935bd9a4164ff7ecd09a37b916ce5bf78487bd19377b5b17be153e39318aee74", "firstIndex": 11878268593}, -{"blockNumber": 18271236, "blockId": "0xe58ebb821f27e3665898f390802a3d129d217b3a3ee36d890a85cf22a0a8aa33", "firstIndex": 11945376750}, -{"blockNumber": 18323007, "blockId": "0x3997a841468efa1bc614bfc3de4502274901b04b428f87a1f3086dfd78cda1eb", "firstIndex": 12012485748}, -{"blockNumber": 18372443, "blockId": "0xc44a13a5d02e8dc39f355de5e21ce7bb311ce7f4d9114ff480dce235a169e416", "firstIndex": 12079595370}, -{"blockNumber": 18421829, "blockId": "0x7da63a0b613d8745597b2ac64fd5cc8b2fb14b24d163b12a0a39d7d3d4ff7b5c", "firstIndex": 12146703582}, -{"blockNumber": 18471706, "blockId": "0xd632a1893f415ff618f4b612a7687e6af1f12feeed81f46f0022090829c1eb4c", "firstIndex": 12213812677}, -{"blockNumber": 18522301, "blockId": "0x44fa2cf08145ae40e8e42f4e6b4ab7df360a17c5a065ce45fcc41b51bee011f4", "firstIndex": 12280921639}, -{"blockNumber": 18572935, "blockId": "0x72b8ab4c78c90425ee054b4806a8be703da0febdf1d51866358ec2bd21ba9529", "firstIndex": 12348029751}, -{"blockNumber": 18623431, "blockId": "0x8c4cb2f13501d9788820280c6f16692d0737258c3896f1e4bded32d838febf7f", "firstIndex": 12415138965}, -{"blockNumber": 18675470, "blockId": "0x523b73c19ea8b3ae32ef141a83ef9855e667ebf51443cfcabd1a06659359062a", "firstIndex": 12482247454}, -{"blockNumber": 18725728, "blockId": "0x0cfbd131eb5dad51488238079fba29a63eebb5c32d1a543cb072e48dc2104ef3", "firstIndex": 12549356369}, -{"blockNumber": 18778387, "blockId": "0xc4906c77af8058b9f172a4f0e8788c7887f05caa5ac752b38b5387080f74ae49", "firstIndex": 12616465992}, -{"blockNumber": 18835044, "blockId": "0x49c5e07f409a841dc81f3ef8417f1951f8fcc13c90134f9d2a0cd11938f9fa36", "firstIndex": 12683575082}, -{"blockNumber": 18883308, "blockId": "0x386a58dd5f79a419eeb05075b07b3ff3bc836a265c9688854a504223b1d6a830", "firstIndex": 12750683753}, -{"blockNumber": 18933635, "blockId": "0xd3881292147589bd2e192769e5c9175b5d03a453fe1ef3c4b5b6858ac9402a2f", "firstIndex": 12817792470}, -{"blockNumber": 18988254, "blockId": "0xcbe72dfa15428ac21b9c59c703ceaa0eb4b2205927687261d7aaed3dbb3783ea", "firstIndex": 12884882858}, -{"blockNumber": 19041325, "blockId": "0x92b077e1c2f8819da728f0307c914fdcd57eba14ea07d9a45c28d1ed8ffff576", "firstIndex": 12952010530}, -{"blockNumber": 19089163, "blockId": "0x43f8ab2d3dfc34c8e18cba903074d54e235dc546f19c4eb78245a522c266c84e", "firstIndex": 13019119228}, -{"blockNumber": 19140629, "blockId": "0xab7b7ae5424b18105a13b657fa6099d4ab67fde5baff39fe6e4de707397e995c", "firstIndex": 13086228236}, -{"blockNumber": 19192118, "blockId": "0x451327e6a5cf6ce1c8c14c01687dc5f719f3c2176f46bac4f264616256e30d1c", "firstIndex": 13153337116}, -{"blockNumber": 19237836, "blockId": "0x9b260d6be369557d1dc88aca423e2697e697d941d1b726c183015b5649e248c8", "firstIndex": 13220445421}, -{"blockNumber": 19291271, "blockId": "0x4878c28d79e1f71bc11e062eb61cb52ae6a18b670b0f9bea38b477944615078e", "firstIndex": 13287554254}, -{"blockNumber": 19344448, "blockId": "0x56243b9ad863bf90953fe9aa6e64a426629384db1190e70dce79575d30595f7e", "firstIndex": 13354663659}, -{"blockNumber": 19394948, "blockId": "0x195173b64dda7908d6aa39a63c8bdd29ec181d401e369d513be1308550d0ddcb", "firstIndex": 13421771935}, -{"blockNumber": 19443075, "blockId": "0xd39c1d60996475e65d1ab5b4e755f510ca466564a8155d35db6667988d6c0e44", "firstIndex": 13488880427}, -{"blockNumber": 19488383, "blockId": "0x28956eb8856fa8db59c02585016b8baf43bc44bc35b00bdaf8a6babe51101c5c", "firstIndex": 13555977105}, -{"blockNumber": 19534584, "blockId": "0x2421c97b0f140185d4c20943cd4ed7d7424468482feb76e3003a1cc69da3fd7b", "firstIndex": 13623097580}, -{"blockNumber": 19579602, "blockId": "0x25f96529028e9f51c59aec9ce8de282b7dd67066fd46a1694130698ed0f40d8b", "firstIndex": 13690207623}, -{"blockNumber": 19621517, "blockId": "0x4f6f6e0a0488f3d51823bc4b07c292348c259b1866968f77ee76b66b37101c75", "firstIndex": 13757315529}, -{"blockNumber": 19665085, "blockId": "0x00f9315f89681b44bff46f1bad8894bc6dfae1c459d3d6520f9881861304a496", "firstIndex": 13824425382}, -{"blockNumber": 19709229, "blockId": "0x24e022b21ae1ba8a3e8c87cb9734aa1d1810fc4a69fe147d3ebb1ff0df8bcc15", "firstIndex": 13891534799}, -{"blockNumber": 19755387, "blockId": "0x77f184b7183b1a351760d242041249464b42cfaa6fbc4326f352b06bb3b21a02", "firstIndex": 13958642483}, -{"blockNumber": 19803894, "blockId": "0xf37eb1b054a6d61272940361f386eb744cded84d15c3250a7eabadede257371c", "firstIndex": 14025751618}, -{"blockNumber": 19847885, "blockId": "0x4659649fa8a3b4bbe8978673ba9a22ae20352c7052b676d373b5a51b1967ffa4", "firstIndex": 14092848654}, -{"blockNumber": 19894193, "blockId": "0x15606bdc0f1a710bd69443c7154d4979aece9329977b65990c9b39d6df84ed5c", "firstIndex": 14159970181}, -{"blockNumber": 19938551, "blockId": "0x6a8f4571924ed902bd8e71bf8ed9cc9d72cabeabc410277c8f0fc2b477d00eb7", "firstIndex": 14227077892}, -{"blockNumber": 19985354, "blockId": "0x7b6fb6376410b4d9e5d7ee02f78b2054e005dd2976eea47fc714f66b967dc285", "firstIndex": 14294187965}, -{"blockNumber": 20028440, "blockId": "0x9b37440b71c24756b8855b8012432b84276ae94c80aa1ccc8b70a7705992103c", "firstIndex": 14361296503}, -{"blockNumber": 20071780, "blockId": "0xa2ed129f343f3d60419772ec5635edcd36b8680c9419b6626e2bc84b230c709b", "firstIndex": 14428405230}, -{"blockNumber": 20113832, "blockId": "0xe7a610e8bcbf8ded141ebc7142de03dfc54b1bcc79e3bf8d07fad4e42b665bba", "firstIndex": 14495512019}, -{"blockNumber": 20156854, "blockId": "0xbe09704f65a70ef8843d9c8e511ddc989ea139dbe94cdfe37f52b03620d62385", "firstIndex": 14562622430}, -{"blockNumber": 20200135, "blockId": "0x9a58c34d5f77342e94065d119905c000223cd988c4b11f1539fff20737159630", "firstIndex": 14629731923}, -{"blockNumber": 20244389, "blockId": "0x1e733f0db9ef21183107259b3c2408c78fa5a01469928cd295f3ea7e8eedda45", "firstIndex": 14696840011}, -{"blockNumber": 20288489, "blockId": "0xb5ad7edd86b181226c8c7be0a08069e3955234e797426843fff9de0f57ec59cc", "firstIndex": 14763949714}, -{"blockNumber": 20333582, "blockId": "0x8040c209f5cd1738ee0f85c2f1db7c43a420d148680c7390fd1701b9f0bb671a", "firstIndex": 14831058335}, -{"blockNumber": 20377087, "blockId": "0x08fdc4cd246b6ae9d4a45646b0aed6af3bb330eb6cd4c8b93646157e7b002b84", "firstIndex": 14898167722}, -{"blockNumber": 20421699, "blockId": "0x5a2912b5fc2f02df33b655155990f92dcaacda5b75427fe3d87fb38f36b1c17d", "firstIndex": 14965275691}, -{"blockNumber": 20467194, "blockId": "0x3deaf4325c461004b090b0261996c645ab529c1471feaf7dc2bbe1f128180297", "firstIndex": 15032385211}, -{"blockNumber": 20512397, "blockId": "0x37e39697ec1b7683a6202be250ffaee7a1102e8030f87550b94af05ec66cec83", "firstIndex": 15099493973}, -{"blockNumber": 20557443, "blockId": "0x8e9c04468f3111eab8b1f6a58b277862c624861c237cadecc53ec249bd811bda", "firstIndex": 15166602882}, -{"blockNumber": 20595899, "blockId": "0x9787555fe57e4650002257eb2c88f1ef435b99d406e33fe2f889be180123ef25", "firstIndex": 15233709908}, -{"blockNumber": 20638606, "blockId": "0x70681cffd159ce2e580dbbbe8fa6b5343dbcb081429cdda6c577e615bef4ef05", "firstIndex": 15300820678}, -{"blockNumber": 20683605, "blockId": "0xb32662d5e241132ffe2249caea67f5746a6f4382297b2ac87c81e2794faf1f7a", "firstIndex": 15367929350}, -{"blockNumber": 20728630, "blockId": "0x15a817c846928b673032d5eacd0cff7a04217d268457aa30a322ecca32be4d49", "firstIndex": 15435037830}, -{"blockNumber": 20771519, "blockId": "0x542bc7b9804bbc45f4be470f4dc56f215a4dec71fed71eba2ffc804afd262b95", "firstIndex": 15502145990}, -{"blockNumber": 20815097, "blockId": "0x798cdd51c964fcf18561d70095d9613b84ba836817972799c9dfd0bfbe1e042b", "firstIndex": 15569256033}, -{"blockNumber": 20857859, "blockId": "0xfb5bb066d419a651d8e0186569eb4e8d8bcd5181d8f02e0d578b5dfe2fc738dd", "firstIndex": 15636364671}, -{"blockNumber": 20896890, "blockId": "0x834b8d6fad779e4cf8214128f6c93d7387b6d6279e517f6f0a284b5d831cc3ae", "firstIndex": 15703472902}, -{"blockNumber": 20939387, "blockId": "0x7adee7c78420c711efa216c61e0b561e581d7ff0331efd91ee16a609b34cfdc2", "firstIndex": 15770582325}, -{"blockNumber": 20981303, "blockId": "0x6f5d7b0cc6dad5eb258176e07de21795a8347d68f7303f06934046e0236bea6d", "firstIndex": 15837691713}, -{"blockNumber": 21023216, "blockId": "0x96cfe35a45df1297a36f42c59ebe706ab0473dfbf59ce910b5c5a8dbf696de1c", "firstIndex": 15904799667}, -{"blockNumber": 21068378, "blockId": "0x93753875ff330d922b23f823203198f3b1bb8833367c6b6a8f896ff54be2c12d", "firstIndex": 15971909040}, -{"blockNumber": 21112445, "blockId": "0x6ac02fa6ae486b86aba562eaf6f3d883befaa8ebedcfd8d74bdb7368d42deee3", "firstIndex": 16039003625}, -{"blockNumber": 21155992, "blockId": "0x25f76896b4b693bafb79e9a535e2bf00ed62a577e35209749346e8e79a60bb71", "firstIndex": 16106126344}, -{"blockNumber": 21200962, "blockId": "0x725f2befe913cb2659d262e2d3b6f79a706b31c557d52669471da22347ec8287", "firstIndex": 16173235265}, -{"blockNumber": 21244663, "blockId": "0x6778c4194f54e70939da38853daddb22bfaf160d35617ab05d0f5c476741147b", "firstIndex": 16240344735}, -{"blockNumber": 21290273, "blockId": "0x433ac819c40bd3061205fe0ece0645eec73f54a0a5c1559c981f983345bc0154", "firstIndex": 16307453543}, -{"blockNumber": 21336156, "blockId": "0x261dc8c1639d505624150d2388d15ed10bfb4c3ce9c0c327a4ec26531689a097", "firstIndex": 16374562466}, -{"blockNumber": 21378880, "blockId": "0x5c78b2b70553140dfdfdd4f415b98f88e74f74662315834038fd99042277d917", "firstIndex": 16441671104}, -{"blockNumber": 21421613, "blockId": "0x854532f9d1c77627b763f9cbc7099a653d59554ed57fa763bc218834c82955fe", "firstIndex": 16508780351}, -{"blockNumber": 21466875, "blockId": "0xb8b83cc62084e948235ef4b5973bf7fd988fa28bcaa72f7d38ad8e50de729618", "firstIndex": 16575888599}, -{"blockNumber": 21511942, "blockId": "0xe806a28bc1b7f8cd752c8ceedbe081d49773d4558a9fb95e3357c0c07172522d", "firstIndex": 16642996907}, -{"blockNumber": 21550291, "blockId": "0x1f3e26d303e7a2a9b0614f12f62b189da365b3947c5fe2d99ed2711b37fe7daa", "firstIndex": 16710106826}, -{"blockNumber": 21592690, "blockId": "0xa1408cfbc693faee4425e8fd9e83a181be535c33f874b56c3a7a114404c4f686", "firstIndex": 16777215566}, -{"blockNumber": 21636275, "blockId": "0x704734c2d0351f8ccd38721a9a4b80c063368afaaa857518d98498180a502bba", "firstIndex": 16844323959}, -{"blockNumber": 21681066, "blockId": "0x1e738568ed393395c498b109ad61c0286747318aae0364936f19a7b6aba94aef", "firstIndex": 16911433076}, -{"blockNumber": 21725592, "blockId": "0xee87b7948e25a7498a247c616a0fbaa27f21b004e11fc56f2a20c03791ed8122", "firstIndex": 16978540993} +{"blockNumber": 4166212, "blockId": "0xd94b724fc1c7dceb3251b51b81f7a0f3220ae5b9add1e62917004f14f2a3532c", "firstIndex": 67108840}, +{"blockNumber": 4513996, "blockId": "0xc0fd25fef5888609d05fa8c5620d8e18c31cdd675dd4ee926ff966668f0e5d76", "firstIndex": 134217480}, +{"blockNumber": 4817399, "blockId": "0x8573c3166fb7f716e97cf6109d382f86441238e0b85828944a072ae8583a6cfa", "firstIndex": 201326547}, +{"blockNumber": 5087706, "blockId": "0xcd2dfae45901e299b25faa04c10df60983d5c0a3d0e478e789c7aeec980cf691", "firstIndex": 268435422}, +{"blockNumber": 5306085, "blockId": "0xe8026984c85873f2b24018a7e0bdf8c2df136b2fc49666b4addf0d2e067890da", "firstIndex": 335544018}, +{"blockNumber": 5509898, "blockId": "0x41bc63dc8184f9a7d4b9a5a554e00eee32fee60ccfa2339264be11270ac28de1", "firstIndex": 402652789}, +{"blockNumber": 5670367, "blockId": "0xe61c2ba463f2f458817ed1bf0ff9fb9d07e9d7354f46b48175b2d784b817bf3b", "firstIndex": 469761896}, +{"blockNumber": 5826113, "blockId": "0xfbbb1fc5e03a5562bf6b7f1799d7a572d9ef66c7fd0e0b9f4fed7262767b5c86", "firstIndex": 536870852}, +{"blockNumber": 5953008, "blockId": "0x6bbdccd094928e846de3c615a33f2952e3259afaa1929f4ee241a56907d0593b", "firstIndex": 603979150}, +{"blockNumber": 6102812, "blockId": "0x2403f2502088a1fa26c1294f5245032fe3b7f3a8bb14c12d0ba8d577156bbc1b", "firstIndex": 671087962}, +{"blockNumber": 6276672, "blockId": "0xeaa05a0e0574fbb164244628a7d14d9d47f1692941098aa737059d44104401df", "firstIndex": 738197399}, +{"blockNumber": 6448662, "blockId": "0xe6f097fa18a2cef425514e863659e42e1aa8d74df49cb0bed5877e82a08d1f84", "firstIndex": 805306056}, +{"blockNumber": 6655925, "blockId": "0xee5c9d87b06dc0c2e850fead07bcd13f85d45ad6b5a6e7ebee53cc8618772786", "firstIndex": 872415229}, +{"blockNumber": 6873878, "blockId": "0x9a0ea103146da21fa1d9cab7ff609ec2c5e7f1856288a1f744239588e33904c2", "firstIndex": 939524080}, +{"blockNumber": 7080893, "blockId": "0x35ad45055d7a1a3ef94efeb05e4122757bcc126ff1bc2b4ac72dc78ab3c0fd75", "firstIndex": 1006632327}, +{"blockNumber": 7266955, "blockId": "0x7b80f70520f2c16eb5890f4963af8f60446f50af82b589ceaf009d09e704b302", "firstIndex": 1073741608}, +{"blockNumber": 7466649, "blockId": "0xea8980b5c692c729013f9cc4c9f66e94d5ef2c5caad23f370be0953b6d85f32c", "firstIndex": 1140850335}, +{"blockNumber": 7661736, "blockId": "0x615cdb03dbf29a22d59bbc769c03f3647c2c9abb7d1767f3f6529708209e7073", "firstIndex": 1207959232}, +{"blockNumber": 7834494, "blockId": "0x05b7dd13e2eb8a833e4b278f9151ca01b294ae5fc5617769422ec85ffb446215", "firstIndex": 1275068098}, +{"blockNumber": 7989998, "blockId": "0x4b5026add7f2fc2c02993b82c61bceba1b75cfcb8a78c5112cc2832bc0413be6", "firstIndex": 1342177025}, +{"blockNumber": 8142950, "blockId": "0x9f56b7753a0e9964973f3e096e1385215e3aef232b448359e5bec05046b016a3", "firstIndex": 1409285545}, +{"blockNumber": 8297598, "blockId": "0x2ddfeb161fe34ff73c4a995bb94256c3b522b39348169edb97bb3d876ccdf77d", "firstIndex": 1476394898}, +{"blockNumber": 8465061, "blockId": "0xfdbe5435da5ae9fb34a9154e5ebe515afac9a128e383f798f1e69952be404fcd", "firstIndex": 1543503805}, +{"blockNumber": 8655740, "blockId": "0x305137325a39a44aba997e214c05f32d965658f7e3fe20322f2e57e72f239977", "firstIndex": 1610612406}, +{"blockNumber": 8807102, "blockId": "0xc32f9fd9a43d2b502dbcc4a683ffe324fca9a871753478bcb1f4fb9573da96a5", "firstIndex": 1677721343}, +{"blockNumber": 8911110, "blockId": "0x806eabee7dc429b57afcb6c0b72b61728b4f13c62ee9942f1813790463f9ab5b", "firstIndex": 1744830172}, +{"blockNumber": 8960262, "blockId": "0xa69f24032e9d5761565e726d484926a0932c6ca7fdb4c04a55bfc5e8f5879c4d", "firstIndex": 1811938729}, +{"blockNumber": 9085906, "blockId": "0x922b4d6aeca64517f5048fa1ffa98d3e813bc415716a1763d07cf2bdea236896", "firstIndex": 1879048062}, +{"blockNumber": 9230838, "blockId": "0xd892f513c48c95a859ae59c4e00ec4be0d684c549456c90e9c36669c11092f50", "firstIndex": 1946156580}, +{"blockNumber": 9390449, "blockId": "0xcc0ced78fa66b570f338d129794d574560a23958e28d0d5288003d4542cec734", "firstIndex": 2013264376}, +{"blockNumber": 9515554, "blockId": "0xc06d04737813c3e2c8e910b2d4543c5c684c7bfe235bb501445f68fd0663f3d9", "firstIndex": 2080374749}, +{"blockNumber": 9659367, "blockId": "0x765fa33d9418b1ed648e88b497b4bf4aa66990e6413dfc155525cda61e3cc813", "firstIndex": 2147483344}, +{"blockNumber": 9793940, "blockId": "0x9527dcbd85a100c51aee4ba90828fee23312f5f041859f3416a54fd87a437ce2", "firstIndex": 2214591669}, +{"blockNumber": 9923756, "blockId": "0xc4da5fdd2de077459fe84f721d7fc01ef69683e03e634b0c5443186486792246", "firstIndex": 2281700949}, +{"blockNumber": 10042325, "blockId": "0x6f554f195d20e4ba1262f73cf2c1cd44b14a71befe5ef0a5318fc309f8cde9fa", "firstIndex": 2348810131}, +{"blockNumber": 10168768, "blockId": "0xdcdf88ec2c197d552343c80d01375a35d6462aff39c5bd976160f7176d2add64", "firstIndex": 2415918723}, +{"blockNumber": 10289467, "blockId": "0x5ece23babcf6b8838695b891b044c3dcf8120cdc15acd83018e96ffc590f6cd0", "firstIndex": 2483027720}, +{"blockNumber": 10386601, "blockId": "0xd398ee129aea1247019fa39dc2f5083819687d8f5ad17ec39ba1803d178fbcda", "firstIndex": 2550136043}, +{"blockNumber": 10479586, "blockId": "0x61c1cccd60a546eacc29f2ae2a6facab402b4f35e2986c6629b302e6b1ebca3e", "firstIndex": 2617245577}, +{"blockNumber": 10562579, "blockId": "0x14fd6a079050e3e110140372605c050945d6db03746fa5b84a8718cb2a0e9b86", "firstIndex": 2684353802}, +{"blockNumber": 10641438, "blockId": "0x1998dcae09111b2c1bf6d0592e2f0b4a0143fe7db7115ff94b2b73b74542cee4", "firstIndex": 2751463334}, +{"blockNumber": 10717094, "blockId": "0x9ce45a150bb6bd13687d0692c86716911ffc6aff5831a03f4015bdffb85f5093", "firstIndex": 2818571144}, +{"blockNumber": 10784494, "blockId": "0xf65c936cf578e673696477f7db7189b47222fdf7ed13096564b9d47b15c86eb9", "firstIndex": 2885680709}, +{"blockNumber": 10848591, "blockId": "0x84ea8e9d5789302ebe88888115488f91cfb75fd89e282dfbd29ba9d6225ab0ca", "firstIndex": 2952789491}, +{"blockNumber": 10909102, "blockId": "0x22275047f643f4a3b82c349274a53d56a00da25beac89655877fb8cbfa3182c8", "firstIndex": 3019898024}, +{"blockNumber": 10972828, "blockId": "0x38e8acfec19bb53ab9fe70dd3577ccd8bd7c41b4d468b8f099a19bf06ce56518", "firstIndex": 3087006561}, +{"blockNumber": 11036532, "blockId": "0x478dac03f520e987a6e845cac1b3a756d69e4f5257f20758b8fc38456e7b6884", "firstIndex": 3154116463}, +{"blockNumber": 11102457, "blockId": "0x1a36616b03351ebe481330fc86578f57d9da5c0dda59f4406d49b53a2fa9b6f6", "firstIndex": 3221224961}, +{"blockNumber": 11168092, "blockId": "0xb23af483ca6dbc2fb8d2fada1f8189de2e155758ed9ecbbb20c234a30c7e093c", "firstIndex": 3288333510}, +{"blockNumber": 11233640, "blockId": "0xdc4c80d8370823773425b31e70b3af59582b556514857d1e9a9b4c53b880a424", "firstIndex": 3355442901}, +{"blockNumber": 11300454, "blockId": "0xdbd4d8e9c1756093037f431330db4604e5a3e67bb74f425f667633d5298fe34c", "firstIndex": 3422551962}, +{"blockNumber": 11367550, "blockId": "0x9e00816bd5b39353657a99421d278091599a39f246bbf8c0898f83e6d70b7c7c", "firstIndex": 3489660743}, +{"blockNumber": 11431820, "blockId": "0xd32d5e5871ce71acc9dcc7cf9d8cb9d964646182529c327a0ddf617814e27d74", "firstIndex": 3556769150}, +{"blockNumber": 11497031, "blockId": "0xed823054d4b293294e3301d3131e2ae9f0ce63c9f07d2d80fc144e42d535d61d", "firstIndex": 3623878513}, +{"blockNumber": 11560027, "blockId": "0x7098177efe77cfb5912dc63069fe366033111041ca5c539907a89e439c861d8f", "firstIndex": 3690987092}, +{"blockNumber": 11625049, "blockId": "0x61fccb47104a076b905d390498a1b38c317348bb1567e69aa1b670bed7a6a13f", "firstIndex": 3758096043}, +{"blockNumber": 11690298, "blockId": "0xaf6f9e9c92db9e155f1ffb35eb68765c09de1206c353c9a56a5d5fa53e2d88d2", "firstIndex": 3825204430}, +{"blockNumber": 11755003, "blockId": "0xf9efb54f08a2b59086a607541005928f1bda54fa2c46fcc48352fa6aa461c03a", "firstIndex": 3892313541}, +{"blockNumber": 11819591, "blockId": "0xa551deb3bd2ec0ca2683cb76adb8fb295d171a3787aa5ff071bd1fa94d3af82e", "firstIndex": 3959422322}, +{"blockNumber": 11883278, "blockId": "0x60c7a359d4aeb09b63b64487fa7794aee87c41b11446f69ba83e24d5bce6947f", "firstIndex": 4026531239}, +{"blockNumber": 11948436, "blockId": "0x1c46fb22653e5f9eb9503fe532151519845d05b121291766715973e027eb9c0c", "firstIndex": 4093639741}, +{"blockNumber": 12013084, "blockId": "0x0b8d1da9e70c4494ba61190390c891230846e69d9b0be605df8b04c4199ffd1b", "firstIndex": 4160749561}, +{"blockNumber": 12078612, "blockId": "0xb049af260be25c6153b67c227689b0556d8fb7d6033c3f61b8218f5c4ac5917a", "firstIndex": 4227857310}, +{"blockNumber": 12143549, "blockId": "0x685dce41efbe2bab0ac9a4b9026d9e5faa3e01ae4637c8181cd2f1eac326fe6a", "firstIndex": 4294966232}, +{"blockNumber": 12207918, "blockId": "0x53644ea8f781c6270f1136ed4bbcb956c98f12a4793c2f56f49b3105282b13ea", "firstIndex": 4362075410}, +{"blockNumber": 12272370, "blockId": "0x503523e1a01cb7d68be0eb22f36280c0ee7a1fc802061a704e326fbb8efcfe27", "firstIndex": 4429184831}, +{"blockNumber": 12329342, "blockId": "0xe6a1bf78dd827def078e23c768cf6e96666ee06b5fdc113380a4769d85f4bd33", "firstIndex": 4496293151}, +{"blockNumber": 12382311, "blockId": "0x352fe793c16b1d8aea195af86ee9aec3dbaa83e7a32872a0d222a77e96fc0e57", "firstIndex": 4563402486}, +{"blockNumber": 12436970, "blockId": "0x90276349edf0941bb3327dc1d749d0371fe550eaddafb5141fcacdd281de0976", "firstIndex": 4630510187}, +{"blockNumber": 12489954, "blockId": "0x71894b1e1608aeba777662a6055e601ce6f17950dbe3844c3601e0f88cbd5848", "firstIndex": 4697619586}, +{"blockNumber": 12541655, "blockId": "0xc34c7299041df72a81c4b86e81639d2dfcebfae603e287fc143881a636ce1188", "firstIndex": 4764728948}, +{"blockNumber": 12597319, "blockId": "0x4c4dc71d8d2f1223ad0469ddfe09fa8934f64e3712ed83708d6e1b9c64eceebb", "firstIndex": 4831837390}, +{"blockNumber": 12651863, "blockId": "0x406a642c8364fb61cbd896512a2d19ecdba3932ab09ac67cf48d91aa1a03f24e", "firstIndex": 4898946362}, +{"blockNumber": 12706391, "blockId": "0xa7fc3739898f7ff5733896313b248fb0b306a132681609b8b9e2671b42dac3a1", "firstIndex": 4966054748}, +{"blockNumber": 12762840, "blockId": "0x5a1048966dfa55a48248517c45683ce342671bf544117d2bac13d8e343563136", "firstIndex": 5033164303}, +{"blockNumber": 12816597, "blockId": "0x97f5b87f422870a8ffeff70c12f4377dde96d248aa912cb64ebfaea51dc086b6", "firstIndex": 5100272541}, +{"blockNumber": 12872315, "blockId": "0x432f7df7973fdea4e5abe2c09b634ca3b724e37a19d5ec23ca5810e4854c6b33", "firstIndex": 5167382243}, +{"blockNumber": 12929620, "blockId": "0xa4524e3b4c5dadac91df2f29696532cfed377f47469fe7cfabf10baac945eea2", "firstIndex": 5234490748}, +{"blockNumber": 12988653, "blockId": "0x9be70e31aeea164c370d8d183a1af45120693b13d360edc78214535c40ef1d63", "firstIndex": 5301600248}, +{"blockNumber": 13049074, "blockId": "0x3076857bd17959c2a3df18357eab3ecbb7ca25457785b07074d2e885eb566fc2", "firstIndex": 5368707746}, +{"blockNumber": 13108823, "blockId": "0x67b2764f6b53779e3d683def2c7659c6d92814152574073d24f10cd67200a22b", "firstIndex": 5435817680}, +{"blockNumber": 13175389, "blockId": "0x0d41e34dbca58dec45e0509b6cbf027b2377220845a85caebbf3a14b8505e8ce", "firstIndex": 5502925085}, +{"blockNumber": 13237624, "blockId": "0x723e0818d54ab1b1c53efc2f2f448c16c1f943af2033575c612c3c71228d4ddf", "firstIndex": 5570034948}, +{"blockNumber": 13298658, "blockId": "0xd7f9f477b20bcf1379ccaaae701c9658a02b177861e0af9e873c63fe8b854f4b", "firstIndex": 5637144562}, +{"blockNumber": 13361166, "blockId": "0x9f9e4130093269d59045f91344a13eb2786023e02099f53dd44854d50a7f5ffb", "firstIndex": 5704253260}, +{"blockNumber": 13421716, "blockId": "0x28752f35ad2adeba342ef6aa51bca1c0abc757c4f712cf620c8a5e6a8576636e", "firstIndex": 5771361775}, +{"blockNumber": 13480516, "blockId": "0x43f4ecf24c17b7a77621e2ca09c88c53b3407fb018f4bb3a032c212015b4e18d", "firstIndex": 5838469616}, +{"blockNumber": 13535688, "blockId": "0x99ffcd9982219a4be45fa5f15622b288afe4f85da536956d39dccac1ec416444", "firstIndex": 5905579739}, +{"blockNumber": 13590487, "blockId": "0xff7b74464a318037e0670c0be7a5b1b79418d1a42c7a3587c46adc1ef720c819", "firstIndex": 5972688013}, +{"blockNumber": 13646596, "blockId": "0xd555ce5f2b65ffeec9b9e3e75a56f5f23d7353a1014bcc861efd7df6e5dc2c92", "firstIndex": 6039797525}, +{"blockNumber": 13702902, "blockId": "0x3e8feab590c6631c63931fee83bf7e10234890bdf377b1b374fb70221b9ac9c0", "firstIndex": 6106905641}, +{"blockNumber": 13760908, "blockId": "0x18e96fa585c57aad1f2a8dc9639d9f4fe53c3f38894862471b7cec17f97f3cb1", "firstIndex": 6174014330}, +{"blockNumber": 13819340, "blockId": "0x25384bf2cef178e30c4d9e0abae59357055241fc715133b2bb078e8278e5296a", "firstIndex": 6241123906}, +{"blockNumber": 13877817, "blockId": "0x4a84a0a7035dbfed0e1281fb1df162a195f2c516686a2e2a6ad605e6f2b3cf57", "firstIndex": 6308232690}, +{"blockNumber": 13935260, "blockId": "0x6c14a8e563614edbad66698ebe911e997e27844f9d33c7069795379540e71a44", "firstIndex": 6375341148}, +{"blockNumber": 13993914, "blockId": "0xc0991d1bc431c5215fb0218b0b541f2b4fae0994775e5eddb016965fe5b4a0c7", "firstIndex": 6442448947}, +{"blockNumber": 14051007, "blockId": "0x854803ce364b6fe4446800e74448a5ee883e7d472e8a0347f1ad13bd347fa69f", "firstIndex": 6509558964}, +{"blockNumber": 14109063, "blockId": "0xa3fadce7ab18a5fedcfed9e3b1f0cf2fb32151172c362327c602343bc26dde8f", "firstIndex": 6576668017}, +{"blockNumber": 14166701, "blockId": "0xaa2443a38e2b0d6b01cf4b73de54d2b7e92c99475bd0802beebd86845c126d1b", "firstIndex": 6643774648}, +{"blockNumber": 14222367, "blockId": "0xa27c78134c1fbf432c8caee7c9d2ef323fdde519a065804c9c8da6ca1615e04f", "firstIndex": 6710886238}, +{"blockNumber": 14277029, "blockId": "0x5d0ed08b65db01e382c339dcb2715b2927fb472b0c1f4256841c386d785c3f3a", "firstIndex": 6777995247}, +{"blockNumber": 14330955, "blockId": "0x3e5014d82c416549acfe2e26b13ff5ddf29ad74943ef5b94a65eca0a4d72d82e", "firstIndex": 6845102969}, +{"blockNumber": 14384139, "blockId": "0x720e1fd687292019c3fa1812c39c73d20fde783c0196f43378a51cb4e0b46d65", "firstIndex": 6912212078}, +{"blockNumber": 14437534, "blockId": "0xe4342ab6f363066d9c2514a870241ceb63df232d33659fdb3928faf433360bb4", "firstIndex": 6979321493}, +{"blockNumber": 14489073, "blockId": "0xeb2f9923d816e773460eccfa42eb4ff2bbfc353b756ba274e98dc85c64ab1536", "firstIndex": 7046428879}, +{"blockNumber": 14541559, "blockId": "0x55cc33721fa9022964f47c30f4146fee7cc80f5b4158d7ecea0ff661cbba3487", "firstIndex": 7113538857}, +{"blockNumber": 14594432, "blockId": "0x676441bdc1ec430a9fbc05e56a0437a475606a253f9fdffc86f5f6286c159b5d", "firstIndex": 7180647956}, +{"blockNumber": 14644942, "blockId": "0x03232a1d3c9b2f1f0c2a4b780c83bce66238d9abdeed71c2a8af9e97a53011b0", "firstIndex": 7247754981}, +{"blockNumber": 14695793, "blockId": "0x6017f6d1d79188692e1a1a18de5f93b83b97905c8ff5bed5cef4b2e9f4d4d41b", "firstIndex": 7314863646}, +{"blockNumber": 14746186, "blockId": "0x332c8ac0b2960fb1627407d45443201a0f6de7421b52af077b32fa6e018e5401", "firstIndex": 7381974499}, +{"blockNumber": 14798403, "blockId": "0x4507ae1d9f27a3eada874dcf04112a5d4ee7155701afd3fb857fe58201a2dede", "firstIndex": 7449082752}, +{"blockNumber": 14848093, "blockId": "0xff98c820c66c3beaaa3b195158c94f099148573a7f032295ba7156c13f78eaa8", "firstIndex": 7516191978}, +{"blockNumber": 14897508, "blockId": "0x49c31d3433b197585f733d7c382ae16d9d78bf59c333a9b76362cc6c5fed270c", "firstIndex": 7583300663}, +{"blockNumber": 14950638, "blockId": "0xf1a1609206788977841927271808fe1c42d059026385d025bf4cea0c0a05966b", "firstIndex": 7650408992}, +{"blockNumber": 15003958, "blockId": "0xf9e6b41108d907672f29ba1df97ed83aca2ba933335e6b5ccfb5d4001807b33f", "firstIndex": 7717519105}, +{"blockNumber": 15056783, "blockId": "0x126a5a5c22e878a4c42bea890d48cfe00edf7fbc27d805c88a5269540bc94a42", "firstIndex": 7784628173}, +{"blockNumber": 15108163, "blockId": "0x5f8494ccacefce9fef8a95a52247f2670ed30bb5a3b3deb85c645e72c115d506", "firstIndex": 7851735261}, +{"blockNumber": 15159385, "blockId": "0x0fed6c5aafb53e9cdb0d8620c3eef9b619fb3ae9559296618f634eddae33f0c6", "firstIndex": 7918844299}, +{"blockNumber": 15210872, "blockId": "0x37bdb94b57044b67fbd9b9a7cdc728fcfd2066cf1904cab478fd42d41ec6e1f0", "firstIndex": 7985954119}, +{"blockNumber": 15264226, "blockId": "0xc173ad8a791e2293efd8e0b2c9f088fac49233aabb1e72db6a045aa9904f4728", "firstIndex": 8053061840}, +{"blockNumber": 15315693, "blockId": "0x985cf97bf28f0becb2b16abfe629a5f520596e45fc23f4936bc2f6298b8145f9", "firstIndex": 8120172048}, +{"blockNumber": 15364606, "blockId": "0x593362ef8a82140f3dd3ff13d0ce50287dcb0b8b3d31ffcc7813c6be16f7438d", "firstIndex": 8187281275}, +{"blockNumber": 15412727, "blockId": "0x5a6ff10acc48e9e8bcf963b042012ea094628b9488b8bf569783b3297c67eedd", "firstIndex": 8254388575}, +{"blockNumber": 15462642, "blockId": "0x137f15c5d112b0fdc6e4c84393032030646122c3b0d86286143a1ab8bfacbac4", "firstIndex": 8321497214}, +{"blockNumber": 15509781, "blockId": "0xe58a8ca223b7c03755d046a6212bc33fa9814f73efe52804d8eea7ae4b4bd7f9", "firstIndex": 8388606496}, +{"blockNumber": 15558625, "blockId": "0x4127d527b58c7685a1237d228508ea77d40f33dbeffb5cc30e47e83a4b6152a5", "firstIndex": 8455714910}, +{"blockNumber": 15604075, "blockId": "0x7d1c1bccfe5c23a1edd81246e6e55277f271cffef30bde22c9d1202f764dd96f", "firstIndex": 8522823324}, +{"blockNumber": 15651742, "blockId": "0x732e5203a504fd97510325c1fb876476ca132831e78182367cd4b4efae1232c3", "firstIndex": 8589934155}, +{"blockNumber": 15700810, "blockId": "0xe031decb5e42a69dafcc814441227085508cf51c9c77f35d06487db19bd05a6a", "firstIndex": 8657041374}, +{"blockNumber": 15762855, "blockId": "0xbdff9b1e193c44f35bf38945d2523c74ef59c4f352efc93fad02ef34eef1c5f0", "firstIndex": 8724150924}, +{"blockNumber": 15814304, "blockId": "0x524a226d9f48e37db00e252c7f6a1350d1829d38fe83c756021cc11c56d3b2dd", "firstIndex": 8791259373}, +{"blockNumber": 15865514, "blockId": "0x4e4ad46f3ae7fb2a2e7cb94aa804929cb7353c79c78cc2cbcee710da5f0ec4b5", "firstIndex": 8858369571}, +{"blockNumber": 15920411, "blockId": "0xa39e4cbd8e84b427e44b8d06b5b1149652f3af5fa88633b802665b8677772bc5", "firstIndex": 8925477782}, +{"blockNumber": 15974193, "blockId": "0x4a3f82935e6fcda6ce7636001989b65b79422d362e60833e01185a7960b2fa55", "firstIndex": 8992587369}, +{"blockNumber": 16032739, "blockId": "0x7f50bb76dfd01125e0a4a7595106e43329eb8bd02e819be2ade4248a403c714a", "firstIndex": 9059696611}, +{"blockNumber": 16090896, "blockId": "0xdc4db130973ef793d2223844e35122a2477e642d86c69616fb3604d2a79805c4", "firstIndex": 9126804425}, +{"blockNumber": 16150783, "blockId": "0xb46294fe1cbcbbdff7309ebf0bf481e78e2589d386b95fa4c781c437e02e0aec", "firstIndex": 9193912139}, +{"blockNumber": 16207278, "blockId": "0x8a3af0387572b56989bb6c98d8860e07b5cdefb2ebe7b1dc58be07c24a53e056", "firstIndex": 9261022215}, +{"blockNumber": 16262389, "blockId": "0xec5a03d88cfc1bf8c278ddce7ace5032c3f7ed5032b4575df54779ec1abca131", "firstIndex": 9328130680}, +{"blockNumber": 16319525, "blockId": "0x74ae702375e242205fe4986448b0ee494d4a38ccc813981489c4b0488e58a949", "firstIndex": 9395240779}, +{"blockNumber": 16373441, "blockId": "0x5f404555cf45c547ef0fc044f2701bc7a28ce1e432de06e0264dbfd1c17447d0", "firstIndex": 9462347671}, +{"blockNumber": 16423343, "blockId": "0xe4af890b6c4dd3b10af2e6c08f14563bd2a3121b22a3cc6a0db29d438c1dcc59", "firstIndex": 9529457704}, +{"blockNumber": 16474687, "blockId": "0x019a8b374cf150fcda1c8af2a3e7ba63603ae81744d6343897c4b17ee58411d5", "firstIndex": 9596566169}, +{"blockNumber": 16525504, "blockId": "0xad394ce7e84e1c904cc5dd6dabd51b1a575c0b2fb006f10f272875b7f921904e", "firstIndex": 9663675287}, +{"blockNumber": 16574051, "blockId": "0x098e2dca2eda0d700aa3127bdfb679ce16e3ca297a305978c6ecfff044269511", "firstIndex": 9730784563}, +{"blockNumber": 16622457, "blockId": "0x8627f1237731c1127116898724f1b6420b8e7bcd8e18a0ea568f192bbd2cd55d", "firstIndex": 9797893241}, +{"blockNumber": 16672410, "blockId": "0x3e32b46faa670b191b89bea84a68022d5ea8b90008b388a948ff27f871d17292", "firstIndex": 9865002964}, +{"blockNumber": 16719964, "blockId": "0x1668c3ea6cb256f306e93851cc2c00612f85df5cf7b5527ffce14c89088d7fdf", "firstIndex": 9932111708}, +{"blockNumber": 16769014, "blockId": "0xed9a6f88e47b5bfe4d5bc92e1c1bfeb252f716f0990ffa1f907655c0f5e3a365", "firstIndex": 9999220312}, +{"blockNumber": 16818134, "blockId": "0x93504ba617500698a1b46f3110cd26827b8efe468190b2a91d3595784e34a1b6", "firstIndex": 10066329167}, +{"blockNumber": 16869369, "blockId": "0x94ca4b395a5c36c4e9d07cbb08271f0680455af4e4659906b647e16abebd7d4e", "firstIndex": 10133438415}, +{"blockNumber": 16921428, "blockId": "0x66650775bff4cb95de56099c10b06f3dfe302a731b391f406d1aa551121308ba", "firstIndex": 10200547148}, +{"blockNumber": 16974677, "blockId": "0x58d5fc4cd45047dac10d55a036e0a4448faacc1041b29c4c7fb41a6e9394c3ff", "firstIndex": 10267655623}, +{"blockNumber": 17031050, "blockId": "0x9d15b831979e95fc4149ecae46478cac1e54470b277d57d7102d19a72f28eeba", "firstIndex": 10334764441}, +{"blockNumber": 17086163, "blockId": "0x978ff48863fcdb50b274ea4220e7635c13c41bae91912db9e24205fb917fde85", "firstIndex": 10401873176}, +{"blockNumber": 17141086, "blockId": "0x4084d442499893361731f17534944d260cccd8e07d41c52d7160910555770b90", "firstIndex": 10468982462}, +{"blockNumber": 17190735, "blockId": "0x42ed86fb42def23870df6ec23204f557669ec7cd8de59c9f3ef9ab90704e4b11", "firstIndex": 10536090726}, +{"blockNumber": 17237026, "blockId": "0x9a49d943f82a7335f8049df4d953080f25920e3b098f6a2d872520166b225961", "firstIndex": 10603199938}, +{"blockNumber": 17287007, "blockId": "0xac3d189676d24eadc62adcf97d8730a001a7766fe3ac27b18e745ca9727f6c46", "firstIndex": 10670308186}, +{"blockNumber": 17338498, "blockId": "0xe50e251aa0a3209fa8a0030b237cf8357b08eeb80492a432c8aa5969a820ee42", "firstIndex": 10737417623}, +{"blockNumber": 17389178, "blockId": "0x1e18cedcfa6c81bc845c9136fc92f9f27ca8fff8847289bb139f374f0f6ba733", "firstIndex": 10804526769}, +{"blockNumber": 17442038, "blockId": "0x07f47ebbee99938fb96beb1ffadf8266f71d0d73f393172f0e24facd40d45c97", "firstIndex": 10871635940}, +{"blockNumber": 17497589, "blockId": "0x247a5e852e36e46f7897ad1875e93d810ff4434bb835b469afae3a3d3d4a9c9a", "firstIndex": 10938744251}, +{"blockNumber": 17554111, "blockId": "0x420e420a1b599e4dc3bd115652b4d6f5eedc391086ef064f97a9cbffc02b62f9", "firstIndex": 11005853686}, +{"blockNumber": 17608282, "blockId": "0xb2ba644d3593d3c832f932f14ffec532e26e9e1cdcd8fbe7477f46e238c6bffd", "firstIndex": 11072962303}, +{"blockNumber": 17664091, "blockId": "0x6d0bfff8145652a617bfe03ab87e3af09219cdb822c9c214b42b16f20742c1eb", "firstIndex": 11140070260}, +{"blockNumber": 17714917, "blockId": "0x61509544b5174d3ff5d48816ff54f2929c8a400ef21454116056baa136b607b6", "firstIndex": 11207179013}, +{"blockNumber": 17763850, "blockId": "0x8da83012621f42670601406c902ce6e48e77c6285dcc6983e108e69b3a8d5d2d", "firstIndex": 11274288732}, +{"blockNumber": 17814186, "blockId": "0xc37bda200856589c220dc206e31186d6c12761fd193f5bc7123cf331fbbed7f5", "firstIndex": 11341397343}, +{"blockNumber": 17864461, "blockId": "0x99af43893dc9f54c55abbdbd7b000e27248145371e9b00db116dfb961b36749c", "firstIndex": 11408506122}, +{"blockNumber": 17913212, "blockId": "0x9b642236d63d756fcd3404713bc6abe97804b46a9e665ae5f1d9aa48e693fe78", "firstIndex": 11475614347}, +{"blockNumber": 17961497, "blockId": "0xaec9b1e851cf51646673ec7698e1d5964d44eb04729ad11385f6ae84d497070a", "firstIndex": 11542723713}, +{"blockNumber": 18010859, "blockId": "0xb15fb7fc308516cad5f1dadfb7dd92fd3fe612308f67154cf2a241990aa15bc4", "firstIndex": 11609833204}, +{"blockNumber": 18061052, "blockId": "0xd8ea94b8b8a7380d21dc433abb69973f3de2eeb339f4aaffdb79f85a5d86929d", "firstIndex": 11676938565}, +{"blockNumber": 18111466, "blockId": "0x5b5cc6d182bffaf06692a7efda8cad5d4742fdbce5c1146b3be3c5be10ea046a", "firstIndex": 11744048539}, +{"blockNumber": 18166001, "blockId": "0x901d8401c795542c75069cd917781fa587ad416da0a5298f144fae2f14305705", "firstIndex": 11811159949}, +{"blockNumber": 18218505, "blockId": "0x5894285023f34ec6fd501cd90b605e118b9688ca7c01082289bf69a6987142bc", "firstIndex": 11878268395}, +{"blockNumber": 18271037, "blockId": "0x3f0fce315fd993d966b6c599ebd360462466e67046d512dc430ac07f75243bae", "firstIndex": 11945377050}, +{"blockNumber": 18322778, "blockId": "0xfa6536b3b06e7b579e53a8d01368182808c950586e39e12485be3145c3acbf43", "firstIndex": 12012486036}, +{"blockNumber": 18372240, "blockId": "0x5531859d5b8a4093020e2fb971808d0eeaddd7d61dc58d70f34923081f97336f", "firstIndex": 12079595358}, +{"blockNumber": 18421630, "blockId": "0xfeadff676fe8f04d0362b77ea2d7c3ea59b05d68cc6b2cc95cfe751558b1bfa7", "firstIndex": 12146703872}, +{"blockNumber": 18471472, "blockId": "0x352b0a17e4160d334b876eb180d686fd67c7ddc40a78219b0bc4b59f5ae5912d", "firstIndex": 12213813174}, +{"blockNumber": 18522088, "blockId": "0xf7d408309246089531b47c787049c7ffd2820c1b87b4804033a458bc193a3432", "firstIndex": 12280921181}, +{"blockNumber": 18572725, "blockId": "0x44126d4a5b43594b09d26e0b0a5dc859c074865cb9b11ab78c80f28406fd0240", "firstIndex": 12348029615}, +{"blockNumber": 18623223, "blockId": "0xd3aec1f98cea30e870d0f530194e9a72e225eb45eefe435e38da5967eb2fa470", "firstIndex": 12415139522}, +{"blockNumber": 18675228, "blockId": "0x24deaef2508038bddf8e9020f95128ae34eb1786f786c3f98df0bdf95f3e97cc", "firstIndex": 12482248123}, +{"blockNumber": 18725495, "blockId": "0x2e5a7edd7337b20e862741e116680a472089426dbc2b0eb5f1c984f8d6da935b", "firstIndex": 12549357121}, +{"blockNumber": 18778174, "blockId": "0xe734a1084fa5eeff427bba8e8d3623181a30dd9bad128fc22de9b15dffd0e549", "firstIndex": 12616466131}, +{"blockNumber": 18834807, "blockId": "0x0924a55ef4f9c2605261433c5b236d47645484efa44b34dc9f15b7c2e6b47a74", "firstIndex": 12683575000}, +{"blockNumber": 18883054, "blockId": "0xef638ec491a1cf685b22bf4072e41710440d6133a53e31250762825247d3eb64", "firstIndex": 12750683712}, +{"blockNumber": 18933405, "blockId": "0x6e9eefc2e121d13e6c04544ea370fe8543afeb36fef3a8e2ea68afb079738c8c", "firstIndex": 12817792510}, +{"blockNumber": 18988033, "blockId": "0x4c98c0dbb6a7217609c4b277fe7ff9e2c32c8d000ee628a3f37b2b296e1ca421", "firstIndex": 12884901389}, +{"blockNumber": 19041096, "blockId": "0x61a219a23d111cd07861ff47b89d4b2840c0ab5421d5d55a6f1fe43da3f33b98", "firstIndex": 12952010147}, +{"blockNumber": 19088914, "blockId": "0xefb26ba58447e50e9b820e0b0d16c08da45793a824af12a2e1f38b6f46b7e36a", "firstIndex": 13019118891}, +{"blockNumber": 19140413, "blockId": "0x91dbb13623fc4d5a4e462dfa0a03c92bf5301948dd4b2c3e5fddd2bc88856d47", "firstIndex": 13086228137}, +{"blockNumber": 19191876, "blockId": "0xdd7f97af7ad206b2636656ed4722fe1c47c6ba9fa5531066785acbe4f8d4c4ad", "firstIndex": 13153336551}, +{"blockNumber": 19237651, "blockId": "0xf1e875f314314abe3654b667c6443c772dd67c525c52877b06515ea8fc5a391b", "firstIndex": 13220444812}, +{"blockNumber": 19291032, "blockId": "0x9eda269ac47957b619d2a8c408b5efc8cfb168a46bfde351f43325fa337fce47", "firstIndex": 13287553967}, +{"blockNumber": 19344225, "blockId": "0x62859b78d45f589f679fc6a3bf266a2e0aa906fd61d787755ec51cfadc05eaf6", "firstIndex": 13354663338}, +{"blockNumber": 19394709, "blockId": "0x7cda3d193bcfe8ddbc89b85e98c814dbbe31a54ccab52ab7738ecd003061f3c8", "firstIndex": 13421772549}, +{"blockNumber": 19442850, "blockId": "0xd95f3f8bab5b91c3e1f848c9455936850bf92cf6a5086b817f6f81031bc0f81e", "firstIndex": 13488880704}, +{"blockNumber": 19488177, "blockId": "0x3a1a9623ee332d6decada9359cf84eeafb81fffa997d6cf4f3efcc92e98e9310", "firstIndex": 13555989570}, +{"blockNumber": 19534365, "blockId": "0x5c7a8ee7cbba4894ea412cc335895a9a7d3369d35779bcdc97a6c24b6a81a7ba", "firstIndex": 13623097446}, +{"blockNumber": 19579409, "blockId": "0x4280f5f76cb0f9947f9da1cb9de6c4b6e65b37b3a3c174f5fb29d360cf389ffb", "firstIndex": 13690206790}, +{"blockNumber": 19621406, "blockId": "0x0a98ca72e97fd295c7c73263b17185d74dbe427f41c616a5d5454faa5a1c63a0", "firstIndex": 13757316653}, +{"blockNumber": 19664868, "blockId": "0xc8c53d89b7a849861f3daaf9503e6397a0340cecb085850c3636bee6ea2b7189", "firstIndex": 13824424608}, +{"blockNumber": 19709011, "blockId": "0xf65babc88ef6a9eb2ffce9a6844adf895995f65a2facd1956f5df17ca68dbf94", "firstIndex": 13891534141}, +{"blockNumber": 19755170, "blockId": "0xf903882449eeb86d69af33b6797099275e82d8787334a02a42a2168dbd739b95", "firstIndex": 13958642627}, +{"blockNumber": 19803666, "blockId": "0xa8a975f570db082a3771790bb1fe405667dde4674ff49e31092687fa92727e36", "firstIndex": 14025752433}, +{"blockNumber": 19847730, "blockId": "0x89d479a4b4d9faa7af3e3f61d13a7a9c80a11c4adf7cd018859eb0ddba30bdd5", "firstIndex": 14092860598}, +{"blockNumber": 19894138, "blockId": "0x740278e6dc79cfd2b030780868a1b8e1520dbae0e8d3ee0a3ef43c84c83c6e0a", "firstIndex": 14159952129}, +{"blockNumber": 19938334, "blockId": "0x05ae7ce9813235c19baa57658bc5c8e160130328a1fd2cc58d2baf2d9a4ae097", "firstIndex": 14227078393}, +{"blockNumber": 19985138, "blockId": "0x511c8876a93a77fa473ae0d29e25d8163162c09b7212f506be0ea73840ea3971", "firstIndex": 14294187397}, +{"blockNumber": 20028221, "blockId": "0x64379baedf429cfa65df076142a7d9909d7e369d6aa6b8e161bb686944d169ff", "firstIndex": 14361296446}, +{"blockNumber": 20071555, "blockId": "0x4f43d8b1091d2bd0c6b080ce51bce08aae6be605e00889be3cdd17cb80614ff8", "firstIndex": 14428405409}, +{"blockNumber": 20113611, "blockId": "0xebbf87409ef6732a53513414926b0aa998a7269f9e9717a3acbe96a33c2de937", "firstIndex": 14495514255}, +{"blockNumber": 20156628, "blockId": "0x70218108c0669f42c4ac1afff80947cf4a0a2fd8e9300b31d45bbe330a5159c9", "firstIndex": 14562622257}, +{"blockNumber": 20199886, "blockId": "0x1d6aaa4b041eb0cc9caed34347bb943e50a139caf8998d781ff4f06d4aa727dc", "firstIndex": 14629732017}, +{"blockNumber": 20244148, "blockId": "0x37fed57b6945a815ec56c84c6261e90cf9f3883d9cbffc62b70d9722c1f0f363", "firstIndex": 14696839730}, +{"blockNumber": 20288249, "blockId": "0x1d9fe664431e093c4ef6139ad35fea767a2e66825e605cfea9e91d1738665619", "firstIndex": 14763948803}, +{"blockNumber": 20333358, "blockId": "0x1ac7c1362791507057b7e87d7118b4f61c2919c8ebdb498ecf7d64a59ba3d2e2", "firstIndex": 14831058814}, +{"blockNumber": 20376855, "blockId": "0xf6a19eac8a402f591079a4aa7ff57349c350ec96d683a031e4d85c05bc0541b2", "firstIndex": 14898167076}, +{"blockNumber": 20421474, "blockId": "0xe8767aa7516b632dc8c0fed3a366273ef14bd7b8c2212998b7105fb80b4c0d9b", "firstIndex": 14965276285}, +{"blockNumber": 20466974, "blockId": "0xafa2d2e6c9594762c163c339069cb15aa9fd27d301268892e12f99a3f2912ade", "firstIndex": 15032384674}, +{"blockNumber": 20512183, "blockId": "0xc1c51f9372d781d7ac0a1a7aa052503bbde3e1706bf65f225b0f6a6eed93d055", "firstIndex": 15099493832}, +{"blockNumber": 20557251, "blockId": "0xa905087c15e2b12b19cd23f20089b471cd9815dcff73842d2043e13a276fcc55", "firstIndex": 15166601921}, +{"blockNumber": 20595654, "blockId": "0x9e7772dd946f65e7569f34f6896d8e597ac8ff88700b871de21ff578c0c995e6", "firstIndex": 15233711474}, +{"blockNumber": 20638340, "blockId": "0xc08b46af83db6f0e5d1acad7020bacd59d0f18df72651d32c9b60edd0351a886", "firstIndex": 15300820584}, +{"blockNumber": 20683359, "blockId": "0x7ec93a1f1d211c24d93ce7e344634efef680a6388374cf3831d250ecd63711f9", "firstIndex": 15367929168}, +{"blockNumber": 20728401, "blockId": "0x4ad4124c2595abcf4477cac4eb70c0c9372bbd48f2066c7ac5b8e9f94817ab48", "firstIndex": 15435038303}, +{"blockNumber": 20771272, "blockId": "0x68fd6b12fdb4add8b2957105a21b571c7dcb114c6c2ca02f5fa40730a108f864", "firstIndex": 15502146509}, +{"blockNumber": 20814862, "blockId": "0x4e7f640bf3c135b2442ca4c744023021af163e8d45e8d776fb7051c49d79fcff", "firstIndex": 15569256173}, +{"blockNumber": 20857627, "blockId": "0x9b023bffed12530b5a68db263bb621a147e0cf02b30ec39a82b82a1fdeb7ca36", "firstIndex": 15636364955}, +{"blockNumber": 20896662, "blockId": "0x8c3adf694b8520ed1edd38282b2f58cf383426176628b5be6330a56882e2a07e", "firstIndex": 15703473445}, +{"blockNumber": 20939156, "blockId": "0x0649e2559f767da945a674c9d6c700628109e18d0757ad33496a228a827413a4", "firstIndex": 15770581979}, +{"blockNumber": 20981054, "blockId": "0xebf0ecca671b833f77df70106cf1171d4dd3fa0f062607e4845569073997f1cd", "firstIndex": 15837691319}, +{"blockNumber": 21022980, "blockId": "0x07479520c08a727c42e3616aca7fc4435773f4ef277d84369190263159606d71", "firstIndex": 15904799391}, +{"blockNumber": 21068139, "blockId": "0x669cfd03b293a61815588cb9cef928a512d328c1fe9191fe6b281573f429f8b8", "firstIndex": 15971908604}, +{"blockNumber": 21112368, "blockId": "0x98957aedaf8b0483be208e8d16a3e6d73ff217e732f3649650671c92c8fb57a5", "firstIndex": 16039007815}, +{"blockNumber": 21155756, "blockId": "0xc301e40988fbe19b1254ab3abe2f78fe0cd35259898be02476179a041926f777", "firstIndex": 16106127130}, +{"blockNumber": 21200717, "blockId": "0x8d35b199ee65c5ccf4ca0fc22b50282850cd88b72580679f1568eea67d3431e8", "firstIndex": 16173235593}, +{"blockNumber": 21244396, "blockId": "0xe7ec084eb8a7125fe26c4ef64fdbb3e0a727b2d882fb1e4fdad1d7fa3dc55fd2", "firstIndex": 16240343803}, +{"blockNumber": 21290031, "blockId": "0x8bb31b883d30875b7ed8a01c35a6f51e603b8d974c4e74af20fb9c723e5ac5df", "firstIndex": 16307452772}, +{"blockNumber": 21335893, "blockId": "0xd0976b7c946a7556e607e47a02d2cfcccd259508e2828b9c4faa5c7105a597a8", "firstIndex": 16374562657}, +{"blockNumber": 21378611, "blockId": "0x72d42953f013aeb7c84bb22c9e07828b92f73885b20c208af612874df8fe85ed", "firstIndex": 16441671192}, +{"blockNumber": 21421343, "blockId": "0x663b3cddcf3f7cfa59ed29b4894e43fa784c07576cfec897ff1a3838235ecb50", "firstIndex": 16508779766}, +{"blockNumber": 21466598, "blockId": "0xe04ebd60289dbcc09a27e053e7b240a59e928e2a76a85a6fa4044ee387c46d18", "firstIndex": 16575888836}, +{"blockNumber": 21511717, "blockId": "0x5ea9863a0b504f381010ff383d60a56390ba9cef162d9d5a077b1884d2adac98", "firstIndex": 16642998234}, +{"blockNumber": 21550050, "blockId": "0x2a7b3e635c80521887ca3ebda04541090ec720dd817cd06fcba61e7ae0f78a4f", "firstIndex": 16710106733}, +{"blockNumber": 21592440, "blockId": "0xc7095268d0d53cb5a069c2ea7107cc42172bb1b817f04154938e6b52bdbb369c", "firstIndex": 16777215261}, +{"blockNumber": 21636007, "blockId": "0x55f463b932053ebfc02728a12c2cdfb2cb4700a115cde686c9ab22bb4c336ecb", "firstIndex": 16844324844}, +{"blockNumber": 21680799, "blockId": "0x4521d473aa7f5150f920c66fe639b20f30cb373b32e425fcb7dcce9803b506be", "firstIndex": 16911431255}, +{"blockNumber": 21725330, "blockId": "0x793876007ce3594df603628766b139a02273ceee43d5b3ca2d0ca2f13a5fc5d9", "firstIndex": 16978541626}, +{"blockNumber": 21771310, "blockId": "0xd0db23bb28d6e39917348b1879dd560c73354ea6d7d0bbee0b7a8fcbcede1091", "firstIndex": 17045650902}, +{"blockNumber": 21811348, "blockId": "0x8165948c08db34aeb8cc2a3e331a00af2d09fbff9c466019fb2f2892ca3f7b71", "firstIndex": 17112760024}, +{"blockNumber": 21851615, "blockId": "0x3083e8f4fbb4d726ef19b1a5bce20e639a0418eac384f9967f24c7697a7e2dfc", "firstIndex": 17179868870}, +{"blockNumber": 21894041, "blockId": "0x9b0776a020374e4985f6a7e1199596bedd387ff085f65224d724ab11955c56fe", "firstIndex": 17246977342}, +{"blockNumber": 21932218, "blockId": "0xe9c50eddc21759d3ab790e2babba56f4b8d38eb739f1b561bc337a1ba6e15393", "firstIndex": 17314085541}, +{"blockNumber": 21959188, "blockId": "0x6b9c482cc4822af2ad62a359b923062556ab6a046d1a39a8243b764848690601", "firstIndex": 17381193886}, +{"blockNumber": 21994911, "blockId": "0x0d99ef9dd42dd1c62fc5249eb4f618e7672ab93fa0ba7545c77360371cf972e5", "firstIndex": 17448302795}, +{"blockNumber": 22026007, "blockId": "0xf7cdc7f6694f2c2ed31fa8a607f65cfa59d0dd7d7424ab5c017f959ae2982c71", "firstIndex": 17515413036}, +{"blockNumber": 22059890, "blockId": "0x82b768a0dddefda2eefd3a33596ea2be075312f1dd4b01f6b0d377faca2af98b", "firstIndex": 17582521768} ] - diff --git a/core/filtermaps/checkpoints_sepolia.json b/core/filtermaps/checkpoints_sepolia.json index 3e3ea23b24..c6d3cd9086 100644 --- a/core/filtermaps/checkpoints_sepolia.json +++ b/core/filtermaps/checkpoints_sepolia.json @@ -1,63 +1,68 @@ [ -{"blockNumber": 3246675, "blockId": "0x36bf7de9e1f151963088ca3efa206b6e78411d699d2f64f3bf86895294275e0b", "firstIndex": 67107286}, -{"blockNumber": 3575582, "blockId": "0x08931012467636d3b67ae187790951daed2bb6423f9cd94e166df787b856788d", "firstIndex": 134217672}, -{"blockNumber": 3694264, "blockId": "0x1f35f276a3c78e5942ee285fcbd0c687691853c599a2f5b174ea88f653bc9514", "firstIndex": 201326578}, -{"blockNumber": 3725632, "blockId": "0x3bcb264c56c3eeab6c8588145f09dff3fb5f821d9fc1e7b92264b14314dae553", "firstIndex": 268433636}, -{"blockNumber": 3795390, "blockId": "0x2d1ef2815bb8e018b275fa65540b98265285016aff12596bd89a3b1442d248eb", "firstIndex": 335542953}, -{"blockNumber": 3856683, "blockId": "0x8a9a46d6f53975cd9ec829c3c307a99fb62b8428cefb63ffe06d17143649c3ee", "firstIndex": 402648835}, -{"blockNumber": 3869370, "blockId": "0x2e8c04e7e5e96d09260b65d77b1770b4105b0db2ee7d638c48f086b8afac17db", "firstIndex": 469759276}, -{"blockNumber": 3938357, "blockId": "0xf20f2cdbcc412d5340e31955d14a6526ea748ba99b5ec70b6615bdb18bcd4cfb", "firstIndex": 536868027}, -{"blockNumber": 3984894, "blockId": "0x0bcd886b3cebb884d5beeaf5ad15ee1514968b5ad07177297c7d9c00f27aa406", "firstIndex": 603968430}, -{"blockNumber": 4002664, "blockId": "0x7d3575b6ca685468fa5a5fa9ff9d5fac4415b0a67a3ed87d3530f127db32fff4", "firstIndex": 671088417}, -{"blockNumber": 4113187, "blockId": "0x3a5313ac5b602134bb73535b22801261e891ccb7bd660ab20e0a536dc46d3e13", "firstIndex": 738197016}, -{"blockNumber": 4260758, "blockId": "0xe30fb9a304d3602896a5716d310f67ba34ccef7f809a3ead4b2d991cb9ee4eb0", "firstIndex": 805306270}, -{"blockNumber": 4391131, "blockId": "0x3958478c1c3be9b7caedbcc96230ed446d711e56580e324bc2fcf903fc87c90f", "firstIndex": 872415115}, -{"blockNumber": 4515650, "blockId": "0x46a3a7b97a9dff4ef4dc2c1cc5cd501f2182d9548655b77b5e07a2dbb41071a4", "firstIndex": 939523930}, -{"blockNumber": 4634818, "blockId": "0x2197d0dd3925c1d7ba3e2c4eef20035b68efc0a2506f76ddd9e481e0ce8ca6e1", "firstIndex": 1006628557}, -{"blockNumber": 4718295, "blockId": "0xcce7bb4af1a41e6056ef68192e60c738be01ac3e071ed1ec52cead08a39995ce", "firstIndex": 1073734698}, -{"blockNumber": 4753438, "blockId": "0xa60e043728a369cdf39a399bd7a903085ee9386f38176947578e5692b4b01f65", "firstIndex": 1140843192}, -{"blockNumber": 4786522, "blockId": "0x10629cadc00e65f193fa4d10ecd2bf1855e442814c4a409d19aae9eb895dce13", "firstIndex": 1207956586}, -{"blockNumber": 4811706, "blockId": "0xf1e94111f0086733bdcb4a653486a8b94ec998b61dda0af0fd465c9b4e344f87", "firstIndex": 1275058221}, -{"blockNumber": 4841796, "blockId": "0xa530f7dd72881ac831affdc579c9d75f6d4b6853b1f1894d320bd9047df5f9eb", "firstIndex": 1342177155}, -{"blockNumber": 4914835, "blockId": "0xbd8321e354f72c4190225f8ed63d4aba794b3b568677d985e099cb62d9d36bae", "firstIndex": 1409286143}, -{"blockNumber": 4992519, "blockId": "0x4a06a5a4aa5bc52151937cc1c0f8da691a0282e94aab8b73b9faa87da8d028de", "firstIndex": 1476384367}, -{"blockNumber": 5088668, "blockId": "0xb7d5ee03c08ed3936348eeb3931be8f804e61f2b09debf305967c6a7bbf007e0", "firstIndex": 1543502599}, -{"blockNumber": 5155029, "blockId": "0x84f590dfc2e11f1ca53c1757ac3c508d56f55ee24d6ca5501895974be4250d76", "firstIndex": 1610605837}, -{"blockNumber": 5204413, "blockId": "0xeaf2c3fb6f927c16d38fab08b34303867b87470558612404c7f9e3256b80c5b9", "firstIndex": 1677720841}, -{"blockNumber": 5269957, "blockId": "0x596e0b2e8e4c18c803b61767320fe32c063153d870c94e4a08e9a68cbaa582a9", "firstIndex": 1744825147}, -{"blockNumber": 5337678, "blockId": "0x7b2d54f8af1ecaaaab994e137d4421d8236c1c10d9a7bdcb9e5500db7a3fe9a3", "firstIndex": 1811939316}, -{"blockNumber": 5399058, "blockId": "0xb61ef16d55c96682fb62b0110a2dbc50d8eff2526be4121ece3690700611c71b", "firstIndex": 1879046044}, -{"blockNumber": 5422707, "blockId": "0xdabcab7c0cc9cb9f22f7507a1076c87831cb1afed9d0aa5bcd93f22266720c91", "firstIndex": 1946156915}, -{"blockNumber": 5454264, "blockId": "0xe1bde812906605ce662f5fd9f01b49c7331fb25f52ab5b12d35ea2b4da5458fe", "firstIndex": 2013259168}, -{"blockNumber": 5498898, "blockId": "0x9533d9c5353d22f8a235e95831cfbf4d5a7220a430ca23494526a9d3aa866fe8", "firstIndex": 2080374321}, -{"blockNumber": 5554801, "blockId": "0xe7b320bbecb19f1e99dd6ce4aed1efc754d7b2022e1f80389e8a21413c465f55", "firstIndex": 2147476253}, -{"blockNumber": 5594725, "blockId": "0xce6750be4a5b3e0fe152dd02308e94f7d56b254852a7e9acef6e14105053d7d1", "firstIndex": 2214591591}, -{"blockNumber": 5645198, "blockId": "0x5d42d39999c546f37001d5f613732fb54032384dd71a686d3664d2c8a1337752", "firstIndex": 2281696503}, -{"blockNumber": 5687659, "blockId": "0x3ed941be39a33ffa69cf3531a67f5a25f712ba05db890ff377f60d26842e4b1c", "firstIndex": 2348801751}, -{"blockNumber": 5727823, "blockId": "0xaf699b6c4cd58181bd609a66990b8edb5d1b94d5ff1ab732ded35ce7b8373353", "firstIndex": 2415917178}, -{"blockNumber": 5784505, "blockId": "0x621c740d04ea41f70a2f0537e21e5b96169aea8a8efee0ae5527717e5c40aa64", "firstIndex": 2483024581}, -{"blockNumber": 5843958, "blockId": "0xec122204a4e4698748f55a1c9f8582c46bacda029aee4de1a234e67e3288e6b1", "firstIndex": 2550136761}, -{"blockNumber": 5906359, "blockId": "0x8af5ce73fbd7a6110fb8b19b75a7322456ece88fcfa1614c745f1a65f4e915c1", "firstIndex": 2617245617}, -{"blockNumber": 5977944, "blockId": "0xbc8186258298a4f376124989cfb7b22c2bea6603a5245bb6c505c5fc45844bbd", "firstIndex": 2684350982}, -{"blockNumber": 6051571, "blockId": "0x54f9df9d9d73d1aa1cfcd6f372377c6013ecba2a1ed158d3c304f4fca51dae58", "firstIndex": 2751463209}, -{"blockNumber": 6118461, "blockId": "0xfea757fad3f763c62a514a9c24924934539ca56620bd811f83e9cc2e671f0cf0", "firstIndex": 2818572283}, -{"blockNumber": 6174385, "blockId": "0x2d8d0226e58f7516c13f9e1c9cf3ea65bb520fa1dfd7249dc9ea34a4e1fd430d", "firstIndex": 2885681036}, -{"blockNumber": 6276318, "blockId": "0xa922e9d54fd062b658c4866ed218632ddd51f250d671628a42968bb912d3ed5d", "firstIndex": 2952789983}, -{"blockNumber": 6368452, "blockId": "0x8d3d7466a7c9ca7298f82c37c38b0f64ec04522d2ed2e2349f8edc020c57f2c4", "firstIndex": 3019898695}, -{"blockNumber": 6470810, "blockId": "0x9887c35542835ee81153fa0e4d8a9e6f170b6e14fc78d8c7f3d900d0a70434f1", "firstIndex": 3087007578}, -{"blockNumber": 6553334, "blockId": "0x7b0d89a0282c18785fcc108dbdc9d45dd9d63b7084ddc676df9e9504585a5969", "firstIndex": 3154115987}, -{"blockNumber": 6663825, "blockId": "0xff6cec99324a89d6d36275c17a4569f0cba203fe5b0350f155a7d5445e0ed419", "firstIndex": 3221224775}, -{"blockNumber": 6767082, "blockId": "0xe10a96a7194f98bf262f0cb1cdfb4d3b9a2072139dfcbe3f1eb01419e353044e", "firstIndex": 3288334139}, -{"blockNumber": 6886709, "blockId": "0x20f6a5d986913025ad5b6b6387d818e49a3caf838326f4002c1439ca61313be5", "firstIndex": 3355442979}, -{"blockNumber": 6978948, "blockId": "0xd7c3024765245ec49e6a48b076d540bc91f57f2ccc125e17d60dd37bb669f843", "firstIndex": 3422551908}, -{"blockNumber": 7098891, "blockId": "0x05114c037e1b4d69a46d74a974be9bce45e87ad2226a59b44dd17f98dd2fd0d1", "firstIndex": 3489659530}, -{"blockNumber": 7203157, "blockId": "0xc0f610014fcd9f2850274b58179d474f0947676fd0639b2884316467c631811d", "firstIndex": 3556769512}, -{"blockNumber": 7256735, "blockId": "0x0324c15b3b23fd82c2962dd167618e77e60ebeac5a2c87f672caddc9732337b3", "firstIndex": 3623876508}, -{"blockNumber": 7307851, "blockId": "0x8e23280d1a3aec877d7758413ed20299d381aa43e7e2fc6f381ad96e8ff0acef", "firstIndex": 3690987098}, -{"blockNumber": 7369389, "blockId": "0xbf6436eb2b88539945d6673141a14cb79ffc1e7db2b57176acf8e02ff3b6fcd3", "firstIndex": 3758096287}, -{"blockNumber": 7445220, "blockId": "0x147619f74815283d834ac08ff494fb4791207b3949c64b2623f11ff6141ee7a7", "firstIndex": 3825204992}, -{"blockNumber": 7511632, "blockId": "0x5094d64868f419e6ac3d253d19d5feda76564a0d56d7bbf8a822dff1c2261b30", "firstIndex": 3892314047}, -{"blockNumber": 7557280, "blockId": "0x54aba9351a1ba51873645221aa7c991024da1fe468a600ddb6e2559351d9c28f", "firstIndex": 3959422859}, -{"blockNumber": 7606304, "blockId": "0xbbe2fed08cf0b0ff2cb6ae9fd7257843f77a04a7d4cafb06d7a4bedea6ab0c98", "firstIndex": 4026531690} +{"blockNumber": 3246675, "blockId": "0x36bf7de9e1f151963088ca3efa206b6e78411d699d2f64f3bf86895294275e0b", "firstIndex": 67108765}, +{"blockNumber": 3575560, "blockId": "0x4652e3bee440f946aeaa3358c9a62b9bc4d41f663737ab00fded00c2662bfa29", "firstIndex": 134217644}, +{"blockNumber": 3694262, "blockId": "0x433573f97e563ca04fa53e0d0eb019d0ffe9dd032a05ee5b33ce7705aedfd34d", "firstIndex": 201316990}, +{"blockNumber": 3725628, "blockId": "0x1e25ebd76b9e2a2007bfdd7c82433cce7dae8beb190d249c82bf83d0cd177735", "firstIndex": 268426577}, +{"blockNumber": 3795378, "blockId": "0xca999083e01d5947212fb878d529abecdd18a6012997b0d5326afcf6908f17e5", "firstIndex": 335543438}, +{"blockNumber": 3856681, "blockId": "0x48148cd35de3de6c626f76ce5d97cfc6acd88772af69c0aef3d640c29ba82096", "firstIndex": 402641717}, +{"blockNumber": 3869367, "blockId": "0x08bafccf9ece7b72e4e069fc3c7493fbf25732881758917df2e8f9a51fc2c75f", "firstIndex": 469761225}, +{"blockNumber": 3938346, "blockId": "0xc638eb890c0e65ce1169ef0bb6b74dede2f4f6144ff8a5739c4894ff41a22fa2", "firstIndex": 536865235}, +{"blockNumber": 3984892, "blockId": "0x6a7782f0765b0f0c406b3cb107fd8bb14c6906293d9bdc5cc00e081bfd1ae59f", "firstIndex": 603972697}, +{"blockNumber": 4002660, "blockId": "0xda473de053602bc42fcbb074a32c908303f7f6db2ed8c38192665dce80276702", "firstIndex": 671087183}, +{"blockNumber": 4113149, "blockId": "0xd1782f677262226194725028858da83e672748e409fe1c21bdd77c07be0662c8", "firstIndex": 738197365}, +{"blockNumber": 4260733, "blockId": "0xc356600819e49d311963312157c481fd9be43a36ad040a0512fd17559c78d394", "firstIndex": 805305957}, +{"blockNumber": 4391073, "blockId": "0x06f903dc765b4b0ca1a40847eb74d30b0b51904a70c46f2e836dc5b3ea3dd8d3", "firstIndex": 872415067}, +{"blockNumber": 4515567, "blockId": "0xe99d3849755a1b4ec9a65d003260c111d4886e29ad6243fc750c72dbc79dbe4c", "firstIndex": 939523746}, +{"blockNumber": 4634815, "blockId": "0x9f281e749b192ef58242ff9fccd496a9075c30bd28c61ed4ce6487fc2d167e5c", "firstIndex": 1006629595}, +{"blockNumber": 4718286, "blockId": "0x23d2a40ec3ae059a45006ba651a70f308b6b282829b9cfdf2534e4311e34050f", "firstIndex": 1073731435}, +{"blockNumber": 4753427, "blockId": "0x6df7295f9ccb45a95e6cc72a8ee56bda992513b3bf4baa3d62219871a0b5a912", "firstIndex": 1140843105}, +{"blockNumber": 4786511, "blockId": "0x705c79435411c1af81385a1eca34343427eceb3f6af72e7f665552d6b0eceb6c", "firstIndex": 1207957185}, +{"blockNumber": 4811696, "blockId": "0x69a6e60d1b958b469feafb1dca97f38f97620a90c21ed92f90080f6443ee6c78", "firstIndex": 1275061401}, +{"blockNumber": 4841770, "blockId": "0x0a0e9d2d3d177601a478428297b5b9a42124166fefe42f2221c7a612c31e7ca1", "firstIndex": 1342174442}, +{"blockNumber": 4914785, "blockId": "0x5ca0e12230aeacbce9064dff15dafb9f24e20b4a737f7d04df8549008b829fa3", "firstIndex": 1409284680}, +{"blockNumber": 4992516, "blockId": "0xb9cb1700d8b65615fc3a019b2e9b2ece3b4f1e0e2fcfc01ff40bcf53e7291b10", "firstIndex": 1476387066}, +{"blockNumber": 5088617, "blockId": "0x33122f693a264fb2ab626232fac5e2714010fd65bdceb772109511f7c9497333", "firstIndex": 1543503849}, +{"blockNumber": 5155024, "blockId": "0x187716822dd6ab9c21138ae5a8e763197a5e7ec48bf0e756c5e6fa2c9f2f24bf", "firstIndex": 1610604247}, +{"blockNumber": 5204373, "blockId": "0x753b6495979ca90a1b915edad6af33c58bbabe68b45f95c7645113ec1d35d0e6", "firstIndex": 1677721200}, +{"blockNumber": 5269952, "blockId": "0xcbb0409ed824631120d6951b55537143d9a1c82ca6dd6255d2eb31c57844685c", "firstIndex": 1744829248}, +{"blockNumber": 5337623, "blockId": "0x0b460658511c4ae5205c038430789f9b31f3c08ba4acc06d273b7a13a149cf8b", "firstIndex": 1811939255}, +{"blockNumber": 5399044, "blockId": "0x9d07e37321e2b019f4837cc4f95f103da87ecdcb77e131e9d42d8643b0446524", "firstIndex": 1879041131}, +{"blockNumber": 5422692, "blockId": "0x9d9e27614635989788e32f15cd7a0abf7470dccf3336b77547baeb3423b629ca", "firstIndex": 1946154968}, +{"blockNumber": 5454259, "blockId": "0x01e694443d471d3411fe0b52720fd4a189e687b14cc8ede116f5e93f0ad96bb6", "firstIndex": 2013265148}, +{"blockNumber": 5498863, "blockId": "0xc4cb906c96d9f80b830b8add25318c38b3ff857c1bd577cd9cae0a324a068ffc", "firstIndex": 2080373677}, +{"blockNumber": 5554788, "blockId": "0x9b934b2d66723559d82e0f4cd49a2497234fe892248cb2f46c13f6a9585cfddd", "firstIndex": 2147475372}, +{"blockNumber": 5594711, "blockId": "0x840c0b7889b8c882cfa0eceb516dd73043b51c0e5a0c1be849a0e2a5dda7e842", "firstIndex": 2214592267}, +{"blockNumber": 5645179, "blockId": "0x188873a43506fee33349bc33df4a55059401e98dc7f0a5d488a0bc5c87dfd431", "firstIndex": 2281695869}, +{"blockNumber": 5687634, "blockId": "0x7ea8d7a2afccf795a2a02eb00746d89db57f13f8936cf7ab0f0a7db1b1ceb341", "firstIndex": 2348806824}, +{"blockNumber": 5727808, "blockId": "0x3f3eaf36c8e0271403737676907eac15312927c1d60ce814337654b7d7f2b2bd", "firstIndex": 2415915517}, +{"blockNumber": 5784480, "blockId": "0x10ec5adf01449c29550f5117f09ecafc49fc614d7da5886cf01619950827f850", "firstIndex": 2483023750}, +{"blockNumber": 5843906, "blockId": "0x9e6e14b3fba395a9ae7b3f1295f33a12d0f8b9f10e1aaab1649a90a56a323676", "firstIndex": 2550136398}, +{"blockNumber": 5906280, "blockId": "0x2188869a164e9c19232f6702a5233dc27a2304839159dd7230cc93ebdfc6c048", "firstIndex": 2617245012}, +{"blockNumber": 5977929, "blockId": "0xb247181ed597d9473c2c6e5762ae45a05f328d8df215fdac83d19ae4c028109a", "firstIndex": 2684347033}, +{"blockNumber": 6051479, "blockId": "0x0e8634c95dac31b7c835b44d0a14290e9212b31b9c3f621997ad1cca2d8ab0ba", "firstIndex": 2751463372}, +{"blockNumber": 6118422, "blockId": "0xa642c4457d4b63d68a5502bdba2355616e34495bf3ab2d8518a45ded54a16be4", "firstIndex": 2818563217}, +{"blockNumber": 6174274, "blockId": "0xbb9808bc56b5f14636e654bdd6d50d35b31345255926341ef3ddb54840095bcc", "firstIndex": 2885680274}, +{"blockNumber": 6276207, "blockId": "0x7ae027a18889e1b60a46ea3e5f2759035f3d9041de68eca0b154068ec533db59", "firstIndex": 2952789656}, +{"blockNumber": 6368344, "blockId": "0x8b4401f89589d9e99259e6c05c18b1780e96b65027caf9d83edf80390d317f66", "firstIndex": 3019898378}, +{"blockNumber": 6470718, "blockId": "0x0e2f8de093392c38750581c41e93c2fb1c0f6676b65c9af52792f3c56e894457", "firstIndex": 3087007589}, +{"blockNumber": 6553237, "blockId": "0xa6c4f3a1fe3b4e116c367fcbb1ae98e0f25570fdb2f1eadee2681deb39462c1e", "firstIndex": 3154116356}, +{"blockNumber": 6663750, "blockId": "0x9049748ddf655c7cc2ccbe2d36010b3a67ae011d114d32b1c812a7a53102f27f", "firstIndex": 3221225375}, +{"blockNumber": 6766894, "blockId": "0xc98188fb313b1beb7fc4f415b4a74c363903b3b5e0172437d79446bc011238b5", "firstIndex": 3288334172}, +{"blockNumber": 6886576, "blockId": "0x4ae8237a94198c82c266f01e472fdcd3ae150c84ecf714205a9be672f1e705b8", "firstIndex": 3355443040}, +{"blockNumber": 6978746, "blockId": "0xb2b69ce29c5c9c46c5cbc6e96260c34313d8b0b4a739cf4091c98f37093aa732", "firstIndex": 3422552017}, +{"blockNumber": 7098871, "blockId": "0x9ab2b681feeb38287a1b689fa64a50efd3407b6331fd6896187cad98a29ce107", "firstIndex": 3489660879}, +{"blockNumber": 7203023, "blockId": "0xdedfff5985c1c93d476120d2afbaa61967bae4edabb9711c7794a1b1cd453aa1", "firstIndex": 3556769365}, +{"blockNumber": 7256709, "blockId": "0xc078041cffc1802b6342fd2ba5a92ff8076ac0f5b72b4a77ba75cd486fe1b3c9", "firstIndex": 3623876563}, +{"blockNumber": 7307744, "blockId": "0xdf8dcdd4af9213a79587eb2f4d91f4111da74f99207ee05e4ae50bcb4f8d5435", "firstIndex": 3690986225}, +{"blockNumber": 7369268, "blockId": "0x62315823621a38a1f138e193b85259988fcc15f4a74d0cf19a412920bcdcad98", "firstIndex": 3758096030}, +{"blockNumber": 7445109, "blockId": "0x8b668ac1274a8dcc9526eb115c2f67bd56f6657b5072c7542da2252daca10fef", "firstIndex": 3825204921}, +{"blockNumber": 7511512, "blockId": "0x056ad7411652aa64849c67705a86e42221cb88f7fb6a7012ab7d3d5b9a30eb76", "firstIndex": 3892313524}, +{"blockNumber": 7557259, "blockId": "0xef660b97661f073b7c7319b69451e16d26ecaed54f9237e209999d137da2bfd4", "firstIndex": 3959415096}, +{"blockNumber": 7606260, "blockId": "0x9f5f06ad381166261780864bf17ad4d3cdd475f87ba51fa7f9ba621bf0ac0acb", "firstIndex": 4026521703}, +{"blockNumber": 7654244, "blockId": "0xefe5b537435001203741bf445ca26466baf7ad3f63c2bba94f0f658aed8c244b", "firstIndex": 4093627258}, +{"blockNumber": 7700433, "blockId": "0x89af6e8c5aa934a9139944b3de7a15232069479dba387e0fdd043ad36156c8bf", "firstIndex": 4160749200}, +{"blockNumber": 7730079, "blockId": "0x08e0d511a93e2a9c004b3456d77d687ae86247417ada1cdd1f85332a62dfed1d", "firstIndex": 4227846795}, +{"blockNumber": 7777515, "blockId": "0xeba8faed2e12bb9ed5e7fad19dd82662f15f6f4f512a0d232eedf1e676712428", "firstIndex": 4294966082}, +{"blockNumber": 7828860, "blockId": "0x62c054bd24be351dc4777460ee922a75fdcea5c9830455cbac6fa29552719c85", "firstIndex": 4362076087}, +{"blockNumber": 7869890, "blockId": "0x5664bcfa6151f798781e22bea4f9a2c796d097402350cb131e4e3c78e13e461a", "firstIndex": 4429183267} ] - diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index 95dd181551..fe75c1ea7d 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -95,11 +95,11 @@ type FilterMaps struct { targetView *ChainView matcherSyncRequest *FilterMapsMatcherBackend + historyCutoff uint64 finalBlock, lastFinal uint64 lastFinalEpoch uint32 stop bool - targetViewCh chan *ChainView - finalBlockCh chan uint64 + targetCh chan targetUpdate blockProcessingCh chan bool blockProcessing bool matcherSyncCh chan *FilterMapsMatcherBackend @@ -145,24 +145,23 @@ func (a FilterRow) Equal(b FilterRow) bool { // filterMapsRange describes the rendered range of filter maps and the range // of fully rendered blocks. type filterMapsRange struct { - initialized bool - headBlockIndexed bool - headBlockDelimiter uint64 // zero if afterLastIndexedBlock != targetBlockNumber - // if initialized then all maps are rendered between firstRenderedMap and - // afterLastRenderedMap-1 - firstRenderedMap, afterLastRenderedMap uint32 + initialized bool + headIndexed bool + headDelimiter uint64 // zero if headIndexed is false + // if initialized then all maps are rendered in the maps range + maps common.Range[uint32] // if tailPartialEpoch > 0 then maps between firstRenderedMap-mapsPerEpoch and // firstRenderedMap-mapsPerEpoch+tailPartialEpoch-1 are rendered tailPartialEpoch uint32 - // if initialized then all log values belonging to blocks between - // firstIndexedBlock and afterLastIndexedBlock are fully rendered - // blockLvPointers are available between firstIndexedBlock and afterLastIndexedBlock-1 - firstIndexedBlock, afterLastIndexedBlock uint64 + // if initialized then all log values in the blocks range are fully + // rendered + // blockLvPointers are available in the blocks range + blocks common.Range[uint64] } // hasIndexedBlocks returns true if the range has at least one fully indexed block. func (fmr *filterMapsRange) hasIndexedBlocks() bool { - return fmr.initialized && fmr.afterLastIndexedBlock > fmr.firstIndexedBlock + return fmr.initialized && !fmr.blocks.IsEmpty() && !fmr.maps.IsEmpty() } // lastBlockOfMap is used for caching the (number, id) pairs belonging to the @@ -183,7 +182,7 @@ type Config struct { } // NewFilterMaps creates a new FilterMaps and starts the indexer. -func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, params Params, config Config) *FilterMaps { +func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, finalBlock uint64, params Params, config Config) *FilterMaps { rs, initialized, err := rawdb.ReadFilterMapsRange(db) if err != nil { log.Error("Error reading log index range", "error", err) @@ -193,22 +192,19 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, params Params, c db: db, closeCh: make(chan struct{}), waitIdleCh: make(chan chan bool), - targetViewCh: make(chan *ChainView, 1), - finalBlockCh: make(chan uint64, 1), + targetCh: make(chan targetUpdate, 1), blockProcessingCh: make(chan bool, 1), history: config.History, disabled: config.Disabled, exportFileName: config.ExportFileName, Params: params, indexedRange: filterMapsRange{ - initialized: initialized, - headBlockIndexed: rs.HeadBlockIndexed, - headBlockDelimiter: rs.HeadBlockDelimiter, - firstIndexedBlock: rs.FirstIndexedBlock, - afterLastIndexedBlock: rs.AfterLastIndexedBlock, - firstRenderedMap: rs.FirstRenderedMap, - afterLastRenderedMap: rs.AfterLastRenderedMap, - tailPartialEpoch: rs.TailPartialEpoch, + initialized: initialized, + headIndexed: rs.HeadIndexed, + headDelimiter: rs.HeadDelimiter, + blocks: common.NewRange(rs.BlocksFirst, rs.BlocksAfterLast-rs.BlocksFirst), + maps: common.NewRange(rs.MapsFirst, rs.MapsAfterLast-rs.MapsFirst), + tailPartialEpoch: rs.TailPartialEpoch, }, matcherSyncCh: make(chan *FilterMapsMatcherBackend), matchers: make(map[*FilterMapsMatcherBackend]struct{}), @@ -223,24 +219,23 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, params Params, c f.targetView = initView if f.indexedRange.initialized { f.indexedView = f.initChainView(f.targetView) - f.indexedRange.headBlockIndexed = f.indexedRange.afterLastIndexedBlock == f.indexedView.headNumber+1 - if !f.indexedRange.headBlockIndexed { - f.indexedRange.headBlockDelimiter = 0 + f.indexedRange.headIndexed = f.indexedRange.blocks.AfterLast() == f.indexedView.headNumber+1 + if !f.indexedRange.headIndexed { + f.indexedRange.headDelimiter = 0 } } if f.indexedRange.hasIndexedBlocks() { log.Info("Initialized log indexer", - "first block", f.indexedRange.firstIndexedBlock, "last block", f.indexedRange.afterLastIndexedBlock-1, - "first map", f.indexedRange.firstRenderedMap, "last map", f.indexedRange.afterLastRenderedMap-1, - "head indexed", f.indexedRange.headBlockIndexed) + "first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(), + "first map", f.indexedRange.maps.First(), "last map", f.indexedRange.maps.Last(), + "head indexed", f.indexedRange.headIndexed) } return f } // Start starts the indexer. func (f *FilterMaps) Start() { - if !f.testDisableSnapshots && f.indexedRange.initialized && f.indexedRange.headBlockIndexed && - f.indexedRange.firstRenderedMap < f.indexedRange.afterLastRenderedMap { + if !f.testDisableSnapshots && f.indexedRange.hasIndexedBlocks() && f.indexedRange.headIndexed { // previous target head rendered; load last map as snapshot if err := f.loadHeadSnapshot(); err != nil { log.Error("Could not load head filter map snapshot", "error", err) @@ -262,7 +257,7 @@ func (f *FilterMaps) Stop() { // Note that the returned view might be shorter than the existing index if // the latest maps are not consistent with targetView. func (f *FilterMaps) initChainView(chainView *ChainView) *ChainView { - mapIndex := f.indexedRange.afterLastRenderedMap + mapIndex := f.indexedRange.maps.AfterLast() for { var ok bool mapIndex, ok = f.lastMapBoundaryBefore(mapIndex) @@ -332,10 +327,8 @@ func (f *FilterMaps) init() error { } if bestLen > 0 { cp := checkpoints[bestIdx][bestLen-1] - fmr.firstIndexedBlock = cp.BlockNumber + 1 - fmr.afterLastIndexedBlock = cp.BlockNumber + 1 - fmr.firstRenderedMap = uint32(bestLen) << f.logMapsPerEpoch - fmr.afterLastRenderedMap = uint32(bestLen) << f.logMapsPerEpoch + fmr.blocks = common.NewRange(cp.BlockNumber+1, 0) + fmr.maps = common.NewRange(uint32(bestLen)<> f.logValuesPerMap) - if mapIndex < f.indexedRange.firstRenderedMap || mapIndex >= f.indexedRange.afterLastRenderedMap { + if !f.indexedRange.maps.Includes(mapIndex) { return nil, nil } // find possible block range based on map to block pointers @@ -425,8 +418,8 @@ func (f *FilterMaps) getLogByLvIndex(lvIndex uint64) (*types.Log, error) { return nil, fmt.Errorf("failed to retrieve last block of map %d before searched log value index %d: %v", mapIndex, lvIndex, err) } } - if firstBlockNumber < f.indexedRange.firstIndexedBlock { - firstBlockNumber = f.indexedRange.firstIndexedBlock + if firstBlockNumber < f.indexedRange.blocks.First() { + firstBlockNumber = f.indexedRange.blocks.First() } // find block with binary search based on block to log value index pointers for firstBlockNumber < lastBlockNumber { @@ -453,6 +446,11 @@ func (f *FilterMaps) getLogByLvIndex(lvIndex uint64) (*types.Log, error) { // iterate through receipts to find the exact log starting at lvIndex for _, receipt := range receipts { for _, log := range receipt.Logs { + l := uint64(len(log.Topics) + 1) + r := f.valuesPerMap - lvPointer%f.valuesPerMap + if l > r { + lvPointer += r // skip to map boundary + } if lvPointer > lvIndex { // lvIndex does not point to the first log value (address value) // generated by a log as true matches should always do, so it @@ -462,7 +460,7 @@ func (f *FilterMaps) getLogByLvIndex(lvIndex uint64) (*types.Log, error) { if lvPointer == lvIndex { return log, nil // potential match } - lvPointer += uint64(len(log.Topics) + 1) + lvPointer += l } } return nil, nil @@ -580,8 +578,8 @@ func (f *FilterMaps) mapRowIndex(mapIndex, rowIndex uint32) uint64 { // Note that this function assumes that the indexer read lock is being held when // called from outside the indexerLoop goroutine. func (f *FilterMaps) getBlockLvPointer(blockNumber uint64) (uint64, error) { - if blockNumber >= f.indexedRange.afterLastIndexedBlock && f.indexedRange.headBlockIndexed { - return f.indexedRange.headBlockDelimiter, nil + if blockNumber >= f.indexedRange.blocks.AfterLast() && f.indexedRange.headIndexed { + return f.indexedRange.headDelimiter, nil } if lvPointer, ok := f.lvPointerCache.Get(blockNumber); ok { return lvPointer, nil @@ -658,26 +656,28 @@ func (f *FilterMaps) deleteTailEpoch(epoch uint32) error { firstBlock++ } fmr := f.indexedRange - if f.indexedRange.firstRenderedMap == firstMap && - f.indexedRange.afterLastRenderedMap > firstMap+f.mapsPerEpoch && + if f.indexedRange.maps.First() == firstMap && + f.indexedRange.maps.AfterLast() > firstMap+f.mapsPerEpoch && f.indexedRange.tailPartialEpoch == 0 { - fmr.firstRenderedMap = firstMap + f.mapsPerEpoch - fmr.firstIndexedBlock = lastBlock + 1 - } else if f.indexedRange.firstRenderedMap == firstMap+f.mapsPerEpoch { + fmr.maps.SetFirst(firstMap + f.mapsPerEpoch) + fmr.blocks.SetFirst(lastBlock + 1) + } else if f.indexedRange.maps.First() == firstMap+f.mapsPerEpoch { fmr.tailPartialEpoch = 0 } else { return errors.New("invalid tail epoch number") } f.setRange(f.db, f.indexedView, fmr) - rawdb.DeleteFilterMapRows(f.db, f.mapRowIndex(firstMap, 0), f.mapRowIndex(firstMap+f.mapsPerEpoch, 0)) + first := f.mapRowIndex(firstMap, 0) + count := f.mapRowIndex(firstMap+f.mapsPerEpoch, 0) - first + rawdb.DeleteFilterMapRows(f.db, common.NewRange(first, count)) for mapIndex := firstMap; mapIndex < firstMap+f.mapsPerEpoch; mapIndex++ { f.filterMapCache.Remove(mapIndex) } - rawdb.DeleteFilterMapLastBlocks(f.db, firstMap, firstMap+f.mapsPerEpoch-1) // keep last enrty + rawdb.DeleteFilterMapLastBlocks(f.db, common.NewRange(firstMap, f.mapsPerEpoch-1)) // keep last enrty for mapIndex := firstMap; mapIndex < firstMap+f.mapsPerEpoch-1; mapIndex++ { f.lastBlockCache.Remove(mapIndex) } - rawdb.DeleteBlockLvPointers(f.db, firstBlock, lastBlock) // keep last enrty + rawdb.DeleteBlockLvPointers(f.db, common.NewRange(firstBlock, lastBlock-firstBlock)) // keep last enrty for blockNumber := firstBlock; blockNumber < lastBlock; blockNumber++ { f.lvPointerCache.Remove(blockNumber) } diff --git a/core/filtermaps/indexer.go b/core/filtermaps/indexer.go index 54a3687648..addda03df4 100644 --- a/core/filtermaps/indexer.go +++ b/core/filtermaps/indexer.go @@ -66,28 +66,25 @@ func (f *FilterMaps) indexerLoop() { } } +type targetUpdate struct { + targetView *ChainView + historyCutoff, finalBlock uint64 +} + // SetTargetView sets a new target chain view for the indexer to render. // Note that SetTargetView never blocks. -func (f *FilterMaps) SetTargetView(targetView *ChainView) { +func (f *FilterMaps) SetTarget(targetView *ChainView, historyCutoff, finalBlock uint64) { if targetView == nil { panic("nil targetView") } for { select { - case <-f.targetViewCh: - case f.targetViewCh <- targetView: - return - } - } -} - -// SetFinalBlock sets the finalized block number used for exporting checkpoints. -// Note that SetFinalBlock never blocks. -func (f *FilterMaps) SetFinalBlock(finalBlock uint64) { - for { - select { - case <-f.finalBlockCh: - case f.finalBlockCh <- finalBlock: + case <-f.targetCh: + case f.targetCh <- targetUpdate{ + targetView: targetView, + historyCutoff: historyCutoff, + finalBlock: finalBlock, + }: return } } @@ -145,26 +142,24 @@ func (f *FilterMaps) processSingleEvent(blocking bool) bool { } if blocking { select { - case targetView := <-f.targetViewCh: - f.setTargetView(targetView) - case f.finalBlock = <-f.finalBlockCh: + case target := <-f.targetCh: + f.setTarget(target) case f.matcherSyncRequest = <-f.matcherSyncCh: case f.blockProcessing = <-f.blockProcessingCh: case <-f.closeCh: f.stop = true case ch := <-f.waitIdleCh: select { - case targetView := <-f.targetViewCh: - f.setTargetView(targetView) + case target := <-f.targetCh: + f.setTarget(target) default: } ch <- !f.blockProcessing && f.targetHeadIndexed() } } else { select { - case targetView := <-f.targetViewCh: - f.setTargetView(targetView) - case f.finalBlock = <-f.finalBlockCh: + case target := <-f.targetCh: + f.setTarget(target) case f.matcherSyncRequest = <-f.matcherSyncCh: case f.blockProcessing = <-f.blockProcessingCh: case <-f.closeCh: @@ -177,8 +172,10 @@ func (f *FilterMaps) processSingleEvent(blocking bool) bool { } // setTargetView updates the target chain view of the iterator. -func (f *FilterMaps) setTargetView(targetView *ChainView) { - f.targetView = targetView +func (f *FilterMaps) setTarget(target targetUpdate) { + f.targetView = target.targetView + f.historyCutoff = target.historyCutoff + f.finalBlock = target.finalBlock } // tryIndexHead tries to render head maps according to the current targetView @@ -196,20 +193,20 @@ func (f *FilterMaps) tryIndexHead() bool { f.lastLogHeadIndex = time.Now() f.startedHeadIndexAt = f.lastLogHeadIndex f.startedHeadIndex = true - f.ptrHeadIndex = f.indexedRange.afterLastIndexedBlock + f.ptrHeadIndex = f.indexedRange.blocks.AfterLast() } if _, err := headRenderer.run(func() bool { f.processEvents() return f.stop }, func() { f.tryUnindexTail() - if f.indexedRange.hasIndexedBlocks() && f.indexedRange.afterLastIndexedBlock >= f.ptrHeadIndex && + if f.indexedRange.hasIndexedBlocks() && f.indexedRange.blocks.AfterLast() >= f.ptrHeadIndex && ((!f.loggedHeadIndex && time.Since(f.startedHeadIndexAt) > headLogDelay) || time.Since(f.lastLogHeadIndex) > logFrequency) { log.Info("Log index head rendering in progress", - "first block", f.indexedRange.firstIndexedBlock, "last block", f.indexedRange.afterLastIndexedBlock-1, - "processed", f.indexedRange.afterLastIndexedBlock-f.ptrHeadIndex, - "remaining", f.indexedView.headNumber+1-f.indexedRange.afterLastIndexedBlock, + "first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(), + "processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex, + "remaining", f.indexedView.headNumber-f.indexedRange.blocks.Last(), "elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt))) f.loggedHeadIndex = true f.lastLogHeadIndex = time.Now() @@ -218,10 +215,10 @@ func (f *FilterMaps) tryIndexHead() bool { log.Error("Log index head rendering failed", "error", err) return false } - if f.loggedHeadIndex { + if f.loggedHeadIndex && f.indexedRange.hasIndexedBlocks() { log.Info("Log index head rendering finished", - "first block", f.indexedRange.firstIndexedBlock, "last block", f.indexedRange.afterLastIndexedBlock-1, - "processed", f.indexedRange.afterLastIndexedBlock-f.ptrHeadIndex, + "first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(), + "processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex, "elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt))) } f.loggedHeadIndex, f.startedHeadIndex = false, false @@ -234,7 +231,11 @@ func (f *FilterMaps) tryIndexHead() bool { // rendered according to targetView and is suspended as soon as the targetView // is changed. func (f *FilterMaps) tryIndexTail() bool { - for firstEpoch := f.indexedRange.firstRenderedMap >> f.logMapsPerEpoch; firstEpoch > 0 && f.needTailEpoch(firstEpoch-1); { + for { + firstEpoch := f.indexedRange.maps.First() >> f.logMapsPerEpoch + if firstEpoch == 0 || !f.needTailEpoch(firstEpoch-1) { + break + } f.processEvents() if f.stop || !f.targetHeadIndexed() { return false @@ -242,25 +243,25 @@ func (f *FilterMaps) tryIndexTail() bool { // resume process if tail rendering was interrupted because of head rendering tailRenderer := f.tailRenderer f.tailRenderer = nil - if tailRenderer != nil && tailRenderer.afterLastMap != f.indexedRange.firstRenderedMap { + if tailRenderer != nil && tailRenderer.renderBefore != f.indexedRange.maps.First() { tailRenderer = nil } if tailRenderer == nil { var err error - tailRenderer, err = f.renderMapsBefore(f.indexedRange.firstRenderedMap) + tailRenderer, err = f.renderMapsBefore(f.indexedRange.maps.First()) if err != nil { log.Error("Error creating log index tail renderer", "error", err) return false } } if tailRenderer == nil { - return true + break } if !f.startedTailIndex { f.lastLogTailIndex = time.Now() f.startedTailIndexAt = f.lastLogTailIndex f.startedTailIndex = true - f.ptrTailIndex = f.indexedRange.firstIndexedBlock - f.tailPartialBlocks() + f.ptrTailIndex = f.indexedRange.blocks.First() - f.tailPartialBlocks() } done, err := tailRenderer.run(func() bool { f.processEvents() @@ -268,14 +269,14 @@ func (f *FilterMaps) tryIndexTail() bool { }, func() { tpb, ttb := f.tailPartialBlocks(), f.tailTargetBlock() remaining := uint64(1) - if f.indexedRange.firstIndexedBlock > ttb+tpb { - remaining = f.indexedRange.firstIndexedBlock - ttb - tpb + if f.indexedRange.blocks.First() > ttb+tpb { + remaining = f.indexedRange.blocks.First() - ttb - tpb } - if f.indexedRange.hasIndexedBlocks() && f.ptrTailIndex >= f.indexedRange.firstIndexedBlock && + if f.indexedRange.hasIndexedBlocks() && f.ptrTailIndex >= f.indexedRange.blocks.First() && (!f.loggedTailIndex || time.Since(f.lastLogTailIndex) > logFrequency) { log.Info("Log index tail rendering in progress", - "first block", f.indexedRange.firstIndexedBlock, "last block", f.indexedRange.afterLastIndexedBlock-1, - "processed", f.ptrTailIndex-f.indexedRange.firstIndexedBlock+tpb, + "first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(), + "processed", f.ptrTailIndex-f.indexedRange.blocks.First()+tpb, "remaining", remaining, "next tail epoch percentage", f.indexedRange.tailPartialEpoch*100/f.mapsPerEpoch, "elapsed", common.PrettyDuration(time.Since(f.startedTailIndexAt))) @@ -283,7 +284,8 @@ func (f *FilterMaps) tryIndexTail() bool { f.lastLogTailIndex = time.Now() } }) - if err != nil { + if err != nil && f.needTailEpoch(firstEpoch-1) { + // stop silently if cutoff point has move beyond epoch boundary while rendering log.Error("Log index tail rendering failed", "error", err) } if !done { @@ -291,10 +293,10 @@ func (f *FilterMaps) tryIndexTail() bool { return false } } - if f.loggedTailIndex { + if f.loggedTailIndex && f.indexedRange.hasIndexedBlocks() { log.Info("Log index tail rendering finished", - "first block", f.indexedRange.firstIndexedBlock, "last block", f.indexedRange.afterLastIndexedBlock-1, - "processed", f.ptrTailIndex-f.indexedRange.firstIndexedBlock, + "first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(), + "processed", f.ptrTailIndex-f.indexedRange.blocks.First(), "elapsed", common.PrettyDuration(time.Since(f.startedTailIndexAt))) f.loggedTailIndex = false } @@ -307,7 +309,7 @@ func (f *FilterMaps) tryIndexTail() bool { // data from the database and is also called while running head indexing. func (f *FilterMaps) tryUnindexTail() bool { for { - firstEpoch := (f.indexedRange.firstRenderedMap - f.indexedRange.tailPartialEpoch) >> f.logMapsPerEpoch + firstEpoch := (f.indexedRange.maps.First() - f.indexedRange.tailPartialEpoch) >> f.logMapsPerEpoch if f.needTailEpoch(firstEpoch) { break } @@ -318,19 +320,19 @@ func (f *FilterMaps) tryUnindexTail() bool { if !f.startedTailUnindex { f.startedTailUnindexAt = time.Now() f.startedTailUnindex = true - f.ptrTailUnindexMap = f.indexedRange.firstRenderedMap - f.indexedRange.tailPartialEpoch - f.ptrTailUnindexBlock = f.indexedRange.firstIndexedBlock - f.tailPartialBlocks() + f.ptrTailUnindexMap = f.indexedRange.maps.First() - f.indexedRange.tailPartialEpoch + f.ptrTailUnindexBlock = f.indexedRange.blocks.First() - f.tailPartialBlocks() } if err := f.deleteTailEpoch(firstEpoch); err != nil { log.Error("Log index tail epoch unindexing failed", "error", err) return false } } - if f.startedTailUnindex { + if f.startedTailUnindex && f.indexedRange.hasIndexedBlocks() { log.Info("Log index tail unindexing finished", - "first block", f.indexedRange.firstIndexedBlock, "last block", f.indexedRange.afterLastIndexedBlock-1, - "removed maps", f.indexedRange.firstRenderedMap-f.ptrTailUnindexMap, - "removed blocks", f.indexedRange.firstIndexedBlock-f.tailPartialBlocks()-f.ptrTailUnindexBlock, + "first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(), + "removed maps", f.indexedRange.maps.First()-f.ptrTailUnindexMap, + "removed blocks", f.indexedRange.blocks.First()-f.tailPartialBlocks()-f.ptrTailUnindexBlock, "elapsed", common.PrettyDuration(time.Since(f.startedTailUnindexAt))) f.startedTailUnindex = false } @@ -340,23 +342,29 @@ func (f *FilterMaps) tryUnindexTail() bool { // needTailEpoch returns true if the given tail epoch needs to be kept // according to the current tail target, false if it can be removed. func (f *FilterMaps) needTailEpoch(epoch uint32) bool { - firstEpoch := f.indexedRange.firstRenderedMap >> f.logMapsPerEpoch + firstEpoch := f.indexedRange.maps.First() >> f.logMapsPerEpoch if epoch > firstEpoch { return true } if epoch+1 < firstEpoch { return false } - tailTarget := f.tailTargetBlock() - if tailTarget < f.indexedRange.firstIndexedBlock { - return true + if epoch > 0 { + lastBlockOfPrevEpoch, _, err := f.getLastBlockOfMap(epoch<= firstEpoch + } + if f.historyCutoff > lastBlockOfPrevEpoch { + return false + } } - tailLvIndex, err := f.getBlockLvPointer(tailTarget) + lastBlockOfEpoch, _, err := f.getLastBlockOfMap((epoch+1)<= firstEpoch } - return uint64(epoch+1)<<(f.logValuesPerMap+f.logMapsPerEpoch) >= tailLvIndex + return f.tailTargetBlock() <= lastBlockOfEpoch } // tailTargetBlock returns the target value for the tail block number according @@ -374,15 +382,15 @@ func (f *FilterMaps) tailPartialBlocks() uint64 { if f.indexedRange.tailPartialEpoch == 0 { return 0 } - end, _, err := f.getLastBlockOfMap(f.indexedRange.firstRenderedMap - f.mapsPerEpoch + f.indexedRange.tailPartialEpoch - 1) + end, _, err := f.getLastBlockOfMap(f.indexedRange.maps.First() - f.mapsPerEpoch + f.indexedRange.tailPartialEpoch - 1) if err != nil { - log.Error("Error fetching last block of map", "mapIndex", f.indexedRange.firstRenderedMap-f.mapsPerEpoch+f.indexedRange.tailPartialEpoch-1, "error", err) + log.Error("Error fetching last block of map", "mapIndex", f.indexedRange.maps.First()-f.mapsPerEpoch+f.indexedRange.tailPartialEpoch-1, "error", err) } var start uint64 - if f.indexedRange.firstRenderedMap-f.mapsPerEpoch > 0 { - start, _, err = f.getLastBlockOfMap(f.indexedRange.firstRenderedMap - f.mapsPerEpoch - 1) + if f.indexedRange.maps.First()-f.mapsPerEpoch > 0 { + start, _, err = f.getLastBlockOfMap(f.indexedRange.maps.First() - f.mapsPerEpoch - 1) if err != nil { - log.Error("Error fetching last block of map", "mapIndex", f.indexedRange.firstRenderedMap-f.mapsPerEpoch-1, "error", err) + log.Error("Error fetching last block of map", "mapIndex", f.indexedRange.maps.First()-f.mapsPerEpoch-1, "error", err) } } return end - start @@ -391,5 +399,5 @@ func (f *FilterMaps) tailPartialBlocks() uint64 { // targetHeadIndexed returns true if the current log index is consistent with // targetView with its head block fully rendered. func (f *FilterMaps) targetHeadIndexed() bool { - return equalViews(f.targetView, f.indexedView) && f.indexedRange.headBlockIndexed + return equalViews(f.targetView, f.indexedView) && f.indexedRange.headIndexed } diff --git a/core/filtermaps/indexer_test.go b/core/filtermaps/indexer_test.go index 17ad765e04..a02f8d2459 100644 --- a/core/filtermaps/indexer_test.go +++ b/core/filtermaps/indexer_test.go @@ -48,16 +48,35 @@ func TestIndexerRandomRange(t *testing.T) { defer ts.close() forks := make([][]common.Hash, 10) - ts.chain.addBlocks(1000, 5, 2, 4, false) // 51 log values per block + ts.chain.addBlocks(1000, 5, 2, 4, false) for i := range forks { if i != 0 { forkBlock := rand.Intn(1000) ts.chain.setHead(forkBlock) - ts.chain.addBlocks(1000-forkBlock, 5, 2, 4, false) // 51 log values per block + ts.chain.addBlocks(1000-forkBlock, 5, 2, 4, false) } forks[i] = ts.chain.getCanonicalChain() } - lvPerBlock := uint64(51) + expspos := func(block uint64) uint64 { // expected position of block start + if block == 0 { + return 0 + } + logCount := (block - 1) * 5 * 2 + mapIndex := logCount / 3 + spos := mapIndex*16 + (logCount%3)*5 + if mapIndex == 0 || logCount%3 != 0 { + spos++ + } + return spos + } + expdpos := func(block uint64) uint64 { // expected position of delimiter + if block == 0 { + return 0 + } + logCount := block * 5 * 2 + mapIndex := (logCount - 1) / 3 + return mapIndex*16 + (logCount-mapIndex*3)*5 + } ts.setHistory(0, false) var ( history int @@ -115,23 +134,26 @@ func TestIndexerRandomRange(t *testing.T) { } var tailEpoch uint32 if tailBlock > 0 { - tailLvPtr := (tailBlock - 1) * lvPerBlock // no logs in genesis block, only delimiter + tailLvPtr := expspos(tailBlock) - 1 tailEpoch = uint32(tailLvPtr >> (testParams.logValuesPerMap + testParams.logMapsPerEpoch)) } + tailLvPtr := uint64(tailEpoch) << (testParams.logValuesPerMap + testParams.logMapsPerEpoch) // first available lv ptr var expTailBlock uint64 if tailEpoch > 0 { - tailLvPtr := uint64(tailEpoch) << (testParams.logValuesPerMap + testParams.logMapsPerEpoch) // first available lv ptr - // (expTailBlock-1)*lvPerBlock >= tailLvPtr - expTailBlock = (tailLvPtr + lvPerBlock*2 - 1) / lvPerBlock + for expspos(expTailBlock) <= tailLvPtr { + expTailBlock++ + } } - if ts.fm.indexedRange.afterLastIndexedBlock != uint64(head+1) { - ts.t.Fatalf("Invalid index head (expected #%d, got #%d)", head, ts.fm.indexedRange.afterLastIndexedBlock-1) + if ts.fm.indexedRange.blocks.Last() != uint64(head) { + ts.t.Fatalf("Invalid index head (expected #%d, got #%d)", head, ts.fm.indexedRange.blocks.Last()) } - if ts.fm.indexedRange.headBlockDelimiter != uint64(head)*lvPerBlock { - ts.t.Fatalf("Invalid index head delimiter pointer (expected %d, got %d)", uint64(head)*lvPerBlock, ts.fm.indexedRange.headBlockDelimiter) + expHeadDelimiter := expdpos(uint64(head)) + if ts.fm.indexedRange.headDelimiter != expHeadDelimiter { + ts.t.Fatalf("Invalid index head delimiter pointer (expected %d, got %d)", expHeadDelimiter, ts.fm.indexedRange.headDelimiter) } - if ts.fm.indexedRange.firstIndexedBlock != expTailBlock { - ts.t.Fatalf("Invalid index tail block (expected #%d, got #%d)", expTailBlock, ts.fm.indexedRange.firstIndexedBlock) + + if ts.fm.indexedRange.blocks.First() != expTailBlock { + ts.t.Fatalf("Invalid index tail block (expected #%d, got #%d)", expTailBlock, ts.fm.indexedRange.blocks.First()) } } } @@ -235,7 +257,7 @@ func (ts *testSetup) setHistory(history uint64, noHistory bool) { History: history, Disabled: noHistory, } - ts.fm = NewFilterMaps(ts.db, view, ts.params, config) + ts.fm = NewFilterMaps(ts.db, view, 0, 0, ts.params, config) ts.fm.testDisableSnapshots = ts.testDisableSnapshots ts.fm.Start() } @@ -420,7 +442,7 @@ func (tc *testChain) setTargetHead() { if tc.ts.fm != nil { if !tc.ts.fm.disabled { //tc.ts.fm.targetViewCh <- NewChainView(tc, head.Number.Uint64(), head.Hash()) - tc.ts.fm.SetTargetView(NewChainView(tc, head.Number.Uint64(), head.Hash())) + tc.ts.fm.SetTarget(NewChainView(tc, head.Number.Uint64(), head.Hash()), 0, 0) } } } diff --git a/core/filtermaps/map_renderer.go b/core/filtermaps/map_renderer.go index 5bd48a4b23..4f45a7f2b2 100644 --- a/core/filtermaps/map_renderer.go +++ b/core/filtermaps/map_renderer.go @@ -46,12 +46,12 @@ var ( // mapRenderer represents a process that renders filter maps in a specified // range according to the actual targetView. type mapRenderer struct { - f *FilterMaps - afterLastMap uint32 - currentMap *renderedMap - finishedMaps map[uint32]*renderedMap - firstFinished, afterLastFinished uint32 - iterator *logIterator + f *FilterMaps + renderBefore uint32 + currentMap *renderedMap + finishedMaps map[uint32]*renderedMap + finished common.Range[uint32] + iterator *logIterator } // renderedMap represents a single filter map that is being rendered in memory. @@ -74,22 +74,22 @@ func (r *renderedMap) firstBlock() uint64 { // specified map index boundary, starting from the latest available starting // point that is consistent with the current targetView. // The renderer ensures that filterMapsRange, indexedView and the actual map -// data are always consistent with each other. If afterLastMap is greater than +// data are always consistent with each other. If renderBefore is greater than // the latest existing rendered map then indexedView is updated to targetView, // otherwise it is checked that the rendered range is consistent with both // views. -func (f *FilterMaps) renderMapsBefore(afterLastMap uint32) (*mapRenderer, error) { - nextMap, startBlock, startLvPtr, err := f.lastCanonicalMapBoundaryBefore(afterLastMap) +func (f *FilterMaps) renderMapsBefore(renderBefore uint32) (*mapRenderer, error) { + nextMap, startBlock, startLvPtr, err := f.lastCanonicalMapBoundaryBefore(renderBefore) if err != nil { return nil, err } - if snapshot := f.lastCanonicalSnapshotBefore(afterLastMap); snapshot != nil && snapshot.mapIndex >= nextMap { + if snapshot := f.lastCanonicalSnapshotBefore(renderBefore); snapshot != nil && snapshot.mapIndex >= nextMap { return f.renderMapsFromSnapshot(snapshot) } - if nextMap >= afterLastMap { + if nextMap >= renderBefore { return nil, nil } - return f.renderMapsFromMapBoundary(nextMap, afterLastMap, startBlock, startLvPtr) + return f.renderMapsFromMapBoundary(nextMap, renderBefore, startBlock, startLvPtr) } // renderMapsFromSnapshot creates a mapRenderer that starts rendering from a @@ -108,17 +108,16 @@ func (f *FilterMaps) renderMapsFromSnapshot(cp *renderedMap) (*mapRenderer, erro lastBlock: cp.lastBlock, blockLvPtrs: cp.blockLvPtrs, }, - finishedMaps: make(map[uint32]*renderedMap), - firstFinished: cp.mapIndex, - afterLastFinished: cp.mapIndex, - afterLastMap: math.MaxUint32, - iterator: iter, + finishedMaps: make(map[uint32]*renderedMap), + finished: common.NewRange(cp.mapIndex, 0), + renderBefore: math.MaxUint32, + iterator: iter, }, nil } // renderMapsFromMapBoundary creates a mapRenderer that starts rendering at a // map boundary. -func (f *FilterMaps) renderMapsFromMapBoundary(firstMap, afterLastMap uint32, startBlock, startLvPtr uint64) (*mapRenderer, error) { +func (f *FilterMaps) renderMapsFromMapBoundary(firstMap, renderBefore uint32, startBlock, startLvPtr uint64) (*mapRenderer, error) { iter, err := f.newLogIteratorFromMapBoundary(firstMap, startBlock, startLvPtr) if err != nil { return nil, fmt.Errorf("failed to create log iterator from map boundary %d: %v", firstMap, err) @@ -130,22 +129,21 @@ func (f *FilterMaps) renderMapsFromMapBoundary(firstMap, afterLastMap uint32, st mapIndex: firstMap, lastBlock: iter.blockNumber, }, - finishedMaps: make(map[uint32]*renderedMap), - firstFinished: firstMap, - afterLastFinished: firstMap, - afterLastMap: afterLastMap, - iterator: iter, + finishedMaps: make(map[uint32]*renderedMap), + finished: common.NewRange(firstMap, 0), + renderBefore: renderBefore, + iterator: iter, }, nil } // lastCanonicalSnapshotBefore returns the latest cached snapshot that matches // the current targetView. -func (f *FilterMaps) lastCanonicalSnapshotBefore(afterLastMap uint32) *renderedMap { +func (f *FilterMaps) lastCanonicalSnapshotBefore(renderBefore uint32) *renderedMap { var best *renderedMap for _, blockNumber := range f.renderSnapshots.Keys() { - if cp, _ := f.renderSnapshots.Get(blockNumber); cp != nil && blockNumber < f.indexedRange.afterLastIndexedBlock && + if cp, _ := f.renderSnapshots.Get(blockNumber); cp != nil && blockNumber < f.indexedRange.blocks.AfterLast() && blockNumber <= f.targetView.headNumber && f.targetView.getBlockId(blockNumber) == cp.lastBlockId && - cp.mapIndex < afterLastMap && (best == nil || blockNumber > best.lastBlock) { + cp.mapIndex < renderBefore && (best == nil || blockNumber > best.lastBlock) { best = cp } } @@ -158,11 +156,11 @@ func (f *FilterMaps) lastCanonicalSnapshotBefore(afterLastMap uint32) *renderedM // or the boundary of a currently rendered map. // Along with the next map index where the rendering can be started, the number // and starting log value pointer of the last block is also returned. -func (f *FilterMaps) lastCanonicalMapBoundaryBefore(afterLastMap uint32) (nextMap uint32, startBlock, startLvPtr uint64, err error) { +func (f *FilterMaps) lastCanonicalMapBoundaryBefore(renderBefore uint32) (nextMap uint32, startBlock, startLvPtr uint64, err error) { if !f.indexedRange.initialized { return 0, 0, 0, nil } - mapIndex := afterLastMap + mapIndex := renderBefore for { var ok bool if mapIndex, ok = f.lastMapBoundaryBefore(mapIndex); !ok { @@ -188,18 +186,18 @@ func (f *FilterMaps) lastCanonicalMapBoundaryBefore(afterLastMap uint32) (nextMa // lastMapBoundaryBefore returns the latest map boundary before the specified // map index. func (f *FilterMaps) lastMapBoundaryBefore(mapIndex uint32) (uint32, bool) { - if !f.indexedRange.initialized || f.indexedRange.afterLastRenderedMap == 0 { + if !f.indexedRange.initialized || f.indexedRange.maps.AfterLast() == 0 { return 0, false } - if mapIndex > f.indexedRange.afterLastRenderedMap { - mapIndex = f.indexedRange.afterLastRenderedMap + if mapIndex > f.indexedRange.maps.AfterLast() { + mapIndex = f.indexedRange.maps.AfterLast() } - if mapIndex > f.indexedRange.firstRenderedMap { + if mapIndex > f.indexedRange.maps.First() { return mapIndex - 1, true } - if mapIndex+f.mapsPerEpoch > f.indexedRange.firstRenderedMap { - if mapIndex > f.indexedRange.firstRenderedMap-f.mapsPerEpoch+f.indexedRange.tailPartialEpoch { - mapIndex = f.indexedRange.firstRenderedMap - f.mapsPerEpoch + f.indexedRange.tailPartialEpoch + if mapIndex+f.mapsPerEpoch > f.indexedRange.maps.First() { + if mapIndex > f.indexedRange.maps.First()-f.mapsPerEpoch+f.indexedRange.tailPartialEpoch { + mapIndex = f.indexedRange.maps.First() - f.mapsPerEpoch + f.indexedRange.tailPartialEpoch } } else { mapIndex = (mapIndex >> f.logMapsPerEpoch) << f.logMapsPerEpoch @@ -218,19 +216,19 @@ func (f *FilterMaps) emptyFilterMap() filterMap { // loadHeadSnapshot loads the last rendered map from the database and creates // a snapshot. func (f *FilterMaps) loadHeadSnapshot() error { - fm, err := f.getFilterMap(f.indexedRange.afterLastRenderedMap - 1) + fm, err := f.getFilterMap(f.indexedRange.maps.Last()) if err != nil { - return fmt.Errorf("failed to load head snapshot map %d: %v", f.indexedRange.afterLastRenderedMap-1, err) + return fmt.Errorf("failed to load head snapshot map %d: %v", f.indexedRange.maps.Last(), err) } - lastBlock, _, err := f.getLastBlockOfMap(f.indexedRange.afterLastRenderedMap - 1) + lastBlock, _, err := f.getLastBlockOfMap(f.indexedRange.maps.Last()) if err != nil { - return fmt.Errorf("failed to retrieve last block of head snapshot map %d: %v", f.indexedRange.afterLastRenderedMap-1, err) + return fmt.Errorf("failed to retrieve last block of head snapshot map %d: %v", f.indexedRange.maps.Last(), err) } var firstBlock uint64 - if f.indexedRange.afterLastRenderedMap > 1 { - prevLastBlock, _, err := f.getLastBlockOfMap(f.indexedRange.afterLastRenderedMap - 2) + if f.indexedRange.maps.AfterLast() > 1 { + prevLastBlock, _, err := f.getLastBlockOfMap(f.indexedRange.maps.Last() - 1) if err != nil { - return fmt.Errorf("failed to retrieve last block of map %d before head snapshot: %v", f.indexedRange.afterLastRenderedMap-2, err) + return fmt.Errorf("failed to retrieve last block of map %d before head snapshot: %v", f.indexedRange.maps.Last()-1, err) } firstBlock = prevLastBlock + 1 } @@ -241,14 +239,14 @@ func (f *FilterMaps) loadHeadSnapshot() error { return fmt.Errorf("failed to retrieve log value pointer of head snapshot block %d: %v", firstBlock+uint64(i), err) } } - f.renderSnapshots.Add(f.indexedRange.afterLastIndexedBlock-1, &renderedMap{ + f.renderSnapshots.Add(f.indexedRange.blocks.Last(), &renderedMap{ filterMap: fm, - mapIndex: f.indexedRange.afterLastRenderedMap - 1, - lastBlock: f.indexedRange.afterLastIndexedBlock - 1, - lastBlockId: f.indexedView.getBlockId(f.indexedRange.afterLastIndexedBlock - 1), + mapIndex: f.indexedRange.maps.Last(), + lastBlock: f.indexedRange.blocks.Last(), + lastBlockId: f.indexedView.getBlockId(f.indexedRange.blocks.Last()), blockLvPtrs: lvPtrs, finished: true, - headDelimiter: f.indexedRange.headBlockDelimiter, + headDelimiter: f.indexedRange.headDelimiter, }) return nil } @@ -277,14 +275,14 @@ func (r *mapRenderer) run(stopCb func() bool, writeCb func()) (bool, error) { } // map finished r.finishedMaps[r.currentMap.mapIndex] = r.currentMap - r.afterLastFinished++ - if len(r.finishedMaps) >= maxMapsPerBatch || r.afterLastFinished&(r.f.baseRowGroupLength-1) == 0 { + r.finished.SetLast(r.finished.AfterLast()) + if len(r.finishedMaps) >= maxMapsPerBatch || r.finished.AfterLast()&(r.f.baseRowGroupLength-1) == 0 { if err := r.writeFinishedMaps(stopCb); err != nil { return false, err } writeCb() } - if r.afterLastFinished == r.afterLastMap || r.iterator.finished { + if r.finished.AfterLast() == r.renderBefore || r.iterator.finished { if err := r.writeFinishedMaps(stopCb); err != nil { return false, err } @@ -293,7 +291,7 @@ func (r *mapRenderer) run(stopCb func() bool, writeCb func()) (bool, error) { } r.currentMap = &renderedMap{ filterMap: r.f.emptyFilterMap(), - mapIndex: r.afterLastFinished, + mapIndex: r.finished.AfterLast(), } } } @@ -323,11 +321,6 @@ func (r *mapRenderer) renderCurrentMap(stopCb func() bool) (bool, error) { } waitCnt = 0 } - r.currentMap.lastBlock = r.iterator.blockNumber - if r.iterator.delimiter { - r.currentMap.lastBlock++ - r.currentMap.blockLvPtrs = append(r.currentMap.blockLvPtrs, r.iterator.lvIndex+1) - } if logValue := r.iterator.getValueHash(); logValue != (common.Hash{}) { lvp, cached := rowMappingCache.Get(logValue) if !cached { @@ -346,9 +339,15 @@ func (r *mapRenderer) renderCurrentMap(stopCb func() bool) (bool, error) { if err := r.iterator.next(); err != nil { return false, fmt.Errorf("failed to advance log iterator at %d while rendering map %d: %v", r.iterator.lvIndex, r.currentMap.mapIndex, err) } - if !r.f.testDisableSnapshots && r.afterLastMap >= r.f.indexedRange.afterLastRenderedMap && - (r.iterator.delimiter || r.iterator.finished) { - r.makeSnapshot() + if !r.iterator.skipToBoundary { + r.currentMap.lastBlock = r.iterator.blockNumber + if r.iterator.blockStart { + r.currentMap.blockLvPtrs = append(r.currentMap.blockLvPtrs, r.iterator.lvIndex) + } + if !r.f.testDisableSnapshots && r.renderBefore >= r.f.indexedRange.maps.AfterLast() && + (r.iterator.delimiter || r.iterator.finished) { + r.makeSnapshot() + } } } if r.iterator.finished { @@ -402,7 +401,7 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error { mapIndices []uint32 rows []FilterRow ) - for mapIndex := r.firstFinished; mapIndex < r.afterLastFinished; mapIndex++ { + for mapIndex := range r.finished.Iter() { row := r.finishedMaps[mapIndex].filterMap[rowIndex] if fm, _ := r.f.filterMapCache.Get(mapIndex); fm != nil && row.Equal(fm[rowIndex]) { continue @@ -410,8 +409,8 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error { mapIndices = append(mapIndices, mapIndex) rows = append(rows, row) } - if newRange.afterLastRenderedMap == r.afterLastFinished { // head updated; remove future entries - for mapIndex := r.afterLastFinished; mapIndex < oldRange.afterLastRenderedMap; mapIndex++ { + if newRange.maps.AfterLast() == r.finished.AfterLast() { // head updated; remove future entries + for mapIndex := r.finished.AfterLast(); mapIndex < oldRange.maps.AfterLast(); mapIndex++ { if fm, _ := r.f.filterMapCache.Get(mapIndex); fm != nil && len(fm[rowIndex]) == 0 { continue } @@ -425,24 +424,24 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error { checkWriteCnt() } // update filter map cache - if newRange.afterLastRenderedMap == r.afterLastFinished { + if newRange.maps.AfterLast() == r.finished.AfterLast() { // head updated; cache new head maps and remove future entries - for mapIndex := r.firstFinished; mapIndex < r.afterLastFinished; mapIndex++ { + for mapIndex := range r.finished.Iter() { r.f.filterMapCache.Add(mapIndex, r.finishedMaps[mapIndex].filterMap) } - for mapIndex := r.afterLastFinished; mapIndex < oldRange.afterLastRenderedMap; mapIndex++ { + for mapIndex := r.finished.AfterLast(); mapIndex < oldRange.maps.AfterLast(); mapIndex++ { r.f.filterMapCache.Remove(mapIndex) } } else { // head not updated; do not cache maps during tail rendering because we // need head maps to be available in the cache - for mapIndex := r.firstFinished; mapIndex < r.afterLastFinished; mapIndex++ { + for mapIndex := range r.finished.Iter() { r.f.filterMapCache.Remove(mapIndex) } } // add or update block pointers - blockNumber := r.finishedMaps[r.firstFinished].firstBlock() - for mapIndex := r.firstFinished; mapIndex < r.afterLastFinished; mapIndex++ { + blockNumber := r.finishedMaps[r.finished.First()].firstBlock() + for mapIndex := range r.finished.Iter() { renderedMap := r.finishedMaps[mapIndex] r.f.storeLastBlockOfMap(batch, mapIndex, renderedMap.lastBlock, renderedMap.lastBlockId) checkWriteCnt() @@ -455,18 +454,18 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error { blockNumber++ } } - if newRange.afterLastRenderedMap == r.afterLastFinished { // head updated; remove future entries - for mapIndex := r.afterLastFinished; mapIndex < oldRange.afterLastRenderedMap; mapIndex++ { + if newRange.maps.AfterLast() == r.finished.AfterLast() { // head updated; remove future entries + for mapIndex := r.finished.AfterLast(); mapIndex < oldRange.maps.AfterLast(); mapIndex++ { r.f.deleteLastBlockOfMap(batch, mapIndex) checkWriteCnt() } - for ; blockNumber < oldRange.afterLastIndexedBlock; blockNumber++ { + for ; blockNumber < oldRange.blocks.AfterLast(); blockNumber++ { r.f.deleteBlockLvPointer(batch, blockNumber) checkWriteCnt() } } r.finishedMaps = make(map[uint32]*renderedMap) - r.firstFinished = r.afterLastFinished + r.finished.SetFirst(r.finished.AfterLast()) r.f.setRange(batch, renderedView, newRange) if err := batch.Write(); err != nil { log.Crit("Error writing log index update batch", "error", err) @@ -481,33 +480,33 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error { // range to the unchanged region until all new map data is committed. func (r *mapRenderer) getTempRange() (filterMapsRange, error) { tempRange := r.f.indexedRange - if err := tempRange.addRenderedRange(r.firstFinished, r.firstFinished, r.afterLastMap, r.f.mapsPerEpoch); err != nil { + if err := tempRange.addRenderedRange(r.finished.First(), r.finished.First(), r.renderBefore, r.f.mapsPerEpoch); err != nil { return filterMapsRange{}, fmt.Errorf("failed to update temporary rendered range: %v", err) } - if tempRange.firstRenderedMap != r.f.indexedRange.firstRenderedMap { + if tempRange.maps.First() != r.f.indexedRange.maps.First() { // first rendered map changed; update first indexed block - if tempRange.firstRenderedMap > 0 { - lastBlock, _, err := r.f.getLastBlockOfMap(tempRange.firstRenderedMap - 1) + if tempRange.maps.First() > 0 { + firstBlock, _, err := r.f.getLastBlockOfMap(tempRange.maps.First() - 1) if err != nil { - return filterMapsRange{}, fmt.Errorf("failed to retrieve last block of map %d before temporary range: %v", tempRange.firstRenderedMap-1, err) + return filterMapsRange{}, fmt.Errorf("failed to retrieve last block of map %d before temporary range: %v", tempRange.maps.First()-1, err) } - tempRange.firstIndexedBlock = lastBlock + 1 + tempRange.blocks.SetFirst(firstBlock + 1) // firstBlock is probably partially rendered } else { - tempRange.firstIndexedBlock = 0 + tempRange.blocks.SetFirst(0) } } - if tempRange.afterLastRenderedMap != r.f.indexedRange.afterLastRenderedMap { - // first rendered map changed; update first indexed block - if tempRange.afterLastRenderedMap > 0 { - lastBlock, _, err := r.f.getLastBlockOfMap(tempRange.afterLastRenderedMap - 1) + if tempRange.maps.AfterLast() != r.f.indexedRange.maps.AfterLast() { + // last rendered map changed; update last indexed block + if !tempRange.maps.IsEmpty() { + lastBlock, _, err := r.f.getLastBlockOfMap(tempRange.maps.Last()) if err != nil { - return filterMapsRange{}, fmt.Errorf("failed to retrieve last block of map %d at the end of temporary range: %v", tempRange.afterLastRenderedMap-1, err) + return filterMapsRange{}, fmt.Errorf("failed to retrieve last block of map %d at the end of temporary range: %v", tempRange.maps.Last(), err) } - tempRange.afterLastIndexedBlock = lastBlock + tempRange.blocks.SetAfterLast(lastBlock) // lastBlock is probably partially rendered } else { - tempRange.afterLastIndexedBlock = 0 + tempRange.blocks.SetAfterLast(0) } - tempRange.headBlockDelimiter = 0 + tempRange.headDelimiter = 0 } return tempRange, nil } @@ -517,39 +516,39 @@ func (r *mapRenderer) getTempRange() (filterMapsRange, error) { func (r *mapRenderer) getUpdatedRange() (filterMapsRange, error) { // update filterMapsRange newRange := r.f.indexedRange - if err := newRange.addRenderedRange(r.firstFinished, r.afterLastFinished, r.afterLastMap, r.f.mapsPerEpoch); err != nil { + if err := newRange.addRenderedRange(r.finished.First(), r.finished.AfterLast(), r.renderBefore, r.f.mapsPerEpoch); err != nil { return filterMapsRange{}, fmt.Errorf("failed to update rendered range: %v", err) } - if newRange.firstRenderedMap != r.f.indexedRange.firstRenderedMap { + if newRange.maps.First() != r.f.indexedRange.maps.First() { // first rendered map changed; update first indexed block - if newRange.firstRenderedMap > 0 { - lastBlock, _, err := r.f.getLastBlockOfMap(newRange.firstRenderedMap - 1) + if newRange.maps.First() > 0 { + firstBlock, _, err := r.f.getLastBlockOfMap(newRange.maps.First() - 1) if err != nil { - return filterMapsRange{}, fmt.Errorf("failed to retrieve last block of map %d before rendered range: %v", newRange.firstRenderedMap-1, err) + return filterMapsRange{}, fmt.Errorf("failed to retrieve last block of map %d before rendered range: %v", newRange.maps.First()-1, err) } - newRange.firstIndexedBlock = lastBlock + 1 + newRange.blocks.SetFirst(firstBlock + 1) // firstBlock is probably partially rendered } else { - newRange.firstIndexedBlock = 0 + newRange.blocks.SetFirst(0) } } - if newRange.afterLastRenderedMap == r.afterLastFinished { + if newRange.maps.AfterLast() == r.finished.AfterLast() { // last rendered map changed; update last indexed block and head pointers - lm := r.finishedMaps[r.afterLastFinished-1] - newRange.headBlockIndexed = lm.finished + lm := r.finishedMaps[r.finished.Last()] + newRange.headIndexed = lm.finished if lm.finished { - newRange.afterLastIndexedBlock = r.f.targetView.headNumber + 1 + newRange.blocks.SetLast(r.f.targetView.headNumber) if lm.lastBlock != r.f.targetView.headNumber { panic("map rendering finished but last block != head block") } - newRange.headBlockDelimiter = lm.headDelimiter + newRange.headDelimiter = lm.headDelimiter } else { - newRange.afterLastIndexedBlock = lm.lastBlock - newRange.headBlockDelimiter = 0 + newRange.blocks.SetAfterLast(lm.lastBlock) // lastBlock is probably partially rendered + newRange.headDelimiter = 0 } } else { // last rendered map not replaced; ensure that target chain view matches // indexed chain view on the rendered section - if lastBlock := r.finishedMaps[r.afterLastFinished-1].lastBlock; !matchViews(r.f.indexedView, r.f.targetView, lastBlock) { + if lastBlock := r.finishedMaps[r.finished.Last()].lastBlock; !matchViews(r.f.indexedView, r.f.targetView, lastBlock) { return filterMapsRange{}, errChainUpdate } } @@ -571,9 +570,9 @@ func (fmr *filterMapsRange) addRenderedRange(firstRendered, afterLastRendered, a m uint32 d int } - endpoints := []endpoint{{fmr.firstRenderedMap, 1}, {fmr.afterLastRenderedMap, -1}, {firstRendered, 1}, {afterLastRendered, -101}, {afterLastRemoved, 100}} + endpoints := []endpoint{{fmr.maps.First(), 1}, {fmr.maps.AfterLast(), -1}, {firstRendered, 1}, {afterLastRendered, -101}, {afterLastRemoved, 100}} if fmr.tailPartialEpoch > 0 { - endpoints = append(endpoints, []endpoint{{fmr.firstRenderedMap - mapsPerEpoch, 1}, {fmr.firstRenderedMap - mapsPerEpoch + fmr.tailPartialEpoch, -1}}...) + endpoints = append(endpoints, []endpoint{{fmr.maps.First() - mapsPerEpoch, 1}, {fmr.maps.First() - mapsPerEpoch + fmr.tailPartialEpoch, -1}}...) } sort.Slice(endpoints, func(i, j int) bool { return endpoints[i].m < endpoints[j].m }) var ( @@ -596,14 +595,12 @@ func (fmr *filterMapsRange) addRenderedRange(firstRendered, afterLastRendered, a case 0: // Initialized database, but no finished maps yet. fmr.tailPartialEpoch = 0 - fmr.firstRenderedMap = firstRendered - fmr.afterLastRenderedMap = firstRendered + fmr.maps = common.NewRange(firstRendered, 0) case 2: // One rendered section (no partial tail epoch). fmr.tailPartialEpoch = 0 - fmr.firstRenderedMap = merged[0] - fmr.afterLastRenderedMap = merged[1] + fmr.maps = common.NewRange(merged[0], merged[1]-merged[0]) case 4: // Two rendered sections (with a gap). @@ -613,8 +610,7 @@ func (fmr *filterMapsRange) addRenderedRange(firstRendered, afterLastRendered, a return fmt.Errorf("invalid tail partial epoch: %v", merged) } fmr.tailPartialEpoch = merged[1] - merged[0] - fmr.firstRenderedMap = merged[2] - fmr.afterLastRenderedMap = merged[3] + fmr.maps = common.NewRange(merged[2], merged[3]-merged[2]) default: return fmt.Errorf("invalid number of rendered sections: %v", merged) @@ -624,12 +620,13 @@ func (fmr *filterMapsRange) addRenderedRange(firstRendered, afterLastRendered, a // logIterator iterates on the linear log value index range. type logIterator struct { - chainView *ChainView - blockNumber uint64 - receipts types.Receipts - blockStart, delimiter, finished bool - txIndex, logIndex, topicIndex int - lvIndex uint64 + params *Params + chainView *ChainView + blockNumber uint64 + receipts types.Receipts + blockStart, delimiter, skipToBoundary, finished bool + txIndex, logIndex, topicIndex int + lvIndex uint64 } var errUnindexedRange = errors.New("unindexed range") @@ -641,12 +638,12 @@ func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber uint64) (*logI if blockNumber > f.targetView.headNumber { return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", blockNumber, f.targetView.headNumber) } - if blockNumber < f.indexedRange.firstIndexedBlock || blockNumber >= f.indexedRange.afterLastIndexedBlock { + if !f.indexedRange.blocks.Includes(blockNumber) { return nil, errUnindexedRange } var lvIndex uint64 - if f.indexedRange.headBlockIndexed && blockNumber+1 == f.indexedRange.afterLastIndexedBlock { - lvIndex = f.indexedRange.headBlockDelimiter + if f.indexedRange.headIndexed && blockNumber+1 == f.indexedRange.blocks.AfterLast() { + lvIndex = f.indexedRange.headDelimiter } else { var err error lvIndex, err = f.getBlockLvPointer(blockNumber + 1) @@ -656,13 +653,16 @@ func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber uint64) (*logI lvIndex-- } finished := blockNumber == f.targetView.headNumber - return &logIterator{ + l := &logIterator{ chainView: f.targetView, + params: &f.Params, blockNumber: blockNumber, finished: finished, delimiter: !finished, lvIndex: lvIndex, - }, nil + } + l.enforceValidState() + return l, nil } // newLogIteratorFromMapBoundary creates a logIterator starting at the given @@ -679,12 +679,13 @@ func (f *FilterMaps) newLogIteratorFromMapBoundary(mapIndex uint32, startBlock, // initialize iterator at block start l := &logIterator{ chainView: f.targetView, + params: &f.Params, blockNumber: startBlock, receipts: receipts, blockStart: true, lvIndex: startLvPtr, } - l.nextValid() + l.enforceValidState() targetIndex := uint64(mapIndex) << f.logValuesPerMap if l.lvIndex > targetIndex { return nil, fmt.Errorf("log value pointer %d of last block of map is after map boundary %d", l.lvIndex, targetIndex) @@ -713,7 +714,7 @@ func (l *logIterator) updateChainView(cv *ChainView) bool { // getValueHash returns the log value hash at the current position. func (l *logIterator) getValueHash() common.Hash { - if l.delimiter || l.finished { + if l.delimiter || l.finished || l.skipToBoundary { return common.Hash{} } log := l.receipts[l.txIndex].Logs[l.logIndex] @@ -725,6 +726,13 @@ func (l *logIterator) getValueHash() common.Hash { // next moves the iterator to the next log value index. func (l *logIterator) next() error { + if l.skipToBoundary { + l.lvIndex++ + if l.lvIndex%l.params.valuesPerMap == 0 { + l.skipToBoundary = false + } + return nil + } if l.finished { return nil } @@ -741,18 +749,26 @@ func (l *logIterator) next() error { l.blockStart = false } l.lvIndex++ - l.nextValid() + l.enforceValidState() return nil } -// nextValid updates the internal transaction, log and topic index pointers +// enforceValidState updates the internal transaction, log and topic index pointers // to the next existing log value of the given block if necessary. -// Note that nextValid does not advance the log value index pointer. -func (l *logIterator) nextValid() { +// Note that enforceValidState does not advance the log value index pointer. +func (l *logIterator) enforceValidState() { + if l.delimiter || l.finished || l.skipToBoundary { + return + } for ; l.txIndex < len(l.receipts); l.txIndex++ { receipt := l.receipts[l.txIndex] for ; l.logIndex < len(receipt.Logs); l.logIndex++ { log := receipt.Logs[l.logIndex] + if l.topicIndex == 0 && uint64(len(log.Topics)+1) > l.params.valuesPerMap-l.lvIndex%l.params.valuesPerMap { + // next log would be split by map boundary; skip to boundary + l.skipToBoundary = true + return + } if l.topicIndex <= len(log.Topics) { return } diff --git a/core/filtermaps/matcher.go b/core/filtermaps/matcher.go index 19acbd762a..6c05672cbc 100644 --- a/core/filtermaps/matcher.go +++ b/core/filtermaps/matcher.go @@ -61,11 +61,9 @@ type SyncRange struct { // block range where the index has not changed since the last matcher sync // and therefore the set of matches found in this region is guaranteed to // be valid and complete. - Valid bool - FirstValid, LastValid uint64 + ValidBlocks common.Range[uint64] // block range indexed according to the given chain head. - Indexed bool - FirstIndexed, LastIndexed uint64 + IndexedBlocks common.Range[uint64] } // GetPotentialMatches returns a list of logs that are potential matches for the @@ -75,7 +73,6 @@ type SyncRange struct { // Also note that the returned list may contain false positives. func GetPotentialMatches(ctx context.Context, backend MatcherBackend, firstBlock, lastBlock uint64, addresses []common.Address, topics [][]common.Hash) ([]*types.Log, error) { params := backend.GetParams() - var getLogStats runtimeStats // find the log value index range to search firstIndex, err := backend.GetBlockLvPointer(ctx, firstBlock) if err != nil { @@ -88,8 +85,6 @@ func GetPotentialMatches(ctx context.Context, backend MatcherBackend, firstBlock if lastIndex > 0 { lastIndex-- } - firstMap, lastMap := uint32(firstIndex>>params.logValuesPerMap), uint32(lastIndex>>params.logValuesPerMap) - firstEpoch, lastEpoch := firstMap>>params.logMapsPerEpoch, lastMap>>params.logMapsPerEpoch // build matcher according to the given filter criteria matchers := make([]matcher, len(topics)+1) @@ -117,45 +112,45 @@ func GetPotentialMatches(ctx context.Context, backend MatcherBackend, firstBlock // matchers signal a match for consecutive log value indices. matcher := newMatchSequence(params, matchers) - // processEpoch returns the potentially matching logs from the given epoch. - processEpoch := func(epochIndex uint32) ([]*types.Log, error) { - var logs []*types.Log - // create a list of map indices to process - fm, lm := epochIndex< lastMap { - lm = lastMap - } - // - mapIndices := make([]uint32, lm+1-fm) - for i := range mapIndices { - mapIndices[i] = fm + uint32(i) - } - // find potential matches - matches, err := getAllMatches(ctx, matcher, mapIndices) - if err != nil { - return logs, err - } - // get the actual logs located at the matching log value indices - var st int - getLogStats.setState(&st, stGetLog) - defer getLogStats.setState(&st, stNone) - for _, m := range matches { - if m == nil { - return nil, ErrMatchAll - } - mlogs, err := getLogsFromMatches(ctx, backend, firstIndex, lastIndex, m) - if err != nil { - return logs, err - } - logs = append(logs, mlogs...) - } - getLogStats.addAmount(st, int64(len(logs))) - return logs, nil + m := &matcherEnv{ + ctx: ctx, + backend: backend, + params: params, + matcher: matcher, + firstIndex: firstIndex, + lastIndex: lastIndex, + firstMap: uint32(firstIndex >> params.logValuesPerMap), + lastMap: uint32(lastIndex >> params.logValuesPerMap), } + start := time.Now() + res, err := m.process() + + if doRuntimeStats { + log.Info("Log search finished", "elapsed", time.Since(start)) + for i, ma := range matchers { + for j, m := range ma.(matchAny) { + log.Info("Single matcher stats", "matchSequence", i, "matchAny", j) + m.(*singleMatcher).stats.print() + } + } + log.Info("Get log stats") + m.getLogStats.print() + } + return res, err +} + +type matcherEnv struct { + getLogStats runtimeStats // 64 bit aligned + ctx context.Context + backend MatcherBackend + params *Params + matcher matcher + firstIndex, lastIndex uint64 + firstMap, lastMap uint32 +} + +func (m *matcherEnv) process() ([]*types.Log, error) { type task struct { epochIndex uint32 logs []*types.Log @@ -175,18 +170,18 @@ func GetPotentialMatches(ctx context.Context, backend MatcherBackend, firstBlock if task == nil { break } - task.logs, task.err = processEpoch(task.epochIndex) + task.logs, task.err = m.processEpoch(task.epochIndex) close(task.done) } wg.Done() } - start := time.Now() - for i := 0; i < 4; i++ { + for range 4 { wg.Add(1) go worker() } + firstEpoch, lastEpoch := m.firstMap>>m.params.logMapsPerEpoch, m.lastMap>>m.params.logMapsPerEpoch var logs []*types.Log // startEpoch is the next task to send whenever a worker can accept it. // waitEpoch is the next task we are waiting for to finish in order to append @@ -220,30 +215,58 @@ func GetPotentialMatches(ctx context.Context, backend MatcherBackend, firstBlock } } } - if doRuntimeStats { - log.Info("Log search finished", "elapsed", time.Since(start)) - for i, ma := range matchers { - for j, m := range ma.(matchAny) { - log.Info("Single matcher stats", "matchSequence", i, "matchAny", j) - m.(*singleMatcher).stats.print() - } - } - log.Info("Get log stats") - getLogStats.print() + return logs, nil +} + +// processEpoch returns the potentially matching logs from the given epoch. +func (m *matcherEnv) processEpoch(epochIndex uint32) ([]*types.Log, error) { + var logs []*types.Log + // create a list of map indices to process + fm, lm := epochIndex< m.lastMap { + lm = m.lastMap + } + // + mapIndices := make([]uint32, lm+1-fm) + for i := range mapIndices { + mapIndices[i] = fm + uint32(i) + } + // find potential matches + matches, err := m.getAllMatches(mapIndices) + if err != nil { + return logs, err + } + // get the actual logs located at the matching log value indices + var st int + m.getLogStats.setState(&st, stGetLog) + defer m.getLogStats.setState(&st, stNone) + for _, match := range matches { + if match == nil { + return nil, ErrMatchAll + } + mlogs, err := m.getLogsFromMatches(match) + if err != nil { + return logs, err + } + logs = append(logs, mlogs...) + } + m.getLogStats.addAmount(st, int64(len(logs))) return logs, nil } // getLogsFromMatches returns the list of potentially matching logs located at // the given list of matching log indices. Matches outside the firstIndex to // lastIndex range are not returned. -func getLogsFromMatches(ctx context.Context, backend MatcherBackend, firstIndex, lastIndex uint64, matches potentialMatches) ([]*types.Log, error) { +func (m *matcherEnv) getLogsFromMatches(matches potentialMatches) ([]*types.Log, error) { var logs []*types.Log for _, match := range matches { - if match < firstIndex || match > lastIndex { + if match < m.firstIndex || match > m.lastIndex { continue } - log, err := backend.GetLogByLvIndex(ctx, match) + log, err := m.backend.GetLogByLvIndex(m.ctx, match) if err != nil { return logs, fmt.Errorf("failed to retrieve log at index %d: %v", match, err) } @@ -254,6 +277,28 @@ func getLogsFromMatches(ctx context.Context, backend MatcherBackend, firstIndex, return logs, nil } +// getAllMatches creates an instance for a given matcher and set of map indices, +// iterates through mapping layers and collects all results, then returns all +// results in the same order as the map indices were specified. +func (m *matcherEnv) getAllMatches(mapIndices []uint32) ([]potentialMatches, error) { + instance := m.matcher.newInstance(mapIndices) + resultsMap := make(map[uint32]potentialMatches) + for layerIndex := uint32(0); len(resultsMap) < len(mapIndices); layerIndex++ { + results, err := instance.getMatchesForLayer(m.ctx, layerIndex) + if err != nil { + return nil, err + } + for _, result := range results { + resultsMap[result.mapIndex] = result.matches + } + } + matches := make([]potentialMatches, len(mapIndices)) + for i, mapIndex := range mapIndices { + matches[i] = resultsMap[mapIndex] + } + return matches, nil +} + // matcher defines a general abstraction for any matcher configuration that // can instantiate a matcherInstance. type matcher interface { @@ -281,28 +326,6 @@ type matcherResult struct { matches potentialMatches } -// getAllMatches creates an instance for a given matcher and set of map indices, -// iterates through mapping layers and collects all results, then returns all -// results in the same order as the map indices were specified. -func getAllMatches(ctx context.Context, matcher matcher, mapIndices []uint32) ([]potentialMatches, error) { - instance := matcher.newInstance(mapIndices) - resultsMap := make(map[uint32]potentialMatches) - for layerIndex := uint32(0); len(resultsMap) < len(mapIndices); layerIndex++ { - results, err := instance.getMatchesForLayer(ctx, layerIndex) - if err != nil { - return nil, err - } - for _, result := range results { - resultsMap[result.mapIndex] = result.matches - } - } - matches := make([]potentialMatches, len(mapIndices)) - for i, mapIndex := range mapIndices { - matches[i] = resultsMap[mapIndex] - } - return matches, nil -} - // singleMatcher implements matcher by returning matches for a single log value hash. type singleMatcher struct { backend MatcherBackend @@ -550,24 +573,18 @@ type matchSequence struct { // newInstance creates a new instance of matchSequence. func (m *matchSequence) newInstance(mapIndices []uint32) matcherInstance { // determine set of indices to request from next matcher - nextIndices := make([]uint32, 0, len(mapIndices)*3/2) needMatched := make(map[uint32]struct{}) baseRequested := make(map[uint32]struct{}) nextRequested := make(map[uint32]struct{}) for _, mapIndex := range mapIndices { needMatched[mapIndex] = struct{}{} baseRequested[mapIndex] = struct{}{} - if _, ok := nextRequested[mapIndex]; !ok { - nextIndices = append(nextIndices, mapIndex) - nextRequested[mapIndex] = struct{}{} - } - nextIndices = append(nextIndices, mapIndex+1) - nextRequested[mapIndex+1] = struct{}{} + nextRequested[mapIndex] = struct{}{} } return &matchSequenceInstance{ matchSequence: m, baseInstance: m.base.newInstance(mapIndices), - nextInstance: m.next.newInstance(nextIndices), + nextInstance: m.next.newInstance(mapIndices), needMatched: needMatched, baseRequested: baseRequested, nextRequested: nextRequested, @@ -687,12 +704,9 @@ func (m *matchSequenceInstance) getMatchesForLayer(ctx context.Context, layerInd if _, ok := m.nextRequested[mapIndex]; ok { continue } - if _, ok := m.nextRequested[mapIndex+1]; ok { - continue - } matchedResults = append(matchedResults, matcherResult{ mapIndex: mapIndex, - matches: m.params.matchResults(mapIndex, m.offset, m.baseResults[mapIndex], m.nextResults[mapIndex], m.nextResults[mapIndex+1]), + matches: m.params.matchResults(mapIndex, m.offset, m.baseResults[mapIndex], m.nextResults[mapIndex]), }) delete(m.needMatched, mapIndex) } @@ -715,9 +729,6 @@ func (m *matchSequenceInstance) dropIndices(dropIndices []uint32) { if m.dropNext(mapIndex) { dropNext = append(dropNext, mapIndex) } - if m.dropNext(mapIndex + 1) { - dropNext = append(dropNext, mapIndex+1) - } } m.nextInstance.dropIndices(dropNext) } @@ -743,9 +754,6 @@ func (m *matchSequenceInstance) evalBase(ctx context.Context, layerIndex uint32) if m.dropNext(r.mapIndex) { dropIndices = append(dropIndices, r.mapIndex) } - if m.dropNext(r.mapIndex + 1) { - dropIndices = append(dropIndices, r.mapIndex+1) - } } if len(dropIndices) > 0 { m.nextInstance.dropIndices(dropIndices) @@ -771,9 +779,6 @@ func (m *matchSequenceInstance) evalNext(ctx context.Context, layerIndex uint32) } m.mergeNextStats(stats) for _, r := range results { - if r.mapIndex > 0 && m.dropBase(r.mapIndex-1) { - dropIndices = append(dropIndices, r.mapIndex-1) - } if m.dropBase(r.mapIndex) { dropIndices = append(dropIndices, r.mapIndex) } @@ -792,12 +797,7 @@ func (m *matchSequenceInstance) dropBase(mapIndex uint32) bool { return false } if _, ok := m.needMatched[mapIndex]; ok { - if next := m.nextResults[mapIndex]; next == nil || - (len(next) > 0 && next[len(next)-1] >= (uint64(mapIndex)< 0 && nextNext[0] < (uint64(mapIndex+1)< 0 { return false } } @@ -812,15 +812,8 @@ func (m *matchSequenceInstance) dropNext(mapIndex uint32) bool { if _, ok := m.nextRequested[mapIndex]; !ok { return false } - if _, ok := m.needMatched[mapIndex-1]; ok { - if prevBase := m.baseResults[mapIndex-1]; prevBase == nil || - (len(prevBase) > 0 && prevBase[len(prevBase)-1]+m.offset >= (uint64(mapIndex)< 0 && base[0]+m.offset < (uint64(mapIndex+1)< 0 { return false } } @@ -833,59 +826,39 @@ func (m *matchSequenceInstance) dropNext(mapIndex uint32) bool { // results at mapIndex and mapIndex+1. Note that acquiring nextNextRes may be // skipped and it can be substituted with an empty list if baseRes has no potential // matches that could be sequence matched with anything that could be in nextNextRes. -func (params *Params) matchResults(mapIndex uint32, offset uint64, baseRes, nextRes, nextNextRes potentialMatches) potentialMatches { +func (params *Params) matchResults(mapIndex uint32, offset uint64, baseRes, nextRes potentialMatches) potentialMatches { if nextRes == nil || (baseRes != nil && len(baseRes) == 0) { // if nextRes is a wild card or baseRes is empty then the sequence matcher // result equals baseRes. return baseRes } - if len(nextRes) > 0 { - // discard items from nextRes whose corresponding base matcher results - // with the negative offset applied would be located at mapIndex-1. - start := 0 - for start < len(nextRes) && nextRes[start] < uint64(mapIndex)<= min { + result = append(result, v-offset) + } } - nextRes = nextRes[start:] + return result } - if len(nextNextRes) > 0 { - // discard items from nextNextRes whose corresponding base matcher results - // with the negative offset applied would still be located at mapIndex+1. - stop := 0 - for stop < len(nextNextRes) && nextNextRes[stop] < uint64(mapIndex+1)< 0 && len(baseRes) > 0 { - if nextRes[0] > baseRes[0]+offset { - baseRes = baseRes[1:] - } else if nextRes[0] < baseRes[0]+offset { - nextRes = nextRes[1:] - } else { - matchedRes = append(matchedRes, baseRes[0]) - baseRes = baseRes[1:] - nextRes = nextRes[1:] - } - } + for len(nextRes) > 0 && len(baseRes) > 0 { + if nextRes[0] > baseRes[0]+offset { + baseRes = baseRes[1:] + } else if nextRes[0] < baseRes[0]+offset { + nextRes = nextRes[1:] } else { - // baseRes is a wild card so just return next matcher results with - // negative offset. - for len(nextRes) > 0 { - matchedRes = append(matchedRes, nextRes[0]-offset) - nextRes = nextRes[1:] - } + matchedRes = append(matchedRes, baseRes[0]) + baseRes = baseRes[1:] + nextRes = nextRes[1:] } } return matchedRes diff --git a/core/filtermaps/matcher_backend.go b/core/filtermaps/matcher_backend.go index 66f95f2145..4c4668e321 100644 --- a/core/filtermaps/matcher_backend.go +++ b/core/filtermaps/matcher_backend.go @@ -19,6 +19,7 @@ package filtermaps import ( "context" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" ) @@ -27,9 +28,8 @@ type FilterMapsMatcherBackend struct { f *FilterMaps // these fields should be accessed under f.matchersLock mutex. - valid bool - firstValid, lastValid uint64 - syncCh chan SyncRange + validBlocks common.Range[uint64] + syncCh chan SyncRange } // NewMatcherBackend returns a FilterMapsMatcherBackend after registering it in @@ -43,11 +43,9 @@ func (f *FilterMaps) NewMatcherBackend() *FilterMapsMatcherBackend { f.indexLock.RUnlock() }() - fm := &FilterMapsMatcherBackend{ - f: f, - valid: f.indexedRange.initialized && f.indexedRange.afterLastIndexedBlock > f.indexedRange.firstIndexedBlock, - firstValid: f.indexedRange.firstIndexedBlock, - lastValid: f.indexedRange.afterLastIndexedBlock - 1, + fm := &FilterMapsMatcherBackend{f: f} + if f.indexedRange.initialized { + fm.validBlocks = f.indexedRange.blocks } f.matchers[fm] = struct{}{} return fm @@ -122,28 +120,16 @@ func (fm *FilterMapsMatcherBackend) synced() { fm.f.indexLock.RUnlock() }() - var ( - indexed bool - lastIndexed, subLastIndexed uint64 - ) - if !fm.f.indexedRange.headBlockIndexed { - subLastIndexed = 1 - } - if fm.f.indexedRange.afterLastIndexedBlock-subLastIndexed > fm.f.indexedRange.firstIndexedBlock { - indexed, lastIndexed = true, fm.f.indexedRange.afterLastIndexedBlock-subLastIndexed-1 + indexedBlocks := fm.f.indexedRange.blocks + if !fm.f.indexedRange.headIndexed && !indexedBlocks.IsEmpty() { + indexedBlocks.SetAfterLast(indexedBlocks.Last()) // remove partially indexed last block } fm.syncCh <- SyncRange{ - HeadNumber: fm.f.indexedView.headNumber, - Valid: fm.valid, - FirstValid: fm.firstValid, - LastValid: fm.lastValid, - Indexed: indexed, - FirstIndexed: fm.f.indexedRange.firstIndexedBlock, - LastIndexed: lastIndexed, + HeadNumber: fm.f.indexedView.headNumber, + ValidBlocks: fm.validBlocks, + IndexedBlocks: indexedBlocks, } - fm.valid = indexed - fm.firstValid = fm.f.indexedRange.firstIndexedBlock - fm.lastValid = lastIndexed + fm.validBlocks = indexedBlocks fm.syncCh = nil } @@ -187,20 +173,10 @@ func (f *FilterMaps) updateMatchersValidRange() { defer f.matchersLock.Unlock() for fm := range f.matchers { - if !f.indexedRange.hasIndexedBlocks() { - fm.valid = false - } - if !fm.valid { + if !f.indexedRange.initialized { + fm.validBlocks = common.Range[uint64]{} continue } - if fm.firstValid < f.indexedRange.firstIndexedBlock { - fm.firstValid = f.indexedRange.firstIndexedBlock - } - if fm.lastValid >= f.indexedRange.afterLastIndexedBlock { - fm.lastValid = f.indexedRange.afterLastIndexedBlock - 1 - } - if fm.firstValid > fm.lastValid { - fm.valid = false - } + fm.validBlocks = fm.validBlocks.Intersection(f.indexedRange.blocks) } } diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go index f679c3aeb3..9f3d55aa3f 100644 --- a/core/rawdb/accessors_indexes.go +++ b/core/rawdb/accessors_indexes.go @@ -356,8 +356,8 @@ func WriteFilterMapBaseRows(db ethdb.KeyValueWriter, mapRowIndex uint64, rows [] } } -func DeleteFilterMapRows(db ethdb.KeyValueRangeDeleter, firstMapRowIndex, afterLastMapRowIndex uint64) { - if err := db.DeleteRange(filterMapRowKey(firstMapRowIndex, false), filterMapRowKey(afterLastMapRowIndex, false)); err != nil { +func DeleteFilterMapRows(db ethdb.KeyValueRangeDeleter, mapRows common.Range[uint64]) { + if err := db.DeleteRange(filterMapRowKey(mapRows.First(), false), filterMapRowKey(mapRows.AfterLast(), false)); err != nil { log.Crit("Failed to delete range of filter map rows", "err", err) } } @@ -396,8 +396,8 @@ func DeleteFilterMapLastBlock(db ethdb.KeyValueWriter, mapIndex uint32) { } } -func DeleteFilterMapLastBlocks(db ethdb.KeyValueRangeDeleter, firstMapIndex, afterLastMapIndex uint32) { - if err := db.DeleteRange(filterMapLastBlockKey(firstMapIndex), filterMapLastBlockKey(afterLastMapIndex)); err != nil { +func DeleteFilterMapLastBlocks(db ethdb.KeyValueRangeDeleter, maps common.Range[uint32]) { + if err := db.DeleteRange(filterMapLastBlockKey(maps.First()), filterMapLastBlockKey(maps.AfterLast())); err != nil { log.Crit("Failed to delete range of filter map last block pointers", "err", err) } } @@ -433,8 +433,8 @@ func DeleteBlockLvPointer(db ethdb.KeyValueWriter, blockNumber uint64) { } } -func DeleteBlockLvPointers(db ethdb.KeyValueRangeDeleter, firstBlockNumber, afterLastBlockNumber uint64) { - if err := db.DeleteRange(filterMapBlockLVKey(firstBlockNumber), filterMapBlockLVKey(afterLastBlockNumber)); err != nil { +func DeleteBlockLvPointers(db ethdb.KeyValueRangeDeleter, blocks common.Range[uint64]) { + if err := db.DeleteRange(filterMapBlockLVKey(blocks.First()), filterMapBlockLVKey(blocks.AfterLast())); err != nil { log.Crit("Failed to delete range of block log value pointers", "err", err) } } @@ -442,10 +442,11 @@ func DeleteBlockLvPointers(db ethdb.KeyValueRangeDeleter, firstBlockNumber, afte // FilterMapsRange is a storage representation of the block range covered by the // filter maps structure and the corresponting log value index range. type FilterMapsRange struct { - HeadBlockIndexed bool - HeadBlockDelimiter uint64 - FirstIndexedBlock, AfterLastIndexedBlock uint64 - FirstRenderedMap, AfterLastRenderedMap, TailPartialEpoch uint32 + HeadIndexed bool + HeadDelimiter uint64 + BlocksFirst, BlocksAfterLast uint64 + MapsFirst, MapsAfterLast uint32 + TailPartialEpoch uint32 } // ReadFilterMapsRange retrieves the filter maps range data. Note that if the diff --git a/eth/api_backend.go b/eth/api_backend.go index 75b039dbe1..b4b63556a9 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" + "github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/txpool" @@ -413,6 +414,10 @@ func (b *EthAPIBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma } } +func (b *EthAPIBackend) NewMatcherBackend() filtermaps.MatcherBackend { + return b.eth.filterMaps.NewMatcherBackend() +} + func (b *EthAPIBackend) Engine() consensus.Engine { return b.eth.engine } diff --git a/eth/backend.go b/eth/backend.go index 1790e413fd..99cb3841c3 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" + "github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/pruner" "github.com/ethereum/go-ethereum/core/txpool" @@ -88,6 +89,9 @@ type Ethereum struct { bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports closeBloomHandler chan struct{} + filterMaps *filtermaps.FilterMaps + closeFilterMaps chan chan struct{} + APIBackend *EthAPIBackend miner *miner.Miner @@ -244,6 +248,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { return nil, err } eth.bloomIndexer.Start(eth.blockchain) + fmConfig := filtermaps.Config{History: config.LogHistory, Disabled: config.LogNoHistory, ExportFileName: config.LogExportCheckpoints} + chainView := eth.newChainView(eth.blockchain.CurrentBlock()) + eth.filterMaps = filtermaps.NewFilterMaps(chainDb, chainView, 0, 0, filtermaps.DefaultParams, fmConfig) + eth.closeFilterMaps = make(chan chan struct{}) if config.BlobPool.Datadir != "" { config.BlobPool.Datadir = stack.ResolvePath(config.BlobPool.Datadir) @@ -398,9 +406,71 @@ func (s *Ethereum) Start() error { // Start the networking layer s.handler.Start(s.p2pServer.MaxPeers) + + // start log indexer + s.filterMaps.Start() + go s.updateFilterMapsHeads() return nil } +func (s *Ethereum) newChainView(head *types.Header) *filtermaps.ChainView { + if head == nil { + return nil + } + return filtermaps.NewChainView(s.blockchain, head.Number.Uint64(), head.Hash()) +} + +func (s *Ethereum) updateFilterMapsHeads() { + headEventCh := make(chan core.ChainEvent, 10) + blockProcCh := make(chan bool, 10) + sub := s.blockchain.SubscribeChainEvent(headEventCh) + sub2 := s.blockchain.SubscribeBlockProcessingEvent(blockProcCh) + defer func() { + sub.Unsubscribe() + sub2.Unsubscribe() + for { + select { + case <-headEventCh: + case <-blockProcCh: + default: + return + } + } + }() + + var head *types.Header + setHead := func(newHead *types.Header) { + if newHead == nil { + return + } + if head == nil || newHead.Hash() != head.Hash() { + head = newHead + chainView := s.newChainView(head) + historyCutoff := s.blockchain.HistoryPruningCutoff() + var finalBlock uint64 + if fb := s.blockchain.CurrentFinalBlock(); fb != nil { + finalBlock = fb.Number.Uint64() + } + s.filterMaps.SetTarget(chainView, historyCutoff, finalBlock) + } + } + setHead(s.blockchain.CurrentBlock()) + + for { + select { + case ev := <-headEventCh: + setHead(ev.Header) + case blockProc := <-blockProcCh: + s.filterMaps.SetBlockProcessing(blockProc) + case <-time.After(time.Second * 10): + setHead(s.blockchain.CurrentBlock()) + case ch := <-s.closeFilterMaps: + close(ch) + return + } + } +} + func (s *Ethereum) setupDiscovery() error { eth.StartENRUpdater(s.blockchain, s.p2pServer.LocalNode()) @@ -443,6 +513,10 @@ func (s *Ethereum) Stop() error { // Then stop everything else. s.bloomIndexer.Close() close(s.closeBloomHandler) + ch := make(chan struct{}) + s.closeFilterMaps <- ch + <-ch + s.filterMaps.Stop() s.txPool.Close() s.blockchain.Stop() s.engine.Close() diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index ec6de9e663..365857347c 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -53,6 +53,7 @@ var Defaults = Config{ NetworkId: 0, // enable auto configuration of networkID == chainID TxLookupLimit: 2350000, TransactionHistory: 2350000, + LogHistory: 2350000, StateHistory: params.FullImmutabilityThreshold, DatabaseCache: 512, TrieCleanCache: 154, @@ -97,8 +98,11 @@ type Config struct { // Deprecated: use 'TransactionHistory' instead. TxLookupLimit uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved. - TransactionHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved. - StateHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose state histories are reserved. + TransactionHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved. + LogHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head where a log search index is maintained. + LogNoHistory bool `toml:",omitempty"` // No log search index is maintained. + LogExportCheckpoints string // export log index checkpoints to file + StateHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose state histories are reserved. // State scheme represents the scheme used to store ethereum states and trie // nodes on top. It can be 'hash', 'path', or none which means use the scheme diff --git a/eth/filters/filter.go b/eth/filters/filter.go index 09ccb93907..cedb5b3519 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -19,12 +19,15 @@ package filters import ( "context" "errors" + "math" "math/big" "slices" + "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/bloombits" + "github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" ) @@ -38,36 +41,14 @@ type Filter struct { block *common.Hash // Block hash if filtering a single block begin, end int64 // Range interval if filtering multiple blocks - matcher *bloombits.Matcher + rangeLogsTestHook chan rangeLogsTestEvent } // NewRangeFilter creates a new filter which uses a bloom filter on blocks to // figure out whether a particular block is interesting or not. func (sys *FilterSystem) NewRangeFilter(begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter { - // Flatten the address and topic filter clauses into a single bloombits filter - // system. Since the bloombits are not positional, nil topics are permitted, - // which get flattened into a nil byte slice. - var filters [][][]byte - if len(addresses) > 0 { - filter := make([][]byte, len(addresses)) - for i, address := range addresses { - filter[i] = address.Bytes() - } - filters = append(filters, filter) - } - for _, topicList := range topics { - filter := make([][]byte, len(topicList)) - for i, topic := range topicList { - filter[i] = topic.Bytes() - } - filters = append(filters, filter) - } - size, _ := sys.backend.BloomStatus() - // Create a generic filter and convert it into a range filter filter := newFilter(sys, addresses, topics) - - filter.matcher = bloombits.NewMatcher(size, filters) filter.begin = begin filter.end = end @@ -113,161 +94,304 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { return nil, errPendingLogsUnsupported } - resolveSpecial := func(number int64) (int64, error) { - var hdr *types.Header + resolveSpecial := func(number int64) (uint64, error) { switch number { - case rpc.LatestBlockNumber.Int64(), 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 - hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) - if hdr == nil { - return 0, errors.New("latest header not found") - } + case rpc.LatestBlockNumber.Int64(): + // when searching from and/or until the current head, we resolve it + // to MaxUint64 which is translated by rangeLogs to the actual head + // in each iteration, ensuring that the head block will be searched + // even if the chain is updated during search. + return math.MaxUint64, nil case rpc.FinalizedBlockNumber.Int64(): - hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.FinalizedBlockNumber) + hdr, _ := f.sys.backend.HeaderByNumber(ctx, rpc.FinalizedBlockNumber) if hdr == nil { return 0, errors.New("finalized header not found") } + return hdr.Number.Uint64(), nil case rpc.SafeBlockNumber.Int64(): - hdr, _ = f.sys.backend.HeaderByNumber(ctx, rpc.SafeBlockNumber) + 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.Uint64(), nil } - return hdr.Number.Int64(), nil + if number < 0 { + return 0, errors.New("negative block number") + } + return uint64(number), nil } - var err error // range query need to resolve the special begin/end block number - if f.begin, err = resolveSpecial(f.begin); err != nil { + begin, err := resolveSpecial(f.begin) + if err != nil { return nil, err } - if f.end, err = resolveSpecial(f.end); err != nil { + end, err := resolveSpecial(f.end) + if err != nil { return nil, err } + return f.rangeLogs(ctx, begin, end) +} - logChan, errChan := f.rangeLogsAsync(ctx) - var logs []*types.Log - for { - select { - case log := <-logChan: - logs = append(logs, log) - case err := <-errChan: - return logs, err - } +const ( + rangeLogsTestSync = iota + rangeLogsTestTrimmed + rangeLogsTestIndexed + rangeLogsTestUnindexed + rangeLogsTestDone +) + +type rangeLogsTestEvent struct { + event int + begin, end uint64 +} + +// searchSession represents a single search session. +type searchSession struct { + ctx context.Context + filter *Filter + mb filtermaps.MatcherBackend + syncRange filtermaps.SyncRange // latest synchronized state with the matcher + firstBlock, lastBlock uint64 // specified search range; each can be MaxUint64 + searchRange common.Range[uint64] // actual search range; end trimmed to latest head + matchRange common.Range[uint64] // range in which we have results (subset of searchRange) + matches []*types.Log // valid set of matches in matchRange + forceUnindexed bool // revert to unindexed search +} + +// newSearchSession returns a new searchSession. +func newSearchSession(ctx context.Context, filter *Filter, mb filtermaps.MatcherBackend, firstBlock, lastBlock uint64) (*searchSession, error) { + s := &searchSession{ + ctx: ctx, + filter: filter, + mb: mb, + firstBlock: firstBlock, + lastBlock: lastBlock, } + // enforce a consistent state before starting the search in order to be able + // to determine valid range later + if err := s.syncMatcher(0); err != nil { + return nil, err + } + return s, nil } -// rangeLogsAsync retrieves block-range logs that match the filter criteria asynchronously, -// it creates and returns two channels: one for delivering log data, and one for reporting errors. -func (f *Filter) rangeLogsAsync(ctx context.Context) (chan *types.Log, chan error) { - var ( - logChan = make(chan *types.Log) - errChan = make(chan error) - ) - - go func() { - defer func() { - close(errChan) - close(logChan) - }() - - // Gather all indexed logs, and finish with non indexed ones - var ( - end = uint64(f.end) - size, sections = f.sys.backend.BloomStatus() - err error - ) - if indexed := sections * size; indexed > uint64(f.begin) { - if indexed > end { - indexed = end + 1 - } - if err = f.indexedLogs(ctx, indexed-1, logChan); err != nil { - errChan <- err - return - } - } - - if err := f.unindexedLogs(ctx, end, logChan); err != nil { - errChan <- err - return - } - - errChan <- nil - }() - - return logChan, errChan -} - -// indexedLogs returns the logs matching the filter criteria based on the bloom -// bits indexed available locally or via the network. -func (f *Filter) indexedLogs(ctx context.Context, end uint64, logChan chan *types.Log) error { - // Create a matcher session and request servicing from the backend - matches := make(chan uint64, 64) - - session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches) +// syncMatcher performs a synchronization step with the matcher. The resulting +// syncRange structure holds information about the latest range of indexed blocks +// and the guaranteed valid blocks whose log index have not been changed since +// the previous synchronization. +// The function also performs trimming of the match set in order to always keep +// it consistent with the synced matcher state. +// Tail trimming is only performed if the first block of the valid log index range +// is higher than trimTailThreshold. This is useful because unindexed log search +// is not affected by the valid tail (on the other hand, valid head is taken into +// account in order to provide reorg safety, even though the log index is not used). +// In case of indexed search the tail is only trimmed if the first part of the +// recently obtained results might be invalid. If guaranteed valid new results +// have been added at the head of previously validated results then there is no +// need to discard those even if the index tail have been unindexed since that. +func (s *searchSession) syncMatcher(trimTailThreshold uint64) error { + if s.filter.rangeLogsTestHook != nil && !s.matchRange.IsEmpty() { + s.filter.rangeLogsTestHook <- rangeLogsTestEvent{event: rangeLogsTestSync, begin: s.matchRange.First(), end: s.matchRange.Last()} + } + var err error + s.syncRange, err = s.mb.SyncLogIndex(s.ctx) if err != nil { return err } - defer session.Close() + // update actual search range based on current head number + first := min(s.firstBlock, s.syncRange.HeadNumber) + last := min(s.lastBlock, s.syncRange.HeadNumber) + s.searchRange = common.NewRange(first, last+1-first) + // discard everything that is not needed or might be invalid + trimRange := s.syncRange.ValidBlocks + if trimRange.First() <= trimTailThreshold { + // everything before this point is already known to be valid; if this is + // valid then keep everything before + trimRange.SetFirst(0) + } + trimRange = trimRange.Intersection(s.searchRange) + s.trimMatches(trimRange) + if s.filter.rangeLogsTestHook != nil { + if !s.matchRange.IsEmpty() { + s.filter.rangeLogsTestHook <- rangeLogsTestEvent{event: rangeLogsTestTrimmed, begin: s.matchRange.First(), end: s.matchRange.Last()} + } else { + s.filter.rangeLogsTestHook <- rangeLogsTestEvent{event: rangeLogsTestTrimmed, begin: 0, end: 0} + } + } + return nil +} - f.sys.backend.ServiceFilter(ctx, session) +// trimMatches removes any entries from the current set of matches that is outside +// the given range. +func (s *searchSession) trimMatches(trimRange common.Range[uint64]) { + s.matchRange = s.matchRange.Intersection(trimRange) + if s.matchRange.IsEmpty() { + s.matches = nil + return + } + for len(s.matches) > 0 && s.matches[0].BlockNumber < s.matchRange.First() { + s.matches = s.matches[1:] + } + for len(s.matches) > 0 && s.matches[len(s.matches)-1].BlockNumber > s.matchRange.Last() { + s.matches = s.matches[:len(s.matches)-1] + } +} - for { - select { - case number, ok := <-matches: - // Abort if all matches have been fulfilled - if !ok { - err := session.Error() - if err == nil { - f.begin = int64(end) + 1 - } - return err - } - f.begin = int64(number) + 1 +// searchInRange performs a single range search, either indexed or unindexed. +func (s *searchSession) searchInRange(r common.Range[uint64], indexed bool) ([]*types.Log, error) { + first, last := r.First(), r.Last() + if indexed { + if s.filter.rangeLogsTestHook != nil { + s.filter.rangeLogsTestHook <- rangeLogsTestEvent{rangeLogsTestIndexed, first, last} + } + results, err := s.filter.indexedLogs(s.ctx, s.mb, first, last) + if err != filtermaps.ErrMatchAll { + return results, err + } + // "match all" filters are not supported by filtermaps; fall back to + // unindexed search which is the most efficient in this case + s.forceUnindexed = true + // fall through to unindexed case + } + if s.filter.rangeLogsTestHook != nil { + s.filter.rangeLogsTestHook <- rangeLogsTestEvent{rangeLogsTestUnindexed, first, last} + } + return s.filter.unindexedLogs(s.ctx, first, last) +} - // Retrieve the suggested block and pull any truly matching logs - header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(number)) - if header == nil || err != nil { - return err - } - found, err := f.checkMatches(ctx, header) +// doSearchIteration performs a search on a range missing from an incomplete set +// of results, adds the new section and removes invalidated entries. +func (s *searchSession) doSearchIteration() error { + switch { + case s.syncRange.IndexedBlocks.IsEmpty(): + // indexer is not ready; fallback to completely unindexed search, do not check valid range + var err error + s.matchRange = s.searchRange + s.matches, err = s.searchInRange(s.searchRange, false) + return err + + case s.matchRange.IsEmpty(): + // no results yet; try search in entire range + indexedSearchRange := s.searchRange.Intersection(s.syncRange.IndexedBlocks) + var err error + if s.forceUnindexed = indexedSearchRange.IsEmpty(); !s.forceUnindexed { + // indexed search on the intersection of indexed and searched range + s.matchRange = indexedSearchRange + s.matches, err = s.searchInRange(indexedSearchRange, true) if err != nil { return err } - for _, log := range found { - logChan <- log + return s.syncMatcher(0) // trim everything that the matcher considers potentially invalid + } else { + // no intersection of indexed and searched range; unindexed search on the whole searched range + s.matchRange = s.searchRange + s.matches, err = s.searchInRange(s.searchRange, false) + if err != nil { + return err } + return s.syncMatcher(math.MaxUint64) // unindexed search is not affected by the tail of valid range + } - case <-ctx.Done(): - return ctx.Err() + case !s.matchRange.IsEmpty() && s.matchRange.First() > s.searchRange.First(): + // we have results but tail section is missing; do unindexed search for + // the tail part but still allow indexed search for missing head section + tailRange := common.NewRange(s.searchRange.First(), s.matchRange.First()-s.searchRange.First()) + tailMatches, err := s.searchInRange(tailRange, false) + if err != nil { + return err + } + s.matches = append(tailMatches, s.matches...) + s.matchRange = tailRange.Union(s.matchRange) + return s.syncMatcher(math.MaxUint64) // unindexed search is not affected by the tail of valid range + + case !s.matchRange.IsEmpty() && s.matchRange.First() == s.searchRange.First() && s.searchRange.AfterLast() > s.matchRange.AfterLast(): + // we have results but head section is missing + headRange := common.NewRange(s.matchRange.AfterLast(), s.searchRange.AfterLast()-s.matchRange.AfterLast()) + if !s.forceUnindexed { + indexedHeadRange := headRange.Intersection(s.syncRange.IndexedBlocks) + if !indexedHeadRange.IsEmpty() && indexedHeadRange.First() == headRange.First() { + // indexed head range search is possible + headRange = indexedHeadRange + } else { + s.forceUnindexed = true + } + } + headMatches, err := s.searchInRange(headRange, !s.forceUnindexed) + if err != nil { + return err + } + s.matches = append(s.matches, headMatches...) + s.matchRange = s.matchRange.Union(headRange) + if s.forceUnindexed { + return s.syncMatcher(math.MaxUint64) // unindexed search is not affected by the tail of valid range + } else { + return s.syncMatcher(headRange.First()) // trim if the tail of latest head search results might be invalid + } + + default: + panic("invalid search session state") + } +} + +func (f *Filter) rangeLogs(ctx context.Context, firstBlock, lastBlock uint64) ([]*types.Log, error) { + if f.rangeLogsTestHook != nil { + defer func() { + f.rangeLogsTestHook <- rangeLogsTestEvent{rangeLogsTestDone, 0, 0} + close(f.rangeLogsTestHook) + }() + } + + if firstBlock > lastBlock { + return nil, nil + } + mb := f.sys.backend.NewMatcherBackend() + defer mb.Close() + + session, err := newSearchSession(ctx, f, mb, firstBlock, lastBlock) + if err != nil { + return nil, err + } + for session.searchRange != session.matchRange { + if err := session.doSearchIteration(); err != nil { + return session.matches, err } } + return session.matches, nil +} + +func (f *Filter) indexedLogs(ctx context.Context, mb filtermaps.MatcherBackend, begin, end uint64) ([]*types.Log, error) { + start := time.Now() + potentialMatches, err := filtermaps.GetPotentialMatches(ctx, mb, begin, end, f.addresses, f.topics) + matches := filterLogs(potentialMatches, nil, nil, f.addresses, f.topics) + log.Trace("Performed indexed log search", "begin", begin, "end", end, "true matches", len(matches), "false positives", len(potentialMatches)-len(matches), "elapsed", common.PrettyDuration(time.Since(start))) + return matches, err } // unindexedLogs returns the logs matching the filter criteria based on raw block // iteration and bloom matching. -func (f *Filter) unindexedLogs(ctx context.Context, end uint64, logChan chan *types.Log) error { - for ; f.begin <= int64(end); f.begin++ { - header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin)) +func (f *Filter) unindexedLogs(ctx context.Context, begin, end uint64) ([]*types.Log, error) { + start := time.Now() + log.Warn("Performing unindexed log search", "begin", begin, "end", end) + var matches []*types.Log + for blockNumber := begin; blockNumber <= end; blockNumber++ { + select { + case <-ctx.Done(): + return matches, ctx.Err() + default: + } + header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber)) if header == nil || err != nil { - return err + return matches, err } found, err := f.blockLogs(ctx, header) if err != nil { - return err - } - for _, log := range found { - select { - case logChan <- log: - case <-ctx.Done(): - return ctx.Err() - } + return matches, err } + matches = append(matches, found...) } - return nil + log.Trace("Performed unindexed log search", "begin", begin, "end", end, "matches", len(matches), "elapsed", common.PrettyDuration(time.Since(start))) + return matches, nil } // blockLogs returns the logs matching the filter criteria within a single block. diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index 86012b3f9a..7531a1ecfc 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -29,7 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/bloombits" + "github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" @@ -69,8 +69,7 @@ type Backend interface { SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription - BloomStatus() (uint64, uint64) - ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) + NewMatcherBackend() filtermaps.MatcherBackend } // FilterSystem holds resources shared by all filters. diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index aec5ee4166..c35d823f5a 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -20,7 +20,6 @@ import ( "context" "errors" "math/big" - "math/rand" "reflect" "runtime" "testing" @@ -29,7 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/bloombits" + "github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" @@ -41,7 +40,7 @@ import ( type testBackend struct { db ethdb.Database - sections uint64 + fm *filtermaps.FilterMaps txFeed event.Feed logsFeed event.Feed rmLogsFeed event.Feed @@ -59,10 +58,28 @@ func (b *testBackend) CurrentHeader() *types.Header { return hdr } +func (b *testBackend) CurrentBlock() *types.Header { + return b.CurrentHeader() +} + func (b *testBackend) ChainDb() ethdb.Database { return b.db } +func (b *testBackend) GetCanonicalHash(number uint64) common.Hash { + return rawdb.ReadCanonicalHash(b.db, number) +} + +func (b *testBackend) GetHeader(hash common.Hash, number uint64) *types.Header { + hdr, _ := b.HeaderByHash(context.Background(), hash) + return hdr +} + +func (b *testBackend) GetReceiptsByHash(hash common.Hash) types.Receipts { + r, _ := b.GetReceipts(context.Background(), hash) + return r +} + func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { var ( hash common.Hash @@ -137,35 +154,26 @@ func (b *testBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subsc return b.chainFeed.Subscribe(ch) } -func (b *testBackend) BloomStatus() (uint64, uint64) { - return params.BloomBitsBlocks, b.sections +func (b *testBackend) NewMatcherBackend() filtermaps.MatcherBackend { + return b.fm.NewMatcherBackend() } -func (b *testBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) { - requests := make(chan chan *bloombits.Retrieval) +func (b *testBackend) startFilterMaps(history uint64, disabled bool, params filtermaps.Params) { + head := b.CurrentBlock() + chainView := filtermaps.NewChainView(b, head.Number.Uint64(), head.Hash()) + config := filtermaps.Config{ + History: history, + Disabled: disabled, + ExportFileName: "", + } + b.fm = filtermaps.NewFilterMaps(b.db, chainView, 0, 0, params, config) + b.fm.Start() + b.fm.WaitIdle() +} - go session.Multiplex(16, 0, requests) - go func() { - for { - // Wait for a service request or a shutdown - select { - case <-ctx.Done(): - return - - case request := <-requests: - task := <-request - - task.Bitsets = make([][]byte, len(task.Sections)) - for i, section := range task.Sections { - if rand.Int()%4 != 0 { // Handle occasional missing deliveries - head := rawdb.ReadCanonicalHash(b.db, (section+1)*params.BloomBitsBlocks-1) - task.Bitsets[i], _ = rawdb.ReadBloomBits(b.db, task.Bit, section, head) - } - } - request <- task - } - } - }() +func (b *testBackend) stopFilterMaps() { + b.fm.Stop() + b.fm = nil } func (b *testBackend) setPending(block *types.Block, receipts types.Receipts) { @@ -173,7 +181,7 @@ func (b *testBackend) setPending(block *types.Block, receipts types.Receipts) { b.pendingReceipts = receipts } -func newTestFilterSystem(t testing.TB, db ethdb.Database, cfg Config) (*testBackend, *FilterSystem) { +func newTestFilterSystem(db ethdb.Database, cfg Config) (*testBackend, *FilterSystem) { backend := &testBackend{db: db} sys := NewFilterSystem(backend, cfg) return backend, sys @@ -189,7 +197,7 @@ func TestBlockSubscription(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - backend, sys = newTestFilterSystem(t, db, Config{}) + backend, sys = newTestFilterSystem(db, Config{}) api = NewFilterAPI(sys) genesis = &core.Genesis{ Config: params.TestChainConfig, @@ -244,7 +252,7 @@ func TestPendingTxFilter(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - backend, sys = newTestFilterSystem(t, db, Config{}) + backend, sys = newTestFilterSystem(db, Config{}) api = NewFilterAPI(sys) transactions = []*types.Transaction{ @@ -300,7 +308,7 @@ func TestPendingTxFilterFullTx(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - backend, sys = newTestFilterSystem(t, db, Config{}) + backend, sys = newTestFilterSystem(db, Config{}) api = NewFilterAPI(sys) transactions = []*types.Transaction{ @@ -356,7 +364,7 @@ func TestPendingTxFilterFullTx(t *testing.T) { func TestLogFilterCreation(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - _, sys = newTestFilterSystem(t, db, Config{}) + _, sys = newTestFilterSystem(db, Config{}) api = NewFilterAPI(sys) testCases = []struct { @@ -403,7 +411,7 @@ func TestInvalidLogFilterCreation(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - _, sys = newTestFilterSystem(t, db, Config{}) + _, sys = newTestFilterSystem(db, Config{}) api = NewFilterAPI(sys) ) @@ -429,7 +437,7 @@ func TestInvalidGetLogsRequest(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - _, sys = newTestFilterSystem(t, db, Config{}) + _, sys = newTestFilterSystem(db, Config{}) api = NewFilterAPI(sys) blockHash = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") ) @@ -455,7 +463,7 @@ func TestInvalidGetRangeLogsRequest(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - _, sys = newTestFilterSystem(t, db, Config{}) + _, sys = newTestFilterSystem(db, Config{}) api = NewFilterAPI(sys) ) @@ -470,7 +478,7 @@ func TestLogFilter(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - backend, sys = newTestFilterSystem(t, db, Config{}) + backend, sys = newTestFilterSystem(db, Config{}) api = NewFilterAPI(sys) firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") @@ -575,7 +583,7 @@ func TestPendingTxFilterDeadlock(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() - backend, sys = newTestFilterSystem(t, db, Config{Timeout: timeout}) + backend, sys = newTestFilterSystem(db, Config{Timeout: timeout}) api = NewFilterAPI(sys) done = make(chan struct{}) ) @@ -599,7 +607,7 @@ func TestPendingTxFilterDeadlock(t *testing.T) { // Create a bunch of filters that will // timeout either in 100ms or 200ms subs := make([]*Subscription, 20) - for i := 0; i < len(subs); i++ { + for i := range subs { fid := api.NewPendingTransactionFilter(nil) api.filtersMu.Lock() f, ok := api.filters[fid] diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index e2790d91eb..4026c03e89 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -46,15 +47,27 @@ func makeReceipt(addr common.Address) *types.Receipt { return receipt } -func BenchmarkFilters(b *testing.B) { +func BenchmarkFiltersIndexed(b *testing.B) { + benchmarkFilters(b, 0, false) +} + +func BenchmarkFiltersHalfIndexed(b *testing.B) { + benchmarkFilters(b, 50000, false) +} + +func BenchmarkFiltersUnindexed(b *testing.B) { + benchmarkFilters(b, 0, true) +} + +func benchmarkFilters(b *testing.B, history uint64, noHistory bool) { var ( - db = rawdb.NewMemoryDatabase() - _, sys = newTestFilterSystem(b, db, Config{}) - key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - addr1 = crypto.PubkeyToAddress(key1.PublicKey) - addr2 = common.BytesToAddress([]byte("jeff")) - addr3 = common.BytesToAddress([]byte("ethereum")) - addr4 = common.BytesToAddress([]byte("random addresses please")) + db = rawdb.NewMemoryDatabase() + backend, sys = newTestFilterSystem(db, Config{}) + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = common.BytesToAddress([]byte("jeff")) + addr3 = common.BytesToAddress([]byte("ethereum")) + addr4 = common.BytesToAddress([]byte("random addresses please")) gspec = &core.Genesis{ Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}}, @@ -94,9 +107,12 @@ func BenchmarkFilters(b *testing.B) { rawdb.WriteHeadBlockHash(db, block.Hash()) rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), receipts[i]) } + backend.startFilterMaps(history, noHistory, filtermaps.DefaultParams) + defer backend.stopFilterMaps() + b.ResetTimer() - filter := sys.NewRangeFilter(0, -1, []common.Address{addr1, addr2, addr3, addr4}, nil) + filter := sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), []common.Address{addr1, addr2, addr3, addr4}, nil) for i := 0; i < b.N; i++ { filter.begin = 0 @@ -107,10 +123,22 @@ func BenchmarkFilters(b *testing.B) { } } -func TestFilters(t *testing.T) { +func TestFiltersIndexed(t *testing.T) { + testFilters(t, 0, false) +} + +func TestFiltersHalfIndexed(t *testing.T) { + testFilters(t, 500, false) +} + +func TestFiltersUnindexed(t *testing.T) { + testFilters(t, 0, true) +} + +func testFilters(t *testing.T, history uint64, noHistory bool) { var ( db = rawdb.NewMemoryDatabase() - backend, sys = newTestFilterSystem(t, db, Config{}) + backend, sys = newTestFilterSystem(db, Config{}) // Sender account key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr = crypto.PubkeyToAddress(key1.PublicKey) @@ -279,6 +307,9 @@ func TestFilters(t *testing.T) { }) backend.setPending(pchain[0], preceipts[0]) + backend.startFilterMaps(history, noHistory, filtermaps.DefaultParams) + defer backend.stopFilterMaps() + for i, tc := range []struct { f *Filter want string @@ -387,3 +418,146 @@ func TestFilters(t *testing.T) { } }) } + +func TestRangeLogs(t *testing.T) { + var ( + db = rawdb.NewMemoryDatabase() + backend, sys = newTestFilterSystem(db, Config{}) + gspec = &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{}, + BaseFee: big.NewInt(params.InitialBaseFee), + } + ) + _, err := gspec.Commit(db, triedb.NewDatabase(db, nil)) + if err != nil { + t.Fatal(err) + } + chain, _ := core.GenerateChain(gspec.Config, gspec.ToBlock(), ethash.NewFaker(), db, 1000, func(i int, gen *core.BlockGen) {}) + var l uint64 + bc, err := core.NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, &l) + if err != nil { + t.Fatal(err) + } + _, err = bc.InsertChain(chain[:600]) + if err != nil { + t.Fatal(err) + } + + backend.startFilterMaps(200, false, filtermaps.RangeTestParams) + defer backend.stopFilterMaps() + + var ( + testCase, event int + filter *Filter + addresses = []common.Address{{}} + ) + + expEvent := func(exp rangeLogsTestEvent) { + event++ + ev := <-filter.rangeLogsTestHook + if ev != exp { + t.Fatalf("Test case #%d: wrong test event #%d received (got %v, expected %v)", testCase, event, ev, exp) + } + } + + newFilter := func(begin, end int64) { + testCase++ + event = 0 + filter = sys.NewRangeFilter(begin, end, addresses, nil) + filter.rangeLogsTestHook = make(chan rangeLogsTestEvent) + go func(filter *Filter) { + filter.Logs(context.Background()) + // ensure that filter will not be blocked if we exit early + for range filter.rangeLogsTestHook { + } + }(filter) + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 0, 0}) + } + + updateHead := func() { + head := bc.CurrentBlock() + backend.fm.SetTarget(filtermaps.NewChainView(backend, head.Number.Uint64(), head.Hash()), 0, 0) + backend.fm.WaitIdle() + } + + // test case #1 + newFilter(300, 500) + expEvent(rangeLogsTestEvent{rangeLogsTestIndexed, 401, 500}) + expEvent(rangeLogsTestEvent{rangeLogsTestSync, 401, 500}) + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 401, 500}) + expEvent(rangeLogsTestEvent{rangeLogsTestUnindexed, 300, 400}) + if _, err := bc.InsertChain(chain[600:700]); err != nil { + t.Fatal(err) + } + updateHead() + expEvent(rangeLogsTestEvent{rangeLogsTestSync, 300, 500}) + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 300, 500}) // unindexed search is not affected by trimmed tail + expEvent(rangeLogsTestEvent{rangeLogsTestDone, 0, 0}) + + // test case #2 + newFilter(400, int64(rpc.LatestBlockNumber)) + expEvent(rangeLogsTestEvent{rangeLogsTestIndexed, 501, 700}) + if _, err := bc.InsertChain(chain[700:800]); err != nil { + t.Fatal(err) + } + updateHead() + expEvent(rangeLogsTestEvent{rangeLogsTestSync, 501, 700}) + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 601, 698}) + expEvent(rangeLogsTestEvent{rangeLogsTestUnindexed, 400, 600}) + expEvent(rangeLogsTestEvent{rangeLogsTestSync, 400, 698}) + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 400, 698}) + expEvent(rangeLogsTestEvent{rangeLogsTestIndexed, 699, 800}) + if err := bc.SetHead(750); err != nil { + t.Fatal(err) + } + updateHead() + expEvent(rangeLogsTestEvent{rangeLogsTestSync, 400, 800}) + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 400, 748}) + expEvent(rangeLogsTestEvent{rangeLogsTestIndexed, 749, 750}) + expEvent(rangeLogsTestEvent{rangeLogsTestSync, 400, 750}) + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 400, 750}) + expEvent(rangeLogsTestEvent{rangeLogsTestDone, 0, 0}) + + // test case #3 + newFilter(int64(rpc.LatestBlockNumber), int64(rpc.LatestBlockNumber)) + expEvent(rangeLogsTestEvent{rangeLogsTestIndexed, 750, 750}) + if err := bc.SetHead(740); err != nil { + t.Fatal(err) + } + updateHead() + expEvent(rangeLogsTestEvent{rangeLogsTestSync, 750, 750}) + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 0, 0}) + expEvent(rangeLogsTestEvent{rangeLogsTestIndexed, 740, 740}) + if _, err := bc.InsertChain(chain[740:750]); err != nil { + t.Fatal(err) + } + updateHead() + expEvent(rangeLogsTestEvent{rangeLogsTestSync, 740, 740}) + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 0, 0}) + expEvent(rangeLogsTestEvent{rangeLogsTestIndexed, 750, 750}) + expEvent(rangeLogsTestEvent{rangeLogsTestSync, 750, 750}) + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 750, 750}) + expEvent(rangeLogsTestEvent{rangeLogsTestDone, 0, 0}) + + // test case #4 + newFilter(400, int64(rpc.LatestBlockNumber)) + expEvent(rangeLogsTestEvent{rangeLogsTestIndexed, 551, 750}) + expEvent(rangeLogsTestEvent{rangeLogsTestSync, 551, 750}) + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 551, 750}) + expEvent(rangeLogsTestEvent{rangeLogsTestUnindexed, 400, 550}) + if _, err := bc.InsertChain(chain[750:1000]); err != nil { + t.Fatal(err) + } + updateHead() + expEvent(rangeLogsTestEvent{rangeLogsTestSync, 400, 750}) + // indexed range affected by tail pruning so we have to discard the entire + // match set + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 0, 0}) + expEvent(rangeLogsTestEvent{rangeLogsTestIndexed, 801, 1000}) + expEvent(rangeLogsTestEvent{rangeLogsTestSync, 801, 1000}) + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 801, 1000}) + expEvent(rangeLogsTestEvent{rangeLogsTestUnindexed, 400, 800}) + expEvent(rangeLogsTestEvent{rangeLogsTestSync, 400, 1000}) + expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 400, 1000}) +} diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index d70cb90ec9..88ff7b8af3 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -46,7 +46,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/bloombits" + "github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -615,11 +615,9 @@ func (b testBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) func (b testBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { panic("implement me") } -func (b testBackend) BloomStatus() (uint64, uint64) { panic("implement me") } -func (b testBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) { +func (b testBackend) NewMatcherBackend() filtermaps.MatcherBackend { panic("implement me") } - func TestEstimateGas(t *testing.T) { t.Parallel() // Initialize test accounts diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 3d8f7aa700..9e2ea2c876 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -27,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/bloombits" + "github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -93,8 +93,8 @@ type Backend interface { GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription - BloomStatus() (uint64, uint64) - ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) + + NewMatcherBackend() filtermaps.MatcherBackend } func GetAPIs(apiBackend Backend) []rpc.API { diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index e017804861..a5fd9bb0d4 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -30,7 +30,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/bloombits" + "github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -393,12 +393,12 @@ func (b *backendMock) TxPoolContent() (map[common.Address][]*types.Transaction, func (b *backendMock) TxPoolContentFrom(addr common.Address) ([]*types.Transaction, []*types.Transaction) { return nil, nil } -func (b *backendMock) SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription { return nil } -func (b *backendMock) BloomStatus() (uint64, uint64) { return 0, 0 } -func (b *backendMock) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {} -func (b *backendMock) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { return nil } +func (b *backendMock) SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription { return nil } +func (b *backendMock) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { return nil } func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { return nil } func (b *backendMock) Engine() consensus.Engine { return nil } + +func (b *backendMock) NewMatcherBackend() filtermaps.MatcherBackend { return nil }