From 7cbf934488b510206b8d0c9e1453fe9fad83692a Mon Sep 17 00:00:00 2001 From: Pierre Rousset Date: Fri, 21 Feb 2025 11:28:11 +0900 Subject: [PATCH 001/658] Fix flakey behavior in simulated backend Rollback --- core/txpool/txpool.go | 1 + 1 file changed, 1 insertion(+) diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 0ebf4c7e4b7..e09c9207790 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -486,6 +486,7 @@ func (p *TxPool) Sync() error { // Clear removes all tracked txs from the subpools. func (p *TxPool) Clear() { + p.Sync() for _, subpool := range p.subpools { subpool.Clear() } From 18faa251b3cdf9ead91d861c524d7a9e3dec0af7 Mon Sep 17 00:00:00 2001 From: Pierre R Date: Tue, 11 Mar 2025 18:26:16 +0900 Subject: [PATCH 002/658] Update core/txpool/txpool.go Co-authored-by: jwasinger --- core/txpool/txpool.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index e09c9207790..9b1136756b6 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -486,6 +486,8 @@ func (p *TxPool) Sync() error { // Clear removes all tracked txs from the subpools. func (p *TxPool) Clear() { + // Invoke Sync to ensure that txs pending addition don't get added to the pool after + // the subpools are subsequently cleared p.Sync() for _, subpool := range p.subpools { subpool.Clear() From 7d99f7df0056ea450119a14767d17e818812e736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladimir=20T=C3=A1mara=20Pati=C3=B1o?= Date: Sat, 15 Mar 2025 13:35:10 -0400 Subject: [PATCH 003/658] cmd/utils: fix geth test issue on OpenBSD (#31357) --- cmd/utils/cmd.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index 33c93c2cc60..d91d6ca5a80 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -63,8 +63,8 @@ var ErrImportInterrupted = errors.New("interrupted") // is redirected to a different file. func Fatalf(format string, args ...interface{}) { w := io.MultiWriter(os.Stdout, os.Stderr) - if runtime.GOOS == "windows" { - // The SameFile check below doesn't work on Windows. + if runtime.GOOS == "windows" || runtime.GOOS == "openbsd" { + // The SameFile check below doesn't work on Windows neither OpenBSD. // stdout is unlikely to get redirected though, so just print there. w = os.Stdout } else { From d25c3c32b24c5e176d9433b9f8da53eaf8afa95f Mon Sep 17 00:00:00 2001 From: Mike Weyandt Date: Sun, 16 Mar 2025 21:48:08 -0400 Subject: [PATCH 004/658] metrics: spin up meter ticker routine when enabling metric system (#31400) Addresses https://github.com/ethereum/go-ethereum/issues/31244 --- metrics/meter.go | 17 +++++++++-------- metrics/metrics.go | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/metrics/meter.go b/metrics/meter.go index 194bd1f3041..197e5abbed4 100644 --- a/metrics/meter.go +++ b/metrics/meter.go @@ -131,19 +131,15 @@ var arbiter = meterTicker{meters: make(map[*Meter]struct{})} type meterTicker struct { mu sync.RWMutex - started bool - meters map[*Meter]struct{} + once sync.Once + meters map[*Meter]struct{} } -// add adds another *Meter ot the arbiter, and starts the arbiter ticker. +// add a *Meter to the arbiter func (ma *meterTicker) add(m *Meter) { ma.mu.Lock() defer ma.mu.Unlock() ma.meters[m] = struct{}{} - if !ma.started { - ma.started = true - go ma.loop() - } } // remove removes a meter from the set of ticked meters. @@ -153,7 +149,7 @@ func (ma *meterTicker) remove(m *Meter) { ma.mu.Unlock() } -// loop ticks meters on a 5 second interval. +// loop ticks meters on a 5-second interval. func (ma *meterTicker) loop() { ticker := time.NewTicker(5 * time.Second) for range ticker.C { @@ -167,3 +163,8 @@ func (ma *meterTicker) loop() { ma.mu.RUnlock() } } + +// startMeterTickerLoop will start the arbiter ticker. +func startMeterTickerLoop() { + arbiter.once.Do(func() { go arbiter.loop() }) +} diff --git a/metrics/metrics.go b/metrics/metrics.go index c4c43b7576b..088948d4034 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -30,6 +30,7 @@ func Enabled() bool { // the program, before any metrics collection will happen. func Enable() { metricsEnabled = true + startMeterTickerLoop() } var threadCreateProfile = pprof.Lookup("threadcreate") From 881ee4062f93eaa6d0cc95202326caa7af4f88c9 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 17 Mar 2025 14:13:12 +0800 Subject: [PATCH 005/658] eth/catalyst: fix flaky test (#31403) This pull request enhances the unit test, avoiding unnecessary failure in CI. ``` --- FAIL: TestSimulatedBeaconSendWithdrawals (12.08s) simulated_beacon_test.go:139: timed out without including all withdrawals/txs FAIL ``` --- eth/catalyst/simulated_beacon_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eth/catalyst/simulated_beacon_test.go b/eth/catalyst/simulated_beacon_test.go index ea354828969..3a74a92ed5b 100644 --- a/eth/catalyst/simulated_beacon_test.go +++ b/eth/catalyst/simulated_beacon_test.go @@ -120,7 +120,7 @@ func TestSimulatedBeaconSendWithdrawals(t *testing.T) { includedTxs := make(map[common.Hash]struct{}) var includedWithdrawals []uint64 - timer := time.NewTimer(12 * time.Second) + timer := time.NewTimer(30 * time.Second) for { select { case ev := <-chainHeadCh: @@ -131,8 +131,8 @@ func TestSimulatedBeaconSendWithdrawals(t *testing.T) { for _, includedWithdrawal := range block.Withdrawals() { includedWithdrawals = append(includedWithdrawals, includedWithdrawal.Index) } - // ensure all withdrawals/txs included. this will take two blocks b/c number of withdrawals > 10 - if len(includedTxs) == len(txs) && len(includedWithdrawals) == len(withdrawals) && ev.Header.Number.Cmp(big.NewInt(2)) == 0 { + // ensure all withdrawals/txs included. this will at least take two blocks b/c number of withdrawals > 10 + if len(includedTxs) == len(txs) && len(includedWithdrawals) == len(withdrawals) { return } case <-timer.C: From 74c6b69384557d33d47a4863d859f6f4cc3485d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 09:22:12 +0100 Subject: [PATCH 006/658] go.mod: bump golang.org/x/net from 0.34.0 to 0.36.0 (#31369) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.34.0 to 0.36.0.
Commits
  • 85d1d54 go.mod: update golang.org/x dependencies
  • cde1dda proxy, http/httpproxy: do not mismatch IPv6 zone ids against hosts
  • fe7f039 publicsuffix: spruce up code gen and speed up PublicSuffix
  • 459513d internal/http3: move more common stream processing to genericConn
  • aad0180 http2: fix flakiness from t.Log when GOOS=js
  • b73e574 http2: don't log expected errors from writing invalid trailers
  • 5f45c77 internal/http3: make read-data tests usable for server handlers
  • 43c2540 http2, internal/httpcommon: reject userinfo in :authority
  • 1d78a08 http2, internal/httpcommon: factor out server header logic for h2/h3
  • 0d7dc54 quic: add Conn.ConnectionState
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golang.org/x/net&package-manager=go_modules&previous-version=0.34.0&new-version=0.36.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/ethereum/go-ethereum/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index eac0d7a7fde..053c7b7a7d9 100644 --- a/go.mod +++ b/go.mod @@ -143,7 +143,7 @@ require ( github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.34.0 // indirect + golang.org/x/net v0.36.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 4537d194d0a..40800ed257f 100644 --- a/go.sum +++ b/go.sum @@ -619,8 +619,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= +golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= From 1cdf4d6da7fbc0d481d25b9510da98c25bce384d Mon Sep 17 00:00:00 2001 From: Shude Li Date: Mon, 17 Mar 2025 16:32:44 +0800 Subject: [PATCH 007/658] eth/catalyst: set FeeRecipient in dev mode (#31316) this adds 2 features to improve `geth --dev` experience. 1. we don't need to use `dev_SetFeeRecipient` to set initial coinbase address. it was a pain. 2. we don't need to unlock keystore if we don't use it. we had it because of clique. --- cmd/geth/config.go | 2 +- cmd/utils/flags.go | 7 +++++-- eth/catalyst/simulated_beacon.go | 3 ++- eth/catalyst/simulated_beacon_test.go | 2 +- ethclient/simulated/backend.go | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 71ab0b6968f..4215403234a 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -233,7 +233,7 @@ func makeFullNode(ctx *cli.Context) *node.Node { if ctx.IsSet(utils.DeveloperFlag.Name) { // Start dev mode. - simBeacon, err := catalyst.NewSimulatedBeacon(ctx.Uint64(utils.DeveloperPeriodFlag.Name), eth) + simBeacon, err := catalyst.NewSimulatedBeacon(ctx.Uint64(utils.DeveloperPeriodFlag.Name), cfg.Eth.Miner.PendingFeeRecipient, eth) if err != nil { utils.Fatalf("failed to register dev mode catalyst service: %v", err) } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 7c32d7e7342..06eabbf3136 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1757,8 +1757,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // the miner will fail to start. cfg.Miner.PendingFeeRecipient = developer.Address - if err := ks.Unlock(developer, passphrase); err != nil { - Fatalf("Failed to unlock developer account: %v", err) + // try to unlock the first keystore account + if len(ks.Accounts()) > 0 { + if err := ks.Unlock(developer, passphrase); err != nil { + Fatalf("Failed to unlock developer account: %v", err) + } } log.Info("Using developer account", "address", developer.Address) diff --git a/eth/catalyst/simulated_beacon.go b/eth/catalyst/simulated_beacon.go index c71add93bc9..dd9d8f90629 100644 --- a/eth/catalyst/simulated_beacon.go +++ b/eth/catalyst/simulated_beacon.go @@ -108,7 +108,7 @@ func payloadVersion(config *params.ChainConfig, time uint64) engine.PayloadVersi } // NewSimulatedBeacon constructs a new simulated beacon chain. -func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, error) { +func NewSimulatedBeacon(period uint64, feeRecipient common.Address, eth *eth.Ethereum) (*SimulatedBeacon, error) { block := eth.BlockChain().CurrentBlock() current := engine.ForkchoiceStateV1{ HeadBlockHash: block.Hash(), @@ -131,6 +131,7 @@ func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, err engineAPI: engineAPI, lastBlockTime: block.Time, curForkchoiceState: current, + feeRecipient: feeRecipient, }, nil } diff --git a/eth/catalyst/simulated_beacon_test.go b/eth/catalyst/simulated_beacon_test.go index 3a74a92ed5b..24eee6e9256 100644 --- a/eth/catalyst/simulated_beacon_test.go +++ b/eth/catalyst/simulated_beacon_test.go @@ -55,7 +55,7 @@ func startSimulatedBeaconEthService(t *testing.T, genesis *core.Genesis, period t.Fatal("can't create eth service:", err) } - simBeacon, err := NewSimulatedBeacon(period, ethservice) + simBeacon, err := NewSimulatedBeacon(period, common.Address{}, ethservice) if err != nil { t.Fatal("can't create simulated beacon:", err) } diff --git a/ethclient/simulated/backend.go b/ethclient/simulated/backend.go index 65d44b9efad..d573c7e7507 100644 --- a/ethclient/simulated/backend.go +++ b/ethclient/simulated/backend.go @@ -120,7 +120,7 @@ func newWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (*Backe return nil, err } // Set up the simulated beacon - beacon, err := catalyst.NewSimulatedBeacon(blockPeriod, backend) + beacon, err := catalyst.NewSimulatedBeacon(blockPeriod, common.Address{}, backend) if err != nil { return nil, err } From 64bd21393e734b266913f549f9f642f2bd5cc4e4 Mon Sep 17 00:00:00 2001 From: jwasinger Date: Mon, 17 Mar 2025 15:56:55 +0100 Subject: [PATCH 008/658] cmd/abigen, accounts/abi/bind: implement abigen version 2 (#31379) This PR implements a new version of the abigen utility (v2) which exists along with the pre-existing v1 version. Abigen is a utility command provided by go-ethereum that, given a solidity contract ABI definition, will generate Go code to transact/call the contract methods, converting the method parameters/results and structures defined in the contract into corresponding Go types. This is useful for preventing the need to write custom boilerplate code for contract interactions. Methods in the generated bindings perform encoding between Go types and Solidity ABI-encoded packed bytecode, as well as some action (e.g. `eth_call` or creating and submitting a transaction). This limits the flexibility of how the generated bindings can be used, and prevents easily adding new functionality, as it will make the generated bindings larger for each feature added. Abigen v2 was conceived of by the observation that the only functionality that generated Go bindings ought to perform is conversion between Go types and ABI-encoded packed data. Go-ethereum already provides various APIs which in conjunction with conversion methods generated in v2 bindings can cover all functionality currently provided by v1, and facilitate all other previously-desired use-cases. ## Generating Bindings To generate contract bindings using abigen v2, invoke the `abigen` command with the `--v2` flag. The functionality of all other flags is preserved between the v2 and v1 versions. ## What is Generated in the Bindings The execution of `abigen --v2` generates Go code containing methods which convert between Go types and corresponding ABI-encoded data expected by the contract. For each input-accepting contract method and the constructor, a "packing" method is generated in the binding which converts from Go types to the corresponding packed solidity expected by the contract. If a method returns output, an "unpacking" method is generated to convert this output from ABI-encoded data to the corresponding Go types. For contracts which emit events, an unpacking method is defined for each event to unpack the corresponding raw log to the Go type that it represents. Likewise, where custom errors are defined by contracts, an unpack method is generated to unpack raw error data into a Go type. ## Using the Generated Bindings For a smooth user-experience, abigen v2 comes with a number of utility functions to be used in conjunction with the generated bindings for performing common contract interaction use-cases. These include: * filtering for historical logs of a given topic * watching the chain for emission of logs with a given topic * contract deployment methods * Call/Transact methods https://geth.ethereum.org will be updated to include a new tutorial page for abigen v2 with full code examples. The page currently exists in a PR: https://github.com/ethereum/go-ethereum/pull/31390 . There are also extensive examples of interactions with contract bindings in [test cases](https://github.com/ethereum/go-ethereum/blob/cc855c7ede460270ae9c83bba278b23cb4f26a00/accounts/abi/bind/v2/lib_test.go) provided with this PR. --------- Co-authored-by: Sina Mahmoodi Co-authored-by: Felix Lange --- accounts/abi/{bind => abigen}/bind.go | 141 ++-- accounts/abi/{bind => abigen}/bind_test.go | 14 +- accounts/abi/abigen/bindv2.go | 373 ++++++++++ accounts/abi/abigen/bindv2_test.go | 342 +++++++++ accounts/abi/{bind => abigen}/source.go.tpl | 0 accounts/abi/abigen/source2.go.tpl | 238 +++++++ accounts/abi/{bind => abigen}/template.go | 67 +- .../abigen/testdata/v2/callbackparam.go.txt | 64 ++ .../abi/abigen/testdata/v2/crowdsale.go.txt | 304 ++++++++ accounts/abi/abigen/testdata/v2/dao.go.txt | 655 ++++++++++++++++++ .../testdata/v2/deeplynestedarray.go.txt | 114 +++ accounts/abi/abigen/testdata/v2/empty.go.txt | 52 ++ .../abigen/testdata/v2/eventchecker.go.txt | 261 +++++++ accounts/abi/abigen/testdata/v2/getter.go.txt | 89 +++ .../testdata/v2/identifiercollision.go.txt | 102 +++ .../abigen/testdata/v2/inputchecker.go.txt | 123 ++++ .../abi/abigen/testdata/v2/interactor.go.txt | 126 ++++ .../abigen/testdata/v2/nameconflict.go.txt | 137 ++++ .../testdata/v2/numericmethodname.go.txt | 129 ++++ .../abigen/testdata/v2/outputchecker.go.txt | 253 +++++++ .../abi/abigen/testdata/v2/overload.go.txt | 159 +++++ .../abigen/testdata/v2/rangekeyword.go.txt | 64 ++ accounts/abi/abigen/testdata/v2/slicer.go.txt | 152 ++++ .../abi/abigen/testdata/v2/structs-abi.go.txt | 116 ++++ .../abi/abigen/testdata/v2/structs.go.txt | 119 ++++ accounts/abi/abigen/testdata/v2/token.go.txt | 319 +++++++++ accounts/abi/abigen/testdata/v2/tuple.go.txt | 228 ++++++ accounts/abi/abigen/testdata/v2/tupler.go.txt | 89 +++ .../abi/abigen/testdata/v2/underscorer.go.txt | 322 +++++++++ accounts/abi/bind/auth.go | 179 ----- accounts/abi/bind/old.go | 294 ++++++++ accounts/abi/bind/v2/auth.go | 96 +++ accounts/abi/bind/{ => v2}/backend.go | 13 + accounts/abi/bind/{ => v2}/base.go | 148 ++-- accounts/abi/bind/{ => v2}/base_test.go | 2 +- accounts/abi/bind/v2/dep_tree.go | 167 +++++ accounts/abi/bind/v2/dep_tree_test.go | 370 ++++++++++ accounts/abi/bind/v2/generate_test.go | 102 +++ .../bind/v2/internal/contracts/db/bindings.go | 293 ++++++++ .../internal/contracts/db/combined-abi.json | 1 + .../v2/internal/contracts/db/contract.sol | 66 ++ .../v2/internal/contracts/events/bindings.go | 160 +++++ .../contracts/events/combined-abi.json | 1 + .../v2/internal/contracts/events/contract.sol | 36 + .../contracts/nested_libraries/abi.json | 1 + .../contracts/nested_libraries/bindings.go | 486 +++++++++++++ .../nested_libraries/combined-abi.json | 1 + .../contracts/nested_libraries/contract.sol | 76 ++ .../contracts/solc_errors/bindings.go | 217 ++++++ .../contracts/solc_errors/combined-abi.json | 1 + .../contracts/solc_errors/contract.sol | 36 + .../contracts/uint256arrayreturn/bindings.go | 77 ++ .../uint256arrayreturn/combined-abi.json | 1 + .../contracts/uint256arrayreturn/contract.sol | 10 + accounts/abi/bind/v2/lib.go | 243 +++++++ accounts/abi/bind/v2/lib_test.go | 361 ++++++++++ accounts/abi/bind/{ => v2}/util.go | 32 +- accounts/abi/bind/{ => v2}/util_test.go | 13 +- cmd/abigen/main.go | 36 +- go.mod | 2 +- 60 files changed, 8271 insertions(+), 402 deletions(-) rename accounts/abi/{bind => abigen}/bind.go (75%) rename accounts/abi/{bind => abigen}/bind_test.go (99%) create mode 100644 accounts/abi/abigen/bindv2.go create mode 100644 accounts/abi/abigen/bindv2_test.go rename accounts/abi/{bind => abigen}/source.go.tpl (100%) create mode 100644 accounts/abi/abigen/source2.go.tpl rename accounts/abi/{bind => abigen}/template.go (64%) create mode 100644 accounts/abi/abigen/testdata/v2/callbackparam.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/crowdsale.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/dao.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/deeplynestedarray.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/empty.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/eventchecker.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/getter.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/identifiercollision.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/inputchecker.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/interactor.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/nameconflict.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/numericmethodname.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/outputchecker.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/overload.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/rangekeyword.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/slicer.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/structs-abi.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/structs.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/token.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/tuple.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/tupler.go.txt create mode 100644 accounts/abi/abigen/testdata/v2/underscorer.go.txt delete mode 100644 accounts/abi/bind/auth.go create mode 100644 accounts/abi/bind/old.go create mode 100644 accounts/abi/bind/v2/auth.go rename accounts/abi/bind/{ => v2}/backend.go (90%) rename accounts/abi/bind/{ => v2}/base.go (85%) rename accounts/abi/bind/{ => v2}/base_test.go (99%) create mode 100644 accounts/abi/bind/v2/dep_tree.go create mode 100644 accounts/abi/bind/v2/dep_tree_test.go create mode 100644 accounts/abi/bind/v2/generate_test.go create mode 100644 accounts/abi/bind/v2/internal/contracts/db/bindings.go create mode 100644 accounts/abi/bind/v2/internal/contracts/db/combined-abi.json create mode 100644 accounts/abi/bind/v2/internal/contracts/db/contract.sol create mode 100644 accounts/abi/bind/v2/internal/contracts/events/bindings.go create mode 100644 accounts/abi/bind/v2/internal/contracts/events/combined-abi.json create mode 100644 accounts/abi/bind/v2/internal/contracts/events/contract.sol create mode 100644 accounts/abi/bind/v2/internal/contracts/nested_libraries/abi.json create mode 100644 accounts/abi/bind/v2/internal/contracts/nested_libraries/bindings.go create mode 100644 accounts/abi/bind/v2/internal/contracts/nested_libraries/combined-abi.json create mode 100644 accounts/abi/bind/v2/internal/contracts/nested_libraries/contract.sol create mode 100644 accounts/abi/bind/v2/internal/contracts/solc_errors/bindings.go create mode 100644 accounts/abi/bind/v2/internal/contracts/solc_errors/combined-abi.json create mode 100644 accounts/abi/bind/v2/internal/contracts/solc_errors/contract.sol create mode 100644 accounts/abi/bind/v2/internal/contracts/uint256arrayreturn/bindings.go create mode 100644 accounts/abi/bind/v2/internal/contracts/uint256arrayreturn/combined-abi.json create mode 100644 accounts/abi/bind/v2/internal/contracts/uint256arrayreturn/contract.sol create mode 100644 accounts/abi/bind/v2/lib.go create mode 100644 accounts/abi/bind/v2/lib_test.go rename accounts/abi/bind/{ => v2}/util.go (60%) rename accounts/abi/bind/{ => v2}/util_test.go (88%) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/abigen/bind.go similarity index 75% rename from accounts/abi/bind/bind.go rename to accounts/abi/abigen/bind.go index 71357c7a8c7..56e5e214de1 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/abigen/bind.go @@ -14,11 +14,11 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// Package bind generates Ethereum contract Go bindings. +// Package abigen generates Ethereum contract Go bindings. // // Detailed usage document and tutorial available on the go-ethereum Wiki page: -// https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts -package bind +// https://geth.ethereum.org/docs/developers/dapp-developer/native-bindings +package abigen import ( "bytes" @@ -33,13 +33,6 @@ import ( "github.com/ethereum/go-ethereum/log" ) -// Lang is a target programming language selector to generate bindings for. -type Lang int - -const ( - LangGo Lang = iota -) - func isKeyWord(arg string) bool { switch arg { case "break": @@ -81,7 +74,7 @@ func isKeyWord(arg string) bool { // to be used as is in client code, but rather as an intermediate struct which // enforces compile time type safety and naming convention as opposed to having to // manually maintain hard coded strings that break on runtime. -func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) { +func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, libs map[string]string, aliases map[string]string) (string, error) { var ( // contracts is the map of each individual contract requested binding contracts = make(map[string]*tmplContract) @@ -125,14 +118,14 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] for _, input := range evmABI.Constructor.Inputs { if hasStruct(input.Type) { - bindStructType[lang](input.Type, structs) + bindStructType(input.Type, structs) } } for _, original := range evmABI.Methods { // Normalize the method for capital cases and non-anonymous inputs/outputs normalized := original - normalizedName := methodNormalizer[lang](alias(aliases, original.Name)) + normalizedName := abi.ToCamelCase(alias(aliases, original.Name)) // Ensure there is no duplicated identifier var identifiers = callIdentifiers if !original.IsConstant() { @@ -159,17 +152,17 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) } if hasStruct(input.Type) { - bindStructType[lang](input.Type, structs) + bindStructType(input.Type, structs) } } normalized.Outputs = make([]abi.Argument, len(original.Outputs)) copy(normalized.Outputs, original.Outputs) for j, output := range normalized.Outputs { if output.Name != "" { - normalized.Outputs[j].Name = capitalise(output.Name) + normalized.Outputs[j].Name = abi.ToCamelCase(output.Name) } if hasStruct(output.Type) { - bindStructType[lang](output.Type, structs) + bindStructType(output.Type, structs) } } // Append the methods to the call or transact lists @@ -188,7 +181,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] normalized := original // Ensure there is no duplicated identifier - normalizedName := methodNormalizer[lang](alias(aliases, original.Name)) + normalizedName := abi.ToCamelCase(alias(aliases, original.Name)) // Name shouldn't start with a digit. It will make the generated code invalid. if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) { normalizedName = fmt.Sprintf("E%s", normalizedName) @@ -213,14 +206,14 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] // Event is a bit special, we need to define event struct in binding, // ensure there is no camel-case-style name conflict. for index := 0; ; index++ { - if !used[capitalise(normalized.Inputs[j].Name)] { - used[capitalise(normalized.Inputs[j].Name)] = true + if !used[abi.ToCamelCase(normalized.Inputs[j].Name)] { + used[abi.ToCamelCase(normalized.Inputs[j].Name)] = true break } normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index) } if hasStruct(input.Type) { - bindStructType[lang](input.Type, structs) + bindStructType(input.Type, structs) } } // Append the event to the accumulator list @@ -233,8 +226,9 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] if evmABI.HasReceive() { receive = &tmplMethod{Original: evmABI.Receive} } + contracts[types[i]] = &tmplContract{ - Type: capitalise(types[i]), + Type: abi.ToCamelCase(types[i]), InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""), InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"), Constructor: evmABI.Constructor, @@ -245,6 +239,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] Events: events, Libraries: make(map[string]string), } + // Function 4-byte signatures are stored in the same sequence // as types, if available. if len(fsigs) > i { @@ -270,6 +265,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] _, ok := isLib[types[i]] contracts[types[i]].Library = ok } + // Generate the contract template data content and render it data := &tmplData{ Package: pkg, @@ -280,36 +276,25 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] buffer := new(bytes.Buffer) funcs := map[string]interface{}{ - "bindtype": bindType[lang], - "bindtopictype": bindTopicType[lang], - "namedtype": namedType[lang], - "capitalise": capitalise, + "bindtype": bindType, + "bindtopictype": bindTopicType, + "capitalise": abi.ToCamelCase, "decapitalise": decapitalise, } - tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang])) + tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource)) if err := tmpl.Execute(buffer, data); err != nil { return "", err } - // For Go bindings pass the code through gofmt to clean it up - if lang == LangGo { - code, err := format.Source(buffer.Bytes()) - if err != nil { - return "", fmt.Errorf("%v\n%s", err, buffer) - } - return string(code), nil + // Pass the code through gofmt to clean it up + code, err := format.Source(buffer.Bytes()) + if err != nil { + return "", fmt.Errorf("%v\n%s", err, buffer) } - // For all others just return as is for now - return buffer.String(), nil -} - -// bindType is a set of type binders that convert Solidity types to some supported -// programming language types. -var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{ - LangGo: bindTypeGo, + return string(code), nil } -// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones. -func bindBasicTypeGo(kind abi.Type) string { +// bindBasicType converts basic solidity types(except array, slice and tuple) to Go ones. +func bindBasicType(kind abi.Type) string { switch kind.T { case abi.AddressTy: return "common.Address" @@ -332,32 +317,26 @@ func bindBasicTypeGo(kind abi.Type) string { } } -// bindTypeGo converts solidity types to Go ones. Since there is no clear mapping +// bindType converts solidity types to Go ones. Since there is no clear mapping // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly // mapped will use an upscaled type (e.g. BigDecimal). -func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { +func bindType(kind abi.Type, structs map[string]*tmplStruct) string { switch kind.T { case abi.TupleTy: return structs[kind.TupleRawName+kind.String()].Name case abi.ArrayTy: - return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs) + return fmt.Sprintf("[%d]", kind.Size) + bindType(*kind.Elem, structs) case abi.SliceTy: - return "[]" + bindTypeGo(*kind.Elem, structs) + return "[]" + bindType(*kind.Elem, structs) default: - return bindBasicTypeGo(kind) + return bindBasicType(kind) } } -// bindTopicType is a set of type binders that convert Solidity types to some -// supported programming language topic types. -var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{ - LangGo: bindTopicTypeGo, -} - -// bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same +// bindTopicType converts a Solidity topic type to a Go one. It is almost the same // functionality as for simple types, but dynamic types get converted to hashes. -func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { - bound := bindTypeGo(kind, structs) +func bindTopicType(kind abi.Type, structs map[string]*tmplStruct) string { + bound := bindType(kind, structs) // todo(rjl493456442) according solidity documentation, indexed event // parameters that are not value types i.e. arrays and structs are not @@ -371,16 +350,10 @@ func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { return bound } -// bindStructType is a set of type binders that convert Solidity tuple types to some supported -// programming language struct definition. -var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{ - LangGo: bindStructTypeGo, -} - -// bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping -// in the given map. -// Notably, this function will resolve and record nested struct recursively. -func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { +// bindStructType converts a Solidity tuple type to a Go one and records the mapping +// in the given map. Notably, this function will resolve and record nested struct +// recursively. +func bindStructType(kind abi.Type, structs map[string]*tmplStruct) string { switch kind.T { case abi.TupleTy: // We compose a raw struct name and a canonical parameter expression @@ -398,16 +371,20 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { fields []*tmplField ) for i, elem := range kind.TupleElems { - name := capitalise(kind.TupleRawNames[i]) + name := abi.ToCamelCase(kind.TupleRawNames[i]) name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] }) names[name] = true - fields = append(fields, &tmplField{Type: bindStructTypeGo(*elem, structs), Name: name, SolKind: *elem}) + fields = append(fields, &tmplField{ + Type: bindStructType(*elem, structs), + Name: name, + SolKind: *elem, + }) } name := kind.TupleRawName if name == "" { name = fmt.Sprintf("Struct%d", len(structs)) } - name = capitalise(name) + name = abi.ToCamelCase(name) structs[id] = &tmplStruct{ Name: name, @@ -415,20 +392,14 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { } return name case abi.ArrayTy: - return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs) + return fmt.Sprintf("[%d]", kind.Size) + bindStructType(*kind.Elem, structs) case abi.SliceTy: - return "[]" + bindStructTypeGo(*kind.Elem, structs) + return "[]" + bindStructType(*kind.Elem, structs) default: - return bindBasicTypeGo(kind) + return bindBasicType(kind) } } -// namedType is a set of functions that transform language specific types to -// named versions that may be used inside method names. -var namedType = map[Lang]func(string, abi.Type) string{ - LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") }, -} - // alias returns an alias of the given string based on the aliasing rules // or returns itself if no rule is matched. func alias(aliases map[string]string, n string) string { @@ -438,21 +409,11 @@ func alias(aliases map[string]string, n string) string { return n } -// methodNormalizer is a name transformer that modifies Solidity method names to -// conform to target language naming conventions. -var methodNormalizer = map[Lang]func(string) string{ - LangGo: abi.ToCamelCase, -} - -// capitalise makes a camel-case string which starts with an upper case character. -var capitalise = abi.ToCamelCase - // decapitalise makes a camel-case string which starts with a lower case character. func decapitalise(input string) string { if len(input) == 0 { return input } - goForm := abi.ToCamelCase(input) return strings.ToLower(goForm[:1]) + goForm[1:] } @@ -471,7 +432,7 @@ func structured(args abi.Arguments) bool { } // If the field name is empty when normalized or collides (var, Var, _var, _Var), // we can't organize into a struct - field := capitalise(out.Name) + field := abi.ToCamelCase(out.Name) if field == "" || exists[field] { return false } diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/abigen/bind_test.go similarity index 99% rename from accounts/abi/bind/bind_test.go rename to accounts/abi/abigen/bind_test.go index a390a3c47c7..3871560912d 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/abigen/bind_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package bind +package abigen import ( "fmt" @@ -2072,20 +2072,22 @@ var bindTests = []struct { // Tests that packages generated by the binder can be successfully compiled and // the requested tester run against it. -func TestGolangBindings(t *testing.T) { +func TestBindings(t *testing.T) { t.Parallel() // Skip the test if no Go command can be found gocmd := runtime.GOROOT() + "/bin/go" if !common.FileExist(gocmd) { t.Skip("go sdk not found for testing") } - // Create a temporary workspace for the test suite - ws := t.TempDir() - pkg := filepath.Join(ws, "bindtest") + // Create a temporary workspace for the test suite + path := t.TempDir() + pkg := filepath.Join(path, "bindtest") if err := os.MkdirAll(pkg, 0700); err != nil { t.Fatalf("failed to create package: %v", err) } + t.Log("tmpdir", pkg) + // Generate the test suite for all the contracts for i, tt := range bindTests { t.Run(tt.name, func(t *testing.T) { @@ -2096,7 +2098,7 @@ func TestGolangBindings(t *testing.T) { types = []string{tt.name} } // Generate the binding and create a Go source file in the workspace - bind, err := Bind(types, tt.abi, tt.bytecode, tt.fsigs, "bindtest", LangGo, tt.libs, tt.aliases) + bind, err := Bind(types, tt.abi, tt.bytecode, tt.fsigs, "bindtest", tt.libs, tt.aliases) if err != nil { t.Fatalf("test %d: failed to generate binding: %v", i, err) } diff --git a/accounts/abi/abigen/bindv2.go b/accounts/abi/abigen/bindv2.go new file mode 100644 index 00000000000..ef4b769bb41 --- /dev/null +++ b/accounts/abi/abigen/bindv2.go @@ -0,0 +1,373 @@ +// Copyright 2024 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 abigen + +import ( + "bytes" + "fmt" + "go/format" + "reflect" + "regexp" + "slices" + "sort" + "strings" + "text/template" + "unicode" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +// underlyingBindType returns a string representation of the Go type +// that corresponds to the given ABI type, panicking if it is not a +// pointer. +func underlyingBindType(typ abi.Type) string { + goType := typ.GetType() + if goType.Kind() != reflect.Pointer { + panic("trying to retrieve underlying bind type of non-pointer type.") + } + return goType.Elem().String() +} + +// isPointerType returns true if the underlying type is a pointer. +func isPointerType(typ abi.Type) bool { + return typ.GetType().Kind() == reflect.Pointer +} + +// OLD: +// binder is used during the conversion of an ABI definition into Go bindings +// (as part of the execution of BindV2). In contrast to contractBinder, binder +// contains binding-generation-state that is shared between contracts: +// +// a global struct map of structs emitted by all contracts is tracked and expanded. +// Structs generated in the bindings are not prefixed with the contract name +// that uses them (to keep the generated bindings less verbose). +// +// This contrasts to other per-contract state (constructor/method/event/error, +// pack/unpack methods) which are guaranteed to be unique because of their +// association with the uniquely-named owning contract (whether prefixed in the +// generated symbol name, or as a member method on a contract struct). +// +// In addition, binder contains the input alias map. In BindV2, a binder is +// instantiated to produce a set of tmplContractV2 and tmplStruct objects from +// the provided ABI definition. These are used as part of the input to rendering +// the binding template. + +// NEW: +// binder is used to translate an ABI definition into a set of data-structures +// that will be used to render the template and produce Go bindings. This can +// be thought of as the "backend" that sanitizes the ABI definition to a format +// that can be directly rendered with minimal complexity in the template. +// +// The input data to the template rendering consists of: +// - the set of all contracts requested for binding, each containing +// methods/events/errors to emit pack/unpack methods for. +// - the set of structures defined by the contracts, and created +// as part of the binding process. +type binder struct { + // contracts is the map of each individual contract requested binding. + // It is keyed by the contract name provided in the ABI definition. + contracts map[string]*tmplContractV2 + + // structs is the map of all emitted structs from contracts being bound. + // it is keyed by a unique identifier generated from the name of the owning contract + // and the solidity type signature of the struct + structs map[string]*tmplStruct + + // aliases is a map for renaming instances of named events/functions/errors + // to specified values. it is keyed by source symbol name, and values are + // what the replacement name should be. + aliases map[string]string +} + +// BindStructType registers the type to be emitted as a struct in the +// bindings. +func (b *binder) BindStructType(typ abi.Type) { + bindStructType(typ, b.structs) +} + +// contractBinder holds state for binding of a single contract. It is a type +// registry for compiling maps of identifiers that will be emitted in generated +// bindings. +type contractBinder struct { + binder *binder + + // all maps are keyed by the original (non-normalized) name of the symbol in question + // from the provided ABI definition. + calls map[string]*tmplMethod + events map[string]*tmplEvent + errors map[string]*tmplError + callIdentifiers map[string]bool + eventIdentifiers map[string]bool + errorIdentifiers map[string]bool +} + +func newContractBinder(binder *binder) *contractBinder { + return &contractBinder{ + binder, + make(map[string]*tmplMethod), + make(map[string]*tmplEvent), + make(map[string]*tmplError), + make(map[string]bool), + make(map[string]bool), + make(map[string]bool), + } +} + +// registerIdentifier applies alias renaming, name normalization (conversion +// from snake to camel-case), and registers the normalized name in the specified identifier map. +// It returns an error if the normalized name already exists in the map. +func (cb *contractBinder) registerIdentifier(identifiers map[string]bool, original string) (normalized string, err error) { + normalized = abi.ToCamelCase(alias(cb.binder.aliases, original)) + + // Name shouldn't start with a digit. It will make the generated code invalid. + if len(normalized) > 0 && unicode.IsDigit(rune(normalized[0])) { + normalized = fmt.Sprintf("E%s", normalized) + normalized = abi.ResolveNameConflict(normalized, func(name string) bool { + _, ok := identifiers[name] + return ok + }) + } + if _, ok := identifiers[normalized]; ok { + return "", fmt.Errorf("duplicate symbol '%s'", normalized) + } + identifiers[normalized] = true + return normalized, nil +} + +// bindMethod registers a method to be emitted in the bindings. The name, inputs +// and outputs are normalized. If any inputs are struct-type their structs are +// registered to be emitted in the bindings. Any methods that return more than +// one output have their results gathered into a struct. +func (cb *contractBinder) bindMethod(original abi.Method) error { + normalized := original + normalizedName, err := cb.registerIdentifier(cb.callIdentifiers, original.Name) + if err != nil { + return err + } + normalized.Name = normalizedName + + normalized.Inputs = normalizeArgs(original.Inputs) + for _, input := range normalized.Inputs { + if hasStruct(input.Type) { + cb.binder.BindStructType(input.Type) + } + } + normalized.Outputs = normalizeArgs(original.Outputs) + for _, output := range normalized.Outputs { + if hasStruct(output.Type) { + cb.binder.BindStructType(output.Type) + } + } + + var isStructured bool + // If the call returns multiple values, gather them into a struct + if len(normalized.Outputs) > 1 { + isStructured = true + } + cb.calls[original.Name] = &tmplMethod{ + Original: original, + Normalized: normalized, + Structured: isStructured, + } + return nil +} + +// normalize a set of arguments by stripping underscores, giving a generic name +// in the case where the arg name collides with a reserved Go keyword, and finally +// converting to camel-case. +func normalizeArgs(args abi.Arguments) abi.Arguments { + args = slices.Clone(args) + used := make(map[string]bool) + + for i, input := range args { + if isKeyWord(input.Name) { + args[i].Name = fmt.Sprintf("arg%d", i) + } + args[i].Name = abi.ToCamelCase(args[i].Name) + if args[i].Name == "" { + args[i].Name = fmt.Sprintf("arg%d", i) + } else { + args[i].Name = strings.ToLower(args[i].Name[:1]) + args[i].Name[1:] + } + + for index := 0; ; index++ { + if !used[args[i].Name] { + used[args[i].Name] = true + break + } + args[i].Name = fmt.Sprintf("%s%d", args[i].Name, index) + } + } + return args +} + +// normalizeErrorOrEventFields normalizes errors/events for emitting through +// bindings: Any anonymous fields are given generated names. +func (cb *contractBinder) normalizeErrorOrEventFields(originalInputs abi.Arguments) abi.Arguments { + normalizedArguments := normalizeArgs(originalInputs) + for _, input := range normalizedArguments { + if hasStruct(input.Type) { + cb.binder.BindStructType(input.Type) + } + } + return normalizedArguments +} + +// bindEvent normalizes an event and registers it to be emitted in the bindings. +func (cb *contractBinder) bindEvent(original abi.Event) error { + // Skip anonymous events as they don't support explicit filtering + if original.Anonymous { + return nil + } + normalizedName, err := cb.registerIdentifier(cb.eventIdentifiers, original.Name) + if err != nil { + return err + } + + normalized := original + normalized.Name = normalizedName + normalized.Inputs = cb.normalizeErrorOrEventFields(original.Inputs) + cb.events[original.Name] = &tmplEvent{Original: original, Normalized: normalized} + return nil +} + +// bindError normalizes an error and registers it to be emitted in the bindings. +func (cb *contractBinder) bindError(original abi.Error) error { + normalizedName, err := cb.registerIdentifier(cb.errorIdentifiers, original.Name) + if err != nil { + return err + } + + normalized := original + normalized.Name = normalizedName + normalized.Inputs = cb.normalizeErrorOrEventFields(original.Inputs) + cb.errors[original.Name] = &tmplError{Original: original, Normalized: normalized} + return nil +} + +// parseLibraryDeps extracts references to library dependencies from the unlinked +// hex string deployment bytecode. +func parseLibraryDeps(unlinkedCode string) (res []string) { + reMatchSpecificPattern, err := regexp.Compile(`__\$([a-f0-9]+)\$__`) + if err != nil { + panic(err) + } + for _, match := range reMatchSpecificPattern.FindAllStringSubmatch(unlinkedCode, -1) { + res = append(res, match[1]) + } + return res +} + +// iterSorted iterates the map in the lexicographic order of the keys calling +// onItem on each. If the callback returns an error, iteration is halted and +// the error is returned from iterSorted. +func iterSorted[V any](inp map[string]V, onItem func(string, V) error) error { + var sortedKeys []string + for key := range inp { + sortedKeys = append(sortedKeys, key) + } + sort.Strings(sortedKeys) + + for _, key := range sortedKeys { + if err := onItem(key, inp[key]); err != nil { + return err + } + } + return nil +} + +// BindV2 generates a Go wrapper around a contract ABI. This wrapper isn't meant +// to be used as is in client code, but rather as an intermediate struct which +// enforces compile time type safety and naming convention as opposed to having to +// manually maintain hard coded strings that break on runtime. +func BindV2(types []string, abis []string, bytecodes []string, pkg string, libs map[string]string, aliases map[string]string) (string, error) { + b := binder{ + contracts: make(map[string]*tmplContractV2), + structs: make(map[string]*tmplStruct), + aliases: aliases, + } + for i := 0; i < len(types); i++ { + // Parse the actual ABI to generate the binding for + evmABI, err := abi.JSON(strings.NewReader(abis[i])) + if err != nil { + return "", err + } + + for _, input := range evmABI.Constructor.Inputs { + if hasStruct(input.Type) { + bindStructType(input.Type, b.structs) + } + } + + cb := newContractBinder(&b) + err = iterSorted(evmABI.Methods, func(_ string, original abi.Method) error { + return cb.bindMethod(original) + }) + if err != nil { + return "", err + } + err = iterSorted(evmABI.Events, func(_ string, original abi.Event) error { + return cb.bindEvent(original) + }) + if err != nil { + return "", err + } + err = iterSorted(evmABI.Errors, func(_ string, original abi.Error) error { + return cb.bindError(original) + }) + if err != nil { + return "", err + } + b.contracts[types[i]] = newTmplContractV2(types[i], abis[i], bytecodes[i], evmABI.Constructor, cb) + } + + invertedLibs := make(map[string]string) + for pattern, name := range libs { + invertedLibs[name] = pattern + } + data := tmplDataV2{ + Package: pkg, + Contracts: b.contracts, + Libraries: invertedLibs, + Structs: b.structs, + } + + for typ, contract := range data.Contracts { + for _, depPattern := range parseLibraryDeps(contract.InputBin) { + data.Contracts[typ].Libraries[libs[depPattern]] = depPattern + } + } + buffer := new(bytes.Buffer) + funcs := map[string]interface{}{ + "bindtype": bindType, + "bindtopictype": bindTopicType, + "capitalise": abi.ToCamelCase, + "decapitalise": decapitalise, + "ispointertype": isPointerType, + "underlyingbindtype": underlyingBindType, + } + tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSourceV2)) + if err := tmpl.Execute(buffer, data); err != nil { + return "", err + } + // Pass the code through gofmt to clean it up + code, err := format.Source(buffer.Bytes()) + if err != nil { + return "", fmt.Errorf("%v\n%s", err, buffer) + } + return string(code), nil +} diff --git a/accounts/abi/abigen/bindv2_test.go b/accounts/abi/abigen/bindv2_test.go new file mode 100644 index 00000000000..2756ba0835e --- /dev/null +++ b/accounts/abi/abigen/bindv2_test.go @@ -0,0 +1,342 @@ +// Copyright 2024 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 abigen + +import ( + "fmt" + "os" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/crypto" +) + +type bindV2Test struct { + name string + abis []string + bytecodes []string + types []string + aliases map[string]string +} + +func bindCombinedJSON(test *bindV2Test) (string, error) { + var ( + abis []string + bins []string + types []string + ) + libs := make(map[string]string) + for i := 0; i < len(test.types); i++ { + // fully qualified name is of the form : + typeName := test.types[i] + abis = append(abis, test.abis[i]) + bins = append(bins, test.bytecodes[i]) + types = append(types, typeName) + + // Derive the library placeholder which is a 34 character prefix of the + // hex encoding of the keccak256 hash of the fully qualified library name. + // Note that the fully qualified library name is the path of its source + // file and the library name separated by ":". + libPattern := crypto.Keccak256Hash([]byte(typeName)).String()[2:36] // the first 2 chars are 0x + libs[libPattern] = typeName + } + if test.aliases == nil { + test.aliases = make(map[string]string) + } + code, err := BindV2(types, abis, bins, "bindtests", libs, test.aliases) + if err != nil { + return "", fmt.Errorf("error creating bindings: %v", err) + } + return code, nil +} + +var combinedJSONBindTestsV2 = []bindV2Test{ + { + "Empty", + []string{`[]`}, + []string{`606060405260068060106000396000f3606060405200`}, + nil, + nil, + }, + { + "Token", + []string{`[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"spentAllowance","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"inputs":[{"name":"initialSupply","type":"uint256"},{"name":"tokenName","type":"string"},{"name":"decimalUnits","type":"uint8"},{"name":"tokenSymbol","type":"string"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]`}, + []string{`60606040526040516107fd3803806107fd83398101604052805160805160a05160c051929391820192909101600160a060020a0333166000908152600360209081526040822086905581548551838052601f6002600019610100600186161502019093169290920482018390047f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56390810193919290918801908390106100e857805160ff19168380011785555b506101189291505b8082111561017157600081556001016100b4565b50506002805460ff19168317905550505050610658806101a56000396000f35b828001600101855582156100ac579182015b828111156100ac5782518260005055916020019190600101906100fa565b50508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017557805160ff19168380011785555b506100c89291506100b4565b5090565b82800160010185558215610165579182015b8281111561016557825182600050559160200191906001019061018756606060405236156100775760e060020a600035046306fdde03811461007f57806323b872dd146100dc578063313ce5671461010e57806370a082311461011a57806395d89b4114610132578063a9059cbb1461018e578063cae9ca51146101bd578063dc3080f21461031c578063dd62ed3e14610341575b610365610002565b61036760008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b6103d5600435602435604435600160a060020a038316600090815260036020526040812054829010156104f357610002565b6103e760025460ff1681565b6103d560043560036020526000908152604090205481565b610367600180546020600282841615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b610365600435602435600160a060020a033316600090815260036020526040902054819010156103f157610002565b60806020604435600481810135601f8101849004909302840160405260608381526103d5948235946024803595606494939101919081908382808284375094965050505050505060006000836004600050600033600160a060020a03168152602001908152602001600020600050600087600160a060020a031681526020019081526020016000206000508190555084905080600160a060020a0316638f4ffcb1338630876040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156102f25780820380516001836020036101000a031916815260200191505b50955050505050506000604051808303816000876161da5a03f11561000257505050509392505050565b6005602090815260043560009081526040808220909252602435815220546103d59081565b60046020818152903560009081526040808220909252602435815220546103d59081565b005b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156103c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a03821660009081526040902054808201101561041357610002565b806003600050600033600160a060020a03168152602001908152602001600020600082828250540392505081905550806003600050600084600160a060020a0316815260200190815260200160002060008282825054019250508190555081600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b820191906000526020600020905b8154815290600101906020018083116104ce57829003601f168201915b505050505081565b600160a060020a03831681526040812054808301101561051257610002565b600160a060020a0380851680835260046020908152604080852033949094168086529382528085205492855260058252808520938552929052908220548301111561055c57610002565b816003600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816003600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816005600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054019250508190555082600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3939250505056`}, + nil, + nil, + }, + { + "Crowdsale", + []string{`[{"constant":false,"inputs":[],"name":"checkGoalReached","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"deadline","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"beneficiary","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"tokenReward","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"fundingGoal","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"amountRaised","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"price","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"funders","outputs":[{"name":"addr","type":"address"},{"name":"amount","type":"uint256"}],"type":"function"},{"inputs":[{"name":"ifSuccessfulSendTo","type":"address"},{"name":"fundingGoalInEthers","type":"uint256"},{"name":"durationInMinutes","type":"uint256"},{"name":"etherCostOfEachToken","type":"uint256"},{"name":"addressOfTokenUsedAsReward","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"backer","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"isContribution","type":"bool"}],"name":"FundTransfer","type":"event"}]`}, + []string{`606060408190526007805460ff1916905560a0806105a883396101006040529051608051915160c05160e05160008054600160a060020a03199081169095178155670de0b6b3a7640000958602600155603c9093024201600355930260045560058054909216909217905561052f90819061007990396000f36060604052361561006c5760e060020a600035046301cb3b20811461008257806329dcb0cf1461014457806338af3eed1461014d5780636e66f6e91461015f5780637a3a0e84146101715780637b3e5e7b1461017a578063a035b1fe14610183578063dc0d3dff1461018c575b61020060075460009060ff161561032357610002565b61020060035460009042106103205760025460015490106103cb576002548154600160a060020a0316908290606082818181858883f150915460025460408051600160a060020a039390931683526020830191909152818101869052517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf6945090819003909201919050a15b60405160008054600160a060020a039081169230909116319082818181858883f150506007805460ff1916600117905550505050565b6103a160035481565b6103ab600054600160a060020a031681565b6103ab600554600160a060020a031681565b6103a160015481565b6103a160025481565b6103a160045481565b6103be60043560068054829081101561000257506000526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f8101547ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d409190910154600160a060020a03919091169082565b005b505050815481101561000257906000526020600020906002020160005060008201518160000160006101000a815481600160a060020a030219169083021790555060208201518160010160005055905050806002600082828250540192505081905550600560009054906101000a9004600160a060020a0316600160a060020a031663a9059cbb3360046000505484046040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506000604051808303816000876161da5a03f11561000257505060408051600160a060020a03331681526020810184905260018183015290517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf692509081900360600190a15b50565b5060a0604052336060908152346080819052600680546001810180835592939282908280158290116102025760020281600202836000526020600020918201910161020291905b8082111561039d57805473ffffffffffffffffffffffffffffffffffffffff19168155600060019190910190815561036a565b5090565b6060908152602090f35b600160a060020a03166060908152602090f35b6060918252608052604090f35b5b60065481101561010e576006805482908110156100025760009182526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0190600680549254600160a060020a0316928490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460405190915082818181858883f19350505050507fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf660066000508281548110156100025760008290526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01548154600160a060020a039190911691908490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460408051600160a060020a0394909416845260208401919091526000838201525191829003606001919050a16001016103cc56`}, + nil, + nil, + }, + { + "DAO", + []string{`[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"},{"name":"description","type":"string"},{"name":"votingDeadline","type":"uint256"},{"name":"executed","type":"bool"},{"name":"proposalPassed","type":"bool"},{"name":"numberOfVotes","type":"uint256"},{"name":"currentResult","type":"int256"},{"name":"proposalHash","type":"bytes32"}],"type":"function"},{"constant":false,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"transactionBytecode","type":"bytes"}],"name":"executeProposal","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"memberId","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"numProposals","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"members","outputs":[{"name":"member","type":"address"},{"name":"canVote","type":"bool"},{"name":"name","type":"string"},{"name":"memberSince","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"debatingPeriodInMinutes","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"minimumQuorum","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"targetMember","type":"address"},{"name":"canVote","type":"bool"},{"name":"memberName","type":"string"}],"name":"changeMembership","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"majorityMargin","outputs":[{"name":"","type":"int256"}],"type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"},{"name":"etherAmount","type":"uint256"},{"name":"JobDescription","type":"string"},{"name":"transactionBytecode","type":"bytes"}],"name":"newProposal","outputs":[{"name":"proposalID","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"minimumQuorumForProposals","type":"uint256"},{"name":"minutesForDebate","type":"uint256"},{"name":"marginOfVotesForMajority","type":"int256"}],"name":"changeVotingRules","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"supportsProposal","type":"bool"},{"name":"justificationText","type":"string"}],"name":"vote","outputs":[{"name":"voteID","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"beneficiary","type":"address"},{"name":"etherAmount","type":"uint256"},{"name":"transactionBytecode","type":"bytes"}],"name":"checkProposalCode","outputs":[{"name":"codeChecksOut","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"type":"function"},{"inputs":[{"name":"minimumQuorumForProposals","type":"uint256"},{"name":"minutesForDebate","type":"uint256"},{"name":"marginOfVotesForMajority","type":"int256"},{"name":"congressLeader","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"description","type":"string"}],"name":"ProposalAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"position","type":"bool"},{"indexed":false,"name":"voter","type":"address"},{"indexed":false,"name":"justification","type":"string"}],"name":"Voted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"result","type":"int256"},{"indexed":false,"name":"quorum","type":"uint256"},{"indexed":false,"name":"active","type":"bool"}],"name":"ProposalTallied","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"member","type":"address"},{"indexed":false,"name":"isMember","type":"bool"}],"name":"MembershipChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"minimumQuorum","type":"uint256"},{"indexed":false,"name":"debatingPeriodInMinutes","type":"uint256"},{"indexed":false,"name":"majorityMargin","type":"int256"}],"name":"ChangeOfRules","type":"event"}]`}, + []string{`606060405260405160808061145f833960e06040529051905160a05160c05160008054600160a060020a03191633179055600184815560028490556003839055600780549182018082558280158290116100b8576003028160030283600052602060002091820191016100b891906101c8565b50506060919091015160029190910155600160a060020a0381166000146100a65760008054600160a060020a031916821790555b505050506111f18061026e6000396000f35b505060408051608081018252600080825260208281018290528351908101845281815292820192909252426060820152600780549194509250811015610002579081527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6889050815181546020848101517401000000000000000000000000000000000000000002600160a060020a03199290921690921760a060020a60ff021916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f9081018390048201949192919091019083901061023e57805160ff19168380011785555b50610072929150610226565b5050600060028201556001015b8082111561023a578054600160a860020a031916815560018181018054600080835592600290821615610100026000190190911604601f81901061020c57506101bb565b601f0160209004906000526020600020908101906101bb91905b8082111561023a5760008155600101610226565b5090565b828001600101855582156101af579182015b828111156101af57825182600050559160200191906001019061025056606060405236156100b95760e060020a6000350463013cf08b81146100bb578063237e9492146101285780633910682114610281578063400e3949146102995780635daf08ca146102a257806369bd34361461032f5780638160f0b5146103385780638da5cb5b146103415780639644fcbd14610353578063aa02a90f146103be578063b1050da5146103c7578063bcca1fd3146104b5578063d3c0715b146104dc578063eceb29451461058d578063f2fde38b1461067b575b005b61069c6004356004805482908110156100025790600052602060002090600a02016000506005810154815460018301546003840154600485015460068601546007870154600160a060020a03959095169750929560020194919360ff828116946101009093041692919089565b60408051602060248035600481810135601f81018590048502860185019096528585526107759581359591946044949293909201918190840183828082843750949650505050505050600060006004600050848154811015610002575090527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19e600a8402908101547f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b909101904210806101e65750600481015460ff165b8061026757508060000160009054906101000a9004600160a060020a03168160010160005054846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020816007016000505414155b8061027757506001546005820154105b1561109257610002565b61077560043560066020526000908152604090205481565b61077560055481565b61078760043560078054829081101561000257506000526003026000805160206111d18339815191528101547fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a820154600160a060020a0382169260a060020a90920460ff16917fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c689019084565b61077560025481565b61077560015481565b610830600054600160a060020a031681565b604080516020604435600481810135601f81018490048402850184019095528484526100b9948135946024803595939460649492939101918190840183828082843750949650505050505050600080548190600160a060020a03908116339091161461084d57610002565b61077560035481565b604080516020604435600481810135601f8101849004840285018401909552848452610775948135946024803595939460649492939101918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024909101945090925082915084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806104ab5750604081205460078054909190811015610002579082526003026000805160206111d1833981519152015460a060020a900460ff16155b15610ce557610002565b6100b960043560243560443560005433600160a060020a03908116911614610b1857610002565b604080516020604435600481810135601f810184900484028501840190955284845261077594813594602480359593946064949293910191819084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806105835750604081205460078054909190811015610002579082526003026000805160206111d18339815191520181505460a060020a900460ff16155b15610f1d57610002565b604080516020606435600481810135601f81018490048402850184019095528484526107759481359460248035956044359560849492019190819084018382808284375094965050505050505060006000600460005086815481101561000257908252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01815090508484846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005054149150610cdc565b6100b960043560005433600160a060020a03908116911614610f0857610002565b604051808a600160a060020a031681526020018981526020018060200188815260200187815260200186815260200185815260200184815260200183815260200182810382528981815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561075e5780601f106107335761010080835404028352916020019161075e565b820191906000526020600020905b81548152906001019060200180831161074157829003601f168201915b50509a505050505050505050505060405180910390f35b60408051918252519081900360200190f35b60408051600160a060020a038616815260208101859052606081018390526080918101828152845460026001821615610100026000190190911604928201839052909160a08301908590801561081e5780601f106107f35761010080835404028352916020019161081e565b820191906000526020600020905b81548152906001019060200180831161080157829003601f168201915b50509550505050505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b600160a060020a03851660009081526006602052604081205414156108a957604060002060078054918290556001820180825582801582901161095c5760030281600302836000526020600020918201910161095c9190610a4f565b600160a060020a03851660009081526006602052604090205460078054919350908390811015610002575060005250600381026000805160206111d183398151915201805474ff0000000000000000000000000000000000000000191660a060020a85021781555b60408051600160a060020a03871681526020810186905281517f27b022af4a8347100c7a041ce5ccf8e14d644ff05de696315196faae8cd50c9b929181900390910190a15050505050565b505050915081506080604051908101604052808681526020018581526020018481526020014281526020015060076000508381548110156100025790600052602060002090600302016000508151815460208481015160a060020a02600160a060020a03199290921690921774ff00000000000000000000000000000000000000001916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f90810183900482019491929190910190839010610ad357805160ff19168380011785555b50610b03929150610abb565b5050600060028201556001015b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610aa15750610a42565b601f016020900490600052602060002090810190610a4291905b80821115610acf5760008155600101610abb565b5090565b82800160010185558215610a36579182015b82811115610a36578251826000505591602001919060010190610ae5565b50506060919091015160029190910155610911565b600183905560028290556003819055604080518481526020810184905280820183905290517fa439d3fa452be5e0e1e24a8145e715f4fd8b9c08c96a42fd82a855a85e5d57de9181900360600190a1505050565b50508585846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005081905550600260005054603c024201816003016000508190555060008160040160006101000a81548160ff0219169083021790555060008160040160016101000a81548160ff02191690830217905550600081600501600050819055507f646fec02522b41e7125cfc859a64fd4f4cefd5dc3b6237ca0abe251ded1fa881828787876040518085815260200184600160a060020a03168152602001838152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015610cc45780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1600182016005555b50949350505050565b6004805460018101808355909190828015829011610d1c57600a0281600a028360005260206000209182019101610d1c9190610db8565b505060048054929450918491508110156100025790600052602060002090600a02016000508054600160a060020a031916871781556001818101879055855160028381018054600082815260209081902096975091959481161561010002600019011691909104601f90810182900484019391890190839010610ed857805160ff19168380011785555b50610b6c929150610abb565b50506001015b80821115610acf578054600160a060020a03191681556000600182810182905560028381018054848255909281161561010002600019011604601f819010610e9c57505b5060006003830181905560048301805461ffff191690556005830181905560068301819055600783018190556008830180548282559082526020909120610db2916002028101905b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610eba57505b5050600101610e44565b601f016020900490600052602060002090810190610dfc9190610abb565b601f016020900490600052602060002090810190610e929190610abb565b82800160010185558215610da6579182015b82811115610da6578251826000505591602001919060010190610eea565b60008054600160a060020a0319168217905550565b600480548690811015610002576000918252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01905033600160a060020a0316600090815260098201602052604090205490915060ff1660011415610f8457610002565b33600160a060020a031660009081526009820160205260409020805460ff1916600190811790915560058201805490910190558315610fcd576006810180546001019055610fda565b6006810180546000190190555b7fc34f869b7ff431b034b7b9aea9822dac189a685e0b015c7d1be3add3f89128e8858533866040518085815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561107a5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1509392505050565b6006810154600354901315611158578060000160009054906101000a9004600160a060020a0316600160a060020a03168160010160005054670de0b6b3a76400000284604051808280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156111225780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f15050505060048101805460ff191660011761ff00191661010017905561116d565b60048101805460ff191660011761ff00191690555b60068101546005820154600483015460408051888152602081019490945283810192909252610100900460ff166060830152517fd220b7272a8b6d0d7d6bcdace67b936a8f175e6d5c1b3ee438b72256b32ab3af9181900360800190a1509291505056a66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688`}, + nil, + nil, + }, + { + "InputChecker", + []string{` + [ + {"type":"function","name":"noInput","constant":true,"inputs":[],"outputs":[]}, + {"type":"function","name":"namedInput","constant":true,"inputs":[{"name":"str","type":"string"}],"outputs":[]}, + {"type":"function","name":"anonInput","constant":true,"inputs":[{"name":"","type":"string"}],"outputs":[]}, + {"type":"function","name":"namedInputs","constant":true,"inputs":[{"name":"str1","type":"string"},{"name":"str2","type":"string"}],"outputs":[]}, + {"type":"function","name":"anonInputs","constant":true,"inputs":[{"name":"","type":"string"},{"name":"","type":"string"}],"outputs":[]}, + {"type":"function","name":"mixedInputs","constant":true,"inputs":[{"name":"","type":"string"},{"name":"str","type":"string"}],"outputs":[]} + ] + `}, + []string{``}, + nil, + nil, + }, + { + "OutputChecker", + []string{` + [ + {"type":"function","name":"noOutput","constant":true,"inputs":[],"outputs":[]}, + {"type":"function","name":"namedOutput","constant":true,"inputs":[],"outputs":[{"name":"str","type":"string"}]}, + {"type":"function","name":"anonOutput","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"}]}, + {"type":"function","name":"namedOutputs","constant":true,"inputs":[],"outputs":[{"name":"str1","type":"string"},{"name":"str2","type":"string"}]}, + {"type":"function","name":"collidingOutputs","constant":true,"inputs":[],"outputs":[{"name":"str","type":"string"},{"name":"Str","type":"string"}]}, + {"type":"function","name":"anonOutputs","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"},{"name":"","type":"string"}]}, + {"type":"function","name":"mixedOutputs","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"},{"name":"str","type":"string"}]} + ] + `}, + []string{``}, + nil, + nil, + }, + { + "EventChecker", + []string{` + [ + {"type":"event","name":"empty","inputs":[]}, + {"type":"event","name":"indexed","inputs":[{"name":"addr","type":"address","indexed":true},{"name":"num","type":"int256","indexed":true}]}, + {"type":"event","name":"mixed","inputs":[{"name":"addr","type":"address","indexed":true},{"name":"num","type":"int256"}]}, + {"type":"event","name":"anonymous","anonymous":true,"inputs":[]}, + {"type":"event","name":"dynamic","inputs":[{"name":"idxStr","type":"string","indexed":true},{"name":"idxDat","type":"bytes","indexed":true},{"name":"str","type":"string"},{"name":"dat","type":"bytes"}]}, + {"type":"event","name":"unnamed","inputs":[{"name":"","type":"uint256","indexed": true},{"name":"","type":"uint256","indexed":true}]} + ] + `}, + []string{``}, + nil, + nil, + }, + { + "Interactor", + []string{`[{"constant":true,"inputs":[],"name":"transactString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":true,"inputs":[],"name":"deployString","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"str","type":"string"}],"name":"transact","outputs":[],"type":"function"},{"inputs":[{"name":"str","type":"string"}],"type":"constructor"}]`}, + []string{`6060604052604051610328380380610328833981016040528051018060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10608d57805160ff19168380011785555b50607c9291505b8082111560ba57838155600101606b565b50505061026a806100be6000396000f35b828001600101855582156064579182015b828111156064578251826000505591602001919060010190609e565b509056606060405260e060020a60003504630d86a0e181146100315780636874e8091461008d578063d736c513146100ea575b005b610190600180546020600282841615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102295780601f106101fe57610100808354040283529160200191610229565b61019060008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102295780601f106101fe57610100808354040283529160200191610229565b60206004803580820135601f81018490049093026080908101604052606084815261002f946024939192918401918190838280828437509496505050505050508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023157805160ff19168380011785555b506102619291505b808211156102665760008155830161017d565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156101f05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b820191906000526020600020905b81548152906001019060200180831161020c57829003601f168201915b505050505081565b82800160010185558215610175579182015b82811115610175578251826000505591602001919060010190610243565b505050565b509056`}, + nil, + nil, + }, + { + "Getter", + []string{`[{"constant":true,"inputs":[],"name":"getter","outputs":[{"name":"","type":"string"},{"name":"","type":"int256"},{"name":"","type":"bytes32"}],"type":"function"}]`}, + []string{`606060405260dc8060106000396000f3606060405260e060020a6000350463993a04b78114601a575b005b600060605260c0604052600260809081527f486900000000000000000000000000000000000000000000000000000000000060a05260017fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060e0829052610100819052606060c0908152600261012081905281906101409060a09080838184600060046012f1505081517fffff000000000000000000000000000000000000000000000000000000000000169091525050604051610160819003945092505050f3`}, + nil, + nil, + }, + { + "Tupler", + []string{`[{"constant":true,"inputs":[],"name":"tuple","outputs":[{"name":"a","type":"string"},{"name":"b","type":"int256"},{"name":"c","type":"bytes32"}],"type":"function"}]`}, + []string{`606060405260dc8060106000396000f3606060405260e060020a60003504633175aae28114601a575b005b600060605260c0604052600260809081527f486900000000000000000000000000000000000000000000000000000000000060a05260017fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060e0829052610100819052606060c0908152600261012081905281906101409060a09080838184600060046012f1505081517fffff000000000000000000000000000000000000000000000000000000000000169091525050604051610160819003945092505050f3`}, + nil, + nil, + }, + { + "Slicer", + []string{`[{"constant":true,"inputs":[{"name":"input","type":"address[]"}],"name":"echoAddresses","outputs":[{"name":"output","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"uint24[23]"}],"name":"echoFancyInts","outputs":[{"name":"output","type":"uint24[23]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"int256[]"}],"name":"echoInts","outputs":[{"name":"output","type":"int256[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"bool[]"}],"name":"echoBools","outputs":[{"name":"output","type":"bool[]"}],"type":"function"}]`}, + []string{`606060405261015c806100126000396000f3606060405260e060020a6000350463be1127a3811461003c578063d88becc014610092578063e15a3db71461003c578063f637e5891461003c575b005b604080516020600480358082013583810285810185019096528085526100ee959294602494909392850192829185019084908082843750949650505050505050604080516020810190915260009052805b919050565b604080516102e0818101909252610138916004916102e491839060179083908390808284375090955050505050506102e0604051908101604052806017905b60008152602001906001900390816100d15790505081905061008d565b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019250505060405180910390f35b60405180826102e0808381846000600461015cf15090500191505060405180910390f3`}, + nil, + nil, + }, + { + "Structs", + []string{`[{"inputs":[],"name":"F","outputs":[{"components":[{"internalType":"bytes32","name":"B","type":"bytes32"}],"internalType":"structStructs.A[]","name":"a","type":"tuple[]"},{"internalType":"uint256[]","name":"c","type":"uint256[]"},{"internalType":"bool[]","name":"d","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"G","outputs":[{"components":[{"internalType":"bytes32","name":"B","type":"bytes32"}],"internalType":"structStructs.A[]","name":"a","type":"tuple[]"}],"stateMutability":"view","type":"function"}]`}, + []string{`608060405234801561001057600080fd5b50610278806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806328811f591461003b5780636fecb6231461005b575b600080fd5b610043610070565b604051610052939291906101a0565b60405180910390f35b6100636100d6565b6040516100529190610186565b604080516002808252606082810190935282918291829190816020015b610095610131565b81526020019060019003908161008d575050805190915061026960611b9082906000906100be57fe5b60209081029190910101515293606093508392509050565b6040805160028082526060828101909352829190816020015b6100f7610131565b8152602001906001900390816100ef575050805190915061026960611b90829060009061012057fe5b602090810291909101015152905090565b60408051602081019091526000815290565b815260200190565b6000815180845260208085019450808401835b8381101561017b578151518752958201959082019060010161015e565b509495945050505050565b600060208252610199602083018461014b565b9392505050565b6000606082526101b3606083018661014b565b6020838203818501528186516101c98185610239565b91508288019350845b818110156101f3576101e5838651610143565b9484019492506001016101d2565b505084810360408601528551808252908201925081860190845b8181101561022b57825115158552938301939183019160010161020d565b509298975050505050505050565b9081526020019056fea2646970667358221220eb85327e285def14230424c52893aebecec1e387a50bb6b75fc4fdbed647f45f64736f6c63430006050033`}, + nil, + nil, + }, + { + `Underscorer`, + []string{`[{"constant":true,"inputs":[],"name":"LowerUpperCollision","outputs":[{"name":"_res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_under_scored_func","outputs":[{"name":"_int","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UnderscoredOutput","outputs":[{"name":"_int","type":"int256"},{"name":"_string","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperLowerCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"AllPurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"__","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperUpperCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"LowerLowerCollision","outputs":[{"name":"_res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"}]`}, []string{`6060604052341561000f57600080fd5b6103858061001e6000396000f30060606040526004361061008e576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806303a592131461009357806346546dbe146100c357806367e6633d146100ec5780639df4848514610181578063af7486ab146101b1578063b564b34d146101e1578063e02ab24d14610211578063e409ca4514610241575b600080fd5b341561009e57600080fd5b6100a6610271565b604051808381526020018281526020019250505060405180910390f35b34156100ce57600080fd5b6100d6610286565b6040518082815260200191505060405180910390f35b34156100f757600080fd5b6100ff61028e565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561014557808201518184015260208101905061012a565b50505050905090810190601f1680156101725780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561018c57600080fd5b6101946102dc565b604051808381526020018281526020019250505060405180910390f35b34156101bc57600080fd5b6101c46102f1565b604051808381526020018281526020019250505060405180910390f35b34156101ec57600080fd5b6101f4610306565b604051808381526020018281526020019250505060405180910390f35b341561021c57600080fd5b61022461031b565b604051808381526020018281526020019250505060405180910390f35b341561024c57600080fd5b610254610330565b604051808381526020018281526020019250505060405180910390f35b60008060016002819150809050915091509091565b600080905090565b6000610298610345565b61013a8090506040805190810160405280600281526020017f7069000000000000000000000000000000000000000000000000000000000000815250915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b6020604051908101604052806000815250905600a165627a7a72305820d1a53d9de9d1e3d55cb3dc591900b63c4f1ded79114f7b79b332684840e186a40029`}, + nil, + nil, + }, + { + `DeeplyNestedArray`, + []string{`[{"constant":false,"inputs":[{"name":"arr","type":"uint64[3][4][5]"}],"name":"storeDeepUintArray","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"retrieveDeepArray","outputs":[{"name":"","type":"uint64[3][4][5]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"deepUint64Array","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"}]`}, + []string{`6060604052341561000f57600080fd5b6106438061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063344248551461005c5780638ed4573a1461011457806398ed1856146101ab575b600080fd5b341561006757600080fd5b610112600480806107800190600580602002604051908101604052809291906000905b828210156101055783826101800201600480602002604051908101604052809291906000905b828210156100f25783826060020160038060200260405190810160405280929190826003602002808284378201915050505050815260200190600101906100b0565b505050508152602001906001019061008a565b5050505091905050610208565b005b341561011f57600080fd5b61012761021d565b604051808260056000925b8184101561019b578284602002015160046000925b8184101561018d5782846020020151600360200280838360005b8381101561017c578082015181840152602081019050610161565b505050509050019260010192610147565b925050509260010192610132565b9250505091505060405180910390f35b34156101b657600080fd5b6101de6004808035906020019091908035906020019091908035906020019091905050610309565b604051808267ffffffffffffffff1667ffffffffffffffff16815260200191505060405180910390f35b80600090600561021992919061035f565b5050565b6102256103b0565b6000600580602002604051908101604052809291906000905b8282101561030057838260040201600480602002604051908101604052809291906000905b828210156102ed578382016003806020026040519081016040528092919082600380156102d9576020028201916000905b82829054906101000a900467ffffffffffffffff1667ffffffffffffffff16815260200190600801906020826007010492830192600103820291508084116102945790505b505050505081526020019060010190610263565b505050508152602001906001019061023e565b50505050905090565b60008360058110151561031857fe5b600402018260048110151561032957fe5b018160038110151561033757fe5b6004918282040191900660080292509250509054906101000a900467ffffffffffffffff1681565b826005600402810192821561039f579160200282015b8281111561039e5782518290600461038e9291906103df565b5091602001919060040190610375565b5b5090506103ac919061042d565b5090565b610780604051908101604052806005905b6103c9610459565b8152602001906001900390816103c15790505090565b826004810192821561041c579160200282015b8281111561041b5782518290600361040b929190610488565b50916020019190600101906103f2565b5b5090506104299190610536565b5090565b61045691905b8082111561045257600081816104499190610562565b50600401610433565b5090565b90565b610180604051908101604052806004905b6104726105a7565b81526020019060019003908161046a5790505090565b82600380016004900481019282156105255791602002820160005b838211156104ef57835183826101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555092602001926008016020816007010492830192600103026104a3565b80156105235782816101000a81549067ffffffffffffffff02191690556008016020816007010492830192600103026104ef565b505b50905061053291906105d9565b5090565b61055f91905b8082111561055b57600081816105529190610610565b5060010161053c565b5090565b90565b50600081816105719190610610565b50600101600081816105839190610610565b50600101600081816105959190610610565b5060010160006105a59190610610565b565b6060604051908101604052806003905b600067ffffffffffffffff168152602001906001900390816105b75790505090565b61060d91905b8082111561060957600081816101000a81549067ffffffffffffffff0219169055506001016105df565b5090565b90565b50600090555600a165627a7a7230582087e5a43f6965ab6ef7a4ff056ab80ed78fd8c15cff57715a1bf34ec76a93661c0029`}, + nil, + nil, + }, + { + `CallbackParam`, + []string{`[ + { + "constant": false, + "inputs": [ + { + "name": "callback", + "type": "function" + } + ], + "name": "test", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ]`}, + []string{`608060405234801561001057600080fd5b5061015e806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063d7a5aba214610040575b600080fd5b34801561004c57600080fd5b506100be6004803603602081101561006357600080fd5b810190808035806c0100000000000000000000000090049068010000000000000000900463ffffffff1677ffffffffffffffffffffffffffffffffffffffffffffffff169091602001919093929190939291905050506100c0565b005b818160016040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561011657600080fd5b505af115801561012a573d6000803e3d6000fd5b50505050505056fea165627a7a7230582062f87455ff84be90896dbb0c4e4ddb505c600d23089f8e80a512548440d7e2580029`}, + nil, + nil, + }, + { + `Tuple`, + []string{` +[{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256[]","name":"b","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[]","name":"c","type":"tuple[]"}],"indexed":false,"internalType":"struct Tuple.S","name":"a","type":"tuple"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"indexed":false,"internalType":"struct Tuple.T[2][]","name":"b","type":"tuple[2][]"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"indexed":false,"internalType":"struct Tuple.T[][2]","name":"c","type":"tuple[][2]"},{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256[]","name":"b","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[]","name":"c","type":"tuple[]"}],"indexed":false,"internalType":"struct Tuple.S[]","name":"d","type":"tuple[]"},{"indexed":false,"internalType":"uint256[]","name":"e","type":"uint256[]"}],"name":"TupleEvent","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint8","name":"x","type":"uint8"},{"internalType":"uint8","name":"y","type":"uint8"}],"indexed":false,"internalType":"struct Tuple.P[]","name":"","type":"tuple[]"}],"name":"TupleEvent2","type":"event"},{"constant":true,"inputs":[{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256[]","name":"b","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[]","name":"c","type":"tuple[]"}],"internalType":"struct Tuple.S","name":"a","type":"tuple"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[2][]","name":"b","type":"tuple[2][]"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[][2]","name":"c","type":"tuple[][2]"},{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256[]","name":"b","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[]","name":"c","type":"tuple[]"}],"internalType":"struct Tuple.S[]","name":"d","type":"tuple[]"},{"internalType":"uint256[]","name":"e","type":"uint256[]"}],"name":"func1","outputs":[{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256[]","name":"b","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[]","name":"c","type":"tuple[]"}],"internalType":"struct Tuple.S","name":"","type":"tuple"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[2][]","name":"","type":"tuple[2][]"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[][2]","name":"","type":"tuple[][2]"},{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256[]","name":"b","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[]","name":"c","type":"tuple[]"}],"internalType":"struct Tuple.S[]","name":"","type":"tuple[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256[]","name":"b","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[]","name":"c","type":"tuple[]"}],"internalType":"struct Tuple.S","name":"a","type":"tuple"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[2][]","name":"b","type":"tuple[2][]"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[][2]","name":"c","type":"tuple[][2]"},{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256[]","name":"b","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct Tuple.T[]","name":"c","type":"tuple[]"}],"internalType":"struct Tuple.S[]","name":"d","type":"tuple[]"},{"internalType":"uint256[]","name":"e","type":"uint256[]"}],"name":"func2","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"uint16","name":"x","type":"uint16"},{"internalType":"uint16","name":"y","type":"uint16"}],"internalType":"struct Tuple.Q[]","name":"","type":"tuple[]"}],"name":"func3","outputs":[],"payable":false,"stateMutability":"pure","type":"function"}]`}, + []string{`60806040523480156100115760006000fd5b50610017565b6110b2806100266000396000f3fe60806040523480156100115760006000fd5b50600436106100465760003560e01c8063443c79b41461004c578063d0062cdd14610080578063e4d9a43b1461009c57610046565b60006000fd5b610066600480360361006191908101906107b8565b6100b8565b604051610077959493929190610ccb565b60405180910390f35b61009a600480360361009591908101906107b8565b6100ef565b005b6100b660048036036100b19190810190610775565b610136565b005b6100c061013a565b60606100ca61015e565b606060608989898989945094509450945094506100e2565b9550955095509550959050565b7f18d6e66efa53739ca6d13626f35ebc700b31cced3eddb50c70bbe9c082c6cd008585858585604051610126959493929190610ccb565b60405180910390a15b5050505050565b5b50565b60405180606001604052806000815260200160608152602001606081526020015090565b60405180604001604052806002905b606081526020019060019003908161016d57905050905661106e565b600082601f830112151561019d5760006000fd5b81356101b06101ab82610d6f565b610d41565b915081818352602084019350602081019050838560808402820111156101d65760006000fd5b60005b8381101561020757816101ec888261037a565b8452602084019350608083019250505b6001810190506101d9565b5050505092915050565b600082601f83011215156102255760006000fd5b600261023861023382610d98565b610d41565b9150818360005b83811015610270578135860161025588826103f3565b8452602084019350602083019250505b60018101905061023f565b5050505092915050565b600082601f830112151561028e5760006000fd5b81356102a161029c82610dbb565b610d41565b915081818352602084019350602081019050838560408402820111156102c75760006000fd5b60005b838110156102f857816102dd888261058b565b8452602084019350604083019250505b6001810190506102ca565b5050505092915050565b600082601f83011215156103165760006000fd5b813561032961032482610de4565b610d41565b9150818183526020840193506020810190508360005b83811015610370578135860161035588826105d8565b8452602084019350602083019250505b60018101905061033f565b5050505092915050565b600082601f830112151561038e5760006000fd5b60026103a161039c82610e0d565b610d41565b915081838560408402820111156103b85760006000fd5b60005b838110156103e957816103ce88826106fe565b8452602084019350604083019250505b6001810190506103bb565b5050505092915050565b600082601f83011215156104075760006000fd5b813561041a61041582610e30565b610d41565b915081818352602084019350602081019050838560408402820111156104405760006000fd5b60005b83811015610471578161045688826106fe565b8452602084019350604083019250505b600181019050610443565b5050505092915050565b600082601f830112151561048f5760006000fd5b81356104a261049d82610e59565b610d41565b915081818352602084019350602081019050838560208402820111156104c85760006000fd5b60005b838110156104f957816104de8882610760565b8452602084019350602083019250505b6001810190506104cb565b5050505092915050565b600082601f83011215156105175760006000fd5b813561052a61052582610e82565b610d41565b915081818352602084019350602081019050838560208402820111156105505760006000fd5b60005b8381101561058157816105668882610760565b8452602084019350602083019250505b600181019050610553565b5050505092915050565b60006040828403121561059e5760006000fd5b6105a86040610d41565b905060006105b88482850161074b565b60008301525060206105cc8482850161074b565b60208301525092915050565b6000606082840312156105eb5760006000fd5b6105f56060610d41565b9050600061060584828501610760565b600083015250602082013567ffffffffffffffff8111156106265760006000fd5b6106328482850161047b565b602083015250604082013567ffffffffffffffff8111156106535760006000fd5b61065f848285016103f3565b60408301525092915050565b60006060828403121561067e5760006000fd5b6106886060610d41565b9050600061069884828501610760565b600083015250602082013567ffffffffffffffff8111156106b95760006000fd5b6106c58482850161047b565b602083015250604082013567ffffffffffffffff8111156106e65760006000fd5b6106f2848285016103f3565b60408301525092915050565b6000604082840312156107115760006000fd5b61071b6040610d41565b9050600061072b84828501610760565b600083015250602061073f84828501610760565b60208301525092915050565b60008135905061075a8161103a565b92915050565b60008135905061076f81611054565b92915050565b6000602082840312156107885760006000fd5b600082013567ffffffffffffffff8111156107a35760006000fd5b6107af8482850161027a565b91505092915050565b6000600060006000600060a086880312156107d35760006000fd5b600086013567ffffffffffffffff8111156107ee5760006000fd5b6107fa8882890161066b565b955050602086013567ffffffffffffffff8111156108185760006000fd5b61082488828901610189565b945050604086013567ffffffffffffffff8111156108425760006000fd5b61084e88828901610211565b935050606086013567ffffffffffffffff81111561086c5760006000fd5b61087888828901610302565b925050608086013567ffffffffffffffff8111156108965760006000fd5b6108a288828901610503565b9150509295509295909350565b60006108bb8383610a6a565b60808301905092915050565b60006108d38383610ac2565b905092915050565b60006108e78383610c36565b905092915050565b60006108fb8383610c8d565b60408301905092915050565b60006109138383610cbc565b60208301905092915050565b600061092a82610f0f565b6109348185610fb7565b935061093f83610eab565b8060005b8381101561097157815161095788826108af565b975061096283610f5c565b9250505b600181019050610943565b5085935050505092915050565b600061098982610f1a565b6109938185610fc8565b9350836020820285016109a585610ebb565b8060005b858110156109e257848403895281516109c285826108c7565b94506109cd83610f69565b925060208a019950505b6001810190506109a9565b50829750879550505050505092915050565b60006109ff82610f25565b610a098185610fd3565b935083602082028501610a1b85610ec5565b8060005b85811015610a585784840389528151610a3885826108db565b9450610a4383610f76565b925060208a019950505b600181019050610a1f565b50829750879550505050505092915050565b610a7381610f30565b610a7d8184610fe4565b9250610a8882610ed5565b8060005b83811015610aba578151610aa087826108ef565b9650610aab83610f83565b9250505b600181019050610a8c565b505050505050565b6000610acd82610f3b565b610ad78185610fef565b9350610ae283610edf565b8060005b83811015610b14578151610afa88826108ef565b9750610b0583610f90565b9250505b600181019050610ae6565b5085935050505092915050565b6000610b2c82610f51565b610b368185611011565b9350610b4183610eff565b8060005b83811015610b73578151610b598882610907565b9750610b6483610faa565b9250505b600181019050610b45565b5085935050505092915050565b6000610b8b82610f46565b610b958185611000565b9350610ba083610eef565b8060005b83811015610bd2578151610bb88882610907565b9750610bc383610f9d565b9250505b600181019050610ba4565b5085935050505092915050565b6000606083016000830151610bf76000860182610cbc565b5060208301518482036020860152610c0f8282610b80565b91505060408301518482036040860152610c298282610ac2565b9150508091505092915050565b6000606083016000830151610c4e6000860182610cbc565b5060208301518482036020860152610c668282610b80565b91505060408301518482036040860152610c808282610ac2565b9150508091505092915050565b604082016000820151610ca36000850182610cbc565b506020820151610cb66020850182610cbc565b50505050565b610cc581611030565b82525050565b600060a0820190508181036000830152610ce58188610bdf565b90508181036020830152610cf9818761091f565b90508181036040830152610d0d818661097e565b90508181036060830152610d2181856109f4565b90508181036080830152610d358184610b21565b90509695505050505050565b6000604051905081810181811067ffffffffffffffff82111715610d655760006000fd5b8060405250919050565b600067ffffffffffffffff821115610d875760006000fd5b602082029050602081019050919050565b600067ffffffffffffffff821115610db05760006000fd5b602082029050919050565b600067ffffffffffffffff821115610dd35760006000fd5b602082029050602081019050919050565b600067ffffffffffffffff821115610dfc5760006000fd5b602082029050602081019050919050565b600067ffffffffffffffff821115610e255760006000fd5b602082029050919050565b600067ffffffffffffffff821115610e485760006000fd5b602082029050602081019050919050565b600067ffffffffffffffff821115610e715760006000fd5b602082029050602081019050919050565b600067ffffffffffffffff821115610e9a5760006000fd5b602082029050602081019050919050565b6000819050602082019050919050565b6000819050919050565b6000819050602082019050919050565b6000819050919050565b6000819050602082019050919050565b6000819050602082019050919050565b6000819050602082019050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600061ffff82169050919050565b6000819050919050565b61104381611022565b811415156110515760006000fd5b50565b61105d81611030565b8114151561106b5760006000fd5b50565bfea365627a7a72315820d78c6ba7ee332581e6c4d9daa5fc07941841230f7ce49edf6e05b1b63853e8746c6578706572696d656e74616cf564736f6c634300050c0040`}, + nil, + nil, + }, + { + "Overload", + []string{`[{"constant":false,"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"i","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"}],"name":"bar","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"},{"indexed":false,"name":"j","type":"uint256"}],"name":"bar","type":"event"}]`}, + []string{`608060405234801561001057600080fd5b50610153806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806304bc52f81461003b5780632fbebd3814610073575b600080fd5b6100716004803603604081101561005157600080fd5b8101908080359060200190929190803590602001909291905050506100a1565b005b61009f6004803603602081101561008957600080fd5b81019080803590602001909291905050506100e4565b005b7fae42e9514233792a47a1e4554624e83fe852228e1503f63cd383e8a431f4f46d8282604051808381526020018281526020019250505060405180910390a15050565b7f0423a1321222a0a8716c22b92fac42d85a45a612b696a461784d9fa537c81e5c816040518082815260200191505060405180910390a15056fea265627a7a72305820e22b049858b33291cbe67eeaece0c5f64333e439d27032ea8337d08b1de18fe864736f6c634300050a0032`}, + nil, + nil, + }, + { + "IdentifierCollision", + []string{`[{"constant":true,"inputs":[],"name":"MyVar","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_myVar","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]`}, + []string{"60806040523480156100115760006000fd5b50610017565b60c3806100256000396000f3fe608060405234801560105760006000fd5b506004361060365760003560e01c806301ad4d8714603c5780634ef1f0ad146058576036565b60006000fd5b60426074565b6040518082815260200191505060405180910390f35b605e607d565b6040518082815260200191505060405180910390f35b60006000505481565b60006000600050549050608b565b9056fea265627a7a7231582067c8d84688b01c4754ba40a2a871cede94ea1f28b5981593ab2a45b46ac43af664736f6c634300050c0032"}, + nil, + map[string]string{"_myVar": "pubVar"}, // alias MyVar to PubVar,\ + }, + { + "NameConflict", + []string{`[ { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "int256", "name": "msg", "type": "int256" }, { "indexed": false, "internalType": "int256", "name": "_msg", "type": "int256" } ], "name": "log", "type": "event" }, { "inputs": [ { "components": [ { "internalType": "bytes", "name": "data", "type": "bytes" }, { "internalType": "bytes", "name": "_data", "type": "bytes" } ], "internalType": "struct oracle.request", "name": "req", "type": "tuple" } ], "name": "addRequest", "outputs": [], "stateMutability": "pure", "type": "function" }, { "inputs": [], "name": "getRequest", "outputs": [ { "components": [ { "internalType": "bytes", "name": "data", "type": "bytes" }, { "internalType": "bytes", "name": "_data", "type": "bytes" } ], "internalType": "struct oracle.request", "name": "", "type": "tuple" } ], "stateMutability": "pure", "type": "function" } ]`}, + []string{"0x608060405234801561001057600080fd5b5061042b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063c2bb515f1461003b578063cce7b04814610059575b600080fd5b610043610075565b60405161005091906101af565b60405180910390f35b610073600480360381019061006e91906103ac565b6100b5565b005b61007d6100b8565b604051806040016040528060405180602001604052806000815250815260200160405180602001604052806000815250815250905090565b50565b604051806040016040528060608152602001606081525090565b600081519050919050565b600082825260208201905092915050565b60005b8381101561010c5780820151818401526020810190506100f1565b8381111561011b576000848401525b50505050565b6000601f19601f8301169050919050565b600061013d826100d2565b61014781856100dd565b93506101578185602086016100ee565b61016081610121565b840191505092915050565b600060408301600083015184820360008601526101888282610132565b915050602083015184820360208601526101a28282610132565b9150508091505092915050565b600060208201905081810360008301526101c9818461016b565b905092915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61022282610121565b810181811067ffffffffffffffff82111715610241576102406101ea565b5b80604052505050565b60006102546101d1565b90506102608282610219565b919050565b600080fd5b600080fd5b600080fd5b600067ffffffffffffffff82111561028f5761028e6101ea565b5b61029882610121565b9050602081019050919050565b82818337600083830152505050565b60006102c76102c284610274565b61024a565b9050828152602081018484840111156102e3576102e261026f565b5b6102ee8482856102a5565b509392505050565b600082601f83011261030b5761030a61026a565b5b813561031b8482602086016102b4565b91505092915050565b60006040828403121561033a576103396101e5565b5b610344604061024a565b9050600082013567ffffffffffffffff81111561036457610363610265565b5b610370848285016102f6565b600083015250602082013567ffffffffffffffff81111561039457610393610265565b5b6103a0848285016102f6565b60208301525092915050565b6000602082840312156103c2576103c16101db565b5b600082013567ffffffffffffffff8111156103e0576103df6101e0565b5b6103ec84828501610324565b9150509291505056fea264697066735822122033bca1606af9b6aeba1673f98c52003cec19338539fb44b86690ce82c51483b564736f6c634300080e0033"}, + nil, + nil, + }, + { + "RangeKeyword", + []string{`[{"inputs":[{"internalType":"uint256","name":"range","type":"uint256"}],"name":"functionWithKeywordParameter","outputs":[],"stateMutability":"pure","type":"function"}]`}, + []string{"0x608060405234801561001057600080fd5b5060dc8061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063527a119f14602d575b600080fd5b60436004803603810190603f9190605b565b6045565b005b50565b6000813590506055816092565b92915050565b600060208284031215606e57606d608d565b5b6000607a848285016048565b91505092915050565b6000819050919050565b600080fd5b6099816083565b811460a357600080fd5b5056fea2646970667358221220d4f4525e2615516394055d369fb17df41c359e5e962734f27fd683ea81fd9db164736f6c63430008070033"}, + nil, + nil, + }, + { + "NumericMethodName", + []string{`[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_param","type":"address"}],"name":"_1TestEvent","type":"event"},{"inputs":[],"name":"_1test","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"__1test","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"__2test","outputs":[],"stateMutability":"pure","type":"function"}]`}, + []string{"0x6080604052348015600f57600080fd5b5060958061001e6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80639d993132146041578063d02767c7146049578063ffa02795146051575b600080fd5b60476059565b005b604f605b565b005b6057605d565b005b565b565b56fea26469706673582212200382ca602dff96a7e2ba54657985e2b4ac423a56abe4a1f0667bc635c4d4371f64736f6c63430008110033"}, + nil, + nil, + }, + { + "Structs", + []string{`[{"inputs":[],"name":"F","outputs":[{"components":[{"internalType":"bytes32","name":"B","type":"bytes32"}],"internalType":"structStructs.A[]","name":"a","type":"tuple[]"},{"internalType":"uint256[]","name":"c","type":"uint256[]"},{"internalType":"bool[]","name":"d","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"G","outputs":[{"components":[{"internalType":"bytes32","name":"B","type":"bytes32"}],"internalType":"structStructs.A[]","name":"a","type":"tuple[]"}],"stateMutability":"view","type":"function"}]`}, + []string{`608060405234801561001057600080fd5b50610278806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806328811f591461003b5780636fecb6231461005b575b600080fd5b610043610070565b604051610052939291906101a0565b60405180910390f35b6100636100d6565b6040516100529190610186565b604080516002808252606082810190935282918291829190816020015b610095610131565b81526020019060019003908161008d575050805190915061026960611b9082906000906100be57fe5b60209081029190910101515293606093508392509050565b6040805160028082526060828101909352829190816020015b6100f7610131565b8152602001906001900390816100ef575050805190915061026960611b90829060009061012057fe5b602090810291909101015152905090565b60408051602081019091526000815290565b815260200190565b6000815180845260208085019450808401835b8381101561017b578151518752958201959082019060010161015e565b509495945050505050565b600060208252610199602083018461014b565b9392505050565b6000606082526101b3606083018661014b565b6020838203818501528186516101c98185610239565b91508288019350845b818110156101f3576101e5838651610143565b9484019492506001016101d2565b505084810360408601528551808252908201925081860190845b8181101561022b57825115158552938301939183019160010161020d565b509298975050505050505050565b9081526020019056fea2646970667358221220eb85327e285def14230424c52893aebecec1e387a50bb6b75fc4fdbed647f45f64736f6c63430006050033`}, + nil, + nil, + }, +} + +// TestBindingV2ConvertedV1Tests regenerates contracts from the v1 binding test +// cases (using v2 binding mode) and ensures that no mutations occurred compared +// to the expected output included under testdata/v2. +func TestBindingV2ConvertedV1Tests(t *testing.T) { + for _, tc := range combinedJSONBindTestsV2 { + fname := fmt.Sprintf("testdata/v2/%v.go.txt", strings.ToLower(tc.name)) + t.Run(tc.name, func(t *testing.T) { + if tc.types == nil { + tc.types = []string{tc.name} + } + have, err := bindCombinedJSON(&tc) + if err != nil { + t.Fatalf("got error from bindCombinedJSON: %v", err) + } + // Set this environment variable to regenerate the test outputs. + if os.Getenv("WRITE_TEST_FILES") != "" { + if err := os.WriteFile((fname), []byte(have), 0666); err != nil { + t.Fatalf("err writing expected output to file: %v\n", err) + } + } + // Verify the generated code + want, err := os.ReadFile(fname) + if err != nil { + t.Fatalf("failed to read file %v", fname) + } + if have != string(want) { + t.Fatalf("wrong output: %v", prettyDiff(have, string(want))) + } + }) + } +} + +func TestNormalizeArgs(t *testing.T) { + type normalizeArgsTc struct { + inp []string + expected []string + } + for i, tc := range []normalizeArgsTc{ + {[]string{"arg1", "arg1"}, []string{"arg1", "arg10"}}, + {[]string{"", ""}, []string{"arg0", "arg1"}}, + {[]string{"var", "const"}, []string{"arg0", "arg1"}}, + {[]string{"_res", "Res"}, []string{"res", "res0"}}, + {[]string{"_", "__"}, []string{"arg0", "arg1"}}} { + var inpArgs abi.Arguments + for _, inpArgName := range tc.inp { + inpArgs = append(inpArgs, abi.Argument{ + Name: inpArgName, + }) + } + res := normalizeArgs(inpArgs) + for j, resArg := range res { + if resArg.Name != tc.expected[j] { + t.Fatalf("mismatch for test index %d, arg index %d: expected %v. got %v", i, j, tc.expected[j], resArg.Name) + } + } + } +} + +// returns a "pretty diff" on two strings. Useful if the strings are large. +func prettyDiff(have, want string) string { + if have == want { + return "" + } + var i = 0 + for ; i < len(want) && i < len(have); i++ { + if want[i] != have[i] { + break + } + } + s := max(0, i-50) + he := min(len(have), i+50) + we := min(len(want), i+50) + return fmt.Sprintf("diff after %d characters\nhave: ...%q...\nwant: ...%q...\n", + i, have[s:he], want[s:we]) +} diff --git a/accounts/abi/bind/source.go.tpl b/accounts/abi/abigen/source.go.tpl similarity index 100% rename from accounts/abi/bind/source.go.tpl rename to accounts/abi/abigen/source.go.tpl diff --git a/accounts/abi/abigen/source2.go.tpl b/accounts/abi/abigen/source2.go.tpl new file mode 100644 index 00000000000..8f9d4d4103e --- /dev/null +++ b/accounts/abi/abigen/source2.go.tpl @@ -0,0 +1,238 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package {{.Package}} + +import ( + "bytes" + "math/big" + "errors" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +{{$structs := .Structs}} +{{range $structs}} + // {{.Name}} is an auto generated low-level Go binding around an user-defined struct. + type {{.Name}} struct { + {{range $field := .Fields}} + {{capitalise $field.Name}} {{$field.Type}}{{end}} + } +{{end}} + +{{range $contract := .Contracts}} + // {{.Type}}MetaData contains all meta data concerning the {{.Type}} contract. + var {{.Type}}MetaData = bind.MetaData{ + ABI: "{{.InputABI}}", + {{if (index $.Libraries .Type) -}} + ID: "{{index $.Libraries .Type}}", + {{ else -}} + ID: "{{.Type}}", + {{end -}} + {{if .InputBin -}} + Bin: "0x{{.InputBin}}", + {{end -}} + {{if .Libraries -}} + Deps: []*bind.MetaData{ + {{- range $name, $pattern := .Libraries}} + &{{$name}}MetaData, + {{- end}} + }, + {{end}} + } + + // {{.Type}} is an auto generated Go binding around an Ethereum contract. + type {{.Type}} struct { + abi abi.ABI + } + + // New{{.Type}} creates a new instance of {{.Type}}. + func New{{.Type}}() *{{.Type}} { + parsed, err := {{.Type}}MetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &{{.Type}}{abi: *parsed} + } + + // Instance creates a wrapper for a deployed contract instance at the given address. + // Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. + func (c *{{.Type}}) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) + } + + {{ if .Constructor.Inputs }} + // PackConstructor is the Go binding used to pack the parameters required for + // contract deployment. + // + // Solidity: {{.Constructor.String}} + func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) PackConstructor({{range .Constructor.Inputs}} {{.Name}} {{bindtype .Type $structs}}, {{end}}) []byte { + enc, err := {{ decapitalise $contract.Type}}.abi.Pack("" {{range .Constructor.Inputs}}, {{.Name}}{{end}}) + if err != nil { + panic(err) + } + return enc + } + {{ end }} + + {{range .Calls}} + // Pack{{.Normalized.Name}} is the Go binding used to pack the parameters required for calling + // the contract method with ID 0x{{printf "%x" .Original.ID}}. + // + // Solidity: {{.Original.String}} + func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) Pack{{.Normalized.Name}}({{range .Normalized.Inputs}} {{.Name}} {{bindtype .Type $structs}}, {{end}}) []byte { + enc, err := {{ decapitalise $contract.Type}}.abi.Pack("{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}}) + if err != nil { + panic(err) + } + return enc + } + + {{/* Unpack method is needed only when there are return args */}} + {{if .Normalized.Outputs }} + {{ if .Structured }} + // {{.Normalized.Name}}Output serves as a container for the return parameters of contract + // method {{ .Normalized.Name }}. + type {{.Normalized.Name}}Output struct { + {{range .Normalized.Outputs}} + {{capitalise .Name}} {{bindtype .Type $structs}}{{end}} + } + {{ end }} + + // Unpack{{.Normalized.Name}} is the Go binding that unpacks the parameters returned + // from invoking the contract method with ID 0x{{printf "%x" .Original.ID}}. + // + // Solidity: {{.Original.String}} + func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) Unpack{{.Normalized.Name}}(data []byte) ( + {{- if .Structured}} {{.Normalized.Name}}Output,{{else}} + {{- range .Normalized.Outputs}} {{bindtype .Type $structs}},{{- end }} + {{- end }} error) { + out, err := {{ decapitalise $contract.Type}}.abi.Unpack("{{.Original.Name}}", data) + {{- if .Structured}} + outstruct := new({{.Normalized.Name}}Output) + if err != nil { + return *outstruct, err + } + {{- range $i, $t := .Normalized.Outputs}} + {{- if ispointertype .Type}} + outstruct.{{capitalise .Name}} = abi.ConvertType(out[{{$i}}], new({{underlyingbindtype .Type }})).({{bindtype .Type $structs}}) + {{- else }} + outstruct.{{capitalise .Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}) + {{- end }} + {{- end }} + return *outstruct, err + {{else}} + if err != nil { + return {{range $i, $_ := .Normalized.Outputs}}{{if ispointertype .Type}}new({{underlyingbindtype .Type }}), {{else}}*new({{bindtype .Type $structs}}), {{end}}{{end}} err + } + {{- range $i, $t := .Normalized.Outputs}} + {{- if ispointertype .Type }} + out{{$i}} := abi.ConvertType(out[{{$i}}], new({{underlyingbindtype .Type}})).({{bindtype .Type $structs}}) + {{- else }} + out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}) + {{- end }} + {{- end}} + return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err + {{- end}} + } + {{end}} + {{end}} + + {{range .Events}} + // {{$contract.Type}}{{.Normalized.Name}} represents a {{.Original.Name}} event raised by the {{$contract.Type}} contract. + type {{$contract.Type}}{{.Normalized.Name}} struct { + {{- range .Normalized.Inputs}} + {{ capitalise .Name}} + {{- if .Indexed}} {{ bindtopictype .Type $structs}}{{- else}} {{ bindtype .Type $structs}}{{ end }} + {{- end}} + Raw *types.Log // Blockchain specific contextual infos + } + + const {{$contract.Type}}{{.Normalized.Name}}EventName = "{{.Original.Name}}" + + // ContractEventName returns the user-defined event name. + func ({{$contract.Type}}{{.Normalized.Name}}) ContractEventName() string { + return {{$contract.Type}}{{.Normalized.Name}}EventName + } + + // Unpack{{.Normalized.Name}}Event is the Go binding that unpacks the event data emitted + // by contract. + // + // Solidity: {{.Original.String}} + func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) Unpack{{.Normalized.Name}}Event(log *types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) { + event := "{{.Original.Name}}" + if log.Topics[0] != {{ decapitalise $contract.Type}}.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new({{$contract.Type}}{{.Normalized.Name}}) + if len(log.Data) > 0 { + if err := {{ decapitalise $contract.Type}}.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range {{ decapitalise $contract.Type}}.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil + } + {{end}} + + {{ if .Errors }} + // UnpackError attempts to decode the provided error data using user-defined + // error definitions. + func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) UnpackError(raw []byte) (any, error) { + {{- range $k, $v := .Errors}} + if bytes.Equal(raw[:4], {{ decapitalise $contract.Type}}.abi.Errors["{{.Normalized.Name}}"].ID.Bytes()[:4]) { + return {{ decapitalise $contract.Type}}.Unpack{{.Normalized.Name}}Error(raw[4:]) + } + {{- end }} + return nil, errors.New("Unknown error") + } + {{ end }} + + {{range .Errors}} + // {{$contract.Type}}{{.Normalized.Name}} represents a {{.Original.Name}} error raised by the {{$contract.Type}} contract. + type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}} + {{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type $structs}}{{else}}{{bindtype .Type $structs}}{{end}}; {{end}} + } + + // ErrorID returns the hash of canonical representation of the error's signature. + // + // Solidity: {{.Original.String}} + func {{$contract.Type}}{{.Normalized.Name}}ErrorID() common.Hash { + return common.HexToHash("{{.Original.ID}}") + } + + // Unpack{{.Normalized.Name}}Error is the Go binding used to decode the provided + // error data into the corresponding Go error struct. + // + // Solidity: {{.Original.String}} + func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) Unpack{{.Normalized.Name}}Error(raw []byte) (*{{$contract.Type}}{{.Normalized.Name}}, error) { + out := new({{$contract.Type}}{{.Normalized.Name}}) + if err := {{ decapitalise $contract.Type}}.abi.UnpackIntoInterface(out, "{{.Normalized.Name}}", raw); err != nil { + return nil, err + } + return out, nil + } + {{end}} +{{end}} diff --git a/accounts/abi/bind/template.go b/accounts/abi/abigen/template.go similarity index 64% rename from accounts/abi/bind/template.go rename to accounts/abi/abigen/template.go index 4a0062af0a8..cbb21037a68 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/abigen/template.go @@ -14,10 +14,12 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package bind +package abigen import ( _ "embed" + "strings" + "unicode" "github.com/ethereum/go-ethereum/accounts/abi" ) @@ -42,10 +44,48 @@ type tmplContract struct { Fallback *tmplMethod // Additional special fallback function Receive *tmplMethod // Additional special receive function Events map[string]*tmplEvent // Contract events accessors - Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs + Libraries map[string]string // Same as tmplData, but filtered to only keep direct deps that the contract needs Library bool // Indicator whether the contract is a library } +type tmplContractV2 struct { + Type string // Type name of the main contract binding + InputABI string // JSON ABI used as the input to generate the binding from + InputBin string // Optional EVM bytecode used to generate deploy code from + Constructor abi.Method // Contract constructor for deploy parametrization + Calls map[string]*tmplMethod // All contract methods (excluding fallback, receive) + Events map[string]*tmplEvent // Contract events accessors + Libraries map[string]string // all direct library dependencies + Errors map[string]*tmplError // all errors defined +} + +func newTmplContractV2(typ string, abiStr string, bytecode string, constructor abi.Method, cb *contractBinder) *tmplContractV2 { + // Strip any whitespace from the JSON ABI + strippedABI := strings.Map(func(r rune) rune { + if unicode.IsSpace(r) { + return -1 + } + return r + }, abiStr) + return &tmplContractV2{ + abi.ToCamelCase(typ), + strings.ReplaceAll(strippedABI, "\"", "\\\""), + strings.TrimPrefix(strings.TrimSpace(bytecode), "0x"), + constructor, + cb.calls, + cb.events, + make(map[string]string), + cb.errors, + } +} + +type tmplDataV2 struct { + Package string // Name of the package to use for the generated bindings + Contracts map[string]*tmplContractV2 // Contracts that will be emitted in the bindings (keyed by contract name) + Libraries map[string]string // Map of the contract's name to link pattern + Structs map[string]*tmplStruct // Contract struct type definitions +} + // tmplMethod is a wrapper around an abi.Method that contains a few preprocessed // and cached data fields. type tmplMethod struct { @@ -61,6 +101,13 @@ type tmplEvent struct { Normalized abi.Event // Normalized version of the parsed fields } +// tmplError is a wrapper around an abi.Error that contains a few preprocessed +// and cached data fields. +type tmplError struct { + Original abi.Error + Normalized abi.Error +} + // tmplField is a wrapper around a struct field with binding language // struct type definition and relative filed name. type tmplField struct { @@ -76,14 +123,14 @@ type tmplStruct struct { Fields []*tmplField // Struct fields definition depends on the binding language. } -// tmplSource is language to template mapping containing all the supported -// programming languages the package can generate to. -var tmplSource = map[Lang]string{ - LangGo: tmplSourceGo, -} - -// tmplSourceGo is the Go source template that the generated Go contract binding +// tmplSource is the Go source template that the generated Go contract binding // is based on. // //go:embed source.go.tpl -var tmplSourceGo string +var tmplSource string + +// tmplSourceV2 is the Go source template that the generated Go contract binding +// for abigen v2 is based on. +// +//go:embed source2.go.tpl +var tmplSourceV2 string diff --git a/accounts/abi/abigen/testdata/v2/callbackparam.go.txt b/accounts/abi/abigen/testdata/v2/callbackparam.go.txt new file mode 100644 index 00000000000..e3205bde0dc --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/callbackparam.go.txt @@ -0,0 +1,64 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// CallbackParamMetaData contains all meta data concerning the CallbackParam contract. +var CallbackParamMetaData = bind.MetaData{ + ABI: "[{\"constant\":false,\"inputs\":[{\"name\":\"callback\",\"type\":\"function\"}],\"name\":\"test\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ID: "949f96f86d3c2e1bcc15563ad898beaaca", + Bin: "0x608060405234801561001057600080fd5b5061015e806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063d7a5aba214610040575b600080fd5b34801561004c57600080fd5b506100be6004803603602081101561006357600080fd5b810190808035806c0100000000000000000000000090049068010000000000000000900463ffffffff1677ffffffffffffffffffffffffffffffffffffffffffffffff169091602001919093929190939291905050506100c0565b005b818160016040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561011657600080fd5b505af115801561012a573d6000803e3d6000fd5b50505050505056fea165627a7a7230582062f87455ff84be90896dbb0c4e4ddb505c600d23089f8e80a512548440d7e2580029", +} + +// CallbackParam is an auto generated Go binding around an Ethereum contract. +type CallbackParam struct { + abi abi.ABI +} + +// NewCallbackParam creates a new instance of CallbackParam. +func NewCallbackParam() *CallbackParam { + parsed, err := CallbackParamMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &CallbackParam{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *CallbackParam) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackTest is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xd7a5aba2. +// +// Solidity: function test(function callback) returns() +func (callbackParam *CallbackParam) PackTest(callback [24]byte) []byte { + enc, err := callbackParam.abi.Pack("test", callback) + if err != nil { + panic(err) + } + return enc +} diff --git a/accounts/abi/abigen/testdata/v2/crowdsale.go.txt b/accounts/abi/abigen/testdata/v2/crowdsale.go.txt new file mode 100644 index 00000000000..60d8b4ec11b --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/crowdsale.go.txt @@ -0,0 +1,304 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// CrowdsaleMetaData contains all meta data concerning the Crowdsale contract. +var CrowdsaleMetaData = bind.MetaData{ + ABI: "[{\"constant\":false,\"inputs\":[],\"name\":\"checkGoalReached\",\"outputs\":[],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"deadline\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"beneficiary\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tokenReward\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"fundingGoal\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"amountRaised\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"price\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"funders\",\"outputs\":[{\"name\":\"addr\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"inputs\":[{\"name\":\"ifSuccessfulSendTo\",\"type\":\"address\"},{\"name\":\"fundingGoalInEthers\",\"type\":\"uint256\"},{\"name\":\"durationInMinutes\",\"type\":\"uint256\"},{\"name\":\"etherCostOfEachToken\",\"type\":\"uint256\"},{\"name\":\"addressOfTokenUsedAsReward\",\"type\":\"address\"}],\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"backer\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"isContribution\",\"type\":\"bool\"}],\"name\":\"FundTransfer\",\"type\":\"event\"}]", + ID: "84d7e935785c5c648282d326307bb8fa0d", + Bin: "0x606060408190526007805460ff1916905560a0806105a883396101006040529051608051915160c05160e05160008054600160a060020a03199081169095178155670de0b6b3a7640000958602600155603c9093024201600355930260045560058054909216909217905561052f90819061007990396000f36060604052361561006c5760e060020a600035046301cb3b20811461008257806329dcb0cf1461014457806338af3eed1461014d5780636e66f6e91461015f5780637a3a0e84146101715780637b3e5e7b1461017a578063a035b1fe14610183578063dc0d3dff1461018c575b61020060075460009060ff161561032357610002565b61020060035460009042106103205760025460015490106103cb576002548154600160a060020a0316908290606082818181858883f150915460025460408051600160a060020a039390931683526020830191909152818101869052517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf6945090819003909201919050a15b60405160008054600160a060020a039081169230909116319082818181858883f150506007805460ff1916600117905550505050565b6103a160035481565b6103ab600054600160a060020a031681565b6103ab600554600160a060020a031681565b6103a160015481565b6103a160025481565b6103a160045481565b6103be60043560068054829081101561000257506000526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f8101547ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d409190910154600160a060020a03919091169082565b005b505050815481101561000257906000526020600020906002020160005060008201518160000160006101000a815481600160a060020a030219169083021790555060208201518160010160005055905050806002600082828250540192505081905550600560009054906101000a9004600160a060020a0316600160a060020a031663a9059cbb3360046000505484046040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506000604051808303816000876161da5a03f11561000257505060408051600160a060020a03331681526020810184905260018183015290517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf692509081900360600190a15b50565b5060a0604052336060908152346080819052600680546001810180835592939282908280158290116102025760020281600202836000526020600020918201910161020291905b8082111561039d57805473ffffffffffffffffffffffffffffffffffffffff19168155600060019190910190815561036a565b5090565b6060908152602090f35b600160a060020a03166060908152602090f35b6060918252608052604090f35b5b60065481101561010e576006805482908110156100025760009182526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0190600680549254600160a060020a0316928490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460405190915082818181858883f19350505050507fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf660066000508281548110156100025760008290526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01548154600160a060020a039190911691908490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460408051600160a060020a0394909416845260208401919091526000838201525191829003606001919050a16001016103cc56", +} + +// Crowdsale is an auto generated Go binding around an Ethereum contract. +type Crowdsale struct { + abi abi.ABI +} + +// NewCrowdsale creates a new instance of Crowdsale. +func NewCrowdsale() *Crowdsale { + parsed, err := CrowdsaleMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &Crowdsale{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *Crowdsale) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackConstructor is the Go binding used to pack the parameters required for +// contract deployment. +// +// Solidity: constructor(address ifSuccessfulSendTo, uint256 fundingGoalInEthers, uint256 durationInMinutes, uint256 etherCostOfEachToken, address addressOfTokenUsedAsReward) returns() +func (crowdsale *Crowdsale) PackConstructor(ifSuccessfulSendTo common.Address, fundingGoalInEthers *big.Int, durationInMinutes *big.Int, etherCostOfEachToken *big.Int, addressOfTokenUsedAsReward common.Address) []byte { + enc, err := crowdsale.abi.Pack("", ifSuccessfulSendTo, fundingGoalInEthers, durationInMinutes, etherCostOfEachToken, addressOfTokenUsedAsReward) + if err != nil { + panic(err) + } + return enc +} + +// PackAmountRaised is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x7b3e5e7b. +// +// Solidity: function amountRaised() returns(uint256) +func (crowdsale *Crowdsale) PackAmountRaised() []byte { + enc, err := crowdsale.abi.Pack("amountRaised") + if err != nil { + panic(err) + } + return enc +} + +// UnpackAmountRaised is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x7b3e5e7b. +// +// Solidity: function amountRaised() returns(uint256) +func (crowdsale *Crowdsale) UnpackAmountRaised(data []byte) (*big.Int, error) { + out, err := crowdsale.abi.Unpack("amountRaised", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackBeneficiary is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x38af3eed. +// +// Solidity: function beneficiary() returns(address) +func (crowdsale *Crowdsale) PackBeneficiary() []byte { + enc, err := crowdsale.abi.Pack("beneficiary") + if err != nil { + panic(err) + } + return enc +} + +// UnpackBeneficiary is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x38af3eed. +// +// Solidity: function beneficiary() returns(address) +func (crowdsale *Crowdsale) UnpackBeneficiary(data []byte) (common.Address, error) { + out, err := crowdsale.abi.Unpack("beneficiary", data) + if err != nil { + return *new(common.Address), err + } + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + return out0, err +} + +// PackCheckGoalReached is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x01cb3b20. +// +// Solidity: function checkGoalReached() returns() +func (crowdsale *Crowdsale) PackCheckGoalReached() []byte { + enc, err := crowdsale.abi.Pack("checkGoalReached") + if err != nil { + panic(err) + } + return enc +} + +// PackDeadline is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x29dcb0cf. +// +// Solidity: function deadline() returns(uint256) +func (crowdsale *Crowdsale) PackDeadline() []byte { + enc, err := crowdsale.abi.Pack("deadline") + if err != nil { + panic(err) + } + return enc +} + +// UnpackDeadline is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x29dcb0cf. +// +// Solidity: function deadline() returns(uint256) +func (crowdsale *Crowdsale) UnpackDeadline(data []byte) (*big.Int, error) { + out, err := crowdsale.abi.Unpack("deadline", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackFunders is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xdc0d3dff. +// +// Solidity: function funders(uint256 ) returns(address addr, uint256 amount) +func (crowdsale *Crowdsale) PackFunders(arg0 *big.Int) []byte { + enc, err := crowdsale.abi.Pack("funders", arg0) + if err != nil { + panic(err) + } + return enc +} + +// FundersOutput serves as a container for the return parameters of contract +// method Funders. +type FundersOutput struct { + Addr common.Address + Amount *big.Int +} + +// UnpackFunders is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xdc0d3dff. +// +// Solidity: function funders(uint256 ) returns(address addr, uint256 amount) +func (crowdsale *Crowdsale) UnpackFunders(data []byte) (FundersOutput, error) { + out, err := crowdsale.abi.Unpack("funders", data) + outstruct := new(FundersOutput) + if err != nil { + return *outstruct, err + } + outstruct.Addr = *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + outstruct.Amount = abi.ConvertType(out[1], new(big.Int)).(*big.Int) + return *outstruct, err + +} + +// PackFundingGoal is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x7a3a0e84. +// +// Solidity: function fundingGoal() returns(uint256) +func (crowdsale *Crowdsale) PackFundingGoal() []byte { + enc, err := crowdsale.abi.Pack("fundingGoal") + if err != nil { + panic(err) + } + return enc +} + +// UnpackFundingGoal is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x7a3a0e84. +// +// Solidity: function fundingGoal() returns(uint256) +func (crowdsale *Crowdsale) UnpackFundingGoal(data []byte) (*big.Int, error) { + out, err := crowdsale.abi.Unpack("fundingGoal", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackPrice is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xa035b1fe. +// +// Solidity: function price() returns(uint256) +func (crowdsale *Crowdsale) PackPrice() []byte { + enc, err := crowdsale.abi.Pack("price") + if err != nil { + panic(err) + } + return enc +} + +// UnpackPrice is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xa035b1fe. +// +// Solidity: function price() returns(uint256) +func (crowdsale *Crowdsale) UnpackPrice(data []byte) (*big.Int, error) { + out, err := crowdsale.abi.Unpack("price", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackTokenReward is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x6e66f6e9. +// +// Solidity: function tokenReward() returns(address) +func (crowdsale *Crowdsale) PackTokenReward() []byte { + enc, err := crowdsale.abi.Pack("tokenReward") + if err != nil { + panic(err) + } + return enc +} + +// UnpackTokenReward is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x6e66f6e9. +// +// Solidity: function tokenReward() returns(address) +func (crowdsale *Crowdsale) UnpackTokenReward(data []byte) (common.Address, error) { + out, err := crowdsale.abi.Unpack("tokenReward", data) + if err != nil { + return *new(common.Address), err + } + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + return out0, err +} + +// CrowdsaleFundTransfer represents a FundTransfer event raised by the Crowdsale contract. +type CrowdsaleFundTransfer struct { + Backer common.Address + Amount *big.Int + IsContribution bool + Raw *types.Log // Blockchain specific contextual infos +} + +const CrowdsaleFundTransferEventName = "FundTransfer" + +// ContractEventName returns the user-defined event name. +func (CrowdsaleFundTransfer) ContractEventName() string { + return CrowdsaleFundTransferEventName +} + +// UnpackFundTransferEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event FundTransfer(address backer, uint256 amount, bool isContribution) +func (crowdsale *Crowdsale) UnpackFundTransferEvent(log *types.Log) (*CrowdsaleFundTransfer, error) { + event := "FundTransfer" + if log.Topics[0] != crowdsale.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(CrowdsaleFundTransfer) + if len(log.Data) > 0 { + if err := crowdsale.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range crowdsale.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} diff --git a/accounts/abi/abigen/testdata/v2/dao.go.txt b/accounts/abi/abigen/testdata/v2/dao.go.txt new file mode 100644 index 00000000000..72a80949f6c --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/dao.go.txt @@ -0,0 +1,655 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// DAOMetaData contains all meta data concerning the DAO contract. +var DAOMetaData = bind.MetaData{ + ABI: "[{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"proposals\",\"outputs\":[{\"name\":\"recipient\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"votingDeadline\",\"type\":\"uint256\"},{\"name\":\"executed\",\"type\":\"bool\"},{\"name\":\"proposalPassed\",\"type\":\"bool\"},{\"name\":\"numberOfVotes\",\"type\":\"uint256\"},{\"name\":\"currentResult\",\"type\":\"int256\"},{\"name\":\"proposalHash\",\"type\":\"bytes32\"}],\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"proposalNumber\",\"type\":\"uint256\"},{\"name\":\"transactionBytecode\",\"type\":\"bytes\"}],\"name\":\"executeProposal\",\"outputs\":[{\"name\":\"result\",\"type\":\"int256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"memberId\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"numProposals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"members\",\"outputs\":[{\"name\":\"member\",\"type\":\"address\"},{\"name\":\"canVote\",\"type\":\"bool\"},{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"memberSince\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"debatingPeriodInMinutes\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"minimumQuorum\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"targetMember\",\"type\":\"address\"},{\"name\":\"canVote\",\"type\":\"bool\"},{\"name\":\"memberName\",\"type\":\"string\"}],\"name\":\"changeMembership\",\"outputs\":[],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"majorityMargin\",\"outputs\":[{\"name\":\"\",\"type\":\"int256\"}],\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"beneficiary\",\"type\":\"address\"},{\"name\":\"etherAmount\",\"type\":\"uint256\"},{\"name\":\"JobDescription\",\"type\":\"string\"},{\"name\":\"transactionBytecode\",\"type\":\"bytes\"}],\"name\":\"newProposal\",\"outputs\":[{\"name\":\"proposalID\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"minimumQuorumForProposals\",\"type\":\"uint256\"},{\"name\":\"minutesForDebate\",\"type\":\"uint256\"},{\"name\":\"marginOfVotesForMajority\",\"type\":\"int256\"}],\"name\":\"changeVotingRules\",\"outputs\":[],\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"proposalNumber\",\"type\":\"uint256\"},{\"name\":\"supportsProposal\",\"type\":\"bool\"},{\"name\":\"justificationText\",\"type\":\"string\"}],\"name\":\"vote\",\"outputs\":[{\"name\":\"voteID\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"proposalNumber\",\"type\":\"uint256\"},{\"name\":\"beneficiary\",\"type\":\"address\"},{\"name\":\"etherAmount\",\"type\":\"uint256\"},{\"name\":\"transactionBytecode\",\"type\":\"bytes\"}],\"name\":\"checkProposalCode\",\"outputs\":[{\"name\":\"codeChecksOut\",\"type\":\"bool\"}],\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"type\":\"function\"},{\"inputs\":[{\"name\":\"minimumQuorumForProposals\",\"type\":\"uint256\"},{\"name\":\"minutesForDebate\",\"type\":\"uint256\"},{\"name\":\"marginOfVotesForMajority\",\"type\":\"int256\"},{\"name\":\"congressLeader\",\"type\":\"address\"}],\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"proposalID\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"description\",\"type\":\"string\"}],\"name\":\"ProposalAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"proposalID\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"position\",\"type\":\"bool\"},{\"indexed\":false,\"name\":\"voter\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"justification\",\"type\":\"string\"}],\"name\":\"Voted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"proposalID\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"result\",\"type\":\"int256\"},{\"indexed\":false,\"name\":\"quorum\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"active\",\"type\":\"bool\"}],\"name\":\"ProposalTallied\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"member\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"isMember\",\"type\":\"bool\"}],\"name\":\"MembershipChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"minimumQuorum\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"debatingPeriodInMinutes\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"majorityMargin\",\"type\":\"int256\"}],\"name\":\"ChangeOfRules\",\"type\":\"event\"}]", + ID: "d0a4ad96d49edb1c33461cebc6fb260919", + Bin: "0x606060405260405160808061145f833960e06040529051905160a05160c05160008054600160a060020a03191633179055600184815560028490556003839055600780549182018082558280158290116100b8576003028160030283600052602060002091820191016100b891906101c8565b50506060919091015160029190910155600160a060020a0381166000146100a65760008054600160a060020a031916821790555b505050506111f18061026e6000396000f35b505060408051608081018252600080825260208281018290528351908101845281815292820192909252426060820152600780549194509250811015610002579081527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6889050815181546020848101517401000000000000000000000000000000000000000002600160a060020a03199290921690921760a060020a60ff021916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f9081018390048201949192919091019083901061023e57805160ff19168380011785555b50610072929150610226565b5050600060028201556001015b8082111561023a578054600160a860020a031916815560018181018054600080835592600290821615610100026000190190911604601f81901061020c57506101bb565b601f0160209004906000526020600020908101906101bb91905b8082111561023a5760008155600101610226565b5090565b828001600101855582156101af579182015b828111156101af57825182600050559160200191906001019061025056606060405236156100b95760e060020a6000350463013cf08b81146100bb578063237e9492146101285780633910682114610281578063400e3949146102995780635daf08ca146102a257806369bd34361461032f5780638160f0b5146103385780638da5cb5b146103415780639644fcbd14610353578063aa02a90f146103be578063b1050da5146103c7578063bcca1fd3146104b5578063d3c0715b146104dc578063eceb29451461058d578063f2fde38b1461067b575b005b61069c6004356004805482908110156100025790600052602060002090600a02016000506005810154815460018301546003840154600485015460068601546007870154600160a060020a03959095169750929560020194919360ff828116946101009093041692919089565b60408051602060248035600481810135601f81018590048502860185019096528585526107759581359591946044949293909201918190840183828082843750949650505050505050600060006004600050848154811015610002575090527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19e600a8402908101547f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b909101904210806101e65750600481015460ff165b8061026757508060000160009054906101000a9004600160a060020a03168160010160005054846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020816007016000505414155b8061027757506001546005820154105b1561109257610002565b61077560043560066020526000908152604090205481565b61077560055481565b61078760043560078054829081101561000257506000526003026000805160206111d18339815191528101547fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a820154600160a060020a0382169260a060020a90920460ff16917fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c689019084565b61077560025481565b61077560015481565b610830600054600160a060020a031681565b604080516020604435600481810135601f81018490048402850184019095528484526100b9948135946024803595939460649492939101918190840183828082843750949650505050505050600080548190600160a060020a03908116339091161461084d57610002565b61077560035481565b604080516020604435600481810135601f8101849004840285018401909552848452610775948135946024803595939460649492939101918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024909101945090925082915084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806104ab5750604081205460078054909190811015610002579082526003026000805160206111d1833981519152015460a060020a900460ff16155b15610ce557610002565b6100b960043560243560443560005433600160a060020a03908116911614610b1857610002565b604080516020604435600481810135601f810184900484028501840190955284845261077594813594602480359593946064949293910191819084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806105835750604081205460078054909190811015610002579082526003026000805160206111d18339815191520181505460a060020a900460ff16155b15610f1d57610002565b604080516020606435600481810135601f81018490048402850184019095528484526107759481359460248035956044359560849492019190819084018382808284375094965050505050505060006000600460005086815481101561000257908252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01815090508484846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005054149150610cdc565b6100b960043560005433600160a060020a03908116911614610f0857610002565b604051808a600160a060020a031681526020018981526020018060200188815260200187815260200186815260200185815260200184815260200183815260200182810382528981815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561075e5780601f106107335761010080835404028352916020019161075e565b820191906000526020600020905b81548152906001019060200180831161074157829003601f168201915b50509a505050505050505050505060405180910390f35b60408051918252519081900360200190f35b60408051600160a060020a038616815260208101859052606081018390526080918101828152845460026001821615610100026000190190911604928201839052909160a08301908590801561081e5780601f106107f35761010080835404028352916020019161081e565b820191906000526020600020905b81548152906001019060200180831161080157829003601f168201915b50509550505050505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b600160a060020a03851660009081526006602052604081205414156108a957604060002060078054918290556001820180825582801582901161095c5760030281600302836000526020600020918201910161095c9190610a4f565b600160a060020a03851660009081526006602052604090205460078054919350908390811015610002575060005250600381026000805160206111d183398151915201805474ff0000000000000000000000000000000000000000191660a060020a85021781555b60408051600160a060020a03871681526020810186905281517f27b022af4a8347100c7a041ce5ccf8e14d644ff05de696315196faae8cd50c9b929181900390910190a15050505050565b505050915081506080604051908101604052808681526020018581526020018481526020014281526020015060076000508381548110156100025790600052602060002090600302016000508151815460208481015160a060020a02600160a060020a03199290921690921774ff00000000000000000000000000000000000000001916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f90810183900482019491929190910190839010610ad357805160ff19168380011785555b50610b03929150610abb565b5050600060028201556001015b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610aa15750610a42565b601f016020900490600052602060002090810190610a4291905b80821115610acf5760008155600101610abb565b5090565b82800160010185558215610a36579182015b82811115610a36578251826000505591602001919060010190610ae5565b50506060919091015160029190910155610911565b600183905560028290556003819055604080518481526020810184905280820183905290517fa439d3fa452be5e0e1e24a8145e715f4fd8b9c08c96a42fd82a855a85e5d57de9181900360600190a1505050565b50508585846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005081905550600260005054603c024201816003016000508190555060008160040160006101000a81548160ff0219169083021790555060008160040160016101000a81548160ff02191690830217905550600081600501600050819055507f646fec02522b41e7125cfc859a64fd4f4cefd5dc3b6237ca0abe251ded1fa881828787876040518085815260200184600160a060020a03168152602001838152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015610cc45780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1600182016005555b50949350505050565b6004805460018101808355909190828015829011610d1c57600a0281600a028360005260206000209182019101610d1c9190610db8565b505060048054929450918491508110156100025790600052602060002090600a02016000508054600160a060020a031916871781556001818101879055855160028381018054600082815260209081902096975091959481161561010002600019011691909104601f90810182900484019391890190839010610ed857805160ff19168380011785555b50610b6c929150610abb565b50506001015b80821115610acf578054600160a060020a03191681556000600182810182905560028381018054848255909281161561010002600019011604601f819010610e9c57505b5060006003830181905560048301805461ffff191690556005830181905560068301819055600783018190556008830180548282559082526020909120610db2916002028101905b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610eba57505b5050600101610e44565b601f016020900490600052602060002090810190610dfc9190610abb565b601f016020900490600052602060002090810190610e929190610abb565b82800160010185558215610da6579182015b82811115610da6578251826000505591602001919060010190610eea565b60008054600160a060020a0319168217905550565b600480548690811015610002576000918252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01905033600160a060020a0316600090815260098201602052604090205490915060ff1660011415610f8457610002565b33600160a060020a031660009081526009820160205260409020805460ff1916600190811790915560058201805490910190558315610fcd576006810180546001019055610fda565b6006810180546000190190555b7fc34f869b7ff431b034b7b9aea9822dac189a685e0b015c7d1be3add3f89128e8858533866040518085815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561107a5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1509392505050565b6006810154600354901315611158578060000160009054906101000a9004600160a060020a0316600160a060020a03168160010160005054670de0b6b3a76400000284604051808280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156111225780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f15050505060048101805460ff191660011761ff00191661010017905561116d565b60048101805460ff191660011761ff00191690555b60068101546005820154600483015460408051888152602081019490945283810192909252610100900460ff166060830152517fd220b7272a8b6d0d7d6bcdace67b936a8f175e6d5c1b3ee438b72256b32ab3af9181900360800190a1509291505056a66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688", +} + +// DAO is an auto generated Go binding around an Ethereum contract. +type DAO struct { + abi abi.ABI +} + +// NewDAO creates a new instance of DAO. +func NewDAO() *DAO { + parsed, err := DAOMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &DAO{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *DAO) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackConstructor is the Go binding used to pack the parameters required for +// contract deployment. +// +// Solidity: constructor(uint256 minimumQuorumForProposals, uint256 minutesForDebate, int256 marginOfVotesForMajority, address congressLeader) returns() +func (dAO *DAO) PackConstructor(minimumQuorumForProposals *big.Int, minutesForDebate *big.Int, marginOfVotesForMajority *big.Int, congressLeader common.Address) []byte { + enc, err := dAO.abi.Pack("", minimumQuorumForProposals, minutesForDebate, marginOfVotesForMajority, congressLeader) + if err != nil { + panic(err) + } + return enc +} + +// PackChangeMembership is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x9644fcbd. +// +// Solidity: function changeMembership(address targetMember, bool canVote, string memberName) returns() +func (dAO *DAO) PackChangeMembership(targetMember common.Address, canVote bool, memberName string) []byte { + enc, err := dAO.abi.Pack("changeMembership", targetMember, canVote, memberName) + if err != nil { + panic(err) + } + return enc +} + +// PackChangeVotingRules is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xbcca1fd3. +// +// Solidity: function changeVotingRules(uint256 minimumQuorumForProposals, uint256 minutesForDebate, int256 marginOfVotesForMajority) returns() +func (dAO *DAO) PackChangeVotingRules(minimumQuorumForProposals *big.Int, minutesForDebate *big.Int, marginOfVotesForMajority *big.Int) []byte { + enc, err := dAO.abi.Pack("changeVotingRules", minimumQuorumForProposals, minutesForDebate, marginOfVotesForMajority) + if err != nil { + panic(err) + } + return enc +} + +// PackCheckProposalCode is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xeceb2945. +// +// Solidity: function checkProposalCode(uint256 proposalNumber, address beneficiary, uint256 etherAmount, bytes transactionBytecode) returns(bool codeChecksOut) +func (dAO *DAO) PackCheckProposalCode(proposalNumber *big.Int, beneficiary common.Address, etherAmount *big.Int, transactionBytecode []byte) []byte { + enc, err := dAO.abi.Pack("checkProposalCode", proposalNumber, beneficiary, etherAmount, transactionBytecode) + if err != nil { + panic(err) + } + return enc +} + +// UnpackCheckProposalCode is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xeceb2945. +// +// Solidity: function checkProposalCode(uint256 proposalNumber, address beneficiary, uint256 etherAmount, bytes transactionBytecode) returns(bool codeChecksOut) +func (dAO *DAO) UnpackCheckProposalCode(data []byte) (bool, error) { + out, err := dAO.abi.Unpack("checkProposalCode", data) + if err != nil { + return *new(bool), err + } + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + return out0, err +} + +// PackDebatingPeriodInMinutes is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x69bd3436. +// +// Solidity: function debatingPeriodInMinutes() returns(uint256) +func (dAO *DAO) PackDebatingPeriodInMinutes() []byte { + enc, err := dAO.abi.Pack("debatingPeriodInMinutes") + if err != nil { + panic(err) + } + return enc +} + +// UnpackDebatingPeriodInMinutes is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x69bd3436. +// +// Solidity: function debatingPeriodInMinutes() returns(uint256) +func (dAO *DAO) UnpackDebatingPeriodInMinutes(data []byte) (*big.Int, error) { + out, err := dAO.abi.Unpack("debatingPeriodInMinutes", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackExecuteProposal is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x237e9492. +// +// Solidity: function executeProposal(uint256 proposalNumber, bytes transactionBytecode) returns(int256 result) +func (dAO *DAO) PackExecuteProposal(proposalNumber *big.Int, transactionBytecode []byte) []byte { + enc, err := dAO.abi.Pack("executeProposal", proposalNumber, transactionBytecode) + if err != nil { + panic(err) + } + return enc +} + +// UnpackExecuteProposal is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x237e9492. +// +// Solidity: function executeProposal(uint256 proposalNumber, bytes transactionBytecode) returns(int256 result) +func (dAO *DAO) UnpackExecuteProposal(data []byte) (*big.Int, error) { + out, err := dAO.abi.Unpack("executeProposal", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackMajorityMargin is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xaa02a90f. +// +// Solidity: function majorityMargin() returns(int256) +func (dAO *DAO) PackMajorityMargin() []byte { + enc, err := dAO.abi.Pack("majorityMargin") + if err != nil { + panic(err) + } + return enc +} + +// UnpackMajorityMargin is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xaa02a90f. +// +// Solidity: function majorityMargin() returns(int256) +func (dAO *DAO) UnpackMajorityMargin(data []byte) (*big.Int, error) { + out, err := dAO.abi.Unpack("majorityMargin", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackMemberId is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x39106821. +// +// Solidity: function memberId(address ) returns(uint256) +func (dAO *DAO) PackMemberId(arg0 common.Address) []byte { + enc, err := dAO.abi.Pack("memberId", arg0) + if err != nil { + panic(err) + } + return enc +} + +// UnpackMemberId is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x39106821. +// +// Solidity: function memberId(address ) returns(uint256) +func (dAO *DAO) UnpackMemberId(data []byte) (*big.Int, error) { + out, err := dAO.abi.Unpack("memberId", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackMembers is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x5daf08ca. +// +// Solidity: function members(uint256 ) returns(address member, bool canVote, string name, uint256 memberSince) +func (dAO *DAO) PackMembers(arg0 *big.Int) []byte { + enc, err := dAO.abi.Pack("members", arg0) + if err != nil { + panic(err) + } + return enc +} + +// MembersOutput serves as a container for the return parameters of contract +// method Members. +type MembersOutput struct { + Member common.Address + CanVote bool + Name string + MemberSince *big.Int +} + +// UnpackMembers is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x5daf08ca. +// +// Solidity: function members(uint256 ) returns(address member, bool canVote, string name, uint256 memberSince) +func (dAO *DAO) UnpackMembers(data []byte) (MembersOutput, error) { + out, err := dAO.abi.Unpack("members", data) + outstruct := new(MembersOutput) + if err != nil { + return *outstruct, err + } + outstruct.Member = *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + outstruct.CanVote = *abi.ConvertType(out[1], new(bool)).(*bool) + outstruct.Name = *abi.ConvertType(out[2], new(string)).(*string) + outstruct.MemberSince = abi.ConvertType(out[3], new(big.Int)).(*big.Int) + return *outstruct, err + +} + +// PackMinimumQuorum is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x8160f0b5. +// +// Solidity: function minimumQuorum() returns(uint256) +func (dAO *DAO) PackMinimumQuorum() []byte { + enc, err := dAO.abi.Pack("minimumQuorum") + if err != nil { + panic(err) + } + return enc +} + +// UnpackMinimumQuorum is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x8160f0b5. +// +// Solidity: function minimumQuorum() returns(uint256) +func (dAO *DAO) UnpackMinimumQuorum(data []byte) (*big.Int, error) { + out, err := dAO.abi.Unpack("minimumQuorum", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackNewProposal is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xb1050da5. +// +// Solidity: function newProposal(address beneficiary, uint256 etherAmount, string JobDescription, bytes transactionBytecode) returns(uint256 proposalID) +func (dAO *DAO) PackNewProposal(beneficiary common.Address, etherAmount *big.Int, jobDescription string, transactionBytecode []byte) []byte { + enc, err := dAO.abi.Pack("newProposal", beneficiary, etherAmount, jobDescription, transactionBytecode) + if err != nil { + panic(err) + } + return enc +} + +// UnpackNewProposal is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xb1050da5. +// +// Solidity: function newProposal(address beneficiary, uint256 etherAmount, string JobDescription, bytes transactionBytecode) returns(uint256 proposalID) +func (dAO *DAO) UnpackNewProposal(data []byte) (*big.Int, error) { + out, err := dAO.abi.Unpack("newProposal", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackNumProposals is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x400e3949. +// +// Solidity: function numProposals() returns(uint256) +func (dAO *DAO) PackNumProposals() []byte { + enc, err := dAO.abi.Pack("numProposals") + if err != nil { + panic(err) + } + return enc +} + +// UnpackNumProposals is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x400e3949. +// +// Solidity: function numProposals() returns(uint256) +func (dAO *DAO) UnpackNumProposals(data []byte) (*big.Int, error) { + out, err := dAO.abi.Unpack("numProposals", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackOwner is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x8da5cb5b. +// +// Solidity: function owner() returns(address) +func (dAO *DAO) PackOwner() []byte { + enc, err := dAO.abi.Pack("owner") + if err != nil { + panic(err) + } + return enc +} + +// UnpackOwner is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x8da5cb5b. +// +// Solidity: function owner() returns(address) +func (dAO *DAO) UnpackOwner(data []byte) (common.Address, error) { + out, err := dAO.abi.Unpack("owner", data) + if err != nil { + return *new(common.Address), err + } + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + return out0, err +} + +// PackProposals is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x013cf08b. +// +// Solidity: function proposals(uint256 ) returns(address recipient, uint256 amount, string description, uint256 votingDeadline, bool executed, bool proposalPassed, uint256 numberOfVotes, int256 currentResult, bytes32 proposalHash) +func (dAO *DAO) PackProposals(arg0 *big.Int) []byte { + enc, err := dAO.abi.Pack("proposals", arg0) + if err != nil { + panic(err) + } + return enc +} + +// ProposalsOutput serves as a container for the return parameters of contract +// method Proposals. +type ProposalsOutput struct { + Recipient common.Address + Amount *big.Int + Description string + VotingDeadline *big.Int + Executed bool + ProposalPassed bool + NumberOfVotes *big.Int + CurrentResult *big.Int + ProposalHash [32]byte +} + +// UnpackProposals is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x013cf08b. +// +// Solidity: function proposals(uint256 ) returns(address recipient, uint256 amount, string description, uint256 votingDeadline, bool executed, bool proposalPassed, uint256 numberOfVotes, int256 currentResult, bytes32 proposalHash) +func (dAO *DAO) UnpackProposals(data []byte) (ProposalsOutput, error) { + out, err := dAO.abi.Unpack("proposals", data) + outstruct := new(ProposalsOutput) + if err != nil { + return *outstruct, err + } + outstruct.Recipient = *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + outstruct.Amount = abi.ConvertType(out[1], new(big.Int)).(*big.Int) + outstruct.Description = *abi.ConvertType(out[2], new(string)).(*string) + outstruct.VotingDeadline = abi.ConvertType(out[3], new(big.Int)).(*big.Int) + outstruct.Executed = *abi.ConvertType(out[4], new(bool)).(*bool) + outstruct.ProposalPassed = *abi.ConvertType(out[5], new(bool)).(*bool) + outstruct.NumberOfVotes = abi.ConvertType(out[6], new(big.Int)).(*big.Int) + outstruct.CurrentResult = abi.ConvertType(out[7], new(big.Int)).(*big.Int) + outstruct.ProposalHash = *abi.ConvertType(out[8], new([32]byte)).(*[32]byte) + return *outstruct, err + +} + +// PackTransferOwnership is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (dAO *DAO) PackTransferOwnership(newOwner common.Address) []byte { + enc, err := dAO.abi.Pack("transferOwnership", newOwner) + if err != nil { + panic(err) + } + return enc +} + +// PackVote is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xd3c0715b. +// +// Solidity: function vote(uint256 proposalNumber, bool supportsProposal, string justificationText) returns(uint256 voteID) +func (dAO *DAO) PackVote(proposalNumber *big.Int, supportsProposal bool, justificationText string) []byte { + enc, err := dAO.abi.Pack("vote", proposalNumber, supportsProposal, justificationText) + if err != nil { + panic(err) + } + return enc +} + +// UnpackVote is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xd3c0715b. +// +// Solidity: function vote(uint256 proposalNumber, bool supportsProposal, string justificationText) returns(uint256 voteID) +func (dAO *DAO) UnpackVote(data []byte) (*big.Int, error) { + out, err := dAO.abi.Unpack("vote", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// DAOChangeOfRules represents a ChangeOfRules event raised by the DAO contract. +type DAOChangeOfRules struct { + MinimumQuorum *big.Int + DebatingPeriodInMinutes *big.Int + MajorityMargin *big.Int + Raw *types.Log // Blockchain specific contextual infos +} + +const DAOChangeOfRulesEventName = "ChangeOfRules" + +// ContractEventName returns the user-defined event name. +func (DAOChangeOfRules) ContractEventName() string { + return DAOChangeOfRulesEventName +} + +// UnpackChangeOfRulesEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event ChangeOfRules(uint256 minimumQuorum, uint256 debatingPeriodInMinutes, int256 majorityMargin) +func (dAO *DAO) UnpackChangeOfRulesEvent(log *types.Log) (*DAOChangeOfRules, error) { + event := "ChangeOfRules" + if log.Topics[0] != dAO.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(DAOChangeOfRules) + if len(log.Data) > 0 { + if err := dAO.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range dAO.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} + +// DAOMembershipChanged represents a MembershipChanged event raised by the DAO contract. +type DAOMembershipChanged struct { + Member common.Address + IsMember bool + Raw *types.Log // Blockchain specific contextual infos +} + +const DAOMembershipChangedEventName = "MembershipChanged" + +// ContractEventName returns the user-defined event name. +func (DAOMembershipChanged) ContractEventName() string { + return DAOMembershipChangedEventName +} + +// UnpackMembershipChangedEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event MembershipChanged(address member, bool isMember) +func (dAO *DAO) UnpackMembershipChangedEvent(log *types.Log) (*DAOMembershipChanged, error) { + event := "MembershipChanged" + if log.Topics[0] != dAO.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(DAOMembershipChanged) + if len(log.Data) > 0 { + if err := dAO.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range dAO.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} + +// DAOProposalAdded represents a ProposalAdded event raised by the DAO contract. +type DAOProposalAdded struct { + ProposalID *big.Int + Recipient common.Address + Amount *big.Int + Description string + Raw *types.Log // Blockchain specific contextual infos +} + +const DAOProposalAddedEventName = "ProposalAdded" + +// ContractEventName returns the user-defined event name. +func (DAOProposalAdded) ContractEventName() string { + return DAOProposalAddedEventName +} + +// UnpackProposalAddedEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event ProposalAdded(uint256 proposalID, address recipient, uint256 amount, string description) +func (dAO *DAO) UnpackProposalAddedEvent(log *types.Log) (*DAOProposalAdded, error) { + event := "ProposalAdded" + if log.Topics[0] != dAO.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(DAOProposalAdded) + if len(log.Data) > 0 { + if err := dAO.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range dAO.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} + +// DAOProposalTallied represents a ProposalTallied event raised by the DAO contract. +type DAOProposalTallied struct { + ProposalID *big.Int + Result *big.Int + Quorum *big.Int + Active bool + Raw *types.Log // Blockchain specific contextual infos +} + +const DAOProposalTalliedEventName = "ProposalTallied" + +// ContractEventName returns the user-defined event name. +func (DAOProposalTallied) ContractEventName() string { + return DAOProposalTalliedEventName +} + +// UnpackProposalTalliedEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event ProposalTallied(uint256 proposalID, int256 result, uint256 quorum, bool active) +func (dAO *DAO) UnpackProposalTalliedEvent(log *types.Log) (*DAOProposalTallied, error) { + event := "ProposalTallied" + if log.Topics[0] != dAO.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(DAOProposalTallied) + if len(log.Data) > 0 { + if err := dAO.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range dAO.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} + +// DAOVoted represents a Voted event raised by the DAO contract. +type DAOVoted struct { + ProposalID *big.Int + Position bool + Voter common.Address + Justification string + Raw *types.Log // Blockchain specific contextual infos +} + +const DAOVotedEventName = "Voted" + +// ContractEventName returns the user-defined event name. +func (DAOVoted) ContractEventName() string { + return DAOVotedEventName +} + +// UnpackVotedEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event Voted(uint256 proposalID, bool position, address voter, string justification) +func (dAO *DAO) UnpackVotedEvent(log *types.Log) (*DAOVoted, error) { + event := "Voted" + if log.Topics[0] != dAO.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(DAOVoted) + if len(log.Data) > 0 { + if err := dAO.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range dAO.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} diff --git a/accounts/abi/abigen/testdata/v2/deeplynestedarray.go.txt b/accounts/abi/abigen/testdata/v2/deeplynestedarray.go.txt new file mode 100644 index 00000000000..00f717d020c --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/deeplynestedarray.go.txt @@ -0,0 +1,114 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// DeeplyNestedArrayMetaData contains all meta data concerning the DeeplyNestedArray contract. +var DeeplyNestedArrayMetaData = bind.MetaData{ + ABI: "[{\"constant\":false,\"inputs\":[{\"name\":\"arr\",\"type\":\"uint64[3][4][5]\"}],\"name\":\"storeDeepUintArray\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"retrieveDeepArray\",\"outputs\":[{\"name\":\"\",\"type\":\"uint64[3][4][5]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"},{\"name\":\"\",\"type\":\"uint256\"},{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"deepUint64Array\",\"outputs\":[{\"name\":\"\",\"type\":\"uint64\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]", + ID: "3a44c26b21f02743d5dbeb02d24a67bf41", + Bin: "0x6060604052341561000f57600080fd5b6106438061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063344248551461005c5780638ed4573a1461011457806398ed1856146101ab575b600080fd5b341561006757600080fd5b610112600480806107800190600580602002604051908101604052809291906000905b828210156101055783826101800201600480602002604051908101604052809291906000905b828210156100f25783826060020160038060200260405190810160405280929190826003602002808284378201915050505050815260200190600101906100b0565b505050508152602001906001019061008a565b5050505091905050610208565b005b341561011f57600080fd5b61012761021d565b604051808260056000925b8184101561019b578284602002015160046000925b8184101561018d5782846020020151600360200280838360005b8381101561017c578082015181840152602081019050610161565b505050509050019260010192610147565b925050509260010192610132565b9250505091505060405180910390f35b34156101b657600080fd5b6101de6004808035906020019091908035906020019091908035906020019091905050610309565b604051808267ffffffffffffffff1667ffffffffffffffff16815260200191505060405180910390f35b80600090600561021992919061035f565b5050565b6102256103b0565b6000600580602002604051908101604052809291906000905b8282101561030057838260040201600480602002604051908101604052809291906000905b828210156102ed578382016003806020026040519081016040528092919082600380156102d9576020028201916000905b82829054906101000a900467ffffffffffffffff1667ffffffffffffffff16815260200190600801906020826007010492830192600103820291508084116102945790505b505050505081526020019060010190610263565b505050508152602001906001019061023e565b50505050905090565b60008360058110151561031857fe5b600402018260048110151561032957fe5b018160038110151561033757fe5b6004918282040191900660080292509250509054906101000a900467ffffffffffffffff1681565b826005600402810192821561039f579160200282015b8281111561039e5782518290600461038e9291906103df565b5091602001919060040190610375565b5b5090506103ac919061042d565b5090565b610780604051908101604052806005905b6103c9610459565b8152602001906001900390816103c15790505090565b826004810192821561041c579160200282015b8281111561041b5782518290600361040b929190610488565b50916020019190600101906103f2565b5b5090506104299190610536565b5090565b61045691905b8082111561045257600081816104499190610562565b50600401610433565b5090565b90565b610180604051908101604052806004905b6104726105a7565b81526020019060019003908161046a5790505090565b82600380016004900481019282156105255791602002820160005b838211156104ef57835183826101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555092602001926008016020816007010492830192600103026104a3565b80156105235782816101000a81549067ffffffffffffffff02191690556008016020816007010492830192600103026104ef565b505b50905061053291906105d9565b5090565b61055f91905b8082111561055b57600081816105529190610610565b5060010161053c565b5090565b90565b50600081816105719190610610565b50600101600081816105839190610610565b50600101600081816105959190610610565b5060010160006105a59190610610565b565b6060604051908101604052806003905b600067ffffffffffffffff168152602001906001900390816105b75790505090565b61060d91905b8082111561060957600081816101000a81549067ffffffffffffffff0219169055506001016105df565b5090565b90565b50600090555600a165627a7a7230582087e5a43f6965ab6ef7a4ff056ab80ed78fd8c15cff57715a1bf34ec76a93661c0029", +} + +// DeeplyNestedArray is an auto generated Go binding around an Ethereum contract. +type DeeplyNestedArray struct { + abi abi.ABI +} + +// NewDeeplyNestedArray creates a new instance of DeeplyNestedArray. +func NewDeeplyNestedArray() *DeeplyNestedArray { + parsed, err := DeeplyNestedArrayMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &DeeplyNestedArray{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *DeeplyNestedArray) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackDeepUint64Array is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x98ed1856. +// +// Solidity: function deepUint64Array(uint256 , uint256 , uint256 ) view returns(uint64) +func (deeplyNestedArray *DeeplyNestedArray) PackDeepUint64Array(arg0 *big.Int, arg1 *big.Int, arg2 *big.Int) []byte { + enc, err := deeplyNestedArray.abi.Pack("deepUint64Array", arg0, arg1, arg2) + if err != nil { + panic(err) + } + return enc +} + +// UnpackDeepUint64Array is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x98ed1856. +// +// Solidity: function deepUint64Array(uint256 , uint256 , uint256 ) view returns(uint64) +func (deeplyNestedArray *DeeplyNestedArray) UnpackDeepUint64Array(data []byte) (uint64, error) { + out, err := deeplyNestedArray.abi.Unpack("deepUint64Array", data) + if err != nil { + return *new(uint64), err + } + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + return out0, err +} + +// PackRetrieveDeepArray is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x8ed4573a. +// +// Solidity: function retrieveDeepArray() view returns(uint64[3][4][5]) +func (deeplyNestedArray *DeeplyNestedArray) PackRetrieveDeepArray() []byte { + enc, err := deeplyNestedArray.abi.Pack("retrieveDeepArray") + if err != nil { + panic(err) + } + return enc +} + +// UnpackRetrieveDeepArray is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x8ed4573a. +// +// Solidity: function retrieveDeepArray() view returns(uint64[3][4][5]) +func (deeplyNestedArray *DeeplyNestedArray) UnpackRetrieveDeepArray(data []byte) ([5][4][3]uint64, error) { + out, err := deeplyNestedArray.abi.Unpack("retrieveDeepArray", data) + if err != nil { + return *new([5][4][3]uint64), err + } + out0 := *abi.ConvertType(out[0], new([5][4][3]uint64)).(*[5][4][3]uint64) + return out0, err +} + +// PackStoreDeepUintArray is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x34424855. +// +// Solidity: function storeDeepUintArray(uint64[3][4][5] arr) returns() +func (deeplyNestedArray *DeeplyNestedArray) PackStoreDeepUintArray(arr [5][4][3]uint64) []byte { + enc, err := deeplyNestedArray.abi.Pack("storeDeepUintArray", arr) + if err != nil { + panic(err) + } + return enc +} diff --git a/accounts/abi/abigen/testdata/v2/empty.go.txt b/accounts/abi/abigen/testdata/v2/empty.go.txt new file mode 100644 index 00000000000..7082e207990 --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/empty.go.txt @@ -0,0 +1,52 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// EmptyMetaData contains all meta data concerning the Empty contract. +var EmptyMetaData = bind.MetaData{ + ABI: "[]", + ID: "c4ce3210982aa6fc94dabe46dc1dbf454d", + Bin: "0x606060405260068060106000396000f3606060405200", +} + +// Empty is an auto generated Go binding around an Ethereum contract. +type Empty struct { + abi abi.ABI +} + +// NewEmpty creates a new instance of Empty. +func NewEmpty() *Empty { + parsed, err := EmptyMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &Empty{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *Empty) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} diff --git a/accounts/abi/abigen/testdata/v2/eventchecker.go.txt b/accounts/abi/abigen/testdata/v2/eventchecker.go.txt new file mode 100644 index 00000000000..92558c5efee --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/eventchecker.go.txt @@ -0,0 +1,261 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// EventCheckerMetaData contains all meta data concerning the EventChecker contract. +var EventCheckerMetaData = bind.MetaData{ + ABI: "[{\"type\":\"event\",\"name\":\"empty\",\"inputs\":[]},{\"type\":\"event\",\"name\":\"indexed\",\"inputs\":[{\"name\":\"addr\",\"type\":\"address\",\"indexed\":true},{\"name\":\"num\",\"type\":\"int256\",\"indexed\":true}]},{\"type\":\"event\",\"name\":\"mixed\",\"inputs\":[{\"name\":\"addr\",\"type\":\"address\",\"indexed\":true},{\"name\":\"num\",\"type\":\"int256\"}]},{\"type\":\"event\",\"name\":\"anonymous\",\"anonymous\":true,\"inputs\":[]},{\"type\":\"event\",\"name\":\"dynamic\",\"inputs\":[{\"name\":\"idxStr\",\"type\":\"string\",\"indexed\":true},{\"name\":\"idxDat\",\"type\":\"bytes\",\"indexed\":true},{\"name\":\"str\",\"type\":\"string\"},{\"name\":\"dat\",\"type\":\"bytes\"}]},{\"type\":\"event\",\"name\":\"unnamed\",\"inputs\":[{\"name\":\"\",\"type\":\"uint256\",\"indexed\":true},{\"name\":\"\",\"type\":\"uint256\",\"indexed\":true}]}]", + ID: "253d421f98e29b25315bde79c1251ab27c", +} + +// EventChecker is an auto generated Go binding around an Ethereum contract. +type EventChecker struct { + abi abi.ABI +} + +// NewEventChecker creates a new instance of EventChecker. +func NewEventChecker() *EventChecker { + parsed, err := EventCheckerMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &EventChecker{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *EventChecker) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// EventCheckerDynamic represents a dynamic event raised by the EventChecker contract. +type EventCheckerDynamic struct { + IdxStr common.Hash + IdxDat common.Hash + Str string + Dat []byte + Raw *types.Log // Blockchain specific contextual infos +} + +const EventCheckerDynamicEventName = "dynamic" + +// ContractEventName returns the user-defined event name. +func (EventCheckerDynamic) ContractEventName() string { + return EventCheckerDynamicEventName +} + +// UnpackDynamicEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event dynamic(string indexed idxStr, bytes indexed idxDat, string str, bytes dat) +func (eventChecker *EventChecker) UnpackDynamicEvent(log *types.Log) (*EventCheckerDynamic, error) { + event := "dynamic" + if log.Topics[0] != eventChecker.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(EventCheckerDynamic) + if len(log.Data) > 0 { + if err := eventChecker.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range eventChecker.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} + +// EventCheckerEmpty represents a empty event raised by the EventChecker contract. +type EventCheckerEmpty struct { + Raw *types.Log // Blockchain specific contextual infos +} + +const EventCheckerEmptyEventName = "empty" + +// ContractEventName returns the user-defined event name. +func (EventCheckerEmpty) ContractEventName() string { + return EventCheckerEmptyEventName +} + +// UnpackEmptyEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event empty() +func (eventChecker *EventChecker) UnpackEmptyEvent(log *types.Log) (*EventCheckerEmpty, error) { + event := "empty" + if log.Topics[0] != eventChecker.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(EventCheckerEmpty) + if len(log.Data) > 0 { + if err := eventChecker.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range eventChecker.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} + +// EventCheckerIndexed represents a indexed event raised by the EventChecker contract. +type EventCheckerIndexed struct { + Addr common.Address + Num *big.Int + Raw *types.Log // Blockchain specific contextual infos +} + +const EventCheckerIndexedEventName = "indexed" + +// ContractEventName returns the user-defined event name. +func (EventCheckerIndexed) ContractEventName() string { + return EventCheckerIndexedEventName +} + +// UnpackIndexedEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event indexed(address indexed addr, int256 indexed num) +func (eventChecker *EventChecker) UnpackIndexedEvent(log *types.Log) (*EventCheckerIndexed, error) { + event := "indexed" + if log.Topics[0] != eventChecker.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(EventCheckerIndexed) + if len(log.Data) > 0 { + if err := eventChecker.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range eventChecker.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} + +// EventCheckerMixed represents a mixed event raised by the EventChecker contract. +type EventCheckerMixed struct { + Addr common.Address + Num *big.Int + Raw *types.Log // Blockchain specific contextual infos +} + +const EventCheckerMixedEventName = "mixed" + +// ContractEventName returns the user-defined event name. +func (EventCheckerMixed) ContractEventName() string { + return EventCheckerMixedEventName +} + +// UnpackMixedEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event mixed(address indexed addr, int256 num) +func (eventChecker *EventChecker) UnpackMixedEvent(log *types.Log) (*EventCheckerMixed, error) { + event := "mixed" + if log.Topics[0] != eventChecker.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(EventCheckerMixed) + if len(log.Data) > 0 { + if err := eventChecker.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range eventChecker.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} + +// EventCheckerUnnamed represents a unnamed event raised by the EventChecker contract. +type EventCheckerUnnamed struct { + Arg0 *big.Int + Arg1 *big.Int + Raw *types.Log // Blockchain specific contextual infos +} + +const EventCheckerUnnamedEventName = "unnamed" + +// ContractEventName returns the user-defined event name. +func (EventCheckerUnnamed) ContractEventName() string { + return EventCheckerUnnamedEventName +} + +// UnpackUnnamedEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event unnamed(uint256 indexed arg0, uint256 indexed arg1) +func (eventChecker *EventChecker) UnpackUnnamedEvent(log *types.Log) (*EventCheckerUnnamed, error) { + event := "unnamed" + if log.Topics[0] != eventChecker.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(EventCheckerUnnamed) + if len(log.Data) > 0 { + if err := eventChecker.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range eventChecker.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} diff --git a/accounts/abi/abigen/testdata/v2/getter.go.txt b/accounts/abi/abigen/testdata/v2/getter.go.txt new file mode 100644 index 00000000000..8e6e7debbfa --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/getter.go.txt @@ -0,0 +1,89 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// GetterMetaData contains all meta data concerning the Getter contract. +var GetterMetaData = bind.MetaData{ + ABI: "[{\"constant\":true,\"inputs\":[],\"name\":\"getter\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"},{\"name\":\"\",\"type\":\"int256\"},{\"name\":\"\",\"type\":\"bytes32\"}],\"type\":\"function\"}]", + ID: "e23a74c8979fe93c9fff15e4f51535ad54", + Bin: "0x606060405260dc8060106000396000f3606060405260e060020a6000350463993a04b78114601a575b005b600060605260c0604052600260809081527f486900000000000000000000000000000000000000000000000000000000000060a05260017fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060e0829052610100819052606060c0908152600261012081905281906101409060a09080838184600060046012f1505081517fffff000000000000000000000000000000000000000000000000000000000000169091525050604051610160819003945092505050f3", +} + +// Getter is an auto generated Go binding around an Ethereum contract. +type Getter struct { + abi abi.ABI +} + +// NewGetter creates a new instance of Getter. +func NewGetter() *Getter { + parsed, err := GetterMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &Getter{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *Getter) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackGetter is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x993a04b7. +// +// Solidity: function getter() returns(string, int256, bytes32) +func (getter *Getter) PackGetter() []byte { + enc, err := getter.abi.Pack("getter") + if err != nil { + panic(err) + } + return enc +} + +// GetterOutput serves as a container for the return parameters of contract +// method Getter. +type GetterOutput struct { + Arg0 string + Arg1 *big.Int + Arg2 [32]byte +} + +// UnpackGetter is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x993a04b7. +// +// Solidity: function getter() returns(string, int256, bytes32) +func (getter *Getter) UnpackGetter(data []byte) (GetterOutput, error) { + out, err := getter.abi.Unpack("getter", data) + outstruct := new(GetterOutput) + if err != nil { + return *outstruct, err + } + outstruct.Arg0 = *abi.ConvertType(out[0], new(string)).(*string) + outstruct.Arg1 = abi.ConvertType(out[1], new(big.Int)).(*big.Int) + outstruct.Arg2 = *abi.ConvertType(out[2], new([32]byte)).(*[32]byte) + return *outstruct, err + +} diff --git a/accounts/abi/abigen/testdata/v2/identifiercollision.go.txt b/accounts/abi/abigen/testdata/v2/identifiercollision.go.txt new file mode 100644 index 00000000000..60554aac132 --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/identifiercollision.go.txt @@ -0,0 +1,102 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// IdentifierCollisionMetaData contains all meta data concerning the IdentifierCollision contract. +var IdentifierCollisionMetaData = bind.MetaData{ + ABI: "[{\"constant\":true,\"inputs\":[],\"name\":\"MyVar\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"_myVar\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]", + ID: "1863c5622f8ac2c09c42f063ca883fe438", + Bin: "0x60806040523480156100115760006000fd5b50610017565b60c3806100256000396000f3fe608060405234801560105760006000fd5b506004361060365760003560e01c806301ad4d8714603c5780634ef1f0ad146058576036565b60006000fd5b60426074565b6040518082815260200191505060405180910390f35b605e607d565b6040518082815260200191505060405180910390f35b60006000505481565b60006000600050549050608b565b9056fea265627a7a7231582067c8d84688b01c4754ba40a2a871cede94ea1f28b5981593ab2a45b46ac43af664736f6c634300050c0032", +} + +// IdentifierCollision is an auto generated Go binding around an Ethereum contract. +type IdentifierCollision struct { + abi abi.ABI +} + +// NewIdentifierCollision creates a new instance of IdentifierCollision. +func NewIdentifierCollision() *IdentifierCollision { + parsed, err := IdentifierCollisionMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &IdentifierCollision{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *IdentifierCollision) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackMyVar is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x4ef1f0ad. +// +// Solidity: function MyVar() view returns(uint256) +func (identifierCollision *IdentifierCollision) PackMyVar() []byte { + enc, err := identifierCollision.abi.Pack("MyVar") + if err != nil { + panic(err) + } + return enc +} + +// UnpackMyVar is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x4ef1f0ad. +// +// Solidity: function MyVar() view returns(uint256) +func (identifierCollision *IdentifierCollision) UnpackMyVar(data []byte) (*big.Int, error) { + out, err := identifierCollision.abi.Unpack("MyVar", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackPubVar is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x01ad4d87. +// +// Solidity: function _myVar() view returns(uint256) +func (identifierCollision *IdentifierCollision) PackPubVar() []byte { + enc, err := identifierCollision.abi.Pack("_myVar") + if err != nil { + panic(err) + } + return enc +} + +// UnpackPubVar is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x01ad4d87. +// +// Solidity: function _myVar() view returns(uint256) +func (identifierCollision *IdentifierCollision) UnpackPubVar(data []byte) (*big.Int, error) { + out, err := identifierCollision.abi.Unpack("_myVar", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} diff --git a/accounts/abi/abigen/testdata/v2/inputchecker.go.txt b/accounts/abi/abigen/testdata/v2/inputchecker.go.txt new file mode 100644 index 00000000000..7b226aa90bd --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/inputchecker.go.txt @@ -0,0 +1,123 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// InputCheckerMetaData contains all meta data concerning the InputChecker contract. +var InputCheckerMetaData = bind.MetaData{ + ABI: "[{\"type\":\"function\",\"name\":\"noInput\",\"constant\":true,\"inputs\":[],\"outputs\":[]},{\"type\":\"function\",\"name\":\"namedInput\",\"constant\":true,\"inputs\":[{\"name\":\"str\",\"type\":\"string\"}],\"outputs\":[]},{\"type\":\"function\",\"name\":\"anonInput\",\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"string\"}],\"outputs\":[]},{\"type\":\"function\",\"name\":\"namedInputs\",\"constant\":true,\"inputs\":[{\"name\":\"str1\",\"type\":\"string\"},{\"name\":\"str2\",\"type\":\"string\"}],\"outputs\":[]},{\"type\":\"function\",\"name\":\"anonInputs\",\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"string\"},{\"name\":\"\",\"type\":\"string\"}],\"outputs\":[]},{\"type\":\"function\",\"name\":\"mixedInputs\",\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"string\"},{\"name\":\"str\",\"type\":\"string\"}],\"outputs\":[]}]", + ID: "e551ce092312e54f54f45ffdf06caa4cdc", +} + +// InputChecker is an auto generated Go binding around an Ethereum contract. +type InputChecker struct { + abi abi.ABI +} + +// NewInputChecker creates a new instance of InputChecker. +func NewInputChecker() *InputChecker { + parsed, err := InputCheckerMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &InputChecker{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *InputChecker) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackAnonInput is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x3e708e82. +// +// Solidity: function anonInput(string ) returns() +func (inputChecker *InputChecker) PackAnonInput(arg0 string) []byte { + enc, err := inputChecker.abi.Pack("anonInput", arg0) + if err != nil { + panic(err) + } + return enc +} + +// PackAnonInputs is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x28160527. +// +// Solidity: function anonInputs(string , string ) returns() +func (inputChecker *InputChecker) PackAnonInputs(arg0 string, arg1 string) []byte { + enc, err := inputChecker.abi.Pack("anonInputs", arg0, arg1) + if err != nil { + panic(err) + } + return enc +} + +// PackMixedInputs is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xc689ebdc. +// +// Solidity: function mixedInputs(string , string str) returns() +func (inputChecker *InputChecker) PackMixedInputs(arg0 string, str string) []byte { + enc, err := inputChecker.abi.Pack("mixedInputs", arg0, str) + if err != nil { + panic(err) + } + return enc +} + +// PackNamedInput is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x0d402005. +// +// Solidity: function namedInput(string str) returns() +func (inputChecker *InputChecker) PackNamedInput(str string) []byte { + enc, err := inputChecker.abi.Pack("namedInput", str) + if err != nil { + panic(err) + } + return enc +} + +// PackNamedInputs is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x63c796ed. +// +// Solidity: function namedInputs(string str1, string str2) returns() +func (inputChecker *InputChecker) PackNamedInputs(str1 string, str2 string) []byte { + enc, err := inputChecker.abi.Pack("namedInputs", str1, str2) + if err != nil { + panic(err) + } + return enc +} + +// PackNoInput is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x53539029. +// +// Solidity: function noInput() returns() +func (inputChecker *InputChecker) PackNoInput() []byte { + enc, err := inputChecker.abi.Pack("noInput") + if err != nil { + panic(err) + } + return enc +} diff --git a/accounts/abi/abigen/testdata/v2/interactor.go.txt b/accounts/abi/abigen/testdata/v2/interactor.go.txt new file mode 100644 index 00000000000..cc0900856ec --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/interactor.go.txt @@ -0,0 +1,126 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// InteractorMetaData contains all meta data concerning the Interactor contract. +var InteractorMetaData = bind.MetaData{ + ABI: "[{\"constant\":true,\"inputs\":[],\"name\":\"transactString\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"deployString\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"str\",\"type\":\"string\"}],\"name\":\"transact\",\"outputs\":[],\"type\":\"function\"},{\"inputs\":[{\"name\":\"str\",\"type\":\"string\"}],\"type\":\"constructor\"}]", + ID: "f63980878028f3242c9033fdc30fd21a81", + Bin: "0x6060604052604051610328380380610328833981016040528051018060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10608d57805160ff19168380011785555b50607c9291505b8082111560ba57838155600101606b565b50505061026a806100be6000396000f35b828001600101855582156064579182015b828111156064578251826000505591602001919060010190609e565b509056606060405260e060020a60003504630d86a0e181146100315780636874e8091461008d578063d736c513146100ea575b005b610190600180546020600282841615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102295780601f106101fe57610100808354040283529160200191610229565b61019060008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156102295780601f106101fe57610100808354040283529160200191610229565b60206004803580820135601f81018490049093026080908101604052606084815261002f946024939192918401918190838280828437509496505050505050508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023157805160ff19168380011785555b506102619291505b808211156102665760008155830161017d565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156101f05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b820191906000526020600020905b81548152906001019060200180831161020c57829003601f168201915b505050505081565b82800160010185558215610175579182015b82811115610175578251826000505591602001919060010190610243565b505050565b509056", +} + +// Interactor is an auto generated Go binding around an Ethereum contract. +type Interactor struct { + abi abi.ABI +} + +// NewInteractor creates a new instance of Interactor. +func NewInteractor() *Interactor { + parsed, err := InteractorMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &Interactor{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *Interactor) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackConstructor is the Go binding used to pack the parameters required for +// contract deployment. +// +// Solidity: constructor(string str) returns() +func (interactor *Interactor) PackConstructor(str string) []byte { + enc, err := interactor.abi.Pack("", str) + if err != nil { + panic(err) + } + return enc +} + +// PackDeployString is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x6874e809. +// +// Solidity: function deployString() returns(string) +func (interactor *Interactor) PackDeployString() []byte { + enc, err := interactor.abi.Pack("deployString") + if err != nil { + panic(err) + } + return enc +} + +// UnpackDeployString is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x6874e809. +// +// Solidity: function deployString() returns(string) +func (interactor *Interactor) UnpackDeployString(data []byte) (string, error) { + out, err := interactor.abi.Unpack("deployString", data) + if err != nil { + return *new(string), err + } + out0 := *abi.ConvertType(out[0], new(string)).(*string) + return out0, err +} + +// PackTransact is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xd736c513. +// +// Solidity: function transact(string str) returns() +func (interactor *Interactor) PackTransact(str string) []byte { + enc, err := interactor.abi.Pack("transact", str) + if err != nil { + panic(err) + } + return enc +} + +// PackTransactString is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x0d86a0e1. +// +// Solidity: function transactString() returns(string) +func (interactor *Interactor) PackTransactString() []byte { + enc, err := interactor.abi.Pack("transactString") + if err != nil { + panic(err) + } + return enc +} + +// UnpackTransactString is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x0d86a0e1. +// +// Solidity: function transactString() returns(string) +func (interactor *Interactor) UnpackTransactString(data []byte) (string, error) { + out, err := interactor.abi.Unpack("transactString", data) + if err != nil { + return *new(string), err + } + out0 := *abi.ConvertType(out[0], new(string)).(*string) + return out0, err +} diff --git a/accounts/abi/abigen/testdata/v2/nameconflict.go.txt b/accounts/abi/abigen/testdata/v2/nameconflict.go.txt new file mode 100644 index 00000000000..6228bf7cc73 --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/nameconflict.go.txt @@ -0,0 +1,137 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// Oraclerequest is an auto generated low-level Go binding around an user-defined struct. +type Oraclerequest struct { + Data []byte + Data0 []byte +} + +// NameConflictMetaData contains all meta data concerning the NameConflict contract. +var NameConflictMetaData = bind.MetaData{ + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"msg\",\"type\":\"int256\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"_msg\",\"type\":\"int256\"}],\"name\":\"log\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"internalType\":\"structoracle.request\",\"name\":\"req\",\"type\":\"tuple\"}],\"name\":\"addRequest\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRequest\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"internalType\":\"structoracle.request\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "8f6e2703b307244ae6bd61ed94ce959cf9", + Bin: "0x608060405234801561001057600080fd5b5061042b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063c2bb515f1461003b578063cce7b04814610059575b600080fd5b610043610075565b60405161005091906101af565b60405180910390f35b610073600480360381019061006e91906103ac565b6100b5565b005b61007d6100b8565b604051806040016040528060405180602001604052806000815250815260200160405180602001604052806000815250815250905090565b50565b604051806040016040528060608152602001606081525090565b600081519050919050565b600082825260208201905092915050565b60005b8381101561010c5780820151818401526020810190506100f1565b8381111561011b576000848401525b50505050565b6000601f19601f8301169050919050565b600061013d826100d2565b61014781856100dd565b93506101578185602086016100ee565b61016081610121565b840191505092915050565b600060408301600083015184820360008601526101888282610132565b915050602083015184820360208601526101a28282610132565b9150508091505092915050565b600060208201905081810360008301526101c9818461016b565b905092915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61022282610121565b810181811067ffffffffffffffff82111715610241576102406101ea565b5b80604052505050565b60006102546101d1565b90506102608282610219565b919050565b600080fd5b600080fd5b600080fd5b600067ffffffffffffffff82111561028f5761028e6101ea565b5b61029882610121565b9050602081019050919050565b82818337600083830152505050565b60006102c76102c284610274565b61024a565b9050828152602081018484840111156102e3576102e261026f565b5b6102ee8482856102a5565b509392505050565b600082601f83011261030b5761030a61026a565b5b813561031b8482602086016102b4565b91505092915050565b60006040828403121561033a576103396101e5565b5b610344604061024a565b9050600082013567ffffffffffffffff81111561036457610363610265565b5b610370848285016102f6565b600083015250602082013567ffffffffffffffff81111561039457610393610265565b5b6103a0848285016102f6565b60208301525092915050565b6000602082840312156103c2576103c16101db565b5b600082013567ffffffffffffffff8111156103e0576103df6101e0565b5b6103ec84828501610324565b9150509291505056fea264697066735822122033bca1606af9b6aeba1673f98c52003cec19338539fb44b86690ce82c51483b564736f6c634300080e0033", +} + +// NameConflict is an auto generated Go binding around an Ethereum contract. +type NameConflict struct { + abi abi.ABI +} + +// NewNameConflict creates a new instance of NameConflict. +func NewNameConflict() *NameConflict { + parsed, err := NameConflictMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &NameConflict{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *NameConflict) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackAddRequest is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xcce7b048. +// +// Solidity: function addRequest((bytes,bytes) req) pure returns() +func (nameConflict *NameConflict) PackAddRequest(req Oraclerequest) []byte { + enc, err := nameConflict.abi.Pack("addRequest", req) + if err != nil { + panic(err) + } + return enc +} + +// PackGetRequest is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xc2bb515f. +// +// Solidity: function getRequest() pure returns((bytes,bytes)) +func (nameConflict *NameConflict) PackGetRequest() []byte { + enc, err := nameConflict.abi.Pack("getRequest") + if err != nil { + panic(err) + } + return enc +} + +// UnpackGetRequest is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xc2bb515f. +// +// Solidity: function getRequest() pure returns((bytes,bytes)) +func (nameConflict *NameConflict) UnpackGetRequest(data []byte) (Oraclerequest, error) { + out, err := nameConflict.abi.Unpack("getRequest", data) + if err != nil { + return *new(Oraclerequest), err + } + out0 := *abi.ConvertType(out[0], new(Oraclerequest)).(*Oraclerequest) + return out0, err +} + +// NameConflictLog represents a log event raised by the NameConflict contract. +type NameConflictLog struct { + Msg *big.Int + Msg0 *big.Int + Raw *types.Log // Blockchain specific contextual infos +} + +const NameConflictLogEventName = "log" + +// ContractEventName returns the user-defined event name. +func (NameConflictLog) ContractEventName() string { + return NameConflictLogEventName +} + +// UnpackLogEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event log(int256 msg, int256 _msg) +func (nameConflict *NameConflict) UnpackLogEvent(log *types.Log) (*NameConflictLog, error) { + event := "log" + if log.Topics[0] != nameConflict.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(NameConflictLog) + if len(log.Data) > 0 { + if err := nameConflict.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range nameConflict.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} diff --git a/accounts/abi/abigen/testdata/v2/numericmethodname.go.txt b/accounts/abi/abigen/testdata/v2/numericmethodname.go.txt new file mode 100644 index 00000000000..5a2208e0d42 --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/numericmethodname.go.txt @@ -0,0 +1,129 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// NumericMethodNameMetaData contains all meta data concerning the NumericMethodName contract. +var NumericMethodNameMetaData = bind.MetaData{ + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_param\",\"type\":\"address\"}],\"name\":\"_1TestEvent\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"_1test\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"__1test\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"__2test\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "a691b347afbc44b90dd9a1dfbc65661904", + Bin: "0x6080604052348015600f57600080fd5b5060958061001e6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80639d993132146041578063d02767c7146049578063ffa02795146051575b600080fd5b60476059565b005b604f605b565b005b6057605d565b005b565b565b56fea26469706673582212200382ca602dff96a7e2ba54657985e2b4ac423a56abe4a1f0667bc635c4d4371f64736f6c63430008110033", +} + +// NumericMethodName is an auto generated Go binding around an Ethereum contract. +type NumericMethodName struct { + abi abi.ABI +} + +// NewNumericMethodName creates a new instance of NumericMethodName. +func NewNumericMethodName() *NumericMethodName { + parsed, err := NumericMethodNameMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &NumericMethodName{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *NumericMethodName) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackE1test is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xffa02795. +// +// Solidity: function _1test() pure returns() +func (numericMethodName *NumericMethodName) PackE1test() []byte { + enc, err := numericMethodName.abi.Pack("_1test") + if err != nil { + panic(err) + } + return enc +} + +// PackE1test0 is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xd02767c7. +// +// Solidity: function __1test() pure returns() +func (numericMethodName *NumericMethodName) PackE1test0() []byte { + enc, err := numericMethodName.abi.Pack("__1test") + if err != nil { + panic(err) + } + return enc +} + +// PackE2test is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x9d993132. +// +// Solidity: function __2test() pure returns() +func (numericMethodName *NumericMethodName) PackE2test() []byte { + enc, err := numericMethodName.abi.Pack("__2test") + if err != nil { + panic(err) + } + return enc +} + +// NumericMethodNameE1TestEvent represents a _1TestEvent event raised by the NumericMethodName contract. +type NumericMethodNameE1TestEvent struct { + Param common.Address + Raw *types.Log // Blockchain specific contextual infos +} + +const NumericMethodNameE1TestEventEventName = "_1TestEvent" + +// ContractEventName returns the user-defined event name. +func (NumericMethodNameE1TestEvent) ContractEventName() string { + return NumericMethodNameE1TestEventEventName +} + +// UnpackE1TestEventEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event _1TestEvent(address _param) +func (numericMethodName *NumericMethodName) UnpackE1TestEventEvent(log *types.Log) (*NumericMethodNameE1TestEvent, error) { + event := "_1TestEvent" + if log.Topics[0] != numericMethodName.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(NumericMethodNameE1TestEvent) + if len(log.Data) > 0 { + if err := numericMethodName.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range numericMethodName.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} diff --git a/accounts/abi/abigen/testdata/v2/outputchecker.go.txt b/accounts/abi/abigen/testdata/v2/outputchecker.go.txt new file mode 100644 index 00000000000..6f1f8e67957 --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/outputchecker.go.txt @@ -0,0 +1,253 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// OutputCheckerMetaData contains all meta data concerning the OutputChecker contract. +var OutputCheckerMetaData = bind.MetaData{ + ABI: "[{\"type\":\"function\",\"name\":\"noOutput\",\"constant\":true,\"inputs\":[],\"outputs\":[]},{\"type\":\"function\",\"name\":\"namedOutput\",\"constant\":true,\"inputs\":[],\"outputs\":[{\"name\":\"str\",\"type\":\"string\"}]},{\"type\":\"function\",\"name\":\"anonOutput\",\"constant\":true,\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\"}]},{\"type\":\"function\",\"name\":\"namedOutputs\",\"constant\":true,\"inputs\":[],\"outputs\":[{\"name\":\"str1\",\"type\":\"string\"},{\"name\":\"str2\",\"type\":\"string\"}]},{\"type\":\"function\",\"name\":\"collidingOutputs\",\"constant\":true,\"inputs\":[],\"outputs\":[{\"name\":\"str\",\"type\":\"string\"},{\"name\":\"Str\",\"type\":\"string\"}]},{\"type\":\"function\",\"name\":\"anonOutputs\",\"constant\":true,\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\"},{\"name\":\"\",\"type\":\"string\"}]},{\"type\":\"function\",\"name\":\"mixedOutputs\",\"constant\":true,\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"string\"},{\"name\":\"str\",\"type\":\"string\"}]}]", + ID: "cc1d4e235801a590b506d5130b0cca90a1", +} + +// OutputChecker is an auto generated Go binding around an Ethereum contract. +type OutputChecker struct { + abi abi.ABI +} + +// NewOutputChecker creates a new instance of OutputChecker. +func NewOutputChecker() *OutputChecker { + parsed, err := OutputCheckerMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &OutputChecker{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *OutputChecker) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackAnonOutput is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x008bda05. +// +// Solidity: function anonOutput() returns(string) +func (outputChecker *OutputChecker) PackAnonOutput() []byte { + enc, err := outputChecker.abi.Pack("anonOutput") + if err != nil { + panic(err) + } + return enc +} + +// UnpackAnonOutput is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x008bda05. +// +// Solidity: function anonOutput() returns(string) +func (outputChecker *OutputChecker) UnpackAnonOutput(data []byte) (string, error) { + out, err := outputChecker.abi.Unpack("anonOutput", data) + if err != nil { + return *new(string), err + } + out0 := *abi.ConvertType(out[0], new(string)).(*string) + return out0, err +} + +// PackAnonOutputs is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x3c401115. +// +// Solidity: function anonOutputs() returns(string, string) +func (outputChecker *OutputChecker) PackAnonOutputs() []byte { + enc, err := outputChecker.abi.Pack("anonOutputs") + if err != nil { + panic(err) + } + return enc +} + +// AnonOutputsOutput serves as a container for the return parameters of contract +// method AnonOutputs. +type AnonOutputsOutput struct { + Arg0 string + Arg1 string +} + +// UnpackAnonOutputs is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x3c401115. +// +// Solidity: function anonOutputs() returns(string, string) +func (outputChecker *OutputChecker) UnpackAnonOutputs(data []byte) (AnonOutputsOutput, error) { + out, err := outputChecker.abi.Unpack("anonOutputs", data) + outstruct := new(AnonOutputsOutput) + if err != nil { + return *outstruct, err + } + outstruct.Arg0 = *abi.ConvertType(out[0], new(string)).(*string) + outstruct.Arg1 = *abi.ConvertType(out[1], new(string)).(*string) + return *outstruct, err + +} + +// PackCollidingOutputs is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xeccbc1ee. +// +// Solidity: function collidingOutputs() returns(string str, string Str) +func (outputChecker *OutputChecker) PackCollidingOutputs() []byte { + enc, err := outputChecker.abi.Pack("collidingOutputs") + if err != nil { + panic(err) + } + return enc +} + +// CollidingOutputsOutput serves as a container for the return parameters of contract +// method CollidingOutputs. +type CollidingOutputsOutput struct { + Str string + Str0 string +} + +// UnpackCollidingOutputs is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xeccbc1ee. +// +// Solidity: function collidingOutputs() returns(string str, string Str) +func (outputChecker *OutputChecker) UnpackCollidingOutputs(data []byte) (CollidingOutputsOutput, error) { + out, err := outputChecker.abi.Unpack("collidingOutputs", data) + outstruct := new(CollidingOutputsOutput) + if err != nil { + return *outstruct, err + } + outstruct.Str = *abi.ConvertType(out[0], new(string)).(*string) + outstruct.Str0 = *abi.ConvertType(out[1], new(string)).(*string) + return *outstruct, err + +} + +// PackMixedOutputs is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x21b77b44. +// +// Solidity: function mixedOutputs() returns(string, string str) +func (outputChecker *OutputChecker) PackMixedOutputs() []byte { + enc, err := outputChecker.abi.Pack("mixedOutputs") + if err != nil { + panic(err) + } + return enc +} + +// MixedOutputsOutput serves as a container for the return parameters of contract +// method MixedOutputs. +type MixedOutputsOutput struct { + Arg0 string + Str string +} + +// UnpackMixedOutputs is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x21b77b44. +// +// Solidity: function mixedOutputs() returns(string, string str) +func (outputChecker *OutputChecker) UnpackMixedOutputs(data []byte) (MixedOutputsOutput, error) { + out, err := outputChecker.abi.Unpack("mixedOutputs", data) + outstruct := new(MixedOutputsOutput) + if err != nil { + return *outstruct, err + } + outstruct.Arg0 = *abi.ConvertType(out[0], new(string)).(*string) + outstruct.Str = *abi.ConvertType(out[1], new(string)).(*string) + return *outstruct, err + +} + +// PackNamedOutput is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x5e632bd5. +// +// Solidity: function namedOutput() returns(string str) +func (outputChecker *OutputChecker) PackNamedOutput() []byte { + enc, err := outputChecker.abi.Pack("namedOutput") + if err != nil { + panic(err) + } + return enc +} + +// UnpackNamedOutput is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x5e632bd5. +// +// Solidity: function namedOutput() returns(string str) +func (outputChecker *OutputChecker) UnpackNamedOutput(data []byte) (string, error) { + out, err := outputChecker.abi.Unpack("namedOutput", data) + if err != nil { + return *new(string), err + } + out0 := *abi.ConvertType(out[0], new(string)).(*string) + return out0, err +} + +// PackNamedOutputs is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x7970a189. +// +// Solidity: function namedOutputs() returns(string str1, string str2) +func (outputChecker *OutputChecker) PackNamedOutputs() []byte { + enc, err := outputChecker.abi.Pack("namedOutputs") + if err != nil { + panic(err) + } + return enc +} + +// NamedOutputsOutput serves as a container for the return parameters of contract +// method NamedOutputs. +type NamedOutputsOutput struct { + Str1 string + Str2 string +} + +// UnpackNamedOutputs is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x7970a189. +// +// Solidity: function namedOutputs() returns(string str1, string str2) +func (outputChecker *OutputChecker) UnpackNamedOutputs(data []byte) (NamedOutputsOutput, error) { + out, err := outputChecker.abi.Unpack("namedOutputs", data) + outstruct := new(NamedOutputsOutput) + if err != nil { + return *outstruct, err + } + outstruct.Str1 = *abi.ConvertType(out[0], new(string)).(*string) + outstruct.Str2 = *abi.ConvertType(out[1], new(string)).(*string) + return *outstruct, err + +} + +// PackNoOutput is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x625f0306. +// +// Solidity: function noOutput() returns() +func (outputChecker *OutputChecker) PackNoOutput() []byte { + enc, err := outputChecker.abi.Pack("noOutput") + if err != nil { + panic(err) + } + return enc +} diff --git a/accounts/abi/abigen/testdata/v2/overload.go.txt b/accounts/abi/abigen/testdata/v2/overload.go.txt new file mode 100644 index 00000000000..ed7c0b543ce --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/overload.go.txt @@ -0,0 +1,159 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// OverloadMetaData contains all meta data concerning the Overload contract. +var OverloadMetaData = bind.MetaData{ + ABI: "[{\"constant\":false,\"inputs\":[{\"name\":\"i\",\"type\":\"uint256\"},{\"name\":\"j\",\"type\":\"uint256\"}],\"name\":\"foo\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"i\",\"type\":\"uint256\"}],\"name\":\"foo\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"i\",\"type\":\"uint256\"}],\"name\":\"bar\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"i\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"j\",\"type\":\"uint256\"}],\"name\":\"bar\",\"type\":\"event\"}]", + ID: "f49f0ff7ed407de5c37214f49309072aec", + Bin: "0x608060405234801561001057600080fd5b50610153806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806304bc52f81461003b5780632fbebd3814610073575b600080fd5b6100716004803603604081101561005157600080fd5b8101908080359060200190929190803590602001909291905050506100a1565b005b61009f6004803603602081101561008957600080fd5b81019080803590602001909291905050506100e4565b005b7fae42e9514233792a47a1e4554624e83fe852228e1503f63cd383e8a431f4f46d8282604051808381526020018281526020019250505060405180910390a15050565b7f0423a1321222a0a8716c22b92fac42d85a45a612b696a461784d9fa537c81e5c816040518082815260200191505060405180910390a15056fea265627a7a72305820e22b049858b33291cbe67eeaece0c5f64333e439d27032ea8337d08b1de18fe864736f6c634300050a0032", +} + +// Overload is an auto generated Go binding around an Ethereum contract. +type Overload struct { + abi abi.ABI +} + +// NewOverload creates a new instance of Overload. +func NewOverload() *Overload { + parsed, err := OverloadMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &Overload{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *Overload) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackFoo is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x04bc52f8. +// +// Solidity: function foo(uint256 i, uint256 j) returns() +func (overload *Overload) PackFoo(i *big.Int, j *big.Int) []byte { + enc, err := overload.abi.Pack("foo", i, j) + if err != nil { + panic(err) + } + return enc +} + +// PackFoo0 is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x2fbebd38. +// +// Solidity: function foo(uint256 i) returns() +func (overload *Overload) PackFoo0(i *big.Int) []byte { + enc, err := overload.abi.Pack("foo0", i) + if err != nil { + panic(err) + } + return enc +} + +// OverloadBar represents a bar event raised by the Overload contract. +type OverloadBar struct { + I *big.Int + Raw *types.Log // Blockchain specific contextual infos +} + +const OverloadBarEventName = "bar" + +// ContractEventName returns the user-defined event name. +func (OverloadBar) ContractEventName() string { + return OverloadBarEventName +} + +// UnpackBarEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event bar(uint256 i) +func (overload *Overload) UnpackBarEvent(log *types.Log) (*OverloadBar, error) { + event := "bar" + if log.Topics[0] != overload.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(OverloadBar) + if len(log.Data) > 0 { + if err := overload.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range overload.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} + +// OverloadBar0 represents a bar0 event raised by the Overload contract. +type OverloadBar0 struct { + I *big.Int + J *big.Int + Raw *types.Log // Blockchain specific contextual infos +} + +const OverloadBar0EventName = "bar0" + +// ContractEventName returns the user-defined event name. +func (OverloadBar0) ContractEventName() string { + return OverloadBar0EventName +} + +// UnpackBar0Event is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event bar(uint256 i, uint256 j) +func (overload *Overload) UnpackBar0Event(log *types.Log) (*OverloadBar0, error) { + event := "bar0" + if log.Topics[0] != overload.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(OverloadBar0) + if len(log.Data) > 0 { + if err := overload.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range overload.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} diff --git a/accounts/abi/abigen/testdata/v2/rangekeyword.go.txt b/accounts/abi/abigen/testdata/v2/rangekeyword.go.txt new file mode 100644 index 00000000000..c7f14253958 --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/rangekeyword.go.txt @@ -0,0 +1,64 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// RangeKeywordMetaData contains all meta data concerning the RangeKeyword contract. +var RangeKeywordMetaData = bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"range\",\"type\":\"uint256\"}],\"name\":\"functionWithKeywordParameter\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "cec8c872ba06feb1b8f0a00e7b237eb226", + Bin: "0x608060405234801561001057600080fd5b5060dc8061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063527a119f14602d575b600080fd5b60436004803603810190603f9190605b565b6045565b005b50565b6000813590506055816092565b92915050565b600060208284031215606e57606d608d565b5b6000607a848285016048565b91505092915050565b6000819050919050565b600080fd5b6099816083565b811460a357600080fd5b5056fea2646970667358221220d4f4525e2615516394055d369fb17df41c359e5e962734f27fd683ea81fd9db164736f6c63430008070033", +} + +// RangeKeyword is an auto generated Go binding around an Ethereum contract. +type RangeKeyword struct { + abi abi.ABI +} + +// NewRangeKeyword creates a new instance of RangeKeyword. +func NewRangeKeyword() *RangeKeyword { + parsed, err := RangeKeywordMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &RangeKeyword{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *RangeKeyword) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackFunctionWithKeywordParameter is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x527a119f. +// +// Solidity: function functionWithKeywordParameter(uint256 range) pure returns() +func (rangeKeyword *RangeKeyword) PackFunctionWithKeywordParameter(arg0 *big.Int) []byte { + enc, err := rangeKeyword.abi.Pack("functionWithKeywordParameter", arg0) + if err != nil { + panic(err) + } + return enc +} diff --git a/accounts/abi/abigen/testdata/v2/slicer.go.txt b/accounts/abi/abigen/testdata/v2/slicer.go.txt new file mode 100644 index 00000000000..b66c05cf0ff --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/slicer.go.txt @@ -0,0 +1,152 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// SlicerMetaData contains all meta data concerning the Slicer contract. +var SlicerMetaData = bind.MetaData{ + ABI: "[{\"constant\":true,\"inputs\":[{\"name\":\"input\",\"type\":\"address[]\"}],\"name\":\"echoAddresses\",\"outputs\":[{\"name\":\"output\",\"type\":\"address[]\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"input\",\"type\":\"uint24[23]\"}],\"name\":\"echoFancyInts\",\"outputs\":[{\"name\":\"output\",\"type\":\"uint24[23]\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"input\",\"type\":\"int256[]\"}],\"name\":\"echoInts\",\"outputs\":[{\"name\":\"output\",\"type\":\"int256[]\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"input\",\"type\":\"bool[]\"}],\"name\":\"echoBools\",\"outputs\":[{\"name\":\"output\",\"type\":\"bool[]\"}],\"type\":\"function\"}]", + ID: "082c0740ab6537c7169cb573d097c52112", + Bin: "0x606060405261015c806100126000396000f3606060405260e060020a6000350463be1127a3811461003c578063d88becc014610092578063e15a3db71461003c578063f637e5891461003c575b005b604080516020600480358082013583810285810185019096528085526100ee959294602494909392850192829185019084908082843750949650505050505050604080516020810190915260009052805b919050565b604080516102e0818101909252610138916004916102e491839060179083908390808284375090955050505050506102e0604051908101604052806017905b60008152602001906001900390816100d15790505081905061008d565b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019250505060405180910390f35b60405180826102e0808381846000600461015cf15090500191505060405180910390f3", +} + +// Slicer is an auto generated Go binding around an Ethereum contract. +type Slicer struct { + abi abi.ABI +} + +// NewSlicer creates a new instance of Slicer. +func NewSlicer() *Slicer { + parsed, err := SlicerMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &Slicer{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *Slicer) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackEchoAddresses is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xbe1127a3. +// +// Solidity: function echoAddresses(address[] input) returns(address[] output) +func (slicer *Slicer) PackEchoAddresses(input []common.Address) []byte { + enc, err := slicer.abi.Pack("echoAddresses", input) + if err != nil { + panic(err) + } + return enc +} + +// UnpackEchoAddresses is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xbe1127a3. +// +// Solidity: function echoAddresses(address[] input) returns(address[] output) +func (slicer *Slicer) UnpackEchoAddresses(data []byte) ([]common.Address, error) { + out, err := slicer.abi.Unpack("echoAddresses", data) + if err != nil { + return *new([]common.Address), err + } + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + return out0, err +} + +// PackEchoBools is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xf637e589. +// +// Solidity: function echoBools(bool[] input) returns(bool[] output) +func (slicer *Slicer) PackEchoBools(input []bool) []byte { + enc, err := slicer.abi.Pack("echoBools", input) + if err != nil { + panic(err) + } + return enc +} + +// UnpackEchoBools is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xf637e589. +// +// Solidity: function echoBools(bool[] input) returns(bool[] output) +func (slicer *Slicer) UnpackEchoBools(data []byte) ([]bool, error) { + out, err := slicer.abi.Unpack("echoBools", data) + if err != nil { + return *new([]bool), err + } + out0 := *abi.ConvertType(out[0], new([]bool)).(*[]bool) + return out0, err +} + +// PackEchoFancyInts is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xd88becc0. +// +// Solidity: function echoFancyInts(uint24[23] input) returns(uint24[23] output) +func (slicer *Slicer) PackEchoFancyInts(input [23]*big.Int) []byte { + enc, err := slicer.abi.Pack("echoFancyInts", input) + if err != nil { + panic(err) + } + return enc +} + +// UnpackEchoFancyInts is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xd88becc0. +// +// Solidity: function echoFancyInts(uint24[23] input) returns(uint24[23] output) +func (slicer *Slicer) UnpackEchoFancyInts(data []byte) ([23]*big.Int, error) { + out, err := slicer.abi.Unpack("echoFancyInts", data) + if err != nil { + return *new([23]*big.Int), err + } + out0 := *abi.ConvertType(out[0], new([23]*big.Int)).(*[23]*big.Int) + return out0, err +} + +// PackEchoInts is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xe15a3db7. +// +// Solidity: function echoInts(int256[] input) returns(int256[] output) +func (slicer *Slicer) PackEchoInts(input []*big.Int) []byte { + enc, err := slicer.abi.Pack("echoInts", input) + if err != nil { + panic(err) + } + return enc +} + +// UnpackEchoInts is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xe15a3db7. +// +// Solidity: function echoInts(int256[] input) returns(int256[] output) +func (slicer *Slicer) UnpackEchoInts(data []byte) ([]*big.Int, error) { + out, err := slicer.abi.Unpack("echoInts", data) + if err != nil { + return *new([]*big.Int), err + } + out0 := *abi.ConvertType(out[0], new([]*big.Int)).(*[]*big.Int) + return out0, err +} diff --git a/accounts/abi/abigen/testdata/v2/structs-abi.go.txt b/accounts/abi/abigen/testdata/v2/structs-abi.go.txt new file mode 100644 index 00000000000..aab62427074 --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/structs-abi.go.txt @@ -0,0 +1,116 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package v1bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// Struct0 is an auto generated low-level Go binding around an user-defined struct. +type Struct0 struct { + B [32]byte +} + +// StructsMetaData contains all meta data concerning the Structs contract. +var StructsMetaData = bind.MetaData{ + ABI: "[{\"inputs\":[],\"name\":\"F\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"B\",\"type\":\"bytes32\"}],\"internalType\":\"structStructs.A[]\",\"name\":\"a\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[]\",\"name\":\"c\",\"type\":\"uint256[]\"},{\"internalType\":\"bool[]\",\"name\":\"d\",\"type\":\"bool[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"G\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"B\",\"type\":\"bytes32\"}],\"internalType\":\"structStructs.A[]\",\"name\":\"a\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ID: "Structs", +} + +// Structs is an auto generated Go binding around an Ethereum contract. +type Structs struct { + abi abi.ABI +} + +// NewStructs creates a new instance of Structs. +func NewStructs() *Structs { + parsed, err := StructsMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &Structs{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +<<<<<<< HEAD +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *Structs) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +======= +// Use this to create the instance object passed to abigen v2 library functions Call, +// Transact, etc. +func (c *Structs) Instance(backend bind.ContractBackend, addr common.Address) bind.BoundContract { + return bind.NewBoundContract(backend, addr, c.abi) +>>>>>>> 854c25e086 (accounts/abi/abigen: improve v2 template) +} + +// F is the Go binding used to pack the parameters required for calling +// the contract method 0x28811f59. +// +// Solidity: function F() view returns((bytes32)[] a, uint256[] c, bool[] d) +func (structs *Structs) PackF() ([]byte, error) { + return structs.abi.Pack("F") +} + +// FOutput serves as a container for the return parameters of contract +// method F. +type FOutput struct { + A []Struct0 + C []*big.Int + D []bool +} + +// UnpackF is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x28811f59. +// +// Solidity: function F() view returns((bytes32)[] a, uint256[] c, bool[] d) +func (structs *Structs) UnpackF(data []byte) (*FOutput, error) { + out, err := structs.abi.Unpack("F", data) + if err != nil { + return nil, err + } + ret := new(FOutput) + ret.A = *abi.ConvertType(out[0], new([]Struct0)).(*[]Struct0) + ret.C = *abi.ConvertType(out[1], new([]*big.Int)).(*[]*big.Int) + ret.D = *abi.ConvertType(out[2], new([]bool)).(*[]bool) + return ret, nil +} + +// G is the Go binding used to pack the parameters required for calling +// the contract method 0x6fecb623. +// +// Solidity: function G() view returns((bytes32)[] a) +func (structs *Structs) PackG() ([]byte, error) { + return structs.abi.Pack("G") +} + +// UnpackG is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x6fecb623. +// +// Solidity: function G() view returns((bytes32)[] a) +func (structs *Structs) UnpackG(data []byte) (*[]Struct0, error) { + out, err := structs.abi.Unpack("G", data) + if err != nil { + return nil, err + } + out0 := *abi.ConvertType(out[0], new([]Struct0)).(*[]Struct0) + return &out0, nil +} diff --git a/accounts/abi/abigen/testdata/v2/structs.go.txt b/accounts/abi/abigen/testdata/v2/structs.go.txt new file mode 100644 index 00000000000..7fe59c5616c --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/structs.go.txt @@ -0,0 +1,119 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// Struct0 is an auto generated low-level Go binding around an user-defined struct. +type Struct0 struct { + B [32]byte +} + +// StructsMetaData contains all meta data concerning the Structs contract. +var StructsMetaData = bind.MetaData{ + ABI: "[{\"inputs\":[],\"name\":\"F\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"B\",\"type\":\"bytes32\"}],\"internalType\":\"structStructs.A[]\",\"name\":\"a\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[]\",\"name\":\"c\",\"type\":\"uint256[]\"},{\"internalType\":\"bool[]\",\"name\":\"d\",\"type\":\"bool[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"G\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"B\",\"type\":\"bytes32\"}],\"internalType\":\"structStructs.A[]\",\"name\":\"a\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ID: "920a35318e7581766aec7a17218628a91d", + Bin: "0x608060405234801561001057600080fd5b50610278806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806328811f591461003b5780636fecb6231461005b575b600080fd5b610043610070565b604051610052939291906101a0565b60405180910390f35b6100636100d6565b6040516100529190610186565b604080516002808252606082810190935282918291829190816020015b610095610131565b81526020019060019003908161008d575050805190915061026960611b9082906000906100be57fe5b60209081029190910101515293606093508392509050565b6040805160028082526060828101909352829190816020015b6100f7610131565b8152602001906001900390816100ef575050805190915061026960611b90829060009061012057fe5b602090810291909101015152905090565b60408051602081019091526000815290565b815260200190565b6000815180845260208085019450808401835b8381101561017b578151518752958201959082019060010161015e565b509495945050505050565b600060208252610199602083018461014b565b9392505050565b6000606082526101b3606083018661014b565b6020838203818501528186516101c98185610239565b91508288019350845b818110156101f3576101e5838651610143565b9484019492506001016101d2565b505084810360408601528551808252908201925081860190845b8181101561022b57825115158552938301939183019160010161020d565b509298975050505050505050565b9081526020019056fea2646970667358221220eb85327e285def14230424c52893aebecec1e387a50bb6b75fc4fdbed647f45f64736f6c63430006050033", +} + +// Structs is an auto generated Go binding around an Ethereum contract. +type Structs struct { + abi abi.ABI +} + +// NewStructs creates a new instance of Structs. +func NewStructs() *Structs { + parsed, err := StructsMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &Structs{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *Structs) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackF is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x28811f59. +// +// Solidity: function F() view returns((bytes32)[] a, uint256[] c, bool[] d) +func (structs *Structs) PackF() []byte { + enc, err := structs.abi.Pack("F") + if err != nil { + panic(err) + } + return enc +} + +// FOutput serves as a container for the return parameters of contract +// method F. +type FOutput struct { + A []Struct0 + C []*big.Int + D []bool +} + +// UnpackF is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x28811f59. +// +// Solidity: function F() view returns((bytes32)[] a, uint256[] c, bool[] d) +func (structs *Structs) UnpackF(data []byte) (FOutput, error) { + out, err := structs.abi.Unpack("F", data) + outstruct := new(FOutput) + if err != nil { + return *outstruct, err + } + outstruct.A = *abi.ConvertType(out[0], new([]Struct0)).(*[]Struct0) + outstruct.C = *abi.ConvertType(out[1], new([]*big.Int)).(*[]*big.Int) + outstruct.D = *abi.ConvertType(out[2], new([]bool)).(*[]bool) + return *outstruct, err + +} + +// PackG is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x6fecb623. +// +// Solidity: function G() view returns((bytes32)[] a) +func (structs *Structs) PackG() []byte { + enc, err := structs.abi.Pack("G") + if err != nil { + panic(err) + } + return enc +} + +// UnpackG is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x6fecb623. +// +// Solidity: function G() view returns((bytes32)[] a) +func (structs *Structs) UnpackG(data []byte) ([]Struct0, error) { + out, err := structs.abi.Unpack("G", data) + if err != nil { + return *new([]Struct0), err + } + out0 := *abi.ConvertType(out[0], new([]Struct0)).(*[]Struct0) + return out0, err +} diff --git a/accounts/abi/abigen/testdata/v2/token.go.txt b/accounts/abi/abigen/testdata/v2/token.go.txt new file mode 100644 index 00000000000..aca18cb227c --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/token.go.txt @@ -0,0 +1,319 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// TokenMetaData contains all meta data concerning the Token contract. +var TokenMetaData = bind.MetaData{ + ABI: "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[],\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"},{\"name\":\"_extraData\",\"type\":\"bytes\"}],\"name\":\"approveAndCall\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"},{\"name\":\"\",\"type\":\"address\"}],\"name\":\"spentAllowance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"},{\"name\":\"\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"type\":\"function\"},{\"inputs\":[{\"name\":\"initialSupply\",\"type\":\"uint256\"},{\"name\":\"tokenName\",\"type\":\"string\"},{\"name\":\"decimalUnits\",\"type\":\"uint8\"},{\"name\":\"tokenSymbol\",\"type\":\"string\"}],\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"}]", + ID: "1317f51c845ce3bfb7c268e5337a825f12", + Bin: "0x60606040526040516107fd3803806107fd83398101604052805160805160a05160c051929391820192909101600160a060020a0333166000908152600360209081526040822086905581548551838052601f6002600019610100600186161502019093169290920482018390047f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56390810193919290918801908390106100e857805160ff19168380011785555b506101189291505b8082111561017157600081556001016100b4565b50506002805460ff19168317905550505050610658806101a56000396000f35b828001600101855582156100ac579182015b828111156100ac5782518260005055916020019190600101906100fa565b50508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017557805160ff19168380011785555b506100c89291506100b4565b5090565b82800160010185558215610165579182015b8281111561016557825182600050559160200191906001019061018756606060405236156100775760e060020a600035046306fdde03811461007f57806323b872dd146100dc578063313ce5671461010e57806370a082311461011a57806395d89b4114610132578063a9059cbb1461018e578063cae9ca51146101bd578063dc3080f21461031c578063dd62ed3e14610341575b610365610002565b61036760008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b6103d5600435602435604435600160a060020a038316600090815260036020526040812054829010156104f357610002565b6103e760025460ff1681565b6103d560043560036020526000908152604090205481565b610367600180546020600282841615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b610365600435602435600160a060020a033316600090815260036020526040902054819010156103f157610002565b60806020604435600481810135601f8101849004909302840160405260608381526103d5948235946024803595606494939101919081908382808284375094965050505050505060006000836004600050600033600160a060020a03168152602001908152602001600020600050600087600160a060020a031681526020019081526020016000206000508190555084905080600160a060020a0316638f4ffcb1338630876040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156102f25780820380516001836020036101000a031916815260200191505b50955050505050506000604051808303816000876161da5a03f11561000257505050509392505050565b6005602090815260043560009081526040808220909252602435815220546103d59081565b60046020818152903560009081526040808220909252602435815220546103d59081565b005b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156103c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a03821660009081526040902054808201101561041357610002565b806003600050600033600160a060020a03168152602001908152602001600020600082828250540392505081905550806003600050600084600160a060020a0316815260200190815260200160002060008282825054019250508190555081600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b820191906000526020600020905b8154815290600101906020018083116104ce57829003601f168201915b505050505081565b600160a060020a03831681526040812054808301101561051257610002565b600160a060020a0380851680835260046020908152604080852033949094168086529382528085205492855260058252808520938552929052908220548301111561055c57610002565b816003600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816003600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816005600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054019250508190555082600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3939250505056", +} + +// Token is an auto generated Go binding around an Ethereum contract. +type Token struct { + abi abi.ABI +} + +// NewToken creates a new instance of Token. +func NewToken() *Token { + parsed, err := TokenMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &Token{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *Token) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackConstructor is the Go binding used to pack the parameters required for +// contract deployment. +// +// Solidity: constructor(uint256 initialSupply, string tokenName, uint8 decimalUnits, string tokenSymbol) returns() +func (token *Token) PackConstructor(initialSupply *big.Int, tokenName string, decimalUnits uint8, tokenSymbol string) []byte { + enc, err := token.abi.Pack("", initialSupply, tokenName, decimalUnits, tokenSymbol) + if err != nil { + panic(err) + } + return enc +} + +// PackAllowance is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xdd62ed3e. +// +// Solidity: function allowance(address , address ) returns(uint256) +func (token *Token) PackAllowance(arg0 common.Address, arg1 common.Address) []byte { + enc, err := token.abi.Pack("allowance", arg0, arg1) + if err != nil { + panic(err) + } + return enc +} + +// UnpackAllowance is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xdd62ed3e. +// +// Solidity: function allowance(address , address ) returns(uint256) +func (token *Token) UnpackAllowance(data []byte) (*big.Int, error) { + out, err := token.abi.Unpack("allowance", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackApproveAndCall is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xcae9ca51. +// +// Solidity: function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns(bool success) +func (token *Token) PackApproveAndCall(spender common.Address, value *big.Int, extraData []byte) []byte { + enc, err := token.abi.Pack("approveAndCall", spender, value, extraData) + if err != nil { + panic(err) + } + return enc +} + +// UnpackApproveAndCall is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xcae9ca51. +// +// Solidity: function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns(bool success) +func (token *Token) UnpackApproveAndCall(data []byte) (bool, error) { + out, err := token.abi.Unpack("approveAndCall", data) + if err != nil { + return *new(bool), err + } + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + return out0, err +} + +// PackBalanceOf is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x70a08231. +// +// Solidity: function balanceOf(address ) returns(uint256) +func (token *Token) PackBalanceOf(arg0 common.Address) []byte { + enc, err := token.abi.Pack("balanceOf", arg0) + if err != nil { + panic(err) + } + return enc +} + +// UnpackBalanceOf is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x70a08231. +// +// Solidity: function balanceOf(address ) returns(uint256) +func (token *Token) UnpackBalanceOf(data []byte) (*big.Int, error) { + out, err := token.abi.Unpack("balanceOf", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackDecimals is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x313ce567. +// +// Solidity: function decimals() returns(uint8) +func (token *Token) PackDecimals() []byte { + enc, err := token.abi.Pack("decimals") + if err != nil { + panic(err) + } + return enc +} + +// UnpackDecimals is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x313ce567. +// +// Solidity: function decimals() returns(uint8) +func (token *Token) UnpackDecimals(data []byte) (uint8, error) { + out, err := token.abi.Unpack("decimals", data) + if err != nil { + return *new(uint8), err + } + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + return out0, err +} + +// PackName is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x06fdde03. +// +// Solidity: function name() returns(string) +func (token *Token) PackName() []byte { + enc, err := token.abi.Pack("name") + if err != nil { + panic(err) + } + return enc +} + +// UnpackName is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x06fdde03. +// +// Solidity: function name() returns(string) +func (token *Token) UnpackName(data []byte) (string, error) { + out, err := token.abi.Unpack("name", data) + if err != nil { + return *new(string), err + } + out0 := *abi.ConvertType(out[0], new(string)).(*string) + return out0, err +} + +// PackSpentAllowance is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xdc3080f2. +// +// Solidity: function spentAllowance(address , address ) returns(uint256) +func (token *Token) PackSpentAllowance(arg0 common.Address, arg1 common.Address) []byte { + enc, err := token.abi.Pack("spentAllowance", arg0, arg1) + if err != nil { + panic(err) + } + return enc +} + +// UnpackSpentAllowance is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xdc3080f2. +// +// Solidity: function spentAllowance(address , address ) returns(uint256) +func (token *Token) UnpackSpentAllowance(data []byte) (*big.Int, error) { + out, err := token.abi.Unpack("spentAllowance", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackSymbol is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x95d89b41. +// +// Solidity: function symbol() returns(string) +func (token *Token) PackSymbol() []byte { + enc, err := token.abi.Pack("symbol") + if err != nil { + panic(err) + } + return enc +} + +// UnpackSymbol is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x95d89b41. +// +// Solidity: function symbol() returns(string) +func (token *Token) UnpackSymbol(data []byte) (string, error) { + out, err := token.abi.Unpack("symbol", data) + if err != nil { + return *new(string), err + } + out0 := *abi.ConvertType(out[0], new(string)).(*string) + return out0, err +} + +// PackTransfer is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xa9059cbb. +// +// Solidity: function transfer(address _to, uint256 _value) returns() +func (token *Token) PackTransfer(to common.Address, value *big.Int) []byte { + enc, err := token.abi.Pack("transfer", to, value) + if err != nil { + panic(err) + } + return enc +} + +// PackTransferFrom is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x23b872dd. +// +// Solidity: function transferFrom(address _from, address _to, uint256 _value) returns(bool success) +func (token *Token) PackTransferFrom(from common.Address, to common.Address, value *big.Int) []byte { + enc, err := token.abi.Pack("transferFrom", from, to, value) + if err != nil { + panic(err) + } + return enc +} + +// UnpackTransferFrom is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x23b872dd. +// +// Solidity: function transferFrom(address _from, address _to, uint256 _value) returns(bool success) +func (token *Token) UnpackTransferFrom(data []byte) (bool, error) { + out, err := token.abi.Unpack("transferFrom", data) + if err != nil { + return *new(bool), err + } + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + return out0, err +} + +// TokenTransfer represents a Transfer event raised by the Token contract. +type TokenTransfer struct { + From common.Address + To common.Address + Value *big.Int + Raw *types.Log // Blockchain specific contextual infos +} + +const TokenTransferEventName = "Transfer" + +// ContractEventName returns the user-defined event name. +func (TokenTransfer) ContractEventName() string { + return TokenTransferEventName +} + +// UnpackTransferEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (token *Token) UnpackTransferEvent(log *types.Log) (*TokenTransfer, error) { + event := "Transfer" + if log.Topics[0] != token.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(TokenTransfer) + if len(log.Data) > 0 { + if err := token.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range token.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} diff --git a/accounts/abi/abigen/testdata/v2/tuple.go.txt b/accounts/abi/abigen/testdata/v2/tuple.go.txt new file mode 100644 index 00000000000..65af7654635 --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/tuple.go.txt @@ -0,0 +1,228 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// TupleP is an auto generated low-level Go binding around an user-defined struct. +type TupleP struct { + X uint8 + Y uint8 +} + +// TupleQ is an auto generated low-level Go binding around an user-defined struct. +type TupleQ struct { + X uint16 + Y uint16 +} + +// TupleS is an auto generated low-level Go binding around an user-defined struct. +type TupleS struct { + A *big.Int + B []*big.Int + C []TupleT +} + +// TupleT is an auto generated low-level Go binding around an user-defined struct. +type TupleT struct { + X *big.Int + Y *big.Int +} + +// TupleMetaData contains all meta data concerning the Tuple contract. +var TupleMetaData = bind.MetaData{ + ABI: "[{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"a\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"b\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[]\",\"name\":\"c\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structTuple.S\",\"name\":\"a\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structTuple.T[2][]\",\"name\":\"b\",\"type\":\"tuple[2][]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structTuple.T[][2]\",\"name\":\"c\",\"type\":\"tuple[][2]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"a\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"b\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[]\",\"name\":\"c\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structTuple.S[]\",\"name\":\"d\",\"type\":\"tuple[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"e\",\"type\":\"uint256[]\"}],\"name\":\"TupleEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"x\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"y\",\"type\":\"uint8\"}],\"indexed\":false,\"internalType\":\"structTuple.P[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"name\":\"TupleEvent2\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"a\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"b\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[]\",\"name\":\"c\",\"type\":\"tuple[]\"}],\"internalType\":\"structTuple.S\",\"name\":\"a\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[2][]\",\"name\":\"b\",\"type\":\"tuple[2][]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[][2]\",\"name\":\"c\",\"type\":\"tuple[][2]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"a\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"b\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[]\",\"name\":\"c\",\"type\":\"tuple[]\"}],\"internalType\":\"structTuple.S[]\",\"name\":\"d\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[]\",\"name\":\"e\",\"type\":\"uint256[]\"}],\"name\":\"func1\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"a\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"b\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[]\",\"name\":\"c\",\"type\":\"tuple[]\"}],\"internalType\":\"structTuple.S\",\"name\":\"\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[2][]\",\"name\":\"\",\"type\":\"tuple[2][]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[][2]\",\"name\":\"\",\"type\":\"tuple[][2]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"a\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"b\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[]\",\"name\":\"c\",\"type\":\"tuple[]\"}],\"internalType\":\"structTuple.S[]\",\"name\":\"\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"a\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"b\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[]\",\"name\":\"c\",\"type\":\"tuple[]\"}],\"internalType\":\"structTuple.S\",\"name\":\"a\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[2][]\",\"name\":\"b\",\"type\":\"tuple[2][]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[][2]\",\"name\":\"c\",\"type\":\"tuple[][2]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"a\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"b\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"structTuple.T[]\",\"name\":\"c\",\"type\":\"tuple[]\"}],\"internalType\":\"structTuple.S[]\",\"name\":\"d\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[]\",\"name\":\"e\",\"type\":\"uint256[]\"}],\"name\":\"func2\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"components\":[{\"internalType\":\"uint16\",\"name\":\"x\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"y\",\"type\":\"uint16\"}],\"internalType\":\"structTuple.Q[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"name\":\"func3\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "96ee1e2b1b89f8c495f200e4956278a4d4", + Bin: "0x60806040523480156100115760006000fd5b50610017565b6110b2806100266000396000f3fe60806040523480156100115760006000fd5b50600436106100465760003560e01c8063443c79b41461004c578063d0062cdd14610080578063e4d9a43b1461009c57610046565b60006000fd5b610066600480360361006191908101906107b8565b6100b8565b604051610077959493929190610ccb565b60405180910390f35b61009a600480360361009591908101906107b8565b6100ef565b005b6100b660048036036100b19190810190610775565b610136565b005b6100c061013a565b60606100ca61015e565b606060608989898989945094509450945094506100e2565b9550955095509550959050565b7f18d6e66efa53739ca6d13626f35ebc700b31cced3eddb50c70bbe9c082c6cd008585858585604051610126959493929190610ccb565b60405180910390a15b5050505050565b5b50565b60405180606001604052806000815260200160608152602001606081526020015090565b60405180604001604052806002905b606081526020019060019003908161016d57905050905661106e565b600082601f830112151561019d5760006000fd5b81356101b06101ab82610d6f565b610d41565b915081818352602084019350602081019050838560808402820111156101d65760006000fd5b60005b8381101561020757816101ec888261037a565b8452602084019350608083019250505b6001810190506101d9565b5050505092915050565b600082601f83011215156102255760006000fd5b600261023861023382610d98565b610d41565b9150818360005b83811015610270578135860161025588826103f3565b8452602084019350602083019250505b60018101905061023f565b5050505092915050565b600082601f830112151561028e5760006000fd5b81356102a161029c82610dbb565b610d41565b915081818352602084019350602081019050838560408402820111156102c75760006000fd5b60005b838110156102f857816102dd888261058b565b8452602084019350604083019250505b6001810190506102ca565b5050505092915050565b600082601f83011215156103165760006000fd5b813561032961032482610de4565b610d41565b9150818183526020840193506020810190508360005b83811015610370578135860161035588826105d8565b8452602084019350602083019250505b60018101905061033f565b5050505092915050565b600082601f830112151561038e5760006000fd5b60026103a161039c82610e0d565b610d41565b915081838560408402820111156103b85760006000fd5b60005b838110156103e957816103ce88826106fe565b8452602084019350604083019250505b6001810190506103bb565b5050505092915050565b600082601f83011215156104075760006000fd5b813561041a61041582610e30565b610d41565b915081818352602084019350602081019050838560408402820111156104405760006000fd5b60005b83811015610471578161045688826106fe565b8452602084019350604083019250505b600181019050610443565b5050505092915050565b600082601f830112151561048f5760006000fd5b81356104a261049d82610e59565b610d41565b915081818352602084019350602081019050838560208402820111156104c85760006000fd5b60005b838110156104f957816104de8882610760565b8452602084019350602083019250505b6001810190506104cb565b5050505092915050565b600082601f83011215156105175760006000fd5b813561052a61052582610e82565b610d41565b915081818352602084019350602081019050838560208402820111156105505760006000fd5b60005b8381101561058157816105668882610760565b8452602084019350602083019250505b600181019050610553565b5050505092915050565b60006040828403121561059e5760006000fd5b6105a86040610d41565b905060006105b88482850161074b565b60008301525060206105cc8482850161074b565b60208301525092915050565b6000606082840312156105eb5760006000fd5b6105f56060610d41565b9050600061060584828501610760565b600083015250602082013567ffffffffffffffff8111156106265760006000fd5b6106328482850161047b565b602083015250604082013567ffffffffffffffff8111156106535760006000fd5b61065f848285016103f3565b60408301525092915050565b60006060828403121561067e5760006000fd5b6106886060610d41565b9050600061069884828501610760565b600083015250602082013567ffffffffffffffff8111156106b95760006000fd5b6106c58482850161047b565b602083015250604082013567ffffffffffffffff8111156106e65760006000fd5b6106f2848285016103f3565b60408301525092915050565b6000604082840312156107115760006000fd5b61071b6040610d41565b9050600061072b84828501610760565b600083015250602061073f84828501610760565b60208301525092915050565b60008135905061075a8161103a565b92915050565b60008135905061076f81611054565b92915050565b6000602082840312156107885760006000fd5b600082013567ffffffffffffffff8111156107a35760006000fd5b6107af8482850161027a565b91505092915050565b6000600060006000600060a086880312156107d35760006000fd5b600086013567ffffffffffffffff8111156107ee5760006000fd5b6107fa8882890161066b565b955050602086013567ffffffffffffffff8111156108185760006000fd5b61082488828901610189565b945050604086013567ffffffffffffffff8111156108425760006000fd5b61084e88828901610211565b935050606086013567ffffffffffffffff81111561086c5760006000fd5b61087888828901610302565b925050608086013567ffffffffffffffff8111156108965760006000fd5b6108a288828901610503565b9150509295509295909350565b60006108bb8383610a6a565b60808301905092915050565b60006108d38383610ac2565b905092915050565b60006108e78383610c36565b905092915050565b60006108fb8383610c8d565b60408301905092915050565b60006109138383610cbc565b60208301905092915050565b600061092a82610f0f565b6109348185610fb7565b935061093f83610eab565b8060005b8381101561097157815161095788826108af565b975061096283610f5c565b9250505b600181019050610943565b5085935050505092915050565b600061098982610f1a565b6109938185610fc8565b9350836020820285016109a585610ebb565b8060005b858110156109e257848403895281516109c285826108c7565b94506109cd83610f69565b925060208a019950505b6001810190506109a9565b50829750879550505050505092915050565b60006109ff82610f25565b610a098185610fd3565b935083602082028501610a1b85610ec5565b8060005b85811015610a585784840389528151610a3885826108db565b9450610a4383610f76565b925060208a019950505b600181019050610a1f565b50829750879550505050505092915050565b610a7381610f30565b610a7d8184610fe4565b9250610a8882610ed5565b8060005b83811015610aba578151610aa087826108ef565b9650610aab83610f83565b9250505b600181019050610a8c565b505050505050565b6000610acd82610f3b565b610ad78185610fef565b9350610ae283610edf565b8060005b83811015610b14578151610afa88826108ef565b9750610b0583610f90565b9250505b600181019050610ae6565b5085935050505092915050565b6000610b2c82610f51565b610b368185611011565b9350610b4183610eff565b8060005b83811015610b73578151610b598882610907565b9750610b6483610faa565b9250505b600181019050610b45565b5085935050505092915050565b6000610b8b82610f46565b610b958185611000565b9350610ba083610eef565b8060005b83811015610bd2578151610bb88882610907565b9750610bc383610f9d565b9250505b600181019050610ba4565b5085935050505092915050565b6000606083016000830151610bf76000860182610cbc565b5060208301518482036020860152610c0f8282610b80565b91505060408301518482036040860152610c298282610ac2565b9150508091505092915050565b6000606083016000830151610c4e6000860182610cbc565b5060208301518482036020860152610c668282610b80565b91505060408301518482036040860152610c808282610ac2565b9150508091505092915050565b604082016000820151610ca36000850182610cbc565b506020820151610cb66020850182610cbc565b50505050565b610cc581611030565b82525050565b600060a0820190508181036000830152610ce58188610bdf565b90508181036020830152610cf9818761091f565b90508181036040830152610d0d818661097e565b90508181036060830152610d2181856109f4565b90508181036080830152610d358184610b21565b90509695505050505050565b6000604051905081810181811067ffffffffffffffff82111715610d655760006000fd5b8060405250919050565b600067ffffffffffffffff821115610d875760006000fd5b602082029050602081019050919050565b600067ffffffffffffffff821115610db05760006000fd5b602082029050919050565b600067ffffffffffffffff821115610dd35760006000fd5b602082029050602081019050919050565b600067ffffffffffffffff821115610dfc5760006000fd5b602082029050602081019050919050565b600067ffffffffffffffff821115610e255760006000fd5b602082029050919050565b600067ffffffffffffffff821115610e485760006000fd5b602082029050602081019050919050565b600067ffffffffffffffff821115610e715760006000fd5b602082029050602081019050919050565b600067ffffffffffffffff821115610e9a5760006000fd5b602082029050602081019050919050565b6000819050602082019050919050565b6000819050919050565b6000819050602082019050919050565b6000819050919050565b6000819050602082019050919050565b6000819050602082019050919050565b6000819050602082019050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600060029050919050565b600081519050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b6000602082019050919050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600061ffff82169050919050565b6000819050919050565b61104381611022565b811415156110515760006000fd5b50565b61105d81611030565b8114151561106b5760006000fd5b50565bfea365627a7a72315820d78c6ba7ee332581e6c4d9daa5fc07941841230f7ce49edf6e05b1b63853e8746c6578706572696d656e74616cf564736f6c634300050c0040", +} + +// Tuple is an auto generated Go binding around an Ethereum contract. +type Tuple struct { + abi abi.ABI +} + +// NewTuple creates a new instance of Tuple. +func NewTuple() *Tuple { + parsed, err := TupleMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &Tuple{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *Tuple) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackFunc1 is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x443c79b4. +// +// Solidity: function func1((uint256,uint256[],(uint256,uint256)[]) a, (uint256,uint256)[2][] b, (uint256,uint256)[][2] c, (uint256,uint256[],(uint256,uint256)[])[] d, uint256[] e) pure returns((uint256,uint256[],(uint256,uint256)[]), (uint256,uint256)[2][], (uint256,uint256)[][2], (uint256,uint256[],(uint256,uint256)[])[], uint256[]) +func (tuple *Tuple) PackFunc1(a TupleS, b [][2]TupleT, c [2][]TupleT, d []TupleS, e []*big.Int) []byte { + enc, err := tuple.abi.Pack("func1", a, b, c, d, e) + if err != nil { + panic(err) + } + return enc +} + +// Func1Output serves as a container for the return parameters of contract +// method Func1. +type Func1Output struct { + Arg0 TupleS + Arg1 [][2]TupleT + Arg2 [2][]TupleT + Arg3 []TupleS + Arg4 []*big.Int +} + +// UnpackFunc1 is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x443c79b4. +// +// Solidity: function func1((uint256,uint256[],(uint256,uint256)[]) a, (uint256,uint256)[2][] b, (uint256,uint256)[][2] c, (uint256,uint256[],(uint256,uint256)[])[] d, uint256[] e) pure returns((uint256,uint256[],(uint256,uint256)[]), (uint256,uint256)[2][], (uint256,uint256)[][2], (uint256,uint256[],(uint256,uint256)[])[], uint256[]) +func (tuple *Tuple) UnpackFunc1(data []byte) (Func1Output, error) { + out, err := tuple.abi.Unpack("func1", data) + outstruct := new(Func1Output) + if err != nil { + return *outstruct, err + } + outstruct.Arg0 = *abi.ConvertType(out[0], new(TupleS)).(*TupleS) + outstruct.Arg1 = *abi.ConvertType(out[1], new([][2]TupleT)).(*[][2]TupleT) + outstruct.Arg2 = *abi.ConvertType(out[2], new([2][]TupleT)).(*[2][]TupleT) + outstruct.Arg3 = *abi.ConvertType(out[3], new([]TupleS)).(*[]TupleS) + outstruct.Arg4 = *abi.ConvertType(out[4], new([]*big.Int)).(*[]*big.Int) + return *outstruct, err + +} + +// PackFunc2 is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xd0062cdd. +// +// Solidity: function func2((uint256,uint256[],(uint256,uint256)[]) a, (uint256,uint256)[2][] b, (uint256,uint256)[][2] c, (uint256,uint256[],(uint256,uint256)[])[] d, uint256[] e) returns() +func (tuple *Tuple) PackFunc2(a TupleS, b [][2]TupleT, c [2][]TupleT, d []TupleS, e []*big.Int) []byte { + enc, err := tuple.abi.Pack("func2", a, b, c, d, e) + if err != nil { + panic(err) + } + return enc +} + +// PackFunc3 is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xe4d9a43b. +// +// Solidity: function func3((uint16,uint16)[] ) pure returns() +func (tuple *Tuple) PackFunc3(arg0 []TupleQ) []byte { + enc, err := tuple.abi.Pack("func3", arg0) + if err != nil { + panic(err) + } + return enc +} + +// TupleTupleEvent represents a TupleEvent event raised by the Tuple contract. +type TupleTupleEvent struct { + A TupleS + B [][2]TupleT + C [2][]TupleT + D []TupleS + E []*big.Int + Raw *types.Log // Blockchain specific contextual infos +} + +const TupleTupleEventEventName = "TupleEvent" + +// ContractEventName returns the user-defined event name. +func (TupleTupleEvent) ContractEventName() string { + return TupleTupleEventEventName +} + +// UnpackTupleEventEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event TupleEvent((uint256,uint256[],(uint256,uint256)[]) a, (uint256,uint256)[2][] b, (uint256,uint256)[][2] c, (uint256,uint256[],(uint256,uint256)[])[] d, uint256[] e) +func (tuple *Tuple) UnpackTupleEventEvent(log *types.Log) (*TupleTupleEvent, error) { + event := "TupleEvent" + if log.Topics[0] != tuple.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(TupleTupleEvent) + if len(log.Data) > 0 { + if err := tuple.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range tuple.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} + +// TupleTupleEvent2 represents a TupleEvent2 event raised by the Tuple contract. +type TupleTupleEvent2 struct { + Arg0 []TupleP + Raw *types.Log // Blockchain specific contextual infos +} + +const TupleTupleEvent2EventName = "TupleEvent2" + +// ContractEventName returns the user-defined event name. +func (TupleTupleEvent2) ContractEventName() string { + return TupleTupleEvent2EventName +} + +// UnpackTupleEvent2Event is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event TupleEvent2((uint8,uint8)[] arg0) +func (tuple *Tuple) UnpackTupleEvent2Event(log *types.Log) (*TupleTupleEvent2, error) { + event := "TupleEvent2" + if log.Topics[0] != tuple.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(TupleTupleEvent2) + if len(log.Data) > 0 { + if err := tuple.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range tuple.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} diff --git a/accounts/abi/abigen/testdata/v2/tupler.go.txt b/accounts/abi/abigen/testdata/v2/tupler.go.txt new file mode 100644 index 00000000000..caa692dadec --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/tupler.go.txt @@ -0,0 +1,89 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// TuplerMetaData contains all meta data concerning the Tupler contract. +var TuplerMetaData = bind.MetaData{ + ABI: "[{\"constant\":true,\"inputs\":[],\"name\":\"tuple\",\"outputs\":[{\"name\":\"a\",\"type\":\"string\"},{\"name\":\"b\",\"type\":\"int256\"},{\"name\":\"c\",\"type\":\"bytes32\"}],\"type\":\"function\"}]", + ID: "a8f4d2061f55c712cfae266c426a1cd568", + Bin: "0x606060405260dc8060106000396000f3606060405260e060020a60003504633175aae28114601a575b005b600060605260c0604052600260809081527f486900000000000000000000000000000000000000000000000000000000000060a05260017fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060e0829052610100819052606060c0908152600261012081905281906101409060a09080838184600060046012f1505081517fffff000000000000000000000000000000000000000000000000000000000000169091525050604051610160819003945092505050f3", +} + +// Tupler is an auto generated Go binding around an Ethereum contract. +type Tupler struct { + abi abi.ABI +} + +// NewTupler creates a new instance of Tupler. +func NewTupler() *Tupler { + parsed, err := TuplerMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &Tupler{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *Tupler) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackTuple is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x3175aae2. +// +// Solidity: function tuple() returns(string a, int256 b, bytes32 c) +func (tupler *Tupler) PackTuple() []byte { + enc, err := tupler.abi.Pack("tuple") + if err != nil { + panic(err) + } + return enc +} + +// TupleOutput serves as a container for the return parameters of contract +// method Tuple. +type TupleOutput struct { + A string + B *big.Int + C [32]byte +} + +// UnpackTuple is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x3175aae2. +// +// Solidity: function tuple() returns(string a, int256 b, bytes32 c) +func (tupler *Tupler) UnpackTuple(data []byte) (TupleOutput, error) { + out, err := tupler.abi.Unpack("tuple", data) + outstruct := new(TupleOutput) + if err != nil { + return *outstruct, err + } + outstruct.A = *abi.ConvertType(out[0], new(string)).(*string) + outstruct.B = abi.ConvertType(out[1], new(big.Int)).(*big.Int) + outstruct.C = *abi.ConvertType(out[2], new([32]byte)).(*[32]byte) + return *outstruct, err + +} diff --git a/accounts/abi/abigen/testdata/v2/underscorer.go.txt b/accounts/abi/abigen/testdata/v2/underscorer.go.txt new file mode 100644 index 00000000000..ef9eb864fa4 --- /dev/null +++ b/accounts/abi/abigen/testdata/v2/underscorer.go.txt @@ -0,0 +1,322 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindtests + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// UnderscorerMetaData contains all meta data concerning the Underscorer contract. +var UnderscorerMetaData = bind.MetaData{ + ABI: "[{\"constant\":true,\"inputs\":[],\"name\":\"LowerUpperCollision\",\"outputs\":[{\"name\":\"_res\",\"type\":\"int256\"},{\"name\":\"Res\",\"type\":\"int256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"_under_scored_func\",\"outputs\":[{\"name\":\"_int\",\"type\":\"int256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"UnderscoredOutput\",\"outputs\":[{\"name\":\"_int\",\"type\":\"int256\"},{\"name\":\"_string\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"PurelyUnderscoredOutput\",\"outputs\":[{\"name\":\"_\",\"type\":\"int256\"},{\"name\":\"res\",\"type\":\"int256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"UpperLowerCollision\",\"outputs\":[{\"name\":\"_Res\",\"type\":\"int256\"},{\"name\":\"res\",\"type\":\"int256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"AllPurelyUnderscoredOutput\",\"outputs\":[{\"name\":\"_\",\"type\":\"int256\"},{\"name\":\"__\",\"type\":\"int256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"UpperUpperCollision\",\"outputs\":[{\"name\":\"_Res\",\"type\":\"int256\"},{\"name\":\"Res\",\"type\":\"int256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"LowerLowerCollision\",\"outputs\":[{\"name\":\"_res\",\"type\":\"int256\"},{\"name\":\"res\",\"type\":\"int256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]", + ID: "5873a90ab43c925dfced86ad53f871f01d", + Bin: "0x6060604052341561000f57600080fd5b6103858061001e6000396000f30060606040526004361061008e576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806303a592131461009357806346546dbe146100c357806367e6633d146100ec5780639df4848514610181578063af7486ab146101b1578063b564b34d146101e1578063e02ab24d14610211578063e409ca4514610241575b600080fd5b341561009e57600080fd5b6100a6610271565b604051808381526020018281526020019250505060405180910390f35b34156100ce57600080fd5b6100d6610286565b6040518082815260200191505060405180910390f35b34156100f757600080fd5b6100ff61028e565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561014557808201518184015260208101905061012a565b50505050905090810190601f1680156101725780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561018c57600080fd5b6101946102dc565b604051808381526020018281526020019250505060405180910390f35b34156101bc57600080fd5b6101c46102f1565b604051808381526020018281526020019250505060405180910390f35b34156101ec57600080fd5b6101f4610306565b604051808381526020018281526020019250505060405180910390f35b341561021c57600080fd5b61022461031b565b604051808381526020018281526020019250505060405180910390f35b341561024c57600080fd5b610254610330565b604051808381526020018281526020019250505060405180910390f35b60008060016002819150809050915091509091565b600080905090565b6000610298610345565b61013a8090506040805190810160405280600281526020017f7069000000000000000000000000000000000000000000000000000000000000815250915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b6020604051908101604052806000815250905600a165627a7a72305820d1a53d9de9d1e3d55cb3dc591900b63c4f1ded79114f7b79b332684840e186a40029", +} + +// Underscorer is an auto generated Go binding around an Ethereum contract. +type Underscorer struct { + abi abi.ABI +} + +// NewUnderscorer creates a new instance of Underscorer. +func NewUnderscorer() *Underscorer { + parsed, err := UnderscorerMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &Underscorer{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *Underscorer) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackAllPurelyUnderscoredOutput is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xb564b34d. +// +// Solidity: function AllPurelyUnderscoredOutput() view returns(int256 _, int256 __) +func (underscorer *Underscorer) PackAllPurelyUnderscoredOutput() []byte { + enc, err := underscorer.abi.Pack("AllPurelyUnderscoredOutput") + if err != nil { + panic(err) + } + return enc +} + +// AllPurelyUnderscoredOutputOutput serves as a container for the return parameters of contract +// method AllPurelyUnderscoredOutput. +type AllPurelyUnderscoredOutputOutput struct { + Arg0 *big.Int + Arg1 *big.Int +} + +// UnpackAllPurelyUnderscoredOutput is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xb564b34d. +// +// Solidity: function AllPurelyUnderscoredOutput() view returns(int256 _, int256 __) +func (underscorer *Underscorer) UnpackAllPurelyUnderscoredOutput(data []byte) (AllPurelyUnderscoredOutputOutput, error) { + out, err := underscorer.abi.Unpack("AllPurelyUnderscoredOutput", data) + outstruct := new(AllPurelyUnderscoredOutputOutput) + if err != nil { + return *outstruct, err + } + outstruct.Arg0 = abi.ConvertType(out[0], new(big.Int)).(*big.Int) + outstruct.Arg1 = abi.ConvertType(out[1], new(big.Int)).(*big.Int) + return *outstruct, err + +} + +// PackLowerLowerCollision is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xe409ca45. +// +// Solidity: function LowerLowerCollision() view returns(int256 _res, int256 res) +func (underscorer *Underscorer) PackLowerLowerCollision() []byte { + enc, err := underscorer.abi.Pack("LowerLowerCollision") + if err != nil { + panic(err) + } + return enc +} + +// LowerLowerCollisionOutput serves as a container for the return parameters of contract +// method LowerLowerCollision. +type LowerLowerCollisionOutput struct { + Res *big.Int + Res0 *big.Int +} + +// UnpackLowerLowerCollision is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xe409ca45. +// +// Solidity: function LowerLowerCollision() view returns(int256 _res, int256 res) +func (underscorer *Underscorer) UnpackLowerLowerCollision(data []byte) (LowerLowerCollisionOutput, error) { + out, err := underscorer.abi.Unpack("LowerLowerCollision", data) + outstruct := new(LowerLowerCollisionOutput) + if err != nil { + return *outstruct, err + } + outstruct.Res = abi.ConvertType(out[0], new(big.Int)).(*big.Int) + outstruct.Res0 = abi.ConvertType(out[1], new(big.Int)).(*big.Int) + return *outstruct, err + +} + +// PackLowerUpperCollision is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x03a59213. +// +// Solidity: function LowerUpperCollision() view returns(int256 _res, int256 Res) +func (underscorer *Underscorer) PackLowerUpperCollision() []byte { + enc, err := underscorer.abi.Pack("LowerUpperCollision") + if err != nil { + panic(err) + } + return enc +} + +// LowerUpperCollisionOutput serves as a container for the return parameters of contract +// method LowerUpperCollision. +type LowerUpperCollisionOutput struct { + Res *big.Int + Res0 *big.Int +} + +// UnpackLowerUpperCollision is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x03a59213. +// +// Solidity: function LowerUpperCollision() view returns(int256 _res, int256 Res) +func (underscorer *Underscorer) UnpackLowerUpperCollision(data []byte) (LowerUpperCollisionOutput, error) { + out, err := underscorer.abi.Unpack("LowerUpperCollision", data) + outstruct := new(LowerUpperCollisionOutput) + if err != nil { + return *outstruct, err + } + outstruct.Res = abi.ConvertType(out[0], new(big.Int)).(*big.Int) + outstruct.Res0 = abi.ConvertType(out[1], new(big.Int)).(*big.Int) + return *outstruct, err + +} + +// PackPurelyUnderscoredOutput is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x9df48485. +// +// Solidity: function PurelyUnderscoredOutput() view returns(int256 _, int256 res) +func (underscorer *Underscorer) PackPurelyUnderscoredOutput() []byte { + enc, err := underscorer.abi.Pack("PurelyUnderscoredOutput") + if err != nil { + panic(err) + } + return enc +} + +// PurelyUnderscoredOutputOutput serves as a container for the return parameters of contract +// method PurelyUnderscoredOutput. +type PurelyUnderscoredOutputOutput struct { + Arg0 *big.Int + Res *big.Int +} + +// UnpackPurelyUnderscoredOutput is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x9df48485. +// +// Solidity: function PurelyUnderscoredOutput() view returns(int256 _, int256 res) +func (underscorer *Underscorer) UnpackPurelyUnderscoredOutput(data []byte) (PurelyUnderscoredOutputOutput, error) { + out, err := underscorer.abi.Unpack("PurelyUnderscoredOutput", data) + outstruct := new(PurelyUnderscoredOutputOutput) + if err != nil { + return *outstruct, err + } + outstruct.Arg0 = abi.ConvertType(out[0], new(big.Int)).(*big.Int) + outstruct.Res = abi.ConvertType(out[1], new(big.Int)).(*big.Int) + return *outstruct, err + +} + +// PackUnderscoredOutput is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x67e6633d. +// +// Solidity: function UnderscoredOutput() view returns(int256 _int, string _string) +func (underscorer *Underscorer) PackUnderscoredOutput() []byte { + enc, err := underscorer.abi.Pack("UnderscoredOutput") + if err != nil { + panic(err) + } + return enc +} + +// UnderscoredOutputOutput serves as a container for the return parameters of contract +// method UnderscoredOutput. +type UnderscoredOutputOutput struct { + Int *big.Int + String string +} + +// UnpackUnderscoredOutput is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x67e6633d. +// +// Solidity: function UnderscoredOutput() view returns(int256 _int, string _string) +func (underscorer *Underscorer) UnpackUnderscoredOutput(data []byte) (UnderscoredOutputOutput, error) { + out, err := underscorer.abi.Unpack("UnderscoredOutput", data) + outstruct := new(UnderscoredOutputOutput) + if err != nil { + return *outstruct, err + } + outstruct.Int = abi.ConvertType(out[0], new(big.Int)).(*big.Int) + outstruct.String = *abi.ConvertType(out[1], new(string)).(*string) + return *outstruct, err + +} + +// PackUpperLowerCollision is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xaf7486ab. +// +// Solidity: function UpperLowerCollision() view returns(int256 _Res, int256 res) +func (underscorer *Underscorer) PackUpperLowerCollision() []byte { + enc, err := underscorer.abi.Pack("UpperLowerCollision") + if err != nil { + panic(err) + } + return enc +} + +// UpperLowerCollisionOutput serves as a container for the return parameters of contract +// method UpperLowerCollision. +type UpperLowerCollisionOutput struct { + Res *big.Int + Res0 *big.Int +} + +// UnpackUpperLowerCollision is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xaf7486ab. +// +// Solidity: function UpperLowerCollision() view returns(int256 _Res, int256 res) +func (underscorer *Underscorer) UnpackUpperLowerCollision(data []byte) (UpperLowerCollisionOutput, error) { + out, err := underscorer.abi.Unpack("UpperLowerCollision", data) + outstruct := new(UpperLowerCollisionOutput) + if err != nil { + return *outstruct, err + } + outstruct.Res = abi.ConvertType(out[0], new(big.Int)).(*big.Int) + outstruct.Res0 = abi.ConvertType(out[1], new(big.Int)).(*big.Int) + return *outstruct, err + +} + +// PackUpperUpperCollision is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xe02ab24d. +// +// Solidity: function UpperUpperCollision() view returns(int256 _Res, int256 Res) +func (underscorer *Underscorer) PackUpperUpperCollision() []byte { + enc, err := underscorer.abi.Pack("UpperUpperCollision") + if err != nil { + panic(err) + } + return enc +} + +// UpperUpperCollisionOutput serves as a container for the return parameters of contract +// method UpperUpperCollision. +type UpperUpperCollisionOutput struct { + Res *big.Int + Res0 *big.Int +} + +// UnpackUpperUpperCollision is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xe02ab24d. +// +// Solidity: function UpperUpperCollision() view returns(int256 _Res, int256 Res) +func (underscorer *Underscorer) UnpackUpperUpperCollision(data []byte) (UpperUpperCollisionOutput, error) { + out, err := underscorer.abi.Unpack("UpperUpperCollision", data) + outstruct := new(UpperUpperCollisionOutput) + if err != nil { + return *outstruct, err + } + outstruct.Res = abi.ConvertType(out[0], new(big.Int)).(*big.Int) + outstruct.Res0 = abi.ConvertType(out[1], new(big.Int)).(*big.Int) + return *outstruct, err + +} + +// PackUnderScoredFunc is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x46546dbe. +// +// Solidity: function _under_scored_func() view returns(int256 _int) +func (underscorer *Underscorer) PackUnderScoredFunc() []byte { + enc, err := underscorer.abi.Pack("_under_scored_func") + if err != nil { + panic(err) + } + return enc +} + +// UnpackUnderScoredFunc is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x46546dbe. +// +// Solidity: function _under_scored_func() view returns(int256 _int) +func (underscorer *Underscorer) UnpackUnderScoredFunc(data []byte) (*big.Int, error) { + out, err := underscorer.abi.Unpack("_under_scored_func", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} diff --git a/accounts/abi/bind/auth.go b/accounts/abi/bind/auth.go deleted file mode 100644 index b5e6e349c44..00000000000 --- a/accounts/abi/bind/auth.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2016 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 bind - -import ( - "context" - "crypto/ecdsa" - "errors" - "io" - "math/big" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/external" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" -) - -// ErrNoChainID is returned whenever the user failed to specify a chain id. -var ErrNoChainID = errors.New("no chain id specified") - -// ErrNotAuthorized is returned when an account is not properly unlocked. -var ErrNotAuthorized = errors.New("not authorized to sign this account") - -// NewTransactor is a utility method to easily create a transaction signer from -// an encrypted json key stream and the associated passphrase. -// -// Deprecated: Use NewTransactorWithChainID instead. -func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) { - log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID") - json, err := io.ReadAll(keyin) - if err != nil { - return nil, err - } - key, err := keystore.DecryptKey(json, passphrase) - if err != nil { - return nil, err - } - return NewKeyedTransactor(key.PrivateKey), nil -} - -// NewKeyStoreTransactor is a utility method to easily create a transaction signer from -// a decrypted key from a keystore. -// -// Deprecated: Use NewKeyStoreTransactorWithChainID instead. -func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) { - log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID") - signer := types.HomesteadSigner{} - return &TransactOpts{ - From: account.Address, - Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { - if address != account.Address { - return nil, ErrNotAuthorized - } - signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes()) - if err != nil { - return nil, err - } - return tx.WithSignature(signer, signature) - }, - Context: context.Background(), - }, nil -} - -// NewKeyedTransactor is a utility method to easily create a transaction signer -// from a single private key. -// -// Deprecated: Use NewKeyedTransactorWithChainID instead. -func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts { - log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID") - keyAddr := crypto.PubkeyToAddress(key.PublicKey) - signer := types.HomesteadSigner{} - return &TransactOpts{ - From: keyAddr, - Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { - if address != keyAddr { - return nil, ErrNotAuthorized - } - signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key) - if err != nil { - return nil, err - } - return tx.WithSignature(signer, signature) - }, - Context: context.Background(), - } -} - -// NewTransactorWithChainID is a utility method to easily create a transaction signer from -// an encrypted json key stream and the associated passphrase. -func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) { - json, err := io.ReadAll(keyin) - if err != nil { - return nil, err - } - key, err := keystore.DecryptKey(json, passphrase) - if err != nil { - return nil, err - } - return NewKeyedTransactorWithChainID(key.PrivateKey, chainID) -} - -// NewKeyStoreTransactorWithChainID is a utility method to easily create a transaction signer from -// a decrypted key from a keystore. -func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accounts.Account, chainID *big.Int) (*TransactOpts, error) { - if chainID == nil { - return nil, ErrNoChainID - } - signer := types.LatestSignerForChainID(chainID) - return &TransactOpts{ - From: account.Address, - Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { - if address != account.Address { - return nil, ErrNotAuthorized - } - signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes()) - if err != nil { - return nil, err - } - return tx.WithSignature(signer, signature) - }, - Context: context.Background(), - }, nil -} - -// NewKeyedTransactorWithChainID is a utility method to easily create a transaction signer -// from a single private key. -func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) { - if chainID == nil { - return nil, ErrNoChainID - } - keyAddr := crypto.PubkeyToAddress(key.PublicKey) - signer := types.LatestSignerForChainID(chainID) - return &TransactOpts{ - From: keyAddr, - Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { - if address != keyAddr { - return nil, ErrNotAuthorized - } - signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key) - if err != nil { - return nil, err - } - return tx.WithSignature(signer, signature) - }, - Context: context.Background(), - }, nil -} - -// NewClefTransactor is a utility method to easily create a transaction signer -// with a clef backend. -func NewClefTransactor(clef *external.ExternalSigner, account accounts.Account) *TransactOpts { - return &TransactOpts{ - From: account.Address, - Signer: func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) { - if address != account.Address { - return nil, ErrNotAuthorized - } - return clef.SignTx(account, transaction, nil) // Clef enforces its own chain id - }, - Context: context.Background(), - } -} diff --git a/accounts/abi/bind/old.go b/accounts/abi/bind/old.go new file mode 100644 index 00000000000..b09f5f3c7ac --- /dev/null +++ b/accounts/abi/bind/old.go @@ -0,0 +1,294 @@ +// Copyright 2016 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 bind is the runtime for abigen v1 generated contract bindings. +// Deprecated: please use github.com/ethereum/go-ethereum/bind/v2 +package bind + +import ( + "context" + "crypto/ecdsa" + "errors" + "io" + "math/big" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/abigen" + bind2 "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/accounts/external" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" +) + +// Bind generates a v1 contract binding. +// Deprecated: binding generation has moved to github.com/ethereum/go-ethereum/accounts/abi/abigen +func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, libs map[string]string, aliases map[string]string) (string, error) { + return abigen.Bind(types, abis, bytecodes, fsigs, pkg, libs, aliases) +} + +// auth.go + +// ErrNoChainID is returned whenever the user failed to specify a chain id. +var ErrNoChainID = errors.New("no chain id specified") + +// ErrNotAuthorized is returned when an account is not properly unlocked. +var ErrNotAuthorized = bind2.ErrNotAuthorized + +// NewTransactor is a utility method to easily create a transaction signer from +// an encrypted json key stream and the associated passphrase. +// +// Deprecated: Use NewTransactorWithChainID instead. +func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) { + log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID") + json, err := io.ReadAll(keyin) + if err != nil { + return nil, err + } + key, err := keystore.DecryptKey(json, passphrase) + if err != nil { + return nil, err + } + return NewKeyedTransactor(key.PrivateKey), nil +} + +// NewKeyStoreTransactor is a utility method to easily create a transaction signer from +// a decrypted key from a keystore. +// +// Deprecated: Use NewKeyStoreTransactorWithChainID instead. +func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) { + log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID") + signer := types.HomesteadSigner{} + return &TransactOpts{ + From: account.Address, + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + if address != account.Address { + return nil, ErrNotAuthorized + } + signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes()) + if err != nil { + return nil, err + } + return tx.WithSignature(signer, signature) + }, + Context: context.Background(), + }, nil +} + +// NewKeyedTransactor is a utility method to easily create a transaction signer +// from a single private key. +// +// Deprecated: Use NewKeyedTransactorWithChainID instead. +func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts { + log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID") + keyAddr := crypto.PubkeyToAddress(key.PublicKey) + signer := types.HomesteadSigner{} + return &TransactOpts{ + From: keyAddr, + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + if address != keyAddr { + return nil, ErrNotAuthorized + } + signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key) + if err != nil { + return nil, err + } + return tx.WithSignature(signer, signature) + }, + Context: context.Background(), + } +} + +// NewTransactorWithChainID is a utility method to easily create a transaction signer from +// an encrypted json key stream and the associated passphrase. +func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) { + json, err := io.ReadAll(keyin) + if err != nil { + return nil, err + } + key, err := keystore.DecryptKey(json, passphrase) + if err != nil { + return nil, err + } + return NewKeyedTransactorWithChainID(key.PrivateKey, chainID) +} + +// NewKeyStoreTransactorWithChainID is a utility method to easily create a transaction signer from +// a decrypted key from a keystore. +func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accounts.Account, chainID *big.Int) (*TransactOpts, error) { + // New version panics for chainID == nil, catch it here. + if chainID == nil { + return nil, ErrNoChainID + } + return bind2.NewKeyStoreTransactor(keystore, account, chainID), nil +} + +// NewKeyedTransactorWithChainID is a utility method to easily create a transaction signer +// from a single private key. +func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) { + // New version panics for chainID == nil, catch it here. + if chainID == nil { + return nil, ErrNoChainID + } + return bind2.NewKeyedTransactor(key, chainID), nil +} + +// NewClefTransactor is a utility method to easily create a transaction signer +// with a clef backend. +func NewClefTransactor(clef *external.ExternalSigner, account accounts.Account) *TransactOpts { + return bind2.NewClefTransactor(clef, account) +} + +// backend.go + +var ( + // ErrNoCode is returned by call and transact operations for which the requested + // recipient contract to operate on does not exist in the state db or does not + // have any code associated with it (i.e. self-destructed). + ErrNoCode = bind2.ErrNoCode + + // ErrNoPendingState is raised when attempting to perform a pending state action + // on a backend that doesn't implement PendingContractCaller. + ErrNoPendingState = bind2.ErrNoPendingState + + // ErrNoBlockHashState is raised when attempting to perform a block hash action + // on a backend that doesn't implement BlockHashContractCaller. + ErrNoBlockHashState = bind2.ErrNoBlockHashState + + // ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves + // an empty contract behind. + ErrNoCodeAfterDeploy = bind2.ErrNoCodeAfterDeploy +) + +// ContractCaller defines the methods needed to allow operating with a contract on a read +// only basis. +type ContractCaller = bind2.ContractCaller + +// PendingContractCaller defines methods to perform contract calls on the pending state. +// Call will try to discover this interface when access to the pending state is requested. +// If the backend does not support the pending state, Call returns ErrNoPendingState. +type PendingContractCaller = bind2.PendingContractCaller + +// BlockHashContractCaller defines methods to perform contract calls on a specific block hash. +// Call will try to discover this interface when access to a block by hash is requested. +// If the backend does not support the block hash state, Call returns ErrNoBlockHashState. +type BlockHashContractCaller = bind2.BlockHashContractCaller + +// ContractTransactor defines the methods needed to allow operating with a contract +// on a write only basis. Besides the transacting method, the remainder are helpers +// used when the user does not provide some needed values, but rather leaves it up +// to the transactor to decide. +type ContractTransactor = bind2.ContractTransactor + +// DeployBackend wraps the operations needed by WaitMined and WaitDeployed. +type DeployBackend = bind2.DeployBackend + +// ContractFilterer defines the methods needed to access log events using one-off +// queries or continuous event subscriptions. +type ContractFilterer = bind2.ContractFilterer + +// ContractBackend defines the methods needed to work with contracts on a read-write basis. +type ContractBackend = bind2.ContractBackend + +// base.go + +type SignerFn = bind2.SignerFn + +type CallOpts = bind2.CallOpts + +type TransactOpts = bind2.TransactOpts + +type FilterOpts = bind2.FilterOpts + +type WatchOpts = bind2.WatchOpts + +type BoundContract = bind2.BoundContract + +func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, filterer ContractFilterer) *BoundContract { + return bind2.NewBoundContract(address, abi, caller, transactor, filterer) +} + +func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) { + packed, err := abi.Pack("", params...) + if err != nil { + return common.Address{}, nil, nil, err + } + addr, tx, err := bind2.DeployContract(opts, bytecode, backend, packed) + if err != nil { + return common.Address{}, nil, nil, err + } + contract := NewBoundContract(addr, abi, backend, backend, backend) + return addr, tx, contract, nil +} + +// MetaData collects all metadata for a bound contract. +type MetaData struct { + Bin string // runtime bytecode (as a hex string) + ABI string // the raw ABI definition (JSON) + Sigs map[string]string // 4byte identifier -> function signature + mu sync.Mutex + parsedABI *abi.ABI +} + +// GetAbi returns the parsed ABI definition. +func (m *MetaData) GetAbi() (*abi.ABI, error) { + m.mu.Lock() + defer m.mu.Unlock() + + if m.parsedABI != nil { + return m.parsedABI, nil + } + if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil { + return nil, err + } else { + m.parsedABI = &parsed + } + return m.parsedABI, nil +} + +// util.go + +// WaitMined waits for tx to be mined on the blockchain. +// It stops waiting when the context is canceled. +func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) { + return bind2.WaitMined(ctx, b, tx.Hash()) +} + +// WaitMinedHash waits for a transaction with the provided hash to be mined on the blockchain. +// It stops waiting when the context is canceled. +func WaitMinedHash(ctx context.Context, b DeployBackend, hash common.Hash) (*types.Receipt, error) { + return bind2.WaitMined(ctx, b, hash) +} + +// WaitDeployed waits for a contract deployment transaction and returns the on-chain +// contract address when it is mined. It stops waiting when ctx is canceled. +func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (common.Address, error) { + if tx.To() != nil { + return common.Address{}, errors.New("tx is not contract creation") + } + return bind2.WaitDeployed(ctx, b, tx.Hash()) +} + +// WaitDeployedHash waits for a contract deployment transaction with the provided hash and returns the on-chain +// contract address when it is mined. It stops waiting when ctx is canceled. +func WaitDeployedHash(ctx context.Context, b DeployBackend, hash common.Hash) (common.Address, error) { + return bind2.WaitDeployed(ctx, b, hash) +} diff --git a/accounts/abi/bind/v2/auth.go b/accounts/abi/bind/v2/auth.go new file mode 100644 index 00000000000..0a452a2c75b --- /dev/null +++ b/accounts/abi/bind/v2/auth.go @@ -0,0 +1,96 @@ +// Copyright 2016 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 bind + +import ( + "context" + "crypto/ecdsa" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/external" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +// ErrNotAuthorized is returned when an account is not properly unlocked. +var ErrNotAuthorized = errors.New("not authorized to sign this account") + +// NewKeyStoreTransactor is a utility method to easily create a transaction signer from +// a decrypted key from a keystore. +func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account, chainID *big.Int) *TransactOpts { + if chainID == nil { + panic("nil chainID") + } + signer := types.LatestSignerForChainID(chainID) + return &TransactOpts{ + From: account.Address, + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + if address != account.Address { + return nil, ErrNotAuthorized + } + signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes()) + if err != nil { + return nil, err + } + return tx.WithSignature(signer, signature) + }, + Context: context.Background(), + } +} + +// NewKeyedTransactor is a utility method to easily create a transaction signer +// from a single private key. +func NewKeyedTransactor(key *ecdsa.PrivateKey, chainID *big.Int) *TransactOpts { + if chainID == nil { + panic("nil chainID") + } + keyAddr := crypto.PubkeyToAddress(key.PublicKey) + signer := types.LatestSignerForChainID(chainID) + return &TransactOpts{ + From: keyAddr, + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + if address != keyAddr { + return nil, ErrNotAuthorized + } + signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key) + if err != nil { + return nil, err + } + return tx.WithSignature(signer, signature) + }, + Context: context.Background(), + } +} + +// NewClefTransactor is a utility method to easily create a transaction signer +// with a clef backend. +func NewClefTransactor(clef *external.ExternalSigner, account accounts.Account) *TransactOpts { + return &TransactOpts{ + From: account.Address, + Signer: func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) { + if address != account.Address { + return nil, ErrNotAuthorized + } + return clef.SignTx(account, transaction, nil) // Clef enforces its own chain id + }, + Context: context.Background(), + } +} diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/v2/backend.go similarity index 90% rename from accounts/abi/bind/backend.go rename to accounts/abi/bind/v2/backend.go index 38b30469708..2f5f17b31eb 100644 --- a/accounts/abi/bind/backend.go +++ b/accounts/abi/bind/v2/backend.go @@ -43,6 +43,11 @@ var ( // ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves // an empty contract behind. ErrNoCodeAfterDeploy = errors.New("no contract code after deployment") + + // ErrNoAddressInReceipt is returned by WaitDeployed when the receipt for the + // transaction hash does not contain a contract address. This error may indicate + // that the transaction hash was not a CREATE transaction. + ErrNoAddressInReceipt = errors.New("no contract address in receipt") ) // ContractCaller defines the methods needed to allow operating with a contract on a read @@ -118,3 +123,11 @@ type ContractBackend interface { ContractTransactor ContractFilterer } + +// Backend combines all backend methods used in this package. This type is provided for +// convenience. It is meant to be used when you need to hold a reference to a backend that +// is used for both deployment and contract interaction. +type Backend interface { + DeployBackend + ContractBackend +} diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/v2/base.go similarity index 85% rename from accounts/abi/bind/base.go rename to accounts/abi/bind/v2/base.go index 0504089c717..a9b667e6977 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/v2/base.go @@ -25,10 +25,10 @@ import ( "sync" "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/event" ) @@ -89,25 +89,42 @@ type WatchOpts struct { // MetaData collects all metadata for a bound contract. type MetaData struct { - mu sync.Mutex - Sigs map[string]string - Bin string - ABI string - ab *abi.ABI + Bin string // deployer bytecode (as a hex string) + ABI string // the raw ABI definition (JSON) + Deps []*MetaData // library dependencies of the contract + + // For bindings that were compiled from combined-json ID is the Solidity + // library pattern: a 34 character prefix of the hex encoding of the keccak256 + // hash of the fully qualified 'library name', i.e. the path of the source file. + // + // For contracts compiled from the ABI definition alone, this is the type name + // of the contract (as specified in the ABI definition or overridden via the + // --type flag). + // + // This is a unique identifier of a contract within a compilation unit. When + // used as part of a multi-contract deployment with library dependencies, the + // ID is used to link contracts during deployment using [LinkAndDeploy]. + ID string + + mu sync.Mutex + parsedABI *abi.ABI } -func (m *MetaData) GetAbi() (*abi.ABI, error) { +// ParseABI returns the parsed ABI specification, or an error if the string +// representation of the ABI set in the MetaData instance could not be parsed. +func (m *MetaData) ParseABI() (*abi.ABI, error) { m.mu.Lock() defer m.mu.Unlock() - if m.ab != nil { - return m.ab, nil + + if m.parsedABI != nil { + return m.parsedABI, nil } if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil { return nil, err } else { - m.ab = &parsed + m.parsedABI = &parsed } - return m.ab, nil + return m.parsedABI, nil } // BoundContract is the base wrapper object that reflects a contract on the @@ -133,125 +150,125 @@ func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller } } -// DeployContract deploys a contract onto the Ethereum blockchain and binds the -// deployment address with a Go wrapper. -func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) { - // Otherwise try to deploy the contract - c := NewBoundContract(common.Address{}, abi, backend, backend, backend) - - input, err := c.abi.Pack("", params...) - if err != nil { - return common.Address{}, nil, nil, err - } - tx, err := c.transact(opts, nil, append(bytecode, input...)) - if err != nil { - return common.Address{}, nil, nil, err - } - c.address = crypto.CreateAddress(opts.From, tx.Nonce()) - return c.address, tx, c, nil -} - // Call invokes the (constant) contract method with params as input values and // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error { - // Don't crash on a lazy user - if opts == nil { - opts = new(CallOpts) - } +func (c *BoundContract) Call(opts *CallOpts, results *[]any, method string, params ...any) error { if results == nil { - results = new([]interface{}) + results = new([]any) } // Pack the input, call and unpack the results input, err := c.abi.Pack(method, params...) if err != nil { return err } + + output, err := c.call(opts, input) + if err != nil { + return err + } + + if len(*results) == 0 { + res, err := c.abi.Unpack(method, output) + *results = res + return err + } + res := *results + return c.abi.UnpackIntoInterface(res[0], method, output) +} + +// CallRaw executes an eth_call against the contract with the raw calldata as +// input. It returns the call's return data or an error. +func (c *BoundContract) CallRaw(opts *CallOpts, input []byte) ([]byte, error) { + return c.call(opts, input) +} + +func (c *BoundContract) call(opts *CallOpts, input []byte) ([]byte, error) { + // Don't crash on a lazy user + if opts == nil { + opts = new(CallOpts) + } var ( msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input} ctx = ensureContext(opts.Context) code []byte output []byte + err error ) if opts.Pending { pb, ok := c.caller.(PendingContractCaller) if !ok { - return ErrNoPendingState + return nil, ErrNoPendingState } output, err = pb.PendingCallContract(ctx, msg) if err != nil { - return err + return nil, err } if len(output) == 0 { // Make sure we have a contract to operate on, and bail out otherwise. if code, err = pb.PendingCodeAt(ctx, c.address); err != nil { - return err + return nil, err } else if len(code) == 0 { - return ErrNoCode + return nil, ErrNoCode } } } else if opts.BlockHash != (common.Hash{}) { bh, ok := c.caller.(BlockHashContractCaller) if !ok { - return ErrNoBlockHashState + return nil, ErrNoBlockHashState } output, err = bh.CallContractAtHash(ctx, msg, opts.BlockHash) if err != nil { - return err + return nil, err } if len(output) == 0 { // Make sure we have a contract to operate on, and bail out otherwise. if code, err = bh.CodeAtHash(ctx, c.address, opts.BlockHash); err != nil { - return err + return nil, err } else if len(code) == 0 { - return ErrNoCode + return nil, ErrNoCode } } } else { output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber) if err != nil { - return err + return nil, err } if len(output) == 0 { // Make sure we have a contract to operate on, and bail out otherwise. if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil { - return err + return nil, err } else if len(code) == 0 { - return ErrNoCode + return nil, ErrNoCode } } } - - if len(*results) == 0 { - res, err := c.abi.Unpack(method, output) - *results = res - return err - } - res := *results - return c.abi.UnpackIntoInterface(res[0], method, output) + return output, nil } // Transact invokes the (paid) contract method with params as input values. -func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { +func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...any) (*types.Transaction, error) { // Otherwise pack up the parameters and invoke the contract input, err := c.abi.Pack(method, params...) if err != nil { return nil, err } - // todo(rjl493456442) check whether the method is payable or not, - // reject invalid transaction at the first place return c.transact(opts, &c.address, input) } // RawTransact initiates a transaction with the given raw calldata as the input. // It's usually used to initiate transactions for invoking **Fallback** function. func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) { - // todo(rjl493456442) check whether the method is payable or not, - // reject invalid transaction at the first place return c.transact(opts, &c.address, calldata) } +// RawCreationTransact creates and submits a contract-creation transaction with +// the given calldata as the input. +func (c *BoundContract) RawCreationTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) { + return c.transact(opts, nil, calldata) +} + // Transfer initiates a plain transaction to move funds to the contract, calling // its default method if one is available. func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) { @@ -433,14 +450,13 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i // FilterLogs filters contract logs for past blocks, returning the necessary // channels to construct a strongly typed bound iterator on top of them. -func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) { +func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]any) (chan types.Log, event.Subscription, error) { // Don't crash on a lazy user if opts == nil { opts = new(FilterOpts) } // Append the event selector to the query parameters and construct the topic set - query = append([][]interface{}{{c.abi.Events[name].ID}}, query...) - + query = append([][]any{{c.abi.Events[name].ID}}, query...) topics, err := abi.MakeTopics(query...) if err != nil { return nil, nil, err @@ -479,13 +495,13 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int // WatchLogs filters subscribes to contract logs for future blocks, returning a // subscription object that can be used to tear down the watcher. -func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) { +func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]any) (chan types.Log, event.Subscription, error) { // Don't crash on a lazy user if opts == nil { opts = new(WatchOpts) } // Append the event selector to the query parameters and construct the topic set - query = append([][]interface{}{{c.abi.Events[name].ID}}, query...) + query = append([][]any{{c.abi.Events[name].ID}}, query...) topics, err := abi.MakeTopics(query...) if err != nil { @@ -509,7 +525,7 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter } // UnpackLog unpacks a retrieved log into the provided output structure. -func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error { +func (c *BoundContract) UnpackLog(out any, event string, log types.Log) error { // Anonymous events are not supported. if len(log.Topics) == 0 { return errNoEventSignature @@ -532,7 +548,7 @@ func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) } // UnpackLogIntoMap unpacks a retrieved log into the provided map. -func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error { +func (c *BoundContract) UnpackLogIntoMap(out map[string]any, event string, log types.Log) error { // Anonymous events are not supported. if len(log.Topics) == 0 { return errNoEventSignature diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/v2/base_test.go similarity index 99% rename from accounts/abi/bind/base_test.go rename to accounts/abi/bind/v2/base_test.go index f7eb7d14d3e..80d0f22f2c7 100644 --- a/accounts/abi/bind/base_test.go +++ b/accounts/abi/bind/v2/base_test.go @@ -26,7 +26,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" diff --git a/accounts/abi/bind/v2/dep_tree.go b/accounts/abi/bind/v2/dep_tree.go new file mode 100644 index 00000000000..72a6468951e --- /dev/null +++ b/accounts/abi/bind/v2/dep_tree.go @@ -0,0 +1,167 @@ +// 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 bind + +import ( + "encoding/hex" + "fmt" + "maps" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// DeploymentParams contains parameters needed to deploy one or more contracts via LinkAndDeploy +type DeploymentParams struct { + // list of all contracts targeted for the deployment + Contracts []*MetaData + + // optional map of ABI-encoded constructor inputs keyed by the MetaData.ID. + Inputs map[string][]byte + + // optional map of override addresses for specifying already-deployed + // contracts. It is keyed by the MetaData.ID. + Overrides map[string]common.Address +} + +// validate determines whether the contracts specified in the DeploymentParams +// instance have embedded deployer code in their provided MetaData instances. +func (d *DeploymentParams) validate() error { + for _, meta := range d.Contracts { + if meta.Bin == "" { + return fmt.Errorf("cannot deploy contract %s: deployer code missing from metadata", meta.ID) + } + } + return nil +} + +// DeploymentResult contains information about the result of a pending +// deployment made by LinkAndDeploy. +type DeploymentResult struct { + // Map of contract MetaData.ID to pending deployment transaction + Txs map[string]*types.Transaction + + // Map of contract MetaData.ID to the address where it will be deployed + Addresses map[string]common.Address +} + +// DeployFn deploys a contract given a deployer and optional input. It returns +// the address and a pending transaction, or an error if the deployment failed. +type DeployFn func(input, deployer []byte) (common.Address, *types.Transaction, error) + +// depTreeDeployer is responsible for taking a dependency, deploying-and-linking +// its components in the proper order. A depTreeDeployer cannot be used after +// calling LinkAndDeploy other than to retrieve the deployment result. +type depTreeDeployer struct { + deployedAddrs map[string]common.Address + deployerTxs map[string]*types.Transaction + inputs map[string][]byte // map of the root contract pattern to the constructor input (if there is any) + deployFn DeployFn +} + +func newDepTreeDeployer(deployParams *DeploymentParams, deployFn DeployFn) *depTreeDeployer { + deployedAddrs := maps.Clone(deployParams.Overrides) + if deployedAddrs == nil { + deployedAddrs = make(map[string]common.Address) + } + inputs := deployParams.Inputs + if inputs == nil { + inputs = make(map[string][]byte) + } + return &depTreeDeployer{ + deployFn: deployFn, + deployedAddrs: deployedAddrs, + deployerTxs: make(map[string]*types.Transaction), + inputs: inputs, + } +} + +// linkAndDeploy deploys a contract and it's dependencies. Because libraries +// can in-turn have their own library dependencies, linkAndDeploy performs +// deployment recursively (deepest-dependency first). The address of the +// pending contract deployment for the top-level contract is returned. +func (d *depTreeDeployer) linkAndDeploy(metadata *MetaData) (common.Address, error) { + // Don't re-deploy aliased or previously-deployed contracts + if addr, ok := d.deployedAddrs[metadata.ID]; ok { + return addr, nil + } + // If this contract/library depends on other libraries deploy them + // (and their dependencies) first + deployerCode := metadata.Bin + for _, dep := range metadata.Deps { + addr, err := d.linkAndDeploy(dep) + if err != nil { + return common.Address{}, err + } + // Link their deployed addresses into the bytecode to produce + deployerCode = strings.ReplaceAll(deployerCode, "__$"+dep.ID+"$__", strings.ToLower(addr.String()[2:])) + } + // Finally, deploy the top-level contract. + code, err := hex.DecodeString(deployerCode[2:]) + if err != nil { + panic(fmt.Sprintf("error decoding contract deployer hex %s:\n%v", deployerCode[2:], err)) + } + addr, tx, err := d.deployFn(d.inputs[metadata.ID], code) + if err != nil { + return common.Address{}, err + } + d.deployedAddrs[metadata.ID] = addr + d.deployerTxs[metadata.ID] = tx + return addr, nil +} + +// result returns a DeploymentResult instance referencing contracts deployed +// and not including any overrides specified for this deployment. +func (d *depTreeDeployer) result() *DeploymentResult { + // filter the override addresses from the deployed address set. + for pattern := range d.deployedAddrs { + if _, ok := d.deployerTxs[pattern]; !ok { + delete(d.deployedAddrs, pattern) + } + } + return &DeploymentResult{ + Txs: d.deployerTxs, + Addresses: d.deployedAddrs, + } +} + +// LinkAndDeploy performs the contract deployment specified by params using the +// provided DeployFn to create, sign and submit transactions. +// +// Contracts can depend on libraries, which in-turn can have their own library +// dependencies. Therefore, LinkAndDeploy performs the deployment recursively, +// starting with libraries (and contracts) that don't have dependencies, and +// progressing through the contracts that depend upon them. +// +// If an error is encountered, the returned DeploymentResult only contains +// entries for the contracts whose deployment submission succeeded. +// +// LinkAndDeploy performs creation and submission of creation transactions, +// but does not ensure that the contracts are included in the chain. +func LinkAndDeploy(params *DeploymentParams, deploy DeployFn) (*DeploymentResult, error) { + if err := params.validate(); err != nil { + return nil, err + } + deployer := newDepTreeDeployer(params, deploy) + for _, contract := range params.Contracts { + if _, err := deployer.linkAndDeploy(contract); err != nil { + return deployer.result(), err + } + } + return deployer.result(), nil +} diff --git a/accounts/abi/bind/v2/dep_tree_test.go b/accounts/abi/bind/v2/dep_tree_test.go new file mode 100644 index 00000000000..e686e3fec48 --- /dev/null +++ b/accounts/abi/bind/v2/dep_tree_test.go @@ -0,0 +1,370 @@ +// Copyright 2024 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 bind + +import ( + "fmt" + "regexp" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "golang.org/x/exp/rand" +) + +type linkTestCase struct { + // map of pattern to unlinked bytecode (for the purposes of tests just contains the patterns of its dependencies) + libCodes map[string]string + contractCodes map[string]string + + overrides map[string]common.Address +} + +func copyMetaData(m *MetaData) *MetaData { + m.mu.Lock() + defer m.mu.Unlock() + + var deps []*MetaData + if len(m.Deps) > 0 { + for _, dep := range m.Deps { + deps = append(deps, copyMetaData(dep)) + } + } + return &MetaData{ + Bin: m.Bin, + ABI: m.ABI, + Deps: deps, + ID: m.ID, + parsedABI: m.parsedABI, + } +} + +func makeLinkTestCase(input map[rune][]rune, overrides map[rune]common.Address) *linkTestCase { + codes := make(map[string]string) + libCodes := make(map[string]string) + contractCodes := make(map[string]string) + + inputMap := make(map[rune]map[rune]struct{}) + // set of solidity patterns for all contracts that are known to be libraries + libs := make(map[string]struct{}) + + // map of test contract id (rune) to the solidity library pattern (hash of that rune) + patternMap := map[rune]string{} + + for contract, deps := range input { + inputMap[contract] = make(map[rune]struct{}) + if _, ok := patternMap[contract]; !ok { + patternMap[contract] = crypto.Keccak256Hash([]byte(string(contract))).String()[2:36] + } + + for _, dep := range deps { + if _, ok := patternMap[dep]; !ok { + patternMap[dep] = crypto.Keccak256Hash([]byte(string(dep))).String()[2:36] + } + codes[patternMap[contract]] = codes[patternMap[contract]] + fmt.Sprintf("__$%s$__", patternMap[dep]) + inputMap[contract][dep] = struct{}{} + libs[patternMap[dep]] = struct{}{} + } + } + overridesPatterns := make(map[string]common.Address) + for contractId, overrideAddr := range overrides { + pattern := crypto.Keccak256Hash([]byte(string(contractId))).String()[2:36] + overridesPatterns[pattern] = overrideAddr + } + + for _, pattern := range patternMap { + if _, ok := libs[pattern]; ok { + // if the library didn't depend on others, give it some dummy code to not bork deployment logic down-the-line + if len(codes[pattern]) == 0 { + libCodes[pattern] = "ff" + } else { + libCodes[pattern] = codes[pattern] + } + } else { + contractCodes[pattern] = codes[pattern] + } + } + + return &linkTestCase{ + libCodes, + contractCodes, + overridesPatterns, + } +} + +var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + +type linkTestCaseInput struct { + input map[rune][]rune + overrides map[rune]struct{} + expectDeployed map[rune]struct{} +} + +// linkDeps will return a set of root dependencies and their sub-dependencies connected via the Deps field +func linkDeps(deps map[string]*MetaData) []*MetaData { + roots := make(map[string]struct{}) + for pattern := range deps { + roots[pattern] = struct{}{} + } + + connectedDeps := make(map[string]*MetaData) + for pattern, dep := range deps { + connectedDeps[pattern] = internalLinkDeps(dep, deps, &roots) + } + + var rootMetadatas []*MetaData + for pattern := range roots { + dep := connectedDeps[pattern] + rootMetadatas = append(rootMetadatas, dep) + } + return rootMetadatas +} + +// internalLinkDeps is the internal recursing logic of linkDeps: +// It links the contract referred to by MetaData given the depMap (map of solidity +// link pattern to contract metadata object), deleting contract entries from the +// roots map if they were referenced as dependencies. It returns a new MetaData +// object which is the linked version of metadata parameter. +func internalLinkDeps(metadata *MetaData, depMap map[string]*MetaData, roots *map[string]struct{}) *MetaData { + linked := copyMetaData(metadata) + depPatterns := parseLibraryDeps(metadata.Bin) + for _, pattern := range depPatterns { + delete(*roots, pattern) + connectedDep := internalLinkDeps(depMap[pattern], depMap, roots) + linked.Deps = append(linked.Deps, connectedDep) + } + return linked +} + +func testLinkCase(tcInput linkTestCaseInput) error { + var ( + testAddr = crypto.PubkeyToAddress(testKey.PublicKey) + overridesAddrs = make(map[common.Address]struct{}) + overrideAddrs = make(map[rune]common.Address) + ) + // generate deterministic addresses for the override set. + rand.Seed(42) + for contract := range tcInput.overrides { + var addr common.Address + rand.Read(addr[:]) + overrideAddrs[contract] = addr + overridesAddrs[addr] = struct{}{} + } + + tc := makeLinkTestCase(tcInput.input, overrideAddrs) + allContracts := make(map[rune]struct{}) + + for contract, deps := range tcInput.input { + allContracts[contract] = struct{}{} + for _, dep := range deps { + allContracts[dep] = struct{}{} + } + } + + var testAddrNonce uint64 + mockDeploy := func(input []byte, deployer []byte) (common.Address, *types.Transaction, error) { + contractAddr := crypto.CreateAddress(testAddr, testAddrNonce) + testAddrNonce++ + + if len(deployer) >= 20 { + // assert that this contract only references libs that are known to be deployed or in the override set + for i := 0; i < len(deployer); i += 20 { + var dep common.Address + dep.SetBytes(deployer[i : i+20]) + if _, ok := overridesAddrs[dep]; !ok { + return common.Address{}, nil, fmt.Errorf("reference to dependent contract that has not yet been deployed: %x\n", dep) + } + } + } + overridesAddrs[contractAddr] = struct{}{} + // we don't care about the txs themselves for the sake of the linking tests. so we can return nil for them in the mock deployer + return contractAddr, nil, nil + } + + contracts := make(map[string]*MetaData) + overrides := make(map[string]common.Address) + + for pattern, bin := range tc.contractCodes { + contracts[pattern] = &MetaData{ID: pattern, Bin: "0x" + bin} + } + for pattern, bin := range tc.libCodes { + contracts[pattern] = &MetaData{ + Bin: "0x" + bin, + ID: pattern, + } + } + + contractsList := linkDeps(contracts) + + for pattern, override := range tc.overrides { + overrides[pattern] = override + } + + deployParams := &DeploymentParams{ + Contracts: contractsList, + Overrides: overrides, + } + res, err := LinkAndDeploy(deployParams, mockDeploy) + if err != nil { + return err + } + + if len(res.Txs) != len(tcInput.expectDeployed) { + return fmt.Errorf("got %d deployed contracts. expected %d.\n", len(res.Addresses), len(tcInput.expectDeployed)) + } + for contract := range tcInput.expectDeployed { + pattern := crypto.Keccak256Hash([]byte(string(contract))).String()[2:36] + if _, ok := res.Addresses[pattern]; !ok { + return fmt.Errorf("expected contract %s was not deployed\n", string(contract)) + } + } + return nil +} + +func TestContractLinking(t *testing.T) { + for i, tc := range []linkTestCaseInput{ + // test simple contract without any dependencies or overrides + { + map[rune][]rune{ + 'a': {}}, + map[rune]struct{}{}, + map[rune]struct{}{ + 'a': {}}, + }, + // test deployment of a contract that depends on somes libraries. + { + map[rune][]rune{ + 'a': {'b', 'c', 'd', 'e'}}, + map[rune]struct{}{}, + map[rune]struct{}{ + 'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}}, + }, + // test deployment of a contract that depends on some libraries, + // one of which has its own library dependencies. + { + map[rune][]rune{ + 'a': {'b', 'c', 'd', 'e'}, + 'e': {'f', 'g', 'h', 'i'}}, + map[rune]struct{}{}, + map[rune]struct{}{ + 'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'i': {}}, + }, + // test single contract only without deps + { + map[rune][]rune{ + 'a': {}}, + map[rune]struct{}{}, + map[rune]struct{}{ + 'a': {}, + }, + }, + // test that libraries at different levels of the tree can share deps, + // and that these shared deps will only be deployed once. + { + map[rune][]rune{ + 'a': {'b', 'c', 'd', 'e'}, + 'e': {'f', 'g', 'h', 'i', 'm'}, + 'i': {'j', 'k', 'l', 'm'}}, + map[rune]struct{}{}, + map[rune]struct{}{ + 'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'i': {}, 'j': {}, 'k': {}, 'l': {}, 'm': {}, + }, + }, + // test two contracts can be deployed which don't share deps + linkTestCaseInput{ + map[rune][]rune{ + 'a': {'b', 'c', 'd', 'e'}, + 'f': {'g', 'h', 'i', 'j'}}, + map[rune]struct{}{}, + map[rune]struct{}{ + 'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'i': {}, 'j': {}, + }, + }, + // test two contracts can be deployed which share deps + linkTestCaseInput{ + map[rune][]rune{ + 'a': {'b', 'c', 'd', 'e'}, + 'f': {'g', 'c', 'd', 'h'}}, + map[rune]struct{}{}, + map[rune]struct{}{ + 'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, + }, + }, + // test one contract with overrides for all lib deps + linkTestCaseInput{ + map[rune][]rune{ + 'a': {'b', 'c', 'd', 'e'}}, + map[rune]struct{}{'b': {}, 'c': {}, 'd': {}, 'e': {}}, + map[rune]struct{}{ + 'a': {}}, + }, + // test one contract with overrides for some lib deps + linkTestCaseInput{ + map[rune][]rune{ + 'a': {'b', 'c'}}, + map[rune]struct{}{'b': {}, 'c': {}}, + map[rune]struct{}{ + 'a': {}}, + }, + // test deployment of a contract with overrides + linkTestCaseInput{ + map[rune][]rune{ + 'a': {}}, + map[rune]struct{}{'a': {}}, + map[rune]struct{}{}, + }, + // two contracts ('a' and 'f') share some dependencies. contract 'a' is marked as an override. expect that any of + // its depdencies that aren't shared with 'f' are not deployed. + linkTestCaseInput{map[rune][]rune{ + 'a': {'b', 'c', 'd', 'e'}, + 'f': {'g', 'c', 'd', 'h'}}, + map[rune]struct{}{'a': {}}, + map[rune]struct{}{ + 'f': {}, 'g': {}, 'c': {}, 'd': {}, 'h': {}}, + }, + // test nested libraries that share deps at different levels of the tree... with override. + // same condition as above test: no sub-dependencies of + { + map[rune][]rune{ + 'a': {'b', 'c', 'd', 'e'}, + 'e': {'f', 'g', 'h', 'i', 'm'}, + 'i': {'j', 'k', 'l', 'm'}, + 'l': {'n', 'o', 'p'}}, + map[rune]struct{}{ + 'i': {}, + }, + map[rune]struct{}{ + 'a': {}, 'b': {}, 'c': {}, 'd': {}, 'e': {}, 'f': {}, 'g': {}, 'h': {}, 'm': {}}, + }, + } { + if err := testLinkCase(tc); err != nil { + t.Fatalf("test case %d failed: %v", i, err) + } + } +} + +func parseLibraryDeps(unlinkedCode string) (res []string) { + reMatchSpecificPattern, err := regexp.Compile(`__\$([a-f0-9]+)\$__`) + if err != nil { + panic(err) + } + for _, match := range reMatchSpecificPattern.FindAllStringSubmatch(unlinkedCode, -1) { + res = append(res, match[1]) + } + return res +} diff --git a/accounts/abi/bind/v2/generate_test.go b/accounts/abi/bind/v2/generate_test.go new file mode 100644 index 00000000000..ae35e0b4755 --- /dev/null +++ b/accounts/abi/bind/v2/generate_test.go @@ -0,0 +1,102 @@ +// Copyright 2024 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 bind_test + +import ( + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/abigen" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common/compiler" + "github.com/ethereum/go-ethereum/crypto" +) + +// Run go generate to recreate the test bindings. +// +//go:generate go run github.com/ethereum/go-ethereum/cmd/abigen -v2 -combined-json internal/contracts/db/combined-abi.json -type DBStats -pkg db -out internal/contracts/db/bindings.go +//go:generate go run github.com/ethereum/go-ethereum/cmd/abigen -v2 -combined-json internal/contracts/events/combined-abi.json -type C -pkg events -out internal/contracts/events/bindings.go +//go:generate go run github.com/ethereum/go-ethereum/cmd/abigen -v2 -combined-json internal/contracts/nested_libraries/combined-abi.json -type C1 -pkg nested_libraries -out internal/contracts/nested_libraries/bindings.go +//go:generate go run github.com/ethereum/go-ethereum/cmd/abigen -v2 -combined-json internal/contracts/solc_errors/combined-abi.json -type C -pkg solc_errors -out internal/contracts/solc_errors/bindings.go +//go:generate go run github.com/ethereum/go-ethereum/cmd/abigen -v2 -combined-json internal/contracts/uint256arrayreturn/combined-abi.json -type C -pkg uint256arrayreturn -out internal/contracts/uint256arrayreturn/bindings.go + +// TestBindingGeneration tests that re-running generation of bindings does not result in +// mutations to the binding code. +func TestBindingGeneration(t *testing.T) { + matches, _ := filepath.Glob("internal/contracts/*") + var dirs []string + for _, match := range matches { + f, _ := os.Stat(match) + if f.IsDir() { + dirs = append(dirs, f.Name()) + } + } + + for _, dir := range dirs { + var ( + abis []string + bins []string + types []string + libs = make(map[string]string) + ) + basePath := filepath.Join("internal/contracts", dir) + combinedJsonPath := filepath.Join(basePath, "combined-abi.json") + abiBytes, err := os.ReadFile(combinedJsonPath) + if err != nil { + t.Fatalf("error trying to read file %s: %v", combinedJsonPath, err) + } + contracts, err := compiler.ParseCombinedJSON(abiBytes, "", "", "", "") + if err != nil { + t.Fatalf("Failed to read contract information from json output: %v", err) + } + + for name, contract := range contracts { + // fully qualified name is of the form : + nameParts := strings.Split(name, ":") + typeName := nameParts[len(nameParts)-1] + abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse + if err != nil { + utils.Fatalf("Failed to parse ABIs from compiler output: %v", err) + } + abis = append(abis, string(abi)) + bins = append(bins, contract.Code) + types = append(types, typeName) + + // Derive the library placeholder which is a 34 character prefix of the + // hex encoding of the keccak256 hash of the fully qualified library name. + // Note that the fully qualified library name is the path of its source + // file and the library name separated by ":". + libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36] // the first 2 chars are 0x + libs[libPattern] = typeName + } + code, err := abigen.BindV2(types, abis, bins, dir, libs, make(map[string]string)) + if err != nil { + t.Fatalf("error creating bindings for package %s: %v", dir, err) + } + + existingBindings, err := os.ReadFile(filepath.Join(basePath, "bindings.go")) + if err != nil { + t.Fatalf("ReadFile returned error: %v", err) + } + if code != string(existingBindings) { + t.Fatalf("code mismatch for %s", dir) + } + } +} diff --git a/accounts/abi/bind/v2/internal/contracts/db/bindings.go b/accounts/abi/bind/v2/internal/contracts/db/bindings.go new file mode 100644 index 00000000000..6291160fe96 --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/db/bindings.go @@ -0,0 +1,293 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package db + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// DBStats is an auto generated low-level Go binding around an user-defined struct. +type DBStats struct { + Gets *big.Int + Inserts *big.Int + Mods *big.Int +} + +// DBMetaData contains all meta data concerning the DB contract. +var DBMetaData = bind.MetaData{ + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"Insert\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"KeyedInsert\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"k\",\"type\":\"uint256\"}],\"name\":\"get\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNamedStatParams\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"gets\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"inserts\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"mods\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStatParams\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStatsStruct\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"gets\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"inserts\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"mods\",\"type\":\"uint256\"}],\"internalType\":\"structDB.Stats\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"k\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"v\",\"type\":\"uint256\"}],\"name\":\"insert\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", + ID: "253cc2574e2f8b5e909644530e4934f6ac", + Bin: "0x60806040525f5f553480156011575f5ffd5b5060405180606001604052805f81526020015f81526020015f81525060035f820151815f015560208201518160010155604082015181600201559050506105f78061005b5f395ff3fe60806040526004361061004d575f3560e01c80631d834a1b146100cb5780636fcb9c70146101075780639507d39a14610133578063e369ba3b1461016f578063ee8161e01461019b5761006a565b3661006a57345f5f82825461006291906103eb565b925050819055005b348015610075575f5ffd5b505f36606082828080601f0160208091040260200160405190810160405280939291908181526020018383808284375f81840152601f19601f820116905080830192505050505050509050915050805190602001f35b3480156100d6575f5ffd5b506100f160048036038101906100ec919061044c565b6101c5565b6040516100fe9190610499565b60405180910390f35b348015610112575f5ffd5b5061011b6102ef565b60405161012a939291906104b2565b60405180910390f35b34801561013e575f5ffd5b50610159600480360381019061015491906104e7565b61030e565b6040516101669190610499565b60405180910390f35b34801561017a575f5ffd5b50610183610341565b604051610192939291906104b2565b60405180910390f35b3480156101a6575f5ffd5b506101af610360565b6040516101bc9190610561565b60405180910390f35b5f5f82036101da5760028054905090506102e9565b5f60015f8581526020019081526020015f20540361023757600283908060018154018082558091505060019003905f5260205f20015f909190919091505560036001015f81548092919061022d9061057a565b9190505550610252565b60036002015f81548092919061024c9061057a565b91905055505b8160015f8581526020019081526020015f20819055507f8b39ff47dca36ab5b8b80845238af53aa579625ac7fb173dc09376adada4176983836002805490506040516102a0939291906104b2565b60405180910390a1827f40bed843c6c5f72002f9b469cf4c1ee9f7fb1eb48f091c1267970f98522ac02d836040516102d89190610499565b60405180910390a260028054905090505b92915050565b5f5f5f60035f0154600360010154600360020154925092509250909192565b5f60035f015f8154809291906103239061057a565b919050555060015f8381526020019081526020015f20549050919050565b5f5f5f60035f0154600360010154600360020154925092509250909192565b610368610397565b60036040518060600160405290815f820154815260200160018201548152602001600282015481525050905090565b60405180606001604052805f81526020015f81526020015f81525090565b5f819050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6103f5826103b5565b9150610400836103b5565b9250828201905080821115610418576104176103be565b5b92915050565b5f5ffd5b61042b816103b5565b8114610435575f5ffd5b50565b5f8135905061044681610422565b92915050565b5f5f604083850312156104625761046161041e565b5b5f61046f85828601610438565b925050602061048085828601610438565b9150509250929050565b610493816103b5565b82525050565b5f6020820190506104ac5f83018461048a565b92915050565b5f6060820190506104c55f83018661048a565b6104d2602083018561048a565b6104df604083018461048a565b949350505050565b5f602082840312156104fc576104fb61041e565b5b5f61050984828501610438565b91505092915050565b61051b816103b5565b82525050565b606082015f8201516105355f850182610512565b5060208201516105486020850182610512565b50604082015161055b6040850182610512565b50505050565b5f6060820190506105745f830184610521565b92915050565b5f610584826103b5565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036105b6576105b56103be565b5b60018201905091905056fea264697066735822122063e58431f2afdc667f8e687d3e6a99085a93c1fd3ce40b218463b8ddd3cc093664736f6c634300081c0033", +} + +// DB is an auto generated Go binding around an Ethereum contract. +type DB struct { + abi abi.ABI +} + +// NewDB creates a new instance of DB. +func NewDB() *DB { + parsed, err := DBMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &DB{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *DB) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackGet is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x9507d39a. +// +// Solidity: function get(uint256 k) returns(uint256) +func (dB *DB) PackGet(k *big.Int) []byte { + enc, err := dB.abi.Pack("get", k) + if err != nil { + panic(err) + } + return enc +} + +// UnpackGet is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x9507d39a. +// +// Solidity: function get(uint256 k) returns(uint256) +func (dB *DB) UnpackGet(data []byte) (*big.Int, error) { + out, err := dB.abi.Unpack("get", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// PackGetNamedStatParams is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xe369ba3b. +// +// Solidity: function getNamedStatParams() view returns(uint256 gets, uint256 inserts, uint256 mods) +func (dB *DB) PackGetNamedStatParams() []byte { + enc, err := dB.abi.Pack("getNamedStatParams") + if err != nil { + panic(err) + } + return enc +} + +// GetNamedStatParamsOutput serves as a container for the return parameters of contract +// method GetNamedStatParams. +type GetNamedStatParamsOutput struct { + Gets *big.Int + Inserts *big.Int + Mods *big.Int +} + +// UnpackGetNamedStatParams is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xe369ba3b. +// +// Solidity: function getNamedStatParams() view returns(uint256 gets, uint256 inserts, uint256 mods) +func (dB *DB) UnpackGetNamedStatParams(data []byte) (GetNamedStatParamsOutput, error) { + out, err := dB.abi.Unpack("getNamedStatParams", data) + outstruct := new(GetNamedStatParamsOutput) + if err != nil { + return *outstruct, err + } + outstruct.Gets = abi.ConvertType(out[0], new(big.Int)).(*big.Int) + outstruct.Inserts = abi.ConvertType(out[1], new(big.Int)).(*big.Int) + outstruct.Mods = abi.ConvertType(out[2], new(big.Int)).(*big.Int) + return *outstruct, err + +} + +// PackGetStatParams is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x6fcb9c70. +// +// Solidity: function getStatParams() view returns(uint256, uint256, uint256) +func (dB *DB) PackGetStatParams() []byte { + enc, err := dB.abi.Pack("getStatParams") + if err != nil { + panic(err) + } + return enc +} + +// GetStatParamsOutput serves as a container for the return parameters of contract +// method GetStatParams. +type GetStatParamsOutput struct { + Arg0 *big.Int + Arg1 *big.Int + Arg2 *big.Int +} + +// UnpackGetStatParams is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x6fcb9c70. +// +// Solidity: function getStatParams() view returns(uint256, uint256, uint256) +func (dB *DB) UnpackGetStatParams(data []byte) (GetStatParamsOutput, error) { + out, err := dB.abi.Unpack("getStatParams", data) + outstruct := new(GetStatParamsOutput) + if err != nil { + return *outstruct, err + } + outstruct.Arg0 = abi.ConvertType(out[0], new(big.Int)).(*big.Int) + outstruct.Arg1 = abi.ConvertType(out[1], new(big.Int)).(*big.Int) + outstruct.Arg2 = abi.ConvertType(out[2], new(big.Int)).(*big.Int) + return *outstruct, err + +} + +// PackGetStatsStruct is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xee8161e0. +// +// Solidity: function getStatsStruct() view returns((uint256,uint256,uint256)) +func (dB *DB) PackGetStatsStruct() []byte { + enc, err := dB.abi.Pack("getStatsStruct") + if err != nil { + panic(err) + } + return enc +} + +// UnpackGetStatsStruct is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xee8161e0. +// +// Solidity: function getStatsStruct() view returns((uint256,uint256,uint256)) +func (dB *DB) UnpackGetStatsStruct(data []byte) (DBStats, error) { + out, err := dB.abi.Unpack("getStatsStruct", data) + if err != nil { + return *new(DBStats), err + } + out0 := *abi.ConvertType(out[0], new(DBStats)).(*DBStats) + return out0, err +} + +// PackInsert is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x1d834a1b. +// +// Solidity: function insert(uint256 k, uint256 v) returns(uint256) +func (dB *DB) PackInsert(k *big.Int, v *big.Int) []byte { + enc, err := dB.abi.Pack("insert", k, v) + if err != nil { + panic(err) + } + return enc +} + +// UnpackInsert is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x1d834a1b. +// +// Solidity: function insert(uint256 k, uint256 v) returns(uint256) +func (dB *DB) UnpackInsert(data []byte) (*big.Int, error) { + out, err := dB.abi.Unpack("insert", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// DBInsert represents a Insert event raised by the DB contract. +type DBInsert struct { + Key *big.Int + Value *big.Int + Length *big.Int + Raw *types.Log // Blockchain specific contextual infos +} + +const DBInsertEventName = "Insert" + +// ContractEventName returns the user-defined event name. +func (DBInsert) ContractEventName() string { + return DBInsertEventName +} + +// UnpackInsertEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event Insert(uint256 key, uint256 value, uint256 length) +func (dB *DB) UnpackInsertEvent(log *types.Log) (*DBInsert, error) { + event := "Insert" + if log.Topics[0] != dB.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(DBInsert) + if len(log.Data) > 0 { + if err := dB.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range dB.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} + +// DBKeyedInsert represents a KeyedInsert event raised by the DB contract. +type DBKeyedInsert struct { + Key *big.Int + Value *big.Int + Raw *types.Log // Blockchain specific contextual infos +} + +const DBKeyedInsertEventName = "KeyedInsert" + +// ContractEventName returns the user-defined event name. +func (DBKeyedInsert) ContractEventName() string { + return DBKeyedInsertEventName +} + +// UnpackKeyedInsertEvent is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event KeyedInsert(uint256 indexed key, uint256 value) +func (dB *DB) UnpackKeyedInsertEvent(log *types.Log) (*DBKeyedInsert, error) { + event := "KeyedInsert" + if log.Topics[0] != dB.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(DBKeyedInsert) + if len(log.Data) > 0 { + if err := dB.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range dB.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} diff --git a/accounts/abi/bind/v2/internal/contracts/db/combined-abi.json b/accounts/abi/bind/v2/internal/contracts/db/combined-abi.json new file mode 100644 index 00000000000..38a67f745ae --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/db/combined-abi.json @@ -0,0 +1 @@ +{"contracts":{"contract.sol:DB":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"key","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"length","type":"uint256"}],"name":"Insert","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"key","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"KeyedInsert","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[{"internalType":"uint256","name":"k","type":"uint256"}],"name":"get","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getNamedStatParams","outputs":[{"internalType":"uint256","name":"gets","type":"uint256"},{"internalType":"uint256","name":"inserts","type":"uint256"},{"internalType":"uint256","name":"mods","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStatParams","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStatsStruct","outputs":[{"components":[{"internalType":"uint256","name":"gets","type":"uint256"},{"internalType":"uint256","name":"inserts","type":"uint256"},{"internalType":"uint256","name":"mods","type":"uint256"}],"internalType":"struct DB.Stats","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"k","type":"uint256"},{"internalType":"uint256","name":"v","type":"uint256"}],"name":"insert","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}],"bin":"60806040525f5f553480156011575f5ffd5b5060405180606001604052805f81526020015f81526020015f81525060035f820151815f015560208201518160010155604082015181600201559050506105f78061005b5f395ff3fe60806040526004361061004d575f3560e01c80631d834a1b146100cb5780636fcb9c70146101075780639507d39a14610133578063e369ba3b1461016f578063ee8161e01461019b5761006a565b3661006a57345f5f82825461006291906103eb565b925050819055005b348015610075575f5ffd5b505f36606082828080601f0160208091040260200160405190810160405280939291908181526020018383808284375f81840152601f19601f820116905080830192505050505050509050915050805190602001f35b3480156100d6575f5ffd5b506100f160048036038101906100ec919061044c565b6101c5565b6040516100fe9190610499565b60405180910390f35b348015610112575f5ffd5b5061011b6102ef565b60405161012a939291906104b2565b60405180910390f35b34801561013e575f5ffd5b50610159600480360381019061015491906104e7565b61030e565b6040516101669190610499565b60405180910390f35b34801561017a575f5ffd5b50610183610341565b604051610192939291906104b2565b60405180910390f35b3480156101a6575f5ffd5b506101af610360565b6040516101bc9190610561565b60405180910390f35b5f5f82036101da5760028054905090506102e9565b5f60015f8581526020019081526020015f20540361023757600283908060018154018082558091505060019003905f5260205f20015f909190919091505560036001015f81548092919061022d9061057a565b9190505550610252565b60036002015f81548092919061024c9061057a565b91905055505b8160015f8581526020019081526020015f20819055507f8b39ff47dca36ab5b8b80845238af53aa579625ac7fb173dc09376adada4176983836002805490506040516102a0939291906104b2565b60405180910390a1827f40bed843c6c5f72002f9b469cf4c1ee9f7fb1eb48f091c1267970f98522ac02d836040516102d89190610499565b60405180910390a260028054905090505b92915050565b5f5f5f60035f0154600360010154600360020154925092509250909192565b5f60035f015f8154809291906103239061057a565b919050555060015f8381526020019081526020015f20549050919050565b5f5f5f60035f0154600360010154600360020154925092509250909192565b610368610397565b60036040518060600160405290815f820154815260200160018201548152602001600282015481525050905090565b60405180606001604052805f81526020015f81526020015f81525090565b5f819050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6103f5826103b5565b9150610400836103b5565b9250828201905080821115610418576104176103be565b5b92915050565b5f5ffd5b61042b816103b5565b8114610435575f5ffd5b50565b5f8135905061044681610422565b92915050565b5f5f604083850312156104625761046161041e565b5b5f61046f85828601610438565b925050602061048085828601610438565b9150509250929050565b610493816103b5565b82525050565b5f6020820190506104ac5f83018461048a565b92915050565b5f6060820190506104c55f83018661048a565b6104d2602083018561048a565b6104df604083018461048a565b949350505050565b5f602082840312156104fc576104fb61041e565b5b5f61050984828501610438565b91505092915050565b61051b816103b5565b82525050565b606082015f8201516105355f850182610512565b5060208201516105486020850182610512565b50604082015161055b6040850182610512565b50505050565b5f6060820190506105745f830184610521565b92915050565b5f610584826103b5565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036105b6576105b56103be565b5b60018201905091905056fea264697066735822122063e58431f2afdc667f8e687d3e6a99085a93c1fd3ce40b218463b8ddd3cc093664736f6c634300081c0033"}},"version":"0.8.28+commit.7893614a.Darwin.appleclang"} diff --git a/accounts/abi/bind/v2/internal/contracts/db/contract.sol b/accounts/abi/bind/v2/internal/contracts/db/contract.sol new file mode 100644 index 00000000000..f24aa8d3818 --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/db/contract.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.7.0 <0.9.0; + +contract DB { + uint balance = 0; + mapping(uint => uint) private _store; + uint[] private _keys; + struct Stats { + uint gets; + uint inserts; + uint mods; // modifications + } + Stats _stats; + + event KeyedInsert(uint indexed key, uint value); + event Insert(uint key, uint value, uint length); + + constructor() { + _stats = Stats(0, 0, 0); + } + + // insert adds a key value to the store, returning the new length of the store. + function insert(uint k, uint v) external returns (uint) { + // No need to store 0 values + if (v == 0) { + return _keys.length; + } + // Check if a key is being overriden + if (_store[k] == 0) { + _keys.push(k); + _stats.inserts++; + } else { + _stats.mods++; + } + _store[k] = v; + emit Insert(k, v, _keys.length); + emit KeyedInsert(k, v); + + return _keys.length; + } + + function get(uint k) public returns (uint) { + _stats.gets++; + return _store[k]; + } + + function getStatParams() public view returns (uint, uint, uint) { + return (_stats.gets, _stats.inserts, _stats.mods); + } + + function getNamedStatParams() public view returns (uint gets, uint inserts, uint mods) { + return (_stats.gets, _stats.inserts, _stats.mods); + } + + function getStatsStruct() public view returns (Stats memory) { + return _stats; + } + + receive() external payable { + balance += msg.value; + } + + fallback(bytes calldata _input) external returns (bytes memory _output) { + _output = _input; + } +} \ No newline at end of file diff --git a/accounts/abi/bind/v2/internal/contracts/events/bindings.go b/accounts/abi/bind/v2/internal/contracts/events/bindings.go new file mode 100644 index 00000000000..580bffa23eb --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/events/bindings.go @@ -0,0 +1,160 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package events + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// CMetaData contains all meta data concerning the C contract. +var CMetaData = bind.MetaData{ + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"basic1\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"flag\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"basic2\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"EmitMulti\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"EmitOne\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ID: "55ef3c19a0ab1c1845f9e347540c1e51f5", + Bin: "0x6080604052348015600e575f5ffd5b506101a08061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063cb49374914610038578063e8e49a7114610042575b5f5ffd5b61004061004c565b005b61004a6100fd565b005b60017f8f17dc823e2f9fcdf730b8182c935574691e811e7d46399fe0ff0087795cd207600260405161007e9190610151565b60405180910390a260037f8f17dc823e2f9fcdf730b8182c935574691e811e7d46399fe0ff0087795cd20760046040516100b89190610151565b60405180910390a25f15157f3b29b9f6d15ba80d866afb3d70b7548ab1ffda3ef6e65f35f1cb05b0e2b29f4e60016040516100f39190610151565b60405180910390a2565b60017f8f17dc823e2f9fcdf730b8182c935574691e811e7d46399fe0ff0087795cd207600260405161012f9190610151565b60405180910390a2565b5f819050919050565b61014b81610139565b82525050565b5f6020820190506101645f830184610142565b9291505056fea26469706673582212207331c79de16a73a1639c4c4b3489ea78a3ed35fe62a178824f586df12672ac0564736f6c634300081c0033", +} + +// C is an auto generated Go binding around an Ethereum contract. +type C struct { + abi abi.ABI +} + +// NewC creates a new instance of C. +func NewC() *C { + parsed, err := CMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &C{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *C) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackEmitMulti is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xcb493749. +// +// Solidity: function EmitMulti() returns() +func (c *C) PackEmitMulti() []byte { + enc, err := c.abi.Pack("EmitMulti") + if err != nil { + panic(err) + } + return enc +} + +// PackEmitOne is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xe8e49a71. +// +// Solidity: function EmitOne() returns() +func (c *C) PackEmitOne() []byte { + enc, err := c.abi.Pack("EmitOne") + if err != nil { + panic(err) + } + return enc +} + +// CBasic1 represents a basic1 event raised by the C contract. +type CBasic1 struct { + Id *big.Int + Data *big.Int + Raw *types.Log // Blockchain specific contextual infos +} + +const CBasic1EventName = "basic1" + +// ContractEventName returns the user-defined event name. +func (CBasic1) ContractEventName() string { + return CBasic1EventName +} + +// UnpackBasic1Event is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event basic1(uint256 indexed id, uint256 data) +func (c *C) UnpackBasic1Event(log *types.Log) (*CBasic1, error) { + event := "basic1" + if log.Topics[0] != c.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(CBasic1) + if len(log.Data) > 0 { + if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range c.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} + +// CBasic2 represents a basic2 event raised by the C contract. +type CBasic2 struct { + Flag bool + Data *big.Int + Raw *types.Log // Blockchain specific contextual infos +} + +const CBasic2EventName = "basic2" + +// ContractEventName returns the user-defined event name. +func (CBasic2) ContractEventName() string { + return CBasic2EventName +} + +// UnpackBasic2Event is the Go binding that unpacks the event data emitted +// by contract. +// +// Solidity: event basic2(bool indexed flag, uint256 data) +func (c *C) UnpackBasic2Event(log *types.Log) (*CBasic2, error) { + event := "basic2" + if log.Topics[0] != c.abi.Events[event].ID { + return nil, errors.New("event signature mismatch") + } + out := new(CBasic2) + if len(log.Data) > 0 { + if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil { + return nil, err + } + } + var indexed abi.Arguments + for _, arg := range c.abi.Events[event].Inputs { + if arg.Indexed { + indexed = append(indexed, arg) + } + } + if err := abi.ParseTopics(out, indexed, log.Topics[1:]); err != nil { + return nil, err + } + out.Raw = log + return out, nil +} diff --git a/accounts/abi/bind/v2/internal/contracts/events/combined-abi.json b/accounts/abi/bind/v2/internal/contracts/events/combined-abi.json new file mode 100644 index 00000000000..bd6b7c3a60c --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/events/combined-abi.json @@ -0,0 +1 @@ +{"contracts":{"contract.sol:C":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"data","type":"uint256"}],"name":"basic1","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"flag","type":"bool"},{"indexed":false,"internalType":"uint256","name":"data","type":"uint256"}],"name":"basic2","type":"event"},{"inputs":[],"name":"EmitMulti","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"EmitOne","outputs":[],"stateMutability":"nonpayable","type":"function"}],"bin":"6080604052348015600e575f5ffd5b506101a08061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063cb49374914610038578063e8e49a7114610042575b5f5ffd5b61004061004c565b005b61004a6100fd565b005b60017f8f17dc823e2f9fcdf730b8182c935574691e811e7d46399fe0ff0087795cd207600260405161007e9190610151565b60405180910390a260037f8f17dc823e2f9fcdf730b8182c935574691e811e7d46399fe0ff0087795cd20760046040516100b89190610151565b60405180910390a25f15157f3b29b9f6d15ba80d866afb3d70b7548ab1ffda3ef6e65f35f1cb05b0e2b29f4e60016040516100f39190610151565b60405180910390a2565b60017f8f17dc823e2f9fcdf730b8182c935574691e811e7d46399fe0ff0087795cd207600260405161012f9190610151565b60405180910390a2565b5f819050919050565b61014b81610139565b82525050565b5f6020820190506101645f830184610142565b9291505056fea26469706673582212207331c79de16a73a1639c4c4b3489ea78a3ed35fe62a178824f586df12672ac0564736f6c634300081c0033"}},"version":"0.8.28+commit.7893614a.Darwin.appleclang"} diff --git a/accounts/abi/bind/v2/internal/contracts/events/contract.sol b/accounts/abi/bind/v2/internal/contracts/events/contract.sol new file mode 100644 index 00000000000..a30b38a9d4b --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/events/contract.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +contract C { + event basic1( + uint256 indexed id, + uint256 data + ); + event basic2( + bool indexed flag, + uint256 data + ); + + function EmitOne() public { + emit basic1( + uint256(1), + uint256(2)); + } + + // emit multiple events, different types + function EmitMulti() public { + emit basic1( + uint256(1), + uint256(2)); + emit basic1( + uint256(3), + uint256(4)); + emit basic2( + false, + uint256(1)); + } + + constructor() { + // do something with these + } +} \ No newline at end of file diff --git a/accounts/abi/bind/v2/internal/contracts/nested_libraries/abi.json b/accounts/abi/bind/v2/internal/contracts/nested_libraries/abi.json new file mode 100644 index 00000000000..7cfcdaa93a4 --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/nested_libraries/abi.json @@ -0,0 +1 @@ +{"contracts":{"contract.sol:Array":{"abi":[],"bin":"61044261004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c8063677ca2d814610038575b5f80fd5b818015610043575f80fd5b5061005e60048036038101906100599190610235565b610060565b005b5f8280549050116100a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161009d906102cd565b60405180910390fd5b81600183805490506100b89190610318565b815481106100c9576100c861034b565b5b905f5260205f2001548282815481106100e5576100e461034b565b5b905f5260205f2001819055508181815481106101045761010361034b565b5b905f5260205f20015473__$e0273646c631009d12385ab5282af2d432$__63ee05608590916040518263ffffffff1660e01b81526004016101459190610387565b602060405180830381865af4158015610160573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061018491906103b4565b8282815481106101975761019661034b565b5b905f5260205f200181905550818054806101b4576101b36103df565b5b600190038181905f5260205f20015f905590555050565b5f80fd5b5f819050919050565b6101e1816101cf565b81146101eb575f80fd5b50565b5f813590506101fc816101d8565b92915050565b5f819050919050565b61021481610202565b811461021e575f80fd5b50565b5f8135905061022f8161020b565b92915050565b5f806040838503121561024b5761024a6101cb565b5b5f610258858286016101ee565b925050602061026985828601610221565b9150509250929050565b5f82825260208201905092915050565b7f43616e27742072656d6f76652066726f6d20656d7074792061727261790000005f82015250565b5f6102b7601d83610273565b91506102c282610283565b602082019050919050565b5f6020820190508181035f8301526102e4816102ab565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61032282610202565b915061032d83610202565b9250828203905081811115610345576103446102eb565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b61038181610202565b82525050565b5f60208201905061039a5f830184610378565b92915050565b5f815190506103ae8161020b565b92915050565b5f602082840312156103c9576103c86101cb565b5b5f6103d6848285016103a0565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffdfea26469706673582212200680afb351728e7eaa7168f68e59cd7151eff98288314447ad7638a444ed11de64736f6c634300081a0033"},"contract.sol:RecursiveDep":{"abi":[{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"AddOne","outputs":[{"internalType":"uint256","name":"ret","type":"uint256"}],"stateMutability":"pure","type":"function"}],"bin":"61019d61004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c8063ee05608514610038575b5f80fd5b610052600480360381019061004d91906100b4565b610068565b60405161005f91906100ee565b60405180910390f35b5f6001826100769190610134565b9050919050565b5f80fd5b5f819050919050565b61009381610081565b811461009d575f80fd5b50565b5f813590506100ae8161008a565b92915050565b5f602082840312156100c9576100c861007d565b5b5f6100d6848285016100a0565b91505092915050565b6100e881610081565b82525050565b5f6020820190506101015f8301846100df565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61013e82610081565b915061014983610081565b925082820190508082111561016157610160610107565b5b9291505056fea2646970667358221220d392325a1e387a65c76bff6fecec456650b48856b1e00afc4fa76fb9181da23c64736f6c634300081a0033"},"contract.sol:TestArray":{"abi":[{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"arr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"testArrayRemove","outputs":[],"stateMutability":"nonpayable","type":"function"}],"bin":"6080604052348015600e575f80fd5b506103438061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806371e5ee5f14610038578063807fc49a14610068575b5f80fd5b610052600480360381019061004d91906101f0565b610084565b60405161005f919061022a565b60405180910390f35b610082600480360381019061007d91906101f0565b6100a3565b005b5f8181548110610092575f80fd5b905f5260205f20015f915090505481565b5f5b60038110156100e0575f81908060018154018082558091505060019003905f5260205f20015f909190919091505580806001019150506100a5565b505f73__$37f5055d0d00ca8ab20a50453e6986094c$__63677ca2d8909160016040518363ffffffff1660e01b815260040161011d92919061028c565b5f6040518083038186803b158015610133575f80fd5b505af4158015610145573d5f803e3d5ffd5b5050505060025f805490501461015e5761015d6102b3565b5b5f805f81548110610172576101716102e0565b5b905f5260205f20015414610189576101886102b3565b5b60025f60018154811061019f5761019e6102e0565b5b905f5260205f200154146101b6576101b56102b3565b5b50565b5f80fd5b5f819050919050565b6101cf816101bd565b81146101d9575f80fd5b50565b5f813590506101ea816101c6565b92915050565b5f60208284031215610205576102046101b9565b5b5f610212848285016101dc565b91505092915050565b610224816101bd565b82525050565b5f60208201905061023d5f83018461021b565b92915050565b8082525050565b5f819050919050565b5f819050919050565b5f61027661027161026c8461024a565b610253565b6101bd565b9050919050565b6102868161025c565b82525050565b5f60408201905061029f5f830185610243565b6102ac602083018461027d565b9392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffdfea26469706673582212204be2c6230af664b290f016e88cfac62bf7c08823b1fd1bcce8bdcd7fbb785b8a64736f6c634300081a0033"}},"version":"0.8.26+commit.8a97fa7a.Darwin.appleclang"} diff --git a/accounts/abi/bind/v2/internal/contracts/nested_libraries/bindings.go b/accounts/abi/bind/v2/internal/contracts/nested_libraries/bindings.go new file mode 100644 index 00000000000..6fd6a1b2859 --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/nested_libraries/bindings.go @@ -0,0 +1,486 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package nested_libraries + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// C1MetaData contains all meta data concerning the C1 contract. +var C1MetaData = bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"v1\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"v2\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"Do\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"res\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "ae26158f1824f3918bd66724ee8b6eb7c9", + Bin: "0x6080604052348015600e575f5ffd5b506040516103983803806103988339818101604052810190602e91906066565b5050609d565b5f5ffd5b5f819050919050565b6048816038565b81146051575f5ffd5b50565b5f815190506060816041565b92915050565b5f5f6040838503121560795760786034565b5b5f6084858286016054565b92505060206093858286016054565b9150509250929050565b6102ee806100aa5f395ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c80632ad112721461002d575b5f5ffd5b6100476004803603810190610042919061019e565b61005d565b60405161005491906101d8565b60405180910390f35b5f600173__$ffc1393672b8ed81d0c8093ffcb0e7fbe8$__632ad112725f6040518263ffffffff1660e01b81526004016100979190610200565b602060405180830381865af41580156100b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100d6919061022d565b73__$5f33a1fab8ea7d932b4bc8c5e7dcd90bc2$__632ad11272856040518263ffffffff1660e01b815260040161010d9190610200565b602060405180830381865af4158015610128573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061014c919061022d565b6101569190610285565b6101609190610285565b9050919050565b5f5ffd5b5f819050919050565b61017d8161016b565b8114610187575f5ffd5b50565b5f8135905061019881610174565b92915050565b5f602082840312156101b3576101b2610167565b5b5f6101c08482850161018a565b91505092915050565b6101d28161016b565b82525050565b5f6020820190506101eb5f8301846101c9565b92915050565b6101fa8161016b565b82525050565b5f6020820190506102135f8301846101f1565b92915050565b5f8151905061022781610174565b92915050565b5f6020828403121561024257610241610167565b5b5f61024f84828501610219565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61028f8261016b565b915061029a8361016b565b92508282019050808211156102b2576102b1610258565b5b9291505056fea26469706673582212205d4715a8d20a3a0a43113e268ec8868b3c3ce24f7cbdb8735b4eeeebf0b5565164736f6c634300081c0033", + Deps: []*bind.MetaData{ + &L1MetaData, + &L4MetaData, + }, +} + +// C1 is an auto generated Go binding around an Ethereum contract. +type C1 struct { + abi abi.ABI +} + +// NewC1 creates a new instance of C1. +func NewC1() *C1 { + parsed, err := C1MetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &C1{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *C1) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackConstructor is the Go binding used to pack the parameters required for +// contract deployment. +// +// Solidity: constructor(uint256 v1, uint256 v2) returns() +func (c1 *C1) PackConstructor(v1 *big.Int, v2 *big.Int) []byte { + enc, err := c1.abi.Pack("", v1, v2) + if err != nil { + panic(err) + } + return enc +} + +// PackDo is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256 res) +func (c1 *C1) PackDo(val *big.Int) []byte { + enc, err := c1.abi.Pack("Do", val) + if err != nil { + panic(err) + } + return enc +} + +// UnpackDo is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256 res) +func (c1 *C1) UnpackDo(data []byte) (*big.Int, error) { + out, err := c1.abi.Unpack("Do", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// C2MetaData contains all meta data concerning the C2 contract. +var C2MetaData = bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"v1\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"v2\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"Do\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"res\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "78ef2840de5b706112ca2dbfa765501a89", + Bin: "0x6080604052348015600e575f5ffd5b506040516103983803806103988339818101604052810190602e91906066565b5050609d565b5f5ffd5b5f819050919050565b6048816038565b81146051575f5ffd5b50565b5f815190506060816041565b92915050565b5f5f6040838503121560795760786034565b5b5f6084858286016054565b92505060206093858286016054565b9150509250929050565b6102ee806100aa5f395ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c80632ad112721461002d575b5f5ffd5b6100476004803603810190610042919061019e565b61005d565b60405161005491906101d8565b60405180910390f35b5f600173__$ffc1393672b8ed81d0c8093ffcb0e7fbe8$__632ad112725f6040518263ffffffff1660e01b81526004016100979190610200565b602060405180830381865af41580156100b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100d6919061022d565b73__$6070639404c39b5667691bb1f9177e1eac$__632ad11272856040518263ffffffff1660e01b815260040161010d9190610200565b602060405180830381865af4158015610128573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061014c919061022d565b6101569190610285565b6101609190610285565b9050919050565b5f5ffd5b5f819050919050565b61017d8161016b565b8114610187575f5ffd5b50565b5f8135905061019881610174565b92915050565b5f602082840312156101b3576101b2610167565b5b5f6101c08482850161018a565b91505092915050565b6101d28161016b565b82525050565b5f6020820190506101eb5f8301846101c9565b92915050565b6101fa8161016b565b82525050565b5f6020820190506102135f8301846101f1565b92915050565b5f8151905061022781610174565b92915050565b5f6020828403121561024257610241610167565b5b5f61024f84828501610219565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61028f8261016b565b915061029a8361016b565b92508282019050808211156102b2576102b1610258565b5b9291505056fea2646970667358221220dd394981f1e9fefa4d88bac1c4f1da4131779c7d3bd4189958d278e57e96d96f64736f6c634300081c0033", + Deps: []*bind.MetaData{ + &L1MetaData, + &L4bMetaData, + }, +} + +// C2 is an auto generated Go binding around an Ethereum contract. +type C2 struct { + abi abi.ABI +} + +// NewC2 creates a new instance of C2. +func NewC2() *C2 { + parsed, err := C2MetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &C2{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *C2) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackConstructor is the Go binding used to pack the parameters required for +// contract deployment. +// +// Solidity: constructor(uint256 v1, uint256 v2) returns() +func (c2 *C2) PackConstructor(v1 *big.Int, v2 *big.Int) []byte { + enc, err := c2.abi.Pack("", v1, v2) + if err != nil { + panic(err) + } + return enc +} + +// PackDo is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256 res) +func (c2 *C2) PackDo(val *big.Int) []byte { + enc, err := c2.abi.Pack("Do", val) + if err != nil { + panic(err) + } + return enc +} + +// UnpackDo is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256 res) +func (c2 *C2) UnpackDo(data []byte) (*big.Int, error) { + out, err := c2.abi.Unpack("Do", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// L1MetaData contains all meta data concerning the L1 contract. +var L1MetaData = bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"Do\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "ffc1393672b8ed81d0c8093ffcb0e7fbe8", + Bin: "0x61011c61004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106032575f3560e01c80632ad11272146036575b5f5ffd5b604c600480360381019060489190609c565b6060565b6040516057919060cf565b60405180910390f35b5f60019050919050565b5f5ffd5b5f819050919050565b607e81606e565b81146087575f5ffd5b50565b5f813590506096816077565b92915050565b5f6020828403121560ae5760ad606a565b5b5f60b984828501608a565b91505092915050565b60c981606e565b82525050565b5f60208201905060e05f83018460c2565b9291505056fea26469706673582212200161c5f22d130a2b7ec6cf22e0910e42e32c2881fa4a8a01455f524f63cf218d64736f6c634300081c0033", +} + +// L1 is an auto generated Go binding around an Ethereum contract. +type L1 struct { + abi abi.ABI +} + +// NewL1 creates a new instance of L1. +func NewL1() *L1 { + parsed, err := L1MetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &L1{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *L1) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackDo is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256) +func (l1 *L1) PackDo(val *big.Int) []byte { + enc, err := l1.abi.Pack("Do", val) + if err != nil { + panic(err) + } + return enc +} + +// UnpackDo is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256) +func (l1 *L1) UnpackDo(data []byte) (*big.Int, error) { + out, err := l1.abi.Unpack("Do", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// L2MetaData contains all meta data concerning the L2 contract. +var L2MetaData = bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"Do\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "2ce896a6dd38932d354f317286f90bc675", + Bin: "0x61025161004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c80632ad1127214610038575b5f5ffd5b610052600480360381019061004d9190610129565b610068565b60405161005f9190610163565b60405180910390f35b5f600173__$ffc1393672b8ed81d0c8093ffcb0e7fbe8$__632ad11272846040518263ffffffff1660e01b81526004016100a29190610163565b602060405180830381865af41580156100bd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100e19190610190565b6100eb91906101e8565b9050919050565b5f5ffd5b5f819050919050565b610108816100f6565b8114610112575f5ffd5b50565b5f81359050610123816100ff565b92915050565b5f6020828403121561013e5761013d6100f2565b5b5f61014b84828501610115565b91505092915050565b61015d816100f6565b82525050565b5f6020820190506101765f830184610154565b92915050565b5f8151905061018a816100ff565b92915050565b5f602082840312156101a5576101a46100f2565b5b5f6101b28482850161017c565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6101f2826100f6565b91506101fd836100f6565b9250828201905080821115610215576102146101bb565b5b9291505056fea264697066735822122026999f96e14b0e279909ca5972343113c358e93a904569409a86866e2064f0fa64736f6c634300081c0033", + Deps: []*bind.MetaData{ + &L1MetaData, + }, +} + +// L2 is an auto generated Go binding around an Ethereum contract. +type L2 struct { + abi abi.ABI +} + +// NewL2 creates a new instance of L2. +func NewL2() *L2 { + parsed, err := L2MetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &L2{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *L2) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackDo is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256) +func (l2 *L2) PackDo(val *big.Int) []byte { + enc, err := l2.abi.Pack("Do", val) + if err != nil { + panic(err) + } + return enc +} + +// UnpackDo is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256) +func (l2 *L2) UnpackDo(data []byte) (*big.Int, error) { + out, err := l2.abi.Unpack("Do", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// L2bMetaData contains all meta data concerning the L2b contract. +var L2bMetaData = bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"Do\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "fd1474cf57f7ed48491e8bfdfd0d172adf", + Bin: "0x61025161004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c80632ad1127214610038575b5f5ffd5b610052600480360381019061004d9190610129565b610068565b60405161005f9190610163565b60405180910390f35b5f600173__$ffc1393672b8ed81d0c8093ffcb0e7fbe8$__632ad11272846040518263ffffffff1660e01b81526004016100a29190610163565b602060405180830381865af41580156100bd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100e19190610190565b6100eb91906101e8565b9050919050565b5f5ffd5b5f819050919050565b610108816100f6565b8114610112575f5ffd5b50565b5f81359050610123816100ff565b92915050565b5f6020828403121561013e5761013d6100f2565b5b5f61014b84828501610115565b91505092915050565b61015d816100f6565b82525050565b5f6020820190506101765f830184610154565b92915050565b5f8151905061018a816100ff565b92915050565b5f602082840312156101a5576101a46100f2565b5b5f6101b28482850161017c565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6101f2826100f6565b91506101fd836100f6565b9250828201905080821115610215576102146101bb565b5b9291505056fea2646970667358221220d6e7078682642d273736fd63baaa28538fe72495816c810fa0e77034de385dc564736f6c634300081c0033", + Deps: []*bind.MetaData{ + &L1MetaData, + }, +} + +// L2b is an auto generated Go binding around an Ethereum contract. +type L2b struct { + abi abi.ABI +} + +// NewL2b creates a new instance of L2b. +func NewL2b() *L2b { + parsed, err := L2bMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &L2b{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *L2b) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackDo is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256) +func (l2b *L2b) PackDo(val *big.Int) []byte { + enc, err := l2b.abi.Pack("Do", val) + if err != nil { + panic(err) + } + return enc +} + +// UnpackDo is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256) +func (l2b *L2b) UnpackDo(data []byte) (*big.Int, error) { + out, err := l2b.abi.Unpack("Do", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// L3MetaData contains all meta data concerning the L3 contract. +var L3MetaData = bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"Do\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "d03b97f5e1a564374023a72ac7d1806773", + Bin: "0x61011c61004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106032575f3560e01c80632ad11272146036575b5f5ffd5b604c600480360381019060489190609c565b6060565b6040516057919060cf565b60405180910390f35b5f60019050919050565b5f5ffd5b5f819050919050565b607e81606e565b81146087575f5ffd5b50565b5f813590506096816077565b92915050565b5f6020828403121560ae5760ad606a565b5b5f60b984828501608a565b91505092915050565b60c981606e565b82525050565b5f60208201905060e05f83018460c2565b9291505056fea264697066735822122094cfcb0ce039318885cc58f6d8e609e6e4bec575e1a046d3d15ea2e01e97241e64736f6c634300081c0033", +} + +// L3 is an auto generated Go binding around an Ethereum contract. +type L3 struct { + abi abi.ABI +} + +// NewL3 creates a new instance of L3. +func NewL3() *L3 { + parsed, err := L3MetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &L3{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *L3) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackDo is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256) +func (l3 *L3) PackDo(val *big.Int) []byte { + enc, err := l3.abi.Pack("Do", val) + if err != nil { + panic(err) + } + return enc +} + +// UnpackDo is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256) +func (l3 *L3) UnpackDo(data []byte) (*big.Int, error) { + out, err := l3.abi.Unpack("Do", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// L4MetaData contains all meta data concerning the L4 contract. +var L4MetaData = bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"Do\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "5f33a1fab8ea7d932b4bc8c5e7dcd90bc2", + Bin: "0x6102d161004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c80632ad1127214610038575b5f5ffd5b610052600480360381019061004d91906101a9565b610068565b60405161005f91906101e3565b60405180910390f35b5f600173__$d03b97f5e1a564374023a72ac7d1806773$__632ad11272846040518263ffffffff1660e01b81526004016100a291906101e3565b602060405180830381865af41580156100bd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100e19190610210565b73__$2ce896a6dd38932d354f317286f90bc675$__632ad11272856040518263ffffffff1660e01b815260040161011891906101e3565b602060405180830381865af4158015610133573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101579190610210565b6101619190610268565b61016b9190610268565b9050919050565b5f5ffd5b5f819050919050565b61018881610176565b8114610192575f5ffd5b50565b5f813590506101a38161017f565b92915050565b5f602082840312156101be576101bd610172565b5b5f6101cb84828501610195565b91505092915050565b6101dd81610176565b82525050565b5f6020820190506101f65f8301846101d4565b92915050565b5f8151905061020a8161017f565b92915050565b5f6020828403121561022557610224610172565b5b5f610232848285016101fc565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61027282610176565b915061027d83610176565b92508282019050808211156102955761029461023b565b5b9291505056fea2646970667358221220531485f0b9ff78ba5ef06ef345aaddccec3ad15d1460014ccd7c2a58d36d0d4464736f6c634300081c0033", + Deps: []*bind.MetaData{ + &L2MetaData, + &L3MetaData, + }, +} + +// L4 is an auto generated Go binding around an Ethereum contract. +type L4 struct { + abi abi.ABI +} + +// NewL4 creates a new instance of L4. +func NewL4() *L4 { + parsed, err := L4MetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &L4{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *L4) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackDo is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256) +func (l4 *L4) PackDo(val *big.Int) []byte { + enc, err := l4.abi.Pack("Do", val) + if err != nil { + panic(err) + } + return enc +} + +// UnpackDo is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256) +func (l4 *L4) UnpackDo(data []byte) (*big.Int, error) { + out, err := l4.abi.Unpack("Do", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} + +// L4bMetaData contains all meta data concerning the L4b contract. +var L4bMetaData = bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"Do\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "6070639404c39b5667691bb1f9177e1eac", + Bin: "0x61025161004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c80632ad1127214610038575b5f5ffd5b610052600480360381019061004d9190610129565b610068565b60405161005f9190610163565b60405180910390f35b5f600173__$fd1474cf57f7ed48491e8bfdfd0d172adf$__632ad11272846040518263ffffffff1660e01b81526004016100a29190610163565b602060405180830381865af41580156100bd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100e19190610190565b6100eb91906101e8565b9050919050565b5f5ffd5b5f819050919050565b610108816100f6565b8114610112575f5ffd5b50565b5f81359050610123816100ff565b92915050565b5f6020828403121561013e5761013d6100f2565b5b5f61014b84828501610115565b91505092915050565b61015d816100f6565b82525050565b5f6020820190506101765f830184610154565b92915050565b5f8151905061018a816100ff565b92915050565b5f602082840312156101a5576101a46100f2565b5b5f6101b28482850161017c565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6101f2826100f6565b91506101fd836100f6565b9250828201905080821115610215576102146101bb565b5b9291505056fea264697066735822122008a2478fd2427f180ace529e137b69337cb655dc21d6426de37054c32e821c6a64736f6c634300081c0033", + Deps: []*bind.MetaData{ + &L2bMetaData, + }, +} + +// L4b is an auto generated Go binding around an Ethereum contract. +type L4b struct { + abi abi.ABI +} + +// NewL4b creates a new instance of L4b. +func NewL4b() *L4b { + parsed, err := L4bMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &L4b{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *L4b) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackDo is the Go binding used to pack the parameters required for calling +// the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256) +func (l4b *L4b) PackDo(val *big.Int) []byte { + enc, err := l4b.abi.Pack("Do", val) + if err != nil { + panic(err) + } + return enc +} + +// UnpackDo is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0x2ad11272. +// +// Solidity: function Do(uint256 val) pure returns(uint256) +func (l4b *L4b) UnpackDo(data []byte) (*big.Int, error) { + out, err := l4b.abi.Unpack("Do", data) + if err != nil { + return new(big.Int), err + } + out0 := abi.ConvertType(out[0], new(big.Int)).(*big.Int) + return out0, err +} diff --git a/accounts/abi/bind/v2/internal/contracts/nested_libraries/combined-abi.json b/accounts/abi/bind/v2/internal/contracts/nested_libraries/combined-abi.json new file mode 100644 index 00000000000..61e928aab15 --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/nested_libraries/combined-abi.json @@ -0,0 +1 @@ +{"contracts":{"contract.sol:C1":{"abi":[{"inputs":[{"internalType":"uint256","name":"v1","type":"uint256"},{"internalType":"uint256","name":"v2","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"Do","outputs":[{"internalType":"uint256","name":"res","type":"uint256"}],"stateMutability":"pure","type":"function"}],"bin":"6080604052348015600e575f5ffd5b506040516103983803806103988339818101604052810190602e91906066565b5050609d565b5f5ffd5b5f819050919050565b6048816038565b81146051575f5ffd5b50565b5f815190506060816041565b92915050565b5f5f6040838503121560795760786034565b5b5f6084858286016054565b92505060206093858286016054565b9150509250929050565b6102ee806100aa5f395ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c80632ad112721461002d575b5f5ffd5b6100476004803603810190610042919061019e565b61005d565b60405161005491906101d8565b60405180910390f35b5f600173__$ffc1393672b8ed81d0c8093ffcb0e7fbe8$__632ad112725f6040518263ffffffff1660e01b81526004016100979190610200565b602060405180830381865af41580156100b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100d6919061022d565b73__$5f33a1fab8ea7d932b4bc8c5e7dcd90bc2$__632ad11272856040518263ffffffff1660e01b815260040161010d9190610200565b602060405180830381865af4158015610128573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061014c919061022d565b6101569190610285565b6101609190610285565b9050919050565b5f5ffd5b5f819050919050565b61017d8161016b565b8114610187575f5ffd5b50565b5f8135905061019881610174565b92915050565b5f602082840312156101b3576101b2610167565b5b5f6101c08482850161018a565b91505092915050565b6101d28161016b565b82525050565b5f6020820190506101eb5f8301846101c9565b92915050565b6101fa8161016b565b82525050565b5f6020820190506102135f8301846101f1565b92915050565b5f8151905061022781610174565b92915050565b5f6020828403121561024257610241610167565b5b5f61024f84828501610219565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61028f8261016b565b915061029a8361016b565b92508282019050808211156102b2576102b1610258565b5b9291505056fea26469706673582212205d4715a8d20a3a0a43113e268ec8868b3c3ce24f7cbdb8735b4eeeebf0b5565164736f6c634300081c0033"},"contract.sol:C2":{"abi":[{"inputs":[{"internalType":"uint256","name":"v1","type":"uint256"},{"internalType":"uint256","name":"v2","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"Do","outputs":[{"internalType":"uint256","name":"res","type":"uint256"}],"stateMutability":"pure","type":"function"}],"bin":"6080604052348015600e575f5ffd5b506040516103983803806103988339818101604052810190602e91906066565b5050609d565b5f5ffd5b5f819050919050565b6048816038565b81146051575f5ffd5b50565b5f815190506060816041565b92915050565b5f5f6040838503121560795760786034565b5b5f6084858286016054565b92505060206093858286016054565b9150509250929050565b6102ee806100aa5f395ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c80632ad112721461002d575b5f5ffd5b6100476004803603810190610042919061019e565b61005d565b60405161005491906101d8565b60405180910390f35b5f600173__$ffc1393672b8ed81d0c8093ffcb0e7fbe8$__632ad112725f6040518263ffffffff1660e01b81526004016100979190610200565b602060405180830381865af41580156100b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100d6919061022d565b73__$6070639404c39b5667691bb1f9177e1eac$__632ad11272856040518263ffffffff1660e01b815260040161010d9190610200565b602060405180830381865af4158015610128573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061014c919061022d565b6101569190610285565b6101609190610285565b9050919050565b5f5ffd5b5f819050919050565b61017d8161016b565b8114610187575f5ffd5b50565b5f8135905061019881610174565b92915050565b5f602082840312156101b3576101b2610167565b5b5f6101c08482850161018a565b91505092915050565b6101d28161016b565b82525050565b5f6020820190506101eb5f8301846101c9565b92915050565b6101fa8161016b565b82525050565b5f6020820190506102135f8301846101f1565b92915050565b5f8151905061022781610174565b92915050565b5f6020828403121561024257610241610167565b5b5f61024f84828501610219565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61028f8261016b565b915061029a8361016b565b92508282019050808211156102b2576102b1610258565b5b9291505056fea2646970667358221220dd394981f1e9fefa4d88bac1c4f1da4131779c7d3bd4189958d278e57e96d96f64736f6c634300081c0033"},"contract.sol:L1":{"abi":[{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"Do","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}],"bin":"61011c61004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106032575f3560e01c80632ad11272146036575b5f5ffd5b604c600480360381019060489190609c565b6060565b6040516057919060cf565b60405180910390f35b5f60019050919050565b5f5ffd5b5f819050919050565b607e81606e565b81146087575f5ffd5b50565b5f813590506096816077565b92915050565b5f6020828403121560ae5760ad606a565b5b5f60b984828501608a565b91505092915050565b60c981606e565b82525050565b5f60208201905060e05f83018460c2565b9291505056fea26469706673582212200161c5f22d130a2b7ec6cf22e0910e42e32c2881fa4a8a01455f524f63cf218d64736f6c634300081c0033"},"contract.sol:L2":{"abi":[{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"Do","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}],"bin":"61025161004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c80632ad1127214610038575b5f5ffd5b610052600480360381019061004d9190610129565b610068565b60405161005f9190610163565b60405180910390f35b5f600173__$ffc1393672b8ed81d0c8093ffcb0e7fbe8$__632ad11272846040518263ffffffff1660e01b81526004016100a29190610163565b602060405180830381865af41580156100bd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100e19190610190565b6100eb91906101e8565b9050919050565b5f5ffd5b5f819050919050565b610108816100f6565b8114610112575f5ffd5b50565b5f81359050610123816100ff565b92915050565b5f6020828403121561013e5761013d6100f2565b5b5f61014b84828501610115565b91505092915050565b61015d816100f6565b82525050565b5f6020820190506101765f830184610154565b92915050565b5f8151905061018a816100ff565b92915050565b5f602082840312156101a5576101a46100f2565b5b5f6101b28482850161017c565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6101f2826100f6565b91506101fd836100f6565b9250828201905080821115610215576102146101bb565b5b9291505056fea264697066735822122026999f96e14b0e279909ca5972343113c358e93a904569409a86866e2064f0fa64736f6c634300081c0033"},"contract.sol:L2b":{"abi":[{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"Do","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}],"bin":"61025161004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c80632ad1127214610038575b5f5ffd5b610052600480360381019061004d9190610129565b610068565b60405161005f9190610163565b60405180910390f35b5f600173__$ffc1393672b8ed81d0c8093ffcb0e7fbe8$__632ad11272846040518263ffffffff1660e01b81526004016100a29190610163565b602060405180830381865af41580156100bd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100e19190610190565b6100eb91906101e8565b9050919050565b5f5ffd5b5f819050919050565b610108816100f6565b8114610112575f5ffd5b50565b5f81359050610123816100ff565b92915050565b5f6020828403121561013e5761013d6100f2565b5b5f61014b84828501610115565b91505092915050565b61015d816100f6565b82525050565b5f6020820190506101765f830184610154565b92915050565b5f8151905061018a816100ff565b92915050565b5f602082840312156101a5576101a46100f2565b5b5f6101b28482850161017c565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6101f2826100f6565b91506101fd836100f6565b9250828201905080821115610215576102146101bb565b5b9291505056fea2646970667358221220d6e7078682642d273736fd63baaa28538fe72495816c810fa0e77034de385dc564736f6c634300081c0033"},"contract.sol:L3":{"abi":[{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"Do","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}],"bin":"61011c61004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106032575f3560e01c80632ad11272146036575b5f5ffd5b604c600480360381019060489190609c565b6060565b6040516057919060cf565b60405180910390f35b5f60019050919050565b5f5ffd5b5f819050919050565b607e81606e565b81146087575f5ffd5b50565b5f813590506096816077565b92915050565b5f6020828403121560ae5760ad606a565b5b5f60b984828501608a565b91505092915050565b60c981606e565b82525050565b5f60208201905060e05f83018460c2565b9291505056fea264697066735822122094cfcb0ce039318885cc58f6d8e609e6e4bec575e1a046d3d15ea2e01e97241e64736f6c634300081c0033"},"contract.sol:L4":{"abi":[{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"Do","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}],"bin":"6102d161004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c80632ad1127214610038575b5f5ffd5b610052600480360381019061004d91906101a9565b610068565b60405161005f91906101e3565b60405180910390f35b5f600173__$d03b97f5e1a564374023a72ac7d1806773$__632ad11272846040518263ffffffff1660e01b81526004016100a291906101e3565b602060405180830381865af41580156100bd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100e19190610210565b73__$2ce896a6dd38932d354f317286f90bc675$__632ad11272856040518263ffffffff1660e01b815260040161011891906101e3565b602060405180830381865af4158015610133573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101579190610210565b6101619190610268565b61016b9190610268565b9050919050565b5f5ffd5b5f819050919050565b61018881610176565b8114610192575f5ffd5b50565b5f813590506101a38161017f565b92915050565b5f602082840312156101be576101bd610172565b5b5f6101cb84828501610195565b91505092915050565b6101dd81610176565b82525050565b5f6020820190506101f65f8301846101d4565b92915050565b5f8151905061020a8161017f565b92915050565b5f6020828403121561022557610224610172565b5b5f610232848285016101fc565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61027282610176565b915061027d83610176565b92508282019050808211156102955761029461023b565b5b9291505056fea2646970667358221220531485f0b9ff78ba5ef06ef345aaddccec3ad15d1460014ccd7c2a58d36d0d4464736f6c634300081c0033"},"contract.sol:L4b":{"abi":[{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"Do","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}],"bin":"61025161004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c80632ad1127214610038575b5f5ffd5b610052600480360381019061004d9190610129565b610068565b60405161005f9190610163565b60405180910390f35b5f600173__$fd1474cf57f7ed48491e8bfdfd0d172adf$__632ad11272846040518263ffffffff1660e01b81526004016100a29190610163565b602060405180830381865af41580156100bd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100e19190610190565b6100eb91906101e8565b9050919050565b5f5ffd5b5f819050919050565b610108816100f6565b8114610112575f5ffd5b50565b5f81359050610123816100ff565b92915050565b5f6020828403121561013e5761013d6100f2565b5b5f61014b84828501610115565b91505092915050565b61015d816100f6565b82525050565b5f6020820190506101765f830184610154565b92915050565b5f8151905061018a816100ff565b92915050565b5f602082840312156101a5576101a46100f2565b5b5f6101b28482850161017c565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6101f2826100f6565b91506101fd836100f6565b9250828201905080821115610215576102146101bb565b5b9291505056fea264697066735822122008a2478fd2427f180ace529e137b69337cb655dc21d6426de37054c32e821c6a64736f6c634300081c0033"}},"version":"0.8.28+commit.7893614a.Darwin.appleclang"} diff --git a/accounts/abi/bind/v2/internal/contracts/nested_libraries/contract.sol b/accounts/abi/bind/v2/internal/contracts/nested_libraries/contract.sol new file mode 100644 index 00000000000..1794e72ac9d --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/nested_libraries/contract.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + + +// L1 +// \ +// L2 L3 L1 +// \ / / +// L4 / +// \ / +// C1 +// +library L1 { + function Do(uint256 val) public pure returns (uint256) { + return uint256(1); + } +} + +library L2 { + function Do(uint256 val) public pure returns (uint256) { + return L1.Do(val) + uint256(1); + } +} + +library L3 { + function Do(uint256 val) public pure returns (uint256) { + return uint256(1); + } +} + +library L4 { + function Do(uint256 val) public pure returns (uint256) { + return L2.Do(uint256(val)) + L3.Do(uint256(val)) + uint256(1); + } +} + +contract C1 { + function Do(uint256 val) public pure returns (uint256 res) { + return L4.Do(uint256(val)) + L1.Do(uint256(0)) + uint256(1); + } + + constructor(uint256 v1, uint256 v2) { + // do something with these + } +} + +// second contract+libraries: slightly different library deps than V1, but sharing several +// L1 +// \ +// L2b L3 L1 +// \ / / +// L4b / +// \ / +// C2 +// +library L4b { + function Do(uint256 val) public pure returns (uint256) { + return L2b.Do(uint256(val)) + uint256(1); + } +} + +library L2b { + function Do(uint256 val) public pure returns (uint256) { + return L1.Do(uint256(val)) + uint256(1); + } +} + +contract C2 { + function Do(uint256 val) public pure returns (uint256 res) { + return L4b.Do(uint256(val)) + L1.Do(uint256(0)) + uint256(1); + } + + constructor(uint256 v1, uint256 v2) { + // do something with these + } +} \ No newline at end of file diff --git a/accounts/abi/bind/v2/internal/contracts/solc_errors/bindings.go b/accounts/abi/bind/v2/internal/contracts/solc_errors/bindings.go new file mode 100644 index 00000000000..067fb2b0e11 --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/solc_errors/bindings.go @@ -0,0 +1,217 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package solc_errors + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// CMetaData contains all meta data concerning the C contract. +var CMetaData = bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"arg1\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"arg2\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"arg3\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"arg4\",\"type\":\"bool\"}],\"name\":\"BadThing\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"arg1\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"arg2\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"arg3\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"arg4\",\"type\":\"uint256\"}],\"name\":\"BadThing2\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Bar\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Foo\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "55ef3c19a0ab1c1845f9e347540c1e51f5", + Bin: "0x6080604052348015600e575f5ffd5b506101c58061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063b0a378b014610038578063bfb4ebcf14610042575b5f5ffd5b61004061004c565b005b61004a610092565b005b5f6001600260036040517fd233a24f00000000000000000000000000000000000000000000000000000000815260040161008994939291906100ef565b60405180910390fd5b5f600160025f6040517fbb6a82f10000000000000000000000000000000000000000000000000000000081526004016100ce949392919061014c565b60405180910390fd5b5f819050919050565b6100e9816100d7565b82525050565b5f6080820190506101025f8301876100e0565b61010f60208301866100e0565b61011c60408301856100e0565b61012960608301846100e0565b95945050505050565b5f8115159050919050565b61014681610132565b82525050565b5f60808201905061015f5f8301876100e0565b61016c60208301866100e0565b61017960408301856100e0565b610186606083018461013d565b9594505050505056fea26469706673582212206a82b4c28576e4483a81102558271cfefc891cd63b95440dea521185c1ff6a2a64736f6c634300081c0033", +} + +// C is an auto generated Go binding around an Ethereum contract. +type C struct { + abi abi.ABI +} + +// NewC creates a new instance of C. +func NewC() *C { + parsed, err := CMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &C{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *C) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackBar is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xb0a378b0. +// +// Solidity: function Bar() pure returns() +func (c *C) PackBar() []byte { + enc, err := c.abi.Pack("Bar") + if err != nil { + panic(err) + } + return enc +} + +// PackFoo is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xbfb4ebcf. +// +// Solidity: function Foo() pure returns() +func (c *C) PackFoo() []byte { + enc, err := c.abi.Pack("Foo") + if err != nil { + panic(err) + } + return enc +} + +// UnpackError attempts to decode the provided error data using user-defined +// error definitions. +func (c *C) UnpackError(raw []byte) (any, error) { + if bytes.Equal(raw[:4], c.abi.Errors["BadThing"].ID.Bytes()[:4]) { + return c.UnpackBadThingError(raw[4:]) + } + if bytes.Equal(raw[:4], c.abi.Errors["BadThing2"].ID.Bytes()[:4]) { + return c.UnpackBadThing2Error(raw[4:]) + } + return nil, errors.New("Unknown error") +} + +// CBadThing represents a BadThing error raised by the C contract. +type CBadThing struct { + Arg1 *big.Int + Arg2 *big.Int + Arg3 *big.Int + Arg4 bool +} + +// ErrorID returns the hash of canonical representation of the error's signature. +// +// Solidity: error BadThing(uint256 arg1, uint256 arg2, uint256 arg3, bool arg4) +func CBadThingErrorID() common.Hash { + return common.HexToHash("0xbb6a82f123854747ef4381e30e497f934a3854753fec99a69c35c30d4b46714d") +} + +// UnpackBadThingError is the Go binding used to decode the provided +// error data into the corresponding Go error struct. +// +// Solidity: error BadThing(uint256 arg1, uint256 arg2, uint256 arg3, bool arg4) +func (c *C) UnpackBadThingError(raw []byte) (*CBadThing, error) { + out := new(CBadThing) + if err := c.abi.UnpackIntoInterface(out, "BadThing", raw); err != nil { + return nil, err + } + return out, nil +} + +// CBadThing2 represents a BadThing2 error raised by the C contract. +type CBadThing2 struct { + Arg1 *big.Int + Arg2 *big.Int + Arg3 *big.Int + Arg4 *big.Int +} + +// ErrorID returns the hash of canonical representation of the error's signature. +// +// Solidity: error BadThing2(uint256 arg1, uint256 arg2, uint256 arg3, uint256 arg4) +func CBadThing2ErrorID() common.Hash { + return common.HexToHash("0xd233a24f02271fe7c9470e060d0fda6447a142bf12ab31fed7ab65affd546175") +} + +// UnpackBadThing2Error is the Go binding used to decode the provided +// error data into the corresponding Go error struct. +// +// Solidity: error BadThing2(uint256 arg1, uint256 arg2, uint256 arg3, uint256 arg4) +func (c *C) UnpackBadThing2Error(raw []byte) (*CBadThing2, error) { + out := new(CBadThing2) + if err := c.abi.UnpackIntoInterface(out, "BadThing2", raw); err != nil { + return nil, err + } + return out, nil +} + +// C2MetaData contains all meta data concerning the C2 contract. +var C2MetaData = bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"arg1\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"arg2\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"arg3\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"arg4\",\"type\":\"bool\"}],\"name\":\"BadThing\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Foo\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "78ef2840de5b706112ca2dbfa765501a89", + Bin: "0x6080604052348015600e575f5ffd5b506101148061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106026575f3560e01c8063bfb4ebcf14602a575b5f5ffd5b60306032565b005b5f600160025f6040517fbb6a82f1000000000000000000000000000000000000000000000000000000008152600401606c949392919060a3565b60405180910390fd5b5f819050919050565b6085816075565b82525050565b5f8115159050919050565b609d81608b565b82525050565b5f60808201905060b45f830187607e565b60bf6020830186607e565b60ca6040830185607e565b60d560608301846096565b9594505050505056fea2646970667358221220e90bf647ffc897060e44b88d54995ed0c03c988fbccaf034375c2ff4e594690764736f6c634300081c0033", +} + +// C2 is an auto generated Go binding around an Ethereum contract. +type C2 struct { + abi abi.ABI +} + +// NewC2 creates a new instance of C2. +func NewC2() *C2 { + parsed, err := C2MetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &C2{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *C2) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackFoo is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xbfb4ebcf. +// +// Solidity: function Foo() pure returns() +func (c2 *C2) PackFoo() []byte { + enc, err := c2.abi.Pack("Foo") + if err != nil { + panic(err) + } + return enc +} + +// UnpackError attempts to decode the provided error data using user-defined +// error definitions. +func (c2 *C2) UnpackError(raw []byte) (any, error) { + if bytes.Equal(raw[:4], c2.abi.Errors["BadThing"].ID.Bytes()[:4]) { + return c2.UnpackBadThingError(raw[4:]) + } + return nil, errors.New("Unknown error") +} + +// C2BadThing represents a BadThing error raised by the C2 contract. +type C2BadThing struct { + Arg1 *big.Int + Arg2 *big.Int + Arg3 *big.Int + Arg4 bool +} + +// ErrorID returns the hash of canonical representation of the error's signature. +// +// Solidity: error BadThing(uint256 arg1, uint256 arg2, uint256 arg3, bool arg4) +func C2BadThingErrorID() common.Hash { + return common.HexToHash("0xbb6a82f123854747ef4381e30e497f934a3854753fec99a69c35c30d4b46714d") +} + +// UnpackBadThingError is the Go binding used to decode the provided +// error data into the corresponding Go error struct. +// +// Solidity: error BadThing(uint256 arg1, uint256 arg2, uint256 arg3, bool arg4) +func (c2 *C2) UnpackBadThingError(raw []byte) (*C2BadThing, error) { + out := new(C2BadThing) + if err := c2.abi.UnpackIntoInterface(out, "BadThing", raw); err != nil { + return nil, err + } + return out, nil +} diff --git a/accounts/abi/bind/v2/internal/contracts/solc_errors/combined-abi.json b/accounts/abi/bind/v2/internal/contracts/solc_errors/combined-abi.json new file mode 100644 index 00000000000..a8fdf9dc3c0 --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/solc_errors/combined-abi.json @@ -0,0 +1 @@ +{"contracts":{"contract.sol:C":{"abi":[{"inputs":[{"internalType":"uint256","name":"arg1","type":"uint256"},{"internalType":"uint256","name":"arg2","type":"uint256"},{"internalType":"uint256","name":"arg3","type":"uint256"},{"internalType":"bool","name":"arg4","type":"bool"}],"name":"BadThing","type":"error"},{"inputs":[{"internalType":"uint256","name":"arg1","type":"uint256"},{"internalType":"uint256","name":"arg2","type":"uint256"},{"internalType":"uint256","name":"arg3","type":"uint256"},{"internalType":"uint256","name":"arg4","type":"uint256"}],"name":"BadThing2","type":"error"},{"inputs":[],"name":"Bar","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"Foo","outputs":[],"stateMutability":"pure","type":"function"}],"bin":"6080604052348015600e575f5ffd5b506101c58061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063b0a378b014610038578063bfb4ebcf14610042575b5f5ffd5b61004061004c565b005b61004a610092565b005b5f6001600260036040517fd233a24f00000000000000000000000000000000000000000000000000000000815260040161008994939291906100ef565b60405180910390fd5b5f600160025f6040517fbb6a82f10000000000000000000000000000000000000000000000000000000081526004016100ce949392919061014c565b60405180910390fd5b5f819050919050565b6100e9816100d7565b82525050565b5f6080820190506101025f8301876100e0565b61010f60208301866100e0565b61011c60408301856100e0565b61012960608301846100e0565b95945050505050565b5f8115159050919050565b61014681610132565b82525050565b5f60808201905061015f5f8301876100e0565b61016c60208301866100e0565b61017960408301856100e0565b610186606083018461013d565b9594505050505056fea26469706673582212206a82b4c28576e4483a81102558271cfefc891cd63b95440dea521185c1ff6a2a64736f6c634300081c0033"},"contract.sol:C2":{"abi":[{"inputs":[{"internalType":"uint256","name":"arg1","type":"uint256"},{"internalType":"uint256","name":"arg2","type":"uint256"},{"internalType":"uint256","name":"arg3","type":"uint256"},{"internalType":"bool","name":"arg4","type":"bool"}],"name":"BadThing","type":"error"},{"inputs":[],"name":"Foo","outputs":[],"stateMutability":"pure","type":"function"}],"bin":"6080604052348015600e575f5ffd5b506101148061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106026575f3560e01c8063bfb4ebcf14602a575b5f5ffd5b60306032565b005b5f600160025f6040517fbb6a82f1000000000000000000000000000000000000000000000000000000008152600401606c949392919060a3565b60405180910390fd5b5f819050919050565b6085816075565b82525050565b5f8115159050919050565b609d81608b565b82525050565b5f60808201905060b45f830187607e565b60bf6020830186607e565b60ca6040830185607e565b60d560608301846096565b9594505050505056fea2646970667358221220e90bf647ffc897060e44b88d54995ed0c03c988fbccaf034375c2ff4e594690764736f6c634300081c0033"}},"version":"0.8.28+commit.7893614a.Darwin.appleclang"} diff --git a/accounts/abi/bind/v2/internal/contracts/solc_errors/contract.sol b/accounts/abi/bind/v2/internal/contracts/solc_errors/contract.sol new file mode 100644 index 00000000000..541352a1d83 --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/solc_errors/contract.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +error BadThing(uint256 arg1, uint256 arg2, uint256 arg3, bool arg4); +error BadThing2(uint256 arg1, uint256 arg2, uint256 arg3, uint256 arg4); + +contract C { + function Foo() public pure { + revert BadThing({ + arg1: uint256(0), + arg2: uint256(1), + arg3: uint256(2), + arg4: false + }); + } + function Bar() public pure { + revert BadThing2({ + arg1: uint256(0), + arg2: uint256(1), + arg3: uint256(2), + arg4: uint256(3) + }); + } +} + +// purpose of this is to test that generation of metadata for contract that emits one error produces valid Go code +contract C2 { + function Foo() public pure { + revert BadThing({ + arg1: uint256(0), + arg2: uint256(1), + arg3: uint256(2), + arg4: false + }); + } +} \ No newline at end of file diff --git a/accounts/abi/bind/v2/internal/contracts/uint256arrayreturn/bindings.go b/accounts/abi/bind/v2/internal/contracts/uint256arrayreturn/bindings.go new file mode 100644 index 00000000000..4999cc75d9e --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/uint256arrayreturn/bindings.go @@ -0,0 +1,77 @@ +// Code generated via abigen V2 - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package uint256arrayreturn + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = bytes.Equal + _ = errors.New + _ = big.NewInt + _ = common.Big1 + _ = types.BloomLookup + _ = abi.ConvertType +) + +// MyContractMetaData contains all meta data concerning the MyContract contract. +var MyContractMetaData = bind.MetaData{ + ABI: "[{\"inputs\":[],\"name\":\"GetNums\",\"outputs\":[{\"internalType\":\"uint256[5]\",\"name\":\"\",\"type\":\"uint256[5]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ID: "e48e83c9c45b19a47bd451eedc725a6bff", + Bin: "0x6080604052348015600e575f5ffd5b506101a78061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063bd6d10071461002d575b5f5ffd5b61003561004b565b6040516100429190610158565b60405180910390f35b610053610088565b5f6040518060a001604052805f8152602001600181526020016002815260200160038152602001600481525090508091505090565b6040518060a00160405280600590602082028036833780820191505090505090565b5f60059050919050565b5f81905092915050565b5f819050919050565b5f819050919050565b6100d9816100c7565b82525050565b5f6100ea83836100d0565b60208301905092915050565b5f602082019050919050565b61010b816100aa565b61011581846100b4565b9250610120826100be565b805f5b8381101561015057815161013787826100df565b9650610142836100f6565b925050600181019050610123565b505050505050565b5f60a08201905061016b5f830184610102565b9291505056fea2646970667358221220ef76cc678ca215c3e9e5261e3f33ac1cb9901c3186c2af167bfcd8f03b3b864c64736f6c634300081c0033", +} + +// MyContract is an auto generated Go binding around an Ethereum contract. +type MyContract struct { + abi abi.ABI +} + +// NewMyContract creates a new instance of MyContract. +func NewMyContract() *MyContract { + parsed, err := MyContractMetaData.ParseABI() + if err != nil { + panic(errors.New("invalid ABI: " + err.Error())) + } + return &MyContract{abi: *parsed} +} + +// Instance creates a wrapper for a deployed contract instance at the given address. +// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. +func (c *MyContract) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { + return bind.NewBoundContract(addr, c.abi, backend, backend, backend) +} + +// PackGetNums is the Go binding used to pack the parameters required for calling +// the contract method with ID 0xbd6d1007. +// +// Solidity: function GetNums() pure returns(uint256[5]) +func (myContract *MyContract) PackGetNums() []byte { + enc, err := myContract.abi.Pack("GetNums") + if err != nil { + panic(err) + } + return enc +} + +// UnpackGetNums is the Go binding that unpacks the parameters returned +// from invoking the contract method with ID 0xbd6d1007. +// +// Solidity: function GetNums() pure returns(uint256[5]) +func (myContract *MyContract) UnpackGetNums(data []byte) ([5]*big.Int, error) { + out, err := myContract.abi.Unpack("GetNums", data) + if err != nil { + return *new([5]*big.Int), err + } + out0 := *abi.ConvertType(out[0], new([5]*big.Int)).(*[5]*big.Int) + return out0, err +} diff --git a/accounts/abi/bind/v2/internal/contracts/uint256arrayreturn/combined-abi.json b/accounts/abi/bind/v2/internal/contracts/uint256arrayreturn/combined-abi.json new file mode 100644 index 00000000000..f0b424b82f5 --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/uint256arrayreturn/combined-abi.json @@ -0,0 +1 @@ +{"contracts":{"contract.sol:MyContract":{"abi":[{"inputs":[],"name":"GetNums","outputs":[{"internalType":"uint256[5]","name":"","type":"uint256[5]"}],"stateMutability":"pure","type":"function"}],"bin":"6080604052348015600e575f5ffd5b506101a78061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063bd6d10071461002d575b5f5ffd5b61003561004b565b6040516100429190610158565b60405180910390f35b610053610088565b5f6040518060a001604052805f8152602001600181526020016002815260200160038152602001600481525090508091505090565b6040518060a00160405280600590602082028036833780820191505090505090565b5f60059050919050565b5f81905092915050565b5f819050919050565b5f819050919050565b6100d9816100c7565b82525050565b5f6100ea83836100d0565b60208301905092915050565b5f602082019050919050565b61010b816100aa565b61011581846100b4565b9250610120826100be565b805f5b8381101561015057815161013787826100df565b9650610142836100f6565b925050600181019050610123565b505050505050565b5f60a08201905061016b5f830184610102565b9291505056fea2646970667358221220ef76cc678ca215c3e9e5261e3f33ac1cb9901c3186c2af167bfcd8f03b3b864c64736f6c634300081c0033"}},"version":"0.8.28+commit.7893614a.Darwin.appleclang"} diff --git a/accounts/abi/bind/v2/internal/contracts/uint256arrayreturn/contract.sol b/accounts/abi/bind/v2/internal/contracts/uint256arrayreturn/contract.sol new file mode 100644 index 00000000000..cb2aa54b38a --- /dev/null +++ b/accounts/abi/bind/v2/internal/contracts/uint256arrayreturn/contract.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +contract MyContract { + // emit multiple events, different types + function GetNums() public pure returns (uint256[5] memory) { + uint256[5] memory myNums = [uint256(0), uint256(1), uint256(2), uint256(3), uint256(4)]; + return myNums; + } +} \ No newline at end of file diff --git a/accounts/abi/bind/v2/lib.go b/accounts/abi/bind/v2/lib.go new file mode 100644 index 00000000000..38311613413 --- /dev/null +++ b/accounts/abi/bind/v2/lib.go @@ -0,0 +1,243 @@ +// Copyright 2024 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 bind implements utilities for interacting with Solidity contracts. +// This is the 'runtime' for contract bindings generated with the abigen command. +// It includes methods for calling/transacting, filtering chain history for +// specific custom Solidity event types, and creating event subscriptions to monitor the +// chain for event occurrences. +// +// Two methods for contract deployment are provided: +// - [DeployContract] is intended to be used for deployment of a single contract. +// - [LinkAndDeploy] is intended for the deployment of multiple +// contracts, potentially with library dependencies. +package bind + +import ( + "errors" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/event" +) + +// ContractEvent is a type constraint for ABI event types. +type ContractEvent interface { + ContractEventName() string +} + +// FilterEvents filters a historical block range for instances of emission of a +// specific event type from a specified contract. It returns an error if the +// provided filter opts are invalid or the backend is closed. +// +// FilterEvents is intended to be used with contract event unpack methods in +// bindings generated with the abigen --v2 flag. It should be +// preferred over BoundContract.FilterLogs. +func FilterEvents[Ev ContractEvent](c *BoundContract, opts *FilterOpts, unpack func(*types.Log) (*Ev, error), topics ...[]any) (*EventIterator[Ev], error) { + var e Ev + logs, sub, err := c.FilterLogs(opts, e.ContractEventName(), topics...) + if err != nil { + return nil, err + } + return &EventIterator[Ev]{unpack: unpack, logs: logs, sub: sub}, nil +} + +// WatchEvents creates an event subscription to notify when logs of the +// specified event type are emitted from the given contract. Received logs are +// unpacked and forwarded to sink. If topics are specified, only events are +// forwarded which match the topics. +// +// WatchEvents returns a subscription or an error if the provided WatchOpts are +// invalid or the backend is closed. +// +// WatchEvents is intended to be used with contract event unpack methods in +// bindings generated with the abigen --v2 flag. It should be +// preferred over BoundContract.WatchLogs. +func WatchEvents[Ev ContractEvent](c *BoundContract, opts *WatchOpts, unpack func(*types.Log) (*Ev, error), sink chan<- *Ev, topics ...[]any) (event.Subscription, error) { + var e Ev + logs, sub, err := c.WatchLogs(opts, e.ContractEventName(), topics...) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + ev, err := unpack(&log) + if err != nil { + return err + } + + select { + case sink <- ev: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// EventIterator is an object for iterating over the results of a event log +// filter call. +type EventIterator[T any] struct { + current *T + unpack func(*types.Log) (*T, error) + logs <-chan types.Log + sub ethereum.Subscription + fail error // error to hold reason for iteration failure + closed bool // true if Close has been called +} + +// Value returns the current value of the iterator, or nil if there isn't one. +func (it *EventIterator[T]) Value() *T { + return it.current +} + +// Next advances the iterator to the subsequent event (if there is one), +// returning true if the iterator advanced. +// +// If the attempt to convert the raw log object to an instance of T using the +// unpack function provided via FilterEvents returns an error: that error is +// returned and subsequent calls to Next will not advance the iterator. +func (it *EventIterator[T]) Next() (advanced bool) { + // If the iterator failed with an error, don't proceed + if it.fail != nil || it.closed { + return false + } + // if the iterator is still active, block until a log is received or the + // underlying subscription terminates. + select { + case log := <-it.logs: + res, err := it.unpack(&log) + if err != nil { + it.fail = err + return false + } + it.current = res + return true + case <-it.sub.Err(): + // regardless of how the subscription ends, still be able to iterate + // over any unread logs. + select { + case log := <-it.logs: + res, err := it.unpack(&log) + if err != nil { + it.fail = err + return false + } + it.current = res + return true + default: + return false + } + } +} + +// Error returns an error if iteration has failed. +func (it *EventIterator[T]) Error() error { + return it.fail +} + +// Close releases any pending underlying resources. Any subsequent calls to +// Next will not advance the iterator, but the current value remains accessible. +func (it *EventIterator[T]) Close() error { + it.closed = true + it.sub.Unsubscribe() + return nil +} + +// Call performs an eth_call to a contract with optional call data. +// +// To call a function that doesn't return any output, pass nil as the unpack +// function. This can be useful if you just want to check that the function +// doesn't revert. +// +// Call is intended to be used with contract method unpack methods in +// bindings generated with the abigen --v2 flag. It should be +// preferred over BoundContract.Call +func Call[T any](c *BoundContract, opts *CallOpts, calldata []byte, unpack func([]byte) (T, error)) (T, error) { + var defaultResult T + packedOutput, err := c.CallRaw(opts, calldata) + if err != nil { + return defaultResult, err + } + if unpack == nil { + if len(packedOutput) > 0 { + return defaultResult, errors.New("contract returned data, but no unpack function was given") + } + return defaultResult, nil + } + res, err := unpack(packedOutput) + if err != nil { + return defaultResult, err + } + return res, err +} + +// Transact creates and submits a transaction to a contract with optional input +// data. +// +// Transact is identical to BoundContract.RawTransact, and is provided as a +// package-level method so that interactions with contracts whose bindings were +// generated with the abigen --v2 flag are consistent (they do not require +// calling methods on the BoundContract instance). +func Transact(c *BoundContract, opt *TransactOpts, data []byte) (*types.Transaction, error) { + return c.RawTransact(opt, data) +} + +// DeployContract creates and submits a deployment transaction based on the +// deployer bytecode and optional ABI-encoded constructor input. It returns +// the address and creation transaction of the pending contract, or an error +// if the creation failed. +// +// To initiate the deployment of multiple contracts with one method call, see the +// [LinkAndDeploy] method. +func DeployContract(opts *TransactOpts, bytecode []byte, backend ContractBackend, constructorInput []byte) (common.Address, *types.Transaction, error) { + c := NewBoundContract(common.Address{}, abi.ABI{}, backend, backend, backend) + + tx, err := c.RawCreationTransact(opts, append(bytecode, constructorInput...)) + if err != nil { + return common.Address{}, nil, err + } + return crypto.CreateAddress(opts.From, tx.Nonce()), tx, nil +} + +// DefaultDeployer returns a DeployFn that signs and submits creation transactions +// using the given signer. +// +// The DeployFn returned by DefaultDeployer should be used by LinkAndDeploy in +// almost all cases, unless a custom DeployFn implementation is needed. +func DefaultDeployer(opts *TransactOpts, backend ContractBackend) DeployFn { + return func(input []byte, deployer []byte) (common.Address, *types.Transaction, error) { + addr, tx, err := DeployContract(opts, deployer, backend, input) + if err != nil { + return common.Address{}, nil, err + } + return addr, tx, nil + } +} diff --git a/accounts/abi/bind/v2/lib_test.go b/accounts/abi/bind/v2/lib_test.go new file mode 100644 index 00000000000..fc895edad5c --- /dev/null +++ b/accounts/abi/bind/v2/lib_test.go @@ -0,0 +1,361 @@ +// Copyright 2024 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 bind_test + +import ( + "context" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2/internal/contracts/events" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2/internal/contracts/nested_libraries" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2/internal/contracts/solc_errors" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/ethclient/simulated" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" +) + +var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") +var testAddr = crypto.PubkeyToAddress(testKey.PublicKey) + +func testSetup() (*backends.SimulatedBackend, error) { + backend := simulated.NewBackend( + types.GenesisAlloc{ + testAddr: {Balance: big.NewInt(10000000000000000)}, + }, + func(nodeConf *node.Config, ethConf *ethconfig.Config) { + ethConf.Genesis.Difficulty = big.NewInt(0) + }, + ) + + // we should just be able to use the backend directly, instead of using + // this deprecated interface. However, the simulated backend no longer + // implements backends.SimulatedBackend... + bindBackend := backends.SimulatedBackend{ + Backend: backend, + Client: backend.Client(), + } + return &bindBackend, nil +} + +func makeTestDeployer(backend simulated.Client) func(input, deployer []byte) (common.Address, *types.Transaction, error) { + chainId, _ := backend.ChainID(context.Background()) + return bind.DefaultDeployer(bind.NewKeyedTransactor(testKey, chainId), backend) +} + +// test that deploying a contract with library dependencies works, +// verifying by calling method on the deployed contract. +func TestDeploymentLibraries(t *testing.T) { + bindBackend, err := testSetup() + if err != nil { + t.Fatalf("err setting up test: %v", err) + } + defer bindBackend.Backend.Close() + + c := nested_libraries.NewC1() + constructorInput := c.PackConstructor(big.NewInt(42), big.NewInt(1)) + deploymentParams := &bind.DeploymentParams{ + Contracts: []*bind.MetaData{&nested_libraries.C1MetaData}, + Inputs: map[string][]byte{nested_libraries.C1MetaData.ID: constructorInput}, + } + res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend.Client)) + if err != nil { + t.Fatalf("err: %+v\n", err) + } + bindBackend.Commit() + + if len(res.Addresses) != 5 { + t.Fatalf("deployment should have generated 5 addresses. got %d", len(res.Addresses)) + } + for _, tx := range res.Txs { + _, err = bind.WaitDeployed(context.Background(), bindBackend, tx.Hash()) + if err != nil { + t.Fatalf("error deploying library: %+v", err) + } + } + + doInput := c.PackDo(big.NewInt(1)) + contractAddr := res.Addresses[nested_libraries.C1MetaData.ID] + callOpts := &bind.CallOpts{From: common.Address{}, Context: context.Background()} + instance := c.Instance(bindBackend, contractAddr) + internalCallCount, err := bind.Call(instance, callOpts, doInput, c.UnpackDo) + if err != nil { + t.Fatalf("err unpacking result: %v", err) + } + if internalCallCount.Uint64() != 6 { + t.Fatalf("expected internal call count of 6. got %d.", internalCallCount.Uint64()) + } +} + +// Same as TestDeployment. However, stagger the deployments with overrides: +// first deploy the library deps and then the contract. +func TestDeploymentWithOverrides(t *testing.T) { + bindBackend, err := testSetup() + if err != nil { + t.Fatalf("err setting up test: %v", err) + } + defer bindBackend.Backend.Close() + + // deploy all the library dependencies of our target contract, but not the target contract itself. + deploymentParams := &bind.DeploymentParams{ + Contracts: nested_libraries.C1MetaData.Deps, + } + res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend)) + if err != nil { + t.Fatalf("err: %+v\n", err) + } + bindBackend.Commit() + + if len(res.Addresses) != 4 { + t.Fatalf("deployment should have generated 4 addresses. got %d", len(res.Addresses)) + } + for _, tx := range res.Txs { + _, err = bind.WaitDeployed(context.Background(), bindBackend, tx.Hash()) + if err != nil { + t.Fatalf("error deploying library: %+v", err) + } + } + + c := nested_libraries.NewC1() + constructorInput := c.PackConstructor(big.NewInt(42), big.NewInt(1)) + overrides := res.Addresses + + // deploy the contract + deploymentParams = &bind.DeploymentParams{ + Contracts: []*bind.MetaData{&nested_libraries.C1MetaData}, + Inputs: map[string][]byte{nested_libraries.C1MetaData.ID: constructorInput}, + Overrides: overrides, + } + res, err = bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend)) + if err != nil { + t.Fatalf("err: %+v\n", err) + } + bindBackend.Commit() + + if len(res.Addresses) != 1 { + t.Fatalf("deployment should have generated 1 address. got %d", len(res.Addresses)) + } + for _, tx := range res.Txs { + _, err = bind.WaitDeployed(context.Background(), bindBackend, tx.Hash()) + if err != nil { + t.Fatalf("error deploying library: %+v", err) + } + } + + // call the deployed contract and make sure it returns the correct result + doInput := c.PackDo(big.NewInt(1)) + instance := c.Instance(bindBackend, res.Addresses[nested_libraries.C1MetaData.ID]) + callOpts := new(bind.CallOpts) + internalCallCount, err := bind.Call(instance, callOpts, doInput, c.UnpackDo) + if err != nil { + t.Fatalf("error calling contract: %v", err) + } + if internalCallCount.Uint64() != 6 { + t.Fatalf("expected internal call count of 6. got %d.", internalCallCount.Uint64()) + } +} + +// returns transaction auth to send a basic transaction from testAddr +func defaultTxAuth() *bind.TransactOpts { + signer := types.LatestSigner(params.AllDevChainProtocolChanges) + opts := &bind.TransactOpts{ + From: testAddr, + Nonce: nil, + Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey) + if err != nil { + return nil, err + } + signedTx, err := tx.WithSignature(signer, signature) + if err != nil { + return nil, err + } + return signedTx, nil + }, + Context: context.Background(), + } + return opts +} + +func TestEvents(t *testing.T) { + // test watch/filter logs method on a contract that emits various kinds of events (struct-containing, etc.) + backend, err := testSetup() + if err != nil { + t.Fatalf("error setting up testing env: %v", err) + } + deploymentParams := &bind.DeploymentParams{ + Contracts: []*bind.MetaData{&events.CMetaData}, + } + res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(backend)) + if err != nil { + t.Fatalf("error deploying contract for testing: %v", err) + } + + backend.Commit() + if _, err := bind.WaitDeployed(context.Background(), backend, res.Txs[events.CMetaData.ID].Hash()); err != nil { + t.Fatalf("WaitDeployed failed %v", err) + } + + c := events.NewC() + instance := c.Instance(backend, res.Addresses[events.CMetaData.ID]) + + newCBasic1Ch := make(chan *events.CBasic1) + newCBasic2Ch := make(chan *events.CBasic2) + watchOpts := &bind.WatchOpts{} + sub1, err := bind.WatchEvents(instance, watchOpts, c.UnpackBasic1Event, newCBasic1Ch) + if err != nil { + t.Fatalf("WatchEvents returned error: %v", err) + } + sub2, err := bind.WatchEvents(instance, watchOpts, c.UnpackBasic2Event, newCBasic2Ch) + if err != nil { + t.Fatalf("WatchEvents returned error: %v", err) + } + defer sub1.Unsubscribe() + defer sub2.Unsubscribe() + + packedInput := c.PackEmitMulti() + tx, err := bind.Transact(instance, defaultTxAuth(), packedInput) + if err != nil { + t.Fatalf("failed to send transaction: %v", err) + } + backend.Commit() + if _, err := bind.WaitMined(context.Background(), backend, tx.Hash()); err != nil { + t.Fatalf("error waiting for tx to be mined: %v", err) + } + + timeout := time.NewTimer(2 * time.Second) + e1Count := 0 + e2Count := 0 + for { + select { + case <-newCBasic1Ch: + e1Count++ + case <-newCBasic2Ch: + e2Count++ + case <-timeout.C: + goto done + } + if e1Count == 2 && e2Count == 1 { + break + } + } +done: + if e1Count != 2 { + t.Fatalf("expected event type 1 count to be 2. got %d", e1Count) + } + if e2Count != 1 { + t.Fatalf("expected event type 2 count to be 1. got %d", e2Count) + } + + // now, test that we can filter those same logs after they were included in the chain + + filterOpts := &bind.FilterOpts{ + Start: 0, + Context: context.Background(), + } + it, err := bind.FilterEvents(instance, filterOpts, c.UnpackBasic1Event) + if err != nil { + t.Fatalf("error filtering logs %v\n", err) + } + it2, err := bind.FilterEvents(instance, filterOpts, c.UnpackBasic2Event) + if err != nil { + t.Fatalf("error filtering logs %v\n", err) + } + + e1Count = 0 + e2Count = 0 + for it.Next() { + if err := it.Error(); err != nil { + t.Fatalf("got error while iterating events for e1: %v", err) + } + e1Count++ + } + for it2.Next() { + if err := it2.Error(); err != nil { + t.Fatalf("got error while iterating events for e2: %v", err) + } + e2Count++ + } + if e1Count != 2 { + t.Fatalf("expected e1Count of 2 from filter call. got %d", e1Count) + } + if e2Count != 1 { + t.Fatalf("expected e2Count of 1 from filter call. got %d", e1Count) + } +} + +func TestErrors(t *testing.T) { + // test watch/filter logs method on a contract that emits various kinds of events (struct-containing, etc.) + backend, err := testSetup() + if err != nil { + t.Fatalf("error setting up testing env: %v", err) + } + deploymentParams := &bind.DeploymentParams{ + Contracts: []*bind.MetaData{&solc_errors.CMetaData}, + } + res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(backend)) + if err != nil { + t.Fatalf("error deploying contract for testing: %v", err) + } + + backend.Commit() + if _, err := bind.WaitDeployed(context.Background(), backend, res.Txs[solc_errors.CMetaData.ID].Hash()); err != nil { + t.Fatalf("WaitDeployed failed %v", err) + } + + c := solc_errors.NewC() + instance := c.Instance(backend, res.Addresses[solc_errors.CMetaData.ID]) + packedInput := c.PackFoo() + opts := &bind.CallOpts{From: res.Addresses[solc_errors.CMetaData.ID]} + _, err = bind.Call[struct{}](instance, opts, packedInput, nil) + if err == nil { + t.Fatalf("expected call to fail") + } + raw, hasRevertErrorData := ethclient.RevertErrorData(err) + if !hasRevertErrorData { + t.Fatalf("expected call error to contain revert error data.") + } + rawUnpackedErr, err := c.UnpackError(raw) + if err != nil { + t.Fatalf("expected to unpack error") + } + + unpackedErr, ok := rawUnpackedErr.(*solc_errors.CBadThing) + if !ok { + t.Fatalf("unexpected type for error") + } + if unpackedErr.Arg1.Cmp(big.NewInt(0)) != 0 { + t.Fatalf("bad unpacked error result: expected Arg1 field to be 0. got %s", unpackedErr.Arg1.String()) + } + if unpackedErr.Arg2.Cmp(big.NewInt(1)) != 0 { + t.Fatalf("bad unpacked error result: expected Arg2 field to be 1. got %s", unpackedErr.Arg2.String()) + } + if unpackedErr.Arg3.Cmp(big.NewInt(2)) != 0 { + t.Fatalf("bad unpacked error result: expected Arg3 to be 2. got %s", unpackedErr.Arg3.String()) + } + if unpackedErr.Arg4 != false { + t.Fatalf("bad unpacked error result: expected Arg4 to be false. got true") + } +} diff --git a/accounts/abi/bind/util.go b/accounts/abi/bind/v2/util.go similarity index 60% rename from accounts/abi/bind/util.go rename to accounts/abi/bind/v2/util.go index c83116e9a16..438848a753a 100644 --- a/accounts/abi/bind/util.go +++ b/accounts/abi/bind/v2/util.go @@ -29,19 +29,13 @@ import ( // WaitMined waits for tx to be mined on the blockchain. // It stops waiting when the context is canceled. -func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) { - return WaitMinedHash(ctx, b, tx.Hash()) -} - -// WaitMinedHash waits for a transaction with the provided hash to be mined on the blockchain. -// It stops waiting when the context is canceled. -func WaitMinedHash(ctx context.Context, b DeployBackend, hash common.Hash) (*types.Receipt, error) { +func WaitMined(ctx context.Context, b DeployBackend, txHash common.Hash) (*types.Receipt, error) { queryTicker := time.NewTicker(time.Second) defer queryTicker.Stop() - logger := log.New("hash", hash) + logger := log.New("hash", txHash) for { - receipt, err := b.TransactionReceipt(ctx, hash) + receipt, err := b.TransactionReceipt(ctx, txHash) if err == nil { return receipt, nil } @@ -61,24 +55,16 @@ func WaitMinedHash(ctx context.Context, b DeployBackend, hash common.Hash) (*typ } } -// WaitDeployed waits for a contract deployment transaction and returns the on-chain -// contract address when it is mined. It stops waiting when ctx is canceled. -func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (common.Address, error) { - if tx.To() != nil { - return common.Address{}, errors.New("tx is not contract creation") - } - return WaitDeployedHash(ctx, b, tx.Hash()) -} - -// WaitDeployedHash waits for a contract deployment transaction with the provided hash and returns the on-chain -// contract address when it is mined. It stops waiting when ctx is canceled. -func WaitDeployedHash(ctx context.Context, b DeployBackend, hash common.Hash) (common.Address, error) { - receipt, err := WaitMinedHash(ctx, b, hash) +// WaitDeployed waits for a contract deployment transaction with the provided hash and +// returns the on-chain contract address when it is mined. It stops waiting when ctx is +// canceled. +func WaitDeployed(ctx context.Context, b DeployBackend, hash common.Hash) (common.Address, error) { + receipt, err := WaitMined(ctx, b, hash) if err != nil { return common.Address{}, err } if receipt.ContractAddress == (common.Address{}) { - return common.Address{}, errors.New("zero address") + return common.Address{}, ErrNoAddressInReceipt } // Check that code has indeed been deployed at the address. // This matters on pre-Homestead chains: OOG in the constructor diff --git a/accounts/abi/bind/util_test.go b/accounts/abi/bind/v2/util_test.go similarity index 88% rename from accounts/abi/bind/util_test.go rename to accounts/abi/bind/v2/util_test.go index 04d1bb63bcc..b1b647a7b9c 100644 --- a/accounts/abi/bind/util_test.go +++ b/accounts/abi/bind/v2/util_test.go @@ -23,7 +23,7 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -31,8 +31,6 @@ import ( "github.com/ethereum/go-ethereum/params" ) -var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - var waitDeployedTests = map[string]struct { code string gas uint64 @@ -77,7 +75,7 @@ func TestWaitDeployed(t *testing.T) { ctx = context.Background() ) go func() { - address, err = bind.WaitDeployed(ctx, backend.Client(), tx) + address, err = bind.WaitDeployed(ctx, backend.Client(), tx.Hash()) close(mined) }() @@ -122,9 +120,8 @@ func TestWaitDeployedCornerCases(t *testing.T) { t.Errorf("failed to send transaction: %q", err) } backend.Commit() - notContractCreation := errors.New("tx is not contract creation") - if _, err := bind.WaitDeployed(ctx, backend.Client(), tx); err.Error() != notContractCreation.Error() { - t.Errorf("error mismatch: want %q, got %q, ", notContractCreation, err) + if _, err := bind.WaitDeployed(ctx, backend.Client(), tx.Hash()); err != bind.ErrNoAddressInReceipt { + t.Errorf("error mismatch: want %q, got %q, ", bind.ErrNoAddressInReceipt, err) } // Create a transaction that is not mined. @@ -133,7 +130,7 @@ func TestWaitDeployedCornerCases(t *testing.T) { go func() { contextCanceled := errors.New("context canceled") - if _, err := bind.WaitDeployed(ctx, backend.Client(), tx); err.Error() != contextCanceled.Error() { + if _, err := bind.WaitDeployed(ctx, backend.Client(), tx.Hash()); err.Error() != contextCanceled.Error() { t.Errorf("error mismatch: want %q, got %q, ", contextCanceled, err) } }() diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 10343190c34..c82358be493 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -24,7 +24,7 @@ import ( "regexp" "strings" - "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/abigen" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/crypto" @@ -63,14 +63,13 @@ var ( Name: "out", Usage: "Output file for the generated binding (default = stdout)", } - langFlag = &cli.StringFlag{ - Name: "lang", - Usage: "Destination language for the bindings (go)", - Value: "go", - } aliasFlag = &cli.StringFlag{ Name: "alias", - Usage: "Comma separated aliases for function and event renaming, e.g. original1=alias1, original2=alias2", + Usage: "Comma separated aliases for function and event renaming. If --v2 is set, errors are aliased as well. e.g. original1=alias1, original2=alias2", + } + v2Flag = &cli.BoolFlag{ + Name: "v2", + Usage: "Generates v2 bindings", } ) @@ -86,13 +85,13 @@ func init() { excFlag, pkgFlag, outFlag, - langFlag, aliasFlag, + v2Flag, } - app.Action = abigen + app.Action = generate } -func abigen(c *cli.Context) error { +func generate(c *cli.Context) error { flags.CheckExclusive(c, abiFlag, jsonFlag) // Only one source can be selected. if c.String(pkgFlag.Name) == "" { @@ -101,13 +100,6 @@ func abigen(c *cli.Context) error { if c.String(abiFlag.Name) == "" && c.String(jsonFlag.Name) == "" { utils.Fatalf("Either contract ABI source (--abi) or combined-json (--combined-json) are required") } - var lang bind.Lang - switch c.String(langFlag.Name) { - case "go": - lang = bind.LangGo - default: - utils.Fatalf("Unsupported destination language \"%s\" (--lang)", c.String(langFlag.Name)) - } // If the entire solidity code was specified, build and bind based on that var ( abis []string @@ -219,7 +211,15 @@ func abigen(c *cli.Context) error { } } // Generate the contract binding - code, err := bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang, libs, aliases) + var ( + code string + err error + ) + if c.IsSet(v2Flag.Name) { + code, err = abigen.BindV2(types, abis, bins, c.String(pkgFlag.Name), libs, aliases) + } else { + code, err = abigen.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), libs, aliases) + } if err != nil { utils.Fatalf("Failed to generate ABI binding: %v", err) } diff --git a/go.mod b/go.mod index 053c7b7a7d9..5ec51dd1ea7 100644 --- a/go.mod +++ b/go.mod @@ -65,6 +65,7 @@ require ( go.uber.org/automaxprocs v1.5.2 go.uber.org/goleak v1.3.0 golang.org/x/crypto v0.35.0 + golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df golang.org/x/sync v0.11.0 golang.org/x/sys v0.30.0 golang.org/x/text v0.22.0 @@ -141,7 +142,6 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect golang.org/x/mod v0.22.0 // indirect golang.org/x/net v0.36.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect From 0f06e3511534477cfb1f8aad23adf4eaa60debcc Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 17 Mar 2025 16:01:37 +0100 Subject: [PATCH 009/658] core/rawdb: allow for truncation in the freezer (#31362) Here we add the notion of prunable tables for the `TruncateTail` operation in the freezer. TruncateTail for the chain freezer now only truncates the body and receipts tables, leaving headers and hashes as-is. This change also requires changing the validation/repair at startup to allow for tables with different tail. For the header and hash tables, we now require them to start at number zero. --------- Co-authored-by: Felix Lange Co-authored-by: Gary Rong --- core/rawdb/ancient_scheme.go | 40 +++++++------ core/rawdb/ancient_utils.go | 12 ++-- core/rawdb/chain_freezer.go | 4 +- core/rawdb/freezer.go | 88 ++++++++++++++++++----------- core/rawdb/freezer_batch.go | 2 +- core/rawdb/freezer_memory.go | 28 +++++---- core/rawdb/freezer_memory_test.go | 14 +++-- core/rawdb/freezer_resettable.go | 2 +- core/rawdb/freezer_table.go | 50 ++++++++-------- core/rawdb/freezer_table_test.go | 94 +++++++++++++++---------------- core/rawdb/freezer_test.go | 28 +++++---- ethdb/database.go | 2 + 12 files changed, 208 insertions(+), 156 deletions(-) diff --git a/core/rawdb/ancient_scheme.go b/core/rawdb/ancient_scheme.go index 67bfa37ecc3..1ffebed3e7c 100644 --- a/core/rawdb/ancient_scheme.go +++ b/core/rawdb/ancient_scheme.go @@ -37,13 +37,21 @@ const ( ChainFreezerReceiptTable = "receipts" ) -// chainFreezerNoSnappy configures whether compression is disabled for the ancient-tables. -// Hashes and difficulties don't compress well. -var chainFreezerNoSnappy = map[string]bool{ - ChainFreezerHeaderTable: false, - ChainFreezerHashTable: true, - ChainFreezerBodiesTable: false, - ChainFreezerReceiptTable: false, +// chainFreezerTableConfigs configures the settings for tables in the chain freezer. +// Compression is disabled for hashes as they don't compress well. Additionally, +// tail truncation is disabled for the header and hash tables, as these are intended +// to be retained long-term. +var chainFreezerTableConfigs = map[string]freezerTableConfig{ + ChainFreezerHeaderTable: {noSnappy: false, prunable: false}, + ChainFreezerHashTable: {noSnappy: true, prunable: false}, + ChainFreezerBodiesTable: {noSnappy: false, prunable: true}, + ChainFreezerReceiptTable: {noSnappy: false, prunable: true}, +} + +// freezerTableConfig contains the settings for a freezer table. +type freezerTableConfig struct { + noSnappy bool // disables item compression + prunable bool // true for tables that can be pruned by TruncateTail } const ( @@ -58,13 +66,13 @@ const ( stateHistoryStorageData = "storage.data" ) -// stateFreezerNoSnappy configures whether compression is disabled for the state freezer. -var stateFreezerNoSnappy = map[string]bool{ - stateHistoryMeta: true, - stateHistoryAccountIndex: false, - stateHistoryStorageIndex: false, - stateHistoryAccountData: false, - stateHistoryStorageData: false, +// stateFreezerTableConfigs configures the settings for tables in the state freezer. +var stateFreezerTableConfigs = map[string]freezerTableConfig{ + stateHistoryMeta: {noSnappy: true, prunable: true}, + stateHistoryAccountIndex: {noSnappy: false, prunable: true}, + stateHistoryStorageIndex: {noSnappy: false, prunable: true}, + stateHistoryAccountData: {noSnappy: false, prunable: true}, + stateHistoryStorageData: {noSnappy: false, prunable: true}, } // The list of identifiers of ancient stores. @@ -85,7 +93,7 @@ var freezers = []string{ChainFreezerName, MerkleStateFreezerName, VerkleStateFre // state freezer. func NewStateFreezer(ancientDir string, verkle bool, readOnly bool) (ethdb.ResettableAncientStore, error) { if ancientDir == "" { - return NewMemoryFreezer(readOnly, stateFreezerNoSnappy), nil + return NewMemoryFreezer(readOnly, stateFreezerTableConfigs), nil } var name string if verkle { @@ -93,5 +101,5 @@ func NewStateFreezer(ancientDir string, verkle bool, readOnly bool) (ethdb.Reset } else { name = filepath.Join(ancientDir, MerkleStateFreezerName) } - return newResettableFreezer(name, "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy) + return newResettableFreezer(name, "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerTableConfigs) } diff --git a/core/rawdb/ancient_utils.go b/core/rawdb/ancient_utils.go index 6804d7a91a2..f4909d86e73 100644 --- a/core/rawdb/ancient_utils.go +++ b/core/rawdb/ancient_utils.go @@ -51,7 +51,7 @@ func (info *freezerInfo) size() common.StorageSize { return total } -func inspect(name string, order map[string]bool, reader ethdb.AncientReader) (freezerInfo, error) { +func inspect(name string, order map[string]freezerTableConfig, reader ethdb.AncientReader) (freezerInfo, error) { info := freezerInfo{name: name} for t := range order { size, err := reader.AncientSize(t) @@ -82,7 +82,7 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) { for _, freezer := range freezers { switch freezer { case ChainFreezerName: - info, err := inspect(ChainFreezerName, chainFreezerNoSnappy, db) + info, err := inspect(ChainFreezerName, chainFreezerTableConfigs, db) if err != nil { return nil, err } @@ -99,7 +99,7 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) { } defer f.Close() - info, err := inspect(freezer, stateFreezerNoSnappy, f) + info, err := inspect(freezer, stateFreezerTableConfigs, f) if err != nil { return nil, err } @@ -119,13 +119,13 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) { func InspectFreezerTable(ancient string, freezerName string, tableName string, start, end int64) error { var ( path string - tables map[string]bool + tables map[string]freezerTableConfig ) switch freezerName { case ChainFreezerName: - path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy + path, tables = resolveChainFreezerDir(ancient), chainFreezerTableConfigs case MerkleStateFreezerName, VerkleStateFreezerName: - path, tables = filepath.Join(ancient, freezerName), stateFreezerNoSnappy + path, tables = filepath.Join(ancient, freezerName), stateFreezerTableConfigs default: return fmt.Errorf("unknown freezer, supported ones: %v", freezers) } diff --git a/core/rawdb/chain_freezer.go b/core/rawdb/chain_freezer.go index 0627aca34cb..f3c671f45a8 100644 --- a/core/rawdb/chain_freezer.go +++ b/core/rawdb/chain_freezer.go @@ -62,9 +62,9 @@ func newChainFreezer(datadir string, namespace string, readonly bool) (*chainFre freezer ethdb.AncientStore ) if datadir == "" { - freezer = NewMemoryFreezer(readonly, chainFreezerNoSnappy) + freezer = NewMemoryFreezer(readonly, chainFreezerTableConfigs) } else { - freezer, err = NewFreezer(datadir, namespace, readonly, freezerTableSize, chainFreezerNoSnappy) + freezer, err = NewFreezer(datadir, namespace, readonly, freezerTableSize, chainFreezerTableConfigs) } if err != nil { return nil, err diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go index c5a72eff7e6..105d3af9344 100644 --- a/core/rawdb/freezer.go +++ b/core/rawdb/freezer.go @@ -78,7 +78,7 @@ type Freezer struct { // // The 'tables' argument defines the data tables. If the value of a map // entry is true, snappy compression is disabled for the table. -func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]bool) (*Freezer, error) { +func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]freezerTableConfig) (*Freezer, error) { // Create the initial freezer object var ( readMeter = metrics.NewRegisteredMeter(namespace+"ancient/read", nil) @@ -121,8 +121,8 @@ func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize ui } // Create the tables. - for name, disableSnappy := range tables { - table, err := newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, readonly) + for name, config := range tables { + table, err := newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, config, readonly) if err != nil { for _, table := range freezer.tables { table.Close() @@ -301,7 +301,8 @@ func (f *Freezer) TruncateHead(items uint64) (uint64, error) { return oitems, nil } -// TruncateTail discards any recent data below the provided threshold number. +// TruncateTail discards all data below the specified threshold. Note that only +// 'prunable' tables will be truncated. func (f *Freezer) TruncateTail(tail uint64) (uint64, error) { if f.readonly { return 0, errReadOnly @@ -314,8 +315,10 @@ func (f *Freezer) TruncateTail(tail uint64) (uint64, error) { return old, nil } for _, table := range f.tables { - if err := table.truncateTail(tail); err != nil { - return 0, err + if table.config.prunable { + if err := table.truncateTail(tail); err != nil { + return 0, err + } } } f.tail.Store(tail) @@ -343,56 +346,77 @@ func (f *Freezer) validate() error { return nil } var ( - head uint64 - tail uint64 - name string + head uint64 + prunedTail *uint64 ) - // Hack to get boundary of any table - for kind, table := range f.tables { + // get any head value + for _, table := range f.tables { head = table.items.Load() - tail = table.itemHidden.Load() - name = kind break } - // Now check every table against those boundaries. for kind, table := range f.tables { + // all tables have to have the same head if head != table.items.Load() { - return fmt.Errorf("freezer tables %s and %s have differing head: %d != %d", kind, name, table.items.Load(), head) + return fmt.Errorf("freezer table %s has a differing head: %d != %d", kind, table.items.Load(), head) } - if tail != table.itemHidden.Load() { - return fmt.Errorf("freezer tables %s and %s have differing tail: %d != %d", kind, name, table.itemHidden.Load(), tail) + if !table.config.prunable { + // non-prunable tables have to start at 0 + if table.itemHidden.Load() != 0 { + return fmt.Errorf("non-prunable freezer table '%s' has a non-zero tail: %d", kind, table.itemHidden.Load()) + } + } else { + // prunable tables have to have the same length + if prunedTail == nil { + tmp := table.itemHidden.Load() + prunedTail = &tmp + } + if *prunedTail != table.itemHidden.Load() { + return fmt.Errorf("freezer table %s has differing tail: %d != %d", kind, table.itemHidden.Load(), *prunedTail) + } } } + + if prunedTail == nil { + tmp := uint64(0) + prunedTail = &tmp + } + f.frozen.Store(head) - f.tail.Store(tail) + f.tail.Store(*prunedTail) return nil } // repair truncates all data tables to the same length. func (f *Freezer) repair() error { var ( - head = uint64(math.MaxUint64) - tail = uint64(0) + head = uint64(math.MaxUint64) + prunedTail = uint64(0) ) + // get the minimal head and the maximum tail for _, table := range f.tables { - items := table.items.Load() - if head > items { - head = items - } - hidden := table.itemHidden.Load() - if hidden > tail { - tail = hidden - } + head = min(head, table.items.Load()) + prunedTail = max(prunedTail, table.itemHidden.Load()) } - for _, table := range f.tables { + // apply the pruning + for kind, table := range f.tables { + // all tables need to have the same head if err := table.truncateHead(head); err != nil { return err } - if err := table.truncateTail(tail); err != nil { - return err + if !table.config.prunable { + // non-prunable tables have to start at 0 + if table.itemHidden.Load() != 0 { + panic(fmt.Sprintf("non-prunable freezer table %s has non-zero tail: %v", kind, table.itemHidden.Load())) + } + } else { + // prunable tables have to have the same length + if err := table.truncateTail(prunedTail); err != nil { + return err + } } } + f.frozen.Store(head) - f.tail.Store(tail) + f.tail.Store(prunedTail) return nil } diff --git a/core/rawdb/freezer_batch.go b/core/rawdb/freezer_batch.go index 801d30f73f7..99b63df4dc1 100644 --- a/core/rawdb/freezer_batch.go +++ b/core/rawdb/freezer_batch.go @@ -96,7 +96,7 @@ type freezerTableBatch struct { // newBatch creates a new batch for the freezer table. func (t *freezerTable) newBatch() *freezerTableBatch { batch := &freezerTableBatch{t: t} - if !t.noCompression { + if !t.config.noSnappy { batch.sb = new(snappyBuffer) } batch.reset() diff --git a/core/rawdb/freezer_memory.go b/core/rawdb/freezer_memory.go index 2d3dbb07dde..4274546de51 100644 --- a/core/rawdb/freezer_memory.go +++ b/core/rawdb/freezer_memory.go @@ -30,17 +30,19 @@ import ( // memoryTable is used to store a list of sequential items in memory. type memoryTable struct { - name string // Table name items uint64 // Number of stored items in the table, including the deleted ones offset uint64 // Number of deleted items from the table data [][]byte // List of rlp-encoded items, sort in order size uint64 // Total memory size occupied by the table lock sync.RWMutex + + name string + config freezerTableConfig } // newMemoryTable initializes the memory table. -func newMemoryTable(name string) *memoryTable { - return &memoryTable{name: name} +func newMemoryTable(name string, config freezerTableConfig) *memoryTable { + return &memoryTable{name: name, config: config} } // has returns an indicator whether the specified data exists. @@ -218,10 +220,10 @@ type MemoryFreezer struct { } // NewMemoryFreezer initializes an in-memory freezer instance. -func NewMemoryFreezer(readonly bool, tableName map[string]bool) *MemoryFreezer { +func NewMemoryFreezer(readonly bool, tableName map[string]freezerTableConfig) *MemoryFreezer { tables := make(map[string]*memoryTable) - for name := range tableName { - tables[name] = newMemoryTable(name) + for name, cfg := range tableName { + tables[name] = newMemoryTable(name, cfg) } return &MemoryFreezer{ writeBatch: newMemoryBatch(), @@ -368,7 +370,9 @@ func (f *MemoryFreezer) TruncateHead(items uint64) (uint64, error) { return old, nil } -// TruncateTail discards any recent data below the provided threshold number. +// TruncateTail discards all data below the provided threshold number. +// Note this will only truncate 'prunable' tables. Block headers and canonical +// hashes cannot be truncated at this time. func (f *MemoryFreezer) TruncateTail(tail uint64) (uint64, error) { f.lock.Lock() defer f.lock.Unlock() @@ -381,8 +385,10 @@ func (f *MemoryFreezer) TruncateTail(tail uint64) (uint64, error) { return old, nil } for _, table := range f.tables { - if err := table.truncateTail(tail); err != nil { - return 0, err + if table.config.prunable { + if err := table.truncateTail(tail); err != nil { + return 0, err + } } } f.tail = tail @@ -412,8 +418,8 @@ func (f *MemoryFreezer) Reset() error { defer f.lock.Unlock() tables := make(map[string]*memoryTable) - for name := range f.tables { - tables[name] = newMemoryTable(name) + for name, table := range f.tables { + tables[name] = newMemoryTable(name, table.config) } f.tables = tables f.items, f.tail = 0, 0 diff --git a/core/rawdb/freezer_memory_test.go b/core/rawdb/freezer_memory_test.go index e71de0f6292..4bd31d80275 100644 --- a/core/rawdb/freezer_memory_test.go +++ b/core/rawdb/freezer_memory_test.go @@ -25,16 +25,22 @@ import ( func TestMemoryFreezer(t *testing.T) { ancienttest.TestAncientSuite(t, func(kinds []string) ethdb.AncientStore { - tables := make(map[string]bool) + tables := make(map[string]freezerTableConfig) for _, kind := range kinds { - tables[kind] = true + tables[kind] = freezerTableConfig{ + noSnappy: true, + prunable: true, + } } return NewMemoryFreezer(false, tables) }) ancienttest.TestResettableAncientSuite(t, func(kinds []string) ethdb.ResettableAncientStore { - tables := make(map[string]bool) + tables := make(map[string]freezerTableConfig) for _, kind := range kinds { - tables[kind] = true + tables[kind] = freezerTableConfig{ + noSnappy: true, + prunable: true, + } } return NewMemoryFreezer(false, tables) }) diff --git a/core/rawdb/freezer_resettable.go b/core/rawdb/freezer_resettable.go index 7c77a06efcd..2e64e6074c5 100644 --- a/core/rawdb/freezer_resettable.go +++ b/core/rawdb/freezer_resettable.go @@ -49,7 +49,7 @@ type resettableFreezer struct { // // The reset function will delete directory atomically and re-create the // freezer from scratch. -func newResettableFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]bool) (*resettableFreezer, error) { +func newResettableFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]freezerTableConfig) (*resettableFreezer, error) { if err := cleanup(datadir); err != nil { return nil, err } diff --git a/core/rawdb/freezer_table.go b/core/rawdb/freezer_table.go index e553df70293..aec24b207e0 100644 --- a/core/rawdb/freezer_table.go +++ b/core/rawdb/freezer_table.go @@ -100,11 +100,11 @@ type freezerTable struct { // should never be lower than itemOffset. itemHidden atomic.Uint64 - noCompression bool // if true, disables snappy compression. Note: does not work retroactively - readonly bool - maxFileSize uint32 // Max file size for data-files - name string - path string + config freezerTableConfig // if true, disables snappy compression. Note: does not work retroactively + readonly bool + maxFileSize uint32 // Max file size for data-files + name string + path string head *os.File // File descriptor for the data head of the table index *os.File // File descriptor for the indexEntry file of the table @@ -125,20 +125,20 @@ type freezerTable struct { } // newFreezerTable opens the given path as a freezer table. -func newFreezerTable(path, name string, disableSnappy, readonly bool) (*freezerTable, error) { - return newTable(path, name, metrics.NewInactiveMeter(), metrics.NewInactiveMeter(), metrics.NewGauge(), freezerTableSize, disableSnappy, readonly) +func newFreezerTable(path, name string, config freezerTableConfig, readonly bool) (*freezerTable, error) { + return newTable(path, name, metrics.NewInactiveMeter(), metrics.NewInactiveMeter(), metrics.NewGauge(), freezerTableSize, config, readonly) } // newTable opens a freezer table, creating the data and index files if they are // non-existent. Both files are truncated to the shortest common length to ensure // they don't go out of sync. -func newTable(path string, name string, readMeter, writeMeter *metrics.Meter, sizeGauge *metrics.Gauge, maxFilesize uint32, noCompression, readonly bool) (*freezerTable, error) { +func newTable(path string, name string, readMeter, writeMeter *metrics.Meter, sizeGauge *metrics.Gauge, maxFilesize uint32, config freezerTableConfig, readonly bool) (*freezerTable, error) { // Ensure the containing directory exists and open the indexEntry file if err := os.MkdirAll(path, 0755); err != nil { return nil, err } var idxName string - if noCompression { + if config.noSnappy { idxName = fmt.Sprintf("%s.ridx", name) // raw index file } else { idxName = fmt.Sprintf("%s.cidx", name) // compressed index file @@ -176,19 +176,19 @@ func newTable(path string, name string, readMeter, writeMeter *metrics.Meter, si } // Create the table and repair any past inconsistency tab := &freezerTable{ - index: index, - metadata: metadata, - lastSync: time.Now(), - files: make(map[uint32]*os.File), - readMeter: readMeter, - writeMeter: writeMeter, - sizeGauge: sizeGauge, - name: name, - path: path, - logger: log.New("database", path, "table", name), - noCompression: noCompression, - readonly: readonly, - maxFileSize: maxFilesize, + index: index, + metadata: metadata, + lastSync: time.Now(), + files: make(map[uint32]*os.File), + readMeter: readMeter, + writeMeter: writeMeter, + sizeGauge: sizeGauge, + name: name, + path: path, + logger: log.New("database", path, "table", name), + config: config, + readonly: readonly, + maxFileSize: maxFilesize, } if err := tab.repair(); err != nil { tab.Close() @@ -871,7 +871,7 @@ func (t *freezerTable) openFile(num uint32, opener func(string) (*os.File, error var exist bool if f, exist = t.files[num]; !exist { var name string - if t.noCompression { + if t.config.noSnappy { name = fmt.Sprintf("%s.%04d.rdat", t.name, num) } else { name = fmt.Sprintf("%s.%04d.cdat", t.name, num) @@ -987,13 +987,13 @@ func (t *freezerTable) RetrieveItems(start, count, maxBytes uint64) ([][]byte, e item := diskData[offset : offset+diskSize] offset += diskSize decompressedSize := diskSize - if !t.noCompression { + if !t.config.noSnappy { decompressedSize, _ = snappy.DecodedLen(item) } if i > 0 && maxBytes != 0 && uint64(outputSize+decompressedSize) > maxBytes { break } - if !t.noCompression { + if !t.config.noSnappy { data, err := snappy.Decode(nil, item) if err != nil { return nil, err diff --git a/core/rawdb/freezer_table_test.go b/core/rawdb/freezer_table_test.go index 9a72af6ccc8..96edac7e4a4 100644 --- a/core/rawdb/freezer_table_test.go +++ b/core/rawdb/freezer_table_test.go @@ -39,7 +39,7 @@ func TestFreezerBasics(t *testing.T) { // set cutoff at 50 bytes f, err := newTable(os.TempDir(), fmt.Sprintf("unittest-%d", rand.Uint64()), - metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, false) + metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -84,7 +84,7 @@ func TestFreezerBasicsClosing(t *testing.T) { f *freezerTable err error ) - f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -98,7 +98,7 @@ func TestFreezerBasicsClosing(t *testing.T) { require.NoError(t, batch.commit()) f.Close() - f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -115,7 +115,7 @@ func TestFreezerBasicsClosing(t *testing.T) { t.Fatalf("test %d, got \n%x != \n%x", y, got, exp) } f.Close() - f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -130,7 +130,7 @@ func TestFreezerRepairDanglingHead(t *testing.T) { // Fill table { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -159,7 +159,7 @@ func TestFreezerRepairDanglingHead(t *testing.T) { // Now open it again { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -182,7 +182,7 @@ func TestFreezerRepairDanglingHeadLarge(t *testing.T) { // Fill a table and close it { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -208,7 +208,7 @@ func TestFreezerRepairDanglingHeadLarge(t *testing.T) { // Now open it again { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -231,7 +231,7 @@ func TestFreezerRepairDanglingHeadLarge(t *testing.T) { // And if we open it, we should now be able to read all of them (new values) { - f, _ := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, _ := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) for y := 1; y < 255; y++ { exp := getChunk(15, ^y) got, err := f.Retrieve(uint64(y)) @@ -253,7 +253,7 @@ func TestSnappyDetection(t *testing.T) { // Open with snappy { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -264,7 +264,7 @@ func TestSnappyDetection(t *testing.T) { // Open with snappy { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -277,7 +277,7 @@ func TestSnappyDetection(t *testing.T) { // Open without snappy { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, false, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: false}, false) if err != nil { t.Fatal(err) } @@ -308,7 +308,7 @@ func TestFreezerRepairDanglingIndex(t *testing.T) { // Fill a table and close it { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -344,7 +344,7 @@ func TestFreezerRepairDanglingIndex(t *testing.T) { // 45, 45, 15 // with 3+3+1 items { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -365,7 +365,7 @@ func TestFreezerTruncate(t *testing.T) { // Fill table { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -381,7 +381,7 @@ func TestFreezerTruncate(t *testing.T) { // Reopen, truncate { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -406,7 +406,7 @@ func TestFreezerRepairFirstFile(t *testing.T) { // Fill table { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -439,7 +439,7 @@ func TestFreezerRepairFirstFile(t *testing.T) { // Reopen { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -474,7 +474,7 @@ func TestFreezerReadAndTruncate(t *testing.T) { // Fill table { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -490,7 +490,7 @@ func TestFreezerReadAndTruncate(t *testing.T) { // Reopen and read all files { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -521,7 +521,7 @@ func TestFreezerOffset(t *testing.T) { fname := fmt.Sprintf("offset-%d", rand.Uint64()) // Fill table - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -545,7 +545,7 @@ func TestFreezerOffset(t *testing.T) { f.Close() // Now open again - f, err = newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false) + f, err = newTable(os.TempDir(), fname, rm, wm, sg, 40, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -597,7 +597,7 @@ func TestFreezerOffset(t *testing.T) { // Check that existing items have been moved to index 1M. { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -631,7 +631,7 @@ func TestTruncateTail(t *testing.T) { fname := fmt.Sprintf("truncate-tail-%d", rand.Uint64()) // Fill table - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -682,7 +682,7 @@ func TestTruncateTail(t *testing.T) { // Reopen the table, the deletion information should be persisted as well f.Close() - f, err = newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false) + f, err = newTable(os.TempDir(), fname, rm, wm, sg, 40, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -716,7 +716,7 @@ func TestTruncateTail(t *testing.T) { // Reopen the table, the above testing should still pass f.Close() - f, err = newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false) + f, err = newTable(os.TempDir(), fname, rm, wm, sg, 40, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -772,7 +772,7 @@ func TestTruncateHead(t *testing.T) { fname := fmt.Sprintf("truncate-head-blow-tail-%d", rand.Uint64()) // Fill table - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -883,7 +883,7 @@ func TestSequentialRead(t *testing.T) { rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge() fname := fmt.Sprintf("batchread-%d", rand.Uint64()) { // Fill table - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -893,7 +893,7 @@ func TestSequentialRead(t *testing.T) { f.Close() } { // Open it, iterate, verify iteration - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -914,7 +914,7 @@ func TestSequentialRead(t *testing.T) { } { // Open it, iterate, verify byte limit. The byte limit is less than item // size, so each lookup should only return one item - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -943,7 +943,7 @@ func TestSequentialReadByteLimit(t *testing.T) { rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge() fname := fmt.Sprintf("batchread-2-%d", rand.Uint64()) { // Fill table - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -965,7 +965,7 @@ func TestSequentialReadByteLimit(t *testing.T) { {100, 109, 10}, } { { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -993,7 +993,7 @@ func TestSequentialReadNoByteLimit(t *testing.T) { rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge() fname := fmt.Sprintf("batchread-3-%d", rand.Uint64()) { // Fill table - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -1011,7 +1011,7 @@ func TestSequentialReadNoByteLimit(t *testing.T) { {31, 30}, } { { - f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, true, false) + f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -1038,7 +1038,7 @@ func TestFreezerReadonly(t *testing.T) { // Case 1: Check it fails on non-existent file. _, err := newTable(tmpdir, fmt.Sprintf("readonlytest-%d", rand.Uint64()), - metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, true) + metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, freezerTableConfig{noSnappy: true}, true) if err == nil { t.Fatal("readonly table instantiation should fail for non-existent table") } @@ -1053,7 +1053,7 @@ func TestFreezerReadonly(t *testing.T) { idxFile.Write(make([]byte, 17)) idxFile.Close() _, err = newTable(tmpdir, fname, - metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, true) + metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, freezerTableConfig{noSnappy: true}, true) if err == nil { t.Errorf("readonly table instantiation should fail for invalid index size") } @@ -1063,7 +1063,7 @@ func TestFreezerReadonly(t *testing.T) { // again in readonly triggers an error. fname = fmt.Sprintf("readonlytest-%d", rand.Uint64()) f, err := newTable(tmpdir, fname, - metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, false) + metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatalf("failed to instantiate table: %v", err) } @@ -1076,7 +1076,7 @@ func TestFreezerReadonly(t *testing.T) { t.Fatal(err) } _, err = newTable(tmpdir, fname, - metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, true) + metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, freezerTableConfig{noSnappy: true}, true) if err == nil { t.Errorf("readonly table instantiation should fail for corrupt table file") } @@ -1085,7 +1085,7 @@ func TestFreezerReadonly(t *testing.T) { // Should be successful. fname = fmt.Sprintf("readonlytest-%d", rand.Uint64()) f, err = newTable(tmpdir, fname, - metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, false) + metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatalf("failed to instantiate table: %v\n", err) } @@ -1094,7 +1094,7 @@ func TestFreezerReadonly(t *testing.T) { t.Fatal(err) } f, err = newTable(tmpdir, fname, - metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, true) + metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, freezerTableConfig{noSnappy: true}, true) if err != nil { t.Fatal(err) } @@ -1234,7 +1234,7 @@ func (randTest) Generate(r *rand.Rand, size int) reflect.Value { func runRandTest(rt randTest) bool { fname := fmt.Sprintf("randtest-%d", rand.Uint64()) - f, err := newTable(os.TempDir(), fname, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, false) + f, err := newTable(os.TempDir(), fname, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, freezerTableConfig{noSnappy: true}, false) if err != nil { panic("failed to initialize table") } @@ -1243,7 +1243,7 @@ func runRandTest(rt randTest) bool { switch step.op { case opReload: f.Close() - f, err = newTable(os.TempDir(), fname, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true, false) + f, err = newTable(os.TempDir(), fname, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, freezerTableConfig{noSnappy: true}, false) if err != nil { rt[i].err = fmt.Errorf("failed to reload table %v", err) } @@ -1381,7 +1381,7 @@ func TestIndexValidation(t *testing.T) { } for _, c := range cases { fn := fmt.Sprintf("t-%d", rand.Uint64()) - f, err := newTable(os.TempDir(), fn, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 10*dataSize, true, false) + f, err := newTable(os.TempDir(), fn, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 10*dataSize, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -1392,7 +1392,7 @@ func TestIndexValidation(t *testing.T) { f.Close() // reopen the table, corruption should be truncated - f, err = newTable(os.TempDir(), fn, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 100, true, false) + f, err = newTable(os.TempDir(), fn, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 100, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -1422,7 +1422,7 @@ func TestFlushOffsetTracking(t *testing.T) { fileSize = 100 ) fn := fmt.Sprintf("t-%d", rand.Uint64()) - f, err := newTable(os.TempDir(), fn, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), fileSize, true, false) + f, err := newTable(os.TempDir(), fn, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), fileSize, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -1533,7 +1533,7 @@ func TestTailTruncationCrash(t *testing.T) { fileSize = 100 ) fn := fmt.Sprintf("t-%d", rand.Uint64()) - f, err := newTable(os.TempDir(), fn, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), fileSize, true, false) + f, err := newTable(os.TempDir(), fn, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), fileSize, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } @@ -1563,7 +1563,7 @@ func TestTailTruncationCrash(t *testing.T) { // the offset f.metadata.setFlushOffset(31*indexEntrySize, true) - f, err = newTable(os.TempDir(), fn, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), fileSize, true, false) + f, err = newTable(os.TempDir(), fn, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), fileSize, freezerTableConfig{noSnappy: true}, false) if err != nil { t.Fatal(err) } diff --git a/core/rawdb/freezer_test.go b/core/rawdb/freezer_test.go index 7d82ea305f2..150734d3ac9 100644 --- a/core/rawdb/freezer_test.go +++ b/core/rawdb/freezer_test.go @@ -31,7 +31,7 @@ import ( "github.com/stretchr/testify/require" ) -var freezerTestTableDef = map[string]bool{"test": true} +var freezerTestTableDef = map[string]freezerTableConfig{"test": {noSnappy: true}} func TestFreezerModify(t *testing.T) { t.Parallel() @@ -47,7 +47,7 @@ func TestFreezerModify(t *testing.T) { valuesRLP = append(valuesRLP, iv) } - tables := map[string]bool{"raw": true, "rlp": false} + tables := map[string]freezerTableConfig{"raw": {noSnappy: true}, "rlp": {noSnappy: false}} f, _ := newFreezerForTesting(t, tables) defer f.Close() @@ -111,7 +111,7 @@ func TestFreezerModifyRollback(t *testing.T) { f.Close() // Reopen and check that the rolled-back data doesn't reappear. - tables := map[string]bool{"test": true} + tables := map[string]freezerTableConfig{"test": {noSnappy: true}} f2, err := NewFreezer(dir, "", false, 2049, tables) if err != nil { t.Fatalf("can't reopen freezer after failed ModifyAncients: %v", err) @@ -249,7 +249,7 @@ func TestFreezerConcurrentModifyTruncate(t *testing.T) { } func TestFreezerReadonlyValidate(t *testing.T) { - tables := map[string]bool{"a": true, "b": true} + tables := map[string]freezerTableConfig{"a": {noSnappy: true}, "b": {noSnappy: true}} dir := t.TempDir() // Open non-readonly freezer and fill individual tables // with different amount of data. @@ -285,7 +285,7 @@ func TestFreezerReadonlyValidate(t *testing.T) { func TestFreezerConcurrentReadonly(t *testing.T) { t.Parallel() - tables := map[string]bool{"a": true} + tables := map[string]freezerTableConfig{"a": {noSnappy: true}} dir := t.TempDir() f, err := NewFreezer(dir, "", false, 2049, tables) @@ -333,7 +333,7 @@ func TestFreezerConcurrentReadonly(t *testing.T) { } } -func newFreezerForTesting(t *testing.T, tables map[string]bool) (*Freezer, string) { +func newFreezerForTesting(t *testing.T, tables map[string]freezerTableConfig) (*Freezer, string) { t.Helper() dir := t.TempDir() @@ -379,7 +379,7 @@ func checkAncientCount(t *testing.T, f *Freezer, kind string, n uint64) { func TestFreezerCloseSync(t *testing.T) { t.Parallel() - f, _ := newFreezerForTesting(t, map[string]bool{"a": true, "b": true}) + f, _ := newFreezerForTesting(t, map[string]freezerTableConfig{"a": {noSnappy: true}, "b": {noSnappy: true}}) defer f.Close() // Now, close and sync. This mimics the behaviour if the node is shut down, @@ -401,17 +401,23 @@ func TestFreezerCloseSync(t *testing.T) { func TestFreezerSuite(t *testing.T) { ancienttest.TestAncientSuite(t, func(kinds []string) ethdb.AncientStore { - tables := make(map[string]bool) + tables := make(map[string]freezerTableConfig) for _, kind := range kinds { - tables[kind] = true + tables[kind] = freezerTableConfig{ + noSnappy: true, + prunable: true, + } } f, _ := newFreezerForTesting(t, tables) return f }) ancienttest.TestResettableAncientSuite(t, func(kinds []string) ethdb.ResettableAncientStore { - tables := make(map[string]bool) + tables := make(map[string]freezerTableConfig) for _, kind := range kinds { - tables[kind] = true + tables[kind] = freezerTableConfig{ + noSnappy: true, + prunable: true, + } } f, _ := newResettableFreezer(t.TempDir(), "", false, 2048, tables) return f diff --git a/ethdb/database.go b/ethdb/database.go index 323f8f5d6fd..b1577512f3a 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -128,6 +128,8 @@ type AncientWriter interface { // is item_n(start from 0). The deleted items may not be removed from the ancient store // immediately, but only when the accumulated deleted data reach the threshold then // will be removed all together. + // + // Note that data marked as non-prunable will still be retained and remain accessible. TruncateTail(n uint64) (uint64, error) // Sync flushes all in-memory ancient store data to disk. 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 010/658] 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 | 287 ++++++------- 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 | 384 +++++++++++------ 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, 1585 insertions(+), 1006 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 c30927d9ff2..95239bd6405 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 fd3f4e1bab6..2995d876249 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 06eabbf3136..2e3bb6aeadc 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 00000000000..c3a26ea7f5d --- /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 00000000000..878b6d66c8f --- /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 c30856db4f2..b8f6be9cafe 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 d2b031a474d..ca30280e030 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 3e3ea23b243..c6d3cd90869 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 95dd1815519..fe75c1ea7d4 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 54a36876488..addda03df42 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 17ad765e042..a02f8d24599 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 5bd48a4b23f..4f45a7f2b21 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 19acbd762af..6c05672cbcf 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 + 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() } - logs = append(logs, mlogs...) } - getLogStats.addAmount(st, int64(len(logs))) - return logs, nil + 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() - } + 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 } - log.Info("Get log stats") - getLogStats.print() + 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)< 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)<= min { + result = append(result, v-offset) + } } - nextNextRes = nextNextRes[:stop] - } - maxLen := len(nextRes) + len(nextNextRes) - if maxLen == 0 { - return nextRes + return result } - if len(baseRes) < maxLen { - maxLen = len(baseRes) + // iterate through baseRes and nextRes in parallel and collect matching results. + maxLen := len(baseRes) + if l := len(nextRes); l < maxLen { + maxLen = l } - // iterate through baseRes, nextRes and nextNextRes and collect matching results. matchedRes := make(potentialMatches, 0, maxLen) - for _, nextRes := range []potentialMatches{nextRes, nextNextRes} { - if baseRes != nil { - 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 { - 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 66f95f21459..4c4668e3213 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 f679c3aeb39..9f3d55aa3fe 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 75b039dbe1a..b4b63556a9c 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 1790e413fd5..99cb3841c37 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 ec6de9e6638..365857347c7 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 09ccb939073..cedb5b35190 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 + } + if number < 0 { + return 0, errors.New("negative block number") } - return hdr.Number.Int64(), nil + 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 } - - logChan, errChan := f.rangeLogsAsync(ctx) - var logs []*types.Log - for { - select { - case log := <-logChan: - logs = append(logs, log) - case err := <-errChan: - return logs, err - } - } + return f.rangeLogs(ctx, begin, end) } -// 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 - } +const ( + rangeLogsTestSync = iota + rangeLogsTestTrimmed + rangeLogsTestIndexed + rangeLogsTestUnindexed + rangeLogsTestDone +) - errChan <- nil - }() +type rangeLogsTestEvent struct { + event int + begin, end uint64 +} - return logChan, errChan +// 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 } -// 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) +// 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 +} - 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) +} + +// 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 - // 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 { + 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 } - found, err := f.checkMatches(ctx, header) + 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 } - for _, log := range found { - logChan <- log + 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(): + // 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 + } - case <-ctx.Done(): - return ctx.Err() + 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 86012b3f9a8..7531a1ecfcd 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 aec5ee41663..c35d823f5ae 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) - - 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 +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() +} - 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 e2790d91ebd..4026c03e892 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 d70cb90ec9d..88ff7b8af35 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 3d8f7aa700b..9e2ea2c876e 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 e017804861f..a5fd9bb0d4d 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 } From 40ad6bedf6cb4cb36851a713720ee81b095f2592 Mon Sep 17 00:00:00 2001 From: Shahar Rosenberg Date: Tue, 18 Mar 2025 11:05:12 +0000 Subject: [PATCH 011/658] eth/tracers: fix precompile move feat for debug_traceCall (#31348) `debug_traceCall` was ignoring the override `movePrecompileToAddress`. Now it is at feature-parity with eth_call. --- eth/tracers/api.go | 26 ++++++++++++++----------- eth/tracers/api_test.go | 43 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index d13aee555f4..627dd487fc3 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -279,7 +279,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed TxIndex: i, TxHash: tx.Hash(), } - res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, task.statedb, config) + res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, task.statedb, config, nil) if err != nil { task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()} log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) @@ -632,7 +632,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac TxIndex: i, TxHash: tx.Hash(), } - res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, statedb, config) + res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, statedb, config, nil) if err != nil { return nil, err } @@ -676,7 +676,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat // concurrent use. // See: https://github.com/ethereum/go-ethereum/issues/29114 blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) - res, err := api.traceTx(ctx, txs[task.index], msg, txctx, blockCtx, task.statedb, config) + res, err := api.traceTx(ctx, txs[task.index], msg, txctx, blockCtx, task.statedb, config, nil) if err != nil { results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()} continue @@ -894,7 +894,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * TxIndex: int(index), TxHash: hash, } - return api.traceTx(ctx, tx, msg, txctx, vmctx, statedb, config) + return api.traceTx(ctx, tx, msg, txctx, vmctx, statedb, config, nil) } // TraceCall lets you trace a given eth_call. It collects the structured logs @@ -907,10 +907,11 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) { // Try to retrieve the specified block var ( - err error - block *types.Block - statedb *state.StateDB - release StateReleaseFunc + err error + block *types.Block + statedb *state.StateDB + release StateReleaseFunc + precompiles vm.PrecompiledContracts ) if hash, ok := blockNrOrHash.Hash(); ok { block, err = api.blockByHash(ctx, hash) @@ -952,7 +953,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc config.BlockOverrides.Apply(&vmctx) rules := api.backend.ChainConfig().Rules(vmctx.BlockNumber, vmctx.Random != nil, vmctx.Time) - precompiles := vm.ActivePrecompiledContracts(rules) + precompiles = vm.ActivePrecompiledContracts(rules) if err := config.StateOverrides.Apply(statedb, precompiles); err != nil { return nil, err } @@ -977,13 +978,13 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc if config != nil { traceConfig = &config.TraceConfig } - return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, traceConfig) + return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, traceConfig, precompiles) } // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig, precompiles vm.PrecompiledContracts) (interface{}, error) { var ( tracer *Tracer err error @@ -1009,6 +1010,9 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor } tracingStateDB := state.NewHookedState(statedb, tracer.Hooks) evm := vm.NewEVM(vmctx, tracingStateDB, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) + if precompiles != nil { + evm.SetPrecompiles(precompiles) + } // Define a meaningful timeout of a single transaction trace if config.Timeout != nil { diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 95e7739be06..796719bd632 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -642,6 +642,7 @@ func TestTracingWithOverrides(t *testing.T) { t.Parallel() // Initialize test accounts accounts := newAccounts(3) + ecRecoverAddress := common.HexToAddress("0x0000000000000000000000000000000000000001") storageAccount := common.Address{0x13, 37} genesis := &core.Genesis{ Config: params.TestChainConfig, @@ -940,6 +941,48 @@ func TestTracingWithOverrides(t *testing.T) { }, want: `{"gas":25288,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000055"}`, }, + { // Call to precompile ECREC (0x01), but code was modified to add 1 to input + blockNumber: rpc.LatestBlockNumber, + call: ethapi.TransactionArgs{ + From: &randomAccounts[0].addr, + To: &ecRecoverAddress, + Data: newRPCBytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")), + }, + config: &TraceCallConfig{ + StateOverrides: &override.StateOverride{ + randomAccounts[0].addr: override.OverrideAccount{ + Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether))), + }, + ecRecoverAddress: override.OverrideAccount{ + // The code below adds one to input + Code: newRPCBytes(common.Hex2Bytes("60003560010160005260206000f3")), + MovePrecompileTo: &randomAccounts[2].addr, + }, + }, + }, + want: `{"gas":21167,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000002"}`, + }, + { // Call to ECREC Precompiled on a different address, expect the original behaviour of ECREC precompile + blockNumber: rpc.LatestBlockNumber, + call: ethapi.TransactionArgs{ + From: &randomAccounts[0].addr, + To: &randomAccounts[2].addr, // Moved EcRecover + Data: newRPCBytes(common.Hex2Bytes("82f3df49d3645876de6313df2bbe9fbce593f21341a7b03acdb9423bc171fcc9000000000000000000000000000000000000000000000000000000000000001cba13918f50da910f2d55a7ea64cf716ba31dad91856f45908dde900530377d8a112d60f36900d18eb8f9d3b4f85a697b545085614509e3520e4b762e35d0d6bd")), + }, + config: &TraceCallConfig{ + StateOverrides: &override.StateOverride{ + randomAccounts[0].addr: override.OverrideAccount{ + Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether))), + }, + ecRecoverAddress: override.OverrideAccount{ + // The code below adds one to input + Code: newRPCBytes(common.Hex2Bytes("60003560010160005260206000f3")), + MovePrecompileTo: &randomAccounts[2].addr, // Move EcRecover to this address + }, + }, + }, + want: `{"gas":25664,"failed":false,"returnValue":"000000000000000000000000c6e93f4c1920eaeaa1e699f76a7a8c18e3056074"}`, + }, } for i, tc := range testSuite { result, err := api.TraceCall(context.Background(), tc.call, rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, tc.config) From 668118bfe12b32c1c0f74878d18806c099426b82 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 18 Mar 2025 12:07:49 +0100 Subject: [PATCH 012/658] params: add hoodi testnet definition (#31406) Adds support for the new hoodi testnet. Hoodi is meant for stakers to test their setup. For more info please refer to https://hoodi.ethpandaops.io/. --- beacon/params/networks.go | 13 +++++++++++ cmd/blsync/main.go | 1 + cmd/devp2p/nodesetcmd.go | 2 ++ cmd/geth/main.go | 4 ++++ cmd/utils/flags.go | 27 ++++++++++++++++++++-- core/filtermaps/checkpoints.go | 4 ++++ core/filtermaps/checkpoints_hoodi.json | 1 + core/forkid/forkid_test.go | 11 +++++++++ core/genesis.go | 16 +++++++++++++ core/genesis_alloc.go | 1 + core/genesis_test.go | 11 +++++++++ core/rawdb/accessors_indexes_test.go | 8 +++++++ params/bootnodes.go | 11 +++++++++ params/config.go | 32 ++++++++++++++++++++++++++ 14 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 core/filtermaps/checkpoints_hoodi.json diff --git a/beacon/params/networks.go b/beacon/params/networks.go index eda225e0fe6..c5479869776 100644 --- a/beacon/params/networks.go +++ b/beacon/params/networks.go @@ -55,4 +55,17 @@ var ( AddFork("CAPELLA", 256, []byte{4, 1, 112, 0}). AddFork("DENEB", 29696, []byte{5, 1, 112, 0}). AddFork("ELECTRA", 115968, []byte{6, 1, 112, 0}) + + HoodiLightConfig = (&ChainConfig{ + GenesisValidatorsRoot: common.HexToHash("0x212f13fc4df078b6cb7db228f1c8307566dcecf900867401a92023d7ba99cb5f"), + GenesisTime: 1742212800, + Checkpoint: common.HexToHash(""), + }). + AddFork("GENESIS", 0, common.FromHex("0x10000910")). + AddFork("ALTAIR", 0, common.FromHex("0x20000910")). + AddFork("BELLATRIX", 0, common.FromHex("0x30000910")). + AddFork("CAPELLA", 0, common.FromHex("0x40000910")). + AddFork("DENEB", 0, common.FromHex("0x50000910")). + AddFork("ELECTRA", 2048, common.FromHex("0x60000910")). + AddFork("FULU", 18446744073709551615, common.FromHex("0x70000910")) ) diff --git a/cmd/blsync/main.go b/cmd/blsync/main.go index 57bc46c3679..60caa4aa2a0 100644 --- a/cmd/blsync/main.go +++ b/cmd/blsync/main.go @@ -47,6 +47,7 @@ func main() { utils.MainnetFlag, utils.SepoliaFlag, utils.HoleskyFlag, + utils.HoodiFlag, utils.BlsyncApiFlag, utils.BlsyncJWTSecretFlag, }, diff --git a/cmd/devp2p/nodesetcmd.go b/cmd/devp2p/nodesetcmd.go index c8177f049d4..d7725fadb1f 100644 --- a/cmd/devp2p/nodesetcmd.go +++ b/cmd/devp2p/nodesetcmd.go @@ -234,6 +234,8 @@ func ethFilter(args []string) (nodeFilter, error) { filter = forkid.NewStaticFilter(params.SepoliaChainConfig, core.DefaultSepoliaGenesisBlock().ToBlock()) case "holesky": filter = forkid.NewStaticFilter(params.HoleskyChainConfig, core.DefaultHoleskyGenesisBlock().ToBlock()) + case "hoodi": + filter = forkid.NewStaticFilter(params.HoodiChainConfig, core.DefaultHoodiGenesisBlock().ToBlock()) default: return nil, fmt.Errorf("unknown network %q", args[0]) } diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 2995d876249..07fbeaca5c1 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -296,6 +296,9 @@ func prepare(ctx *cli.Context) { case ctx.IsSet(utils.HoleskyFlag.Name): log.Info("Starting Geth on Holesky testnet...") + case ctx.IsSet(utils.HoodiFlag.Name): + log.Info("Starting Geth on Hoodi testnet...") + case ctx.IsSet(utils.DeveloperFlag.Name): log.Info("Starting Geth in ephemeral dev mode...") log.Warn(`You are running Geth in --dev mode. Please note the following: @@ -322,6 +325,7 @@ func prepare(ctx *cli.Context) { // Make sure we're not on any supported preconfigured testnet either if !ctx.IsSet(utils.HoleskyFlag.Name) && !ctx.IsSet(utils.SepoliaFlag.Name) && + !ctx.IsSet(utils.HoodiFlag.Name) && !ctx.IsSet(utils.DeveloperFlag.Name) { // Nope, we're really on mainnet. Bump that cache up! log.Info("Bumping default cache on mainnet", "provided", ctx.Int(utils.CacheFlag.Name), "updated", 4096) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 2e3bb6aeadc..d60f9c64d5e 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -134,7 +134,7 @@ var ( } NetworkIdFlag = &cli.Uint64Flag{ Name: "networkid", - Usage: "Explicitly set network id (integer)(For testnets: use --sepolia, --holesky instead)", + Usage: "Explicitly set network id (integer)(For testnets: use --sepolia, --holesky, --hoodi instead)", Value: ethconfig.Defaults.NetworkId, Category: flags.EthCategory, } @@ -153,6 +153,11 @@ var ( Usage: "Holesky network: pre-configured proof-of-stake test network", Category: flags.EthCategory, } + HoodiFlag = &cli.BoolFlag{ + Name: "hoodi", + Usage: "Hoodi network: pre-configured proof-of-stake test network", + Category: flags.EthCategory, + } // Dev mode DeveloperFlag = &cli.BoolFlag{ Name: "dev", @@ -958,6 +963,7 @@ var ( TestnetFlags = []cli.Flag{ SepoliaFlag, HoleskyFlag, + HoodiFlag, } // NetworkFlags is the flag group of all built-in supported networks. NetworkFlags = append([]cli.Flag{MainnetFlag}, TestnetFlags...) @@ -984,6 +990,9 @@ func MakeDataDir(ctx *cli.Context) string { if ctx.Bool(HoleskyFlag.Name) { return filepath.Join(path, "holesky") } + if ctx.Bool(HoodiFlag.Name) { + return filepath.Join(path, "hoodi") + } return path } Fatalf("Cannot determine default data directory, please set manually (--datadir)") @@ -1044,6 +1053,8 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { urls = params.HoleskyBootnodes case ctx.Bool(SepoliaFlag.Name): urls = params.SepoliaBootnodes + case ctx.Bool(HoodiFlag.Name): + urls = params.HoodiBootnodes } } cfg.BootstrapNodes = mustParseBootnodes(urls) @@ -1428,6 +1439,8 @@ func SetDataDir(ctx *cli.Context, cfg *node.Config) { cfg.DataDir = filepath.Join(node.DefaultDataDir(), "sepolia") case ctx.Bool(HoleskyFlag.Name) && cfg.DataDir == node.DefaultDataDir(): cfg.DataDir = filepath.Join(node.DefaultDataDir(), "holesky") + case ctx.Bool(HoodiFlag.Name) && cfg.DataDir == node.DefaultDataDir(): + cfg.DataDir = filepath.Join(node.DefaultDataDir(), "hoodi") } } @@ -1554,7 +1567,7 @@ func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) { // SetEthConfig applies eth-related command line flags to the config. func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // Avoid conflicting network flags - flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag) + flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag, HoodiFlag) flags.CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer // Set configurations from CLI flags @@ -1738,6 +1751,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } cfg.Genesis = core.DefaultSepoliaGenesisBlock() SetDNSDiscoveryDefaults(cfg, params.SepoliaGenesisHash) + case ctx.Bool(HoodiFlag.Name): + if !ctx.IsSet(NetworkIdFlag.Name) { + cfg.NetworkId = 560048 + } + cfg.Genesis = core.DefaultHoodiGenesisBlock() + SetDNSDiscoveryDefaults(cfg, params.HoodiGenesisHash) case ctx.Bool(DeveloperFlag.Name): if !ctx.IsSet(NetworkIdFlag.Name) { cfg.NetworkId = 1337 @@ -1852,6 +1871,8 @@ func MakeBeaconLightConfig(ctx *cli.Context) bparams.ClientConfig { config.ChainConfig = *bparams.SepoliaLightConfig case ctx.Bool(HoleskyFlag.Name): config.ChainConfig = *bparams.HoleskyLightConfig + case ctx.Bool(HoodiFlag.Name): + config.ChainConfig = *bparams.HoodiLightConfig default: if !customConfig { config.ChainConfig = *bparams.MainnetLightConfig @@ -2118,6 +2139,8 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis { genesis = core.DefaultHoleskyGenesisBlock() case ctx.Bool(SepoliaFlag.Name): genesis = core.DefaultSepoliaGenesisBlock() + case ctx.Bool(HoodiFlag.Name): + genesis = core.DefaultHoodiGenesisBlock() case ctx.Bool(DeveloperFlag.Name): Fatalf("Developer chains are ephemeral") } diff --git a/core/filtermaps/checkpoints.go b/core/filtermaps/checkpoints.go index f8d6500aa76..face4ea5d33 100644 --- a/core/filtermaps/checkpoints.go +++ b/core/filtermaps/checkpoints.go @@ -46,6 +46,9 @@ var checkpointsSepoliaJSON []byte //go:embed checkpoints_holesky.json var checkpointsHoleskyJSON []byte +//go:embed checkpoints_hoodi.json +var checkpointsHoodiJSON []byte + // checkpoints lists sets of checkpoints for multiple chains. The matching // checkpoint set is autodetected by the indexer once the canonical chain is // known. @@ -53,6 +56,7 @@ var checkpoints = []checkpointList{ decodeCheckpoints(checkpointsMainnetJSON), decodeCheckpoints(checkpointsSepoliaJSON), decodeCheckpoints(checkpointsHoleskyJSON), + decodeCheckpoints(checkpointsHoodiJSON), } func decodeCheckpoints(encoded []byte) (result checkpointList) { diff --git a/core/filtermaps/checkpoints_hoodi.json b/core/filtermaps/checkpoints_hoodi.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/core/filtermaps/checkpoints_hoodi.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index bebc20ad9db..31e2b534bef 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -112,6 +112,17 @@ func TestCreation(t *testing.T) { {123, 2740434112, ID{Hash: checksumToBytes(0xdfbd9bed), Next: 0}}, // Future Prague block }, }, + // Hoodi test cases + { + params.HoodiChainConfig, + core.DefaultHoodiGenesisBlock().ToBlock(), + []testcase{ + {0, 0, ID{Hash: checksumToBytes(0xbef71d30), Next: 1742999832}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin, London, Paris, Shanghai, Cancun block + {123, 1742999831, ID{Hash: checksumToBytes(0xbef71d30), Next: 1742999832}}, // Last Cancun block + {123, 1742999832, ID{Hash: checksumToBytes(0x0929e24e), Next: 0}}, // First Prague block + {123, 2740434112, ID{Hash: checksumToBytes(0x0929e24e), Next: 0}}, // Future Prague block + }, + }, } for i, tt := range tests { for j, ttt := range tt.cases { diff --git a/core/genesis.go b/core/genesis.go index 23f4a73ec51..95782a827a4 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -221,6 +221,8 @@ func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.Gene genesis = DefaultSepoliaGenesisBlock() case params.HoleskyGenesisHash: genesis = DefaultHoleskyGenesisBlock() + case params.HoodiGenesisHash: + genesis = DefaultHoodiGenesisBlock() } if genesis != nil { return genesis.Alloc, nil @@ -431,6 +433,8 @@ func (g *Genesis) chainConfigOrDefault(ghash common.Hash, stored *params.ChainCo return params.HoleskyChainConfig case ghash == params.SepoliaGenesisHash: return params.SepoliaChainConfig + case ghash == params.HoodiGenesisHash: + return params.HoodiChainConfig default: return stored } @@ -625,6 +629,18 @@ func DefaultHoleskyGenesisBlock() *Genesis { } } +// DefaultHoodiGenesisBlock returns the Hoodi network genesis block. +func DefaultHoodiGenesisBlock() *Genesis { + return &Genesis{ + Config: params.HoodiChainConfig, + Nonce: 0x1234, + GasLimit: 0x2255100, + Difficulty: big.NewInt(0x01), + Timestamp: 1742212800, + Alloc: decodePrealloc(hoodiAllocData), + } +} + // DeveloperGenesisBlock returns the 'geth --dev' genesis block. func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis { // Override the default period to the user requested one diff --git a/core/genesis_alloc.go b/core/genesis_alloc.go index 28bb2d5c108..68a1838d982 100644 --- a/core/genesis_alloc.go +++ b/core/genesis_alloc.go @@ -24,3 +24,4 @@ package core const mainnetAllocData = "\xfa\x04]X\u0793\r\x83b\x011\x8e\u0189\x9agT\x06\x908'\x80t2\x80\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0793\x17bC\x0e\xa9\u00e2nWI\xaf\xdbp\xda_x\u077b\x8c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0793\x1d\x14\x80K9\x9cn\xf8\x0edWoev`\x80O\xec\v\x89\u3bb5sr@\xa0\x00\x00\u07932@5\x87\x94{\x9f\x15b*h\xd1\x04\xd5M3\xdb\xd1\u0349\x043\x87Oc,\xc6\x00\x00\u0793I~\x92\xcd\xc0\xe0\xb9c\xd7R\xb2)j\u02c7\u0682\x8b$\x89\n\x8fd\x9f\xe7\xc6\x18\x00\x00\u0793K\xfb\xe1Tk\xc6\xc6[\\~\xaaU0K8\xbb\xfe\xc6\u04c9lk\x93[\x8b\xbd@\x00\x00\u0793Z\x9c\x03\xf6\x9d\x17\xd6l\xbb\x8a\xd7!\x00\x8a\x9e\xbb\xb86\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u0793]\x0e\xe8\x15^\xc0\xa6\xffh\bU,\xa5\xf1k\xb5\xbe2:\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u0793v\"\xd8J#K\xb8\xb0x#\x0f\u03c4\xb6z\u9a2c\xae\x89%\xe1\xccQ\x99R\xf8\x00\x00\u0793{\x9f\xc3\x19\x05\xb4\x99K\x04\xc9\xe2\xcf\xdc^'pP?B\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u0793\u007fJ#\xca\x00\xcd\x04=%\u0088\x8c\x1a\xa5h\x8f\x81\xa3D\x89)\xf0\xa9[\xfb\xf7)\x00\x00\u0793\x869\u06bb\u3bac\x88{]\xc0\xe4>\x13\xbc\u0487\xd7l\x89\x10\xd0\xe3\xc8}n,\x00\x00\u0793\x89P\x86y\xab\xf8\xc7\x1b\xf6x\x16\x87\x12\x0e>j\x84XM\x89a\x94\x04\x9f0\xf7 \x00\x00\u0793\x8f\xc7\u02ed\xff\xbd\r\u007f\xe4O\x8d\xfd`\xa7\x9dr\x1a\x1c\x9c\x8965\u026d\xc5\u07a0\x00\x00\u0793\x95`\xa3\xdebxh\xf9\x1f\xa8\xbf\xe1\xc1\xb7\xaf\xaf\b\x18k\x89\x1cg\xf5\xf7\xba\xa0\xb0\x00\x00\u0793\x96\x97G\xf7\xa5\xb3\x06E\xfe\x00\xe4I\x01CZ\xce$\xcc7\x89\\(=A\x03\x94\x10\x00\x00\u0793\x9am}\xb3&g\x9bw\xc9\x03\x91\xa7Gm#\x8f;\xa3>\x89\n\xdaUGK\x814\x00\x00\u0793\x9e\xef\n\b\x86\x05n?i!\x18S\xb9\xb7E\u007f7\x82\u4262\xa8x\x06\x9b(\xe0\x00\x00\u0793\x9f\xdb\xf4N\x1fJcb\xb7i\u00daG_\x95\xa9l+\u01c9\x1e\x93\x12\x83\xcc\xc8P\x00\x00\u07d3\xa5y\u007fR\xc9\u054f\x18\x9f6\xb1\xd4]\x1b\xf6\x04\x1f/k\x8a\x01'\u0473F\x1a\xcd\x1a\x00\x00\u0793\xaaS\x81\xb2\x13\x8e\xbe\xff\xc1\x91\xd5\xd8\u00d1u;p\x98\u04895\xab\xb0\x9f\xfe\u07b6\x80\x00\u0793\xaa\xda%\xea\"\x86p\x9a\xbbB-A\x92?\u04c0\xcd\x04\u01c9#=\xf3)\x9far\x00\x00\u0793\xac\xbf\xb2\xf2ZT\x85\xc79\xefp\xa4N\xee\xeb|e\xa6o\x89\x05k\xc7^-c\x10\x00\x00\u07d3\xac\xc6\xf0\x82\xa4B\x82\x87d\xd1\x1fX\u0589J\xe4\b\xf0s\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u0793\xb2w\xb0\x99\xa8\xe8f\xca\x0e\xc6[\u02c7(O\xd1B\xa5\x82\x89j\xcb=\xf2~\x1f\x88\x00\x00\u0753\xbd\xd4\x01:\xa3\x1c\x04al+\xc9x_'\x88\xf9\x15g\x9b\x88\xb9\xf6]\x00\xf6<\x00\x00\u0793\xc2}c\xfd\xe2K\x92\xee\x8a\x1e~\xd5\xd2m\x8d\xc5\xc8;\x03\x89lk\x93[\x8b\xbd@\x00\x00\u0553\xc4\x0f\xe2\tT#P\x9b\x9f\u0677T21X\xaf#\x10\xf3\x80\u0793\xd7^\xd6\fwO\x8b:ZQs\xfb\x183\xadq\x05\xa2\u0649l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d3\u05cd\x89\xb3_G'\x16\xec\xea\xfe\xbf`\x05'\u04e1\xf9i\x8a\x05\xe0T\x9c\x962\xe1\xd8\x00\x00\u0793\xda\xe2{5\v\xae \xc5e!$\xaf]\x8b\\\xba\x00\x1e\xc1\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u0793\xdc\x01\xcb\xf4Ix\xa4.\x8d\xe8\xe46\xed\xf9B\x05\u03f6\xec\x89O\x0f\xeb\xbc\u068c\xb4\x00\x00\u07d3\u607c-\x10\xdbb\u0785\x84\x83$I\"P4\x8e\x90\xbf\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d3\xf4c\xe17\xdc\xf6%\xfb\U000fc8de\u0298\u04b9h\xcf\u007f\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\x01\x00\a9K\x8bue\xa1e\x8a\xf8\x8c\xe4cI\x915\u05b7\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x01\r\xf1\xdfK\xed#v\r-\x1c\x03x\x15\x86\xdd\xf7\x91\x8eT\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\x01\x0fJ\x98\u07e1\xd9y\x9b\xf5\u01d6\xfbU\x0e\xfb\xe7\xec\xd8w\x8a\x01\xb2\xf2\x92#b\x92\xc7\x00\x00\u07d4\x01\x15PW\x00/k\r\x18\xac\xb98\x8d;\xc8\x12\x9f\x8fz \x89H\xa4\xa9\x0f\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01k`\xbbmg\x92\x8c)\xfd\x03\x13\xc6f\u068f\x16\x98\xd9\u0149lk\x93[\x8b\xbd@\x00\x00\u07d4\x01l\x85\xe1a;\x90\x0f\xa3W\xb8(;\x12\x0ee\xae\xfc\xdd\b\x89+]\x97\x84\xa9|\xd5\x00\x00\u07d4\x01\x84\x92H\x8b\xa1\xa2\x924\"G\xb3\x18U\xa5Y\x05\xfe\xf2i\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x01\x8f \xa2{'\xecD\x1a\xf7#\xfd\x90\x99\xf2\u02f7\x9dbc\x89uy*\x8a\xbd\xef|\x00\x00\u07d4\x01\x91\xebT~{\xf6\x97k\x9b\x1bWuFv\x1d\xe6V\"\xe2\x89lkLM\xa6\u077e\x00\x00\u07d4\x01\x9dp\x95y\xffK\xc0\x9f\xdc\xdd\xe41\xdc\x14G\xd2\xc2`\xbc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x01\xa2Z_Z\xf0\x16\x9b0\x86L;\xe4\xd7V<\xcdD\xf0\x9e\x89M\x85<\x8f\x89\b\x98\x00\x00\xe0\x94\x01\xa7\xd9\xfa}\x0e\xb1\x18\\g\xe5M\xa8<.u\xdbi\u37ca\x01\x9dJ\xdd\xd0\u063c\x96\x00\x00\u07d4\x01\xa8\x18\x13ZAB\x10\xc3|b\xb6%\xac\xa1\xa5F\x11\xac6\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\x01\xb1\xca\xe9\x1a;\x95Y\xaf\xb3<\xdcmh\x94B\xfd\xbf\xe07\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01\xb5\xb5\xbcZ\x11\u007f\xa0\x8b4\xed\x1d\xb9D\x06\bYz\xc5H\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01\xbb\xc1Og\xaf\x069\xaa\xb1D\x1ej\b\xd4\xceqb\t\x0f\x89F\xfc\xf6\x8f\xf8\xbe\x06\x00\x00\xe0\x94\x01\xd08\x15\xc6\x1fAkq\xa2a\n-\xab\xa5\x9f\xf6\xa6\xde[\x8a\x02\x05\xdf\xe5\v\x81\xc8.\x00\x00\u07d4\x01\u0559\xee\r_\x8c8\xab-9.,e\xb7L<\xe3\x18 \x89\x1b\xa5\xab\xf9\xe7y8\x00\x00\u07d4\x01\xe4\x05!\x12%0\u066c\x91\x11<\x06\xa0\x19\vmc\x85\v\x89Hz\x9a0E9D\x00\x00\u07d4\x01\xe6A]X{\x06T\x90\xf1\xed\u007f!\xd6\xe0\xf3\x86\xeegG\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x01\xe8d\xd3Tt\x1bB>oB\x85\x17$F\x8ct\xf5\xaa\x9c\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x01\xed_\xba\x8d.\xabg:\xec\x04-0\xe4\xe8\xa6\x11\xd8\xc5Z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01\xfb\x8e\xc1$%\xa0O\x81>F\xc5L\x05t\x8c\xa6\xb2\x9a\xa9\x89\x0e\x15s\x03\x85F|\x00\x00\u07d4\x01\xff\x1e\xb1\u07adP\xa7\xf2\xf9c\x8f\xde\xe6\xec\xcf:{*\u0209 \x86\xac5\x10R`\x00\x00\u07d4\x02\x03b\u00ed\xe8x\u0290\u05b2\u0609\xa4\xccU\x10\xee\xd5\xf3\x898\x88\xe8\xb3\x11\xad\xb3\x80\x00\u07d4\x02\x03\xae\x01\xd4\xc4\x1c\xae\x18e\xe0K\x1f[S\xcd\xfa\xec\xae1\x896\x89\xcd\u03b2\x8c\xd7\x00\x00\u07d4\x02\b\x93a\xa3\xfetQ\xfb\x1f\x87\xf0\x1a-\x86fS\xdc\v\a\x89\x02*\xc7H2\xb5\x04\x00\x00\u07d4\x02\x1fi\x04=\xe8\x8cI\x17\xca\x10\xf1\x84(\x97\xee\xc0X\x9c|\x89kD\u03f8\x14\x87\xf4\x00\x00\u07d4\x02)\x0f\xb5\xf9\xa5\x17\xf8(E\xac\xde\xca\x0f\xc8F\x03\x9b\xe23\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x029\xb4\xf2\x1f\x8e\x05\xcd\x01Q++\xe7\xa0\xe1\x8am\x97F\a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x02Gr\x12\xff\xddu\xe5\x15VQ\xb7e\x06\xb1dfq\xa1\xeb\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x02J\t\x8a\xe7\x02\xbe\xf5@l\x9c\"\xb7\x8b\xd4\xeb,\u01e2\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x02K\xdd,{\xfdP\x0e\xe7@O\u007f\xb3\xe9\xfb1\xdd \xfb\u0449\t\xc2\x00vQ\xb2P\x00\x00\u07d4\x02Sg\x96\x03\x04\xbe\xee4Y\x11\x18\xe9\xac-\x13X\xd8\x02\x1a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x02V\x14\x9f[Pc\xbe\xa1N\x15f\x1f\xfbX\xf9\xb4Y\xa9W\x89&)\xf6n\fS\x00\x00\x00\u07d4\x02`=z;\xb2\x97\xc6|\x87~]4\xfb\u0579\x13\xd4\xc6:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x02a\xad:\x17*\xbf\x13\x15\xf0\xff\xec2p\x98j\x84\t\xcb%\x89\v\b!;\u03cf\xfe\x00\x00\u07d4\x02d2\xaf7\xdcQ\x13\xf1\xf4mH\nM\u0c80R#~\x89\x13I\xb7\x86\xe4\v\xfc\x00\x00\u07d4\x02f\xab\x1ck\x02\x16#\v\x93\x95D=_\xa7^hEh\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x02u\x1d\u018c\xb5\xbdsp'\xab\xf7\u0777s\x90\xcdw\xc1k\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x02w\x8e9\x0f\xa1u\x10\xa3B\x8a\xf2\x87\fBsT}8l\x8a\x03lw\x80\x18\x8b\xf0\xef\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x037|\x0eUkd\x01\x03(\x9aa\x89\u1baecI4g\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03IcM\u00a9\xe8\f?w!\xee+PF\xae\xaa\xed\xfb\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x03U\xbc\xac\xbd!D\x1e\x95\xad\xee\xdc0\xc1r\x18\u0224\b\u0389\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x03n\xef\xf5\xba\x90\xa6\x87\x9a\x14\xdf\xf4\xc5\x04;\x18\xca\x04`\u0249\x05k\xc7^-c\x10\x00\x00\xe0\x94\x03qKA\u04a6\xf7Q\x00\x8e\xf8\xddM+)\xae\u02b8\xf3n\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x03r\xe8RX.\t44J\x0f\xed!x0M\xf2]F(\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03r\xeeU\b\xbf\x81c\xed(N^\xef\x94\xceMsg\xe5\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x03}\xd0V\xe7\xfd\xbdd\x1d\xb5\xb6\xbe\xa2\xa8x\n\x83\xfa\u1009\a\x96\xe3\xea?\x8a\xb0\x00\x00\xe0\x94\x03\x83#\xb1\x84\xcf\xf7\xa8*\xe2\u1f67y?\xe41\x9c\xa0\xbf\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03\x87y\xca-\xbef>c\xdb?\xe7V\x83\xea\x0e\xc6.#\x83\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\x03\x8eE\xea\xdd=\x88\xb8\u007f\xe4\u06b0fh\x05\"\xf0\xdf\xc8\xf9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x03\x92T\x9ar\u007f\x81eT)\u02d2\x8bR\x9f%\xdfM\x13\x85\x89\x01lC\xa0\xee\xa0t\x00\x00\u07d4\x03\x94\xb9\x0f\xad\xb8`O\x86\xf4?\xc1\xe3]1$\xb3*Y\x89\x89)j\xa1@'\x8ep\x00\x00\u0794\x03\x9ezN\xbc(N,\xcdB\xb1\xbd\xd6\v\xd6Q\x1c\x0fw\x06\x88\xf0\x15\xf2W6B\x00\x00\u07d4\x03\x9e\xf1\xceR\xfeyc\xf1f\u0562u\u0131\x06\x9f\xe3\xa82\x89\x15\xaf9\u4ab2t\x00\x00\u07d4\x03\xa2l\xfcL\x181op\u055e\x9e\x1ay\xee>\x8b\x96/L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x03\xaab(\x81#m\xd0\xf4\x94\f$\xc3$\xff\x8b{~!\x86\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4\x03\xafz\xd9\xd5\"<\xf7\xc8\xc1? \xdfg\xeb\xe5\xff\u017bA\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x03\xb0\xf1|\xd4F\x9d\xdc\u03f7\xdai~\x82\xa9\x1a_\x9ewt\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x03\xb4\x1bQ\xf4\x1d\xf2\r\xd2y\xba\xe1\x8c\x12w_w\xadw\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x03\xbe[F)\xae\xfb\xbc\xab\x9d\xe2m9Wl\xb7\xf6\x91\xd7d\x89\n\xdf0\xbap\u0217\x00\x00\u07d4\x03\xc6G\xa9\xf9)\xb0x\x1f\xe9\xae\x01\u02a3\xe1\x83\xe8vw~\x89\x18*\xb7\xc2\f\xe5$\x00\x00\u07d4\x03\xc9\x1d\x92\x946\x03\xe7R >\x054\x0eV`\x13\xb9\x00E\x89+|\xc2\xe9\xc3\"\\\x00\x00\u07d4\x03\xcbLOE\x16\xc4\xffy\xa1\xb6$O\xbfW.\x1c\u007f\xeay\x89\x94\x89#z\u06daP\x00\x00\u07d4\x03\u02d8\u05ec\xd8\x17\u079d\x88m\"\xfa\xb3\xf1\xb5}\x92\xa6\b\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x03\u031d-!\xf8k\x84\xac\x8c\xea\xf9q\u06e7\x8a\x90\xe6%p\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\x03\xd1rO\xd0\x0eT\xaa\xbc\xd2\xde*\x91\xe8F+\x10I\xdd:\x89\x8f\x1d\\\x1c\xae7@\x00\x00\u07d4\x03\xde\xdf\xcd\v<.\x17\xc7\x05\xda$\x87\x90\uf626\xbdWQ\x89Hz\x9a0E9D\x00\x00\u07d4\x03\u8c04SuW\xe7\t\xea\xe2\xe1\u1966\xbc\xe1\xef\x83\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x03\xeam&\u0400\xe5z\xee9&\xb1\x8e\x8e\xd7:N[(&\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x03\xeb<\xb8`\xf6\x02\x8d\xa5T\xd3D\xa2\xbbZP\n\xe8\xb8o\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x03\xeb\xc6?\xdaf`\xa4e\x04^#_\xben\\\xf1\x95s_\x89\a\xb0l\xe8\u007f\xddh\x00\x00\xe0\x94\x03\xefj\xd2\x0f\xf7\xbdO\x00+\xacX\xd4uD\u03c7\x9a\xe7(\x8a\x01u\xc7X\u0439n\\\x00\x00\u07d4\x03\xf7\xb9 \b\x81:\xe0\xa6v\xeb!(\x14\xaf\xab5\"\x10i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\x11p\xf5\x81\u0780\xe5\x8b*\x04\\\x8f|\x14\x93\xb0\x01\xb7\u02c90\xc8\xeca2\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\x04i\xe8\xc4@E\v\x0eQ&&\xfe\x81~gT\xa8\x15(0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04m'K\x1a\xf6\x15\xfbPZvJ\xd8\u0767p\xb1\xdb/=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x04}Z&\u05ed\x8f\x8ep`\x0fp\xa3\x98\u076a\x1c-\xb2o\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x04~\x87\xc8\xf7\xd1\xfc\xe3\xb0\x13S\xa8Xb\xa9H\xac\x04\x9f>\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x04\u007f\x9b\xf1R\x9d\xaf\x87\xd4\a\x17^o\x17\x1b^Y\xe9\xff>\x89#<\x8f\xe4'\x03\xe8\x00\x00\xe0\x94\x04\x85'2\xb4\xc6R\xf6\xc2\u53b3e\x87\xe6\nb\xda\x14\u06ca\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x04\x8a\x89p\xeaAE\xc6MU\x17\xb8\xde[F\xd0YZ\xad\x06\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x04\x9c]K\xc6\xf2]NEli{R\xa0x\x11\xcc\u045f\xb1\x89\x10D\x00\xa2G\x0eh\x00\x00\u07d4\x04\xa1\xca\xda\x1c\xc7Q\b/\xf8\u0692\x8e<\xfa\x00\b \xa9\xe9\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x04\xa8\n\xfa\xd5>\xf1\xf8Ae\xcf\xd8R\xb0\xfd\xf1\xb1\xc2K\xa8\x89\x03$\xe9d\xb3\xec\xa8\x00\x00\u07d4\x04\xaa\xfc\x8a\xe5\xceoI\x03\u021d\u007f\xac\x9c\xb1\x95\x12\"Gw\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x04\xbaK\xb8q@\x02,!Jo\xacB\xdbZ\x16\u0755@E\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x04\xba\x8a?\x03\xf0\x8b\x89P\x95\x99M\xdaa\x9e\u06ac\xee>z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\xc2\xc6K\xb5L>\xcc\xd0U\x85\xe1\x0e\xc6\xf9\x9a\f\xdb\x01\xa3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x04\xceE\xf6\x00\xdb\x18\xa9\u0405\x1b)\xd99>\xbd\xaa\xfe=\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x04\u05b8\xd4\u0686t\a\xbb\x99wI\u07bb\xcd\xc0\xb3XS\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x04\xd78\x96\xcfe\x93\xa6\x91\x97*\x13\xa6\xe4\x87\x1f\xf2\xc4+\x13\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\xd8*\xf9\xe0\x1a\x93m\x97\xf8\xf8Y@\xb9p\xf9\xd4\u06d96\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x04\xe5\xf5\xbc|\x92?\xd1\xe3\x175\xe7.\xf9h\xfdg\x11\fn\x89WU\x1d\xbc\x8ebL\x00\x00\u07d4\x04\xec\xa5\x01c\n\xbc\xe3R\x18\xb1t\x95k\x89\x1b\xa2^\xfb#\x8966\x9e\xd7t}&\x00\x00\u07d4\x05\x05\xa0\x8e\"\xa1\t\x01Z\"\xf6\x850STf*U1\u0549\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\x05\x14\x95L\xe8\x81\xc807\x03d\x00\x89lO\xd1\xee$nx\x00\x00\u07d4\x05\x1dBBv\xb2\x129fQ\x86\x13=e;\xb8\xb1\x86/\x89\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x05!\xbc:\x9f\x87\x11\xfe\xcb\x10\xf5\a\x97\xd7\x10\x83\xe3A\ub749\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x05#mL\x90\xd0e\xf9\u34c3X\xaa\xff\xd7w\xb8j\xecI\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x05*X\xe05\xf1\xfe\x9c\xdd\x16\x9b\xcf \x97\x03E\xd1+\x9cQ\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x05.\xab\x1fa\xb6\xd4U\x17(?A\xd1D\x18$\x87\x87I\u0409\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x053n\x9ar'(\xd9c\xe7\xa1\xcf'Y\xfd\x02tS\x0f\u02891\xa2D?\x88\x8ay\x80\x00\u07d4\x054q\u035aA\x92[9\x04\xa5\xa8\xff\xca6Y\xe04\xbe#\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4\x056\x1d\x8e\xb6\x94\x1dN\x90\xfb~\x14\x18\xa9Z2\xd5%w2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x05B:T\xc8\xd0\xf9p~pAs\xd9#\xb9F\xed\xc8\xe7\x00\x89\x06\xea\x03\u00bf\x8b\xa5\x80\x00\u07d4\x05D\f[\a;R\x9bH) \x9d\xff\x88\t\x0e\a\xc4\xf6\xf5\x89E\u04977\xe2/ \x00\x00\u07d4\x05Z\xb6X\xc6\xf0\xedO\x87^\xd6t.K\xc7)-\x1a\xbb\xf0\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\u07d4\x05[\xd0,\xaf\x19\xd6 +\xbc\u0703m\x18{\xd1\xc0\x1c\xf2a\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x05^\xacO\x1a\xd3\xf5\x8f\v\xd0$\u058e\xa6\r\xbe\x01\u01af\xb3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x05fQU\xccI\xcb\xf6\xaa\xbd\u056e\x92\xcb\xfa\xad\x82\xb8\xc0\xc1\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x05f\x86\a\x8f\xb6\xbc\xf9\xba\n\x8a\x8d\xc6:\x90o_\xea\xc0\xea\x89\x1b\x18\x1eK\xf24<\x00\x00\u07d4\x05iks\x91k\xd3\x03>\x05R\x1e2\x11\xdf\xec\x02n\x98\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x05k\x15F\x89O\x9a\x85\xe2\x03\xfb3m\xb5i\xb1l%\xe0O\x89\t.\xdb\t\xff\b\u0600\x00\u07d4\x05yI\xe1\xca\x05pF\x9eL\xe3\u0190\xaea:k\x01\xc5Y\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x05}\u049f-\x19\xaa=\xa4#'\xeaP\xbc\xe8o\xf5\xc9\x11\u0649\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x05\u007f\u007f\x81\xcdz@o\xc4Y\x94@\x8bPI\x91,Vdc\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x05\x91]N\"Zf\x81b\xae\xe7\xd6\xc2_\xcf\xc6\xed\x18\xdb\x03\x89\x03\x98\xc3ry%\x9e\x00\x00\u07d4\x05\x96\xa2}\xc3\xee\x11_\xce/\x94\xb4\x81\xbc z\x9e&\x15%\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x05\xa80rC\x02\xbc\x0fn\xbd\xaa\x1e\xbe\xee\xb4nl\xe0\v9\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\x05\xae\u007f\u053b\u0300\xca\x11\xa9\n\x1e\u01e3\x01\xf7\xcc\u0303\u06c91T\xc9r\x9d\x05x\x00\x00\u07d4\x05\xbbd\xa9\x16\xbef\xf4`\xf5\xe3\xb6C2\x11\r \x9e\x19\xae\x89\u3bb5sr@\xa0\x00\x00\xe0\x94\x05\xbfO\xcf\xe7r\xe4[\x82dC\x85.l5\x13P\xcer\xa2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\x05\xc6@\x04\xa9\xa8&\xe9N^N\xe2g\xfa*v2\xddNo\x8a\x03m\xc4.\xbf\xf9\v\u007f\x80\x00\xe0\x94\x05\xc76\xd3e\xaa7\xb5\xc0\xbe\x9c\x12\u022d\\\xd9\x03\xc3,\xf9\x8a\x01E^{\x80\n\x86\x88\x00\x00\xe0\x94\x05\xcbl;\x00r\xd3\x11ga\xb52\xb2\x18D;S\xe8\xf6\u014a\x1e\x02\xc3\xd7\xfc\xa9\xb6(\x00\x00\u07d4\x05\xd0\xf4\xd7(\xeb\xe8.\x84\xbfYu\x15\xadA\xb6\v\xf2\x8b9\x89\u3bb5sr@\xa0\x00\x00\u07d4\x05\u058d\xada\u04fb\u07f3\xf7y&\\IGJ\xff?\xcd0\x89\x02\"\xc5]\xc1Q\x9d\x80\x00\u07d4\x05\xe6q\xdeU\xaf\xec\x96K\aM\xe5t\xd5\x15\x8d]!\xb0\xa3\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x05\xe9{\tI,\u058fc\xb1+\x89.\xd1\xd1\x1d\x15,\x0e\u02897\b\xba\xed=h\x90\x00\x00\u07d4\x05\xf3c\x1fVd\xbd\xad]\x012\xc88\x8d6\xd7\u0612\t\x18\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x06\t\xd8:l\xe1\xff\u0276\x90\xf3\xe9\xa8\x1e\x98>\x8b\xdcM\x9d\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\u07d4\x06\x1e\xa4\x87|\u0409D\xebd\u0096n\x9d\xb8\xde\xdc\xfe\xc0k\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x06%\xd0`V\x96\x8b\x00\"\x06\xff\x91\x98\x01@$+\xfa\xa4\x99\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x06(\xbf\xbeU5x/\xb5\x88@k\xc9f`\xa4\x9b\x01\x1a\xf5\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\x061\u044b\xbb\xbd0\xd9\xe1s+\xf3n\xda\xe2\u0389\x01\xab\x80\x89\xa3\xf9\x88U\xec9\x90\x00\x00\u07d4\x061\xdc@\xd7NP\x95\xe3r\x9e\xdd\xf4\x95D\xec\xd49og\x89\b\xacr0H\x9e\x80\x00\x00\xe0\x94\x067Y\xdd\x1cN6.\xb1\x93\x98\x95\x1f\xf9\xf8\xfa\xd1\xd3\x10h\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x06_\xf5u\xfd\x9c\x16\xd3\xcbo\u058f\xfc\x8fH?\xc3.\xc85\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x06a\x8e\x9dWb\xdfb\x02\x86\x01\xa8\x1dD\x87\u05a0\xec\xb8\x0e\x89Hz\x9a0E9D\x00\x00\xe0\x94\x06fG\xcf\xc8]#\xd3v\x05W= \x8c\xa1T\xb2D\xd7l\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x06xeJ\xc6v\x1d\xb9\x04\xa2\xf7\xe8Y^\xc1\xea\xacsC\b\x89/\x98\xb2\x9c(\x18\xf8\x00\x00\u07d4\x06\x86\n\x93RYU\xffbI@\xfa\xdc\xff\xb8\xe1I\xfdY\x9c\x89lh\xcc\u041b\x02,\x00\x00\xe0\x94\x06\x8c\xe8\xbdn\x90*E\u02c3\xb5\x15A\xb4\x0f9\xc4F\x97\x12\x8a\x01\x1c\x0f\x9b\xadJF\xe0\x00\x00\u07d4\x06\x8e)\xb3\xf1\x91\xc8\x12\xa699\x18\xf7\x1a\xb93\xaehG\xf2\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x06\x8eeWf\xb9D\xfb&6\x19e\x87@\xb8P\xc9J\xfa1\x89\x01\xe8\u007f\x85\x80\x9d\xc0\x00\x00\u0794\x06\x96N-\x17\xe9\x18\x9f\x88\xa8 96\xb4\n\xc9nS<\x06\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x06\x99L\xd8:\xa2d\n\x97\xb2`\vA3\x9d\x1e\r>\xdel\x89\r\x8drkqw\xa8\x00\x00\u07d4\x06\x9e\u042bz\xa7}\xe5q\xf1a\x06\x05\x1d\x92\xaf\xe1\x95\xf2\u0409\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x06\xac&\xad\x92\u02c5\x9b\u0550]\xdc\xe4&j\xa0\xecP\xa9\u0149*\x03I\x19\u07ff\xbc\x00\x00\u07d4\x06\xb0\xc1\xe3\u007fZ^\u013b\xf5\b@T\x8f\x9d:\xc0(\x88\x97\x89\xd8\u0602\u148e}\x00\x00\u07d4\x06\xb0\xff\x83@s\xcc\xe1\xcb\xc9\xeaU~\xa8{`Yc\u8d09\x10CV\x1a\x88)0\x00\x00\xe0\x94\x06\xb1\x06d\x9a\xa8\xc4!\xdd\xcd\x1b\x8c2\xcd\x04\x18\xcf0\xda\x1f\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x06\xb5\xed\xe6\xfd\xf1\xd6\xe9\xa3G!7\x9a\xea\xa1|q=\xd8*\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x06\xcb\xfa\b\xcd\xd4\xfb\xa77\xba\xc4\a\xbe\x82$\xf4\xee\xf3X(\x89 +\xe5\xe88.\x8b\x80\x00\u07d4\x06\xd6\xcb0\x84\x81\xc36\xa6\xe1\xa2%\xa9\x12\xf6\xe65Y@\xa1\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x06\xdc\u007f\x18\xce\xe7\xed\xab[yS7\xb1\xdfj\x9e\x8b\u062eY\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x06\xf6\x8d\xe3\xd79\xdbA\x12\x1e\xac\xf7y\xaa\xda=\xe8v!\a\x89\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\x06\xf7\u070d\x1b\x94b\xce\xf6\xfe\xb13h\xa7\xe3\x97K\t\u007f\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\a\x01\xf9\xf1G\xecHhV\xf5\xe1\xb7\x1d\xe9\xf1\x17\xe9\x9e!\x05\x89\te\xdaq\u007f\u0578\x00\x00\u07d4\a\r]6L\xb7\xbb\xf8\"\xfc,\xa9\x1a5\xbd\xd4A\xb2\x15\u0549lk\x93[\x8b\xbd@\x00\x00\xe0\x94\a\x1d\xd9\r\x14\xd4\x1fO\xf7\xc4\x13\xc2B8\xd35\x9c\xd6\x1a\a\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\u07d4\a&\xc4.\x00\xf4T\x04\x83n\xb1\xe2\x80\xd0s\xe7\x05\x96\x87\xf5\x89X\x00>?\xb9G\xa3\x80\x00\xe0\x94\a'\xbe\n*\x00! H\xb5R\x0f\xbe\xfb\x95>\xbc\x9dT\xa0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\a)\xa8\xa4\xa5\xba#\xf5y\xd0\x02[\x1a\xd0\xf8\xa0\xd3\\\xdf\u048a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4\a)\xb4\xb4|\t\xeb\x16\x15\x84d\u022a\u007f\xd9i\vC\x889\x89lh\xcc\u041b\x02,\x00\x00\u0794\a4\xa0\xa8\x1c\x95b\xf4\xd9\xe9\xe1\n\x85\x03\xda\x15\xdbF\xd7n\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\a\xa7\xef[G\x00\x00\xe0\x94\ap\xc6\x1b\xe7\x87r#\f\xb5\xa3\xbb$)\xa7&\x14\xa0\xb36\x8a\x01n\u0899\xb7\x13A\x80\x00\u07d4\ar><0\xe8\xb71\xeeEj)\x1e\xe0\u7630 Jw\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\as\xee\xac\xc0P\xf7G \xb4\xa1\xbdW\x89[\x1c\xce\xebI]\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\a\x80\r/\x80h\xe4H\u01daOi\xb1\xf1^\xf6\x82\xaa\xe5\xf6\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\a\xa8\xda\xde\xc1BW\x1a}S\xa4)pQxm\a,\xbaU\x89\x01;m\xa1\x13\x9b\u0680\x00\u07d4\a\xaf\x93\x8c\x127\xa2|\x900\tM\xcf$\aP$n=,\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\a\xb1\xa3\x06\xcbC\x12\xdffH,,\xaer\xd1\xe0a@\x0f\u034a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\a\xb7\xa5p3\xf8\xf1\x130\xe4f^\x18]#N\x83\xec\x14\v\x89\xea~\xe9*\f\x9a\v\x80\x00\u07d4\a\xbc,\xc8\xee\xdc\x01\x97\a\x00\xef\xc9\xc4\xfb6s^\x98\xcdq\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\a\xd4\x12\x17\xba\u0725\xe0\xe6\x03'\xd8E\xa3FO\x0f'\xf8J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\a\xd43N\u00c5\xe8\xaaT\xee\xda\xea\xdb0\x02/\f\u07e4\xab\x89\x8e\x91\xd5 \xf2\xeby\x00\x00\u07d4\a\xda\xe6\"c\r\x1168\x193\u04adk\"\xb89\xd8!\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\a\xdc+\xf8;\u01af\x19\xa8B\xff\xeaf\x1a\xf5\xb4\x1bg\xfd\xa1\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\a\u070c\x8b\x92z\xdb\xed\xfa\x8f]c\x9bCR5\x1f/6\u0489\x11\n\xed;U0\xdb\x00\x00\u07d4\a\xdd\xd0B,\x86\xefe\xbf\f\u007f\xc3E(b\xb1\"\x8b\b\xb8\x89o\xf5\u04aa\x8f\x9f\xcf\x00\x00\u07d4\a\xe1\x16,\xea\xe3\xcf!\xa3\xf6-\x10Y\x900.0\u007fN;\x89R\xf1\x03\xed\xb6k\xa8\x00\x00\u07d4\a\xe2\xb4\xcd\xee\xd9\u0407\xb1.Um\x9ew\f\x13\xc0\x99a_\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\a\xfe\xefT\xc16\x85\b)\xba\xdcKI\xc3\xf2\xa7<\x89\xfb\x9e\x89\x06hZ\xc1\xbf\xe3,\x00\x00\u07d4\b\x05FP\x8a=&\x82\u0239\x88O\x13c{\x88G\xb4M\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\t\bv\xba\xad\xfe\xe6\\=6;\xa5S\x12t\x8c\xfa\x87=\x89\\*\x997\x1c\xff\xe1\x00\x00\u07d4\b\x16o\x021?\xea\u12f0D\xe7\x87|\x80\x8bU\xb5\xbfX\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\b)\xd0\xf7\xbb|Dl\xfb\xb0\u07ad\xb29M\x9d\xb7$\x9a\x87\x89\x02,\xa3X|\xf4\xeb\x00\x00\u07d4\b0m\xe5\x19\x81\u7b21\x85hY\xb7\xc7xijki\xf9\x89\xadx\xeb\u016cb\x00\x00\x00\xe0\x94\b7S\x9b_jR*H,\xdc\u04e9\xbbpC\xaf9\xbd\u048a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\b8\xa7v\x8d\x9c*\u028b\xa2y\xad\xfe\xe4\xb1\xf4\x91\xe3&\xf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\bA\x16R\xc8qq6\t\xaf\x00b\xa8\xa1(\x1b\xf1\xbb\xcf\u0649K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\bM\x102Tu\x9b4<\xb2\xb9\xc2\xd8\xff\x9e\x1a\xc5\xf1E\x96\x8a\x01\x9b\xff/\xf5yh\xc0\x00\x00\u07d4\bPO\x05d?\xabY\x19\xf5\xee\xa5Y%\u05e3\xed}\x80z\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\b[J\xb7]\x83b\xd9\x14C\\\xed\xee\x1d\xaa+\x1e\xe1\xa2;\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\b[\xa6_\xeb\xe2>\xef\xc2\xc8\x02fj\xb1&#\x82\xcf\u0114\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\bt\x98\xc0FFh\xf3\x11P\xf4\xd3\u013c\u0765\"\x1b\xa1\x02\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\bw\uebabx\xd5\xc0\x0e\x83\xc3+-\x98\xfay\xadQH/\x89\x17\xd2-q\xdab&\x00\x00\u0794\b\x93j7\u07c5\xb3\xa1X\xca\xfd\x9d\xe0!\xf5\x817h\x13G\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\b\xa9\xa4N\x1fA\xde=\xbb\xa7\xa3c\xa3\xabA,\x12L\xd1^\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\b\xb7\xbd\u03d4MUp\x83\x8b\xe7\x04`$:\x86\x94HXX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xb8E6\xb7L\x8c\x01T=\xa8\x8b\x84\u05cb\xb9WG\xd8\"\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\b\xc2\xf26\xacJ\xdc\xd3\xfd\xa9\xfb\xc6\xe4S\"S\xf9\xda;\xec\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\b\xc8\x02\xf8wX4\x9f\xa0>k\xc2\xe2\xfd\a\x91\x19~\ua689lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xc9\U0007fd89\xfd\xf8\x04\xd7i\xf8!#6\x02\x15\xaf\xf9;\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\b\xca\u0215&A\xd8\xfcRn\xc1\xabO-\xf8&\xa5\xe7q\x0f\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\b\xcc\xdaP\xe4\xb2j\x0f\xfc\x0e\xf9.\x92\x051\a\x06\xbe\xc2\u01ca\x01Iul8W\xc6\x00\x00\x00\u07d4\b\u0406M\xc3/\x9a\xcb6\xbfN\xa4G\xe8\xddg&\x90j\x15\x89lnY\xe6|xT\x00\x00\u07d4\b\xd4&\u007f\xeb\x15\u0697\x00\xf7\xcc\xc3\xc8J\x89\x18\xbf\x17\xcf\u0789a\t=|,m8\x00\x00\xe0\x94\b\xd41\x1c\x9c\x1b\xba\xf8\u007f\xab\xe1\xa1\xd0\x14c\x82\x8d]\x98\u038a\x13\x0e\xe8\xe7\x17\x90D@\x00\x00\u07d4\b\xd5N\x83\xadHj\x93L\xfa\xea\u20e3>\xfd\"|\x0e\x99\x898S\x05\x83$^\xdc\x00\x00\u07d4\b\xd9~\xad\xfc\xb7\xb0d\xe1\xcc\xd9\u0217\x9f\xbe\xe5\xe7z\x97\x19\x89\x0el]\xa8\xd6z\xc1\x80\x00\u07d4\b\xda:z\x0fE!a\u03fc\xec1\x1b\xb6\x8e\xbf\xde\xe1~\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xe3\x8e\xe0\xceH\xc9\xcad\\\x10\x19\xf7;SUX\x1cV\xe6\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\b\xef?\xa4\xc4<\xcd\xc5{\"\xa4\xb9\xb23\x1a\x82\xe58\x18\xf2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\t\td\x8c\x18\xa3\xce[\xaez\x04~\xc2\xf8h\xd2L\u0768\x1d\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\t\f\xd6{`\xe8\x1dT\xe7\xb5\xf6\a\x8f>\x02\x1b\xa6[\x9a\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\f\xeb\xef),>\xb0\x81\xa0_\u062a\xf7\u04db\xf0{\x89\u0509\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\t\x0f\xa96{\xdaW\xd0\xd3%:\n\x8f\xf7l\xe0\xb8\xe1\x9as\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\x14n\xa3\x88Qv\xf0w\x82\xe1\xfe0\xdc\xe3\xce$\u011e\x1f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\t!`_\x99\x16N;\xcc(\xf3\x1c\xae\xcex\x971\x82V\x1d\x89+\ai*\x90e\xa8\x00\x00\xe0\x94\t&\x1f\x9a\xcbE\x1c7\x88\x84O\f\x14Q\xa3[\xadP\x98\xe3\x8a\x01\u056d'P) `\x00\x00\xe0\x94\t'\"\x04\x92\x19K.\u069f\u013b\xe3\x8f%\u0581\xdf\xd3l\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\t*\xcbbK\b\xc0U\x10\x18\x9b\xbb\xe2\x1ee$\xd6D\u032d\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\t.\x81UX@-g\xf9\rk\xfem\xa0\xb2\xff\xfa\x91EZ\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\tP0\xe4\xb8&\x92\xdc\xf8\xb8\u0411$\x94\xb9\xb3x\xec\x93(\x89H\xa4zu\x80\x00\u07d4\t\x89\xc2\x00D\v\x87\x89\x91\xb6\x9d`\x95\xdf\xe6\x9e3\xa2.p\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\t\x90\xe8\x1c\u05c5Y\x9e\xa26\xbd\x19f\xcfRc\x02\xc3[\x9c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\x98\xd8'1\x15\xb5j\xf4%\xff\xc8>!\x8c\x1e\n\xfe\x89(\u01c8\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\t\xaeI\xe3\u007f\x12\x1d\xf5\xdc\x15\x8c\xfd\xe8\x06\xf1s\xa0k\f\u007f\x89\xd80\x9e&\xab\xa1\xd0\x00\x00\u07d4\t\xaf\xa7;\xc0G\xefF\xb9w\xfd\x97c\xf8r\x86\xa6\xbeh\u0189\x1b/\xb5\xe8\xf0jf\x00\x00\u07d4\t\xb4f\x86\x96\xf8j\b\x0f\x8b\xeb\xb9\x1d\xb8\xe6\xf8p\x15\x91Z\x89#\x8f\xf7\xb3O`\x01\x00\x00\xe0\x94\t\xb5\x9b\x86\x98\xa7\xfb\xd3\xd2\xf8\xc7:\x00\x89\x88\xde>@k+\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94\t\xb7\xa9\x88\xd1?\xf8\x91\x86so\x03\xfd\xf4au\xb5=\x16\xe0\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\t\xc1w\xf1\xaeD$\x11\u076c\xf1\x87\xd4m\xb9V\x14\x83`\xe7\x8a\x01\xe5.3l\xde\"\x18\x00\x00\xe0\x94\t\u020f\x91~Mj\xd4s\xfa\x12\u93a3\xc4G*^\xd6\u068a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\t\u0438\xcd\a|i\xd9\xf3-\x9c\xcaC\xb3\xc2\b\xa2\x1e\u050b\x89\b!\xd2!\xb5)\x1f\x80\x00\xe0\x94\t\xd6\xce\xfdu\xb0\u0133\xf8\xf1\u0587\xa5\"\xc9a#\xf1\xf59\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\t\xe47\xd4H\x86\x12(\xa22\xb6.\xe8\xd3ye\xa9\x04\ud70a\x04\x98\xcf@\x1d\xf8\x84.\x80\x00\u07d4\t\xee\x12\xb1\xb4+\x05\xaf\x9c\xf2\a\xd5\xfc\xac%[.\xc4\x11\xf2\x89\x031\xcd\xddG\xe0\xfe\x80\x00\u07d4\t\xf3\xf6\x01\xf6\x05D\x11@Xl\xe0eo\xa2J\xa5\xb1\u066e\x89Sswo\xe8\xc4T\x00\x00\u07d4\t\xf9W[\xe5}\x00G\x93\u01e4\ub137\x15\x87\xf9|\xbbj\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n\x06P\x86\x1fx^\xd8\xe4\xbf\x10\x05\xc4P\xbb\xd0n\xb4\x8f\xb6\x89\xa6A;y\x14N~\x00\x00\u07d4\n\x06\xfa\xd7\xdc\u05e4\x92\xcb\xc0S\xee\xab\xdei4\xb3\x9d\x867\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\n\a}\xb1?\xfe\xb0\x94\x84\xc2\x17p\x9dX\x86\xb8\xbf\x9cZ\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\n\x0e\u0366cow\x16\xef\x19saF\x87\xfd\x89\xa8 \xa7\x06\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\n)\xa8\xa4\xd5\xfd\x95\x00u\xff\xb3Mw\xaf\xeb-\x82;\u0589\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n*\u0795\xb2\xe8\xc6m\x8a\xe6\xf0\xbad\xcaW\u05c3\xbemD\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\n+O\xc5\xd8\x1a\xceg\xdcK\xba\x03\xf7\xb4UA=F\xfe=\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\n-\xcbzg\x17\x01\u06f8\xf4\x95r\x80\x88&Xs5l\x8e\x89\b?\x16\xce\b\xa0l\x00\x00\u07d4\n=\xe1U\xd5\xec\xd8\xe8\x1c\x1f\xf9\xbb\xf07\x83\x01\xf8\xd4\xc6#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\nG\xad\x90Y\xa2I\xfc\x93k&b5=\xa6\x90_u\u00b9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\nH)ov1p\x8c\x95\u04b7Iu\xbcJ\xb8\x8a\xc19*\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\nJ\x01\x19\x95\u0181\xbc\x99\x9f\xddyuN\x9a2J\xe3\xb3y\x8a\b\xc1\x9a\xb0n\xb8\x9a\xf6\x00\x00\u07d4\nX\xfd\xddq\x89\x8d\xe7s\xa7O\xda\xe4^{\xd8N\xf46F\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\n[y\xd8\xf2;d\x83\xdb\u2f6ab\xb1\x06L\xc7cf\xae\x89j\u0202\x10\tR\u01c0\x00\u07d4\ne.*\x8bw\xbd\x97\xa7\x90\xd0\xe9\x13a\u0248\x90\u06f0N\x8965\u026d\xc5\u07a0\x00\x00\u07d4\nn\xber;n\xd1\xf9\xa8ji\xdd\xdah\xdcGF\\+\x1b\x89@=-\xb5\x99\xd5\xe4\x00\x00\u07d4\nw\xe7\xf7+C{WO\x00\x12\x8b!\xf2\xac&Q3R\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\n\x91\u007f;\\\xb0\xb8\x83\x04\u007f\u0676Y=\xbc\xd5W\xf4S\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\n\x93\x1bD\x9e\xa8\xf1,\xdb\xd5\xe2\xc8\xccv\xba\xd2\xc2|\x069\x89\x01?\x9e\x8cy\xfe\x05\x80\x00\u0794\n\x98\x04\x13x\x03\xbahh\xd9:U\xf9\x98_\xcdT\x04Q\u4239\x8b\xc8)\xa6\xf9\x00\x00\u07d4\n\x9a\xb2c\x8b\x1c\xfdeM%\u06b0\x18\xa0\xae\xbd\u07c5\xfdU\x89\x01.\x8c\xb5\xfeLJ\x80\x00\u07d4\n\xb3f\xe6\xe7\u056b\xbc\xe6\xb4JC\x8di\xa1\u02bb\x90\xd13\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\n\xb4(\x1e\xbb1\x85\x90\xab\xb8\x9a\x81\xdf\a\xfa:\xf9\x04%\x8a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\n\xb5\x9d9\a\x02\xc9\xc0Y\xdb\x14\x8e\xb4\xf3\xfc\xfa}\x04\xc7\xe7\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\n\xbf\xb3\x9b\x11HmyW(f\x19[\xa2lc\vg\x84\u06ca\x19\xba\x877\xf9i(\xf0\x00\x00\u07d4\n\u029aV&\x91;\b\xcf\u0266m@P\x8d\xceR\xb6\x0f\x87\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\n\xd3\xe4M<\x00\x1f\xa2\x90\xb3\x93ap0TA\b\xacn\xb9\x89j\xbd\xa0\xbc0\xb2\u07c0\x00\u07d4\n\xec.Bn\xd6\xcc\f\xf3\xc2I\xc1\x89~\xacG\xa7\xfa\xa9\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n\xf6_\x14xNU\xa6\xf9Vg\xfds%*\x1c\x94\a-*\x89\nv;\x8e\x02\xd4O\x80\x00\u07d4\n\xf6\xc8\xd59\xc9mP%\x9e\x1b\xa6q\x9e\x9c\x80`\xf3\x88\u008965\u026d\xc5\u07a0\x00\x00\u07d4\v\x069\x0f$7\xb2\x0e\u0123\xd3C\x1b2y\xc6X>^\u05c9\n\x84Jt$\xd9\xc8\x00\x00\u07d4\v\v8b\x11*\xee\u00e04\x92\xb1\xb0_D\x0e\xcaT%n\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\v\x0e\x05[(\xcb\xd0=\xc5\xffD\xaad\xf3\xdc\xe0O^c\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\x11\x9d\xf9\x9ck\x8d\xe5\x8a\x1e,?)zgD\xbfU\"w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\x14\x89\x19\x99\xa6\\\x9e\xf73\b\xef\xe3\x10\f\xa1\xb2\x0e\x81\x92\x89+^:\xf1k\x18\x80\x00\x00\u07d4\v!\x13PE4d*\x1d\xaf\x10.\xee\x10\xb9\xeb\xdev\xe2a\x89\x94,\xdd|\x95\xf2\xbd\x80\x00\xe0\x94\v(\x8aZ\x8bu\xf3\xdcA\x91\xeb\x04W\xe1\xc8=\xbd M%\x8a\x01\a\x14\xe7{\xb4:\xb4\x00\x00\u07d4\v6\x9e\x00.\x1bLy\x13\xfc\xf0\x0f-^\x19\u0141eG\x8f\x89\x03\u007fe\x16(\x8c4\x00\x00\u07d4\vC\xbd#\x91\x02U\x81\u0615l\xe4*\a%y\u02ff\xcb\x14\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u07d4\vP|\xf5SV\x8d\xaa\xf6U\x04\xaeN\xaa\x17\xa8\xea<\xdb\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v]f\xb1<\x87\xb3\x92\xe9M\x91\xd5\xf7l\rE\nU(C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v^ \x11\xeb\xc2Z\x00\u007f!6)`I\x8a\xfb\x8a\xf2\x80\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vd\x9d\xa3\xb9j\x10,\xdcm\xb6R\xa0\xc0}e\xb1\xe4C\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vi \xa6K6;\x8d]\x90\x80$\x94\xcfVKT|C\r\x89A\rXj \xa4\xc0\x00\x00\u07d4\vp\x11\x01\xa4\x10\x9f\x9c\xb3`\xdcW\xb7tBg=^Y\x83\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vq\xf5T\x12$i\uf5ce/\x1f\xef\xd7\u02f4\x10\x98'r\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\xe0\x94\v{\xb3B\xf0\x1b\u0248\x8ej\x9a\xf4\xa8\x87\xcb\xf4\xc2\xdd,\xaf\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\v}3\x93q\xe5\xbeg'\xe6\xe31\xb5\x82\x1f\xa2K\u06ddZ\x89.\u007f\x81\x86\x82b\x01\x00\x00\u07d4\v\u007f\xc9\xdd\xf7\x05v\xf63\x06i\xea\xaaq\xb6\xa81\xe9\x95(\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\v\x80\xfcp(,\xbd\xd5\xfd\xe3[\xf7\x89\x84\xdb;\xdb\x12\x01\x88\x8968\x02\x1c\xec\u06b0\x00\x00\u07d4\v\x92M\xf0\a\xe9\xc0\x87\x84\x17\xcf\xe6;\x97n\xa1\xa3\x82\xa8\x97\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\v\x93\xfc\xa4\xa4\xf0\x9c\xac \xdb`\xe0e\xed\xcc\xcc\x11\u0976\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\v\x9d\xf8\x0f\xbe# \t\xda\xcf\n\xa8\xca\u0153v\xe2Gb\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xa6\xe4j\xf2Z\x13\xf5qi%Z4\xa4\xda\xc7\xce\x12\xbe\x04\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\v\xa8p[\xf5\\\xf2\x19\xc0\x95k^?\xc0\x1cDt\xa6\xcd\xc1\x89\x05%\xe0Y]Mk\x80\x00\u07d4\v\xafn\u0379\x1a\xcb6\x06\xa85|\v\xc4\xf4\\\xfd-~o\x8965\u026d\xc5\u07a0\x00\x00\u07d4\v\xb0_r$\xbbX\x04\x85eV\xc0~\xea\xdb\ud1fa\x8f|\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\v\xb0\xc1&\x82\xa2\xf1\\\x9bWA\xb28\\\xbeA\xf04\x06\x8e\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\v\xb2\\\xa7\u0448\xe7\x1eMi={\x17\a\x17\xd6\xf8\xf0\xa7\n\x89\x12C\x02\xa8/\xad\xd7\x00\x00\u07d4\v\xb2e\x0e\xa0\x1a\xcau[\xc0\xc0\x17\xb6K\x1a\xb5\xa6m\x82\xe3\x89Hz\x9a0E9D\x00\x00\u07d4\v\xb5Lr\xfdf\x10\xbf\xa463\x97\xe0 8K\x02+\fI\x89Hz\x9a0E9D\x00\x00\u07d4\v\xb7\x16\n\xba)7b\xf8sO>\x03&\xff\u0264\xca\xc1\x90\x8965\u026d\xc5\u07a0\x00\x00\u07d4\v\xc9\\\xb3-\xbbWL\x83/\xa8\x17J\x815m8\xbc\x92\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xd6}\xbd\xe0z\x85n\xbd\x89;^\xdcO:[\xe4 &\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xdb\xc5L\u023d\xbb\xb4\x02\xa0\x89\x11\xe2#*T`\u0386k\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\v\xddX\xb9n|\x91m\xd2\xfb05o*\xeb\xfa\xaf\x1d\x860\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\v\u1f39\x03C\xfa\xe501s\xf4a\xbd\x91JH9\x05l\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\v\xe1\xfd\xf6&\xeea\x89\x10-p\xd1;1\x01,\x95\xcd\x1c\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xe2\xb9J\xd9P\xa2\xa6&@\xc3[\xfc\xcdlg\xda\xe4P\xf6\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\v\u681eC\a\xfeH\xd4\x12\xb8\u0461\xa8(M\xceHba\x8a\x04\x0f\xbf\xf8\\\x0180\x00\x00\u07d4\v\xef\xb5G\a\xf6\x1b,\x9f\xb0G\x15\xab\x02n\x1b\xb7 B\xbd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\v\xf0dB\x8f\x83bg\"\xa7\xb5\xb2j\x9a\xb2\x04!\xa7r>\x89\a?u\u0460\x85\xba\x00\x00\u07d4\v\xfb\xb6\x92]\xc7^R\xcf&\x84\"K\xbe\x05P\xfe\xa6\x85\u04c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\f\b\x80\x06\xc6K0\xc4\u076f\xbc6\xcb_\x05F\x9e\xb6(4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f s\xbaD\xd3\u077d\xb69\xc0N\x19\x109\xa7\x17\x16#\u007f\x89M\x85<\x8f\x89\b\x98\x00\x00\xe0\x94\f\",|A\u0270H\xef\xcc\xe0\xa22CCb\xe1-g;\x8a\x02\x1e\x83Yivw8\x00\x00\xe0\x94\f(\b\xb9Q\ud787-{2y\x0f\xccY\x94\xaeA\xff\u070a\x15\x99n[<\u05b3\xc0\x00\x00\u07d4\f(\x84~O\t\xdf\xce_\x9b%\xaf|NS\x0fY\u0200\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f-\\\x92\x058\xe9S\u02af$\xf0s\u007fUL\u0192wB\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f0\xca\xcc?r&\x9f\x8bO\x04\xcf\a=+\x05\xa8=\x9a\u0449lyt\x12?d\xa4\x00\x00\u07d4\f29\xe2\xe8A$-\xb9\x89\xa6\x15\x18\xc2\"G\xe8\xc5R\b\x89\x0eJ\xf6G\x174d\x00\x00\xe0\x94\fH\r\xe9\xf7F\x10\x02\x90\x8bI\xf6\x0f\xc6\x1e+b\xd3\x14\v\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\fH\xaeb\xd1S\x97\x88\xeb\xa0\x13\xd7^\xa6\vd\xee\xbaN\x80\x89w\xfb\xdcC\xe00\x99\x80\x00\u07d4\fU\x89\xa7\xa8\x9b\x9a\xd1[\x02u\x190AYH\xa8u\xfb\xef\x89\x06\u0519\xeclc8\x00\x00\u07d4\fg\x03=\xd8\xee\u007f\f\x8a\xe54\xd4*Q\xf7\xd9\xd4\xf7\x97\x8f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\fhE\xbfA\xd5\xee'<>\u6d70\u059fo\xd5\xea\xbb\xf7\x89\xa2\xa1\xb9h.X\t\x00\x00\xe0\x94\f\u007f\x86\x9f\x8e\x90\xd5?\xdc\x03\u8c81\x9b\x01k\x9d\x18\xeb&\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\f\x86\x92\xee\xff*S\xd6\xd1h\x8e\xd5j\x9d\u06fdh\u06bb\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f\x8ff\xc6\x01{\xce[ 4r\x04\xb6\x02\xb7C\xba\u05cd`\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\f\x8f\xd7w^T\xa6\xd9\u0263\xbf\x89\x0ev\x1fewi?\xf0\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\f\x92Z\xd5\xeb5,\x8e\xf7m\f\"-\x11[\a\x91\xb9b\xa1\x89\xacc]\u007f\xa3N0\x00\x00\u07d4\f\x96~0a\xb8zu>\x84P~\xb6\t\x86x,\x8f0\x13\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\f\xa1*\xb0\xb9fl\xf0\xce\xc6g\x1a\x15)/&SGj\xb2\x8a,x'\xc4-\"\xd0|\x00\x00\u07d4\f\xa6p\xeb,\x8b\x96\u02e3y!\u007fY)\u00b8\x92\xf3\x9e\xf6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f\xae\x10\x8em\xb9\x9b\x9ecxv\xb0d\xc60>\u068ae\u0209\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\f\xbd\x92\x1d\xbe\x12\x15c\xb9\x8ahq\xfe\xcb\x14\xf1\xcc~\x88\u05c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\f\xbf\x87p\xf0\xd1\b.\\ \u016e\xad4\xe5\xfc\xa9\xaez\xe2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f\xc6\u007f\x82s\xe1\xba\xe0\x86\u007f\xd4.\x8b\x81\x93\xd7&y\xdb\xf8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\f\u05a1A\x91\x8d\x12k\x10m\x9f.\xbfi\xe1\x02\xdeM2w\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\f\xda\x12\xbfr\xd4a\xbb\xc4y\xeb\x92\xe6I\x1d\x05~kZ\u044a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\f\u0716\v\x99\x8c\x14\x19\x98\x16\r\xc1y\xb3l\x15\u0484p\xed\x89\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\f\xfb\x17#5\xb1l\x87\xd5\x19\xcd\x14uS\r W\u007f^\x0e\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\r\x1f*Wq>\xbcn\x94\xde)\x84n\x88D\xd3vfWc\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\r2e\xd3\u7f79=^\x8e\x8b\x1c\xa4\u007f!\ny>\u030e\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\r5@\x8f\"ef\x11o\xb8\xac\u06a9\xe2\xc9\u055bvh?\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\rU\x1e\xc1\xa2\x13<\x98\x1d_\u01a8\xc8\x17?\x9e|OG\xaf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r]\x98V\\d|\xa5\xf1w\xa2\xad\xb9\xd3\x02/\xac(\u007f!\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\re\x80\x14\xa1\x99\x06\x1c\xf6\xb3\x943\x14\x03\x03\xc2\x0f\xfdNZ\x8a\x01\xbc\x85\xdc*\x89\xbb \x00\x00\u07d4\rg\x87\x06\xd07\x18\u007f>\"\xe6\xf6\x9b\x99\xa5\x92\xd1\x1e\xbcY\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\ri\x10\f9\\\xe6\xc5\xea\xad\xf9]\x05\xd8r\x83~\xde\xdd!\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\rt~\u559b\xf7\x9dW8\x1do\xe3\xa2@l\xd0\xd8\xce'\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\r\x80#\x92\x9d\x91r4\xae@Q+\x1a\xab\xb5\xe8\xa4Q'q\x89\b\x05\xe9\x9f\xdc\xc5\xd0\x00\x00\xe0\x94\r\x8a\xab\x8ft\xea\x86,\xdfvh\x05\x00\x9d?>B\xd8\xd0\v\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\r\x8c@\xa7\x9e\x18\x99O\xf9\x9e\xc2Q\xee\x10\u0408\u00d1.\x80\x89\x066d\xfc\u04bb\xc4\x00\x00\u07d4\r\x8e\xd7\xd0\xd1V83\x0e\xd7\xe4\xea\u032b\x8aE\x8dus~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r\x92X/\u06e0^\xab\xc3\xe5\x158\xc5m\xb8\x817\x85\xb3(\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\r\x94C\xa7\x94h\xa5\xbb\xf7\xc1\xe5\xb9\x15\xb3d\x87\xf9\x16\x1f\x19\x84m\x10\x1431\x8a\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\r\xbdA|7+\x8b\r\x01\xbc\xd9Dpk\xd3.`\xae(\u0449\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\r\xc1\x00\xb1\a\x01\x1c\u007f\xc0\xa13\x96\x12\xa1l\xce\xc3(R\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r\u03dd\x8c\x98\x04E\x9fd|\x14\x13\x8e\xd5\x0f\xadV;AT\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4\r\xcf\xe87\xea\x1c\xf2\x8ce\xfc\xce\u00fe\xf1\xf8NY\xd1P\xc0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\r\xd4\xe6t\xbb\xad\xb1\xb0\u0702D\x98q=\xce;QV\xda)\x89\t79SM(h\x00\x00\u07d4\r\xfb\u0501pP\xd9\x1d\x9db\\\x02\x05<\xf6\x1a>\xe2\x85r\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\x0e\x02N\u007f\x02\x9cj\xaf:\x8b\x91\x0f^\b\bs\xb8W\x95\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0e\tdl\x99\xafC\x8e\x99\xfa'L\xb2\xf9\xc8V\xcbe\xf76\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x0e\f\x9d\x00^\xa0\x16\u0095\xcdy\\\xc9!>\x87\xfe\xbc3\xeb\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\x0e\rf3\xdb\x1e\f\u007f#Jm\xf1c\xa1\x0e\n\xb3\x9c \x0f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x0e\x11\xd7z\x89w\xfa\xc3\r&\x84E\xe51\x14\x9b1T\x1a$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\x12=}\xa6\xd1\xe6\xfa\xc2\u072d\xd2p)$\v\xb3\x90R\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0e\x18\x01\xe7\vbb\x86\x1b\x114\u033c9\x1fV\x8a\xfc\x92\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x0e \x94\xac\x16T\xa4k\xa1\xc4\u04e4\v\xb8\xc1}\xa7\U000d6209\x13h?\u007f<\x15\xd8\x00\x00\u07d4\x0e!\xaf\x1b\x8d\xbf'\xfc\xf6?7\xe0G\xb8z\x82\\\xbe|'\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x0e.PJ-\x11\"\xb5\xa9\xfe\xee\\\xb1E\x1b\xf4\u00ac\xe8{\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x0e/\x8e(\xa6\x81\xf7|X;\xd0\xec\xde\x16cK\xdd~\x00\u0349\x05'8\xf6Y\xbc\xa2\x00\x00\u07d4\x0e2\x02\x19\x83\x8e\x85\x9b/\x9f\x18\xb7.=@s\xcaP\xb3}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e3\xfc\xbb\xc0\x03Q\v\xe3W\x85\xb5*\x9c]!k\xc0\x05\xf4\x89e\xea=\xb7UF`\x00\x00\u07d4\x0e6\x96\xcf\x1fB\x17\xb1c\u047c\x12\xa5\xeas\x0f\x1c2\xa1J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x0e9\x0fD\x05=\xdf\xce\xf0\xd6\b\xb3^M\x9c,\xbe\x98q\xbb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x0e:(\xc1\u07ef\xb0P[\xdc\xe1\x9f\xe0%\xf5\x06\xa6\xd0\x1c\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e=\xd7\xd4\xe4)\xfe90\xa6A@5\xf5+\xdcY\x9dxM\x89\x02,\xa3X|\xf4\xeb\x00\x00\u07d4\x0eGey\x03Rek\xc6Vh,$\xfc^\xf3\xe7j#\u01c9\x02\x86\xd7\xfc\f\xb4\xf5\x00\x00\u07d4\x0eI\x88\x00Dqw\xb8\u022f\xc3\xfd\xfa\u007fi\xf4\x05\x1b\xb6)\x89t\x05\xb6\x9b\x8d\xe5a\x00\x00\u07d4\x0ek\xaa\xa3\u07b9\x89\xf2\x89b\x00vf\x86\x18\xe9\xac3(e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x0el\xd6d\xad\x9c\x1e\xd6K\xf9\x87I\xf4\x06D\xb6&\xe3y,\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\xe0\x94\x0em\xfdU;.\x87=*\xec\x15\xbd_\xbb?\x84r\xd8\u04d4\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\x0en\xc3\x137bq\xdf\xf5T#\xabT\"\xcc:\x8b\x06\xb2+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x0en\u0399\x11\x1c\xad\x19a\xc7H\xed=\xf5\x1e\xddi\u04a3\xb1\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x0e\x83\xb8PH\x1a\xb4MI\xe0\xa2)\xa2\xe4d\x90,iS\x9b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0e\x89\xed\xdd?\xa0\xd7\x1d\x8a\xb0\xff\x8d\xa5X\x06\x86\xe3\xd4\xf7O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\x90\x96\xd3C\xc0`\xdbX\x1a\x12\x01\x12\xb2x`~\xc6\xe5+\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x0e\x9cQ\x18d\xa1w\xf4\x9b\xe7\x82\x02w?`H\x9f\xe0NR\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x0e\xa2\xa2\x101+>\x86~\xe0\xd1\xcch,\xe1\xd6f\xf1\x8e\u054a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x0e\xb1\x89\xef,-Wb\xa9c\u05b7\xbd\xf9i\x8e\xa8\u7d0a\x89Hz\x9a0E9D\x00\x00\xe0\x94\x0e\xb5\xb6b\xa1\xc7\x18`\x8f\xd5/\f%\xf97\x880\x17\x85\x19\x8a\x01J7(\x1aa.t\x00\x00\xe0\x94\x0e\xc4f\x96\xff\xac\x1fX\x00_\xa8C\x98$\xf0\x8e\xed\x1d\xf8\x9b\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x0e\xc5\n\xa8#\xf4e\xb9FK\v\xc0\u0125w$\xa5U\xf5\u058a\f\x83\xd1Bj\u01f1\xf0\x00\x00\u07d4\x0e\xc50\x8b1(.!\x8f\xc9\xe7Y\xd4\xfe\xc5\xdb7\b\xce\u01096C\xaady\x86\x04\x00\x00\u07d4\x0e\xcc\xf6\x17\x84O\xd6\x1f\xbab\xcb\x0eD[z\u018b\xcc\x1f\xbe\x89\x14\xfeO\xe65e\xc6\x00\x00\u07d4\x0e\u04fb:N\xb5T\xcf\u0297\x94}WU\a\xcd\xfdm!\u0609\x1d\xb3 _\xcc#\u0540\x00\u07d4\x0e\xd7l,;]P\xff\x8f\xb5\v>\xea\xcdh\x15\x90\xbe\x1c-\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0e\u0680\xf4\xed\aJ\xeaiz\xed\xdf(;c\xdb\xca=\xc4\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\xddKX\x0f\xf1\x0f\xe0lJ\x03\x11b9\xef\x96b+\xae5\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x0e\xe3\x91\xf0^\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x0f\x92\x9c\xf8\x95\xdb\x01z\xf7\x9f>\xad\"\x16\xb1\xbdi\xc3}\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f\xa0\x10\xce\fs\x1d;b\x8e6\xb9\x1fW\x13\x00\u477e\xab\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\x0f\xa5\xd8\u0173\xf2\x94\xef\u0515\xabi\xd7h\xf8\x18rP\x85H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f\xa6\u01f0\x97=\v\xae)@T\x0e$}6'\xe3|\xa3G\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0f\xad\x05P|\u070f$\xb2\xbeL\xb7\xfa]\x92}\u06d1\x1b\x88\x89\xa2\xdf\x13\xf4A\xf0\t\x80\x00\u07d4\x0f\xb5\xd2\xc6s\xbf\xb1\xdd\xca\x14\x1b\x98\x94\xfdm?\x05\xdag \x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0f\u0260\xe3AE\xfb\xfd\xd2\xc9\u04a4\x99\xb6\x17\u05e0)i\xb9\x89\t\xc2\x00vQ\xb2P\x00\x00\xe0\x94\x0f\xcf\xc4\x06P\b\xcf\xd3#0_b\x86\xb5zM\xd7\xee\xe2;\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x0f\xdde@#\x95\u07db\u045f\xeeE\a\xefSE\xf7E\x10L\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x0f\xecN\xe0\xd7\xca\x18\x02\x90\xb6\xbd \xf9\x99#B\xf6\x0f\xf6\x8d\x89\x12 \u007f\x0e\xdc\xe9q\x80\x00\u07d4\x0f\ue06c3\x1e\xfd\x8f\x81\x16\x1cW8+\xb4P{\xb9\xeb\xec\x89\x15\xaf\x88\r\x8c\u06c3\x00\x00\u07d4\x0f\xfe\xa0mq\x13\xfbj\xec(i\xf4\xa9\u07f0\x90\a\xfa\xce\xf4\x89\f8F\x81\xb1\xe1t\x00\x00\u07d4\x10\tq\x98\xb4\xe7\xee\x91\xff\x82\xcc/;\xd9_\xeds\xc5@\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x10\vM\tw\xfc\xba\xd4\u07bd^d\xa0Iz\xea\xe5\x16\x8f\xab\x89\x11\f\x90s\xb5$Z\x00\x00\xe0\x94\x10\x1a\nd\xf9\xaf\xccD\x8a\x8a\x13\rM\xfc\xbe\xe8\x957\xd8T\x8a\x037\xfe_\xea\xf2\u0440\x00\x00\u07d4\x10,G}i\xaa\u06e9\xa0\xb0\xf6+tY\xe1\u007f\xbb\x1c\x15a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x101\xe0\xec\xb5I\x85\xae!\xaf\x17\x93\x95\r\xc8\x11\x88\x8f\xde|\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x104d\x14\xbe\xc6\xd3\xdc\xc4NP\xe5MT\u00b8\xc3sN>\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x108\x98X\xb8\x00\xe8\xc0\xec2\xf5\x1e\xd6\x1a5YF\xcc@\x9b\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x10Y\xcb\xc6>6\xc4>\x88\xf3\x00\b\xac\xa7\xce\x05\x8e\ua816\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\x10n\xd5\xc7\x19\xb5&\x14w\x89\x04%\xaeuQ\xdcY\xbd%\\\x8a\x02\x89jX\xc9[\xe5\x88\x00\x00\u07d4\x10q\x1c=\xda21x\x85\xf0\xa2\xfd\x8a\xe9.\x82\x06\x9b\r\v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x10sy\xd4\xc4gFO#[\xc1\x8eU\x93\x8a\xad>h\x8a\u05c9\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x10v!-Ou\x8c\x8e\xc7\x12\x1c\x1c}t%I&E\x92\x84\x8a\ai[Y\xb5\xc1{L\x00\x00\u07d4\x10x\xd7\xf6\x1b\x0eV\xc7N\xe6c[.\x18\x19\xef\x1e=\x87\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x10z\x03\xcf\bB\xdb\u07b0a\x8f\xb5\x87\xcai\x18\x9e\xc9/\xf5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x10\x80\xc1\xd85\x8a\x15\xbc\x84\xda\xc8%\x89\u0392\xb9\x81\x89t\xc1\xfa\xb8\xad\xb4T\x00\x00\u07d4\x10\xe1\xe37x\x85\xc4-}\xf2\x18R.\xe7vh\x87\xc0^j\x89\x10C\xc4<\xde\x1d9\x80\x00\u07d4\x10\u342d+\xa3=\x82\xb3s\x88\u041cED\u01b0\"]\xe5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x10\xf4\xbf\xf0\u02a5\x02|\nj-\xcf\xc9R\x82M\xe2\x94\t\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11\x00\x1b\x89\xed\x87>:\xae\xc1\x15V4\xb4h\x16C\x98c#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x11\x027\u03d1\x17\xe7g\x92/\u0121\xb7\x8dyd\u0682\xdf \x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x11\x11\xe5\xdb\xf4^o\x90mb\x86o\x17\b\x10\x17\x88\xdd\xd5q\x89F{\xe6S>\xc2\xe4\x00\x00\xe0\x94\x11\x17+'\x8d\xddD\xee\xa2\xfd\xf4\xcb\x1d\x16\x96#\x91\xc4S\u064a\xc6/=\x9b\xfdH\x95\xf0\x00\x00\u07d4\x11&4\xb4\xec0\xffxn\x02AY\xf7\x96\xa5y9\xea\x14N\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x110l}WX\x867x\x0f\xc9\xfd\xe8\xe9\x8e\xcb\x00\x8f\x01d\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x116\x12\xbc;\xa0\xeeH\x98\xb4\x9d\xd2\x023\x90_/E\x8fb\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x11A_\xaba\xe0\xdf\u0539\x06v\x14\x1aUz\x86\x9b\xa0\xbd\xe9\x89o\x05\xb5\x9d; \x00\x00\x00\u07d4\x11L\xbb\xbfo\xb5*\xc4\x14\xbe~\xc6\x1f{\xb7\x14\x95\xce\x1d\xfa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x11L\xfe\xfeP\x17\r\xd9z\xe0\x8f\nDTIx\u0159T\x8d\x89.\u0207\xe7\xa1J\x1c\x00\x00\u07d4\x11a\b\xc1 \x84a.\xed\xa7\xa9=\xdc\xf8\xd2`.'\x9e\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11d\u02aa\x8c\u0157z\xfe\x1f\xad\x8a}`(\xce-W)\x9b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x11gZ%UF\a\xa3\xb6\xc9*\x9e\xe8\xf3ou\xed\xd3\xe36\x89\b\xa9\xab\xa5W\xe3l\x00\x00\u07d4\x11j\t\xdff\xcb\x15\x0e\x97W\x8e)\u007f\xb0n\x13\x04\f\x89<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11o\xef^`\x16B\xc9\x18\u02c9\x16\x0f\xc2);\xa7\x1d\xa96\x89+|\xc2\xe9\xc3\"\\\x00\x00\u07d4\x11xP\x1f\xf9J\xdd\x1cX\x81\xfe\x88a6\xf6\xdf\xdb\xe6\x1a\x94\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x11y\xc6\r\xbd\x06\x8b\x15\v\aM\xa4\xbe#\x03; \u0185X\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\x11}\x9a\xa3\xc4\xd1;\xee\x12\xc7P\x0f\t\xf5\xdd\x1cf\xc4e\x04\x89\v*\xd3\x04\x90\xb2x\x00\x00\xe0\x94\x11}\xb867\u007f\xe1TU\xe0,.\xbd\xa4\v\x1c\xebU\x1b\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x11\x8c\x18\xb2\xdc\xe1p\xe8\xf4Eu;\xa5\xd7Q<\xb7cm-\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\x11\x8f\xbdu;\x97\x929Z\xefzMx\xd2c\xcd\u02ab\xd4\xf7\x8963\x03\"\xd5#\x8c\x00\x00\xe0\x94\x11\x92\x83x\xd2}U\xc5 \xce\xed\xf2L\xeb\x1e\x82-\x89\r\xf0\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x11\x9a\xa6M[}\x18\x1d\xae\x9d<\xb4I\x95\\\x89\xc1\xf9c\xfa\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\x11\xc05\x8a\xa6G\x9d\xe2\x18f\xfe!\a\x19$\xb6^p\xf8\xb9\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\xe0\x94\x11\xd2$z\"\x1ep\xc2\xd6m\x17\xee\x13\x8d8\xc5_\xfb\x86@\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x11\u05c4JG\x1e\xf8\x9a\x8d\x87uUX<\xee\xbd\x149\xea&\x8a\x02#i\u6e80\u0188\x00\x00\u07d4\x11\xdda\x85\u0668\xd7=\xdf\u06a7\x1e\x9bwtC\x1cM\xfe\u008965\u026d\xc5\u07a0\x00\x00\u07d4\x11\xe7\x99~\u0750E\x03\xd7}\xa6\x03\x8a\xb0\xa4\xc84\xbb\xd5c\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\x11\xec\x00\xf8I\xb61\x9c\xf5\x1a\xa8\u074ff\xb3U)\xc0\xbew\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11\ufe22\x04Q\x16\x1bdJ\x8c\u03bb\xc1\xd3C\xa3\xbb\xcbR\x89\xadx\xeb\u016cb\x00\x00\x00\xe0\x94\x11\xfe\xfb]\xc1\xa4Y\x8a\xa7\x12d\fQwu\u07e1\xd9\x1f\x8c\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x12\x0f\x9d\xe6\xe0\xaf~\xc0*\a\xc6\t\u0284G\xf1W\xe64L\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x12\x10\xf8\v\u06c2l\x17Tb\xab\a\x16\xe6\x9eF\xc2J\xd0v\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x12\x13N\u007fk\x01{\xf4\x8e\x85Z9\x9c\xa5\x8e.\x89/\xa5\u020965\u026d\xc5\u07a0\x00\x00\u07d4\x12\x170t\x98\x01S\xae\xaaK\r\xcb\xc7\x13.\xad\xce\xc2\x1bd\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4\x12\x1f\x85[p\x14\x9a\xc84s\xb9po\xb4MG\x82\x8b\x98;\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x12'\xe1\nM\xbf\x9c\xac\xa3\x1b\x17\x80#\x9fUv\x15\xfc5\xc1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x12-\xcf\xd8\x1a\u0779}\x1a\x0eI%\u0135I\x80n\x9f;\xeb\x89R 5\xccn\x01!\x00\x00\u07d4\x12/V\x12%I\xd1h\xa5\xc5\xe2g\xf5&b\xe5\xc5\xcc\xe5\u0209\n\ad\a\xd3\xf7D\x00\x00\xe0\x94\x121o\xc7\xf1x\xea\xc2.\xb2\xb2Z\xed\xea\xdf=u\xd0\x01w\x8a\x04<3\xbe\x05\xf6\xbf\xb9\x80\x00\xe0\x94\x127Y\xf33\xe1>0i\xe2\x03KO\x059\x89\x18\x11\x9d6\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x12\\\xc5\xe4\xd5k+\xcc.\xe1\xc7\t\xfb\x9eh\xfb\x17t@\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x12c#\x88\xb2v^\xe4E+P\x16\x1d\x1f\xff\xd9\x1a\xb8\x1fJ\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\u07d4\x12h\x97\xa3\x11\xa1J\xd4;x\xe0\x92\x01\x00\xc4Bk\xfdk\u07494\xc7&\x89?-\x94\x80\x00\u07d4\x12m\x91\xf7\xad\x86\u07bb\x05W\xc6\x12\xca'n\xb7\xf9m\x00\xa1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x12}?\xc5\x00;\xf6<\r\x83\xe99W\x83e\x15\xfd'\x90E\x89\x06\x10\xc9\".nu\x00\x00\xe0\x94\x12}\xb1\xca\xdf\x1bw\x1c\xbdtu\xe1\xb2ri\x0fU\x8c\x85e\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x12\x84\xf0\xce\xe9\xd2\xff)\x89\xb6Ut\xd0o\xfd\x9a\xb0\xf7\xb8\x05\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x12\x8b\x90\x8f\xe7C\xa44 =\xe2\x94\xc4A\xc7\xe2\n\x86\xeag\x89&\xab\x14\xe0\xc0\xe1<\x00\x00\xe0\x94\x12\x93\u01cc}jD;\x9dt\xb0\xba^\xe7\xbbG\xfdA\x85\x88\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\x12\x96\xac\xde\xd1\xe0c\xaf9\xfe\x8b\xa0\xb4\xb6=\xf7\x89\xf7\x05\x17\x89\x05k\xf9\x1b\x1ae\xeb\x00\x00\u07d4\x12\xaa}\x86\xdd\xfb\xad0\x16\x92\xfe\xac\x8a\b\xf8A\xcb!\\7\x89\amA\xc6$\x94\x84\x00\x00\xe0\x94\x12\xaf\xbc\xba\x14'\xa6\xa3\x9e{\xa4\x84\x9fz\xb1\xc45\x8a\xc3\x1b\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x12\xb5\xe2\x89E\xbb)i\xf9\xc6Lc\xcc\x05\xb6\xf1\xf8\xd6\xf4\u054a\x01\xa2\x9e\x86\x91;t\x05\x00\x00\u0794\x12\u03cb\x0eFR\x13!\x1a[S\u07f0\xdd'\x1a(,\x12\u0248\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x12\xd2\a\x90\xb7\xd3\xdb\u060c\x81\xa2y\xb8\x12\x03\x9e\x8a`;\u0409V\xf9\x85\u04c6D\xb8\x00\x00\xe0\x94\x12\xd6\re\xb7\xd9\xfcH\x84\v\xe5\xf8\x91\xc7E\xcev\xeeP\x1e\x8a\x04\x85\xe58\x8d\fv\x84\x00\x00\u0794\x12\xd9\x1a\x92\xd7O\xc8a\xa7)dm\xb1\x92\xa1%\xb7\x9fSt\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\x12\u992d*\xd5t\x84\xddp\x05e\xbd\xdbFB;\u067d1\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x12\xf3,\n\x1f-\xaa\xb6v\xfei\xab\xd9\xe0\x185-L\xcdE\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x12\xf4`\xaedl\xd2x\x0f\xd3\\P\xa6\xafK\x9a\xcc\xfa\x85\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x12\xff\xc1\x12\x86\x05\xcb\f\x13p\x9ar\x90Po&\x90\x97q\x93\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x13\x03$F\xe7\xd6\x10\xaa\x00\xec\x8cV\u0275t\xd3l\xa1\xc0\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\x1cy,\x19}\x18\xbd\x04]p$\x93|\x1f\x84\xb6\x0fD8\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13\x1d\xf8\xd30\xeb|\xc7\x14}\nUWo\x05\u078d&\xa8\xb7\x89\n1\x06+\xee\xedp\x00\x00\u07d4\x13\x1f\xae\xd1%a\xbbz\xee\x04\xe5\x18Z\xf8\x02\xb1\xc3C\x8d\x9b\x89\v\xdf\x0e\u0733\x90\xc9\xc8V\b\xb7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13!\xcc\xf2\x979\xb9t\xe5\xa5\x16\xf1\x8f:\x846q\xe3\x96B\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13'\xd7Y\xd5n\n\xb8z\xf3~\xcfc\xfe\x01\xf3\x10\xbe\x10\n\x89#\xbc<\xdbh\xa1\x80\x00\x00\u07d4\x13)\xdd\x19\xcdK\xaa\x9f\xc6C\x10\xef\xec\xea\xb2!\x17%\x1f\x12\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x137\x1f\x92\xa5n\xa88\x1eC\x05\x9a\x95\x12\x8b\xdcMC\u0166\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x13O\x15\xe1\xe3\x9cSCY0\xaa\xed\xf3\xe0\xfeV\xfd\xe8C\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x13Ac\xbe\x9f\xbb\xe1\xc5in\xe2U\xe9\v\x13%C\x95\xc3\x18\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x13\\\xec\xd9U\xe5y\x83pv\x920\x15\x93\x03\u0671\x83\x9ff\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x13]\x17\x19\xbf\x03\xe3\xf8f1$y\xfe3\x81\x18\xcd8~p\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x13^\xb8\xc0\xe9\xe1\x01\xde\xed\xec\x11\xf2\xec\xdbf\xae\x1a\xae\x88g\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x13`\xe8}\xf2Li\xeemQ\xc7nsv\u007f\xfe\x19\xa2\x13\x1c\x89\x04\xfc\xc1\xa8\x90'\xf0\x00\x00\u07d4\x13l\x83K\xf1\x112m s\x95)[.X>\xa7\xf35r\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x13mKf+\xbd\x10\x80\xcf\xe4D[\x0f\xa2\x13\x86D5\xb7\xf1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13oI\a\u02b4\x1e'\bK\x98E\x06\x9f\xf2\xfd\f\x9a\xdey\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13t\xfa\xcd{?\x8dhd\x9d`\xd4U\x0e\xe6\x9f\xf0HA3\x89\x0e\x9e\xd6\xe1\x11r\xda\x00\x00\u07d4\x13|\xf3A\xe8Ql\x81X\x14\xeb\xcds\xe6V\x9a\xf1L\xf7\xbc\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x13\x84\x8bF\xeau\xbe\xb7\xea\xa8_Y\xd8f\xd7\u007f\xd2L\xf2\x1a\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x13\x9d51\u0252*\xd5bi\xf60\x9a\xa7\x89\xfb$\x85\xf9\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13\x9eG\x97d\xb4\x99\xd6f \x8cJ\x8a\x04z\x97\x041c\u0749 w!*\xffm\xf0\x00\x00\u07d4\x13\xa5\xee\xcb80]\xf9Iq\xef-\x9e\x17\x9a\xe6\u03ba\xb37\x89\x11\u3ac3\x95\xc6\xe8\x00\x00\u07d4\x13\xac\xad\xa8\x98\n\xff\xc7PI!\xbe\x84\xebID\xc8\xfb\xb2\xbd\x89V\u04aa:\\\t\xa0\x00\x00\u07d4\x13\xb9\xb1\a\x15qL\t\xcf\xd6\x10\u03dc\x98F\x05\x1c\xb1\xd5\x13\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x13\xce3-\xffe\xa6\xab\x938\x97X\x8a\xa2>\x00\t\x80\xfa\x82\x89\x0e\x02\x056\xf0(\xf0\x00\x00\u07d4\x13\xd6z~%\xf2\xb1,\u06c5XP\t\xf8\xac\u011b\x96s\x01\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x13\xde\xe0>7\x99\x95-\a8\x84=K\xe8\xfc\n\x80?\xb2\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\xe0/\xb4H\xd6\xc8J\xe1}\xb3\x10\xad(m\x05a`\u0695\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\xe3!r\x8c\x9cWb\x80X\xe9?\xc8f\xa02\xdd\v\u0690\x89&\xbc\xca#\xfe.\xa2\x00\x00\u07d4\x13\xec\x81\"\x84\x02n@\x9b\xc0f\xdf\xeb\xf9\u0564\xa2\xbf\x80\x1e\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\x14\x01)\xea\xa7f\xb5\xa2\x9f[:\xf2WND\t\xf8\xf6\xd3\xf1\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u07d4\x14\x05\x18\xa3\x19K\xad\x13P\xb8\x94\x9ee\x05e\u07bem\xb3\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14\x06\x85M\x14\x9e\b\x1a\xc0\x9c\xb4\xcaV\r\xa4c\xf3\x120Y\x89Hz\x9a0E9D\x00\x00\u07d4\x14\f\xa2\x8f\xf3;\x9ff\xd7\xf1\xfc\x00x\xf8\xc1\xee\xf6\x9a\x1b\xc0\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x14\x0f\xbaX\xdb\xc0H\x03\xd8L!0\xf0\x19x\xf9\xe0\xc71)\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\x14\x1a^9\xee/h\n`\x0f\xbfo\xa2\x97\u0790\xf3\"\\\u074a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x14%N\xa1&\xb5-\x01B\xda\n~\x18\x8c\xe2U\xd8\xc4qx\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\x14+\x87\xc5\x04?\xfbZ\x91\xdf\x18\xc2\xe1\t\xce\xd6\xfeJq\u06c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x14\x87\xf5\xa5$\u0288Q^\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4\x14\xa75 f6D\x04\xdbP\xf0\xd0\u05cduJ\"\x19\x8e\xf4\x89e\xea=\xb7UF`\x00\x00\u07d4\x14\xab\x16K;RL\x82\u05ab\xfb\xc0\u0783\x11&\xae\x8d\x13u\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14\xb1`>\xc6+ \x02 3\xee\xc4\xd6\xd6eZ\xc2J\x01Z\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x14\xc6;\xa2\u0731\xddM\xf3=\u06b1\x1cO\x00\a\xfa\x96\xa6-\x8a\x03HA\xb6\x05z\xfa\xb0\x00\x00\xe0\x94\x14\xcd\u077c\x8b\t\xe6gZ\x9e\x9e\x05\t\x1c\xb9\"8\u00de\x1e\x8a\x01\x14x\xb7\xc3\n\xbc0\x00\x00\u07d4\x14\xd0\n\xad9\xa0\xa7\u045c\xa0SP\xf7\xb07'\xf0\x8d\xd8.\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x14\xee\xc0\x9b\xf0>5+\xd6\xff\x1b\x1e\x87k\xe6d\xce\xff\xd0\u03c9\x01\x16\xdc:\x89\x94\xb3\x00\x00\u07d4\x14\xf2!\x15\x95\x18x;\u0127\x06go\xc4\xf3\xc5\xee@X)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x14\xfc\xd19\x1e}s/Avl\xda\u0344\xfa\x1d\xeb\x9f\xfd\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x0e=\xbc\xbc\xfc\x84\xcc\xf8\x9bsBwc\xa5e\xc2>`\u0409\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\x15\x18b{\x885\x1f\xed\xe7\x96\xd3\xf3\b3d\xfb\u0508{\f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u0794\x15\"J\xd1\xc0\xfa\xceF\xf9\xf5V\xe4wJ0%\xad\x06\xbdR\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x15/+\xd2)\xdd\xf3\xcb\x0f\xda\xf4U\xc1\x83 \x9c\x0e\x1e9\xa2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x15/N\x86\x0e\xf3\xee\x80jP'w\xa1\xb8\xdb\xc9\x1a\x90vh\x89 \x86\xac5\x10R`\x00\x00\u07d4\x15<\b\xaa\x8b\x96\xa6\x11\xefc\xc0%>*C4\x82\x9eW\x9d\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\x15<\xf2\x84,\xb9\u0787l'o\xa6Gg\u0468\xec\xf5s\xbb\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15>\xf5\x8a\x1e.z>\xb6\xb4Y\xa8\n\xb2\xa5G\xc9A\x82\xa2\x8a\x14T+\xa1*3|\x00\x00\x00\u07d4\x15DY\xfa/!1\x8e44D\x97\x89\xd8&\xcd\xc1W\f\xe5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15G\xb9\xbfz\xd6bt\xf3A8'#\x1b\xa4\x05\ue308\xc1\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\x15H\xb7p\xa5\x11\x8e\u0787\u06e2\xf6\x903\u007fam\u60eb\x89\x1c\x99V\x85\u0fc7\x00\x00\u07d4\x15R\x83P\xe0\xd9g\n.\xa2\u007f{J3\xb9\xc0\xf9b\x1d!\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x15[7y\xbbmV4./\u0681{[-\x81\xc7\xf4\x13'\x89\x02\xb8\xaa:\al\x9c\x00\x00\u07d4\x15e\xaf\x83~\xf3\xb0\xbdN+#V\x8dP#\xcd4\xb1d\x98\x89\x15Q\xe9rJ\u013a\x00\x00\u07d4\x15f\x91\x80\xde\u2558\x86\x9b\b\xa7!\xc7\xd2LL\x0e\xe6?\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x15r\xcd\xfa\xb7*\x01\u0396\x8ex\xf5\xb5D\x8d\xa2\x98S\xfb\u074a\x01\x12blI\x06\x0f\xa6\x00\x00\xe0\x94\x15uY\xad\xc5Wd\xccm\xf7\x93#\t%4\xe3\xd6dZf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x15x\xbd\xbc7\x1bM$8E3\x05V\xff\xf2\xd5\xefM\xffg\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x15~\xb3\xd3\x11;\u04f5\x97qM:\x95N\xdd\x01\x89\x82\xa5\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x84\xa2\xc0f\xb7\xa4U\xdb\u05ae(\a\xa73N\x83\xc3_\xa5\x89\a\f\x1c\xc7;\x00\xc8\x00\x00\u07d4\x15\x87F\x86\xb6s=\x10\xd7\x03\xc9\xf9\xbe\xc6\xc5.\xb8b\x8dg\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x8a\ra\x92S\xbfD2\xb5\xcd\x02\u01f8b\xf7\u00b7V6\x89\a[\xac|[\x12\x18\x80\x00\u07d4\x15\x98\x12y\x82\xf2\xf8\xad;k\x8f\xc3\xcf'\xbfax\x01\xba+\x89\t`\xdbwh\x1e\x94\x00\x00\xe0\x94\x15\x9a\xdc\xe2z\xa1\vG#d)\xa3JZ\xc4,\xad[d\x16\x8a\x06\xbf\x90\xa9n\xdb\xfaq\x80\x00\u07d4\x15\xa0\xae\xc3\u007f\xf9\xff=T\t\xf2\xa4\xf0\xc1!*\xac\xcb\x02\x96\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x15\xaaS\r\xc3iX\xb4\xed\xb3\x8e\xeem\xd9\xe3\xc7}L\x91E\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15\xac\xb6\x15h\xecJ\xf7\xea(\x198a\x81\xb1\x16\xa6\xc5\xeep\x8a\x06\x90\x83l\n\xf5\xf5`\x00\x00\u07d4\x15\xb9o0\xc2;\x86d\xe7I\x06Q\x06k\x00\xc49\x1f\xbf\x84\x89\x16B\xe9\xdfHv)\x00\x00\u07d4\x15\xc7\xed\xb8\x11\x8e\xe2{4\"\x85\xebY&\xb4z\x85[\u01e5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x15\u0654hPz\xa0A?\xb6\r\xca*\xdc\u007fV\x9c\xb3kT\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15\u06f4\x8c\x980\x97d\xf9\x9c\xed6\x92\xdc\xca5\xee0k\xac\x8a\x1f\u00c4+\xd1\xf0q\xc0\x00\x00\xe0\x94\x15\u072f\xcc+\xac\xe7\xb5[T\xc0\x1a\x1cQF&\xbfa\xeb\u060a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x15\u3d44\x05kb\xc9s\xcf^\xb0\x96\xf1s>T\xc1\\\x91\x892\xc7Z\x02#\xdd\xf3\x00\x00\u07d4\x15\xeb\xd1\xc7\xca\u04af\xf1\x92u\xc6W\xc4\xd8\b\xd0\x10\xef\xa0\xf5\x89\n\xdf0\xbap\u0217\x00\x00\u07d4\x15\xee\x0f\xc6>\xbf\x1b\x1f\u011d{\xb3\x8f\x88c\x82:.\x17\u0489g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x15\xf1\xb3R\x11\rh\x90\x1d\x8fg\xaa\xc4jl\xfa\xfe\x03\x14w\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x15\xf2\xb7\xb1d2\xeeP\xa5\xf5[A#/c4\xedX\xbd\xc0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x16\x01\x9aM\xaf\xabC\xf4\u067fAc\xfa\xe0\x84}\x84\x8a\xfc\xa2\x89\x01[\xc7\x019\xf7J\x00\x00\u07d4\x16\x02&\xef\xe7\xb5:\x8a\xf4b\xd1\x17\xa0\x10\x80\x89\xbd\xec\xc2\u0449\n\xdf0\xbap\u0217\x00\x00\u07d4\x16\f\xebo\x98\x0e\x041_S\xc4\xfc\x98\x8b+\xf6\x9e(M}\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\xe0\x94\x16\x1c\xafZ\x97*\u0383y\xa6\u0420J\xe6\xe1c\xfe!\xdf+\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x16\x1d&\xefgY\xba[\x9f \xfd\xcdf\xf1a2\xc3RA^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16!\x10\xf2\x9e\xac_}\x02\xb5C\xd8\xdc\u057bY\xa5\xe3;s\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x16+\xa5\x03'b\x14\xb5\t\xf9u\x86\xbd\x84!\x10\xd1\x03\xd5\x17\x8a\x01\xe7\xff\u0609\\\"h\x00\x00\u07d4\x16-v\xc2\xe6QJ:\xfbo\xe3\xd3\u02d3\xa3\\Z\xe7\x83\xf1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16;\xadJ\x12+E}d\xe8\x15\nA>\xaeM\a\x02>k\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u07d4\x16<\u023e\"vF\xcb\tq\x91Y\xf2\x8e\u041c]\xc0\xdc\xe0\x89Hz\x9a0E9D\x00\x00\u07d4\x16=\xcas\xd7\xd6\xea?>`b2*\x874\x18\f\vx\uf25ft \x03\xcb}\xfc\x00\x00\u07d4\x16Mz\xac>\xec\xba\uc86dQ\x91\xb7S\xf1s\xfe\x12\xec3\x89(VR\xb8\xa4hi\x00\x00\u07d4\x16Rl\x9e\u07d4>\xfaOm\x0f\v\xae\x81\xe1\x8b1\xc5@y\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x16S\x05\xb7\x872.%\xdcj\xd0\xce\xfelo3Fx\xd5i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16e\xab\x179\xd7\x11\x19\xeea2\xab\xbd\x92j'\x9f\xe6yH\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x16k\xf6\u06b2-\x84\x1bHl8\xe7\xbaj\xb3:\x14\x87\ud30a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x16v\x99\xf4\x8ax\xc6\x15Q%\x15s\x99X\x993\x12WO\a\x89\x02\x1d;\xd5^\x80<\x00\x00\u07d4\x16x\xc5\xf2\xa5\"92%\x19ca\x89OS\xccu/\xe2\xf3\x89h\xf3e\xae\xa1\xe4@\x00\x00\u07d4\x16|\xe7\xdee\xe8G\bYZRT\x97\xa3\xeb^ZfPs\x89\x1f1Gsfo\xc4\x00\x00\u07d4\x16~>:\xe2\x003HE\x93\x92\xf7\xdf\xceD\xaf|!\xadY\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x16\x80\xce\xc5\x02\x1e\xe90P\xf8\xae\x12rQ\x83\x9et\xc1\xf1\xfd\x8a\x02\xc6\x14a\xe5\xd7C\u0580\x00\u07d4\x16\x81j\xac\x0e\xde\r-<\xd4B\xday\xe0c\x88\x0f\x0f\x1dg\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x16\x8bP\x19\xb8\x18i\x16D\x83_\xe6\x9b\xf2)\xe1q\x12\xd5,\x8a\x05\xed\xe2\x0f\x01\xa4Y\x80\x00\x00\u07d4\x16\x8b\xde\xc8\x18\xea\xfcm)\x92\xe5\xefT\xaa\x0e\x16\x01\xe3\xc5a\x8967Pz0\xab\xeb\x00\x00\u07d4\x16\x8d0\xe5?\xa6\x81\t+R\xe9\xba\xe1Z\r\xcbA\xa8\u027b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x16\x9b\xbe\xfcA\xcf\xd7\xd7\u02f8\xdf\xc60 \xe9\xfb\x06\u0515F\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xa5\x8e\x98]\xcc\xd7\a\xa5\x94\u0453\xe7\u0327\x8b]\x02xI\x89I\xb9\u029aiC@\x00\x00\u07d4\x16\xa9\xe9\xb7:\u92c6M\x17(y\x8b\x87f\xdb\xc6\xea\x8d\x12\x893\xe7\xb4K\r\xb5\x04\x00\x00\u07d4\x16\xaaR\xcb\vUG#\xe7\x06\x0f!\xf3'\xb0\xa6\x83\x15\xfe\xa3\x89\r\x8drkqw\xa8\x00\x00\u07d4\x16\xab\xb8\xb0!\xa7\x10\xbd\u01ce\xa54\x94\xb2\x06\x14\xffN\xaf\xe8\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x16\xaf\xa7\x87\xfc\x9f\x94\xbd\xffiv\xb1\xa4/C\n\x8b\xf6\xfb\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xba\xe5\xd2N\xff\x91w\x8c\u064bM:\x1c\xc3\x16/D\xaaw\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\x16\xbc@!Z\xbb\u066e](\v\x95\xb8\x01\vE\x14\xff\x12\x92\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x16\xbeu\u9299Z9R\"\xd0\v\u05df\xf4\xb6\xe68\u144a\a\x9f\x90\\o\xd3N\x80\x00\x00\u07d4\x16\xc1\xbf[}\xc9\xc8<\x17\x9e\xfa\xcb\xcf.\xb1t\xe3V\x1c\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x16\u01f3\x1e\x8c7b\x82\xac\"qr\x8c1\xc9^5\xd9R\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xf3\x13\u03ca\xd0\x00\x91J\n\x17m\u01a44+y\xec%8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xff\xac\x84\x03)@\xf0\x12\x1a\tf\x8b\x85\x8a~y\xff\xa3\xbb\x89\xd2J\xdan\x10\x87\x11\x00\x00\xe0\x94\x17\x03\xb4\xb2\x92\xb8\xa9\xde\xdd\xed\xe8\x1b\xb2]\x89\x17\x9fdF\xb6\x8a\x04+e\xa4U\xe8\xb1h\x00\x00\u07d4\x17\x04\x93\x11\x10\x1d\x81~\xfb\x1de\x91\x0ff6b\xa6\x99\u024c\x89lh\xcc\u041b\x02,\x00\x00\u07d4\x17\x04\xce\xfc\xfb\x131\xeczx8\x8b)9>\x85\xc1\xafy\x16\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17\n\x88\xa8\x99\u007f\x92\xd287\x0f\x1a\xff\xde\xe64pP\xb0\x13\x89\xa2\xacw5\x14\x880\x00\x00\u07d4\x17\x10\x8d\xab,P\xf9\x9d\xe1\x10\u1cf3\xb4\u0342\xf5\xdf(\xe7\x895 ;g\xbc\xca\xd0\x00\x00\xe0\x94\x17\x12[Y\xacQ\xce\xe0)\xe4\xbdx\xd7\xf5\x94}\x1e\xa4\x9b\xb2\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4\x17\x1a\u0660K\xed\u0238a\xe8\xedK\xdd\xf5qx\x13\xb1\xbbH\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17\x1c\xa0*\x8bmb\xbfL\xa4~\x90i\x14\a\x98a\x97,\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x17\"\xc4\xcb\xe7\n\x94\xb6U\x9dBP\x84\xca\xee\xd4\xd6\xe6n!\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x17X\vvotSR\\\xa4\u01a8\x8b\x01\xb5\x05p\xea\b\x8c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x17X\x9al\x00jT\xca\xd7\x01\x03\x12:\xae\n\x82\x13_\u07b4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x17Z\x18::#_\xfb\xb0;\xa85gRg\"\x94\x17\xa0\x91\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x17_\xee\xea*\xa4\xe0\xef\xda\x12\xe1X\x8d/H2\x90\xed\xe8\x1a\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x17e6\x1c.\xc2\xf86\x16\u0383c\xaa\xe2\x10%\xf2Vo@\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x17gR\\_Z\"\xed\x80\xe9\xd4\xd7q\x0f\x03b\u049e\xfa3\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17v%`\xe8*\x93\xb3\xf5\"\xe0\xe5$\xad\xb8a,:tp\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x17}\xaex\xbc\x01\x13\xd8\u04dcD\x02\xf2\xa6A\xae*\x10Z\xb8\x89b\x92BV \xb4H\x00\x00\xe0\x94\x17\x84\x94\x8b\xf9\x98H\u021eDV8PM\u0598'\x1bY$\x8a\x01GLA\r\x87\xba\xee\x00\x00\u07d4\x17\x88\u069bW\xfd\x05\xed\xc4\xff\x99\xe7\xfe\xf3\x01Q\x9c\x8a\n\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\x8e\xafk\x85T\xc4]\xfd\xe1kx\xce\f\x15\u007f.\xe3\x13Q\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\x17\x96\x1dc;\xcf \xa7\xb0)\xa7\xd9K}\xf4\xda.\xc5B\u007f\x89\fo\xf0p\U000532c0\x00\u07d4\x17\x96\xbc\xc9{\x8a\xbcq\u007fKJ|k\x106\xea!\x82c\x9f\x89\x13A\xf9\x1c\xd8\xe3Q\x00\x00\u07d4\x17\x99=1*\xa1\x10iW\x86\x8fjU\xa5\xe8\xf1/w\xc8C\x89\x18e\xe8\x14\xf4\x14.\x80\x00\u07d4\x17\x9a\x82^\x0f\x1fn\x98S\tf\x84e\xcf\xfe\xd46\xf6\xae\xa9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x17\xb2\xd6\xcfe\xc6\xf4\xa3G\xdd\xc6W&U5M\x8aA+)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xb8\a\xaf\xa3\xdd\xd6G\xe7#T.{R\xfe\xe3\x95'\xf3\x06\x89\x15\xaf@\xff\xa7\xfc\x01\x00\x00\u07d4\x17\xc0G\x86W\xe1\xd3\xd1z\xaa3\x1d\xd4)\xce\u03d1\xf8\xae]\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\x17\xc0\xfe\xf6\x98l\xfb.@A\xf9\x97\x9d\x99@\xb6\x9d\xff=\xe2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x17\u0511\x8d\xfa\xc1]w\xc4\u007f\x9e\xd4\x00\xa8P\x19\rd\xf1Q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xd5!\xa8\xd9w\x90#\xf7\x16M#<;d \xff\xd2#\xed\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x17\xd91\xd4\xc5b\x94\u073ew\xc8e[\xe4i_\x00mJ<\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xdfIQ\x8ds\xb1)\xf0\xda6\xb1\u0274\f\xb6d \xfd\u01ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x17\xe4\xa0\xe5+\xac>\xe4N\xfe\tT\xe7S\u0538]dN\x05\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xe5\x84\xe8\x10\xe5gp,a\xd5]CK4\u0375\xee0\xf6\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x17\xe8.px\xdcO\xd9\xe8y\xfb\x8aPf\u007fS\xa5\xc5E\x91\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x17\xe8o;[0\xc0\xbaY\xf2\xb2\xe8XB[\xa8\x9f\n\x10\xb0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xee\x9fT\xd4\xdd\xc8Mg\x0e\xff\x11\xe5Je\x9f\xd7/DU\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94\x17\xefJ\xcc\x1b\xf1G\xe3&t\x9d\x10\xe6w\xdc\xff\xd7o\x9e\x06\x8a\bwQ\xf4\xe0\xe1\xb50\x00\x00\u07d4\x17\xf1F2\xa7\xe2\x82\v\xe6\xe8\xf6\u07c25X(=\xad\xab-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xf5#\xf1\x17\xbc\x9f\xe9x\xaaH\x1e\xb4\xf5V\x17\x117\x1b\u0209li\xf7>)\x13N\x00\x00\u07d4\x17\xfd\x9bU\x1a\x98\xcba\xc2\xe0\u007f\xbfA\xd3\xe8\u02650\u02e5\x89\x01v\x8c0\x81\x93\x04\x80\x00\u07d4\x18\x04x\xa6U\u05cd\x0f;\fO +aH[\xc4\x00/\u0549lk\x93[\x8b\xbd@\x00\x00\u07d4\x18\x13l\x9d\xf1g\xaa\x17\xb6\xf1\x8e\"\xa7\x02\u020fK\u0082E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x18\x15'\x9d\xff\x99R\xda;\xe8\xf7rI\xdb\xe2\"C7{\xe7\x8a\x01\x01|\xb7n{&d\x00\x00\u07d4\x18\x1f\xbb\xa8R\xa7\xf5\x01x\xb1\xc7\xf0>\xd9\xe5\x8dT\x16))\x89$\x1a\x9bOaz(\x00\x00\xe0\x94\x18'\x03\x9f\tW\x02\x94\b\x8f\xdd\xf0G\x16\\3\u65a4\x92\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\x18-\xb8R\x93\xf6\x06\u8248\xc3pL\xb3\xf0\xc0\xbb\xbf\xcaZ\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x18H\x00<%\xbf\u052a\x90\xe7\xfc\xb5\u05f1k\xcd\f\xff\xc0\u060965\u026d\xc5\u07a0\x00\x00\xe0\x94\x18JO\v\xebq\xff\xd5X\xa6\xb6\xe8\xf2(\xb7\x87\x96\xc4\xcf>\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94\x18M\x86\xf3Fj\xe6h;\x19r\x99\x82\xe7\xa7\u1903G\xb2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x18Q\xa0c\xcc\xdb0T\x90w\xf1\xd19\xe7-\xe7\x97\x11\x97\u0549lk\x93[\x8b\xbd@\x00\x00\u07d4\x18UF\xe8v\x8dPhs\x81\x8a\xc9u\x1c\x1f\x12\x11j;\xef\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x18X\xcf\x11\xae\xa7\x9fS\x98\xad+\xb2\"g\xb5\xa3\xc9R\xeat\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\xe0\x94\x18Z\u007f\u012c\xe3h\xd23\xe6 \xb2\xa4Y5f\x12\x92\xbd\xf2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x18d\xa3\u01f4\x81UD\x8cT\u020cp\x8f\x16g\tsm1\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x18j\xfd\xc0\x85\xf2\xa3\xdc\xe4a^\xdf\xfb\xad\xf7\x1a\x11x\x0fP\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x18k\x95\xf8\xe5\xef\xfd\xdc\xc9O\x1a1[\xf0)];\x1e\xa5\x88\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x18}\x9f\f\a\xf8\xebt\xfa\xaa\xd1^\xbc{\x80Dt\x17\xf7\x82\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x18\x95\xa0\xebJCrr/\xcb\u016f\xe6\x93o(\x9c\x88\xa4\x19\x891T\xc9r\x9d\x05x\x00\x00\u07d4\x18\x99\xf6\x9fe;\x05\xa5\xa6\xe8\x1fH\a\x11\u041b\xbf\x97X\x8c\x89i\xfb\x13=\xf7P\xac\x00\x00\u07d4\x18\xa6\xd2\xfcR\xbes\b@#\xc9\x18\x02\xf0[\xc2JK\xe0\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x18\xb0@|\xda\xd4\xceR`\x06#\xbd^\x1fj\x81\xaba\xf0&\x89\x11Q\xcc\xf0\xc6T\u0180\x00\u07d4\x18\xb8\xbc\xf9\x83!\xdaa\xfbN>\xac\xc1\xecT\x17'-\xc2~\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\x18\xc6r:gS)\x9c\xb9\x14G}\x04\xa3\xbd!\x8d\xf8\xc7u\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x18\xe1\x13\xd8\x17|i\x1aa\xbexXR\xfa[\xb4z\uef6f\x89Hz\x9a0E9D\x00\x00\xe0\x94\x18\xe4\xceGH;S\x04\n\u06eb5\x17,\x01\xefdPn\f\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\xe0\x94\x18\xe52C\x98\x1a\xab\xc8v}\xa1\fsD\x9f\x13\x91V\x0e\xaa\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x18\xfa\x86%\xc9\u0704>\x00\x15\x9e\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94\x193\xe34\xc4\x0f:\u02ed\f\v\x85\x11X i$\xbe\xca:\x8a\x01\x99^\xaf\x01\xb8\x96\x18\x80\x00\xe0\x94\x197\xc5\xc5\x15\x05uS\u033dF\u0546dU\xcef)\x02\x84\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\u07d4\x19:\xc6Q\x83e\x18\x00\xe25\x80\xf8\xf0\xea\u04fbY~\xb8\xa4\x89\x02\xb6*\xbc\xfb\x91\n\x00\x00\u07d4\x19=7\xed4}\x1c/N55\r\x9aDK\xc5|\xa4\xdbC\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\x19@\u0713d\xa8R\x16_GAN'\xf5\x00$E\xa4\xf1C\x8a\x02L-\xffj<|H\x00\x00\u07d4\x19E\xfe7\u007f\xe6\u0537\x1e>y\x1fo\x17\xdb$<\x9b\x8b\x0f\x89vy\u7fb9\x886\x00\x00\u07d4\x19Jk\xb3\x02\xb8\xab\xa7\xa5\xb5y\u07d3\xe0\xdf\x15t\x96v%\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x19L\ubd12\x98\x82\xbf;K\xf9\x86L+\x1b\x0fb\u0083\xf9\x89\x1e\xf8aS\x1ft\xaa\x00\x00\u07d4\x19O\xf4J\xef\xc1{\xd2\x0e\xfdz LG\xd1b\f\x86\xdb]\x89\xa2\x99\th\u007fj\xa4\x00\x00\xe0\x94\x19O\xfex\xbb\xf5\xd2\r\u044a\x1f\x01\xdaU.\x00\xb7\xb1\x1d\xb1\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x19S1>*\xd7F#\x9c\xb2'\x0fH\xaf4\u063b\x9cDe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19W\x1a+\x8f\x81\u01bc\xf6j\xb3\xa1\x00\x83)V\x17\x15\x00\x03\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\xe0\x94\x19h}\xaa9\xc3h\x13\x9bn{\xe6\r\xc1u:\x9f\f\xbe\xa3\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x19l\x02!\nE\n\xb0\xb3cpe_qz\xa8{\xd1\xc0\x04\x89\x0e\x10\xac\xe1W\xdb\xc0\x00\x00\u07d4\x19n\x85\xdf~s+J\x8f\x0e\xd06#\xf4\u06dd\xb0\xb8\xfa1\x89\x01%\xb9/\\\xef$\x80\x00\u07d4\x19s+\xf9s\x05]\xbd\x91\xa4S:\u06a2\x14\x9a\x91\u04c3\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19vr\xfd9\xd6\xf2F\xcef\xa7\x90\xd1:\xa9\"\xd7\x0e\xa1\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x19y\x8c\xbd\xa7\x15\ua69b\x9dj\xab\x94,U\x12\x1e\x98\xbf\x91\x89A\rXj \xa4\xc0\x00\x00\u07d4\x19\x8b\xfc\xf1\xb0z\xe3\b\xfa,\x02\x06\x9a\xc9\xda\xfeq5\xfbG\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x19\x8e\xf1\xec2Z\x96\xcc5Lrf\xa08\xbe\x8b\\U\x8fg\x8a\x80\xd1\xe47>\u007f!\xda\x00\x00\xe0\x94\x19\x91\x8a\xa0\x9e}IN\x98\xff\xa5\xdbP5\b\x92\xf7\x15j\u018a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x19\xb3k\f\x87\xeafN\xd8\x03\x18\xdcw\xb6\x88\xdd\xe8}\x95\xa5\x89i\x9fI\x98\x020=\x00\x00\u07d4\x19\u07d4E\xa8\x1c\x1b=\x80J\xea\xebon NB6f?\x89\x02\x06\xd9NjI\x87\x80\x00\u07d4\x19\xe5\u07a37\n,tj\xae4\xa3|S\x1fA\xda&N\x83\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x19\xe7\xf3\xeb{\xf6\u007f5\x99 \x9e\xbe\b\xb6*\xd32\u007f\x8c\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\x19\xe9Nb\x00P\xaa\xd7f\xb9\xe1\xba\xd91#\x83\x12\u053fI\x89\x81\xe3-\xf9r\xab\xf0\x00\x00\u07d4\x19\xec\xf2\xab\xf4\f\x9e\x85{%/\xe1\xdb\xfd=L]\x8f\x81n\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19\xf5\xca\xf4\xc4\x0ei\b\x81<\aE\xb0\xae\xa9Xm\x9d\xd91\x89#\xfe\xd9\xe1\xfa+`\x00\x00\u07d4\x19\xf6C\xe1\xa8\xfa\x04\xae\x16\x00`(\x13\x833\xa5\x9a\x96\u0787\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x19\xf9\x9f,\vF\u0389\x06\x87]\xc9\xf9\n\xe1\x04\xda\xe3U\x94\x89\xf4WZ]M\x16*\x00\x00\u07d4\x19\xff$O\xcf\xe3\xd4\xfa/O\u065f\x87\xe5[\xb3\x15\xb8\x1e\xb6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1a\x04\xce\xc4 \xadC\"\x15$mw\xfe\x17\x8d3\x9e\u0435\x95\x89\x11!a\x85\u009fp\x00\x00\xe0\x94\x1a\x04\xd58\x9e\xb0\x06\xf9\u0388\f0\xd1SS\xf8\xd1\x1cK1\x8a\x03\x9d\x84\xb2\x18m\xc9\x10\x00\x00\u07d4\x1a\bA\xb9*\u007fpuV\x9d\xc4b~kv\u02b0Z\u0791\x89Rf<\u02b1\xe1\xc0\x00\x00\xe0\x94\x1a\b]C\xec\x92AN\xa2{\x91O\xe7g\xb6\xd4k\x1e\xefD\x8a\x06A\xe8\xa15c\xd8\xf8\x00\x00\u07d4\x1a\t\xfd\xc2\u01e2\x0e#WK\x97\u019e\x93\u07bag\xd3r \x89lO\xd1\xee$nx\x00\x00\u07d4\x1a\n\x1d\u07f01\xe5\xc8\xcc\x1dF\xcf\x05\x84-P\xfd\xdcq0\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1a\x1c\x9a&\xe0\xe0$\x18\xa5\xcfh}\xa7Z'\\b,\x94@\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x1a \x1bC'\u03a7\xf3\x99\x04bF\xa3\xc8~n\x03\xa3\u0368\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a$4\xccwD\"\u050dS\u055c]V,\u0384\a\xc9K\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\x1a%\xe1\u017c~_P\xec\x16\xf8\x88_!\x0e\xa1\xb98\x80\x0e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x1a&\x94\xec\a\xcf^Mh\xba@\xf3\xe7\xa1LS\xf3\x03\x8cn\x8966\xcd\x06\xe2\xdb:\x80\x00\u07d4\x1a5 E5\x82\xc7\x18\xa2\x1cB7[\xc5\as%RS\xe1\x89*\xd3s\xcef\x8e\x98\x00\x00\xe0\x94\x1a7n\x1b-/Y\ai\xbb\x85\x8dEu2\rN\x14\x99p\x8a\x01\x06q%v9\x1d\x18\x00\x00\u07d4\x1a:3\x0eO\xcbi\xdb\xef^i\x01x;\xf5\x0f\xd1\xc1SB\x89\u3bb5sr@\xa0\x00\x00\u07d4\x1aN\u01a0\xae\u007fZ\x94'\xd2=\xb9rL\r\f\xff\xb2\xab/\x89\t\xb4\x1f\xbf\x9e\n\xec\x00\x00\u07d4\x1aP^b\xa7N\x87\xe5wG>O:\xfa\x16\xbe\xdd<\xfaR\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x1a^\xe53\xac\xbf\xb3\xa2\xd7m[hRw\xb7\x96\xc5j\x05+\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1adJP\xcb\u00ae\xe8#\xbd+\xf2C\xe8%\xbeMG\xdf\x02\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\x1apD\xe28?\x87\b0[I[\xd1\x17k\x92\xe7\xef\x04:\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1ay\xc7\xf4\x03\x9cg\xa3\x9du\x13\x88L\xdc\x0e,4\"$\x90\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1a\x89\x89\x9c\xbe\xbd\xbbd\xbb&\xa1\x95\xa6<\bI\x1f\u035e\xee\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1a\x8a\\\xe4\x14\u079c\xd1r\x93~7\xf2\u055c\xffq\xceW\xa0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1a\x95\xa8\xa8\b.FR\xe4\x17\r\xf9'\x1c\xb4\xbbC\x05\xf0\xb2\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x1a\x95\u0277Tk]\x17\x86\u00c5\x8f\xb1#dF\xbc\f\xa4\u0389j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1a\x98~?\x83\xdeu\xa4/\x1b\xde|\x99|\x19!{J_$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1a\x9ep/8]\xcd\x10^\x8b\x9f\xa4(\xee\xa2\x1cW\xffR\x8a\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1a\xa1\x02\x1fU\n\xf1X\xc7Gf\x8d\xd1;F1`\xf9Z@\x89O\xb0Y\x1b\x9b08\x00\x00\u07d4\x1a\xa2v\x99\xca\u068d\u00e7oy3\xaaf\xc7\x19\x19\x04\x0e\x88\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x1a\xa4\x02p\xd2\x1e\\\u0786\xb61m\x1a\xc3\xc53IKy\xed\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1a\xb5:\x11\xbc\xc6=\u07ea@\xa0+\x9e\x18d\x96\u037b\x8a\xff\x89l?*\xac\x80\f\x00\x00\x00\u07d4\x1a\xbcN%;\b\n\xebCy\x84\xab\x05\xbc\xa0\x97\x9a\xa4>\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a\xc0\x89\u00fcM\x82\xf0j \x05\x1a\x9ds-\xc0\xe74\xcba\x89%\xf6\x9dc\xa6\xce\x0e\x00\x00\xe0\x94\x1a\xd4V>\xa5xk\xe1\x15\x995\xab\xb0\xf1\u0547\x9c>sr\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1a\xd7- \xa7n\u007f\xcckv@X\xf4\x8dA}Io\xa6\u0349lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1a\xda\xf4\xab\xfa\x86}\xb1\u007f\x99\xafj\xbe\xbfpz<\xf5]\xf6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1a\xf6\x03C6\x0e\v-u%R\x107W \xdf!\xdb\\}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a\xfc\u0145\x89l\xd0\xed\xe1)\xee-\xe5\xc1\x9e\xa8\x11T\vd\x89\xaf*\xba\f\x8e[\xef\x80\x00\u07d4\x1b\x05\xeajj\u022f|\xb6\xa8\xb9\x11\xa8\xcc\xe8\xfe\x1a*\xcf\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\v1\xaf\xffKm\xf3e:\x94\xd7\xc8yx\xae5\xf3J\xae\x89\x139\x10E?\xa9\x84\x00\x00\u07d4\x1b\r\ah\x17\xe8\u058e\xe2\xdfN\x1d\xa1\xc1\x14-\x19\x8cD5\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\x1b\x13\ro\xa5\x1d\\H\xec\x8d\x1dR\u070a\"{\xe8s\\\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b#\u02c6cUHq\xfb\xbe\r\x9e`9~\xfbo\xae\xdc>\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1b&9X\x8bU\xc3D\xb0#\xe8\xde_\xd4\b{\x1f\x04\x03a\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x1b9 \xd0\x01\xc4>r\xb2N|\xa4o\x0f\xd6\xe0\xc2\n_\xf2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1b<\xb8\x1eQ\x01\x1bT\x9dx\xbfr\v\r\x92J\xc7c\xa7\u008av\x95\xa9, \xd6\xfe\x00\x00\x00\u07d4\x1bC#,\xcdH\x80\xd6\xf4o\xa7Q\xa9l\xd8$s1XA\x89\x04V9\x18$O@\x00\x00\u07d4\x1bK\xbc\xb1\x81e!\x1b&[(\a\x16\xcb?\x1f!!v\xe8\x89\x19\x9a\xd3}\x03\xd0`\x80\x00\u07d4\x1bM\a\xac\u04c1\x83\xa6\x1b\xb2x=+{\x17\x8d\xd5\x02\xac\x8d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1bckzIo\x04MsYYn5:\x10F\x16Cok\x89\x13\x88\xea\x95\xc3?\x1d\x00\x00\u07d4\x1bd\x95\x89\x12@\xe6NYD\x93\xc2f!q\xdb^0\xce\x13\x89\tX\x87\u0595\xedX\x00\x00\u07d4\x1bf\x10\xfbh\xba\xd6\xed\x1c\xfa\xa0\xbb\xe3:$\xeb.\x96\xfa\xfb\x89\b=lz\xabc`\x00\x00\u07d4\x1by\x903\xefm\xc7\x12x\"\xf7EB\xbb\"\xdb\xfc\t\xa3\b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x1b~\xd9t\xb6\xe24\u0381$t\x98B\x9a[\u0520\xa2\xd19\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\x82o\xb3\xc0\x12\xb0\xd1Y\u253a[\x8aI\x9f\xf3\xc0\xe0<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\x8a\xa0\x16\f\u05df\x00_\x88Q\nqI\x13\xd7\n\u04fe3\x89\n\xef\xfb\x83\a\x9a\xd0\x00\x00\xe0\x94\x1b\x8b\xd6\xd2\xec\xa2\x01\x85\xa7\x8e}\x98\xe8\xe1\x85g\x8d\xacH0\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\x1b\x9b-\u0096\x0eL\xb9@\x8ft\x05\x82|\x9bY\a\x16\x12\xfd\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1b\xa9\"\x8d8\x87'\xf3\x89\x15\x0e\xa0;s\xc8-\xe8\xeb.\t\x8a\x01\x89t\xfb\xe1w\xc9(\x00\x00\u07d4\x1b\xa9\xf7\x99~S\x87\xb6\xb2\xaa\x015\xac$R\xfe6\xb4\xc2\r\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\x1b\xba\x03\xffkJ\u057f\x18\x18J\xcb!\xb1\x88\xa3\x99\xe9\xebJ\x89a\t=|,m8\x00\x00\u07d4\x1b\xbc\x19\x9eXg\x90\xbe\x87\xaf\xed\xc8I\xc0G&t\\]{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x1b\xbc`\xbc\xc8\x0e\\\xdc5\xc5Aj\x1f\n@\xa8=\xae\x86{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xc4L\x87a#\x1b\xa1\xf1\x1f_\xaa@\xfaf\x9a\x01>\x12\u0389\v\tR\xc4Z\xea\xad\x00\x00\u07d4\x1b\xcf4A\xa8f\xbd\xbe\x960\t\xce3\xc8\x1c\xbb\x02a\xb0,\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\x1b\u048c\xd5\u01ca\xeeQ5|\x95\xc1\xef\x925\xe7\xc1\x8b\xc8T\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xd8\xeb\xaavt\xbb\x18\u1458\xdb$OW\x03\x13\a_C\x89\b!\xab\rD\x14\x98\x00\x00\u07d4\x1b\xd9\t\xac\rJ\x11\x02\xec\x98\xdc\xf2\u0329j\n\xdc\u05e9Q\x89\x01\x16Q\xac>zu\x80\x00\u07d4\x1b\xe3T,6\x13hte\xf1Zp\xae\xeb\x81f+e\u0328\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xeaM\xf5\x12/\xaf\u07b3`~\xdd\xda\x1e\xa4\xff\u06da\xbf*\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d4\x1b\xecM\x02\u0385\xfcH\xfe\xb6$\x89\x84\x1d\x85\xb1pXj\x9b\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\x1b\xf9t\u0650OE\u0381\xa8E\xe1\x1e\xf4\xcb\xcf'\xafq\x9e\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x1c\x04VI\xcdS\xdc#T\x1f\x8e\xd4\xd3A\x81(\b\xd5\u075c\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x1c\x12\x8b\xd6\u0365\xfc\xa2uu\xe4\xb4;2S\xc8\xc4\x17*\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\x13\u04c67\xb9\xa4|\xe7\x9d7\xa8oP\xfb@\x9c\x06\a(\x89Hz\x9a0E9D\x00\x00\u07d4\x1c \x10\xbdf-\xf4\x17\xf2\xa2q\x87\x9a\xfb\x13\xefL\x88\xa3\xae\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x1c%z\u0525Q\x05\xea;X\xed7K\x19\x8d\xa2f\xc8_c\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x1c.6\a\xe1'\xca\xca\x0f\xbd\\YH\xad\xad}\xd80\xb2\x85\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x1c5l\xfd\xb9_\xeb\xb7\x14c;(\xd5\xc12\u0744\xa9\xb46\x89\x01Z\xf1\u05cbX\xc4\x00\x00\u07d4\x1c5\xaa\xb6\x88\xa0\u034e\xf8.vT\x1b\xa7\xac9R\u007ft;\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x1c>\xf0]\xae\x9d\xcb\u0509\xf3\x02D\bf\x9d\xe2D\xc5*\x02\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x1cJ\xf0\xe8c\xd2el\x865\xbco\xfe\xc8\u0759(\x90\x8c\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c`\x19\x93x\x92\a\xf9e\xbb\x86\\\xbbL\xd6W\xcc\xe7o\xc0\x89\x05T\x1ap7P?\x00\x00\u07d4\x1cc\xfa\x9e,\xbb\xf21a\xda3\xa1\xda}\xf7\r\x1b\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1c\xb6\xb2\xd7\xcf\xc5Y\xb7\xf4\x1eoV\xab\x95\xc7\xc9X\xcd\x0eL\x89Hz\x9a0E9D\x00\x00\u07d4\x1c\xc1\xd3\xc1O\x0f\xb8d\x0e6rM\xc42)\xd2\xeaz\x1eH\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x1c\xc9\bv\x00A\t\xcdy\xa3\u07a8f\u02c4\n\xc3d\xba\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xd1\xf0\xa3\x14\u02f2\x00\xde\n\f\xb1\xef\x97\xe9 p\x9d\x97\u0089lk\x93[\x8b\xbd@\x00\x00\u0794\x1c\xdaA\x1b\xd5\x16;\xae\xca\x1eU\x85c`\x1c\xe7 \xe2N\xe1\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1c\xe8\x1d1\xa7\x920\"\xe1%\xbfH\xa3\xe06\x93\xb9\x8d\xc9\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xeb\xf0\x98]\u007fh\n\xaa\x91\\D\xccb\xed\xb4\x9e\xab&\x9e\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1c\xedg\x15\xf8b\xb1\xff\x86\x05\x82\x01\xfc\xceP\x82\xb3nb\xb2\x8a\x01j^`\xbe\xe2s\xb1\x00\x00\u07d4\x1c\xf0L\xb1C\x80\x05\x9e\xfd?#\x8be\u057e\xb8j\xfa\x14\u0609\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1c\xf1\x05\xab#\x02;ULX>\x86\u05d2\x11y\xee\x83\x16\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1c\xf2\xebz\x8c\xca\u00ad\xea\xef\x0e\xe8sG\xd55\u04f9@X\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xfc\xf7Q\u007f\f\bE\x97 \x94+dz\u0452\xaa\x9c\x88(\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\x1d\t\xad$\x12i\x1c\u0141\xc1\xab6\xb6\xf9CL\xd4\xf0\x8bT\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x1d\x15|Xv\xc5\xca\xd5S\xc9\x12\xca\xf6\xce-Rw\xe0\\s\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1d&\x15\xf8\xb6\xcaP\x12\xb6c\xbd\u0414\xb0\xc5\x13|w\x8d\u07ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1d)\u01ea\xb4+ H\u04b2R%\u0518\u06e6z\x03\xfb\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0794\x1d4\x1f\xa5\xa3\xa1\xbd\x05\x1f}\xb8\a\xb6\xdb/\u01faO\x9bE\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1d4N\x96%g\xcb'\xe4M\xb9\xf2\xfa\u01f6\x8d\xf1\xc1\xe6\xf7\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x1d6h0c\xb7\xe9\xeb\x99F-\xab\xd5i\xbd\xdc\xe7\x16\x86\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1d7aky?\x94\x91\x188\xac\x8e\x19\xee\x94I\u07d2\x1e\u0109QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94\x1d9[0\xad\xda\x1c\xf2\x1f\t\x1aOJ{u3q\x18\x94A\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x1dEXn\xb8\x03\xca!\x90e\v\xf7H\xa2\xb1t1+\xb5\a\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1dW.\xdd-\x87\xca'\x1ag\x14\xc1Z;7v\x1d\u0320\x05\x89\x06\xeb\xd5*\x8d\xdd9\x00\x00\u07d4\x1dc0\x97\xa8R%\xa1\xffC!\xb1)\x88\xfd\xd5\\+8D\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x1di\xc8=(\xff\x04t\xce\xeb\xea\xcb:\xd2'\xa1D\xec\u78ca\x01(\xcc\x03\x92\nb\u0480\x00\u07d4\x1d\x96\xbc\u0544W\xbb\xf1\xd3\u00a4o\xfa\xf1m\xbf}\x83hY\x89\tIr\t\xd8F~\x80\x00\u07d4\x1d\x9ej\xaf\x80\x19\xa0_#\x0e]\xef\x05\xaf]\x88\x9b\xd4\xd0\xf2\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x1d\xab\x17.\xff\xa6\xfb\xeeSL\x94\xb1~yN\xda\xc5OU\xf8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1d\xb9\xac\x9a\x9e\xae\xec\nR7W\x05\fq\xf4rx\xc7-P\x89Hz\x9a0E9D\x00\x00\u07d4\x1d\xbe\x8e\x1c+\x8a\x00\x9f\x85\xf1\xad<\xe8\r.\x055\x0e\u3709\aW\rn\x9e\xbb\xe4\x00\x00\u07d4\x1d\xc7\xf7\xda\xd8]\xf5?\x12q\x15$\x03\xf4\xe1\xe4\xfd\xb3\xaf\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1d\u03bc\xb7em\xf5\u072a3h\xa0U\xd2/\x9e\xd6\xcd\xd9@\x89\x1b\x18\x1eK\xf24<\x00\x00\xe0\x94\x1d\xd7tA\x84J\xfe\x9c\xc1\x8f\x15\xd8\xc7{\xcc\xfbe^\xe04\x8a\x01\x06\xebEW\x99D\x88\x00\x00\u07d4\x1d\xde\xfe\xfd5\xab\x8fe\x8b$q\xe5G\x90\xbc\x17\xaf\x98\u07a4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1d\xee\xc0\x1a\xbe\\\r\x95-\xe9\x10l=\xc3\x069\xd8P\x05\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\x1d\xf6\x91\x16rg\x9b\xb0\xef5\t\x03\x8c\f'\xe3\x94\xfd\xfe0\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\x1d\xfa\xee\ar\x12\xf1\xbe\xaf\x0eo/\x18@Sz\xe1T\xad\x86\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1e\x06\r\xc6\xc5\xf1\u02cc\xc7\xe1E.\x02\xee\x16u\b\xb5eB\x8a\x02\xb1O\x02\xc8d\xc7~\x00\x00\xe0\x94\x1e\x13\xecQ\x14,\ubde2`\x83A,<\xe3QD\xbaV\xa1\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x1e\x1aH(\x11\x9b\xe3\t\xbd\x88#nMH+PM\xc5W\x11\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\x1e\x1a\ud178leb\u02cf\xa1\xebo\x8f;\xc9\u072eny\x89\xf4\xd2\u0744%\x9b$\x00\x00\u07d4\x1e\x1ccQwj\xc3\x10\x919~\xcf\x16\x00-\x97\x9a\x1b-Q\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1e\x1dz_$h\xb9N\xa8&\x98-\xbf!%yR*\xb7\xdf\n\u02ac\x9e\xee\xd3Y09\xe5\xacuy\x8a+\x14F\xddj\xef\xe4\x1c\x00\x00\u07d4\x1e{^M\x1fW+\xec\xf2\xc0\x0f\xc9\f\xb4v{Jn3\u0509\x06\x1f\xc6\x10u\x93\xe1\x00\x00\u07d4\x1e\x8eh\x9b\x02\x91|\xdc)$]\f\x9ch\xb0\x94\xb4\x1a\x9e\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xa34\xb5u\b\a\xeat\xaa\u016b\x86\x94\xec_(\xaaw\u03c9\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\x1e\xa4qU\x04\u01af\x10{\x01\x94\xf4\xf7\xb1\xcbo\xcc\xcdoK\x89 \x041\x97\xe0\xb0'\x00\x00\u07d4\x1e\xa4\x92\xbc\xe1\xad\x10~3\u007fK\u0527\xac\x9a{\xab\xcc\u036b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x1e\xa6\xbf/\x15\xae\x9c\x1d\xbcd\u06a7\xf8\xeaM\r\x81\xaa\xd3\xeb\x89\u3bb5sr@\xa0\x00\x00\u07d4\x1e\xb4\xbfs\x15j\x82\xa0\xa6\x82 \x80\xc6\xed\xf4\x9cF\x9a\xf8\xb9\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\x1e\xba\xcbxD\xfd\xc3\"\xf8\x05\x90O\xbf\x19b\x80-\xb1S|\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1e\xc4\xecKw\xbf\x19\u0411\xa8h\xe6\xf4\x91T\x18\x05A\xf9\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xd0n\xe5\x16b\xa8lcE\x88\xfbb\xdcC\xc8\xf2~|\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1e\u063b?\x06w\x8b\x03\x9e\x99a\xd8\x1c\xb7\x1as\xe6x|\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xda\bNye\x00\xba\x14\xc5\x12\x1c\r\x90\x84of\xe4\xbeb\x89\x1c\xfd\xd7F\x82\x16\xe8\x00\x00\u07d4\x1e\xeel\xbe\xe4\xfe\x96\xadaZ\x9c\xf5\x85zdy@\u07ccx\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4\x1e\xf2\u073f\xe0\xa5\x00A\x1d\x95n\xb8\u0213\x9c=l\xfef\x9d\x89*\x11)\u0413g \x00\x00\xe0\x94\x1e\xf5\xc9\xc76P\u03fb\xde\\\x88U1\xd4'\xc7\xc3\xfeUD\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1f\x04\x12\xbf\xed\u0356N\x83}\t,q\xa5\xfc\xba\xf3\x01&\xe2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\x17O@\xa0Dr4\xe6fS\x91Mu\xbc\x00>V\x90\u0709\b\xacr0H\x9e\x80\x00\x00\u07d4\x1f!\x86\xde\xd2>\f\xf9R\x16\x94\xe4\xe1dY>i\n\x96\x85\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x1f*\xfc\n\xed\x11\xbf\xc7\x1ew\xa9\ae{6\xeav\xe3\xfb\x99\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\x1f9Y\xfc)\x11\x10\xe8\x822\xc3kvg\xfcx\xa3ya?\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1f=\xa6\x8f\xe8~\xafC\xa8)\xabm~\u0166\xe0\t\xb2\x04\xfb\x89\x1e\x16\x01u\x8c,~\x00\x00\u07d4\x1fI\xb8m\r9EY\x06\x98\xa6\xaa\xf1g<7u\\\xa8\r\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\x1f_;4\xbd\x13K'\x81\xaf\xe5\xa0BJ\u0144l\xde\xfd\x11\x89\x05]\xe6\xa7y\xbb\xac\x00\x00\u07d4\x1fo\x0004\x97R\x06\x1c\x96\a+\xc3\xd6\xeb5I \x8dk\x89\x01K\x8d\xe1\xeb\x88\u06c0\x00\u07d4\x1f}\x8e\x86\xd6\xee\xb0%E\xaa\xd9\x0e\x912{\xd3i\xd7\xd2\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\x81\x16\xbd\n\xf5W\x0e\xaf\fV\u011cz\xb5\xe3zX\x04X\x89lk\x93[\x8b\xbd@\x00\x00\u0794\x1f\x88\xf8\xa13\x8f\xc7\xc1\tv\xab\xcd?\xb8\u04c5T\xb5\uc708\xb9\xf6]\x00\xf6<\x00\x00\u07d4\x1f\x9c2hE\x8d\xa3\x01\xa2\xbeZ\xb0\x82W\xf7{\xb5\xa9\x8a\xa4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1f\xa21\x9f\xed\x8c-F*\xdf.\x17\xfe\xecjo0Qn\x95\x89\x06\xca\xe3\x06!\xd4r\x00\x00\u07d4\x1f\xb4c\xa08\x99\x83\xdf}Y?{\xddmxI\u007f\xed\x88y\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\xb7\xbd1\r\x95\xf2\xa6\u067a\xaf\x8a\x8aC\n\x9a\x04E:\x8b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x1f\xcc|\xe6\xa8HX\x95\xa3\x19\x9e\x16H\x1fr\xe1\xf7b\xde\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1f\xcf\xd1\xd5\u007f\x87\"\x90V\f\xb6-`\x0e\x1d\xef\xbe\xfc\xcc\x1c\x89P\xc5\xe7a\xa4D\b\x00\x00\u0794\x1f\u0496\xbe\x03\xads|\x92\xf9\u0186\x9e\x8d\x80\xa7\x1cW\x14\xaa\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x1f\xdd\xd8_\u024b\xe9\xc4\x04Ya\xf4\x0f\x93\x80^\xccEI\xe5\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4 \x01\xbe\xf7{f\xf5\x1e\x15\x99\xb0/\xb1\x10\x19J\x00\x99\xb7\x8d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4 \x02d\xa0\x9f\x8ch\xe3\xe6b\x97\x95(\x0fV%O\x86@\u0409\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \x03qy\a\xa7%`\xf40\u007f\x1b\xee\xccT6\xf4=!\xe7\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4 \r\xfc\vq\xe3Y\xb2\xb4eD\n6\xa6\xcd\xc3Rw0\a\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4 \x13L\xbf\xf8\x8b\xfa\xdcFkR\xec\ua9d8W\x89\x1d\x83\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4 \x14&\x1f\x01\b\x9fSyV0\xba\x9d\xd2O\x9a4\xc2\xd9B\x89Hz\x9a0E9D\x00\x00\u07d4 \x16\x89]\xf3,\x8e\xd5G\x82iF\x84#\xae\xa7\xb7\xfb\xceP\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \x18\x1cKA\xf6\xf9r\xb6iX!_\x19\xf5p\xc1]\xdf\xf1\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4 \x18d\xa8\xf7\x84\xc2'{\v|\x9e\xe74\xf7\xb3w\xea\xb6H\x89\xf2(\x14\x00\xd1\xd5\xec\x00\x00\u07d4 \xb8\x1a\xe59&\xac\xe9\xf7\xd7AZ\x05\f\x03\x1dX_ \x89\x12\u007f\x19\xe8>\xb3H\x00\x00\xe0\x94 \x1d\x9e\xc1\xbc\v\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4 \xa1RV\xd5\f\xe0X\xbf\x0e\xacC\xaaS:\xa1n\u0273\x80\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \xa2\x9cPy\xe2k?\x181\x8b\xb2\xe5\x0e\x8e\x8b4n[\xe8\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4 \xa8\x16\x80\xe4e\xf8\x87\x90\xf0\aO`\xb4\xf3_]\x1ej\xa5\x89Ea\x80'\x8f\fw\x80\x00\u07d4 \xb9\xa9\u6f48\x80\u0659J\xe0\r\u0439(*\v\xea\xb8\x16\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4 \u0084\xba\x10\xa2\b0\xfc=i\x9e\xc9}-\xfa'\xe1\xb9^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4 \xd1A\u007f\x99\xc5i\u3fb0\x95\x85e0\xfe\x12\xd0\xfc\uaa89@\x15\xf9K\x11\x83i\x80\x00\u07d4 \u074f\u02f4n\xa4o\u3066\x8b\x8c\xa0\xea[\xe2\x1f\u9949lk\x93[\x8b\xbd@\x00\x00\xe0\x94 \xff>\u078c\xad\xb5\xc3{H\xcb\x14X\x0f\xb6^#\t\n{\x8a\b\xe4\xd3\x16\x82v\x86@\x00\x00\xe0\x94!\x008\x1d`\xa5\xb5J\xdc\t\u0456\x83\xa8\xf6\u057bK\xfb\u02ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94!\x18\xc1\x16\xab\f\xdfo\xd1\x1dT\xa40\x93\a\xb4w\xc3\xfc\x0f\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94!\x1b)\xce\xfcy\xae\x97gD\xfd\xeb\u03bd<\xbb2\xc5\x13\x03\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4! l\xe2.\xa4\x80\xe8Y@\xd3\x13\x14\xe0\xd6ONM:\x04\x8965\u026d\xc5\u07a0\x00\x00\u07d4!2\xc0Qj.\x17\x17J\xc5G\xc4;{\x00 \xd1\xebLY\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94!@\x8bMz,\x0en\xcaAC\xf2\xca\u037b\u033a\x12\x1b\u060a\x04<3\xc1\x93ud\x80\x00\x00\u07d4!Kt9U\xa5\x12\xden\r\x88j\x8c\xbd\x02\x82\xbe\xe6\u04a2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!L\x89\u017d\x8e}\"\xbcWK\xb3^H\x95\x02\x11\xc6\xf7v\x89\x01\x06T\xf2X\xfd5\x80\x00\xe0\x94!Ti\x14\xdf\u04ef*\xddA\xb0\xff>\x83\xff\xdat\x14\xe1\xe0\x8a\x01C\x95\xe78ZP.\x00\x00\u07d4!X.\x99\xe5\x02\xcb\xf3\xd3\xc2;\xdf\xfbv\xe9\x01\xacmV\xb2\x89\x05k\xc7^-c\x10\x00\x00\u07d4!Y$\b\x13\xa70\x95\xa7\xeb\xf7\u00f3t>\x80(\xae_\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!`\xb4\xc0,\xac\n\x81\u0791\b\xdeCE\x90\xa8\xbf\xe6\x875\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94!nA\x86N\xf9\x8f\x06\r\xa0\x8e\xca\xe1\x9a\xd1\x16j\x17\xd06\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4!\x84o/\xdfZA\xed\x8d\xf3n^\xd8TM\xf7Y\x88\xec\xe3\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94!\xa6\xdbe'F{\xc6\xda\xd5K\xc1n\x9f\xe2\x95;g\x94\xed\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4!\xa6\xfe\xb6\xab\x11\xc7f\xfd\xd9w\xf8\xdfA!\x15_G\xa1\xc0\x89\x03\x19\xcf8\xf1\x00X\x00\x00\u07d4!\xb1\x82\xf2\xda+8D\x93\xcf_5\xf8=\x9d\x1e\xe1O*!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!\xbf\xe1\xb4\\\xac\xdebt\xfd\x86\b\u0661x\xbf>\xebn\u0709l\xee\x06\u077e\x15\xec\x00\x00\u07d4!\xc0s\x80HOl\xbc\x87$\xad2\xbc\x86L;Z\xd5\x00\xb7\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94!\u00e8\xbb\xa2g\xc8\u0322{\x1a\x9a\xfa\xba\xd8o`z\xf7\b\x8a\x01\xe4\xa3lI\u06580\x00\x00\u07d4!\xcem[\x90\x18\xce\xc0J\u0596yD\xbe\xa3\x9e\x800\xb6\xb8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4!\xd0'\x05\xf3\xf6I\x05\xd8\x0e\xd9\x14y\x13\xea\x8cs\a\u0595\x89I\xed\xb1\xc0\x98\x876\x00\x00\u07d4!\xd1?\f@$\xe9g\xd9G\a\x91\xb5\x0f\"\xde:\xfe\xcf\x1b\x89\xf1Z\xd3^.1\xe5\x00\x00\xe0\x94!\xdb\u06c1z\r\x84\x04\u01bd\xd6\x15\x047N\x9cC\xc9!\x0e\x8a\x02\x1e\x18\xb9\xe9\xabE\xe4\x80\x00\xe0\x94!\xdf\x1e\xc2KNK\xfey\xb0\xc0\x95\u03ba\xe1\x98\xf2\x91\xfb\u044a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94!\xdf-\u036ft\xb2\xbf\x804\x04\xddM\xe6\xa3^\xab\xec\x1b\xbd\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4!\xe2\x19\u021c\xa8\xac\x14\xaeL\xbaa0\xee\xb7}\x9em9b\x89*\u035f\xaa\xa08\xee\x00\x00\u07d4!\xe5\u04ba\xe9\x95\xcc\xfd\b\xa5\xc1k\xb5$\xe1\xf60D\x8f\x82\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4!\xe5\xd7s 0L \x1c\x1eS\xb2a\xa1#\u0421\x06>\x81\x89\x04\xb6\xfa\x9d3\xddF\x00\x00\xe0\x94!\xea\xe6\xfe\xff\xa9\xfb\xf4\u0347OG9\xac\xe50\u033eY7\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4!\xec\xb2\u07e6Wy\xc7Y-\x04\x1c\xd2\x10Z\x81\xf4\xfdNF\x8965\u026d\xc5\u07a0\x00\x00\u07d4!\uff20\x9b5\x80\xb9\x8es\xf5\xb2\xf7\xf4\xdc\v\xf0,R\x9c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!\xfd\v\xad\xe5\xf4\xeftt\xd0X\xb7\xf3\xd8T\xcb\x13\x00RN\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94!\xfdG\xc5%`\x12\x19\x8f\xa5\xab\xf11\xc0mj\xa1\x96_u\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4!\xfdl]\x97\xf9\xc6\x00\xb7h!\xdd\xd4\xe7v5\x0f\xce+\xe0\x89lj\u04c2\xd4\xfba\x00\x00\u07d4\"\r\u018d\xf0\x19\xb6\xb0\u033f\xfbxKZZ\xb4\xb1]@`\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\"\x0e+\x92\xc0\xf6\xc9\x02\xb5\x13\xd9\xf1\xe6\xfa\xb6\xa8\xb0\xde\xf3\u05c9+^:\xf1k\x18\x80\x00\x00\u07d4\"V\x1cY1\x14560\x9c\x17\xe82X{b\\9\v\x9a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\"W\xfc\xa1jn\\*d|<)\xf3l\xe2)\xab\x93\xb1~\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\"]5\xfa\xed\xb3\x91\u01fc-\xb7\xfa\x90q\x16\x04\x05\x99m\x00\x89\t\x18T\xfc\x18bc\x00\x00\u07d4\"_\x9e\xb3\xfbo\xf3\xe9\xe3\xc8D~\x14\xa6n\x8dO7y\xf6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\"r\x18n\xf2}\xcb\xe2\xf5\xfc70P\xfd\xae\u007f*\xce#\x16\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4\"s\xba\u05fcNHv\"\xd1u\xefzf\x98\x8bj\x93\xc4\xee\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\"v&K\xec\x85&\xc0\xc0\xf2pgz\xba\xf4\xf0\xe4A\xe1g\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\"\x82B\xf83n\xec\xd8$.\x1f\x00\x0fA\x93~q\xdf\xfb\xbf\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\"\x84*\xb80\xdaP\x99\x13\xf8\x1d\xd1\xf0O\x10\xaf\x9e\xdd\x1cU\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\"\x94O\xbc\xa9\xb5yc\bN\xb8M\xf7\xc8_\xb9\xbc\u07f8V\x89\xfc\x11\x8f\uf43a8\x80\x00\u07d4\"\x9c\xc4q\x1bbu^\xa2\x96DZ\u00f7\u007f\xc63\x82\x1c\xf2\x89\x02#\xe8\xb0R\x192\x80\x00\u0794\"\x9eC\r\xe2\xb7OD&Q\xdd\u0377\x01v\xbc\x05L\xadT\x88\xbb\xf9\x81\xbcJ\xaa\x80\x00\u07d4\"\x9fO\x1a*OT\atP[G\a\xa8\x1d\xe4D\x10%[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\"\x9f\xf8\v\xf5p\x80\t\xa9\xf79\xe0\xf8\xb5`\x91@\x16\u0566\x89\x12\x11\xec\xb5m\x13H\x80\x00\u07d4\"\xa2X\x12\xabV\xdc\xc4#\x17^\xd1\u062d\xac\xce3\xcd\x18\x10\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94\"\xb9j\xb2\xca\xd5]\xb1\x00\xb50\x01\xf9\xe4\xdb7\x81\x04\xc8\a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\"\xbd\xff\xc2@\xa8\x8f\xf7C\x1a\xf3\xbf\xf5\x0e\x14\xda7\xd5\x18>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\"\xce4\x91Y\xee\xb1D\xef\x06\xff&6X\x8a\xefy\xf6(2\x89\n1\x06+\xee\xedp\x00\x00\u07d4\"\xdbU\x9f,<\x14u\xa2\xe6\xff\xe8:YyY\x91\x96\xa7\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\"\xe1QX\xb5\xee>\x86\xeb\x032\xe3\u6a6cl\u0675^\u0349\b\xacr0H\x9e\x80\x00\x00\u07d4\"\xe2H\x8e-\xa2jI\xae\x84\xc0\x1b\xd5K!\xf2\x94x\x91\u0189]\u0212\xaa\x111\xc8\x00\x00\u07d4\"\xe5\x12\x14\x9a\x18\xd3i\xb7\x86\xc9\xed\xab\xaf\x1d\x89N\xe0.g\x14a\\\x00\x00\u07d4\"\xeb}\xb0\xbaV\xb0\xf8\xb8\x16\u0332\x06\xe6\x15\xd9)\x18[\r\x89\x04])s~\"\xf2\x00\x00\u07d4\"\xee\xd3'\xf8\xeb\x1d\x138\xa3\xcb{\x0f\x8aK\xaaY\a\u0355\x89\x01E]_Hw\b\x80\x00\xe0\x94\"\xf0\x04\u07cd\xe9\xe6\xeb\xf5#\u032c\xe4W\xac\xcb&\xf9r\x81\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794\"\xf2\xdc\xffZ\u05cc>\xb6\x85\v\\\xb9Q\x12{e\x95\"\u623e -j\x0e\xda\x00\x00\u07d4\"\xf3\xc7y\xddy\x02>\xa9*x\xb6\\\x1a\x17\x80\xf6-\\J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\"\xfe\x88M\x907)\x1bMR\xe6(Z\xe6\x8d\xea\v\xe9\xff\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\x06\u07d3\x1a\x94\rX\xc0\x16e\xfaM\b\x00\x80,\x02\xed\xfe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94#\t\xd3@\x91D[22Y\v\xd7\x0fO\x10\x02[,\x95\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4#\x12\x00F\xf6\x83!\x02\xa7R\xa7fVi\x1c\x86>\x17\u5709\x11\xe0\xe4\xf8\xa5\v\xd4\x00\x00\u07d4#\x1a\x15\xac\xc1\x99\u021f\xa9\xcb\"D\x1c\xc7\x030\xbd\xcc\xe6\x17\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4#\x1d\x94\x15]\xbc\xfe*\x93\xa3\x19\xb6\x17\x1fc\xb2\v\u04b6\xfa\x89\xcf\x14{\xb9\x06\xe2\xf8\x00\x00\u07d4#(2\xcdYw\xe0\nL0\xd0\x16?.$\xf0\x88\xa6\xcb\t\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4#,m\x03\xb5\xb6\xe6q\x1e\xff\xf1\x90\xe4\x9c(\xee\xf3l\x82\xb0\x89Hz\x9a0E9D\x00\x00\xe0\x94#,\xb1\xcdI\x99<\x14J?\x88\xb3a\x1e#5i\xa8k\u058a\x03L`lB\u042c`\x00\x00\u07d4#,\xe7\x82Pb%\xfd\x98`\xa2\xed\xc1Jz0Gsm\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4#/R]U\x85\x9b}N`\x8d H\u007f\xaa\xdb\x00)15\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94#4\u0150\u01e4\x87i\x100E\u0176SL\x8a4i\xf4J\x8a\x03\xb1\x99\a=\xf7-\xc0\x00\x00\u07d4#7n\u02bftl\xe53!\xcfB\xc8fI\xb9+g\xb2\xff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#7\x8fB\x92m\x01\x84\xb7\x93\xb0\xc8'\xa6\xdd>=3O\u0349\x03\t'\xf7L\x9d\xe0\x00\x00\u07d4#8B\xb1\xd0i/\xd1\x11@\xcfZ\u0364\xbf\x960\xba\xe5\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#9\xe9I(p\xaf\xea%7\xf3\x89\xac/\x83\x83\x02\xa3<\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#;\xdd\xdd]\xa9HR\xf4\xad\xe8\xd2\x12\x88V\x82\xd9\ak\u0189\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#OF\xba\xb7?\xe4]1\xbf\x87\xf0\xa1\xe0Fa\x99\xf2\ubb09\x1aJ\xba\"\\ t\x00\x00\u07d4#U\x1fV\x97_\xe9+1\xfaF\x9cI\xeaf\xeefb\xf4\x1e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4#V\x95B\xc9}V`\x18\xc9\a\xac\xfc\xf3\x91\xd1@g\xe8~\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94#_\xa6l\x02^\xf5T\x00p\xeb\xcf\r7-\x81w\xc4g\xab\x8a\a\x12\x9e\x1c\xdf7>\xe0\x00\x00\xe0\x94#r\xc4\xc1\u0253\x9fz\xafl\xfa\xc0@\x90\xf0\x04t\x84\n\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4#s\f5z\x91\x02nD\xb1\xd0\xe2\xfc*Q\xd0q\xd8\xd7{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#v\xad\xa9\x033\xb1\u0441\bL\x97\xe6E\xe8\x10\xaa[v\xf1\x89(\xa8WBTf\xf8\x00\x00\u07d4#x\xfdC\x82Q\x1e\x96\x8e\u0452\x10g7\xd3$\xf4T\xb55\x8965\u026d\xc5\u07a0\x00\x00\u07d4#\x82\xa9\u050e\xc8>\xa3e(\x90\xfd\x0e\u7710{[-\xc1\x89\a?u\u0460\x85\xba\x00\x00\u07d4#\x83\xc2\"\xe6~\x96\x91\x90\xd3!\x9e\xf1M\xa3xP\xe2lU\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\x8akv5%/RDHl\n\xf0\xa7: s\x85\xe09\x89JD\x91\xbdm\xcd(\x00\x00\u07d4#\x9as>k\x85Z\u0152\xd6c\x15a\x86\xa8\xa1t\xd2D\x9e\x89X\xbe7X\xb2A\xf6\x00\x00\xe0\x94#\xab\t\xe7?\x87\xaa\x0f;\xe0\x13\x9d\xf0\xc8\xebk\xe5cO\x95\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94#\xab\xd9\xe9>yW\xe5\xb66\xbeey\x05\x1c\x15\xe5\xce\v\x0e\x8a\x03\xa3\xc8\xf7\xcb\xf4,8\x00\x00\u07d4#\xb1\u0111\u007f\xbd\x93\xee=H8\x93\x06\x95s\x84\xa5Il\xbf\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94#\xba8d\xdaX=\xabV\xf4 \x87<7g\x96\x90\xe0/\x00\x8a\x02\x13BR\r_\xec \x00\x00\u07d4#\xc5Z\xebW9\x87o\n\xc8\xd7\xeb\xea\x13\xber\x96\x85\xf0\x00\x89Hz\x9a0E9D\x00\x00\u07d4#\u025b\xa0\x87D\x8e\x19\xc9p\x1d\xf6n\f\xabR6\x831\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xcc\xc3\u01ac\xd8\\.F\fO\xfd\xd8+\xc7]\xc8I\xea\x14\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#\xcd%\x98\xa2\x0e\x14\x9e\xad*\u0593yWn\xce\xdb`\u3389lk\x93[\x8b\xbd@\x00\x00\u07d4#\u07cfH\xee\x00\x92V\xeay~\x1f\xa3i\xbe\xeb\xcfk\xc6c\x89|\xd3\xfa\xc2m\x19\x81\x80\x00\u07d4#\xe2\u01a8\xbe\x8e\n\u03e5\xc4\xdf^6\x05\x8b\xb7\u02ecZ\x81\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xeaf\x9e5d\x81\x9a\x83\xb0\xc2l\x00\xa1m\x9e\x82olF\x89M\x8dl\xa9h\xca\x13\x00\x00\u07d4#\xebo\xd8Vq\xa9\x06:\xb7g\x8e\xbe&Z \xf6\x1a\x02\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xf9\xec\xf3\xe5\xdd\u0723\x88\x15\xd3\xe5\x9e\xd3K[\x90\xb4\xa3S\x89\v\x17\x81\xa3\xf0\xbb \x00\x00\u07d4#\xfa~\xb5\x1aH\"\x95\x98\xf9~v+\xe0\x86\x96R\xdf\xfcf\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94$\x03\x05rs\x13\xd0\x1esT,w_\xf5\x9d\x11\xcd5\xf8\x19\x8a\x01A\x88Vf\x80\u007f\\\x80\x00\u07d4$\x04k\x91\u069ba\xb6)\u02cb\x8e\xc0\xc3Q\xa0~\a\x03\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4$\x0eU\x9e'J\xae\xf0\xc2X\x99\x8c\x97\x9fg\x1d\x11s\xb8\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94$\x13aU\x9f\xee\xf8\x0e\xf170!S\xbd\x9e\xd2\xf2]\xb3\xef\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94$;;\xcaj)\x93Y\xe8\x86\xce3\xa3\x03A\xfa\xfeMW=\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4$<\x84\xd1$ W\f\xc4\xef;\xab\xa1\xc9Y\u0083$\x95 \x89\u007f\x1fi\x93\xa8S\x04\x00\x00\xe0\x94$CJ>2\xe5N\xcf'/\xe3G\v_oQ/gU \x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4$HYo\x91\xc0\x9b\xaa0\xbc\x96\x10j-7\xb5p^](\x89lk\x93[\x8b\xbd@\x00\x00\u0794$Xn\xc5E\x175\xee\xaa\xebG\r\xc8sj\xaeu/\x82\xe5\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4$X\xd6U_\xf9\x8a\x12\x9c\xce@7\x95=\x00 n\xffB\x87\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4$b\x91\x16[Y3-\xf5\xf1\x8c\xe5\u0248V\xfa\xe9X\x97\u0589\\(=A\x03\x94\x10\x00\x00\u07d4$g\u01a5\u0196\xed\xe9\xa1\xe5B\xbf\x1a\xd0k\xccK\x06\xac\xa0\x89\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4$v\xb2\xbbu\x1c\xe7H\xe1\xa4\xc4\xff{#\v\xe0\xc1]\"E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4$z\n\x11\xc5\u007f\x03\x83\xb9I\xdeT\vf\xde\xe6\x86\x04\xb0\xa1\x899\xfb\xae\x8d\x04-\xd0\x00\x00\u07d4$\x87\xc3\u013e\x86\xa2r=\x91|\x06\xb4XU\x01p\xc3\xed\xba\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\x89\xac\x12i4\xd4\u05a9M\xf0\x87C\xda{v\x91\xe9y\x8e\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\x9d\xb2\x9d\xbc\x19\xd1#]\xa7)\x8a\x04\b\x1c1WB\u9b09a\xac\xff\x81\xa7\x8a\xd4\x00\x00\u07d4$\xa4\xeb6\xa7\xe4\x98\xc3o\x99\x97\\\x1a\x8dr\x9f\u05b3\x05\u05c9\r\xfcx!\x0e\xb2\xc8\x00\x00\u07d4$\xa7P\xea\xe5\x87G\x11\x11m\xd7\xd4{q\x86\u0399\r1\x03\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4$\xaa\x11Q\xbbv_\xa3\xa8\x9c\xa5\x0e\xb6\xe1\xb1\xc7\x06A\u007f\u0509\xa8\r$g~\xfe\xf0\x00\x00\u0794$\xac\xa0\x8d[\xe8^\xbb\x9f12\xdf\xc1\xb6 \x82N\xdf\xed\xf9\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4$\xb2\xbe\x11\x8b\x16\u0632\x17Gi\xd1{L\xf8O\a\u0294m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4$\xb8\xb4F\u07bd\x19G\x95]\u0404\xf2\xc5D\x933F\u04ed\x89\xeaim\x90@9\xbd\x80\x00\u07d4$\xb9^\xbe\xf7\x95\x00\xba\xa0\xed\xa7.w\xf8wA]\xf7\\3\x891T\xc9r\x9d\x05x\x00\x00\u07d4$\xb9\xe6dOk\xa4\xcd\xe1&'\r\x81\xf6\xab`\xf2\x86\xdf\xf4\x89\a?u\u0460\x85\xba\x00\x00\u07d4$\xbdY\x04\x05\x90\x91\xd2\xf9\xe1-j&\xa0\x10\xca\"\xab\x14\xe8\x89e\xea=\xb7UF`\x00\x00\u07d4$\xc0\u020bT\xa3TG\t\x82\x8a\xb4\xab\x06\x84\x05Y\xf6\xc5\u2250\xf54`\x8ar\x88\x00\x00\u07d4$\xc1\x17\xd1\u04b3\xa9z\xb1\x1aFy\u025awJ\x9e\xad\xe8\u044965\u026d\xc5\u07a0\x00\x00\u07d4$\xcf\xf0\xe93j\x9f\x80\xf9\xb1\u02d6\x8c\xafk\x1d\x1cI2\xa4\x89\n\xdaUGK\x814\x00\x00\u07d4$\u06aa\xdd\xf7\xb0k\xbc\ua6c0Y\x00\x85\xa8\x85gh+N\x89\x11K \x15\u04bb\xd0\x00\x00\u07d4$\xdc\xc2K\xd9\xc7!\f\xea\u03f3\r\xa9\x8a\xe0JM{\x8a\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\xf7E\r\xdb\xf1\x8b\x02\x0f\xeb\x1a 2\xd9\xd5Kc>\xdf7\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4$\xfcs\xd2\a\x93\t\x8e\t\u076bW\x98Pb$\xfa\x1e\x18P\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4$\xfd\x9al\x87L/\xab?\xf3n\x9a\xfb\xf8\xce\r2\xc7\u0792\x89Hz\x9a0E9D\x00\x00\u07d4%\n@\xce\xf3 #\x97\xf2@F\x95H\xbe\xb5bj\xf4\xf2<\x89\x05\x03\xb2\x03\xe9\xfb\xa2\x00\x00\u07d4%\niC\av\xf64w\x03\xf9R\x97\x83\x95Za\x97\xb6\x82\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4%\x0e\xb7\xc6o\x86\x9d\xdfI\u0685\xf39>\x98\f\x02\x9a\xa44\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%\x10j\xb6u]\xf8mkc\xa1\x87p;\f\xfe\xa0\u5520\x89\x01|@Z\xd4\x1d\xb4\x00\x00\xe0\x94%\x18_2Z\xcf-dP\x06\x98\xf6\\v\x9d\xdfh0\x16\x02\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4%\x1c\x12r,hy\"y\x92\xa3\x04\xeb5v\xcd\x18CN\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\x1eh8\xf7\xce\u0173\x83\xc1\xd9\x01F4\x12t\xda\xf8\xe5\x02\x89\a\xff\x1c\xcbua\xdf\x00\x00\u07d4%%\x9d\x97Z!\xd8:\xe3\x0e3\xf8\x00\xf5?7\u07e0\x198\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4%({\x81_\\\x828\ns\xb0\xb1?\xba\xf9\x82\xbe$\xc4\u04c9\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94%+eU\xaf\u0700\xf2\xd9m\x97-\x17\u06c4\xeaZ\xd5!\xac\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4%8S)6\x81<\x91\xe6S(O\x01|\x80\u00f8\xf8\xa3o\x89l\x87T\xc8\xf3\f\b\x00\x00\xe0\x94%>2\xb7N\xa4I\n\xb9&\x06\xfd\xa0\xaa%{\xf2=\u02cb\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94%?\x1et*,\uc1b0\u05f3\x06\xe5\xea\xcbl\xcb/\x85T\x8a\x04>^\xde\x1f\x87\x8c \x00\x00\u07d4%A1J\v@\x8e\x95\xa6\x94DIwq*Pq5\x91\xab\x89X\x9e\x1a]\xf4\u05f5\x00\x00\u07d4%L\x1e\xccc\f(w\u0780\x95\xf0\xa8\u06e1\xe8\xbf\x1fU\f\x89\\(=A\x03\x94\x10\x00\x00\u07d4%Z\xbc\x8d\b\xa0\x96\xa8\x8f=j\xb5_\xbcsR\xbd\u0739\u0389\x04t6\x821>\u0780\x00\u07d4%[\xdddt\u0302b\xf2j\"\u00cfE\x94\x0e\x1c\ue99b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%`\xb0\x9b\x89\xa4\xaehI\xedZ<\x99XBf1qDf\x89\\(=A\x03\x94\x10\x00\x00\u07d4%a\xa18\xdc\xf8;\xd8\x13\xe0\xe7\xf1\bd+\xe3\xde=o\x05\x8964\xf4\x84\x17@\x1a\x00\x00\u0794%a\xec\x0f7\x92\x18\xfe^\xd4\xe0(\xa3\xf7D\xaaAuLr\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u0794%b\x92\xa1\x91\xbd\xda4\xc4\xdakk\u0591G\xbfu\u2a6b\x88\xc2\xff.\r\xfb\x03\x80\x00\u07d4%i~\xf2\f\u032ap\xd3-7o\x82r\xd9\xc1\a\f=x\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4%o\xa1P\u0307\xb5\x05j\a\xd0\x04\xef\xc8E$s\x9eb\xb5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4%r\x1c\x87\xb0\xdc!7|r\x00\xe5$\xb1J\"\xf0\xafi\xfb\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%\x899\xbb\xf0\f\x9d\xe9\xafS8\xf5\xd7\x14\xab\xf6\xd0\xc1\xc6q\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94%\x90\x12hp\xe0\xbd\xe8\xa6c\xab\x04\nr\xa5W=\x8dA\u008a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4%\x9e\xc4\xd2e\xf3\xabSk|p\xfa\x97\xac\xa1Bi,\x13\xfc\x89\x01\x1b\x1b[\xea\x89\xf8\x00\x00\xe0\x94%\xa5\x00\xee\xeczf*\x84\x15R\xb5\x16\x8bp{\r\xe2\x1e\x9e\x8a\x02\x1f/o\x0f\xc3\xc6\x10\x00\x00\xe0\x94%\xa5\xa4M8\xa2\xf4Lj\x9d\xb9\u037ck\x1e.\x97\xab\xb5\t\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4%\xa7L*\xc7]\u023a\xa8\xb3\x1a\x9c|\xb4\xb7\x82\x9b$V\u0689lk\x93[\x8b\xbd@\x00\x00\xe0\x94%\xad\xb8\xf9o9I,\x9b\xb4|^\u0708bNF\aV\x97\x8a\x05\xa9\x94\v\xc5hyP\x00\x00\u07d4%\xae\xe6\x8d\t\xaf\xb7\x1d\x88\x17\xf3\xf1\x84\xecV/x\x97\xb74\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\xb0S;\x81\xd0*a{\x92)\xc7\xec]o/g.[Z\x8965\u026d\xc5\u07a0\x00\x00\u07d4%\xb7\x8c\x9f\xad\x85\xb43C\xf0\xbf\xcd\x0f\xac\x11\u0254\x9c\xa5\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\xbcI\xef(\x8c\xd1e\xe5%\xc6a\xa8\x12\u03c4\xfb\xec\x8f3\x89\x12Y!\xae\xbd\xa9\xd0\x00\x00\u07d4%\xbd\xfa>\xe2o8Ia{#\x00bX\x8a\x97\xe3\xca\xe7\x01\x8965\xe6\x19\xbb\x04\xd4\x00\x00\u07d4%\xc1\xa3~\xe5\xf0\x82e\xa1\xe1\r=\x90\xd5G)U\xf9x\x06\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4%\xc6\xe7O\xf1\xd9(\u07d8\x13z\xf4\u07c40\xdf$\xf0|\u05c9\x15$VU\xb1\x02X\x00\x00\xe0\x94%\xcf\xc4\xe2\\5\xc1;i\xf7\xe7}\xbf\xb0\x8b\xafXuk\x8d\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94%\xda\u0515\xa1\x1a\x86\xb9\xee\xec\xe1\xee\xec\x80^W\xf1W\xfa\xff\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94%\xe07\xf0\n\x18'\v\xa5\xec4 \"\x9d\xdb\n,\u33e2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4%\xe6a\xc99\x86:\xcc\x04No\x17\xb5i\x8c\xce7\x9e\xc3\u0309JD\x91\xbdm\xcd(\x00\x00\u07d4&\x04\x8f\xe8M\x9b\x01\nb\xe71b~I\xbc.\xb7?@\x8f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4&\x06\u00f3\xb4\xca\x1b\t\x14\x98`,\xb1\x97\x8b\xf3\xb9R!\xc0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4&\n#\x0eDe\a~\v\x14\xeeDB\xa4\x82\u0570\xc9\x14\xbf\x89Z\xf6\x06\xa0k[\x11\x80\x00\u07d4&\r\xf8\x94:\x8c\x9a]\xbayE2\u007f\xd7\xe0\x83|\x11\xad\a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4&\x14\xf4-]\xa8D7ux\xe6\xb4H\xdc$0[\xef+\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\x15\x10\x0e\xa7\xe2[\xba\x9b\xcat`X\xaf\xbb\xb4\xff\xbeBD\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4&\x15u\xe9\xcfY\xc8\"o\xa7\xaa\xf9\x1d\xe8o\xb7\x0fZ\u00ee\x89\x10C\xa4CjR?\x00\x00\xe0\x94&\x1e\x0f\xa6LQ\x13te\xee\xcf[\x90\xf1\x97\xf7\x93\u007f\xdb\x05\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4&*\x8b\xfd}\x9d\xc5\xdd:\u05c1a\xb6\xbbV\b$76U\x89?j\x83\x84\a+v\x00\x00\xe0\x94&*\xedK\xc0\xf4\xa4\xb2\xc6\xfb5y>\x83ZI\x18\x9c\xdf\xec\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94&-\xc16L\xcfm\xf8\\C&\x8e\xe1\x82UM\xaei.)\x8a\x01\v /\xect\xce\xd8\x00\x00\u07d4&8\x140\x9d\xe4\xe65\xcfX^\r6Tw\xfc@\xe6l\xf7\x89\a\xea(2uw\b\x00\x00\u07d4&9\xee\xe9\x87<\xee\xc2o\u0314T\xb5H\xb9\xe7\xc5J\xa6\\\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94&>W\xda\xcb\xe0\x14\x9f\x82\xfee\xa2fH\x98\x86o\xf5\xb4c\x8a\b\v\xfb\xef\xcb_\v\xc0\x00\x00\u07d4>\x19\xc0m_\x14z\xa5\x97$\x8e\xb4l\xf7\xbe\xfad\xa5\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4&L\xc8\bj\x87\x10\xf9\x1b!r\t\x05\x91,\u05d6J\xe8h\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94&S\x83\u058bR\xd04\x16\x1b\xfa\xb0\x1a\xe1\xb0G\x94/\xbc2\x8a\x04rq\xde\xe2\rt\\\x00\x00\u07d4&Y\xfa\xcb\x1e\x83CeS\xb5\xb4)\x89\xad\xb8\a_\x99S\xed\x89\x01\x97evw\x1a^\x00\x00\xe0\x94&o-\xa7\xf0\b^\xf3\xf3\xfa\t\xba\xee#+\x93\xc7D\xdb.\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4&qH\xfdr\xc5Ob\nY/\xb9'\x991\x9c\xc4S+\\\x89\x169\u46fa\x16(\x00\x00\xe0\x94&xJ\u0791\u0228:\x8e9e\x8c\x8d\x82wA<\u0319T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4&z~n\x82\xe1\xb9\x1dQ\xde\u0776D\xf0\xe9m\xbb\x1f\u007f~\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4&\x80q=@\x80\x8e*P\xed\x011P\xa2\xa6\x94\xb9j\u007f\x1d\x89a\t=|,m8\x00\x00\u07d4&\x97\xb39\x81;\f-\x96K$q\xeb\x1c`oN\u02d6\x16\x89>\x8e\xf7\x95\u0610\xc8\x00\x00\u07d4&\xa6\x8e\xab\x90Z\x8b=\xce\x00\xe3\x170\x82%\u06b1\xb9\xf6\xb8\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4&\xb1\x1d\x06e\x88\xcet\xa5r\xa8Zc(s\x92\x12\xaa\x8b@\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xba\xbfB\xb2g\xfd\xcf8a\xfd\xd4#j^GHH\xb3X\x8965\u026d\xc5\u07a0\x00\x00\u07d4&\xc0\x05Kp\r:|-\xcb\xe2uh\x9dOL\xad\x16\xa35\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xc2\xff\xc3\x0e\xfd\xc5'>v\x18:\x16\xc2i\x8dnS\x12\x86\x89*\x11)\u0413g \x00\x00\u07d4&\u025f\x88I\u0240+\x83\xc8a!\u007f\xd0z\x9e\x84\u0377\x9d\x89\x10CV\x1a\x88)0\x00\x00\u07d4&\xcf\xff\xd0R\x15+\xb3\xf9W\xb4x\xd5\xf9\x8b#:|+\x92\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4&\u0521h\x91\xf5)\"x\x92\x17\xfc\u0606\xf7\xfc\xe2\x96\xd4\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xd4\xec\x17\xd5\u03b2\u0214\xbd\u015d\nji]\xad+C\u0309\x9f\x1fxv\x1d4\x1a\x00\x00\u07d4&\xe8\x01\xb6,\x82q\x91\xddh\xd3\x1a\x01\x19\x90\x94\u007f\xd0\xeb\xe0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4&\xe9\xe2\xadr\x97\x02bd\x17\xef%\xde\r\xc8\x00\xf7\xa7y\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4&\xf9\xf7\xce\xfd~9K\x9d9$A+\xf2\u0083\x1f\xaf\x1f\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94&\xfe\x17L\xbfRfP\xe0\xcd\x00\x9b\xd6\x12e\x02\u038ehM\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\xe0\x94&\xff\nQ\xe7\xce\u0384\x00'ix\xdb\xd6#n\xf1b\xc0\xe6\x8a\x15.\x18V'T\nP\x00\x00\u07d4'\x10\x1a\x0fV\u04da\x88\u0168O\x9b2L\xdd\xe3>\\\xb6\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'\x14L\xa9\xa7w\x1a\x83j\xd5\x0f\x80?d\xd8i\xb2\xae+ \x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4'\x14i\x13V:\xa7E\xe2X\x840\xd94\x8e\x86\xea|5\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4'\x1d=H\x1c\xb8\x8evq\xad!iI\xb66^\x060=\xe0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4' \xf9\xcaBn\xf2\xf2\xcb\xd2\xfe\xcd9\x92\fO\x1a\x89\xe1m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'*\x13\x1aZejz:\xca5\u023d \"\"\xa7Y\"X\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4'D\xffgFA!\xe3Z\xfc)\"\x17qd\xfa/\xcb\x02g\x89\x05k\xc7^-c\x10\x00\x00\u07d4'J=w\x1a=p\x97\x96\xfb\xc4\xd5\xf4\x8f\xce/\xe3\x8cy\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4'Mi\x17\x0f\xe7\x14\x14\x01\x88+\x88j\xc4a\x8cj\xe4\x0e\u06c93\xc5I\x901r\f\x00\x00\u07d4'R\x1d\xeb;n\xf1An\xa4\u01c1\xa2\xe5\u05f3n\xe8\x1ca\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'Xu\xffO\xbb\f\xf3\xa40!1'H\u007fv\b\xd0L\xba\x89\x1b\x1c\x01\x0evmX\x00\x00\u07d4'j\x00n0(\xec\xd4L\xdbb\xba\nw\u0394\xeb\xd9\xf1\x0f\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4'k\x05!\xb0\xe6\x8b'}\xf0\xbb2\xf3\xfdH2cP\xbf\xb2\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4'o\xd7\xd2O\x8f\x88?Zz()[\xf1qQ\u01e8K\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'p\xf1N\xfb\x16]\u07bay\xc1\v\xb0\xaf1\xc3\x1eY3L\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4'vw\xab\xa1\xe5,;S\xbf\xa2\a\x1dN\x85\x9a\n\xf7\xe8\xe1\x8965\u026d\xc5\u07a0\x00\x00\u07d4'\x82Ff\xd2x\xd7\x04#\xf0=\xfe\x1d\u01e3\xf0/C\u2d4966\xc2^f\xec\xe7\x00\x00\u07d4'\x83\f_`#\xaf\xaa\xf7\x97Egl J\x0f\xac\u0360\xba\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94'\x84\x90?\x1d|\x1b\\\xd9\x01\xf8\x87]\x14\xa7\x9b<\xbe*V\x8a\x04\xbd\xa7\xe9\xd7J\xd5P\x00\x00\u07d4'\x8c\v\xdec\x0e\u00d3\xb1\xe7&\u007f\xc9\xd7\xd9p\x19\xe4\x14[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'\x98q\x10\"\x1a\x88\b&\xad\xb2\xe7\xab^\xcax\xc6\xe3\x1a\xec\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94'\xac\a;\xe7\x9c\xe6W\xa9:\xa6\x93\xeeC\xbf\x0f\xa4\x1f\xef\x04\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4'\xb1iN\xaf\xa1e\xeb\xd7\xcc{\u025et\x81J\x95\x14\x19\u0709+^:\xf1k\x18\x80\x00\x00\u07d4'\xb6(\x16\xe1\xe3\xb8\u045by\xd1Q=]\xfa\x85[\f:*\x89\x05j\xf5\xc1\xfdiP\x80\x00\u07d4'\xbf\x94<\x163\xfe2\xf8\xbc\xcc\xdbc\x02\xb4\a\xa5rND\x892\xf8Lm\xf4\b\xc0\x80\x00\u07d4'\xbf\x9fD\xba}\x05\xc35@\u00e5;\xb0,\xbb\xff\xe7\xc3\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4'\xc2\xd7\xcaPM\xaa=\x90f\xdc\t\x13}\xc4/:\xaa\xb4R\x89 \x86\xac5\x10R`\x00\x00\u07d4'\xd1X\xac=>\x11\t\xabnW\x0e\x90\xe8]8\x92\xcdv\x80\x89\x05k\xc7^-c\x10\x00\x00\u07d4'\xe69\x89\xca\x1e\x90;\xc6 \xcf\x1b\x9c?g\xb9\xe2\xaee\x81\x89Hz\x9a0E9D\x00\x00\xe0\x94'\xf0<\xf1\xab\xc5\xe1\xb5\x1d\xbcDK(\x9eT,\x9d\u07f0\xe6\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4'\xfc\x85\xa4\x9c\xff\x90\xdb\xcf\xda\u071d\xdd@\u05b9\xa2!\nl\x89\x05k\xc7^-c\x10\x00\x00\u07d4(\x05A^\x1d\u007f\xde\xc6\xde\u07f8\x9eR\x1d\x10Y-t<\x10\x89\x05k\xc7^-c\x10\x00\x00\u07d4(\a>\xfc\x17\xd0\\\xab1\x95\xc2\xdb3+a\x98Gw\xa6\x12\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x12P\xa2\x91!'\nN\xe5\u05cd$\xfe\xaf\xe8,p\xba:\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x13\xd2c\xfc_\xf2G\x9e\x97\x05\x95\u05b6\xb5`\xf8\xd6\xd6\u0449lk\x93[\x8b\xbd@\x00\x00\u07d4(.\x80\xa5T\x87ZVy\x9f\xa0\xa9\u007fU\x10\u7557LN\x8965\u026d\xc5\u07a0\x00\x00\u07d4(3\x96\xce<\xac9\x8b\xcb\xe7\"\u007f2>x\xff\x96\u0407g\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4(4\x9f~\xf9t\xeaU\xfe6\xa1X;4\xce\xc3\xc4Pe\xf0\x89\f\xb63\u051eeY\x00\x00\u07d4(6\x120F\xb2\x84\xe5\xef\x10+\xfd\"\xb1v^P\x81\x16\xad\x89\x16S\xfb\xb5\xc4'\xe4\x00\x00\u07d4(<#\x14(<\x92\u0530d\xf0\xae\xf9\xbbRF\xa7\x00\u007f9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4(>\x11 7I\xb1\xfaO2\xfe\xbbq\xe4\x9d\x13Y\x198*\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94(>bR\xb4\xef\xcfFT9\x1a\xcbu\xf9\x03\u015bx\xc5\xfb\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94(Q\x0en\xff\x1f\xc8)\xb6WoC(\xbc98\xecze\x80\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4(X\xac\xac\xaf!\xea\x81\u02b7Y\x8f\xdb\xd8kE.\x9e\x8e\x15\x89$\x1a\x9bOaz(\x00\x00\u07d4(Z\xe5\x1b\x95\x00\u014dT\x13e\xd9ui\xf1K\xb2\xa3p\x9b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(f\xb8\x1d\xec\xb0.\xe7\n\xe2P\xce\xe5\xcd\xc7{Y\u05f6y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(i\x06\xb6\xbdIr\xe3\xc7\x16U\xe0K\xaf6&\f|\xb1S\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4(k\x18ma\xea\x1f\u05cd\x990\xfe\x12\xb0e7\xb0\\=Q\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94(t\xf3\xe2\x98]_{@f'\xe1{\xaaw+\x01\xab\u031e\x8a\x01F\x05\x04\x10v_8\x00\x00\xe0\x94(|\xf9\u0410.\xf8\x19\xa7\xa5\xf1ID[\xf1w^\xe8\xc4|\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4(\x81\x8e\x18\xb6\x10\x00\x13!\xb3\x1d\xf6\xfe}(\x15\u036d\xc9\xf5\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x86\x83$3~\x11\xba\x10l\xb4\x81\u0696/:\x84S\x80\x8d\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94(\x90K\xb7\xc40)C\xb7\t\xb1Myp\xe4+\x83$\u184a\x02\x1f\x97\x84j\a-~\x00\x00\u07d4(\x95\xe8\t\x99\xd4\x06\xadY.+&'7\xd3_}\xb4\xb6\x99\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4(\x96r\x80!N!\x8a\x12\f]\xda7\x04\x1b\x11\x1e\xa3mt\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4(\xa3\xda\t\xa8\x19H\x19\xae\x19\x9f.m\x9d\x13\x04\x81~(\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(\xab\x16_\xfbi\xed\xa0\xc5I\xae8\xe9\x82o_\u007f\x92\xf8S\x89FM\xf6\xd7\xc8DY\x00\x00\u07d4(\xb7u\x85\xcb=U\xa1\x99\xab)\x1d:\x18\u018f\u8684\x8a\x89j@v\xcfy\x95\xa0\x00\x00\xe0\x94(\xd4\xeb\xf4\x1e=\x95\xf9\xbb\x9a\x89u#\\\x1d\x009>\x80\x00\u07d4)\nV\xd4\x1fn\x9e\xfb\xdc\xea\x03B\u0dd2\x9a\x8c\xdf\xcb\x05\x89\x12\xa5\xf5\x81h\xee`\x00\x00\u07d4)\x15bK\xcbg\x917\xb8\xda\xe9\xabW\xd1\x1bI\x05\xea\xeeK\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4)\x1e\xfe\x00\x81\xdc\xe8\xc1G\x99\xf7\xb2\xa46\x19\xc0\u00f3\xfc\x1f\x89A\rXj \xa4\xc0\x00\x00\u07d4)\x1f\x92\x9c\xa5\x9bT\xf8D>=Mu\xd9]\xee$<\xefx\x89\x1b\x1a\b\x927\a=\x00\x00\xe0\x94))\x8c\xcb\xdf\xf6\x89\xf8\u007f\xe4\x1a\xa6\xe9\x8f\u07f5=\xea\xf3z\x8a\x041\\2\xd7\x1a\x9e`\x00\x00\u07d4)/\"\x8b\n\x94t\x8c\x8e\xeca-$o\x98\x93c\xe0\x8f\b\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4)3\x84\xc4+o\x8f)\x05\xceR\xb7 \\\"t7la+\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4)4\xc0\xdf{\xbc\x17+l\x18k\vrTz\u038b\xf7TT\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4)<#\x06\xdf6\x04\xaeO\xda\r z\xbasog\xde\a\x92\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94)I\xfd\x1d\xef\\v\xa2\x86\xb3\x87$$\x80\x9a\a\xdb9f\xf3\x8a\x01\x1b\xd9\x06\u06a0\xc9C\x80\x00\u07d4)OIK?.\x14\xa3\xf8\xab\x00\x00\x00\u07d4)U\xc3W\xfd\x8fu\xd5\x15\x9a=\xfai\u0178z5\x9d\ua309lk\x93[\x8b\xbd@\x00\x00\u07d4)a\xfb9\x1ca\x95|\xb5\xc9\xe4\a\u0762\x938\u04f9,\x80\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4)h\x1d\x99\x12\xdd\xd0~\xaa\xbb\x88\xd0]\x90\xf7f\xe8bA}\x8965\u026d\xc5\u07a0\x00\x00\u07d4)kq\xc0\x01X\x19\xc2B\xa7\x86\x1eo\xf7\xed\xed\x8a_q\xe3\x89lh\xcc\u041b\x02,\x00\x00\u07d4)mf\xb5!W\x1aNA\x03\xa7\xf5b\xc5\x11\xe6\xaas-\x81\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4)o\x00\xde\x1d\u00fb\x01\xd4z\x8c\xcd\x1e]\x1d\u0661\xebw\x91\x8965\u026d\xc5\u07a0\x00\x00\u07d4)s\x85\xe8\x864FV\x85\xc21\xa3\x14\xa0\xd5\xdc\xd1F\xaf\x01\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4)v=\xd6\u069a|\x16\x11s\x88\x83!\ub9b6<\x8f\xb8E\x89\x11\xc7\xea\x16.x \x00\x00\u07d4)yt\x11t\xa8\xc1\xea\v\u007f\x9e\xdfe\x81w\x85\x94\x17\xf5\x12\x89\x19\x01\x96l\x84\x96\x83\x80\x00\u07d4)z\x88\x92\x1b_\xca\x10\u5edd\xed`\x02T7\xae\"\x16\x94\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94)}]\xbe\"//\xb5%1\xac\xbd\v\x01=\xc4F\xacsh\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4)\x82N\x94\xccCH\xbc\x962y\xdc\xdfG9\x17\x152L\u04c9i*\xe8\x89p\x81\xd0\x00\x00\u07d4)\x82\xd7j\x15\xf8G\xddA\xf1\x92*\xf3h\xfeg\x8d\x0eh\x1e\x89\x05k\xc7^-c\x10\x00\x00\u07d4)\x88\x87\xba\xb5|[\xa4\xf0aR)\xd7R_\xa1\x13\xb7\ua249\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94)\x8e\xc7kD\r\x88\a\xb3\xf7\x8b_\x90\x97\x9b\xeeB\xedC\u06ca\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4)\x93h`\x90B\xa8X\xd1\xec\xdf\x1f\xc0\xad\xa5\xea\xce\xca)\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4)\x9e\v\xcaU\xe0i\u0785\x04\xe8\x9a\xcan\xca!\u04ca\x9a]\x89\x03\x027\x9b\xf2\xca.\x00\x00\u07d4)\xac+E\x84T\xa3l~\x96\xc7:\x86g\"*\x12$,q\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94)\xad\u03c3\xb6\xb2\n\u01a44\xab\xb1\x99<\xbd\x05\xc6\x0e\xa2\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94)\xae\xf4\x8d\xe8\xc9\xfb\xadK\x9eL\xa9pyzU3\xebr-\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4)\xb3\xf5a\xeezn%\x94\x1e\x98\xa52[x\xad\u01d7\x85\xf3\x89\x05k\xc7^-c\x10\x00\x00\u07d4)\xbd\xc4\xf2\x8d\xe0\x18\x0fC<&\x94\xebt\xf5PL\xe9C7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4)\u0300M\x92+\xe9\x1fY\t\xf3H\xb0\xaa\xa5\xd2\x1b`x0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94)\xda>5\xb2;\xb1\xf7/\x8e\"X\xcf\u007fU3Y\xd2K\xac\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94)\xe6y\x90\xe1\xb6\xd5.\x10U\xff\xe0I\xc51\x95\xa8\x15B\u03ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4)\uab82v\x17b\xf4\xd2\xdbS\xa9\u018b\x0fk\vmNf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4)\xeb~\xef\xda\xe9\xfe\xb4I\xc6?\xf5\xf2y\xd6u\x10\xeb\x14\"\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4)\xf0\xed\xc6\x038\xe7\x11 \x85\xa1\xd1\x14\u068cB\u038fU\u0589\xa0Z\u007f\x0f\xd8%x\x00\x00\u07d4)\xf8\xfb\xa4\xc3\ar\xb0W\xed\xbb\xe6*\xe7B\f9\x05r\xe1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94)\xf9(l\x0es\x8d\x17!\xa6\x91\u01b9Z\xb3\u0667\x97\xed\xe8\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4*\b^%\xb6Hb\xf5\xe6\x8dv\x8e+\x0fz\x85)\x85\x8e\xee\x89k\x88:\xcdWf\xcd\x00\x00\u07d4**\xb6\xb7Lz\xf1\xd9Gk\xb5\xbc\xb4RG\x97\xbe\xdc5R\x8965\u026d\xc5\u07a0\x00\x00\u07d4*9\x19\nO\u0783\u07f3\xdd\xcbL_\xbb\x83\xaclIu\\\x8965\u026d\xc5\u07a0\x00\x00\u07d4*@\r\xff\x85\x94\xder(\xb4\xfd\x15\xc3#\"\xb7[\xb8}\xa8\x89\x051\xa1\u007f`z-\x00\x00\xe0\x94*D\xa7!\x8f\xe4Me\xa1\xb4\xb7\xa7\u0671\xc2\xc5,\x8c>4\x8a\r-\x06\xc3\x05\xa1\xebW\x80\x00\u07d4*F\xd3Swqv\xff\x8e\x83\xff\xa8\x00\x1fOp\xf9s:\xa5\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4*Y_\x16\xee\xe4\xcb\f\x17\u0662\xd99\xb3\xc1\x0flgrC\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4*Y\xe4~\xa5\xd8\xf0\xe7\xc0(\xa3\xe8\xe0\x93\xa4\x9c\x1bP\xb9\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4*[\xa9\xe3L\u054d\xa5L\x9a'\x12f:;\xe2t\xc8\xe4{\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94*^:@\xd2\xcd\x03%vm\xe7:=g\x18\x96\xb3b\xc7;\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94*cY\x0e\xfe\x99\x86\xc3\xfe\xe0\x9b\n\n3\x8b\x15\xbe\xd9\x1f!\x8a\x01^\x1cN\x05\xee&\xd0\x00\x00\u07d4*gf\n\x13h\xef\xcdbn\xf3k+\x1b`\x19\x80\x94\x1c\x05\x89\a?u\u0460\x85\xba\x00\x00\u07d4*t+\x89\x10\x94\x1e\t2\x83\n\x1d\x96\x92\xcf\u0484\x94\xcf@\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4*tl\xd4@'\xaf>\xbd7\xc3x\xc8^\xf7\xf7T\xab_(\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4*\x81\xd2|\xb6\xd4w\x0f\xf4\xf3\u0123\xba\x18\xe5\xe5\u007f\aQ|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4*\x91\xa9\xfe\xd4\x1b}\x0e\\\xd2\xd81X\xd3\xe8\xa4\x1a\x9a-q\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94*\x9cW\xfe{k\x13\x8a\x92\rgo{\x1a%\x10\x80\xff\xb9\x8a4\xf0\x86\xf3\xb3;h@\x00\x00\u07d4+p\x1d\x16\xc0\xd3\xcc\x1eL\xd8TE\xe6\xad\x02\ue92c\x01-\x89 \x86\xac5\x10R`\x00\x00\xe0\x94+q|\xd42\xa3#\xa4e\x909\x84\x8d;\x87\xde&\xfc\x95F\x8ai\xe1\r\xe7fv\u0400\x00\x00\u07d4+t\xc3s\xd0K\xfb\x0f\xd6\n\x18\xa0\x1a\x88\xfb\xe8Gp\u5309\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4+w\xa4\u060c\rV\xa3\xdb\xe3\xba\xe0J\x05\xf4\xfc\u0477W\xe1\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94+\x84\x88\xbd-<\x19z=&\x15\x18\x15\xb5\xa7\x98\xd2qh\u070a\x01j\x1f\x9f_\xd7\xd9`\x00\x00\u07d4+\x8a\r\xee\\\xb0\xe1\xe9~\x15\xcf\xcan\x19\xad!\xf9\x95\ufb49\x1bUC\x8d\x9a$\x9b\x00\x00\xe0\x94+\x8f\xe4\x16n#\xd1\x19c\xc0\x93+\x8a\u078e\x01E\xea\ap\x8a\t(\x96R\x9b\xad\u0708\x00\x00\xe0\x94+\x99\xb4.OBa\x9e\xe3k\xaa~J\xf2\xd6^\xac\xfc\xba5\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4+\xab\x0f\xbe(\u0544 \xb5 6w\n\x12\xf9\x95*\xeai\x11\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4+\xad\xe9\x1d\x15E\x17b\x0f\u05349\xac\x97\x15zA\x02\xa9\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4+\xaf\x8dn\"\x11t\x12H \xeeI+\x94Y\xecO\xad\xaf\xbb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4+\xaf\xbf\x9e\x9e\xd2\xc2\x19\xf7\xf2y\x13t\xe7\xd0\\\xb0gw\xe7\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\xe0\x94+\xb3f\xb9\xed\xcb\r\xa6\x80\xf0\xe1\v;n(t\x81\x90\xd6\u00ca\x01:b\u05f5v@d\x00\x00\xe0\x94+\xb6\xf5x\xad\xfb\u7ca1\x16\xb3UO\xac\xf9\x96\x98\x13\xc3\x19\x8a\x01\x91'\xa19\x1e\xa2\xa0\x00\x00\u07d4+\xbeb\xea\xc8\f\xa7\xf4\xd6\xfd\xee~}\x8e(\xb6:\xcfw\x0e\x89\x81\xe3-\xf9r\xab\xf0\x00\x00\u07d4+\xbeg*\x18WP\x8fc\x0f*^\xdbV=\x9e\x9d\xe9(\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4+\xc4)\xd6\x18\xa6jL\xf8-\xbb-\x82N\x93V\xef\xfa\x12j\x89lj\xccg\u05f1\xd4\x00\x00\u07d4+\xd2R\xe0\xd72\xff\x1d|x\xf0\xa0.l\xb2T#\xcf\x1b\x1a\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4+\xdd\x03\xbe\xbb\xee';l\xa1\x05\x9b4\x99\x9a[\xbda\xbby\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,\x04\x11\\>R\x96\x1b\r\xc0\xb0\xbf1\xfb\xa4ToYf\xfd\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94,\x06\u0752+aQJ\xaf\xed\xd8D\x88\xc0\u008em\xcf\x0e\x99\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94,\f\xc3\xf9QH,\u0222\x92X\x15hN\xb9\xf9N\x06\x02\x00\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4,\x0e\xe14\u0633aE\xb4{\xee\u7bcd'8\xdb\xda\b\xe8\x89\n\xe5os\x0em\x84\x00\x00\u07d4,\x0f[\x9d\xf46%y\x8e~\x03\xc1\xa5\xfdjm\t\x1a\xf8+\x89\x01\xb0\xfc\xaa\xb2\x000\x00\x00\u07d4,\x12\x8c\x95\xd9W!Q\x01\xf0C\u074f\u0142EmA\x01m\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4,\x18\x00\xf3_\xa0->\xb6\xff[%(_^J\xdd\x13\xb3\x8d\x891\"\u04ed\xaf\xde\x10\x00\x00\u07d4,\x1c\x19\x11N=m\xe2xQHK\x8d'\x15\xe5\x0f\x8a\x10e\x89\x05k\xc7^-c\x10\x00\x00\u07d4,\x1c\xc6\xe1\x8c\x15$\x88\xba\x11\xc2\xcc\x1b\xce\xfa-\xf3\x06\xab\u0449Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94,\x1d\xf8\xa7oH\xf6\xb5K\u03dc\xafV\xf0\xee\x1c\xf5z\xb3=\x8a\x02$\u007fu\x00\x89\xdaX\x00\x00\u07d4,!G\x94z\xe3?\xb0\x98\xb4\x89\xa5\xc1k\xff\xf9\xab\xcdN*\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4,#OP\\\xa8\xdc\xc7}\x9b~\x01\xd2W\xc3\x18\xcc\x199m\x89\x05k\xc7^-c\x10\x00\x00\u07d4,$(\xe4\xa6it\xed\xc8\"\xd5\xdb\xfb$\x1b'(\aQX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,-\x15\xff9V\x1c\x1br\xed\xa1\xcc\x02\u007f\xfe\xf27C\xa1D\x89\u0500\xed\x9e\xf3+@\x00\x00\u07d4,-\xb2\x8c3\t7^\xea1\x82\x1b\x84\xd4\b\x93\x0e\xfa\x1a\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4,Z-\n\xbd\xa0;\xbe!W\x81\xb4\xff)l\x8ca\xbd\xba\xf6\x89\x01\xa8\xe5oH\xc0\"\x80\x00\u07d4,[}{\x19Z7\x1b\xf9\xab\u0774/\xe0O/\x1d\x9a\x99\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4,]\xf8ffj\x19K&\u03bb@~J\x1f\xd7> \x8d^\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94,`?\xf0\xfe\x93alCW>\xf2y\xbf\xea@\x88\x8dj\xe7\x8a\x01\x00\xf4\xb6\xd6gW\x90\x00\x00\xe0\x94,hF\xa1\xaa\x99\x9a\"F\xa2\x87\x05`\x00\xbaM\u02e8\xe6=\x8a\x02\x1f/o\x0f\xc3\xc6\x10\x00\x00\u0794,j\xfc\xd4\x03|\x1e\xd1O\xa7O\xf6u\x8e\tE\xa1\x85\xa8\xe8\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4,ki\x9d\x9e\xad4\x9f\x06\u007fEq\x1a\aJd\x1d\xb6\xa8\x97\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,o\\\x12L\u01c9\xf8\xbb9\x8e?\x88\x97Q\xbcK`-\x9e\x89\x01Y\xf2\v\xed\x00\xf0\x00\x00\u07d4,\x83\xae\xb0/\xcf\x06}e\xa4p\x82\xfd\x97x3\xab\x1c\uc449\b'8#%\x8a\xc0\x00\x00\xe0\x94,\x89\xf5\xfd\xca=\x15T\t\xb68\xb9\x8at.U\xebFR\xb7\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4,\x96HI\xb1\xf6\x9c\xc7\u03a4D%8\xed\x87\xfd\xf1l\xfc\x8f\x89lk\x93[\x8b\xbd@\x00\x00\u0794,\x9f\xa7,\x95\xf3}\b\xe9\xa3`\t\u7930\u007f)\xba\xd4\x1a\x88\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4,\xafk\xf4\xec}Z\x19\xc5\xe0\x89z^\xeb\x01\x1d\xce\xceB\x10\x89\a\x93H5\xa01\x16\x00\x00\u07d4,\xb4\xc3\xc1k\xb1\xc5^|kz\x19\xb1'\xa1\xac\x93\x90\xcc\t\x89\xb8'\x94\xa9$O\f\x80\x00\xe0\x94,\xb5IZPS6\xc2FT\x10\xd1\xca\xe0\x95\xb8\xe1\xba\\\u074a\x04<3\xc1\x93ud\x80\x00\x00\u07d4,\xb6\x15\a:@\xdc\u06d9\xfa\xa8HW.\x98{;\x05n\xfb\x89+X\xad\u06c9\xa2X\x00\x00\u07d4,\xbam]\r\xc2\x04\xea\x8a%\xad\xa2\xe2oVu\xbd_/\u0709H#\xef}\u06da\xf3\x80\x00\u07d4,\xbb\fs\u07d1\xb9\x17@\xb6i;wJ}\x05\x17~\x8eX\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4,\xcbfIM\n\xf6\x89\xab\xf9H=6]x$D\xe7\u07ad\x8965\u026d\xc5\u07a0\x00\x00\u07d4,\xcc\x1f\x1c\xb5\xf4\xa8\x00.\x18k \x88]\x9d\xbc\x03\f\b\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,\u03c0\xe2\x18\x98\x12^\xb4\xe8\a\u0342\xe0\x9b\x9d(Y/n\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,\u0456\x94\u0452j\x0f\xa9\x18\x9e\u07ba\xfcg\x1c\xf1\xb2\u02a5\x8965\u026d\xc5\u07a0\x00\x00\u07d4,\u04d34\xac~\xacyrW\xab\xe3sa\x95\xf5\xb4\xb5\xce\x0f\x89\x05kGx^7&\x00\x00\u07d4,\u05de\xb5 '\xb1,\x18\x82\x8e>\xaa\xb2\x96\x9b\xfc\u0487\xe9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,\xd8xfV\x8d\xd8\x1a\xd4}\x9d:\u0404nZePss\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4,\xdb9De\x06\x16\xe4|\xb1\x82\xe0`2/\xa1Hyx\u0389b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4,\xe1\x1a\x92\xfa\xd0$\xff+>\x87\xe3\xb5B\xe6\xc6\r\xcb\u0656\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4-\x03&\xb2?\x04\t\xc0\xc0\xe9#hc\xa13\aZ\x94\xba\x18\x89\vg\x9b\xe7[\xe6\xae\x00\x00\u07d4-\r\xecQ\xa6\xe8s0\xa6\xa8\xfa*\x0fe\u060dJ\xbc\xdfs\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4-#vkok\x05s}\xad\x80\xa4\x19\xc4\x0e\xdaMw\x10>\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4-+\x03#Y\xb3c\x96O\xc1\x1aQ\x82c\xbf\xd0T1\xe8g\x89\b\x1c\x1d\xf7b\x9ep\x00\x00\u07d4-4\x80\xbf\be\aJr\xc7u\x9e\xe5\x13{Mp\xc5\x1c\xe9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4-5\xa9\xdfbu\u007f\u007f\xfa\xd1\x04\x9a\xfb\x06\xcaJ\xfcFLQ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4-@U\x8b\x06\xf9\n9#\x14U\x92\x12;gt\xe4n1\xf4\x8965\u026d\xc5\u07a0\x00\x00\u07d4-Bi\x12\xd0Y\xfa\xd9t\v.9\n.\xea\xc0To\xf0\x1b\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4-S-\xf4\xc69\x11\xd1\u0391\xf6\xd1\xfc\xbf\xf7\x96\x0fx\xa8\x85\x89Z\x85\x96\x8aXx\u0680\x00\u07d4-S\x91\xe98\xb3HX\u03d6[\x84\x051\xd5\xef\xdaA\v\t\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94-[B\xfcY\xeb\xda\r\xfdf\xae\x91K\u008c\x1b\nn\xf8:\x8a+\u0235\x9f\xdc\xd86c\x80\x00\u07d4-]s5\xac\xb06+G\u07e3\xa8\xa4\xd3\xf5\x94\x95D\u04c0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-a\xbf\xc5hs\x92<+\x00\t]\xc3\xea\xa0\xf5\x90\u062e\x0f\x8a\x04ef\xdf\xf8\xceU`\x00\x00\u07d4-e\x11\xfdz8\x00\xb2hT\xc7\xec9\xc0\u0735\xf4\xc4\xe8\xe8\x89\x15\xad\u077a/\x9ew\x00\x00\u07d4-}\\@\u076f\xc4P\xb0Jt\xa4\u06bc+\xb5\xd6e\x00.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4-\x89\xa8\x00jO\x13z \xdc+\xecF\xfe.\xb3\x12\xea\x96T\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4-\x8cR2\x9f8\u04a2\xfa\x9c\xba\xf5\u0143\xda\xf1I\v\xb1\x1c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4-\x8e\x06\x18\x92\xa5\xdc\xce!\x96j\xe1\xbb\a\x88\xfd>\x8b\xa0Y\x89\r\x8e\\\xe6\x17\xf2\xd5\x00\x00\u07d4-\x8e[\xb8\xd3R\x16\x95\xc7~|\x83N\x02\x91\xbf\xac\xeet\b\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4-\x90\xb4\x15\xa3\x8e.\x19\xcd\xd0/\U000ed069z\xf7\xcb\xf6r\x89\x05\xf3\xc7\xf6A1\xe4\x00\x00\u07d4-\x9b\xado\x1e\xe0*p\xf1\xf1=\xef\\\u0332z\x9a'@1\x89a\t=|,m8\x00\x00\u07d4-\x9c_\xec\u04b4O\xbbj\x1e\xc72\xea\x05\x9fO\x1f\x9d+\\\x896\xca2f\x1d\x1a\xa7\x00\x00\xe0\x94-\xa6\x17iP\t\xccW\xd2j\u0510\xb3*]\xfb\xeb\x93N^\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4-\xa7k|9\xb4 \u323a,\x10 \xb0\x85k\x02pd\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4-\u01ddn\u007fU\xbc\xe2\xe2\xd0\xc0*\xd0|\uca3bR\x93T\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\xe0\x94-\xca\x0eD\x9a\xb6F\xdb\xdf\u04d3\xa9fb\x96\v\u02b5\xae\x1e\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4-\xd3%\xfd\xff\xb9{\x19\x99R\x84\xaf\xa5\xab\xdbWJ\x1d\xf1j\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4-\xd5x\xf7@}\xfb\xd5H\xd0^\x95\xcc\u00dcHT)bj\x89\u3bb5sr@\xa0\x00\x00\u07d4-\xd8\xee\xef\x87\x19J\xbc,\xe7X]\xa1\xe3[|\xeax\f\xb7\x8965\xc6 G9\u0640\x00\u07d4-\xdf@\x90Wi\xbc\xc4&\xcb,)8\xff\xe0w\xe1\u8758\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4-\xe0\x96D\x00\u0082\xbd\u05ca\x91\x9ck\xf7|k_yay\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-\xe3\x1a\xfd\x18\x9a\x13\xa7o\xf6\xfes\xea\xd9\xf7K\xb5\u0126)\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4-\xec\x982\x9d\x1f\x96\u00e5\x9c\xaay\x81uTR\xd4\xdaI\u0549\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-\ue422\x8f\x19-gj\x87s#+V\xf1\x8f#\x9e/\xad\x8a\x03\xef\xa7\xe7G\xb6\u046d\x00\x00\xe0\x94.\b\x80\xa3E\x96#\a \xf0Z\xc8\xf0e\xaf\x86\x81\u0736\u008a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4.\fW\xb4qP\xf9Z\xa6\xa7\xe1j\xb9\xb1\xcb\xf5C(\x97\x9a\x89\x05k\xc7^-c\x10\x00\x00\u07d4.\x10\x91\v\xa6\xe0\xbc\x17\xe0UUf\x14\u02c7\t\x0fM~[\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94.$\xb5\x97\x87;\xb1A\xbd\xb27\xea\x8aZ\xb7Gy\x9a\xf0-\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4.(\x10\xde\xe4J\xe4\xdf\xf3\xd8cB\xab\x12fW\xd6S\xc36\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4.,\xbdz\xd8%G\xb4\xf5\xff\x8b:\xb5o\x94*dE\xa3\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4.-~\xa6k\x9fG\xd8\xccR\xc0\x1cR\xb6\u147c}G\x86\x89\xd8\xd4`,&\xbfl\x00\x00\u07d4.C\x93H\u07caBw\xb2*v\x84W\xd1\x15\x8e\x97\xc4\t\x04\x89*\x1e\x9f\xf2o\xbfA\x00\x00\xe0\x94.F\xfc\xeej;\xb1E\xb5\x94\xa2C\xa3\x91?\xce]\xado\xba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794.G\xf2\x87\xf4\x98#7\x13\x85\r1&\x82<\xc6}\xce\xe2U\x88\u029d\x9e\xa5X\xb4\x00\x00\u07d4.N\u1b99j\xa0\xa1\xd9$(\xd0fR\xa6\xbe\xa6\xd2\xd1]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.R\x91+\xc1\x0e\xa3\x9dT\xe2\x93\xf7\xae\u05b9\x9a\x0fLs\xbe\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4.a\x9fW\xab\xc1\u91ea\x93j\xe3\xa2&Ib\xe7\xeb-\x9a\x89(\xfb\x9b\x8a\x8aSP\x00\x00\u07d4.d\xa8\xd7\x11\x11\xa2/L]\xe1\xe09\xb36\xf6\x8d9\x8a|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.i3T=O,\xc0\vSP\xbd\x80h\xba\x92C\u05be\xb0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94.~\x05\xe2\x9e\u0767\xe4\xae%\xc5\x175C\xef\xd7\x1fm=\x80\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4.\u007fFU \xec5\xcc#\u058eue\x1b\xb6h\x95D\xa1\x96\x898\xec[r\x1a\x1a&\x80\x00\u07d4.\x8e\xb3\nqn_\xe1\\t#>\x03\x9b\xfb\x11\x06\xe8\x1d\x12\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94.\x98$\xb5\xc12\x11\x1b\xca$\xdd\xfb\xa7\xe5u\xa5\xcdr\x96\xc1\x8a\x03\xa4\x84Qnm\u007f\xfe\x00\x00\u07d4.\xa5\xfe\xe6?3z7nK\x91\x8e\xa8!H\xf9MH\xa6&\x89e\x0f\x8e\r\u0493\xc5\x00\x00\u07d4.\xafN*F\xb7\x89\xcc\u0088\xc8\xd1\xd9)N?\xb0\x858\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.\xaf\xf9\xf8\xf8\x110d\u04d5z\xc6\xd6\xe1\x1e\xeeB\xc8\x19]\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4.\xba\fn\xe5\xa1\x14\\\x1cW9\x84\x96:`]\x88\nz \x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4.\xc9X\"\xeb\x88{\xc1\x13\xb4q*M\xfd\u007f\x13\xb0\x97\xb5\xe7\x8965\u026d\xc5\u07a0\x00\x00\u07d4.\xcaj<]\x9fD\x9d\tV\xbdC\xfa{M{\xe8CYX\x89lk\xdaip\x9c\xc2\x00\x00\xe0\x94.\xca\xc5\x04\xb23\x86n\xb5\xa4\xa9\x9e{\u0490\x13Y\xe4;=\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4.\xeb\xf5\x942\xb5(\x92\xf98\v\xd1@\xaa\x99\xdc\xf8\xad\f\x0f\x89\b=lz\xabc`\x00\x00\u07d4.\xee\xd5\x04q\xa1\xa2\xbfS\xee0\xb1#.n\x9d\x80\xef\x86m\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4.\xefk\x14\x17\u05f1\x0e\xcf\xc1\x9b\x12:\x8a\x89\xe7>RlX\x89 \x86\xac5\x10R`\x00\x00\u07d4.\xf8i\xf05\vW\xd54x\xd7\x01\xe3\xfe\xe5)\xbc\x91\x1cu\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4.\xf9\xe4eqj\xca\u03f8\xc8%/\xa8\xe7\xbcyi\xeb\xf6\u4255\x9e\xb1\xc0\xe4\xae \x00\x00\xe0\x94.\xfcLd}\xacj\xca\xc3Uw\xad\"\x17X\xfe\xf6ao\xaa\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4/\x13eu&\xb1w\xca\xd5G\u00d0\x8c\x84\x0e\xffd{E\u0649?v\x84\x9c\xf1\xee,\x80\x00\u07d4/\x18}ZpMZ3\x8c[(v\xa0\x90\xdc\xe9d(N)\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94/%#\u0303O\x00\x86\x05$\x02bb\x96gQ\x86\xa8\u508a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4/(*\xbb\xb6\u0523\xc3\xcd;\\\xa8\x12\xf7d>\x800_\x06\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4/+\xba\x1b\x17\x96\x82\x1avo\xced\xb8O(\xech\xf1Z\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4/1]\x90\x16\xe8\xee_Sf\x81 /\x90\x84\xb02TMM\x898<\xd1+\x9e\x86<\x00\x00\u07d4/M\xa7SC\x0f\xc0\x9es\xac\xbc\xcd\xcd\xe9\xdad\u007f+]7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4/P\x80\xb8?~-\xc0\xa1\xdd\x11\xb0\x92\xad\x04+\xffx\x8fL\x89\xb4\xf8\xfby#\x1d+\x80\x00\u07d4/a\uf941\x9dp_+\x1eN\xe7T\xae\xb8\xa8\x19Pju\x89O%\x91\xf8\x96\xa6P\x00\x00\xe0\x94/f\xbf\xbf\"b\xef\u030d+\xd0DO\u0170ib\x98\xff\x1e\x8a\x02\x1a\xd95\xf7\x9fv\xd0\x00\x00\u07d4/m\xce\x130\u015e\xf9!`!TW-MK\xac\xbd\x04\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4/}2\x90\x85\x1b\xe5\u01b4\xb4?}Et2\x9fa\xa7\x92\u00c9\x05k\xc7^-c\x10\x00\x00\u07d4/\x858\x17\xaf\u04f8\xf3\xb8n\x9f`\xeew\xb5\xd9ws\xc0\xe3\x89N\xae\xeaD\xe3h\xb9\x00\x00\u07d4/\xa4\x91\xfbY \xa6WN\xbd(\x9f9\xc1\xb2C\r-\x9aj\x89lk\x93[\x8b\xbd@\x00\x00\u07d4/\xb5f\xc9K\xbb\xa4\xe3\xcbg\xcd\xda}_\xadq1S\x91\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4/\xbbPJ]\xc5'\xd3\xe3\xeb\x00\x85\xe2\xfc<}\xd58\xcbz\x89C\u00b1\x8a\xec<\n\x80\x00\u07d4/\xbc\x85y\x8aX5\x98\xb5\"\x16mn\x9d\xda\x12\x1db}\xbc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4/\xbc\xef3\x84\xd4 \xe4\xbfa\xa0f\x99\x90\xbcpT\U00065bc9lk\x93[\x8b\xbd@\x00\x00\xe0\x94/\xc8.\xf0v\x93#A&Oaz\f\x80\xddW\x1ej\xe99\x8a\x01\x84$\xf5\xf0\xb1\xb4\xe0\x00\x00\u07d4/\u075by\u07cd\xf50\xadc\xc2\x0eb\xafC\x1a\xe9\x92\x16\xb8\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4/\xe0\x02?W\"e\x0f:\x8a\xc0\x10\t\x12^t\xe3\xf8.\x9b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4/\xe0\xccBKS\xa3\x1f\t\x16\xbe\b\xec\x81\xc5\v\xf8\xea\xb0\xc1\x89 \x86\xac5\x10R`\x00\x00\u07d4/\xe1:\x8d\a\x85\u0787X\xa5\xe4\x18v\xc3n\x91l\xf7Pt\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4/\xea\x1b/\x83O\x02\xfcT3?\x8a\x80\x9f\x048\xe5\x87\n\xa9\x89\x01\x18T\xd0\xf9\xce\xe4\x00\x00\u07d4/\xee6\xa4\x9e\xe5\x0e\xcfqo\x10G\x91VFw\x9f\x8b\xa0?\x899B\"\xc4\u0686\xd7\x00\x00\u07d4/\xef\x81G\x8aK.\x80\x98\xdb_\xf3\x87\xba!S\xf4\xe2+y\x896'\xe8\xf7\x127<\x00\x00\u07d4/\xf1`\xc4Or\xa2\x99\xb5\xec-q\xe2\x8c\xe5Dm/\u02ef\x89\x13\x84\x00\xec\xa3d\xa0\x00\x00\u07d4/\xf1\xcaU\xfd\x9c\xec\x1b\x1f\xe9\U00029af7LQ<\x1e*\xaa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94/\xf5\u02b1,\r\x95\u007f\xd33\xf3\x82\xee\xb7Q\a\xa6L\xb8\xe8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94/\xf80\xcfU\xfb\x00\u0560\xe05\x14\xfe\xcdD1K\xd6\xd9\xf1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4/\xfe\x93\xec\x1aV6\xe9\xee4\xafp\xdf\xf5&\x82\xe6\xffpy\x89lk\x93[\x8b\xbd@\x00\x00\u07d40\x03y\x88p&q\xac\xbe\x89,\x03\xfeW\x88\xaa\x98\xaf(z\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d40$\x8dX\xe4\x14\xb2\x0f\xed:lH+Y\xd9\xd8\xf5\xa4\xb7\xe2\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4019\xbcYd\x03\xd5\u04d3\x1fwLf\u013aFtT\u06c9\\%\xe1J\xea(?\x00\x00\u079408\x00\x87xie\x14\x9e\x81B;\x15\xe3\x13\xba2\xc5\u01c3\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d40:0\xacB\x86\xae\x17\xcfH=\xad{\x87\fk\xd6M{J\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d40?\xba\xeb\xbeF\xb3[n[t\x94j_\x99\xbc\x15\x85\xca\xe7\x89/\x9a\xc0i_[\xba\x00\x00\u07d40ADZ3\xba\x15\x87A\x16\r\x9c4N\xb8\x8e\\0o\x94\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d40H\x01d\xbc\xd8It\xeb\xc0\xd9\f\x9b\x9a\xfa\xb6&\xcd\x1cs\x89+^:\xf1k\x18\x80\x00\x00\u07d40N\u019atTW!\xd71j\xefM\u03f4\x1a\u015e\xe2\xf0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x940Q\x182\x91\x8d\x804\xa7\xbe\xe7.\xf2\xbf\xeeD\x0e\u02fc\xf6\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d40Q?\u029f6\xfdx\x8c\xfe\xa7\xa3@\xe8m\xf9\x82\x94\xa2D\x89\x18;_\x03\xb1G\x9c\x00\x00\u07d40U\xef\xd2`)\xe0\xd1\x1b\x93\r\xf4\xf5;\x16,\x8c?\xd2\u0389\x1b\x1a\b\x927\a=\x00\x00\u07d40]&\xc1\v\xdc\x10?k\x9c!'.\xb7\xcb-\x91\b\xc4~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d40_x\xd6\x18\xb9\x90\xb4)[\xac\x8a-\xfa&(\x84\xf8\x04\xea\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x940d\x89\x9a\x96\x1a>\x1d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d40\x98\xb6]\xb9>\xca\xca\xf75\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d40\uc4d2$J!\b\u0247\xbc\\\xdd\xe0\ud7c3z\x81{\x89T\x99%\xf6\xc9\xc5%\x00\x00\xe0\x940\xed\x11\xb7{\xc1~^f\x94\u023c[nG\x98\xf6\x8d\x9c\xa7\x8a\x1eo\xb3B\x1f\xe0)\x9e\x00\x00\u07d40\xf7\xd0%\xd1o{\xee\x10U\x80Ho\x9fV\x1c{\xae?\xef\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x940\xfb\xe5\x88_\x9f\xcc\xe9\xea^\u06c2\xedJ\x11\x96\xdd%\x9a\xed\x8a\x01\x19\xe4\u007f!8\x1f@\x00\x00\u07d41\x04}p?c\xb94$\xfb\xbdn/\x1f\x9et\xde\x13\xe7\t\x89\x9a\x81f\xf7\u6ca7\x80\x00\u07d411?\xfdc[\xf2\xf32HA\xa8\x8c\a\xed\x14aD\xce\xeb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d41Y\xe9\fH\xa9\x15\x90J\xdf\u24b2/\xa5\xfd^ryk\x896\xaf\xe9\x8f&\x06\x10\x00\x00\u07d41]\xb7C\x9f\xa1\u0574#\xaf\xa7\xddq\x98\xc1\xcft\xc9\x18\xbc\x89 \x86\xac5\x10R`\x00\x00\u07d41^\xf2\xdab\x0f\xd30\xd1.\xe5]\xe5\xf3)\xa6\x96\xe0\xa9h\x89\b!\xab\rD\x14\x98\x00\x00\u07d41n\x92\xa9\x1b\xbd\xa6\x8b\x9e/\x98\xb3\xc0H\x93N<\xc0\xb4\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d41n\xb4\xe4}\xf7\x1bB\xe1mo\xe4h%\xb72{\xaf1$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d41q\x87~\x9d\x82\f\xc6\x18\xfc\t\x19\xb2\x9e\xfd3?\xdaI4\x8965\u026d\xc5\u07a0\x00\x00\u07d41|\xf4\xa2<\xb1\x91\xcd\xc5c\x12\u009d\x15\xe2\x10\xb3\xb9\xb7\x84\x89\a\xcef\xc5\x0e(@\x00\x00\u07d41\x8b.\xa5\xf0\xaa\xa8y\xc4\xd5\xe5H\xac\x9d\x92\xa0\xc6t\x87\xb7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d41\x8cv\xec\xfd\x8a\xf6\x8dpUSR\xe1\xf6\x01\xe3Y\x88\x04-\x89\x1b1\x19.h\xc7\xf0\x00\x00\u07d41\x8f\x1f\x8b\xd2 \xb0U\x8b\x95\xfb3\x10\x0f\xfd\xbbd\r|\xa6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d41\xaa;\x1e\xbe\x8cM\xbc\xb6\xa7\b\xb1\xd7H1\xe6\x0eIv`\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d41\xab\b\x89f\xec\xc7\"\x92X\xf6\t\x8f\xceh\xcf9\xb3\x84\x85\x8965\u026d\xc5\u07a0\x00\x00\xe0\x941\xadM\x99F\xef\t\xd8\xe9\x88\xd9F\xb1\"\u007f\x91A\x90\x176\x8a\x04\xd8S\xc8\xf8\x90\x89\x80\x00\x00\xe0\x941\xb4;\x01]\x00\x81d~h\x00\x00\u07d424\x86\xcad\xb3uGO\xb2\xb7Y\xa9\xe7\xa15\x85\x9b\xd9\xf6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d427I\xa3\xb9q\x95\x9eF\u0234\x82-\xca\xfa\xf7\xaa\xf9\xbdn\x89\x01\x16q\xa5\xb2Ep\x00\x00\u07d42:\xadA\xdfKo\xc8\xfe\u038c\x93\x95\x8a\xa9\x01\xfah\bC\x894\x95tD\xb8@\xe8\x00\x00\xe0\x942;<\xfe>\xe6+\xbd\xe2\xa2a\xe5<\xb3\xec\xc0X\x10\xf2\u018a\x02\ub3b1\xa1r\u0738\x00\x00\u07d42?\xca^\xd7\u007fi\x9f\x9d\x990\xf5\xce\xef\xf8\xe5oY\xf0<\x89Hz\x9a0E9D\x00\x00\u07d42H\\\x81\x87(\xc1\x97\xfe\xa4\x87\xfb\xb6\xe8)\x15\x9e\xba\x83p\x899!\xb4\x13\xbcN\xc0\x80\x00\xe0\x942P\xe3\xe8X\xc2j\xde\u032d\xf3jVc\xc2*\xa8LAp\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x942Y\xbd/\xdd\xfb\xbco\xba\u04f6\xe8t\xf0\xbb\xc0,\xda\x18\xb5\x8a\x02\x84`VI[\r\x18\x80\x00\u07d42uIo\xd4\u07491\xfdi\xfb\n\v\x04\xc4\xd1\xff\x87\x9e\xf5\x89\x18-~L\xfd\xa08\x00\x00\u07d42{\xb4\x9euOo\xb4\xf73\xc6\xe0o9\x89\xb4\xf6]K\xee\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d42\x82y\x1do\xd7\x13\xf1\xe9OK\xfdV^\xaax\xb3\xa0Y\x9d\x89Hz\x9a0E9D\x00\x00\u07d42\x83\xeb\u007f\x917\xdd9\xbe\xd5_\xfek\x8d\xc8E\xf3\xe1\xa0y\x89\x03\x97\n\xe9!Ux\x00\x00\u07d42\x86\t\x97\xd70\xb2\xd8;s$\x1a%\xd3f}Q\xc9\b\xef\x89\x1b\x1a\b\x927\a=\x00\x00\xe0\x942\x86\u047cez1,\x88G\xd9<\xb3\xcbyP\xf2\xb0\xc6\xe3\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x942\xa2\r\x02\x8e,b\x18\xb9\xd9[D\\w\x15$cj\"\xef\x8a\x02\x02\xfe\xfb\xf2\xd7\xc2\xf0\x00\x00\u07d42\xa7\x06\x91%\\\x9f\xc9y\x1aOu\u0238\x1f8\x8e\n%\x03\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d42\xb7\xfe\xeb\xc5\u015b\xf6^\x86\x1cL\v\xe4*v\x11\xa5T\x1a\x89w\u9aa8R\\\x10\x00\x00\xe0\x942\xba\x9a}\x04#\xe0:R_\xe2\xeb\xebf\x1d \x85w\x8b\u060a\x04<3\xc1\x93ud\x80\x00\x00\u07d42\xbb.\x96\x93\xe4\xe0\x854M/\r\xbdF\xa2\x83\u3807\xfd\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x942\xc2\xfd\u2daa\xbb\x80\u5ba2\xb9I\xa2\x17\xf3\xcb\t\"\x83\x8a\x010a`\xaf\xdf 7\x80\x00\u07d42\xd9P\xd5\xe9>\xa1\u0574\x8d\xb4qO\x86{\x03 \xb3\x1c\x0f\x897\b\xba\xed=h\x90\x00\x00\u07d42\u06f6qlT\xe81e\x82\x9aJ\xbb6uxI\xb6\xe4}\x8965\u026d\xc5\u07a0\x00\x00\u07d42\xebd\xbe\x1b]\xed\xe4\b\u01bd\xef\xben@\\\x16\xb7\xed\x02\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d42\xef\\\xdcg\x1d\xf5V*\x90\x1a\xee]\xb7\x16\xb9\xbev\xdc\xf6\x89lk\x93[\x8b\xbd@\x00\x00\u07d42\xf2\x9e\x87'\xa7LkC\x01\xe3\xff\xff\x06\x87\xc1\xb8p\xda\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d42\xfa\x0e\x86\xcd\b}\u058di1\x90\xf3-\x931\t\t\xedS\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d42\xfb\xee\xd6\xf6&\xfc\xdf\xd5\x1a\xca\xfbs\v\x9e\xef\xf6\x12\xf5d\x89lk\x93[\x8b\xbd@\x00\x00\u07943\x00\xfb\x14\x9a\xde\xd6[\u02e6\xc0N\x9c\u05b7\xa0;\x89;\xb1\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x943\x01\xd9\xca/;\xfe\x02by\xcdh\x19\xf7\x9a)=\x98\x15n\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\xe0\x943\b\xb04f\xc2z\x17\xdf\xe1\xaa\xfc\xeb\x81\xe1m)4Vo\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07943\x1a\x1c&\xcci\x94\xcd\xd3\xc1K\xec\xe2v\xff\xffK\x9d\xf7|\x88\xfaz\xed\xdfO\x06\x80\x00\xe0\x943&\xb8\x8d\xe8\x06\x18DT\xc4\v'\xf3\t\xd9\xddm\u03f9x\x8a\x03\xca\\f\u067cD0\x00\x00\xe0\x943)\xeb;\xafCE\xd6\x00\xce\xd4\x0en\x99ueo\x117B\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d432\r\xd9\x0f+\xaa\x11\r\xd34\x87*\x99\x8f\x14\x84&E<\x8965f3\xeb\xd8\xea\x00\x00\u07d436\xc3\xefn\x8bP\xee\x90\xe07\xb1d\xb7\xa8\xea_\xaa\xc6]\x89\x0e\u0223\xa7\x1c\"T\x00\x00\xe0\x9438\fo\xffZ\xcd&Q0\x96)\u06daq\xbf? \u017a\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d43:\xd1Yd\x01\xe0Z\xea-6\xcaG1\x8e\xf4\xcd,\xb3\u07c9\x9d\xc0\\\xce(\u00b8\x00\x00\u07d43C@\xeeK\x9c\u0701\xf8P\xa7Q\x16\xd5\x0e\u9d98%\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d43H\x1e\x85n\xbe\u050e\xa7\b\xa2t&\xef(\xe8g\xf5|\u0449\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x943V[\xa9\xda,\x03\xe7x\xce\x12)O\b\x1d\xfe\x81\x06M$\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07943X\x1c\xee#0\x88\xc0\x86\r\x94N\f\xf1\u03ab\xb8&\x1c.\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d43XX\xf7I\xf1i\u02bc\xfeR\xb7\x96\xe3\xc1\x1e\xc4~\xa3\u0089\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x943^\"\x02[zw\u00e0t\u01cb\x8e=\xfe\a\x13A\x94n\x8a\x02'\xcas\n\xb3\xf6\xac\x00\x00\u07d43b\x9b\xd5/\x0e\x10{\xc0q\x17ld\xdf\x10\x8fdw}I\x89\x01\xcf\xddth!n\x80\x00\u07d43{;\u07c6\xd7\x13\xdb\xd0{]\xbf\xcc\x02+z{\x19F\xae\x89\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d43|\xfe\x11W\xa5\u0191 \x10\xddV\x153y\x17i\u00b6\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d43\xb36\xf5\xba^\xdb{\x1c\xcc~\xb1\xa0\u0644\xc1#\x1d\x0e\u0709lk\x93[\x8b\xbd@\x00\x00\u07d43\xc4\a\x13;\x84\xb3\xcaL=\xed\x1fFX\x90\f8\x10\x16$\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\xe0\x943\xd1r\xab\a\\Q\xdb\x1c\xd4\n\x8c\xa8\xdb\xff\r\x93\xb8C\xbb\x8a\x016x\x05\x10\xd1-\xe3\x80\x00\u07d43\xe9\xb7\x18#\x95.\x1ff\x95\x8c'\x8f\u008b\x11\x96\xa6\u0164\x89\x05k\xc7^-c\x10\x00\x00\u07d43\xeakxU\xe0[\a\xab\x80\u06b1\xe1M\xe9\xb6I\xe9\x9bl\x89\x1c\xd6\xfb\xadW\xdb\xd0\x00\x00\u07d43\xf1R#1\rD\u078bf6h_:L=\x9cVU\xa5\x89\r\x94b\xc6\xcbKZ\x00\x00\u07d43\xf4\xa6G\x1e\xb1\xbc\xa6\xa9\xf8[;Hr\xe1\aU\xc8+\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d43\xfbWzM!O\xe0\x10\xd3,\xca|>\xed\xa6?\x87\xce\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d43\xfdq\x8f\v\x91\xb5\xce\u020a]\xc1^\xec\xf0\xec\xef\xa4\xef=\x89\x17r$\xaa\x84Lr\x00\x00\u07d44\x14\x80\u030c\xb4v\xf8\xd0\x1f\xf3\b\x12\xe7\xc7\x0e\x05\xaf\xaf]\x89lk\x93[\x8b\xbd@\x00\x00\u07d44'-^ut1]\xca\u9afd1{\xac\x90(\x9dGe\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x9440\xa1c\x81\xf8i\xf6\xeaT#\x91XU\xe8\x00\x885%\xa9\x8a\x03\xca\\f\u067cD0\x00\x00\u07d441\x86%\x81\x8e\xc1?\x11\x83Z\xe9sS\xce7}oY\n\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d449<]\x91\xb9\xdeYr\x03\xe7[\xacC\t\xb5\xfa=(\u00c9\n\x84Jt$\xd9\xc8\x00\x00\u07d449\x99\x8b$|\xb4\xbf\x8b\xc8\nm+5'\xf1\xdf\xe9\xa6\u0489\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d44C}\x14ed\v\x13l\xb5\x84\x1c?\x93O\x9b\xa0\xb7\t}\x89\t`\xdbwh\x1e\x94\x00\x00\u07d44J\x8d\xb0\x86\xfa\xedN\xfc7\x13\x1b:\"\xb0x-\xadp\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x944fM\"\x0f\xa7\xf3yX\x02J32\u0584\xbc\xc6\xd4\u023d\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d44f\xf6~9cl\x01\xf4;:!\xa0\xe8R\x93%\xc0\x86$\x89-\xb1\x16vP\xac\xd8\x00\x00\u07d44\x856\x1e\xe6\xbf\x06\xefe\b\xcc\xd2=\x94d\x1f\x81M>/\x89lk\x93[\x8b\xbd@\x00\x00\u07d44\x85\xf6!%d3\xb9\x8aB\x00\xda\xd8W\xef\xe5Y7\uc609lk\x93[\x8b\xbd@\x00\x00\u07d44\x95\x8aF\xd3\x0e0\xb2s\xec\xc6\xe5\xd3X\xa2\x12\xe50~\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d44\x97\xddf\xfd\x11\x80q\xa7\x8c,\xb3n@\xb6e\x1c\xc8%\x98\x89\x05\xf1\x01kPv\xd0\x00\x00\xe0\x944\x9a\x81k\x17\xab='\xbb\xc0\xae\x00Q\xf6\xa0p\xbe\x1f\xf2\x9d\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d44\x9d,\x91\x8f\u041e(\a1\x8ef\xceC)\t\x17k\xd5\v\x89<\xb7\x1fQ\xfcU\x80\x00\x00\u07d44\xa0C\x1f\xff^\xad\x92\u007f\xb6`\f\x1e\xa8\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d44\xff&\xeb`\xa8\u0469ZH\x9f\xae\x13n\xe9\x1dNX\bL\x89 \x86\xac5\x10R`\x00\x00\u07d44\xffX)R\xff$E\x8f{\x13\xd5\x1f\vO\x98p\"\xc1\xfe\x89\x98\x06\xde=\xa6\xe9x\x00\x00\u07d45\x10k\xa9N\x85c\u0533\xcb<\\i,\x10\xe6\x04\xb7\xce\u0609lk\x93[\x8b\xbd@\x00\x00\xe0\x945\x14_b\x03\x97\u019c\xb8\xe0\tb\x96\x1f\x0fH\x86d9\x89\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d45\x14t0\xc3\x10e\x00\u77e2\xf5\x02F.\x94p<#\xb1\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x945\x17\x87\x845\x05\xf8\xe4\xef\xf4ef\xcc\u695fM\x1c_\xe7\x8a\x01\xf5q\x89\x87fKH\x00\x00\xe0\x945\x1f\x16\xe5\xe0sZ\xf5gQ\xb0\xe2%\xb2B\x11q9@\x90\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\xe0\x945$\xa0\x00#N\xba\xaf\a\x89\xa14\xa2\xa4\x178<\xe5(*\x8a\x011yU\x94}\x8e,\x00\x00\u07d45&\xee\xce\x1ak\xdc>\xe7\xb4\x00\xfe\x93[HF?1\xbe\u05c9\x04w\x87\x9bm\x140\x00\x00\u07d45*x_J\x92\x162PL\xe5\xd0\x15\xf8\xd7FO\xa3\xdb\x14\xe7r\x92\x13\u03aa7\x8c\t^\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d45\xaf\x04\n\f\xc23zv\xaf(\x81T\xc7V\x1e\x1a#3I\x8965\u026d\xc5\u07a0\x00\x00\u07d45\xb0>\xa4$W6\xf5{\x85\xd2\xebyb\x8f\x03m\xdc\xd7\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d45\xbd$he\xfa\xb4\x90\xac\bz\xc1\xf1\xd4\xf2\xc1\r\f\xda\x03\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x945\xbff\x88R/5Fz\u007fu0#\x14\xc0+\xa1v\x80\x0e\x8a\x03\xafA\x82\x02\xd9T\xe0\x00\x00\u07d45\u022d\xc1\x11%C+;w\xac\xd6F%\xfeX\xeb\xee\x9df\x89lk\x93[\x8b\xbd@\x00\x00\u07d45\u0497\x0fI\xdc\xc8\x1e\xa9\xeep~\x9c\x8a\n\xb2\xa8\xbbtc\x89N\x10\x03\xb2\x8d\x92\x80\x00\x00\u07d45\xe0\x96\x12\r\xea\xa5\xc1\xec\xb1d^,\u02cbN\xdb\xd9)\x9a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d45\xea!c\xa3\x8c\u07da\x12?\x82\xa5\xec\x00%\x8d\xae\v\xc7g\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d45\xf1\xda\x12{\x837o\x1b\x88\xc8*3Y\xf6z^g\xddP\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d45\xf2\x94\x9c\xf7\x8b\xc2\x19\xbbO\x01\x90|\xf3\xb4\xb3\u04c6T\x82\x89\x0f\xb5\xc8l\x92\xe44\x00\x00\u07d45\xf5\x86\x01I\xe4\xbb\xc0K\x8a\u0172r\xbeU\xad\x1a\xcaX\xe0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d46\x02E\x8d\xa8omj\x9d\x9e\xb0=\xaf\x97\xfeV\x19\xd4B\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x057-\x93\xa9\x01\t\x88\x01\x8f\x9f1]\x03.\u0448\x0f\xa1\x89\x1b\x1b\xcfQ\x89j}\x00\x00\u07d46\x16\xd4H\x98_]2\xae\xfa\x8b\x93\xa9\x93\xe0\x94\xbd\x85I\x86\x89\v\"\u007fc\xbe\x81<\x00\x00\u07d46\x16\xfbF\xc8\x15x\xc9\xc8\xebM;\xf8\x80E\x1a\x887\x9d}\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d46\x1cu\x93\x16\x96\xbc=B}\x93\xe7lw\xfd\x13\xb2A\xf6\xf4\x89\x1d\xc5\xd8\xfc&m\xd6\x00\x00\u07d46\x1d\x9e\xd8\v[\xd2|\xf9\xf1\"o&u2X\xee_\x9b?\x89\xbfi\x14\xba}r\xc2\x00\x00\u07d46\x1f;\xa9\xed\x95kw\x0f%}6r\xfe\x1f\xf9\xf7\xb0$\f\x89 \x86\xac5\x10R`\x00\x00\u07d46\"|\u07e0\xfd;\x9d~jtF\x85\xf5\xbe\x9a\xa3f\xa7\xf0\x89\n\xc2s\x0e\xe9\xc6\xc1\x80\x00\u07d46/\xbc\xb1\x06b7\n\x06\x8f\xc2e&\x02\xa2Wy7\xcc\xe6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d460\xc5\xe5e\u03aa\x8a\x0f\x0f\xfe2\x87^\xae*l\xe6<\x19\x89\t7r+7t\xd0\x00\x00\u07d463\x9f\x84\xa5\u00b4L\xe5=\xfd\xb6\xd4\xf9}\xf7\x82\x12\xa7\u07c9\x11o\x18\xb8\x17\x15\xa0\x00\x00\u07d464:\xec\xa0{n\u054a\x0eb\xfaN\xcbI\x8a\x12O\xc9q\x89\x10CV\x1a\x88)0\x00\x00\u07d46au@4\x81\xe0\xab\x15\xbbQF\x15\u02f9\x89\xeb\u018f\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d46ro;\x88Z$\xf9)\x96\u0681b^\u022d\x16\xd8\xcb\xe6\x89S\xafu\u0441HW\x80\x00\xe0\x946s\x95C\x99\xf6\u07feg\x18\x18%\x9b\xb2x\xe2\xe9.\xe3\x15\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d46u\x8e\x04\x9c\u064b\u03a1\"w\xa6v\xf9)sb\x89\x00#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d46\u007fY\u0302yS)8NA\xe1(1\x15\xe7\x91\xf2j\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x81\x0f\xf9\xd2\x13\xa2q\xed\xa2\xb8\xaay\x8b\xe6T\xfaK\xbe\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x8cT\x14\xb5k\x84U\x17\x1f\xbf\ab \xc1\u02e4\xb5\xca1\x89\x1e>\xf9\x11\xe8=r\x00\x00\xe0\x946\x90$k\xa3\xc8\x06y\xe2.\xacD\x12\xa1\xae\xfc\xe6\xd7\u0342\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d46\x92\x8bU\xbc\x86\x15\t\xd5\x1c\x8c\xf1\xd5F\xbf\xecn>\x90\xaf\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d46\x98\"\xf5W\x8b@\xdd\x1fDqpk\"\u0357\x13R\xdak\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d46\x9e\xf7a\x19_:7>$\xec\xe6\xcd\"R\x0f\xe0\xb9\xe8n\x89\x1c\xff\xaf\xc9M\xb2\b\x80\x00\u07d46\xa0\x8f\xd6\xfd\x1a\xc1|\xe1^\xd5~\xef\xb1*+\u2048\xbf\x89Hz\x9a0E9D\x00\x00\u07d46\xa0\xe6\x1e\x1b\xe4\u007f\xa8~0\xd3(\x88\xee\x030\x90\x1c\xa9\x91\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x946\xb2\xc8^:\xee\xeb\xb7\rc\u0124s\f\xe2\xe8\xe8\x8a6$\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x946\xbfC\xff5\u07d0\x90\x88$3l\x9b1\xce3\x06~/P\x8aIr\x15\x10\xc1\xc1\xe9H\x00\x00\u07d46\xbf\xe1\xfa;{p\xc1r\xeb\x04/h\x19\xa8\x97%\x95A>\x8965\u026d\xc5\u07a0\x00\x00\xe0\x946\xc5\x10\xbf\x8dnV\x9b\xf2\xf3}G&]\xbc\xb5\x02\xff+\u038a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x946\xd8]\xc3h1V\xe6;\xf8\x80\xa9\xfa\xb7x\x8c\xf8\x14:'\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d46\u07cf\x88<\x12s\xec\x8a\x17\x1fz3\xcf\xd6I\xb1\xfe`u\x89\fRHJ\xc4\x16\x89\x00\x00\xe0\x946\xe1Va\f\xd8\xffd\xe7\x80\u061d\x00T8\\\xa7gU\xaa\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d46\xfe\xc6,,B^!\x9b\x18D\x8a\xd7W\x00\x9d\x8cT\x02o\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d47\x00\xe3\x02t$\xd99\xdb\xde]B\xfbx\xf6\xc4\xdb\xec\x1a\x8f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d47\x02\xe7\x04\xcc!at9\xadN\xa2zW\x14\xf2\xfd\xa1\xe92\x8965\u026d\xc5\u07a0\x00\x00\u07d47\x035\fMo\xe374,\xdd\xc6[\xf1\xe28k\xf3\xf9\xb2\x89m\x81!\xa1\x94\xd1\x10\x00\x00\xe0\x947\b\xe5\x9d\xe6\xb4\x05P\x88x)\x02\xe0W\x9cr\x01\xa8\xbfP\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d47\x126~^U\xa9mZ\x19\x16\x8fn\xb2\xbc~\x99q\xf8i\x8965\u026d\xc5\u07a0\x00\x00\u07d47\x19Zc]\xccb\xf5jq\x80I\xd4~\x8f\x9f\x96\x83(\x91\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d47'4\x1f&\xc1 \x01\xe3x@^\xe3\x8b-\x84d\xecq@\x89lk\x93[\x8b\xbd@\x00\x00\u07d47.E:kb\x9f'g\x8c\u022e\xb5\xe5|\xe8^\xc0\xae\xf9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d474\xcb\x18t\x91\xed\xe7\x13\xae[;-\x12(J\xf4k\x81\x01\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d477!n\xe9\x1f\x17w2\xfbX\xfa@\x97&r\a\xe2\xcfU\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d47M;\xbb\x057Q\xf9\xf6\x8d\xdb\a\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d48r\xf4\x8d\xc5\xe3\xf8\x17\xbck*\xd2\xd00\xfc^\x04q\x19=\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d48~\xea\xfdk@\t\u07af\x8b\u0578Zr\x98:\x8d\xcc4\x87\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d48\x81\xde\xfa\xe1\xc0{<\xe0Lx\xab\xe2k\f\u070ds\xf0\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d48\x83\xbe\xcc\b\xb9\xbeh\xad;\b6\xaa\u00f6 \xdc\x00\x17\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d48\x85\xfe\xe6q\a\xdc:\xa9\x8a\x1d\x99:t\xdf\\\xd7T\xb9\x8dR\x9a\x89a\t=|,m8\x00\x00\u07d48\xe4m\xe4E<8\xe9A\xe7\x93\x0fC0O\x94\xbb{+\xe8\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d48\xe7\u06e8\xfdO\x1f\x85\r\xbc&I\xd8\xe8O\tR\xe3\xeb<\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d48\xe8\xa3\x1a\xf2\xd2e\xe3\x1a\x9f\xff-\x8fF(m\x12E\xa4g\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d48\xeao[Z{\x88AuQ\xb4\x12=\xc1'\xdf\xe94-\xa6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d48\xee\xc6\xe2\x17\xf4\xd4\x1a\xa9 \xe4$\xb9RQ\x97\x04\x1c\xd4\u0189\xf0\r%\xeb\x92.g\x00\x00\xe0\x948\xf3\x87\xe1\xa4\xedJs\x10n\xf2\xb4b\xe4t\xe2\xe3\x14:\u040a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d49\x11a\xb0\xe4<0 f\u898d,\xe7\xe1\x99\xec\xdb\x1dW\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x949\x15\uad6b.Yw\xd0u\xde\xc4}\x96\xb6\x8bK\\\xf5\x15\x8a\r\a\x01\x81\x85\x12\x0f@\x00\x00\u07d49\x1aw@\\\t\xa7+^\x846#z\xaa\xf9]h\xda\x17\t\x89\x02\xa9&J\xf3\u0479\x00\x00\u07d49\x1f \x17m\x126\rrMQG\n\x90p6uYJM\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d49$3\xd2\u0383\xd3\xfbJv\x02\u0323\xfa\xcaN\xc1@\xa4\xb0\x89\x02\xc3\xc4e\xcaX\xec\x00\x00\xe0\x949?x;\\\u06c6\"\x1b\xf0)O\xb7\x14\x95\x9c{E\x89\x9c\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d49?\xf4%^\\e\x8f.\u007f\x10\xec\xbd)%rg\x1b\xc2\u0489lk\x93[\x8b\xbd@\x00\x00\u07d49A2`\x0fAU\xe0\u007fME\xbc>\xb8\xd9\xfbr\xdc\u05c4\x89\x9fn\x92\xed\xea\a\xd4\x00\x00\u07d49Q\xe4\x8e<\x86\x9ekr\xa1C\xb6\xa4Ph\u0379\xd4f\u0409\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x949T\xbd\xfe\v\xf5\x87\u0195\xa3\x05\xd9$L=[\xdd\xda\u027b\x8a\x04\x10'\x83'\xf9\x85`\x80\x00\u07d49]m%U \xa8\xdb)\xab\xc4}\x83\xa5\u06ca\x1a}\xf0\x87\x89\x05k\xc7^-c\x10\x00\x00\u07d49ck%\x81\x1b\x17j\xbf\xcf\xee\xcad\xbc\x87E/\x1f\xdf\xf4\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d49i\xb4\xf7\x1b\xb8u\x1e\xdeC\xc0\x166:zaOv\x11\x8e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x949x/\xfe\x06\xacx\x82*<:\x8a\xfe0^P\xa5a\x88\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d49zn\xf8v:\x18\xf0\x0f\xac!~\x05\\\r0\x94\x10\x10\x11\x89lk\x93[\x8b\xbd@\x00\x00\u07d49|\u06cc\x80\xc6yP\xb1\x8deB)a\x0e\x93\xbf\xa6\xee\x1a\x89?\x95\xc8\xe0\x82\x15!\x00\x00\u07d49\x82O\x8b\xce\xd1v\xfd>\xa2.\u01a4\x93\xd0\xcc\xc3?\xc1G\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d49\x93l'\x19E\v\x94 \xcc%\"\u03d1\xdb\x01\xf2'\xc1\xc1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d49\x95\xe0\x96\xb0\x8aZrh\x00\xfc\xd1}\x9cd\xc6N\b\x8d+\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d49\x9a\xa6\xf5\xd0x\xcb\tp\x88+\u0259 \x06\xf8\xfb\xdf4q\x8965\u026d\xc5\u07a0\x00\x00\u07d49\xaa\x05\xe5m}28T!\u03d36\xe9\r=\x15\xa9\xf8Y\x89\x01h\u048e?\x00(\x00\x00\u07d49\xaa\xf0\x85M\xb6\xeb9\xbc{.C\x84jv\x17\x1c\x04E\u0789dI\xe8NG\xa8\xa8\x00\x00\u07d49\xb1\xc4q\xae\x94\xe1!dE.\x81\x1f\xbb\xe2\xb3\xcdru\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d49\xb2\x992t\x90\xd7/\x9a\x9e\xdf\xf1\x1b\x83\xaf\xd0\xe9\xd3\xc4P\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d49\xba\u018d\x94xY\xf5\x9e\x92&\b\x9c\x96\xd6.\x9f\xbe<\u0789\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x949\xbf\xd9xh\x9b\xec\x04\x8f\xc7v\xaa\x15$\u007f^\x1d|9\xa2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d49\xc7s6|\x88%\xd3YlhoB\xbf\r\x141\x9e?\x84\x89\a?u\u0460\x85\xba\x00\x00\u07d49\u05291@,\fy\xc4W\x18o$\u07c7)\u03d5p1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d49\xd6\xca\xca\"\xbc\xcdjr\xf8~\xe7\u05b5\x9e\v\xde!\xd7\x19\x89l\x87T\xc8\xf3\f\b\x00\x00\u07d49\xe0\xdbM`V\x8c\x80\v\x8cU\x00\x02l%\x94\xf5v\x89`\x8965\u026d\xc5\u07a0\x00\x00\xe0\x949\xeeO\xe0\x0f\xbc\xeddph\xd4\xf5|\x01\xcb\"\xa8\v\xcc\u044a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d49\xf1\x983\x1eK!\xc1\xb7`\xa3\x15_J\xb2\xfe\x00\xa7F\x19\x89lk\x93[\x8b\xbd@\x00\x00\u07d49\xf4Fc\xd9%a\t\x1b\x82\xa7\r\xcfY=u@\x05\x97:\x89\n\u05cb.\xdc!Y\x80\x00\u07d4:\x03U\x94\xc7GGmB\xd1\xee\x96l6\"L\xdd\"I\x93\x89\x13J\xf7Ei\xf9\xc5\x00\x00\u07d4:\x04W(G\xd3\x1e\x81\xf7v\\\xa5\xbf\xc9\xd5W\x15\x9f6\x83\x89\a6-\r\xab\xea\xfd\x80\x00\xe0\x94:\x06\xe3\xbb\x1e\xdc\xfd\fD\xc3\aM\xe0\xbb`k\x04\x98\x94\xa2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794:\x10\x88\x8b~\x14\x9c\xae',\x010,2}\n\xf0\x1a\v$\x88\xeb\xec!\xee\x1d\xa4\x00\x00\u07d4:1\b\xc1\u6023;3l!\x13\x134@\x9d\x97\xe5\xad\xec\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4:6\x8e\xfeJ\u05c6\xe2c\x95\xec\x9f\u01adi\x8c\xae)\xfe\x01\x89\"E\x89\x96u\xf9\xf4\x00\x00\u07d4:=\xd1\x04\xcd~\xb0O!\x93/\xd43\xeaz\xff\u04d3i\xf5\x89\x13aO#\xe2B&\x00\x00\u07d4:B\x97\xda\xc4.\x1eO\xb8\xcb1\xec\xddC\xaew<\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94:\x99`&m\xf6I cS\x8a\x99\xf4\x87\xc9P\xa3\xa5\uc78a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4:\x9b\x11\x10)\xce\x1f \xc9\x10\x9czt\xee\xee\xf3OO.\xb2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4:\x9eTA\xd4K$;\xe5[u\x02z\x1c\ub7ac\xf5\r\xf2\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94:\xa0z4\xa1\xaf\u0216}=\x13\x83\xb9kb\u03d6\xd5\xfa\x90\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94:\xa4,!\xb9\xb3\x1c>'\xcc\xd1~\t\x9a\xf6y\xcd\xf5i\a\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4:\xa9H\xea\x029wU\xef\xfb/\x9d\xc99-\xf1\x05\x8f~3\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4:\xad\xf9\x8ba\xe5\u0216\xe7\xd1\x00\xa39\x1d2P\"]a\u07c9\f\xafg\x007\x01h\x00\x00\u07d4:\xaeHr\xfd\x90\x93\xcb\xca\xd1@o\x1e\x80x\xba\xb5\x03Y\xe2\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4:\xbb\x8a\xdf\xc6\x04\xf4\x8dY\x84\x81\x1d\u007f\x1dR\xfe\xf6u\x82p\x89\xf2\x97\x19\xb6o\x11\f\x00\x00\u07d4:\xc2\xf0\xff\x16\x12\xe4\xa1\xc3F\xd53\x82\xab\xf6\u0622[\xaaS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4:\xc9\xdczCj\xe9\x8f\xd0\x1cz\x96!\xaa\x8e\x9d\v\x8bS\x1d\x89a\t=|,m8\x00\x00\xe0\x94:\xd0aI\xb2\x1cU\xff\x86|\xc3\xfb\x97@\u04bc\xc7\x10\x121\x8a)\xb7d2\xb9DQ \x00\x00\u07d4:\xd7\x02C\u060b\xf0@\x0fW\xc8\xc1\xfdW\x81\x18H\xaf\x16*\x89.\x9e\xe5\u00c6S\xf0\x00\x00\u07d4:\xd9\x15\xd5P\xb7#AV \xf5\xa9\xb5\xb8\x8a\x85\xf3\x82\xf05\x8965\u026d\xc5\u07a0\x00\x00\u07d4:\xe1`\xe3\xcd`\xae1\xb9\xd6t-h\xe1Nv\xbd\x96\xc5\x17\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4:\xe6+\xd2q\xa7`c\u007f\xady\xc3\x1c\x94\xffb\xb4\xcd\x12\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4:\xeaN\x82\xd2@\x02H\xf9\x98q\xa4\x1c\xa2W\x06\r:\"\x1b\x8965\u026d\xc5\u07a0\x00\x00\u07d4:\xf6[>(\x89ZJ\x00\x11S9\x1d\x1ei\xc3\x1f\xb9\xdb9\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4;\a\xdbZ5\u007fZ\xf2HL\xbc\x9dw\xd7;\x1f\xd0Q\x9f\u01c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4;\n\u032fK`|\xfea\xd1s4\xc2\x14\xb7\\\xde\xfd\xbd\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\x13c\x1a\x1b\x89\xcbVeH\x89\x9a\x1d`\x91\\\xdc\xc4 [\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\x15\x90\x99\aR\a\u0180vc\xb1\xf0\xf7\xed\xa5J\xc8\xcc\xe3\x89j\xc4\xe6[i\xf9-\x80\x00\u07d4;\x197\xd5\u74f8\x9bc\xfb\x8e\xb5\xf1\xb1\xc9\xcak\xa0\xfa\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\"\xda*\x02q\xc8\xef\xe1\x02S'scji\xb1\xc1~\t\x89\x1b6\xa6DJ>\x18\x00\x00\u07d4;\"\u07a3\xc2_\x1bY\u01fd'\xbb\x91\u04e3\xea\xec\xef9\x84\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94;#g\xf8IK_\xe1\x8dh<\x05]\x89\x99\x9c\x9f=\x1b4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4;,E\x99\x0e!GDQ\xcfOY\xf0\x19U\xb31\xc7\xd7\u0249lk\x93[\x8b\xbd@\x00\x00\xe0\x94;A\x00\xe3\ns\xb0\xc74\xb1\x8f\xfa\x84&\u045b\x191/\x1a\x8a\v\xb5\u046ap\n\xfd\x90\x00\x00\u07d4;B\xa6m\x97\x9fX(4tz\x8b`B\x8e\x9bN\xec\xcd#\x89!\xa1\u01d0\xfa\xdcX\x00\x00\u07d4;Gh\xfdq\xe2\xdb,\xbe\u007f\xa0PH<'\xb4\xeb\x93\x1d\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;Vj\x8a\xfa\u0456\x82\xdc,\xe8g\x9a<\xe4D\xa5\xb0\xfdO\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94;\\%\x1d\u007f\u05c9;\xa2\t\xfeT\x1c\xec\xd0\xce%:\x99\r\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4;^\x8b\x17w\xca\x18A\x896\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94;\x93\xb1a6\xf1\x1e\xaf\x10\x99l\x95\x99\r;'9\xcc\xea_\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4;\xabK\x01\xa7\xc8K\xa1?\uea70\xbb\x19\x1bw\xa3\xaa\u0723\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4;\xb55\x98\xcc \xe2\x05]\xc5S\xb0I@J\u0277\xdd\x1e\x83\x89!W\x1d\xf7|\x00\xbe\x00\x00\u07d4;\xbc\x13\xd0J\xcc\xc0pz\xeb\u072e\xf0\x87\u0438~\v^\u327e\xd1\xd0&=\x9f\x00\x00\x00\u07d4;\xc6\xe3\xeezV\u038f\x14\xa3u2Y\x0fcqk\x99f\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\xc8]ls[\x9c\xdaK\xba_H\xb2K\x13\xe7\x0600{\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4;\xd6$\xb5H\xcbe\x976\x90~\u062a<\fp^$\xb5u\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\u0660m\x1b\xd3lN\xdd'\xfc\r\x1f[\b\x8d\xda\xe3\xc7*\x89\x1b\x1azB\v\xa0\r\x00\x00\u0794;\u077c\x814\xf7}UY\u007f\xc9|&\xd2f\x98\t\x06\x04\ub23e -j\x0e\xda\x00\x00\xe0\x94;\xf8n\u0623\x15>\xc93xj\x02\xac\t\x03\x01\x85^Wk\x8a_J\x8c\x83u\xd1U@\x00\x00\u07d4;\xfb\u04c4|\x17\xa6\x1c\xf3\xf1{R\xf8\ub879`\xb3\U000df262\xa1]\tQ\x9b\xe0\x00\x00\u07d4<\x03\xbb\xc0#\xe1\xe9?\xa3\xa3\xa6\xe4(\xcf\f\xd8\xf9^\x1e\u0189Rf<\u02b1\xe1\xc0\x00\x00\u07d4<\f=\ufb1c\xeaz\xcc1\x9a\x96\xc3\v\x8e\x1f\xed\xabEt\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4<\x15\xb3Q\x1d\xf6\xf04.sH\u0309\xaf9\xa1h\xb7s\x0f\x8965\u026d\xc5\u07a0\x00\x00\u07d4<\x1f\x91\xf3\x01\xf4\xb5e\xbc\xa2GQ\xaa\x1fv\x13\"p\x9d\u0749a\t=|,m8\x00\x00\xe0\x94<(l\xfb0\x14n_\u05d0\xc2\xc8T\x15RW\x8d\xe34\u060a\x02)\x1b\x11\xaa0n\x8c\x00\x00\u07d4<2.a\x1f\u06c2\rG\xc6\xf8\xfcd\xb6\xfa\xd7L\xa9_^\x89\r%\x8e\xce\x1b\x13\x15\x00\x00\u07d4\xa5\xe5\xbfb\xbb\u0309\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4<\x86\x9c\tie#\xce\xd8$\xa0pAF\x05\xbbv#\x1f\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4<\x92V\x19\u02731DF?\x057\u06165\x87\x06\xc5 \xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\x98YK\xf6\x8bW5\x1e\x88\x14\xae\x9em\xfd-%J\xa0o\x89\x10CV\x1a\x88)0\x00\x00\u07d4<\xad\xeb=>\xed?b1\x1dRU>p\xdfJ\xfc\xe5o#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94<\xae\xdbS\x19\xfe\x80eC\xc5nP!\xd3r\xf7\x1b\xe9\x06.\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4<\xaf\xaf^bPV\x15\x06\x8a\xf8\xeb\"\xa1:\u0629\xe5Pp\x89lf\x06E\xaaG\x18\x00\x00\u07d4<\xb1y\xcbH\x01\xa9\x9b\x95\u00f0\xc3$\xa2\xbd\xc1\x01\xa6S`\x89\x01h\u048e?\x00(\x00\x00\u07d4<\xb5a\u0386BK5\x98\x91\xe3d\xec\x92_\xfe\xff'}\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xcbq\xaah\x80\xcb\v\x84\x01-\x90\xe6\a@\xec\x06\xac\u05cf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\xce\xf8\x86yW9G\xe9I\x97y\x8a\x1e2~\b`:e\x89+\xc9\x16\u059f;\x02\x00\x00\xe0\x94<\xd1\xd9s\x1b\xd5H\xc1\xddo\u03a6\x1b\xebu\xd9\x17T\xf7\u04ca\x01\x16\x1d\x01\xb2\x15\xca\xe4\x80\x00\u07d4<\u04e6\xe95y\xc5mIAq\xfcS>z\x90\xe6\xf5\x94d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\u05b7Y<\xbe\xe7x0\xa8\xb1\x9d\b\x01\x95\x8f\xcdK\xc5z\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4<\xd7\xf7\xc7\xc257\x80\xcd\xe0\x81\xee\xecE\x82+%\xf2\x86\f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xe1\u0717\xfc\u05f7\xc4\u04e1\x8aI\xd6\xf2\xa5\xc1\xb1\xa9\x06\u05c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xea0*G*\x94\x03y\xdd9\x8a$\xea\xfd\xba\u07c8\xady\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94<\xec\xa9k\xb1\xcd\xc2\x14\x02\x9c\xbc^\x18\x1d9\x8a\xb9M=A\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4<\xf4\x84RO\xbd\xfa\xda\xe2m\xc1\x85\xe3++c\x0f\xd2\xe7&\x89\x18TR\xcb*\x91\xc3\x00\x00\u07d4<\xf9\xa1\xd4e\xe7\x8bp9\xe3iDx\xe2b{6\xfc\xd1A\x89J`S*\xd5\x1b\xf0\x00\x00\u07d4<\xfb\xf0fVYpc\x9e\x13\r\xf2\xa7\xd1k\x0e\x14\xd6\t\x1c\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94=\th\x8d\x93\xad\a\xf3\xab\xe6\x8cr'#\xcdh\t\x90C^\x8a\x06ZL\xe9\x9fv\x9en\x00\x00\u07d4=1X{_\u0546\x98Ex\x87%\xa6c)\nI\xd3g\x8c\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4=?\xadI\xc9\xe5\xd2u\x9c\x8e\x8eZzM`\xa0\xdd\x13V\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4=WO\xcf\x00\xfa\xe1\u064c\u023f\x9d\u07e1\xb3\x95;\x97A\xbc\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4=Z\x8b+\x80\xbe\x8b5\xd8\xec\xf7\x89\xb5\xedz\au\xc5\al\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4=f\xcdK\xd6M\\\x8c\x1b^\xea(\x1e\x10m\x1cZ\xad#s\x89i\xc4\xf3\xa8\xa1\x10\xa6\x00\x00\u0794=j\xe0S\xfc\xbc1\x8do\xd0\xfb\xc3S\xb8\xbfT.h\r'\x88\xc6s\xce<@\x16\x00\x00\u07d4=o\xf8,\x93w\x05\x9f\xb3\r\x92\x15r?`\xc7u\u0211\xfe\x89\r\x8e\\\xe6\x17\xf2\xd5\x00\x00\u07d4=y\xa8S\xd7\x1b\xe0b\x1bD\xe2\x97Yel\xa0u\xfd\xf4\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4=~\xa5\xbf\x03R\x81\x00\xed\x8a\xf8\xae\xd2e>\x92\x1bng%\x8965\u026d\xc5\u07a0\x00\x00\u07d4=\x81?\xf2\xb6\xedW\xb97\u06bf+8\x1d\x14\x8aA\x1f\xa0\x85\x89\x05k\xc7^-c\x10\x00\x00\u07d4=\x88\x143\xf0J}\r'\xf8ID\xe0\x8aQ-\xa3UR\x87\x89A\rXj \xa4\xc0\x00\x00\u07d4=\x89\xe5\x05\xcbF\xe2\x11\xa5?2\xf1g\xa8w\xbe\xc8\u007fK\n\x89\x01[5W\xf1\x93\u007f\x80\x00\xe0\x94=\x8d\a#r\x1es\xa6\xc0\xd8`\xaa\x05W\xab\xd1L\x1e\xe3b\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4=\x8f9\x88\x1b\x9e\xdf\xe9\x12'\xc3?\xa4\xcd\xd9\x1eg\x85D\xb0\x89\x04\xab\a\xbaC\xad\xa9\x80\x00\u07d4=\x9dk\xe5\u007f\xf8>\x06Y\x85fO\x12VD\x83\xf2\xe6\x00\xb2\x89n\xac\xe4?#\xbd\x80\x00\x00\u07d4=\xa3\x9c\xe3\xefJz9f\xb3.\xe7\xeaN\xbc#5\xa8\xf1\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4=\xaa\x01\u03b7\x0e\xaf\x95\x91\xfaR\x1b\xa4\xa2~\xa9\xfb\x8e\xdeJ\x89Zc\xd2\u027cvT\x00\x00\u07d4=\xb5\xfejh\xbd6\x12\xac\x15\xa9\x9aa\xe5U\x92\x8e\xec\xea\xf3\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4=\xb9\xed\u007f\x02L~&7/\xea\xcf+\x05\b\x03D^8\x10\x89E\xb1H\xb4\x99j0\x00\x00\u07d4=\xbf\r\xbf\xd7x\x90\x80\x053\xf0\x9d\xea\x83\x01\xb9\xf0%\u04a6\x8965\u026d\xc5\u07a0\x00\x00\u07d4=\xce\U0005c18b\x15\xd3N\xdaBn\xc7\xe0K\x18\xb6\x01p\x02\x89lh\xcc\u041b\x02,\x00\x00\xe0\x94=\xd1.Uj`76\xfe\xbaJo\xa8\xbdJ\xc4]f*\x04\x8a#u{\x91\x83\xe0x(\x00\x00\u07d4=\u078b\x15\xb3\u033a\xa5x\x01\x12\xc3\xd6t\xf3\x13\xbb\xa6\x80&\x89`\x1dQZ>O\x94\x00\x00\xe0\x94=\xde\xdb\xe4\x89#\xfb\xf9\xe56\xbf\x9f\xfb\aG\xc9\xcd\u04de\xef\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4=\xea\xe43'\x91?b\x80\x8f\xaa\x1bbv\xa2\xbdch\xea\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4=\xf7b\x04\x9e\u068a\u0192}\x90Lz\xf4/\x94\xe5Q\x96\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>\x04\r@\u02c0\xba\x01%\xf3\xb1_\xde\xfc\xc8?0\x05\xda\x1b\x898E$\xccp\xb7x\x00\x00\u07d4>\v\x8e\xd8n\xd6i\xe1'#\xafur\xfb\xac\xfe\x82\x9b\x1e\x16\x89QM\xe7\xf9\xb8\x12\xdc\x00\x00\xe0\x94>\f\xbejm\xcba\xf1\x10\xc4[\xa2\xaa6\x1d\u007f\xca\xd3\xdas\x8a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4>\x19KN\xce\xf8\xbbq\x1e\xa2\xff$\xfe\xc4\xe8{\xd02\xf7\u0449\x8b\x9d\xc1\xbc\x1a\x03j\x80\x00\xe0\x94>\x1b\"0\xaf\xbb\xd3\x10\xb4\x92jLwmZ\u705cf\x1d\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4>\x1cS0\x0eL\x16\x89\x12\x16<~\x99\xb9]\xa2h\xad(\n\x896b2\\\u044f\xe0\x00\x00\u07d4>\x1c\x96 c\xe0\xd5)YA\xf2\x10\u0723\xabS\x1e\xec\x88\t\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4>,\xa0\xd24\xba\xf6\a\xadFj\x1b\x85\xf4\xa6H\x8e\xf0\n\xe7\x89\x04\xda!\xa3H=V\x80\x00\u07d4>/&#^\x13zs$\xe4\xdc\x15K]\xf5\xafF\xea\x1aI\x89\x017\xaa\xd8\x03-\xb9\x00\x00\xe0\x94>1a\xf1\xea/\xbf\x12ny\xda\x18\x01\u0695\x12\xb3y\x88\u024a\nm\xd9\f\xaeQ\x14H\x00\x00\xe0\x94>6\xc1rS\xc1\x1c\xf3\x89t\xed\r\xb1\xb7Y\x16\r\xa67\x83\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4><\u04fe\xc0e\x91\xd64o%Kb\x1e\xb4\x1c\x89\x00\x8d1\x895\u07fe\u069f74\x00\x00\u07d4>E\xbdU\u06d0`\xec\xed\x92;\xb9\xcbs<\xb3W?\xb51\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4>M\x13\xc5Z\x84\xe4n\xd7\xe9\u02d0\xfd5^\x8a\u0651\u33c965\u026d\xc5\u07a0\x00\x00\u07d4>N\x92e\"<\x9782L\xf2\v\xd0`\x06\xd0\a>\u06cc\x89\a?u\u0460\x85\xba\x00\x00\xe0\x94>O\xbdf\x10\x15\xf6F\x1e\xd6s\\\xef\xef\x01\xf3\x14E\xde:\x8a\x03n4)\x98\xb8\xb0 \x00\x00\xe0\x94>S\xff!\a\xa8\u07be3(I:\x92\xa5\x86\xa7\xe1\xf4\x97X\x8a\x04\xe6\x9c*q\xa4\x05\xab\x00\x00\u07d4>Z9\xfd\xdap\xdf\x11&\xab\r\u011asx1\x1aSz\x1f\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\xe0\x94>Z\xbd\t\xceZ\xf7\xba\x84\x87\xc3Y\xe0\xf2\xa9:\x98k\v\x18\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4>\\\xb8\x92\x8cAx%\xc0:;\xfc\xc5!\x83\xe5\xc9\x1eB\u05c9\xe71\xd9\xc5,\x96/\x00\x00\u07d4>^\x93\xfbL\x9c\x9d\x12F\xf8\xf2G5\x8e\"\xc3\xc5\xd1{j\x89\b!\xab\rD\x14\x98\x00\x00\u07d4>a\x83P\xfa\x01ez\xb0\xef>\xba\xc8\xe3p\x12\xf8\xfc+o\x89\x98\x06\xde=\xa6\xe9x\x00\x00\u07d4>c\xce;$\xca(e\xb4\u0166\x87\xb7\xae\xa3Y~\xf6\xe5H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>f\xb8GiVj\xb6yE\xd5\xfa\x8175V\xbc\u00e1\xfa\x89\b=lz\xabc`\x00\x00\xe0\x94>v\xa6-\xb1\x87\xaat\xf68\x17S;0l\xea\xd0\xe8\u03be\x8a\x06\x9bZ\xfa\xc7P\xbb\x80\x00\x00\u07d4>z\x96k]\xc3W\xff\xb0~\x9f\xe0g\xc4W\x91\xfd\x8e0I\x89\x034-`\xdf\xf1\x96\x00\x00\xe0\x94>\x81w!u#~\xb4\xcb\xe0\xfe-\xca\xfd\xad\xff\xebj\x19\x99\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4>\x83I\xb6\u007fWED\x9fe\x93g\u066dG\x12\xdb[\x89Z\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4>\x83TO\x00\x82U%r\u01c2\xbe\xe5\xd2\x18\xf1\xef\x06J\x9d\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4>\x84\xb3\\[\"ePpa\xd3\vo\x12\xda\x03?\xe6\xf8\xb9\x89a\t=|,m8\x00\x00\u07d4>\x86A\xd4\x87E\xba2/_\xd6\xcbP\x12N\xc4f\x88\u01e6\x9a\u007f\xae\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4>\x91N0\x18\xac\x00D\x93A\u011d\xa7\x1d\x04\xdf\xee\xedb!\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4>\x94\x10\u04f9\xa8~\xd5\xe4Q\xa6\xb9\x1b\xb8\x92?\xe9\x0f\xb2\xb5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4>\x94\xdfS\x13\xfaR\x05p\xef#+\xc31\x1d_b/\xf1\x83\x89lk\x93[\x8b\xbd@\x00\x00\u0794>\x9b4\xa5\u007f3u\xaeY\xc0\xa7^\x19\u0136A\"\x8d\x97\x00\x88\xf8i\x93)g~\x00\x00\u07d4>\xad\xa8\xc9/V\x06~\x1b\xb7<\xe3x\xdaV\xdc,\xdf\xd3e\x89w\xcd\xe9:\xeb\rH\x00\x00\xe0\x94>\xaf\by\xb5\xb6\xdb\x15\x9bX\x9f\x84W\x8bjt\xf6\xc1\x03W\x8a\x01\x898\xb6q\xfae\xa2\x80\x00\u07d4>\xaf1k\x87a]\x88\xf7\xad\xc7|X\xe7\x12\xedMw\x96k\x89\x05m\xbcL\xee$d\x80\x00\u07d4>\xb8\xb3;!\xd2<\u0686\xd8(\x88\x84\xabG\x0e\x16F\x91\xb5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4>\xb9\xef\x06\xd0\xc2Y\x04\x03\x19\x94~\x8czh\x12\xaa\x02S\u0609\t\r\x97/22<\x00\x00\u07d4>\u030e\x16h\xdd\xe9\x95\xdcW\x0f\xe4\x14\xf4B\x11\xc54\xa6\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>\u03752\xe3\x97W\x96b\xb2\xa4aA\u73c25\x93j_\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4>\xeeo\x1e\x966\vv\x89\xb3\x06\x9a\xda\xf9\xaf\x8e\xb6\f\u404965\u026d\xc5\u07a0\x00\x00\xe0\x94?\b\u066d\x89O\x81>\x8e!H\xc1`\xd2K5:\x8et\xb0\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4?\f\x83\xaa\xc5qybsN\\\xea\xea\xec\u04db(\xad\x06\xbe\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94?\x10\x80\x02\x82\u0477\xdd\u01cf\xa9-\x820\aN\x1b\xf6\xae\xae\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4?\x123qO M\xe9\xdeN\xe9m\a;6\x8d\x81\x97\x98\x9f\x89\x02\x17\xc4\x10t\xe6\xbb\x00\x00\u07d4?\x17:\xa6\xed\xf4i\u0445\xe5\x9b\xd2j\xe4#k\x92\xb4\xd8\xe1\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4?\x1b\xc4 \xc5<\x00,\x9e\x90\x03|D\xfej\x8e\xf4\xdd\xc9b\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4?#a\b\xee\xc7\"\x89\xba\u00e6\\\u0483\xf9^\x04\x1d\x14L\x8964\xbf9\xab\x98x\x80\x00\u07d4?-\xa0\x93\xbb\x16\xeb\x06O\x8b\xfa\x9e0\xb9)\xd1_\x8e\x1cL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?-\xd5]\xb7\xea\xb0\xeb\xeee\xb3>\xd8 ,\x1e\x99.\x95\x8b\x89,s\xc97t,P\x00\x00\u07d4?/8\x14\x91y|\xc5\xc0\u0502\x96\xc1O\xd0\xcd\x00\xcd\xfa-\x89+\x95\xbd\xcc9\xb6\x10\x00\x00\u07d4?0\u04fc\x9f`\"2\xbcrB\x88\xcaF\xcd\v\a\x88\xf7\x15\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4?<\x8ea\xe5`L\xef\x06\x05\xd46\xdd\"\xac\u0346\"\x17\xfc\x89Hz\x9a0E9D\x00\x00\u07d4??F\xb7\\\xab\xe3{\xfa\u0307`(\x1fCA\xca\u007fF=\x89 \xacD\x825\xfa\xe8\x80\x00\u07d4?G)c\x19x\x83\xbb\xdaZ\x9b}\xfc\xb2-\xb1\x14@\xad1\x89\x1a\x19d<\xb1\xef\xf0\x80\x00\u07d4?L\xd19\x9f\x8a4\xed\u06da\x17\xa4q\xfc\x92+Xp\xaa\xfc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4?U\x1b\xa9<\xd5F\x93\xc1\x83\xfb\x9a\xd6\re\xe1`\x96s\u0249lk\x93[\x8b\xbd@\x00\x00\xe0\x94?bzv\x9ej\x95\x0e\xb8p\x17\xa7\u035c\xa2\bq\x13h1\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4?m\xd3e\x0e\xe4(\u0737u\x95S\xb0\x17\xa9j\x94(j\u0249Hz\x9a0E9D\x00\x00\u07d4?tr7\x80o\xed?\x82\x8ahR\xeb\bg\xf7\x90'\xaf\x89\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4?u\xaea\xcc\x1d\x80Be;[\xae\xc4D>\x05\x1c^z\xbd\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4?\xb7\u0457\xb3\xbaO\xe0E\xef\xc2=P\xa1E\x85\xf5X\u0672\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94?\xbc\x1eE\x18\xd74\x00\xc6\xd0F5\x949\xfbh\xea\x1aI\xf4\x8a\x03y\v\xb8U\x13v@\x00\x00\u07d4?\xbe\xd6\xe7\xe0\u029c\x84\xfb\xe9\xeb\u03ddN\xf9\xbbIB\x81e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?\u043bGy\x8c\xf4L\u07feM3=\xe67\xdfJ\x00\xe4\\\x89\x05lUy\xf7\"\x14\x00\x00\xe0\x94?\xe4\x0f\xbd\x91\x9a\xad(\x18\xdf\x01\xeeM\xf4lF\x84*\xc59\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4?\xe8\x01\xe6\x135\xc5\x14\r\xc7\xed\xa2\xefR\x04F\nP\x120\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?\xf86\xb6\xf5{\x90\x1bD\f0\xe4\xdb\xd0e\xcf7\xd3\u050c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4?\xfc\xb8p\xd4\x02=%]Qg\u0625\a\xce\xfc6kh\xba\x89#4^\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4@s\xfaI\xb8q\x17\u02d0\x8c\xf1\xabQ-\xa7T\xa92\xd4w\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4@\x8ai\xa4\a\x15\xe1\xb3\x13\xe15N`\b\x00\xa1\xe6\xdc\x02\xa5\x89\x01\u7e11\u0312T\x00\x00\u07d4@\x9b\xd7P\x85\x82\x1c\x1d\xe7\f\xdc;\x11\xff\xc3\xd9#\xc7@\x10\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4@\x9dZ\x96.\xde\uefa1x\x01\x8c\x0f8\xb9\u0372\x13\xf2\x89\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4@\xa31\x19[\x97s%\u00aa(\xfa/B\xcb%\xec<%<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4@\xa7\xf7(g\xa7\u0706w\v\x16+uW\xa44\xedP\xcc\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d4@\xab\n>\x83\xd0\u022c\x93f\x91\x05 \xea\xb1w+\xac;\x1a\x894\xf1\f-\xc0^|\x00\x00\u07d4@\xabf\xfe!>\xa5l:\xfb\x12\xc7[\xe3?\x8e2\xfd\b]\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94@\xadt\xbc\v\xce*E\xe5/6\xc3\u07bb\x1b:\xda\x1bv\x19\x8a\x01p\x16-\xe1\t\xc6X\x00\x00\u07d4@\u03c9\x05\x91\xea\u484f\x81*)T\xcb)_c3'\xe6\x89\x02\x9b\xf76\xfcY\x1a\x00\x00\u07d4@\u03d0\xef[v\x8c]\xa5\x85\x00,\xcb\xe6avP\xd8\xe87\x8963\x03\"\xd5#\x8c\x00\x00\xe0\x94@\xd4]\x9dv%\xd1QV\xc92\xb7q\xca{\x05'\x13\tX\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4@\xdb\x1b\xa5\x85\xce4S\x1e\xde\xc5IHI9\x13\x81\xe6\xcc\u04c9a\t=|,m8\x00\x00\xe0\x94@\xdfI^\xcf?\x8bL\xef*l\x18\x99W$\x8f\u813c+\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4@\xe0\xdb\xf3\xef\uf404\xea\x1c\xd7\xe5\x03\xf4\v;J\x84C\xf6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4@\xe2D\n\xe1B\u02006j\x12\xc6\xd4\x10/K\x844\xb6*\x8965\u026d\xc5\u07a0\x00\x00\u07d4@\xe3\u0083\xf7\xe2M\xe0A\f\x12\x1b\xee`\xa5`\u007f>)\xa6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94@\xeaPD\xb2\x04\xb20v\xb1\xa5\x80;\xf1\xd3\f\x0f\x88\x87\x1a\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94@\xed\xdbD\x8di\x0e\xd7.\x05\xc2%\xd3O\xc85\x0f\xa1\xe4\u014a\x01{x\x83\xc0i\x16`\x00\x00\xe0\x94@\xf4\xf4\xc0ls,\xd3[\x11\x9b\x89;\x12~}\x9d\aq\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4A\x01\x0f\u023a\xf8C}\x17\xa0Ci\x80\x9a\x16\x8a\x17\xcaV\xfb\x89\x05k\xc7^-c\x10\x00\x00\u07d4A\x03)\x96q\xd4gc\x97\x8f\xa4\xaa\x19\xee4\xb1\xfc\x95'\x84\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4A\x03<\x1bm\x05\xe1\u0289\xb0\x94\x8f\xc6DS\xfb\xe8z\xb2^\x89Hz\x9a0E9D\x00\x00\u07d4A\t\x8a\x81E#\x17\xc1\x9e>\xef\v\xd1#\xbb\xe1x\xe9\xe9\u0289\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4A\x16\x10\xb1x\xd5a}\xfa\xb94\u0493\xf5\x12\xa9>\\\x10\xe1\x89\t79SM(h\x00\x00\u07d4A\x1c\x83\x1c\xc6\xf4O\x19e\xecWW\xabN[<\xa4\xcf\xfd\x1f\x89\x17\n\x0fP@\xe5\x04\x00\x00\xe0\x94A*h\xf6\xc6EU\x9c\xc9w\xfcId\x04z \x1d\x1b\xb0\xe2\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4A?K\x02f\x9c\xcf\xf6\x80k\xc8&\xfc\xb7\xde\xca;\x0e\xa9\xbc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4AE\x99\t.\x87\x9a\xe2Sr\xa8MsZ\xf5\xc4\xe5\x10\xcdm\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4AHV\x12\xd04F\xecL\x05\xe5$NV?\x1c\xba\xe0\xf1\x97\x894\x95tD\xb8@\xe8\x00\x00\u07d4A]\tj\xb0b\x93\x18?<\x03=%\xf6\xcfqx\xac;\u01c9\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4Af\xfc\b\u0285\xf7f\xfd\xe81F\x0e\x9d\xc9<\x0e!\xaal\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Ag\x84\xaf`\x960\xb0p\u051a\x8b\xcd\x12#\\d(\xa4\b\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Ag\xcdH\xe73A\x8e\x8f\x99\xff\xd14\x12\x1cJJ\xb2x\u0109\xc5S%\xcat\x15\xe0\x00\x00\u07d4Al\x86\xb7 \x83\xd1\xf8\x90}\x84\xef\xd2\xd2\u05c3\xdf\xfa>\xfb\x89lj\xccg\u05f1\xd4\x00\x00\u07d4AsA\x9d\\\x9fc)U\x1d\xc4\xd3\xd0\u03ac\x1bp\x1b\x86\x9e\x89\x04\xc5>\xcd\xc1\x8a`\x00\x00\u07d4At\xfa\x1b\xc1*;q\x83\u02eb\xb7z\vYU{\xa5\xf1\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4Axj\x10\xd4G\xf4\x84\xd32D\u0337\xfa\u034bB{[\x8c\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Az<\u0454\x96S\nmB\x04\u00f5\xa1|\xe0\xf2\a\xb1\xa5\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4A~N&\x88\xb1\xfdf\xd8!R\x9eF\xedOB\xf8\xb3\xdb=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94A\x9aq\xa3l\x11\xd1\x05\xe0\xf2\xae\xf5\xa3\xe5\x98\a\x8e\x85\xc8\v\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94A\x9b\xdes\x16\xcc\x1e\u0495\u0205\xac\xe3B\u01db\xf7\xee3\xea\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4A\xa2\xf2\xe6\xec\xb8c\x94\xec\x0e3\x8c\x0f\xc9~\x9cU\x83\xde\u0489l\xee\x06\u077e\x15\xec\x00\x00\u07d4A\xa8\u0083\x00\x81\xb1\x02\xdfn\x011e|\a\xabc[T\u0389lj\xccg\u05f1\xd4\x00\x00\u07d4A\xa8\xe26\xa3\x0emc\xc1\xffdM\x13*\xa2\\\x89S~\x01\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4A\xa9\xa4\x04\xfc\x9f[\xfe\xe4\x8e\xc2e\xb1%#3\x8e)\xa8\xbf\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4A\xad6\x9fu\x8f\xef8\xa1\x9a\xa3\x14\x93y\x83,\x81\x8e\xf2\xa0\x8966\x9e\xd7t}&\x00\x00\u07d4A\xb2\xd3O\xde\v\x10)&+Ar\xc8\x1c\x15\x90@[\x03\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4A\xb2\xdb\u05dd\u069b\x86Ojp0'T\x19\u00dd>\xfd;\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4A\xc3\xc26u4\xd1;\xa2\xb3?\x18\\\xdb\xe6\xacC\xc2\xfa1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4A\u02d8\x96D_p\xa1\n\x14!R\x96\xda\xf6\x14\xe3,\xf4\u0549g\x8a\x93 b\xe4\x18\x00\x00\u07d4A\xcey\x95\t5\xcf\xf5[\xf7\x8eL\xce\xc2\xfec\x17\x85\u06d5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4A\u04f71\xa3&\xe7hX\xba\xa5\xf4\xbd\x89\xb5{6\x93#C\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\xe0\x94A\xe4\xa2\x02u\xe3\x9b\xdc\xef\xebe\\\x03\"tKvQ@\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4A\xed-\x8ep\x81H,\x91\x9f\xc2=\x8f\x00\x91\xb3\xc8,F\x85\x89F:\x1ev[\u05ca\x00\x00\xe0\x94A\xf2~tK\u049d\xe2\xb0Y\x8f\x02\xa0\xbb\x9f\x98\xe6\x81\ua90a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\u07d4A\xf4\x89\xa1\xect{\u009c>_\x9d\x8d\xb9xw\xd4\u0474\xe9\x89\a?u\u0460\x85\xba\x00\x00\u07d4B\x0f\xb8n}+Q@\x1f\xc5\xe8\xc7 \x15\xde\xcbN\xf8\xfc.\x8965\u026d\xc5\u07a0\x00\x00\u07d4B\x16\x84\xba\xa9\xc0\xb4\xb5\xf5S8\xe6\xf6\xe7\xc8\xe1F\xd4\x1c\xb7\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4B9\x96Y\xac\xa6\xa5\xa8c\xea\"E\xc93\xfe\x9a5\xb7\x88\x0e\x89n\xce2\xc2l\x82p\x00\x00\xe0\x94B;\xcaG\xab\xc0\fpW\xe3\xad4\xfc\xa6>7_\xbd\x8bJ\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4B<1\a\xf4\xba\xceANI\x9cd9\nQ\xf7F\x15\xca^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B<\xc4YL\xf4\xab\xb66\x8d\xe5\x9f\u04b1#\a4a!C\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94BD\xf13\x11X\xb9\xce&\xbb\xe0\xb9#k\x92\x03\xca5\x144\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794BQw\xebt\xad\n\x9d\x9aWR\"\x81G\xeemcV\xa6\u6239\x8b\xc8)\xa6\xf9\x00\x00\u07d4BW%\xc0\xf0\x8f\b\x11\xf5\xf0\x06\xee\xc9\x1c\\\\\x12k\x12\xae\x89\b!\xab\rD\x14\x98\x00\x00\xe0\x94BX\xfdf/\xc4\xce2\x95\xf0\xd4\xed\x8f{\xb1D\x96\x00\xa0\xa9\x8a\x01lE.\xd6\b\x8a\xd8\x00\x00\xe0\x94B\\\x18\x16\x86\x8fww\xcc+\xa6\xc6\u048c\x9e\x1eylR\xb3\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4B\\3\x8a\x13%\xe3\xa1W\x8e\xfa)\x9eW\u0646\xebGO\x81\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94BbY\xb0\xa7Vp\x1a\x8bf5(R!V\xc0(\x8f\x0f$\x8a\x02\x18\xae\x19k\x8dO0\x00\x00\u07d4Bm\x15\xf4\a\xa0\x115\xb1:kr\xf8\xf2R\v51\xe3\x02\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Box\xf7\r\xb2Y\xac\x854\x14[)4\xf4\xef\x10\x98\xb5\u0609\x13\x84\x00\xec\xa3d\xa0\x00\x00\u07d4Bs-\x8e\xf4\x9f\xfd\xa0K\x19x\x0f\xd3\xc1\x84i\xfb7A\x06\x89\x17\v\x00\xe5\u4a7e\x00\x00\u07d4Bt\x17\xbd\x16\xb1\xb3\xd2-\xbb\x90-\x8f\x96W\x01o$\xa6\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Btj\xee\xa1O'\xbe\xff\f\r\xa6BS\xf1\xe7\x97\x18\x90\xa0\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4B{F*\xb8NP\x91\xf4\x8aF\xeb\f\u0712\xdd\xcb&\xe0x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B~GQ\u00fa\xbex\xcf\xf8\x83\b\x86\xfe\xbc\x10\xf9\x90\x8dt\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94B~\xc6h\xac\x94\x04\xe8\x95\u0306\x15\x11\xd1b\nI\x12\xbe\x98\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4B\x80\xa5\x8f\x8b\xb1\v\x94@\u0794\xf4+OY! \x82\x01\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B\x8a\x1e\xe0\xed3\x1dyR\u033e\x1cyt\xb2\x85+\u0453\x8a\x89w\xb7JN\x8d\xe5e\x00\x00\u0794B\x9c\x06\xb4\x87\xe8Tj\xbd\xfc\x95\x8a%\xa3\xf0\xfb\xa5?o\x00\x88\xbbdJ\xf5B\x19\x80\x00\xe0\x94B\xa9\x8b\xf1`'\xceX\x9cN\xd2\xc9X1\xe2rB\x05\x06N\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4B\xc6\xed\xc5\x15\xd3UW\x80\x8d\x13\xcdD\xdc\xc4@\v%\x04\xe4\x89\n\xba\x14\u015b\xa72\x00\x00\u07d4B\xce\xcf\u0492\x10y\xc2\xd7\xdf?\b\xb0z\xa3\xbe\xee^!\x9a\x8965\u026d\xc5\u07a0\x00\x00\u07d4B\u04669\x9b0\x16\xa8Y\u007f\x8bd\t'\xb8\xaf\xbc\xe4\xb2\x15\x89\xa1\x8b\xce\xc3H\x88\x10\x00\x00\u07d4B\xd3I@\xed\xd2\xe7\x00]F\xe2\x18\x8eL\xfe\u0383\x11\xd7M\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4B\u04e5\xa9\x01\xf2\xf6\xbd\x93V\xf1\x12\xa7\x01\x80\xe5\xa1U\v`\x892$\xf4'#\xd4T\x00\x00\u07d4B\u05b2c\xd9\xe9\xf4\x11lA\x14$\xfc\x99Ux;\xa1\xc5\x1b\x81\x0f\xc4g\u057aM\xeaB\xf7\xa9\x88^i\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94C>\xb9J3\x90\x86\xed\x12\u067d\xe9\xcd\x1dE\x86\x03\xc9}\u058a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4CI\"Zb\xf7\n\xeaH\n\x02\x99\x15\xa0\x1eSy\xe6O\xa5\x89\x8c\xd6~#4\xc0\xd8\x00\x00\u07d4CT\"\x1eb\xdc\t\xe6@d6\x16:\x18^\xf0m\x11J\x81\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94CTC\xb8\x1d\xfd\xb9\xbd\x8cg\x87\xbc%\x18\xe2\xd4~W\xc1_\x8a\x01C\x8d\x93\x97\x88\x1e\xf2\x00\x00\u07d4Ca\u0504o\xaf\xb3w\xb6\xc0\xeeI\xa5\x96\xa7\x8d\xdf5\x16\xa3\x89\xc2\x12z\xf8X\xdap\x00\x00\xe0\x94Cd0\x9a\x9f\xa0p\x95`\x0fy\xed\xc6Q \xcd\xcd#\xdcd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Cg\xaeK\f\xe9d\xf4\xa5J\xfdK\\6\x84\x96\xdb\x16\x9e\x9a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Ct\x89(\xe8\xc3\xecD6\xa1\u0412\xfb\xe4:\xc7I\xbe\x12Q\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4Cv{\xf7\xfd*\xf9[r\xe91-\xa9D<\xb1h\x8eCC\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94Cy\x838\x8a\xb5\x9aO\xfc!_\x8e\x82iF\x10)\xc3\xf1\xc1\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4C\x89\x8cI\xa3MP\x9b\xfe\xd4\xf7`A\xee\x91\xca\xf3\xaaj\xa5\x89\x10CV\x1a\x88)0\x00\x00\u07d4C\x8c/T\xff\x8eb\x9b\xab6\xb1D+v\v\x12\xa8\x8f\x02\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\x98b\x8e\xa6c-9>\x92\x9c\xbd\x92\x84d\xc5h\xaaJ\f\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4C\x9d//Q\x10\xa4\u054b\x17W\x93P\x15@\x87@\xfe\xc7\xf8\x89\u03e5\xc5\x15\x0fL\x88\x80\x00\u07d4C\x9d\xee?vy\xff\x100s?\x93@\xc0\x96hkI9\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xb0y\xba\xf0ry\x99\xe6k\xf7C\u057c\xbfwl;\t\"\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xbc-M\xdc\xd6X;\xe2\u01fc\tK(\xfbr\xe6+\xa8;\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xc7\xeb\u0173\xe7\xaf\x16\xf4}\xc5az\xb1\x0e\x0f9\xb4\xaf\xbb\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4C\u02d6R\x81\x8coMg\x96\xb0\xe8\x94\t0ly\xdbcI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xcc\b\xd0s*\xa5\x8a\xde\xf7a\x9b\xedFU\x8a\xd7wAs\x89\xf0\xe7\u0730\x12*\x8f\x00\x00\xe0\x94C\u0567\x1c\xe8\xb8\xf8\xae\x02\xb2\xea\xf8\xea\xf2\xca(@\xb9?\xb6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794C\xdb\u007f\xf9Z\bm(\ubff8/\xb8\xfb_#\n^\xbc\u0348\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4C\xe7\xec\x84cX\xd7\xd0\xf97\xad\x1c5\v\xa0i\u05ffr\xbf\x89\x06p\xaeb\x92\x14h\x00\x00\u07d4C\xf1o\x1eu\xc3\xc0j\x94x\xe8\u0157\xa4\n<\xb0\xbf\x04\u0309\x9d\xf7\u07e8\xf7`H\x00\x00\u07d4C\xf4p\xede\x9e)\x91\xc3u\x95~]\xde\u017d\x1d8\"1\x89\x05k\xc7^-c\x10\x00\x00\u07d4C\xf7\xe8n8\x1e\xc5\x1e\u0110m\x14v\u02e9z=\xb5\x84\xe4\x8965\u026d\xc5\u07a0\x00\x00\u07d4C\xff8t>\xd0\xcdC0\x8c\x06e\t\u030e~r\xc8b\xaa\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94C\xff\x88S\xe9\x8e\xd8@k\x95\x00\n\u0684\x83b\u05a09*\x8a\x04\xae\v\x1cM.\x84\xd0\x00\x00\u07d4D\t\x88f\xa6\x9bh\xc0\xb6\xbc\x16\x82)\xb9`5\x87\x05\x89g\x89\n1\x06+\xee\xedp\x00\x00\u07d4D\x19\xaca\x8d]\xea|\xdc`w o\xb0}\xbd\xd7\x1c\x17\x02\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4D\x1aR\x00\x16a\xfa\xc7\x18\xb2\u05f3Q\xb7\xc6\xfbR\x1az\xfd\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94D\x1a\u0282c\x13$\xac\xbf\xa2F\x8b\xda2[\xbdxG{\xbf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4D\x1f7\xe8\xa0)\xfd\x02H/(\x9cI\xb5\xd0m\x00\xe4\b\xa4\x89\x12\x11\xec\xb5m\x13H\x80\x00\u07d4D \xaa5F[\xe6\x17\xad$\x98\xf3p\xde\n<\xc4\xd20\xaf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4D#/\xf6m\xda\xd1\xfd\x84\x12f8\x006\xaf\xd7\xcf}\u007fB\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4D%\rGn\x06$\x84\xe9\b\n9g\xbf:Js*\xd7?\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4D)\xa2\x9f\xee\x19\x84Pg,\f\x1d\a1b%\v\xecdt\x896*\xaf\x82\x02\xf2P\x00\x00\u07d4D5RS\xb2wH\xe3\xf3O\xe9\xca\xe1\xfbq\x8c\x8f$\x95)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4D8\xe8\x80\xcb'f\xb0\xc1\u03ae\xc9\xd2A\x8f\u03b9R\xa0D\x89\a?\xa0s\x90?\b\x00\x00\u07d4DL\xafy\xb7\x138\ue6a7\xc73\xb0*\u02a7\xdc\x02YH\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4D\\\xb8\xde^=\xf5 \xb4\x99\xef\u0240\xf5+\xff@\xf5\\v\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Dj\x809\xce\u03dd\xceHy\xcb\xca\xf3I;\xf5E\xa8\x86\x10\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4Dt)\x9d\x0e\xe0\x90\u0710x\x9a\x14\x86H\x9c=\rd^m\x8965\u026d\xc5\u07a0\x00\x00\u07d4D\x8b\xf4\x10\xad\x9b\xbc/\xec\xc4P\x8d\x87\xa7\xfc.K\x85a\xad\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4D\x90\x1e\r\x0e\b\xac=^\x95\xb8\xec\x9d^\x0f\xf5\xf1.\x03\x93\x89\x16\xa1\xf9\xf5\xfd}\x96\x00\x00\xe0\x94D\x93\x12<\x02\x1e\xce;3\xb1\xa4R\xc9&\x8d\xe1@\a\xf9\u04ca\x01je\x02\xf1Z\x1eT\x00\x00\xe0\x94D\x9a\xc4\xfb\xe3\x83\xe3g8\x85^6JW\xf4q\xb2\xbf\xa11\x8a)\xb7d2\xb9DQ \x00\x00\u07d4D\xa0\x1f\xb0J\xc0\xdb,\xce]\xbe(\x1e\x1cF\xe2\x8b9\xd8x\x89lj\xccg\u05f1\xd4\x00\x00\u07d4D\xa6=\x18BE\x87\xb9\xb3\a\xbf\xc3\xc3d\xae\x10\xcd\x04\xc7\x13\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94D\xa8\x98\x9e20\x81!\xf7$f\x97\x8d\xb3\x95\xd1\xf7l:K\x8a\x01\x88P)\x9fB\xb0j\x00\x00\u07d4D\xc1\x11\v\x18\x87\x0e\xc8\x11x\xd9=!X8\xc5Q\u050ed\x89\n\xd6\xf9\x85\x93\xbd\x8f\x00\x00\u07d4D\xc1Ge\x12|\xde\x11\xfa\xb4l],\xf4\u0532\x89\x00#\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94D\xc5N\xaa\x8a\xc9@\xf9\xe8\x0f\x1et\xe8/\xc1O\x16v\x85j\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4D\xcdwSZ\x89?\xa7\xc4\xd5\xeb:$\x0ey\u0419\xa7--\x89,s\xc97t,P\x00\x00\u07d4D\u07faP\xb8)\xbe\xcc_O\x14\u0470J\xab3 \xa2\x95\xe5\x8965\u026d\xc5\u07a0\x00\x00\u07d4D\xe2\xfd\xc6y\xe6\xbe\xe0\x1e\x93\xefJ:\xb1\xbc\xce\x01*\xbc|\x89\x16=\x19I\x00\xc5E\x80\x00\xe0\x94D\xf6/*\xaa\xbc)\xad:k\x04\xe1\xffo\x9c\xe4R\xd1\xc1@\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4D\xff\xf3{\xe0\x1a8\x88\u04f8\xb8\u1200\xa7\xdd\xef\xee\xea\u04c9\x0e\f[\xfc}\xae\x9a\x80\x00\u07d4E\x06\xfe\x19\xfaK\x00k\xaa9\x84R\x9d\x85\x16\xdb++P\xab\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\x1b6\x99G[\xed]y\x05\xf8\x90Z\xa3Eo\x1e\u05c8\xfc\x89\x8a\xc7#\x04\x89\xe8\x00\x00\x00\u0794E\x1bpp%\x9b\u06e2q\x00\xe3n#B\x8aS\xdf\xe3\x04\u9239\x8b\xc8)\xa6\xf9\x00\x00\u07d4E'+\x8fb\xe9\xf9\xfa\x8c\xe0D \u1ba3\xeb\xa9hn\xac\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94E+d\u06ce\xf7\xd6\u07c7\u01c8c\x9c\"\x90\xbe\x84\x82\xd5u\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4E>5\x9a3\x97\x94LZ'Z\xb1\xa2\xf7\n^Z?i\x89\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4EI\xb1Yy%_~e\xe9\x9b\rV\x04\u06d8\xdf\xca\u023f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4EKa\xb3D\xc0\xef\x96Qy#\x81U\xf2w\u00c2\x9d\v8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94EO\x01A\xd7!\xd3<\xbd\xc4\x10\x18\xbd\x01\x11\x9a\xa4xH\x18\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4ES3\x90\xe3@\xfe\r\xe3\xb3\xcf_\xb9\xfc\x8e\xa5R\xe2\x9eb\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4ES\x96\xa4\xbb\u067a\u8bdf\xb7\xc4\xd6MG\x1d\xb9\xc2E\x05\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4E[\x92\x96\x92\x1at\xd1\xfcAa\u007fC\xb80>o>\xd7l\x89\u3bb5sr@\xa0\x00\x00\u07d4E\\\xb8\xee9\xff\xbcu#1\xe5\xae\xfcX\x8e\xf0\xeeY4T\x8965F:x\r\xef\x80\x00\u07d4Ej\u0b24\x8e\xbc\xfa\xe1f\x06\x02PR_c\x96^v\x0f\x89\x10CV\x1a\x88)0\x00\x00\u07d4Eo\x8dtf\x82\xb2$g\x93I\x06M\x1b6\x8c|\x05\xb1v\x89\u0213\u041c\x8fQP\x00\x00\u07d4Ep)\xc4i\xc4T\x8d\x16\x8c\xec>e\x87.D(\xd4+g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Eq\xdeg+\x99\x04\xba\xd8t6\x92\xc2\x1cO\xdc\xeaL.\x01\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Ex\x1b\xbew\x14\xa1\xc8\xf7;\x1cty!\xdfO\x84'\x8bp\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E{\xce\xf3}\xd3\xd6\v-\xd0\x19\xe3\xfea\xd4k?\x1erR\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94E\x8e<\u025e\x94xD\xa1\x8ejB\x91\x8f\xef~\u007f_^\xb3\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\u07d4E\x93\x93\xd6:\x06>\xf3r\x1e\x16\xbd\x9f\xdeE\ue77dw\xfb\x89j\xba\u05a3\xc1S\x05\x00\x00\u07d4E\xa5p\xdc\xc2\t\f\x86\xa6\xb3\xea)\xa6\bc\xdd\xe4\x1f\x13\xb5\x89\f\x9a\x95\xee)\x86R\x00\x00\u07d4E\xa8 \xa0g/\x17\xdct\xa0\x81\x12\xbcd?\xd1\x16w6\u00c9\n\xd6\xc4;(\x15\xed\x80\x00\u07d4E\xb4q\x05\xfeB\xc4q-\xcen*!\xc0[\xff\xd5\xeaG\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\xbb\x82\x96R\u063f\xb5\x8b\x85'\xf0\xec\xb6!\u009e!.\u00c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\xc0\u045f\v\x8e\x05O\x9e\x8986\xd5\xec\xaey\x01\xaf(\x12\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4E\xc4\xec\xb4\xee\x89\x1e\xa9\x84\xa7\xc5\xce\xfd\x8d\xfb\x001\v(P\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4E\u028d\x95f\b\xf9\xe0\n/\x99t\x02\x86@\x88\x84ef\x8f\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\u0298b\x00;N@\xa3\x17\x1f\xb5\xca\xfa\x90(\xca\xc8\xde\x19\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4E\xd1\xc9\xee\xdf|\xabA\xa7y\x05{y9_T(\xd8\x05(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\u0535M7\xa8\xcfY\x98!#_\x06/\xa9\xd1p\xed\u8909\x11\x90g;_\u0690\x00\x00\xe0\x94E\xdb\x03\xbc\xcf\u05a5\xf4\xd0&k\x82\xa2*6\x87\x92\xc7}\x83\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4E\xe3\xa9>r\x14J\u0686\f\xbcV\xff\x85\x14Z\xda8\xc6\u0689WG=\x05\u06ba\xe8\x00\x00\u07d4E\u6378\u06fa\xba_\xc2\xcb3|b\xbc\xd0\xd6\x1b\x05\x91\x89\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\u6379L}\n\xb7\xacA\x85zq\xd6qG\x87\x0fNq\x8aT\xb4\v\x1f\x85+\xda\x00\x00\x00\u07d4E\xf4\xfc`\xf0\x8e\xac\xa1\x05\x98\xf03c)\x80\x1e<\x92\xcbF\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4F\rSU\xb2\xce\xebnb\x10}\x81\xe5\x12p\xb2k\xf4V \x89l\xb7\xe7Hg\xd5\xe6\x00\x00\xe0\x94F\"O2\xf4\xec\xe5\u0206p\x90\xd4@\x9dU\xe5\v\x18C-\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4F'\xc6\x06\x84&q\xab\u0782\x95\xee]\xd9L\u007fT\x954\xf4\x89\x0f\x89_\xbd\x872\xf4\x00\x00\u07d4F+g\x8bQ\xb5\x84\xf3\xedz\xda\a\v\\\u065c\v\xf7\xb8\u007f\x89\x05k\xc7^-c\x10\x00\x00\u07d4FM\x9c\x89\xcc\xe4\x84\xdf\x00\x02w\x19\x8e\xd8\a_\xa65r\u0449\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4FPNj!Z\xc8;\xcc\xf9V\xbe\xfc\x82\xabZg\x93q\u0209\x1c!(\x05\u00b4\xa5\x00\x00\xe0\x94FQ\xdcB\x0e\b\xc3);'\xd2Ix\x90\xebP\":\xe2\xf4\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4FS\x1e\x8b\x1b\xde\t\u007f\u07c4\x9dm\x11\x98\x85`\x8a\x00\x8d\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4Fb\x92\xf0\xe8\rC\xa7\x87t'u\x90\xa9\xebE\x96\x12\x14\xf4\x894\x95tD\xb8@\xe8\x00\x00\xe0\x94Fb\xa1v^\xe9!\x84-\u0708\x89\x8d\x1d\xc8bu\x97\xbd~\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Fe\xe4s\x96\xc7\u06d7\xeb*\x03\xd9\bc\xd5\u053a1\x9a\x94\x89 \x86\xac5\x10R`\x00\x00\u07d4Fo\xdak\x9bX\xc5S'P0j\x10\xa2\xa8\xc7h\x10;\a\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4Fq$\xae\u007fE/&\xb3\xd5t\xf6\b\x88\x94\xfa]\x1c\xfb;\x89\x92^\x06\xee\xc9r\xb0\x00\x00\u0794Fr*6\xa0\x1e\x84\x1d\x03\xf7\x80\x93^\x91}\x85\u0566z\xbd\x88\xce\xc7o\x0eqR\x00\x00\u07d4Fw\x9aVV\xff\x00\xd7>\xac:\xd0\u00cbl\x850\x94\xfb@\x89\f\x82S\xc9lj\xf0\x00\x00\u07d4Fw\xb0N\x03C\xa3!1\xfdj\xbb9\xb1\xb6\x15k\xba=[\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4F}Y\x88$\x9ahaG\x16e\x98@\xed\n\xe6\xf6\xf4W\xbc\x89\x15\x01\xa4\x8c\xef\xdf\xde\x00\x00\u07d4F~\x0e\xd5O;v\xae\x066\x17n\aB\b\x15\xa0!sn\x89lk\x93[\x8b\xbd@\x00\x00\u07d4F~\xa1\x04E\x82~\xf1\xe5\x02\xda\xf7k\x92\x8a \x9e\r@2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94F\u007f\xbfAD\x16\x00u\u007f\xe1X0\xc8\xcd_O\xfb\xbb\xd5`\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94F\x93Xp\x932\xc8+\x88~ \xbc\xdd\xd0\"\x0f\x8e\u06e7\u040a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4F\x97\xba\xaf\x9c\xcb`?\xd3\x040h\x9dCTE\xe9\u024b\xf5\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4F\xa3\v\x8a\x80\x891!tE\xc3\xf5\xa9>\x88,\x03E\xb4&\x89\r\x8d\xb5\xeb\u05f2c\x80\x00\u07d4F\xa40\xa2\u0528\x94\xa0\u062a?\xea\xc6\x156\x14\x15\xc3\xf8\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4F\xaaP\x18pg~\u007f\nPHv\xb4\xe8\x80\x1a\n\xd0\x1cF\x89+^:\xf1k\x18\x80\x00\x00\u07d4F\xbf\u0172\a\xeb \x13\xe2\xe6\x0fw_\xec\xd7\x18\x10\u0159\f\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4F\xc1\xaa\"D\xb9\u0229W\u028f\xacC\x1b\x05\x95\xa3\xb8h$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4F\xd8\x061(B\x03\xf6(\x8e\xcdNWX\xbb\x9dA\xd0]\xbe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\n\xc5\xd1\xf3\xef\xe2\x8f8\x02\xaf\x92[W\x1ec\x86\x8b9}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\x10\x10\xdaI/@\x18\x83;\b\x8d\x98r\x90\x1e\x06\x12\x91t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4G\x12T\x02e\xcb\xee\u00c4p\"\u015f\x1b1\x8dC@\n\x9e\x89\xbd\xbcA\xe04\x8b0\x00\x00\xe0\x94G\x14\u03e4\xf4k\u05bdps}u\x87\x81\x97\xe0\x8f\x88\xe61\x8a\x02\u007f>\u07f3Nn@\x00\x00\u07d4G H\xcc`\x9a\xeb$!e\uaa87\x05\x85\f\xf3\x12]\xe0\x8965\u026d\xc5\u07a0\x00\x00\u07d4G!\x92)\xe8\xcdVe\x9ae\u00a9C\xe2\u075a\x8fK\xfd\x89\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4G7\xd0B\xdcj\xe7>\xc7:\xe2Qz\u03a2\xfd\xd9d\x87\u014965\u026d\xc5\u07a0\x00\x00\u07d4GAX\xa1\xa9\xdci<\x13?e\xe4{\\:\xe2\xf7s\xa8o\x89\n\xdaUGK\x814\x00\x00\u07d4GE\xab\x18\x1a6\xaa\x8c\xbf\"\x89\xd0\xc4Qe\xbc~\xbe#\x81\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4GPf\xf9\xad&eQ\x96\xd5SS'\xbb\xeb\x9by)\xcb\x04\x89\xa4\xccy\x95c\u00c0\x00\x00\xe0\x94GR!\x8eT\xdeB?\x86\xc0P\x193\x91z\xea\b\xc8\xfe\u054a\x04<3\xc1\x93ud\x80\x00\x00\u07d4GZa\x93W-JNY\u05fe\t\u02d6\r\u074cS\x0e/\x89$,\xf7\x8c\xdf\a\xff\x80\x00\u07d4Gd\x8b\xed\x01\xf3\xcd2I\bNc]\x14\u06a9\xe7\xec<\x8a\x89\n\x84Jt$\xd9\xc8\x00\x00\u07d4Gh\x84\x10\xff%\xd6T\xd7.\xb2\xbc\x06\xe4\xad$\xf83\xb0\x94\x89\b\xb2\x8da\xf3\u04ec\x00\x00\u07d4GkU\x99\b\x9a?\xb6\xf2\x9clr\xe4\x9b.G@\ua00d\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4Gs\x0f_\x8e\xbf\x89\xacr\xef\x80\xe4l\x12\x19P8\xec\xdcI\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94G{$\xee\u80deO\u045d\x12P\xbd\vfEyJa\u028a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4G\x81\xa1\nM\xf5\uef02\xf4\xcf\xe1\a\xba\x1d\x8av@\xbdf\x89a\t=|,m8\x00\x00\u07d4G\x88Z\xba\xbe\xdfM\x92\x8e\x1c\x88\x83\xa6a\x9cl(\x11\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94G\xe2]\xf8\x82%8\xa8Yk(\xc67\x89kM\x14<5\x1d\x8a\x11\v\xe9\xeb$\xb8\x81P\x00\x00\u07d4G\xf4ik\xd4b\xb2\r\xa0\x9f\xb8>\xd2\x03\x98\x18\xd7v%\xb3\x89\b\x13\xcaV\x90m4\x00\x00\u07d4G\xfe\xf5\x85\x84FRH\xa0\x81\r`F>\xe9>Zn\xe8\u04c9\x0fX\xcd>\x12i\x16\x00\x00\u07d4G\xffo\xebC! `\xbb\x15\x03\u05e3\x97\xfc\b\xf4\xe7\x03R\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\xff\xf4,g\x85Q\xd1A\xebu\xa6\xee9\x81\x17\xdf>J\x8d\x89\x05k\xea\xe5\x1f\xd2\xd1\x00\x00\u07d4H\x01\x0e\xf3\xb8\xe9^?0\x8f0\xa8\xcb\u007fN\xb4\xbf`\xd9e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4H\n\xf5 v\x00\x9c\xa77\x81\xb7\x0eC\xb9Y\x16\xa6\"\x03\xab\x892\x19r\xf4\b=\x87\x80\x00\u07d4H\x0f1\xb9\x891\x1eA$\u01a7F_ZD\tM6\xf9\u04097\x90\xbb\x85Q7d\x00\x00\xe0\x94H\x11\x15)j\xb7\xdbRI/\xf7\xb6G\xd63)\xfb\\\xbck\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4H\x1e:\x91\xbf\xdc/\x1c\x84(\xa0\x11\x9d\x03\xa4\x16\x01A~\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4H(\xe4\xcb\xe3N\x15\x10\xaf\xb7,+\ueb0aE\x13\xea\xeb\u0649\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94H)\x82\xac\x1f\x1cm\x17!\xfe\xec\u0679\xc9l\xd9I\x80PU\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4H0,1\x1e\xf8\xe5\xdcfAX\xddX<\x81\x19Mn\rX\x89\xb6gl\xe0\xbc\xcb\\\x00\x00\u07d4H;\xa9\x904\xe9\x00\xe3\xae\xdfaI\x9d;+\xce9\xbe\xb7\xaa\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4HT\x8bK\xa6+\xcb/\r4\xa8\x8d\u019ah\x0eS\x9c\xf0F\x89\x05l\xf1\u02fbt2\x00\x00\u07d4Hc\x84\x979&Zc\xb0\xa2\xbf#jY\x13\xe6\xf9Y\xce\x15\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4He\x9d\x8f\x8c\x9a/\xd4Oh\u06a5]#\xa6\b\xfb\xe5\x00\u0709lk\x93[\x8b\xbd@\x00\x00\xe0\x94Hf\x9e\xb5\xa8\x01\u0637_\xb6\xaaX\xc3E\x1bpX\xc2C\xbf\x8a\x06\x8dB\xc18\u06b9\xf0\x00\x00\u07d4Hjl\x85\x83\xa8D\x84\xe3\xdfC\xa1#\x83\u007f\x8c~#\x17\u0409\x11\x87\xc5q\xab\x80E\x00\x00\u07d4Hz\xdf}p\xa6t\x0f\x8dQ\xcb\xddh\xbb?\x91\u0125\xceh\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4H~\x10\x85\x02\xb0\xb1\x89\uf70cm\xa4\xd0\xdbba\xee\xc6\xc0\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94H\x88\xfb%\xcdP\u06f9\xe0H\xf4\x1c\xa4}x\xb7\x8a'\xc7\u064a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u0794H\x934\u00b6\x95\xc8\xee\a\x94\xbd\x86B\x17\xfb\x9f\xd8\xf8\xb15\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4H\xa3\r\xe1\xc9\x19\xd3\xfd1\x80\xe9}_+*\x9d\xbd\x96M-\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4H\xbf\x14\u05f1\xfc\x84\xeb\xf3\xc9k\xe1/{\xce\x01\xaai\xb0>\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4H\xc2\ue465\aV\xd8\u039a\xbe\xebu\x89\xd2,o\xee]\xfb\x89\xae\x8ez\v\xb5u\xd0\x00\x00\u07d4H\xc5\u0197\v\x91a\xbb\x1c{z\xdf\xed\x9c\xde\u078a\x1b\xa8d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94H\xd2CKz}\xbb\xff\b\";c\x87\xb0]\xa2\xe5\t1&\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4H\xd4\xf2F\x8f\x96?\u05da\x00a\x98\xbbg\x89]-Z\xa4\u04c9K\xe4\xe7&{j\xe0\x00\x00\u07d4H\xe0\xcb\xd6\u007f\x18\xac\xdbzb\x91\xe1%M\xb3.\trs\u007f\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4H\xf6\n5HO\xe7y+\u030a{c\x93\xd0\u0761\xf6\xb7\x17\x89\xc3(\t>a\xee@\x00\x00\u07d4H\xf8\x83\xe5g\xb46\xa2{\xb5\xa3\x12M\xbc\x84\xde\xc7u\xa8\x00\x89)\xd7n\x86\x9d\u0340\x00\x00\xe0\x94I\x01E\xaf\xa8\xb5E\"\xbb!\xf3R\xf0m\xa5\xa7\x88\xfa\x8f\x1d\x8a\x01\xf4lb\x90\x1a\x03\xfb\x00\x00\u07d4I\t\xb3\x19\x98\xea\xd4\x14\xb8\xfb\x0e\x84k\xd5\xcb\xde995\xbe\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4I\x12\xd9\x02\x93\x16v\xff9\xfc4\xfe<<\xc8\xfb!\x82\xfaz\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4I\x13o\xe6\xe2\x8btS\xfc\xb1kk\xbb\u9aac\xba\x837\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94I\x15a\u06cbo\xaf\xb9\x00~b\xd0P\u0082\xe9,Kk\u020a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4I\x18]\xd7\xc262\xf4lu\x94s\ubb96`\b\xcd5\x98\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4I,\xb5\xf8a\xb1\x87\xf9\xdf!\xcdD\x85\xbe\xd9\vP\xff\xe2-\x89\x1b\x19\xe5\vD\x97|\x00\x00\u07d4I-\xe4j\xaf\x8f\x1dp\x8dY\u05da\xf1\xd0:\xd2\xcb`\x90/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I.p\xf0M\x18@\x8c\xb4\x1e%`70Pk5\xa2\x87k\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4I:g\xfe#\xde\xccc\xb1\r\xdau\xf3(v\x95\xa8\x1b\u056b\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4I=H\xbd\xa0\x15\xa9\xbf\xcf\x16\x03\x93n\xabh\x02L\xe5Q\xe0\x89\x018\xa3\x88\xa4<\x00\x00\x00\xe0\x94IBV\xe9\x9b\x0f\x9c\xd6\xe5\xeb\xca8\x99\x862R\x90\x01e\u020a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4IM\xecM^\xe8\x8a'q\xa8\x15\xf1\xeerd\x94/\xb5\x8b(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I[d\x1b\x1c\u07a3b\u00f4\u02fd\x0f\\\xc5\v\x1e\x17k\x9c\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Ih\xa2\xce\xdbEuU\xa19)Z\xea(wnT\x00<\x87\x8a\x02#\x1a\xef\u0266b\x8f\x00\x00\u07d4Im6U4S\n_\xc1W|\nRA\u02c8\xc4\xdapr\x89a\t=|,m8\x00\x00\xe0\x94In1\x95\x92\xb3A\xea\xcc\xd7x\u0767\xc8\x19mT\xca\xc7u\x8a\x01\xf5q\x89\x87fKH\x00\x00\u07d4IoXC\xf6\xd2L\u064d%^L#\xd1\xe1\xf0#\"uE\x89_\x17\x9f\u0526\xee\t\x80\x00\xe0\x94Ip\u04ec\xf7+[\x1f2\xa7\x00<\xf1\x02\xc6N\xe0TyA\x8a\x1d\xa5jK\b5\xbf\x80\x00\x00\u07d4Iw\xa7\x93\x9d\t9h\x94U\xce&9\xd0\xeeZL\xd9\x10\xed\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4Iy\x19N\xc9\xe9}\xb9\xbe\xe84;|w\xd9\xd7\xf3\xf1\u071f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Iy4c\xe1h\x10\x83\u05ab\xd6\xe7%\u057b\xa7E\xdc\xcd\xe8\x89\x1d\x98\xe9LNG\x1f\x00\x00\u07d4I\x81\xc5\xfff\xccN\x96\x80%\x1f\xc4\xcd/\xf9\a\xcb2xe\x89(\xa8WBTf\xf8\x00\x00\u07d4I\x89\u007f\xe92\xbb\xb3\x15L\x95\u04fc\xe6\xd9;ms)\x04\u0749\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4I\x89\xe1\xab^|\xd0\aF\xb3\x93\x8e\xf0\xf0\xd0d\xa2\x02[\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I\x8a\xbd\xeb\x14\xc2k{r4\xd7\x0f\u03ae\xf3a\xa7m\xffr\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4I\xa6E\xe0f}\xfd{2\xd0u\xcc$g\u074ch\t\a\u0109\a\x06\x01\x95\x8f\u02dc\x00\x00\xe0\x94I\xb7N\x16\x92e\xf0\x1a\x89\xecL\x90r\u0164\xcdr\xe4\xe85\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4I\xbd\xbc{\xa5\xab\xeb\xb68\x9e\x91\xa3(R \xd3E\x1b\xd2S\x8965\u026d\xc5\u07a0\x00\x00\u07d4I\xc9A\xe0\xe5\x01\x87&\xb7)\x0f\xc4s\xb4q\xd4\x1d\xae\x80\u0449\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94I\xc9w\x1f\xca\x19\u0579\xd2E\u0211\xf8\x15\x8f\xe4\x9fG\xa0b\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4I\xcf\x1eT\xbe61\x06\xb9 r\x9d-\v\xa4o\bg\x98\x9a\x89\x0e\x87?D\x13<\xb0\x00\x00\u07d4I\xd2\u008e\xe9\xbcT^\xaa\xf7\xfd\x14\xc2|@s\xb4\xbb_\x1a\x89O\xe9\xb8\x06\xb4\r\xaf\x00\x00\u07d4I\xdd\xee\x90.\x1d\f\x99\u0471\x1a\xf3\u030a\x96\xf7\x8eM\xcf\x1a\x89\n\u03a5\xe4\xc1\x8cS\x00\x00\u07d4I\xf0(9[Z\x86\xc9\xe0\u007fwxc\x0eL.=7:w\x89\x06\xa7JP8\u06d1\x80\x00\xe0\x94J\x19 5\xe2a\x9b$\xb0p\x9dVY\x0e\x91\x83\xcc\xf2\xc1\u064a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4J@S\xb3\x1d\x0e\xe5\u06ef\xb1\xd0k\u05ec\u007f\xf3\",G\u0589K\xe4\xe7&{j\xe0\x00\x00\u07d4JC\x01p\x15-\xe5\x17&3\u0742b\xd1\a\xa0\xaf\xd9j\x0f\x89\xabM\xcf9\x9a:`\x00\x00\u07d4JG\xfc>\x17\u007fVz\x1e8\x93\xe0\x00\xe3k\xba#R\n\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4JR\xba\xd2\x03W\"\x8f\xaa\x1e\x99k\xedy\f\x93gK\xa7\u0409Hz\x9a0E9D\x00\x00\u07d4JS\xdc\xdbV\xceL\xdc\xe9\xf8.\xc0\xeb\x13\xd6sR\xe7\u020b\x89\u3bb5sr@\xa0\x00\x00\u07d4J_\xae;\x03r\xc20\xc1%\xd6\xd4p\x14\x037\xab\x91VV\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4Jq\x90a\xf5(T\x95\xb3{\x9d~\xf8\xa5\x1b\a\xd6\u6b2c\x89\n\xd4\xc81j\v\f\x00\x00\u07d4Js8\x92\x98\x03\x1b\x88\x16\u0329FB\x1c\x19\x9e\x18\xb3C\u0589\"8h\xb8y\x14o\x00\x00\u07d4Js]\"G\x927m3\x13g\xc0\x93\xd3\x1c\x87\x944\x15\x82\x89f\xff\xcb\xfd^Z0\x00\x00\u07d4Jt\x94\xcc\xe4HU\u0300X(B\xbe\x95\x8a\r\x1c\x00r\ue242\x1a\xb0\xd4AI\x80\x00\x00\u07d4Ju\xc3\xd4\xfao\u033d]\u0567\x03\xc1Sy\xa1\xe7\x83\u9dc9b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94J\x81\xab\xe4\x98L|k\xefc\u0598 \xe5WC\xc6\x1f \x1c\x8a\x03d\x01\x00N\x9a\xa3G\x00\x00\u07d4J\x82iO\xa2\x9d\x9e!2\x02\xa1\xa2\t(]\xf6\xe7E\xc2\t\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4J\x83\\%\x82LG\xec\xbf\u01d49\xbf?\\4\x81\xaau\u0349K\xe4\xe7&{j\xe0\x00\x00\u07d4J\x91\x802C\x91Y\xbb1[g%\xb6\x83\r\xc86\x97s\x9f\x89\x12\xa3.\xf6x3L\x00\x00\u07d4J\x97\xe8\xfc\xf4c^\xa7\xfc^\x96\xeeQu.\u00c8qk`\x89\x1d\x99E\xab+\x03H\x00\x00\u07d4J\x9a&\xfd\n\x8b\xa1\x0f\x97}\xa4\xf7|1\x90\x8d\xabJ\x80\x16\x89a\t=|,m8\x00\x00\u07d4J\xa1H\xc2\xc34\x01\xe6j+Xnew\u0132\x92\xd3\xf2@\x89\v\xb8`\xb2\x85\xf7t\x00\x00\u07d4J\xa6\x93\xb1\"\xf3\x14H*G\xb1\x1c\xc7|h\xa4\x97\x87ab\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4J\xb2\xd3O\x04\x83O\xbftyd\x9c\xab\x92=,G%\xc5S\x89\xbe\xd1\xd0&=\x9f\x00\x00\x00\u07d4J\xc0vs\xe4/d\xc1\xa2^\xc2\xfa-\x86\xe5\xaa+4\xe09\x89lk\x93[\x8b\xbd@\x00\x00\u07d4J\u016c\xad\x00\v\x88w!L\xb1\xae\x00\xea\u0263}Y\xa0\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4J\u0250ZL\xb6\xab\x1c\xfdbTn\xe5\x91s\x00\xb8|O\u07897\b\xba\xed=h\x90\x00\x00\u07d4J\u03e9\xd9N\xdaf%\xc9\u07e5\xf9\xf4\xf5\xd1\a\xc4\x03\x1f\u07c9\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4J\xd0G\xfa\xe6~\xf1b\xfeh\xfe\xdb\xc2};e\xca\xf1\f6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4J\xd9]\x18\x8dddp\x9a\xdd%U\xfbM\x97\xfe\x1e\xbf1\x1f\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d4J\xdb\xf4\xaa\xe0\xe3\xefD\xf7\xddM\x89\x85\u03ef\tn\u010e\x98\x89\b!\xab\rD\x14\x98\x00\x00\u07d4J\xe2\xa0M9\t\xefENTL\xcf\xd6\x14\xbf\xef\xa7\x10\x89\xae\x89\x18\x01\x15\x9d\xf1\xee\xf8\x00\x00\xe0\x94J\xe90\x82\xe4Q\x87\xc2a`\xe6g\x92\xf5\u007f\xad5Q\xc7:\x8a\x04\x96\x15 \xda\xff\x82(\x00\x00\u07d4J\xf0\xdb\a{\xb9\xba^D>!\xe1H\xe5\x9f7\x91\x05\u0152\x89 \x86\xac5\x10R`\x00\x00\u07d4K\x06\x19\xd9\u062a1:\x951\xac}\xbe\x04\xca\rjZ\u0476\x89lk\x93[\x8b\xbd@\x00\x00\u07d4K\v\u062c\xfc\xbcS\xa6\x01\v@\xd4\u040d\xdd-\x9dib-\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4K\x19\xeb\f5K\xc199`\xeb\x06\x06;\x83\x92o\rg\xb2\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4K)C|\x97\xb4\xa8D\xbeq\u0323\xb6H\xd4\xca\x0f\u075b\xa4\x89\b$q\x984\u03ec\x00\x00\u07d4K1\xbfA\xab\xc7\\\x9a\xe2\u034f\u007f5\x16;n+tPT\x89\x14\xb5P\xa0\x13\xc78\x00\x00\u07d4K:|\u00e7\u05f0\x0e\xd5(\"!\xa6\x02Y\xf2[\xf6S\x8a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94K:\xab3^\xbb\xfa\xa8p\xccM`^}.t\xc6h6\x9f\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4K\xcd\xc1\x8a`\x00\x00\u07d4K`\xa3\xe2S\xbf8\xc8\xd5f \x10\xbb\x93\xa4s\xc9e\xc3\xe5\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4Kt\xf5\xe5\x8e.\xdfv\xda\xf7\x01Q\x96J\v\x8f\x1d\xe0f<\x89\x11\x90\xaeID\xba\x12\x00\x00\u07d4Kv!f\xdd\x11\x18\xe8Ci\xf8\x04\xc7_\x9c\xd6W\xbfs\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Ky.)h>\xb5\x86\u353b3Rl`\x01\xb3\x97\x99\x9e\x89 \x86\xac5\x10R`\x00\x00\u07d4K\x90N\x93K\xd0\u030b p_\x87\x9e\x90[\x93\xea\f\xcc0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94K\x92\x06\xbakT\x9a\x1a\u007f\x96\x9e\x1d]\xba\x86u9\xd1\xfag\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4K\x98N\xf2lWn\x81Z.\xae\xd2\xf5\x17\u007f\a\u06f1\xc4v\x89T\x91YV\xc4\t`\x00\x00\u07d4K\x9e\x06\x8f\xc4h\tv\xe6\x15\x04\x91)\x85\xfd\\\xe9K\xab\r\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4K\xa0\xd9\xe8\x96\x01w+IhG\xa2\xbbC@\x18g\x87\xd2e\x8965\u026d\xc5\u07a0\x00\x00\u07d4K\xa5:\xb5I\xe2\x01m\xfa\"<\x9e\u0563\x8f\xad\x91(\x8d\a\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94K\xa8\xe0\x11\u007f\xc0\xb6\xa3\xe5k$\xa3\xa5\x8f\xe6\xce\xf4B\xff\x98\x8a\x011\xbe\xb9%\xff\xd3 \x00\x00\u07d4K\xac\x84j\xf4\x16\x9f\x1d\x95C\x1b4\x1d\x88\x00\xb2!\x80\xaf\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4K\xb6\xd8k\x83\x14\xc2-\x8d7\xeaQm\x00\x19\xf1V\xaa\xe1-\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94K\xb9e\\\xfb*6\xea|cz{\x85\x9bJ1T\xe2n\xbe\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94K\xbc\xbf8\xb3\xc9\x01c\xa8K\x1c\u04a9;X\xb2\xa34\x8d\x87\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94K\xd6\xdd\f\xff#@\x0e\x170\xba{\x89E\x04W}\x14\xe7J\x8a+\xa0\xcc\xdd\xd0\xdfs\xb0\x00\x00\u07d4K\xe8b\x8a\x81T\x87N\x04\x8d\x80\xc1B\x18\x10\"\xb1\x80\xbc\xc1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4K\xe9\rA!)\u0564\xd0BCa\xd6d\x9dNG\xa6#\x16\x897\b\xba\xed=h\x90\x00\x00\xe0\x94K\xea(\x8e\xeaB\u0115^\xb9\xfa\xad*\x9f\xafG\x83\xcb\u076c\x8a\x06\x18\xbe\x16c\u012fI\x00\x00\u07d4K\xf4G\x97\x99\xef\x82\xee\xa2\tC7OV\xa1\xbfT\x00\x1e^\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4K\xf8\xbf\x1d5\xa211Wd\xfc\x80\x01\x80\x9a\x94\x92\x94\xfcI\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4K\xf8\xe2oL'\x90\xdae3\xa2\xac\x9a\xba\xc3\u019a\x19\x943\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0794L\n\xcaP\x8b<\xaf^\xe0(\xbcp}\xd1\xe8\x00\xb88\xf4S\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94L\v\x15\x15\xdf\xce\u05e1>\x13\xee\x12\xc0\xf5#\xaePO\x03+\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4L\x13\x98\f2\xdc\xf3\x92\vx\xa4\xa7\x903\x12\x90|\x1b\x12?\x89\x03A\x00\x15\xfa\xae\f\x00\x00\u07d4L\x15y\xaf3\x12\xe4\xf8\x8a\xe9\x95\xcc9W\xd2R\xce\v\xf0\xc8}[O\"4g.p\x89\x87\x86x2n\xac\x90\x00\x00\u07d4LB1y\x82i\x1d\x10\x89\x05k\xc7^-c\x10\x00\x00\u07d4LZ\xfe@\xf1\x8f\xfcH\u04e1\xae\xc4\x1f\u009d\xe1y\xf4\u0497\x89lk\x93[\x8b\xbd@\x00\x00\u07d4L[=\xc0\xe2\xb96\x0f\x91(\x9b\x1f\xe1<\xe1,\x0f\xbd\xa3\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Lfk\x86\xf1\xc5\ue324\x12\x85\xf5\xbd\xe4\xf7\x90R\b\x14\x06\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Lik\xe9\x9f:i\x04@\xc3CjY\xa7\xd7\xe97\u05ba\r\x89\xbb\x91%T\"c\x90\x00\x00\u07d4Lj$\x8f\xc9}p]\xefI\\\xa2\aY\x16\x9e\xf0\xd3dq\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4Lj\x9d\xc2\u02b1\n\xbb.|\x13p\x06\xf0\x8f\ucd77y\xe1\x89\x1b\r\x04 /G\xec\x00\x00\u07d4Lk\x93\xa3\xbe\xc1cIT\f\xbf\xca\xe9l\x96!\xd6dP\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Lu\x98\x13\xad\x13\x86\xbe\xd2\u007f\xfa\xe9\xe4\x81^60\u0323\x12\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Lv\f\xd9\xe1\x95\xeeO-k\xce%\x00\xff\x96\xda|C\ue44a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4Lv{e\xfd\x91\x16\x1fO\xbd\xccji\xe2\xf6\xadq\x1b\xb9\x18\x89'\b\x01\xd9F\xc9@\x00\x00\u07d4L~.+w\xad\f\xd6\xf4J\xcb(a\xf0\xfb\x8b(u\x0e\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4L\x85\xed6/$\xf6\xb9\xf0L\xdf\xcc\xd0\"\xaeSQG\u02f9\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4L\x93[\xb2Pw\x8b0\x9b==\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4L\xee\x90\x1bJ\u0231V\xc5\xe2\xf8\xa6\xf1\xbe\xf5r\xa7\xdc\xeb~\x8965\u026d\xc5\u07a0\x00\x00\u07d4L\xef\xbe#\x98\xe4}R\u73743L\x8bivu\U00053b89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4L\xf5S{\x85\x84/\x89\xcf\xee5\x9e\xaeP\x0f\xc4I\xd2\x11\x8f\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94M\bG\x1dh\x00z\xff*\xe2y\xbc^?\xe4\x15o\xbb\xe3\u078a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4M \x01\x10\x12@\b\xd5ov\x98\x12VB\f\x94jo\xf4\\\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4M$\xb7\xacG\xd2\xf2}\xe9\tt\xba=\xe5\xea\xd2\x03TK\u0349\x05k\xc7^-c\x10\x00\x00\u0794M)\xfcR:,\x16)S!!\u0699\x98\u9d6b\x9d\x1bE\x88\xdbD\xe0I\xbb,\x00\x00\u07d4M8\xd9\x0f\x83\xf4Q\\\x03\xccx2j\x15M5\x8b\u0602\xb7\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4ML\xf5\x80t)a^0\xcd\xfa\xce\x1eZ\xaeM\xad0U\xe6\x89 \x86\xac5\x10R`\x00\x00\u07d4MW\xe7\x16\x87l\f\x95\xef^\xae\xbd5\xc8\xf4\x1b\x06\x9bk\xfe\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Mg\U000ab159\xfe\xf5\xfcA9\x99\xaa\x01\xfd\u007f\xcep\xb4=\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Mn\x8f\xe1\t\xcc\xd2\x15\x8eM\xb1\x14\x13/\xe7_\xec\u023e[\x89\x01[5W\xf1\x93\u007f\x80\x00\xe0\x94Mq\xa6\xeb=\u007f2~\x184'\x8e(\v\x03\x9e\xdd\xd3\x1c/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4M|\xfa\xa8L\xb31\x06\x80\n\x8c\x80/\xb8\xaaF8\x96\u0159\x89a\t=|,m8\x00\x00\u07d4M\x80\x10\x93\xc1\x9c\xa9\xb8\xf3B\xe3<\xc9\xc7{\xbdL\x83\x12\u03c9\x12\xb3\xe7\xfb\x95\u0364\x80\x00\u07d4M\x82\x88\x94u/o%\x17]\xaf!w\tD\x87\x95Ko\x9f\x89O!+\xc2\u011c\x83\x80\x00\xe0\x94M\x82\xd7p\f\x12;\xb9\x19A\x9b\xba\xf0Fy\x9ck\x0e,f\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4M\x83m\x9d;\x0e,\xbdM\xe0PYo\xaaI\f\xff\xb6\r]\x89\x10CV\x1a\x88)0\x00\x00\u07d4M\x86\x97\xaf\x0f\xbf,\xa3n\x87h\xf4\xaf\"\x135phZ`\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4M\x92y\x96 )\xa8\xbdEc\x977\xe9\x8bQ\x1e\xff\aL!\x89Hz\x9a0E9D\x00\x00\u07d4M\x93io\xa2HY\xf5\u0493\x9a\xeb\xfaT\xb4\xb5\x1a\xe1\xdc\u0309\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4M\x9cw\xd0u\f^o\xbc$\u007f/\u05d2thl\xb3S\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4M\xa5\xed\u0188\xb0\xcbb\xe1@=\x17\x00\xd9\u0739\x9f\xfe?\u04c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94M\xa8\x03\ai\x84K\xc3A\x86\xb8\\\xd4\xc74\x88I\xffI\xe9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4M\xb1\xc4:\x0f\x83M}\x04x\xb8\x96\ag\xec\x1a\xc4L\x9a\xeb\x89/Q\x810V'7\x00\x00\u07d4M\xb2\x12\x84\xbc\xd4\xf7\x87\xa7Ue\x00\xd6\xd7\xd8\xf3f#\xcf5\x89i(7Ow\xa3c\x00\x00\u07d4M\xc3\xda\x13\xb2\xb4\xaf\xd4O]\r1\x89\xf4D\xd4\xdd\xf9\x1b\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4M\u013f^u\x89\xc4{(7\x8du\x03\u03d6H\x80a\u06fd\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4M\xc9\u057bK\x19\xce\u0354\xf1\x9e\xc2] \x0e\xa7/%\xd7\xed\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94M\xcd\x11\x81X\x18\xae)\xb8]\x016sI\xa8\xa7\xfb\x12\xd0k\x8a\x01\xacB\x86\x10\x01\x91\xf0\x00\x00\u07d4M\xcfb\xa3\xde?\x06\x1d\xb9\x14\x98\xfda\x06\x0f\x1fc\x98\xffs\x89lj\xccg\u05f1\xd4\x00\x00\u07d4M\xd11\xc7J\x06\x8a7\xc9\n\xde\xd4\xf3\t\xc2@\x9fdx\u04c9\x15\xaf9\u4ab2t\x00\x00\xe0\x94M\u0767Xk\"7\xb0S\xa7\xf3(\x9c\xf4`\xdcW\xd3z\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4M\xe3\xfe4\xa6\xfb\xf64\xc0Q\x99\u007fG\xcc\u007fHy\x1fX$\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4M\xf1@\xbaye\x85\xddT\x891[\xcaK\xbah\n\u06f8\x18\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4N\x02\ay\xb5\xdd\xd3\xdf\"\x8a\x00\xcbH\xc2\xfc\x97\x9d\xa6\xae8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4N\v\xd3$s\xc4\xc5\x1b\xf2VT\xde\xf6\x9fy|k)\xa22\x89V\xc9]\xe8\xe8\xca\x1d\x00\x00\u07d4N\"%\xa1\xbbY\xbc\x88\xa21ft\xd33\xb9\xb0\xaf\xcafU\x89\bg\x0e\x9e\xc6Y\x8c\x00\x00\u07d4N#\x10\x19\x1e\xad\x8d;\xc6H\x98s\xa5\xf0\xc2\xeck\x87\u1f8965\u026d\xc5\u07a0\x00\x00\u07d4N#-S\xb3\u6f8f\x89Sa\xd3\x1c4\xd4v+\x12\xc8.\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4N+\xfaJFo\x82g\x1b\x80\x0e\xeeBj\xd0\f\a\x1b\xa1p\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4N>\xda\u0506M\xabd\xca\xe4\xc5Azvw@S\xdcd2\x89 \b\xfbG\x8c\xbf\xa9\x80\x00\u07d4NC\x18\xf5\xe1>\x82JT\xed\xfe0\xa7\xedO&\xcd=\xa5\x04\x89lk\x93[\x8b\xbd@\x00\x00\u07d4N[w\xf9\x06aY\xe6\x15\x93?-\xdatw\xfaNG\xd6H\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94Nf\x00\x80b\x89EJ\u03630\xa2\xa3U`\x10\u07ec\xad\xe6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Ns\xcf#y\xf1$\x86\x0fs\xd6\xd9\x1b\xf5\x9a\xcc\\\xfc\x84[\x89\x02,\xa3X|\xf4\xeb\x00\x00\xe0\x94Nz\xa6~\x12\x18>\xf9\xd7F\x8e\xa2\x8a\xd29\xc2\xee\xf7\x1bv\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\xe0\x94N{TGM\x01\xfe\xfd8\x8d\xfc\xd5;\x9ff&$A\x8a\x05\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94N\x89.\x80\x81\xbf6\xe4\x88\xfd\xdb;&0\xf3\xf1\xe8\xda0\u048a\x02\x8a\xba0u$Q\xfc\x00\x00\xe0\x94N\x8amcH\x9c\xcc\x10\xa5\u007f\x88_\x96\xeb\x04\xec\xbbT`$\x8a\x03\xea\xe3\x13\x0e\u0316\x90\x00\x00\u07d4N\x8eG\xae;\x1e\xf5\f\x9dT\xa3\x8e\x14 \x8c\x1a\xbd6\x03\u0089y(\xdb\x12vf\f\x00\x00\u0794N\x90\u03312X\xac\xaa\x9fO\xeb\xc0\xa3B\x92\xf9Y\x91\xe20\x88\xdbD\xe0I\xbb,\x00\x00\u07d4N\xa5n\x11\x12d\x1c\x03\x8d\x05e\xa9\u0096\xc4c\xaf\xef\xc1~\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\xe0\x94N\xa7\x0f\x041?\xaee\xc3\xff\"J\x05\\=-\xab(\xdd\u07ca\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4N\xb1EKW8\x05\u022c\xa3~\xde\xc7\x14\x9aA\xf6\x12\x02\xf4\x89\x10CV\x1a\x88)0\x00\x00\u07d4N\xb8{\xa8x\x8e\xba\r\xf8~[\x9b\xd5\n\x8eE6\x80\x91\xc1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4N\xbcV)\xf9\xa6\xa6k,\xf36:\u0109\\\x03H\u8fc7\x8967\tlK\xcci\x00\x00\u07d4N\xc7h)^\xea\xba\xfcB\x95\x84\x15\xe2+\xe2\x16\xcd\xe7v\x18\x89\x03;\x1d\xbc9\xc5H\x00\x00\u07d4N\xcc\x19\x94\x8d\xd9\u0347\xb4\xc7 \x1a\xb4\x8eu\x8f(\xe7\xccv\x89\x1b\x1d\xaba\u04ead\x00\x00\u07d4N\xd1M\x81\xb6\v#\xfb%\x05M\x89%\u07e5s\u072eah\x89\x12nr\xa6\x9aP\xd0\x00\x00\xe0\x94N\xe1<\rA \vF\u045d\xee\\K\xce\xc7\x1d\x82\xbb\x8e8\x8a\x01\xab\xee\x13\u033e\ufbc0\x00\u07d4N\xea\xd4\n\xad\x8cs\xef\b\xfc\x84\xbc\n\x92\xc9\t/j6\xbf\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4N\xeb\xe8\f\xb6\xf3\xaeY\x04\xf6\xf4\xb2\x8d\x90\u007f\x90q\x89\xfc\xab\x89lj\xccg\u05f1\xd4\x00\x00\u07d4N\xeb\xf1 ]\f\xc2\f\xeel\u007f\x8f\xf3\x11_V\u050f\xba&\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4N\xf1\xc2\x14c:\xd9\xc0p;N#t\xa2\xe3>>B\x92\x91\x89Hz\x9a0E9D\x00\x00\u07d4N\xfc\xd9\u01df\xb43L\xa6${\n3\xbd\x9c\xc32\b\xe2r\x89Hz\x9a0E9D\x00\x00\xe0\x94O\x06$k\x8dK\u0496a\xf4>\x93v\"\x01\u0486\x93Z\xb1\x8a\x01\x059O\xfcF6\x11\x00\x00\u07d4O\x15+/\xb8e\x9dCwn\xbb\x1e\x81g:\xa8Ai\xbe\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4O\x17\u007f\x9dV\x95=\xedq\xa5a\x1f93\"\xc3\x02y\x89\\\x89\rU\uf422\xda\x18\x00\x00\u07d4O\x1a-\xa5JLm\xa1\x9d\x14$\x12\xe5n\x81WA\xdb#%\x89\x05k\xc7^-c\x10\x00\x00\u07d4O#\xb6\xb8\x17\xff\xa5\xc6d\xac\xda\u05db\xb7\xb7&\xd3\n\xf0\xf9\x89_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94O&i\f\x99+z1*\xb1.\x13\x85\xd9J\xcdX(\x8e{\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4O+G\xe2wZ\x1f\xa7\x17\x8d\xad\x92\x98Z[\xbeI;\xa6\u0589\n\u05ce\xbcZ\xc6 \x00\x00\u07d4O:HT\x91\x11E\xea\x01\xc6D\x04K\xdb.Z\x96\n\x98/\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4O?,g0i\xac\x97\xc2\x026\a\x15)\x81\xf5\xcd`c\xa0\x89 \x86\xac5\x10R`\x00\x00\xe0\x94OJ\x9b\xe1\f\xd5\xd3\xfb]\xe4\x8c\x17\xbe)o\x89V\x90d[\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4OR\xadap\xd2[*.\x85\x0e\xad\xbbRA?\xf20>\u007f\x89\xa4\xccy\x95c\u00c0\x00\x00\u07d4OX\x01\xb1\xeb0\xb7\x12\u0620WZ\x9aq\xff\x96]O4\xeb\x89\x10CV\x1a\x88)0\x00\x00\u07d4O]\xf5\xb9CW\u0794\x86\x04\xc5\x1bx\x93\xcd\xdf`v\xba\xad\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\u07d4Od\xa8^\x8e\x9a@I\x8c\fu\xfc\xeb\x037\xfbI\b>^\x8965\u026d\xc5\u07a0\x00\x00\u07d4Og9m%S\xf9\x98x_pN\a\xa69\x19}\u0454\x8d\x89\x10DrR\x1b\xa78\x00\x00\u07d4OmG7\u05e9@8$\x87&H\x86i|\xf7c\u007f\x80\x15\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4Os0\toy\xed&N\xe0\x12\u007f]0\xd2\xf7?!\xcb\u007f\x04\x89\x04\x82\xfe&\f\xbc\xa9\x00\x00\u07d4O\xeeP\xc5\xf9\x88 k\t\xa5sF\x9f\xb1\u0434.\xbbm\u0389l\xee\x06\u077e\x15\xec\x00\x00\u07d4O\xf6v\xe2\u007fh\x1a\x98-\x8f\xd9\xd2\x0ed\x8b=\xce\x05\xe9E\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4O\xf6\u007f\xb8\u007fn\xfb\xa9'\x990\u03fd\x1bz4L\u057a\x8bN\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94PFf\u03891\x17^\x11\xa5\xed\x11\xc1\u072a\x06\xe5\u007fNf\x8a\x02\u007f>\u07f3Nn@\x00\x00\u0794PXM\x92\x06\xa4l\xe1\\0\x11\x17\xee(\xf1\\0\xe6\x0eu\x88\xb9\xf6]\x00\xf6<\x00\x00\xe0\x94PZ3\xa1\x864\xddH\x00i)\x13N\x00\x00\u07d4P\u0286\xb5\xeb\x1d\x01\x87M\xf8\xe5\xf3IE\u051cl\x1a\xb8H\x8965\u026d\xc5\u07a0\x00\x00\u07d4P\u0357\xe97\x8b\\\xf1\x8f\x179c#l\x99Q\xeft8\xa5\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4P\u073c'\xbc\xad\x98@\x93\xa2\x12\xa9\xb4\x17\x8e\xab\xe9\x01ua\x89\a\xe3by\v\\\xa4\x00\x00\u07d4P\xe10#\xbd\x9c\xa9j\xd4\xc5?\xdf\xd4\x10\xcbk\x1fB\v\u07c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94P\xe1\xc8\xec\x98A[\xefD&\x18p\x87\x99C{\x86\xe6\xc2\x05\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4P\xf8\xfaK\xb9\xe2g|\x99\nN\xe8\xcep\xdd\x15#%\x1eO\x89\x01i=#\x16Ok\x00\x00\u07d4P\xfb6\xc2q\a\xee,\xa9\xa3#n'F\u0321\x9a\xcekI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4P\xfe\xf2\x96\x95U\x88\u02aet\xc6.\xc3*#\xa4T\xe0\x9a\xb8\x89A\x1d\xff\xab\xc5\a8\x00\x00\u07d4Q\x02\xa4\xa4 w\xe1\x1cX\xdfGs\u3b14F#\xa6m\x9f\x89lp\x15\xfdR\xed@\x80\x00\u07d4Q\x03\x93w\xee\xd0\xc5s\xf9\x86\xc5\xe8\xa9_\xb9\x9aY\xe93\x0f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Q\x03\xbc\t\x93>\x99!\xfdS\xdcSo\x11\xf0]\rG\x10}\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94Q\x04\xec\xc0\xe30\xdd\x1f\x81\xb5\x8a\xc9\u06f1\xa9\xfb\xf8\x8a<\x85\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4Q\r\x81Y\u0314Wh\xc7E\a\x90\xba\a>\xc0\xd9\xf8\x9e0\x89\x8a\xc7#\x04\x89\xe8\x00\x00\x00\u07d4Q\x0e\xdaV\x01I\x9a\r^\x1a\x00k\xff\xfd\x836r\xf2\xe2g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x12dF\xab=\x802U~\x8e\xbaeY}u\xfa\u0701\\\x89\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\xe0\x94Q\x18U}`\r\x05\xc2\xfc\xbf8\x06\xff\xbd\x93\xd0 %\xd70\x8a\x02g\u04ebd#\xf5\x80\x00\x00\u07d4Q\x1e\x0e\xfb\x04\xacN?\xf2\xe6U\x0eI\x82\x95\xbf\xcdV\xff\u0549$=M\x18\"\x9c\xa2\x00\x00\u07d4Q!\x16\x81{\xa9\xaa\xf8C\xd1P|e\xa5\xead\n{\x9e\xec\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4Q&F\ri,q\u026fo\x05WM\x93\x99\x83h\xa27\x99\x89\x02\u0465\x1c~\x00P\x00\x00\u07d4Q'\u007f\xe7\xc8\x1e\xeb\xd2R\xa0=\xf6\x9ak\x9f2n'\"\a\x89\x03@.y\u02b4L\x80\x00\u07d4Q)oPD'\r\x17pvF\x12\x9c\x86\xaa\xd1d^\xad\xc1\x89H|r\xb3\x10\xd4d\x80\x00\xe0\x94Q+\x91\xbb\xfa\xa9\xe5\x81\xefh?\xc9\r\x9d\xb2*\x8fI\xf4\x8b\x8aA\xa5\"8m\x9b\x95\xc0\x00\x00\u07d4Q5\xfb\x87W`\f\xf4tTbR\xf7M\xc0tm\x06&,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4QF2\xef\xbdd,\x04\xdel\xa3B1]@\u0750\xa2\u06e6\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4QKu\x12\u026e^\xa6<\xbf\x11q[c\xf2\x1e\x18\u0496\xc1\x89lj\xccg\u05f1\xd4\x00\x00\u07d4QS\xa0\xc3\u0211(\x81\xbf\x1c5\x01\xbfd\xb4VI\xe4\x82\"\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94QVQ\xd6\xdbO\xaf\x9e\xcd\x10:\x92\x1b\xbb\xbej\xe9p\xfd\u050a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94Q_0\xbc\x90\xcd\xf4W~\xe4}e\u05c5\xfb\xe2\xe87\u01bc\x8a\x02'\x1b^\x01\x8b\xa0X\x00\x00\u07d4Q`\xeda.\x1bH\xe7??\xc1[\xc42\x1b\x8f#\xb8\xa2K\x89\x1e\x82kB(e\xd8\x00\x00\u07d4Qa\xfdI\xe8G\xf6tU\xf1\u023bz\xbb6\xe9\x85&\r\x03\x89A\rXj \xa4\xc0\x00\x00\u07d4QiT\x02_\xca&\b\xf4}\xa8\x1c!^\xed\xfd\x84J\t\xff\x89\x14\xb5P\xa0\x13\xc78\x00\x00\u07d4Qi\xc6\n\xeeL\xee\u0444\x9a\xb3mfL\xff\x97\x06\x1e\x8e\xa8\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4Q|uC\r\xe4\x01\xc3A\x03&\x86\x11'\x90\xf4mM6\x9e\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4Q|\xd7`\x8e]\r\x83\xa2kq\u007f6\x03\xda\xc2'}\u00e4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x86]\xb1H\x88\x19Q\xf5\x12Qq\x0e\x82\xb9\xbe\r~\xad\xb2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x89\x1b,\xcd\xd2\xf5\xa4K*\x8b\u011a]\x9b\xcadw%\x1c\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4Q\x8c\xef'\xb1\x05\x82\xb6\xd1OiH=\u06a0\xdd<\x87\xbb\\\x89 \x86\xac5\x10R`\x00\x00\u07d4Q\xa6\xd6'\xf6j\x89#\u060d`\x94\xc4qS\x80\xd3\x05|\xb6\x89>s\xd2z5\x94\x1e\x00\x00\u07d4Q\xa8\xc2\x166\x02\xa3.\xe2L\xf4\xaa\x97\xfd\x9e\xa4\x14QiA\x89\x03h\xf7\xe6\xb8g,\x00\x00\u07d4Q\xb4u\x8e\x9e\x14P\xe7\xafBh\xc3\u01f1\xe7\xbdo\\uP\x8965\u026d\xc5\u07a0\x00\x00\u07d4Q\u028b\xd4\xdcdO\xacG\xafgUc\u0540J\r\xa2\x1e\xeb\x89*\xb7\xb2`\xff?\xd0\x00\x00\u07d4Q\xd2K\xc3so\x88\xddc\xb7\" &\x88f0\xb6\ub1cd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\u05cb\x17\x8dp~9n\x87\x10\x96\\OA\xb1\xa1\xd9\x17\x9d\x89\x05\xfe\xe2\"\x04\x1e4\x00\x00\u07d4Q\xe3/\x14\xf4\xca^(|\xda\xc0W\xa7y^\xa9\xe0C\x99S\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Q\xe4?\xe0\xd2\\x(`\xaf\x81\xea\x89\xddy<\x13\xf0\u02f1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4Q\xe7\xb5\\/\x98 \xee\xd78\x846\x1bPf\xa5\x9boE\u0189lk\x93[\x8b\xbd@\x00\x00\xe0\x94Q\xea\x1c\t4\xe3\xd0@\"\ud715\xa0\x87\xa1P\xefp^\x81\x8a\x01Tp\x81\xe7\"M \x00\x00\u07d4Q\xee\f\xca;\xcb\x10\xcd>\x987\"\xce\xd8I=\x92l\bf\x8965f3\xeb\xd8\xea\x00\x00\xe0\x94Q\xf4f:\xb4O\xf7\x93E\xf4'\xa0\xf6\xf8\xa6\u0225?\xf24\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Q\xf5^\xf4~dV\xa4\x18\xab2\xb9\"\x1e\xd2}\xbaf\b\xee\x89\u3bb5sr@\xa0\x00\x00\xe0\x94Q\xf9\xc42\xa4\xe5\x9a\xc8b\x82\u05ad\xabL.\xb8\x91\x91`\xeb\x8ap;[\x89\u00e6\xe7@\x00\x00\u07d4R\x0ff\xa0\xe2e\u007f\xf0\xacA\x95\xf2\xf0d\xcf/\xa4\xb2BP\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4R\x10#T\xa6\xac\xa9]\x8a.\x86\xd5\u07bd\xa6\xdei4`v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\x13\xf4Y\xe0x\xad:\xb9Z\t #\x9f\xcf\x163\xdc\x04\u0289\x8c\xf2\x18|*\xfb\x18\x80\x00\u07d4R\x15\x18;\x8f\x80\xa9\xbc\x03\xd2l\xe9\x12\a\x83*\r9\xe6 \x8965\u026d\xc5\u07a0\x00\x00\xe0\x94R!Cx\xb5@\x04\x05j|\xc0\x8c\x89\x13'y\x8a\u01b2H\x8a\x037\xfe_\xea\xf2\u0440\x00\x00\xe0\x94R##\xaa\xd7\x1d\xbc\x96\xd8Z\xf9\x0f\bK\x99\xc3\xf0\x9d\ucdca\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4R>\x14\r\xc8\x11\xb1\x86\xde\xe5\xd6\u020b\xf6\x8e\x90\xb8\xe0\x96\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R?mdi\x0f\xda\u0354(SY\x1b\xb0\xff \xd3em\x95\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4RO\xb2\x10R,^#\xbbg\u07ff\x8c&\xaaam\xa4\x99U\x8965b\xa6m4#\x80\x00\u07d4RU\xdci\x15ZE\xb9p\xc6\x04\xd3\x00G\xe2\xf50i\x0e\u007f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4R`\xdcQ\xee\a\xbd\u06ab\xab\xb9\xeetK9<\u007fG\x93\xa6\x89\x01\xd8f_\xa5\xfaL\x00\x00\u07d4Rg\xf4\xd4\x12\x92\xf3p\x86<\x90\u05d3)i\x03\x846%\u01c9K\xe4\xe7&{j\xe0\x00\x00\u07d4Rk\xb53\xb7n \xc8\xee\x1e\xbf\x12?\x1e\x9f\xf4\x14\x8e@\xbe\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4Rl\xb0\x9c\u3b63g.\xec\x1d\xebF [\xe8\x9aKV>\x89\x85\xcaa[\xf9\xc0\x10\x00\x00\u07d4Rs\x8c\x90\xd8`\xe0L\xb1/I\x8d\x96\xfd\xb5\xbf6\xfc4\x0e\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4Rz\x8c\xa1&\x863\xa6\xc99\xc5\xde\x1b\x92\x9a\ue4ae\xac\x8d\x890\xca\x02O\x98{\x90\x00\x00\u07d4R\x81\x01\xceF\xb7 \xa2!M\u036ef\x18\xa51w\xff\xa3w\x89\x1b\x96\x12\xb9\xdc\x01\xae\x00\x00\xe0\x94R\x81s4s\xe0\r\x87\xf1\x1e\x99U\u5275\x9fJ\u008ez\x8a\x8b\xd6/\xf4\xee\xc5Y \x00\x00\u07d4R\x98\xab\x18*\x195\x9f\xfc\xec\xaf\xd7\u0475\xfa!-\xed\xe6\u0749\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4R\x9a\xa0\x02\u0196*:\x85E\x02\u007f\u0630_\"\xb5\xbf\x95d\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4R\x9e\x82O\xa0rX+@2h:\xc7\xee\xcc\x1c\x04\xb4\xca\xc1\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94R\xa5\xe4\xdeC\x93\xee\xcc\xf0X\x1a\xc1\x1bR\u0183\xc7n\xa1]\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4R\xb4%|\xf4\x1bn(\x87\x8dP\xd5{\x99\x91O\xfa\x89\x87:\x89\xd5\r\u026a,Aw\x00\x00\u07d4R\xb8\xa9Y&4\xf70\v|\\Y\xa34[\x83_\x01\xb9\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\xbd\u066fYx\x85\v\xc2A\x10q\x8b7#u\x9bC~Y\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4R\xcd @;\xa7\xed\xa6\xbc0z=c\xb5\x91\x1b\x81|\x12c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794R\u04c0Q\x1d\xf1\x9d^\u0080{\xbc\xb6vX\x1bg\xfd7\xa3\x88\xb9\xf6]\x00\xf6<\x00\x00\xe0\x94R\xe1s\x13P\xf9\x83\xcc,A\x89\x84/\xde\x06\x13\xfa\xd5\f\xe1\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\u07d4R\xe4g\x832\x9av\x93\x01\xb1u\x00\x9d4gh\xf4\xc8~\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\xf0X\xd4aG\xe9\x00m)\xbf,\t0J\xd1\xcd\xddn\x15\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4R\xf1T#2<$\xf1\x9a\xe2\xabg7\x17\"\x9d?t}\x9b\x897\xa04\xcb\xe8\xe3\xf3\x80\x00\u07d4R\xf8\xb5\t\xfe\xe1\xa8t\xabo\x9d\x876\u007f\xbe\xaf\x15\xac\x13\u007f\x8965\u026d\xc5\u07a0\x00\x00\u07d4R\xfbF\xac]\x00\xc3Q\x8b,:\x1c\x17}D/\x81eU_\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4S\x00w\xc9\xf7\xb9\a\xff\x9c\xec\fw\xa4\x1ap\xe9\x02\x9a\xddJ\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\x03\x19\xdb\n\x8f\x93\xe5\xbb}M\xbfH\x161O\xbe\xd86\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\x04}\u022c\x90\x83\xd9\x06r\xe8\xb3G<\x10\f\xcd'\x83#\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4S\va\xe4/9Bm$\b\xd4\bR\xb9\xe3J\xb5\xeb\xeb\u0149\x0e~\xeb\xa3A\vt\x00\x00\u07d4S\x0f\xfa\u00fc4\x12\xe2\xec\x0e\xa4{y\x81\xc7p\xf5\xbb/5\x89\a?u\u0460\x85\xba\x00\x00\u07d4S\x17\xec\xb0#\x05,\xa7\xf5e+\xe2\xfa\x85L\xfeEc\xdfM\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4S\x19M\x8a\xfa>\x885\x02v~\xdb\xc3\x05\x86\xaf3\xb1\x14\u04c9lk\x93[\x8b\xbd@\x00\x00\u07d4S*}\xa0\xa5\xadt\aF\x8d;\xe8\xe0~i\xc7\xddd\xe8a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4S-2\xb0\x0f0[\xcc$\xdc\xefV\x81}b/4\xfb,$\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4S4DX@\x82\xeb\xa6T\xe1\xad0\xe1Is\\o{\xa9\"\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4S8\xefp\xea\xc9\u075a\xf5\xa0P;^\xfa\xd1\x03\x9eg\xe7%\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94S9oJ&\u00b4`D\x960lTB\xe7\xfc\xba'.6\x8a\x04?/\b\xd4\x0eZ\xfc\x00\x00\xe0\x94S:s\xa4\xa2\"\x8e\xee\x05\xc4\xff\xd7\x18\xbb\xf3\xf9\xc1\xb1)\xa7\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4S<\x06\x92\x8f\x19\u0429V\xcc(\x86k\xf6\xc8\xd8\xf4\x19\x1a\x94\x89\x0f\xd8\xc1C8\xe60\x00\x00\u07d4S@e6\x1c\xb8T\xfa\xc4+\xfb\\\x9f\xcd\xe0`J\xc9\x19\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4SC\u007f\xec\xf3J\xb9\xd45\xf4\u07b8\xca\x18\x15\x19\xe2Y 5\x89\n1\x06+\xee\xedp\x00\x00\u07d4SR\x01\xa0\xa1\xd74\"\x80\x1fU\xde\xd4\u07ee\xe4\xfb\xaan;\x89\x02&!\x1fy\x15B\x80\x00\xe0\x94S`\x81\x05\xceK\x9e\x11\xf8k\xf4\x97\xff\xca;x\x96{_\x96\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4SnM\x80)\xb7?Uy\u0723>p\xb2N\xba\x89\xe1\x1d~\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Sp\rS%MC\x0f\"x\x1aJv\xa4c\x93;]k\b\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94S\u007f\x9dM1\xefp\x83\x9d\x84\xb0\xd9\u0377+\x9a\xfe\xdb\xdf5\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\xe0\x94S\x81D\x85\x03\xc0\xc7\x02T+\x1d\xe7\xcc_\xb5\xf6\xab\x1c\xf6\xa5\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94S\x94.yI\xd6x\x8b\xb7\x80\xa7\xe8\xa0y'\x81\xb1aK\x84\x8a\x03]\xebFhO\x10\xc8\x00\x00\u07d4S\x95\xa4E]\x95\xd1x\xb4S*\xa4r[\x19?\xfeQ)a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94S\x98\x9e\xd30V?\xd5}\xfe\u027d4<7`\xb0y\x93\x90\x8a\x01P\x89N\x84\x9b9\x00\x00\x00\u07d4S\xa2Dg(\x95H\x0fJ+\x1c\xdf}\xa5\xe5\xa2B\xecM\xbc\x8965\u026d\xc5\u07a0\x00\x00\u07d4S\xa7\x14\xf9\x9f\xa0\x0f\xefu\x8e#\xa2\xe7F2m\xad$|\xa7\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4S\xaf2\xc2/\uf640?\x17\x8c\xf9\v\x80/\xb5q\xc6\x1c\xb9\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4S\xc0\xbb\u007f\u020e\xa4\"\xd2\xef~T\x0e-\x8f(\xb1\xbb\x81\x83\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94S\xc5\xfe\x01\x19\xe1\xe8Hd\f\xee0\xad\ua594\x0f*]\x8b\x8a\x04\x9a\xda_\xa8\xc1\f\x88\x00\x00\u07d4S\xc9\xec\xa4\ts\xf6;\xb5\x92{\xe0\xbcj\x8a\x8b\xe1\x95\x1ft\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\u0388\xe6lZ\xf2\U0009bf4fY*V\xa3\xd1_ l2\x89\a\xa2\x8c1\xcc6\x04\x00\x00\u07d4S\xce\xc6\u0200\x92\xf7V\xef\xe5o}\xb1\x12(\xa2\xdbE\xb1\"\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4S\xe3[\x12#\x1f\x19\xc3\xfdwL\x88\xfe\xc8\xcb\xee\xdf\x14\b\xb2\x89\x1b\xc1mgN\xc8\x00\x00\x00\u07d4S\xe4\xd9im\xcb?M{?p\u072aN\xec\xb7\x17\x82\xff\\\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4S\xfa\xf1e\xbe\x03\x1e\xc1\x830\xd9\xfc\xe5\xbd\x12\x81\xa1\xaf\b\u06c9\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4T\n\x18\x19\xbd|5\x86\x1ey\x18\x04\xe5\xfb\xb3\xbc\x97\u026b\xb1\x89N\xd7\xda\xc6B0 \x00\x00\xe0\x94T\f\a(\x02\x01N\xf0\xd5a4Z\xecH\x1e\x8e\x11\xcb5p\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94T\f\xf2=\xd9\\MU\x8a'\x9dw\x8d+75\xb3\x16A\x91\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4T\x10`\xfcX\xc7P\xc4\x05\x12\xf83i\xc0\xa63@\xc1\"\xb6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4T\x13\xc9\u007f\xfaJn*{\xba\x89a\u071f\u03850\xa7\x87\u05c965\u026d\xc5\u07a0\x00\x00\u07d4T\x1d\xb2\n\x80\xcf;\x17\xf1b\x1f\x1b?\xf7\x9b\x88/P\xde\xf3\x8965\u026d\xc5\u07a0\x00\x00\u07d4T.\x80\x96\xba\xfb\x88\x16&\x06\x00.\x8c\x8a>\u0458\x14\xae\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T1\v:\xa8\x87\x03\xa7%\u07e5}\xe6\xe6F\x93Qd\x80,\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4T1\xb1\u0447Q\xb9\x8f\xc9\u220a\xc7u\x9f\x155\xa2\xdbG\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T1\xcaB~ae\xa6D\xba\xe3&\xbd\tu\n\x17\x8ce\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T5\xc6\xc1y3\x17\xd3,\xe1;\xbaLO\xfe\xb9s\xb7\x8a\u0709\r\x8ek\x1c\x12\x85\xef\x00\x00\xe0\x94T6)\xc9\\\xde\xf4(\xad7\xd4S\u02958\xa9\xf9\t\x00\xac\x8a\t(\x96R\x9b\xad\u0708\x00\x00\u07d4T9\x1bM\x17mGl\xea\x16N_\xb55\u0197\x00\xcb%5\x89\x05l\xd5_\xc6M\xfe\x00\x00\xe0\x94T:\x8c\x0e\xfb\x8b\xcd\x15\xc5C\u29a4\xf8\aYv1\xad\xef\x8a\x01?\x80\xe7\xe1O-D\x00\x00\u07d4T?\x8cgN$b\xd8\xd5\u06a0\xe8\x01\x95\xa8p\x8e\x11\xa2\x9e\x89\x03wX\x83;:z\x00\x00\xe0\x94TK[5\x1d\x1b\xc8.\x92\x97C\x99H\xcfHa\xda\u026e\x11\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4TM\xdaB\x1d\xc1\xebs\xbb$\xe3\xe5j$\x80\x13\xb8|\x0fD\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4TW\\1\x14u\x1e\x14o\xfe\u00c7nE\xf2\x0e\xe8AJ\u07ba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4T\xb4B\x9b\x18/\x03w\xbe~bi9\xc5\xdbd@\xf7]z\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4T\xbc\xb8\xe7\xf7<\xda=s\xf4\u04cb-\bG\xe6\x00\xba\r\xf8\x89:pAX\x82\xdf\x18\x00\x00\u07d4T\xc9>\x03\xa9\xb2\xe8\xe4\xc3g(5\xa9\xeev\xf9a[\xc1N\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4T\u0388'YV\xde\xf5\xf9E\x8e;\x95\xde\xca\xcdH@!\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T\xdb^\x06\xb4\x81]1\xcbV\xa8q\x9b\xa3:\xf2\xd7>rR\x89$R\x1e*0\x17\xb8\x00\x00\xe0\x94T\xe0\x12\x83\u030b8E8\xdddgp\xb3W\xc9`\xd6\xca\u034a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4T\xecs\x00\xb8\x1a\xc8C3\xed\x1b\x03<\xd5\u05e39r\xe24\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4T\xfe\xbc\xce \xfez\x90\x98\xa7U\xbd\x90\x98\x86\x02\xa4\x8c\b\x9e\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4U\n\xad\xae\x12!\xb0z\xfe\xa3\x9f\xba.\xd6.\x05\u5df5\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4U\f0o\x81\xef]\x95\x80\xc0l\xb1\xab \x1b\x95\xc7H\xa6\x91\x89$\x17\xd4\xc4p\xbf\x14\x00\x00\xe0\x94U\x19\x99\xdd\xd2\x05V3'\xb9\xb50xZ\xcf\xf9\xbcs\xa4\xba\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4U\x1ew\x84w\x8e\xf8\xe0H\xe4\x95\xdfI\xf2aO\x84\xa4\xf1\u0709 \x86\xac5\x10R`\x00\x00\xe0\x94U)\x83\na\xc1\xf1<\x19~U\v\xed\xdf\u05bd\x19\\\x9d\x02\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4U)\x87\xf0e\x1b\x91[.\x1eS(\xc1!\x96\rK\xddj\xf4\x89a\t=|,m8\x00\x00\u07d4U;k\x1cW\x05\x0e\x88\xcf\f1\x06{\x8dL\xd1\xff\x80\xcb\t\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4U?7\xd9$fU\x0e\x9f\xd7u\xaet6-\xf00\x17\x912\x89lk\x93[\x8b\xbd@\x00\x00\u07d4UC6\xeeN\xa1U\xf9\xf2O\x87\xbc\xa9\xcar\xe2S\xe1,\u0489\x05k\xc7^-c\x10\x00\x00\u0794UC\xddm\x16\x9e\xec\x8a!;\xbfz\x8a\xf9\xff\xd1]O\xf7Y\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4UG\xfd\xb4\xae\x11\x95>\x01)+x\a\xfa\x92#\xd0\xe4`j\x89\x05]\x11}\xcb\x1d&\x00\x00\u07d4UR\xf4\xb3\xed>\x1d\xa7\x9a/x\xbb\x13\xe8\xaeZh\xa9\xdf;\x8965\u026d\xc5\u07a0\x00\x00\u07d4U\\\xa9\xf0\\\xc14\xabT\xae\x9b\xea\x1c?\xf8z\xa8Q\x98\u0289\x05k\xc7^-c\x10\x00\x00\xe0\x94U]\x8d<\xe1y\x8a\u0290'T\xf1d\xb8\xbe*\x022\x9cl\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4U]\xf1\x93\x90\xc1m\x01)\x87r\xba\xe8\xbc:\x11R\x19\x9c\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4U^\xbe\x84\u06a4+\xa2V\xeax\x91\x05\xce\u0136\x93\xf1/\x18\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94U\u007f^e\xe0\xda3\x99\x82\x19\xadN\x99W\x05E\xb2\xa9\xd5\x11\x8a\x02U\x9c\xbb\x98XB@\x00\x00\u07d4U\x83` h\x83\xdd\x1bmJYc\x9eV)\xd0\xf0\xc6u\u0409lk\x93[\x8b\xbd@\x00\x00\u07d4U\x84B0P\xe3\xc2\x05\x1f\v\xbd\x8fD\xbdm\xbc'\xec\xb6,\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4U\x85)CI)p\xf8\xd6)\xa1Sf\xcd\xda\x06\xa9OE\x13\x89lk\x93[\x8b\xbd@\x00\x00\u0794U\x86d\x86\xec\x16\x8fy\xdb\xe0\u1af1\x88d\u0649\x91\xae,\x88\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4U\x8cTd\x9a\x8an\x94r+\xd6\xd2\x1d\x14qOqx\x054\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\x91\x940O\x14\xb1\xb9:\xfeDO\x06$\xe0S\xc2:\x00\t\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4U\x93\xc9\u0536ds\x0f\xd9<\xa6\x01Q\xc2\\.\xae\xd9<;\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4U\x97\x06\xc32\xd2\ay\xc4_\x8am\x04ji\x91Y\xb7I!\x89\x14\x9bD.\x85\xa3\u03c0\x00\u07d4U\x98\xb3\xa7\x9aH\xf3+\x1f_\xc9\x15\xb8{d]\x80]\x1a\xfe\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4U\xa3\xdfW\xb7\xaa\xec\x16\xa1b\xfdS\x16\xf3[\xec\b(!\u03c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4U\xa4\xca\xc0\u02cbX-\x9f\xef8\xc5\xc9\xff\xf9\xbdS\t=\x1f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4U\xa6\x1b\x10\x94\x80\xb5\xb2\xc4\xfc\xfd\xef\x92\xd9\x05\x84\x16\f\r5\x89\x02lVM+S\xf6\x00\x00\u07d4U\xaa]1>\xbb\bM\xa0\xe7\x80\x10\x91\u2792\xc5\xde\u00ea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\xab\x99\xb0\xe0\xe5]{\xb8t\xb7\xcf\xe84\xdec\x1c\x97\xec#\x897\xe9\x8c\xe3h\x99\xe4\x00\x00\u07d4U\xaf\t/\x94\xbajy\x91\x8b\f\xf99\xea\xb3\xf0\x1b?Q\u01c9\b \xd5\xe3\x95v\x12\x00\x00\u07d4U\xc5dfAf\xa1\xed\xf3\x91>\x01i\xf1\xcdE\x1f\xdb]\f\x89\x82\x17\xeaIP\x8el\x00\x00\xe0\x94U\xcaj\xbey\xea$\x97\xf4o\u06f804`\x10\xfeF\x9c\xbe\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4U\xca\xffK\xba\x04\xd2 \u0265\xd2\x01\x86r\xec\x85\xe3\x1e\xf8>\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\xd0W\xbc\xc0K\xd0\xf4\xaf\x96BQ:\xa5\t\v\xb3\xff\x93\xfe\x89;\xfeE,\x8e\xddL\x00\x00\u07d4U\xd4.\xb4\x95\xbfF\xa64\x99{_.\xa3b\x81I\x18\u2c09\x05\xc0\xd2e\xb5\xb2\xa8\x00\x00\u07d4U\u069d\xcd\xcaa\xcb\xfe\x1f\x13<{\xce\xfc\x86{\x9c\x81\"\xf9\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4U\xe2 \x87bb\xc2\x18\xafOVxG\x98\xc7\xe5]\xa0\x9e\x91\x89\a=\x99\xc1VE\xd3\x00\x00\u07d4U\xfd\b\u0440d\xbd ,\x0e\xc3\xd2\xcc\xe0\xce\v\x9d\x16\x9cM\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4V\x00s\nU\xf6\xb2\x0e\xbd$\x81\x1f\xaa=\xe9m\x16b\xab\xab\x89e\xea=\xb7UF`\x00\x00\u07d4V\x03$\x1e\xb8\xf0\x8fr\x1e4\x8c\x9d\x9a\xd9/H\u342a$\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4V\x056yJ\x9e+\x00I\xd1\x023\xc4\x1a\xdc_A\x8a&J\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\aY\x00Y\xa9\xfe\xc1\x88\x11I\xa4K6\x94\x9a\xef\x85\xd5`\x89lk\x93[\x8b\xbd@\x00\x00\u07d4V\v\xec\xdfR\xb7\x1f=\x88'\xd9'a\x0f\x1a\x98\x0f3qo\x89\x17GMp_V\u0400\x00\xe0\x94V\r\xa3~\x95m\x86/\x81\xa7_\u0540\xa7\x13\\\x1b$cR\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94V\x0f\xc0\x8d\a\x9f\x04~\xd8\xd7\xdfuU\x1a\xa55\x01\xf5p\x13\x8a\x01\x9b\xff/\xf5yh\xc0\x00\x00\u07d4V\x1b\xe9)\x9b>k>c\xb7\x9b\t\x16\x9d\x1a\x94\x8a\xe6\xdb\x01\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94V \xe3\xedy-/\x185\xfe_UA}Q\x11F\fj\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4V \xf4m\x14Q\xc25=bC\xa5\u0534'\x13\v\xe2\xd4\a\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94V!\x05\xe8+\t\x975\xdeI\xf6&\x92\u0307\xcd8\xa8\xed\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94V*\x8d\u02fe\xee\xf7\xb3`h]'0;\u059e\tJ\xcc\xf6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4V+\xce\u04ca\xb2\xabl\b\x0f;\x05A\xb8Enp\x82K?\x89\"\xca5\x87\xcfN\xb0\x00\x00\xe0\x94V+\xe9Z\xba\x17\xc57\x1f\u2e82\x87\x99\xb1\xf5]!w\u058a\b\x16\xd3~\x87\xb9\xd1\xe0\x00\x00\u07d4V/\x16\u05da\xbf\xce\u00d4>4\xb2\x0f\x05\xf9{\xdf\u0366\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4V7=\xaa\xb4c\x16\xfd~\x15v\xc6\x1ej\xff\xcbeY\xdd\u05c9\v\xacq]\x14l\x9e\x00\x00\u07d4V9v8\xbb<\xeb\xf1\xf6 byK^\xb9B\xf9\x16\x17\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4V:\x03\xab\x9cV\xb6\x00\xf6\xd2[f\f!\xe1c5Qzu\x8965\u026d\xc5\u07a0\x00\x00\u07d4V<\xb8\x80<\x1d2\xa2['\xb6A\x14\x85+\xd0M\x9c \u0349\v\x14\x9e\xad\n\xd9\xd8\x00\x00\u07d4VXc\x91\x04\fW\xee\xc6\xf5\xaf\xfd\x8c\u052b\xde\x10\xb5\n\u0309\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Vl\x10\xd68\u8e0bG\xd6\xe6\xa4\x14Iz\xfd\xd0\x06\x00\u0509\x05k9Bc\xa4\f\x00\x00\u07d4Vl(\xe3L8\b\xd9vo\xe8B\x1e\xbfO+\x1cO}w\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4V\x8d\xf3\x18Vi\x9b\xb5\xac\xfc\x1f\xe1\u0580\u07d9`\xcaCY\x89J\xcfUR\xf3\xb2I\x80\x00\u07d4V\x91\xdd/gE\xf2\x0e\"\xd2\xe1\u0479U\xaa)\x03\xd6VV\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4V\xa1\xd6\r@\xf5\u007f0\x8e\xeb\xf0\x87\xde\xe3\xb3\u007f\x1e|,\xba\x89>\u072e\xc8-\x06\xf8\x00\x00\u07d4V\xac \xd6;\xd8\x03Y\\\xec\x03m\xa7\xed\x1d\xc6n\n\x9e\a\x89\x03w*S\xcc\xdce\x80\x00\u07d4V\xb6\xc2=\xd2\uc434r\x8f;\xb2\xe7d\xc3\xc5\f\x85\xf1D\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\xdf\x05\xba\xd4l?\x00\xaeGn\xcf\x01{\xb8\xc8w8?\xf1\x89\n\xb1]\xaa\xefp@\x00\x00\u07d4V\xee\x19\u007fK\xbf\x9f\x1b\x06b\xe4\x1c+\xbd\x9a\xa1\xf7\x99\xe8F\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\xf4\x93\xa3\xd1\b\xaa\xa2\u044d\x98\x92/\x8e\xfe\x16b\u03f7=\x89m\x81!\xa1\x94\xd1\x10\x00\x00\u07d4V\xfc\x1a{\xad@G#|\xe1\x16\x14b\x96#\x8e\a\x8f\x93\xad\x89\t\xa6?\b\xeac\x88\x00\x00\u07d4V\xfe\xbf\x9e\x10\x03\xaf\x15\xb1\xbdI\a\xec\b\x9aJ\x1b\x91\xd2h\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4W\x17\u0313\x01Q\x1dJ\x81\xb9\xf5\x83\x14\x8b\xee\xd3\xd3\u0303\t\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4W\x17\xf2\xd8\xf1\x8f\xfc\xc0\xe5\xfe$}:B\x19\x03|:d\x9c\x89\u063beI\xb0+\xb8\x00\x00\u07d4W\x19P\xea,\x90\xc1B}\x93\x9da\xb4\xf2\xdeL\xf1\u03ff\xb0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4W\x19\xf4\x9br\r\xa6\x88V\xf4\xb9\xe7\b\xf2VE\xbd\xbcKA\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4W*\xc1\xab\xa0\xde#\xaeA\xa7\xca\xe1\xdc\bB\u062b\xfc\x10;\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94W-\xd8\xcd?\xe3\x99\xd1\xd0\xec(\x121\xb7\xce\xfc \xb9\u4eca\x023\xc8\xfeBp>\x80\x00\x00\xe0\x94WI!\x83\x8c\xc7}l\x98\xb1}\x90::\xe0\xee\r\xa9[\u040a\vS(\x17\x8a\xd0\xf2\xa0\x00\x00\u07d4WJ\xd95S\x90\u421e\xf4*\xcd\x13\x8b*'\xe7\x8c\x00\xae\x89Tg\xb72\xa9\x134\x00\x00\u07d4WM\xe1\xb3\xf3\x8d\x91XF\xae7\x18VJZ\xda \xc2\xf3\xed\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94W\\\x00\u0081\x82\x10\u0085U\xa0\xff)\x01\x02\x89\xd3\xf8#\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94Ws\xb6\x02g!\xa1\xdd\x04\xb7\x82\x8c\xd6+Y\x1b\xfb4SL\x8a\x05\xb7\xacES\xdez\xe0\x00\x00\xe0\x94WwD\x1c\x83\xe0?\v\xe8\xdd4\v\xdechP\x84|b\v\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Wx\xff\u071b\x94\u0165\x9e\"N\xb9e\xb6\u0790\xf2\"\xd1p\x89\x12-\u007f\xf3f\x03\xfc\x00\x00\u07d4Wz\xee\xe8\u053c\b\xfc\x97\xab\x15n\xd5\u007f\xb9p\x92Sf\xbe\x89\x12\r\xf1\x14rX\xbf\x00\x00\u07d4W{-\a\xe9\xcfRJ\x18\u04c9\x15Vak\x96\x06g\x00\x00\u07d4W\xd5\xfd\x0e=0I3\x0f\xfc\xdc\xd0 Ei\x17e{\xa2\u0689k\xf2\x01\x95\xf5T\xd4\x00\x00\u07d4W\u0754q\xcb\xfa&'\t\xf5\U00106f37t\xc5\xf5'\xb8\xf8\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4W\xdf#\xbe\xbd\xc6^\xb7_\ub732\xfa\xd1\xc0si++\xaf\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4X\x00\u03410\x83\x9e\x94I]-\x84\x15\xa8\xea,\x90\xe0\xc5\u02c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94X\x03\xe6\x8b4\xda\x12\x1a\xef\b\xb6\x02\xba\u06ef\xb4\xd1$\x81\u028a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\xe0\x94X\x16\xc2hww\xb6\xd7\u04a2C-Y\xa4\x1f\xa0Y\xe3\xa4\x06\x8a\x1cO\xe4:\xdb\n^\x90\x00\x00\u07d4X\x1a:\xf2\x97\xef\xa4Cj)\xaf\x00r\x92\x9a\xbf\x98&\xf5\x8b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\x1b\x9f\xd6\xea\xe3r\xf3P\x1fB\xeb\x96\x19\xee\xc8 \xb7\x8a\x84\x8a\x04+\xe2\xc0\f\xa5;\x8d\x80\x00\u07d4X\x1b\xdf\x1b\xb2v\xdb\u0746\xae\xdc\xdb9z\x01\xef\xc0\xe0\f[\x8965\u026d\xc5\u07a0\x00\x00\u07d4X\x1f4\xb5#\xe5\xb4\x1c\t\xc8|)\x8e)\x9c\xbc\x0e)\xd0f\x89=X3\xaa\xfd9u\x80\x00\xe0\x94X$\xa7\xe2(8'q40\x8c_KP\u06b6^C\xbb1\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4X+pf\x9c\x97\xaa\xb7\u0581H\xd8\xd4\xe9\x04\x11\xe2\x81\rV\x8965f3\xeb\xd8\xea\x00\x00\u07d4X.|\xc4o\x1d{Nn\x9d\x95\x86\x8b\xfd7\x05s\x17\x8fL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4X>\x83\xbaU\xe6~\x13\xe0\xe7o\x83\x92\xd8s\xcd!\xfb\xf7\x98\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Xi\xfb\x86}q\xf18\u007f\x86;i\x8d\t\xfd\xfb\x87\u011b\\\x89\u01bb\xf8X\xb3\x16\b\x00\x00\u07d4X}hI\xb1h\xf6\xc33+z\xba\xe7\xeblB\xc3\u007fH\xbf\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4X\x87\xdcj3\xdf\xedZ\xc1\xed\xef\xe3^\xf9\x1a!b1\xac\x96\x89\r\x8drkqw\xa8\x00\x00\xe0\x94X\x8e\u0650\xa2\xaf\xf4J\x94\x10]X\xc3\x05%w5\xc8h\xac\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4X\xae-\xdc_L\x8a\u0697\xe0l\x00\x86\x17\x17g\xc4#\xf5\u05c9WG=\x05\u06ba\xe8\x00\x00\u07d4X\xae\xd6gJ\xff\xd9\xf6B3'*W\x8d\xd98k\x99\xc2c\x89\xb8Pz\x82\a( \x00\x00\xe0\x94X\xb8\b\xa6[Q\xe63\x89i\xaf\xb9^\xc7\a5\xe4Q\xd5&\x8a\bxK\xc1\xb9\x83z8\x00\x00\u07d4X\xb8\xae\x8fc\xef5\xed\ab\xf0\xb6#=J\xc1Nd\xb6M\x89lk\x93[\x8b\xbd@\x00\x00\u07d4X\xba\x15ie\x0e[\xbb\xb2\x1d5\xd3\xe1u\xc0\u05b0\xc6Q\xa9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4X\xc5U\xbc)<\xdb\x16\xc66.\xd9z\xe9U\v\x92\xea\x18\x0e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4X\xc6P\xce\xd4\v\xb6VA\xb8\xe8\xa9$\xa09\xde\xf4hT\u07c9\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4X\xc9\aT\xd2\xf2\n\x1c\xb1\xdd3\x06%\xe0KE\xfaa\x9d\\\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\xe2\xf1\x12#\xfc\x827\xf6\x9d\x99\xc6(\x9c\x14\x8c\x06\x04\xf7B\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4X\xe5T\xaf=\x87b\x96 \xdaa\xd58\xc7\xf5\xb4\xb5LJ\xfe\x89FP\x9diE4r\x80\x00\u07d4X\xe5\xc9\xe3D\xc8\x06e\r\xac\xfc\x90M3\xed\xbaQ\a\xb0\u0789\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4X\xe6a\u043as\xd6\xcf$\t\x9aUb\xb8\b\xf7\xb3g;h\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\xf0[&%`P<\xa7a\xc6\x18\x90\xa4\x03_Lsr\x80\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4X\xfb\x94sd\xe7iWe6\x1e\xbb\x1e\x80\x1f\xfb\x8b\x95\xe6\u0409\n\u05ce\xbcZ\xc6 \x00\x00\u07d4Y\x01\x81\xd4E\x00{\u0407Z\xaf\x06\x1c\x8dQ\x159\x00\x83j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y\x02\xe4J\xf7i\xa8rF\xa2\x1e\a\x9c\b\xbf6\xb0n\xfe\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4Y\n\xcb\xda7)\f\r>\xc8O\xc2\x00\rv\x97\xf9\xa4\xb1]\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94Y\f\xcbY\x11\xcfx\xf6\xf6\"\xf55\xc4t7_J\x12\xcf\u03ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y\x10\x10m\xeb\u0491\xa1\u0340\xb0\xfb\xbb\x8d\x8d\x9e\x93\xa7\xcc\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y\x16\x17I\xfe\xdc\xf1\xc7!\xf2 -\x13\xad\xe2\xab\xcfF\v=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Y\x1b\xef1q\xd1\u0155w\x17\xa4\xe9\x8d\x17\xeb\x14,!NV\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y <\xc3u\x99\xb6H1*|\xc9\xe0m\xac\xb5\x89\xa9\xaej\x89\b\x0fyq\xb6@\x0e\x80\x00\u07d4Y&\x81q\xb83\xe0\xaa\x13\xc5KR\xcc\xc0B.O\xa0:\ub262\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94Y'w&\x1e;\xd8R\u010e\u0295\xb3\xa4L[\u007f-B,\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y0Dg\x0f\xae\xff\x00\xa5[Z\xe0Q\xeb{\xe8p\xb1\x16\x94\x89\a?u\u0460\x85\xba\x00\x00\xe0\x94Y;E\xa1\x86J\xc5\xc7\xe8\xf0\u02ae\xba\r\x87<\xd5\xd1\x13\xb2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Y_^\xdajV\xf1N%\xe0\xc6\xf3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Z\x1a3ib\xd6\xe0\xc601\u0303\u01a5\u01a6\xf4G\x8e\u02c965\u026d\xc5\u07a0\x00\x00\u07d4Z\x1d--\x1dR\x03\x04\xb6 \x88IW\x047\xeb0\x91\xbb\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Z&s1\xfa\xcb&-\xaa\xec\xd9\xddc\xa9p\f_RY\u07c9\x05k\xc7^-c\x10\x00\x00\xe0\x94Z(WU9\x1e\x91NX\x02_\xaaH\xcch_O\xd4\xf5\xb8\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4Z)\x16\xb8\xd2\xe8\xcc\x12\xe2\a\xabFMC>#p\xd8#\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4Z+\x1c\x85:\xeb(\xc4U9\xafv\xa0\n\xc2\u0628$(\x96\x89\x01Z\xf1\u05cbX\xc4\x00\x00\u07d4Z-\xaa\xb2\\1\xa6\x1a\x92\xa4\xc8,\x99%\xa1\xd2\xefXX^\x89\f8\r\xa9\u01d5\f\x00\x00\u07d4Z0\xfe\xac7\xac\x9fr\u05f4\xaf\x0f+\xc79R\xc7O\xd5\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4ZTh\xfa\\\xa2&\xc7S.\xcf\x06\xe1\xbc\x1cE\"]~\u0249g\x8a\x93 b\xe4\x18\x00\x00\u07d4ZVR\x857JI\xee\xddPL\x95}Q\bt\xd0\x04U\xbc\x89\x05k\xc7^-c\x10\x00\x00\u07d4Z^\xe8\xe9\xbb\x0e\x8a\xb2\xfe\xcbK3\u0494x\xbeP\xbb\xd4K\x89*\x11)\u0413g \x00\x00\xe0\x94Z_\x85\b\xda\x0e\xbe\xbb\x90\xbe\x903\xbdM\x9e'A\x05\xae\x00\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4Z`q\xbc\xeb\xfc\xbaJ\xb5\u007fM\xb9o\u01e6\x8b\xec\xe2\xba[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z`\xc9$\x16(s\xfc~\xa4\xda\u007f\x97.5\x01g7`1\x89\x04\x87\xf2w\xa8\x85y\x80\x00\u07d4Zf\x86\xb0\xf1~\a\xed\xfcY\xb7Y\xc7}[\xef\x16M8y\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4Zp\x10o \xd6?\x87Re\xe4\x8e\r5\xf0\x0e\x17\xd0+\u0249\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794Zt\xbab\xe7\xc8\x1a4t\xe2}\x89O\xed3\xdd$\xad\x95\xfe\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94Zw5\x00}p\xb0hD\u0699\x01\xcd\xfa\xdb\x11\xa2X,/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Z\x82\xf9l\u0537\xe2\xd9=\x10\xf3\x18]\xc8\xf4=Ku\xaai\x89lc?\xba\xb9\x8c\x04\x00\x00\u07d4Z\x87\xf04\xe6\xf6\x8fNt\xff\xe6\fd\x81\x946\x03l\xf7\u05c9\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94Z\x89\x11U\xf5\x0eB\aCt\xc79\xba\xad\xf7\xdf&Q\x15:\x8a\x01\x02\xdao\xd0\xf7:<\x00\x00\u07d4Z\x9c\x8bi\xfcaMiVI\x99\xb0\r\xcbB\xdbg\xf9~\x90\x89\xb9\xe6\x15\xab\xad:w\x80\x00\xe0\x94Z\xaf\x1c1%Jn\x00_\xba\u007fZ\xb0\xecy\xd7\xfc+c\x0e\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4Z\xb1\xa5aSH\x00\x1c|w]\xc7WHf\x9b\x8b\xe4\xde\x14\x89%jr\xfb)\xe6\x9c\x00\x00\xe1\x94Z\xbf\xec%\xf7L\u06047c\x1aw1\x90i2wcV\xf9\x8b\t\xd8<\xc0\u07e1\x11w\xff\x80\x00\u07d4Z\u0090\x8b\x0f9\x8c\r\xf5\xba\xc2\xcb\x13\xcas\x14\xfb\xa8\xfa=\x89\n\xd4\xc81j\v\f\x00\x00\xe0\x94Z\u025a\u05c1j\xe9\x02\x0f\xf8\xad\xf7\x9f\xa9\x86\x9b|\xeaf\x01\x8a\x04ri\x8bA;C \x00\x00\u07d4Z\xd1,^\xd4\xfa\x82~!P\u03e0\u058c\n\xa3{\x17i\xb8\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94Z\xd5\xe4 uV\x13\x88o5\xaaV\xac@>\xeb\xdf\xe4\xb0\u040a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4Z\xdew\xfd\x81\xc2\\\n\xf7\x13\xb1\a\x02v\x8c\x1e\xb2\xf9u\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Z\xe6N\x85;\xa0\xa5\x12\x82\u02cd\xb5.Aa^|\x9fs?\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z\xed\x0el\xfe\x95\xf9\u0580\xc7dr\xa8\x1a+h\n\u007f\x93\xe2\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4Z\xef\x16\xa2&\xddh\a\x1f$\x83\xe1\xdaBY\x83\x19\xf6\x9b,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z\xf4j%\xac\t\xcbsakS\xb1O\xb4/\xf0\xa5\x1c\u0772\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Z\xf7\xc0r\xb2\u016c\xd7\x1cv\xad\xdc\xceS\\\xf7\xf8\xf95\x85\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94Z\xfd\xa9@\\\x8e\x976QEt\u0692\x8d\xe6tV\x01\t\x18\x8a\x01E\xb8\xb0#\x9aF\x92\x00\x00\u07d4[\x06\xd1\xe6\x93\f\x10Ti+y\xe3\xdb\xe6\xec\xceS\x96d \x89\v\"\u007fc\xbe\x81<\x00\x00\u07d4[%\xca\xe8m\xca\xfa*`\xe7r61\xfc_\xa4\x9c\x1a\xd8}\x89\x87\fXQ\x0e\x85 \x00\x00\u07d4[(|~sB\x99\xe7'bo\x93\xfb\x11\x87\xa6\rPW\xfe\x89\x05|\xd94\xa9\x14\xcb\x00\x00\u07d4[)\f\x01\x96|\x81.M\xc4\xc9\v\x17L\x1b@\x15\xba\xe7\x1e\x89\b \xeb4\x8dR\xb9\x00\x00\u07d4[+d\xe9\xc0X\u30a8\xb2\x99\"N\xec\xaa\x16\xe0\x9c\x8d\x92\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\xe0\x94[./\x16\x18U.\xab\r\xb9\x8a\xddUc|)Q\xf1\xfb\x19\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4[0`\x8cg\x8e\x1a\xc4d\xa8\x99L;3\xe5\xcd\xf3Iq\x12\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4[36\x96\xe0L\xca\x16\x92\xe7\x19\x86W\x9c\x92\rk)\x16\xf9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94[C\rw\x96\x96\xa3e?\xc6\x0et\xfb\u02ec\xf6\xb9\u00ba\xf1\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4[Cse\xae:\x9a/\xf9|h\xe6\xf9\nv \x18\x8c}\x19\x89l\x87T\xc8\xf3\f\b\x00\x00\u07d4[I\xaf\xcduDx8\xf6\xe7\xce\u068d!w}O\xc1\xc3\xc0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4[L\f`\xf1\x0e\u0489K\xdbB\xd9\xdd\x1d!\x05\x87\x81\n\r\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4[N\xa1m\xb6\x80\x9b\x03R\u0536\xe8\x1c9\x13\xf7jQ\xbb2\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4[[\xe0\xd8\xc6rv\xba\xab\xd8\xed\xb3\rH\xeaud\v\x8b)\x89,\xb1\xf5_\xb7\xbe\x10\x00\x00\u07d4[]Qp)2\x15b\x11\x1bC\bm\v\x045\x91\x10\x9ap\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\xe0\x94[]\x8c\x8e\xedl\x85\xac!Va\xde\x02fv\x82?\xaa\n\f\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4[mU\xf6q)g@\\e\x91)\xf4\xb1\xde\t\xac\xf2\xcb{\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4[p\u011c\u024b=\xf3\xfb\xe2\xb1Y\u007f\\\x1bcG\xa3\x88\xb7\x894\x95tD\xb8@\xe8\x00\x00\u07d4[sn\xb1\x83Sb\x9b\u0796v\xda\xdd\x16P4\xce^\xcch\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4[u\x9f\xa1\x10\xa3\x1c\x88F\x9fT\xd4K\xa3\x03\xd5}\xd3\xe1\x0f\x89[F\xdd/\x0e\xa3\xb8\x00\x00\u07d4[w\x84\xca\xea\x01y\x9c\xa3\x02'\x82vg\xce |\\\xbcv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4[x\xec\xa2\u007f\xbd\xeao&\xbe\xfb\xa8\x97+)^x\x146K\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94[\x80\v\xfd\x1b>\u0525}\x87Z\xed&\xd4/\x1aw\b\xd7*\x8a\x01Z\x82\xd1\u057b\x88\xe0\x00\x00\u07d4[\x85\xe6\x0e*\xf0TO/\x01\xc6N 2\x90\x0e\xbd8\xa3\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4[\xa2\xc6\xc3]\xfa\xec)h&Y\x19\x04\xd5DFJ\xea\xbd^\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94[\xafmt\x96 \x80>\x83H\xaf7\x10\xe5\xc4\xfb\xf2\x0f\u0214\x8a\x01\x0f@\x02a]\xfe\x90\x00\x00\u07d4[\xc1\xf9U\a\xb1\x01\x86B\xe4\\\xd9\xc0\xe2'3\xb9\xb1\xa3&\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94[\xd25GG\u007fm\t\u05f2\xa0\x05\xc5\xeee\fQ\fV\u05ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4[\xd2J\xac6\x12\xb2\f`\x9e\xb4gy\xbf\x95i\x84\a\xc5|\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4[\u0586-Q}M\xe4U\x9dN\xec\n\x06\xca\xd0^/\x94n\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4[\xe0EQ*\x02n?\x1c\xeb\xfdZ~\xc0\xcf\xc3o-\xc1k\x89\x06\x81U\xa46v\xe0\x00\x00\xe0\x94[\xf9\xf2\"nZ\xea\xcf\x1d\x80\xae\nY\xc6\xe3\x808\xbc\x8d\xb5\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4[\xfa\xfe\x97\xb1\xdd\x1dq+\xe8mA\xdfy\x89SE\x87Z\x87\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\\\x0f.Q7\x8fk\r{\xabas1X\vn9\xad<\xa5\x8a\x02\bj\xc3Q\x05&\x00\x00\x00\u07d4\\)\xf9\xe9\xa5#\xc1\xf8f\x94H\xb5\\H\xcb\xd4|%\xe6\x10\x894F\xa0\xda\xd0L\xb0\x00\x00\xe0\x94\\0\x8b\xacHW\xd3;\xae\xa0t\xf3\x95m6!\xd9\xfa(\xe1\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\\1*V\u01c4\xb1\"\t\x9bvM\x05\x9c!\xec\xe9^\x84\u0289\x05&c\u032b\x1e\x1c\x00\x00\u07d4\\1\x99m\xca\xc0\x15\xf9\xbe\x98[a\x1fF\x870\xef$M\x90\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\\24W\xe1\x87v\x1a\x82v\xe3Y\xb7\xb7\xaf?;n=\xf6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\\<\x1cd[\x91uC\x11;>l\x1c\x05M\xa1\xfet+\x9a\x89+^:\xf1k\x18\x80\x00\x00\u0794\\=\x19D\x1d\x19l\xb4Cf \xfc\xad\u007f\xbby\xb2\x9ex\x88\xc6s\xce<@\x16\x00\x00\u07d4\\?V\u007f\xaf\xf7\xba\u0475\x12\x00\"\xe8\xcb\u02a8+I\x17\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\\Ch\x91\x8a\xced\t\u01de\u0280\u036a\xe49\x1d+bN\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\\FA\x97y\x1c\x8a=\xa3\xc9%Co'z\xb1;\xf2\xfa\xa2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\\H\x81\x16\\\xb4+\xb8.\x979l\x8e\xf4J\xdb\xf1s\xfb\x99\x89\x05\xfe\xe2\"\x04\x1e4\x00\x00\xe0\x94\\H\x92\x90z\a \xdfo\xd3A>c\xffv}k9\x80#\x8a\x02\xcb\x00\x9f\u04f5y\x0f\x80\x00\u07d4\\O$\xe9\x94\ud3c5\x0e\xa7\x81\x8fG\x1c\x8f\xac;\xcf\x04R\x89]\x80h\x8d\x9e1\xc0\x00\x00\u07d4\\T\x19V\\:\xadNqN\a92\x8e5!\u024f\x05\u0309\x1c\x9fx\u0489>@\x00\x00\u07d4\\a6\xe2\x18\xde\na\xa17\xb2\xb3\x96-*a\x12\xb8\t\u05c9\x0f\xf3\u06f6_\xf4\x86\x80\x00\xe0\x94\\a\xaby\xb4\b\xdd2)\xf6bY7\x05\xd7/\x1e\x14{\xb8\x8a\x04\xd0$=4\x98\u0344\x00\x00\u07d4\\m\x04\x1d\xa7\xafD\x87\xb9\xdcH\xe8\xe1\xf6\af\u0425m\xbc\x89O\a\n\x00>\x9ct\x00\x00\u07d4\\o6\xaf\x90\xab\x1aeln\xc8\xc7\xd5!Q'b\xbb\xa3\xe1\x89lh\xcc\u041b\x02,\x00\x00\u07d4\\{\x9e\u01e2C\x8d\x1eD*\x86\x0f\x8a\x02\x1e\x18\x99\xf07z\xea\x00\x00\u07d4\\\xcc\xf1P\x8b\xfd5\xc2\x050\xaad%\x00\xc1\r\xeee\xea\xed\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\\\xcer\xd0h\xc7\xc3\xf5[\x1d(\x19T^w1|\xae\x82@\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\\\xd0\xe4u\xb5D!\xbd\xfc\f\x12\xea\x8e\b+\u05e5\xaf\nj\x89\x032\xca\x1bg\x94\f\x00\x00\u07d4\\\u0548\xa1N\xc6H\xcc\xf6G)\xf9\x16z\xa7\xbf\x8b\xe6\xeb=\x8965\u026d\xc5\u07a0\x00\x00\u07d4\\\u062f`\xdee\xf2M\xc3\xceW0\xba\x92e0\"\xdcYc\x89a\t=|,m8\x00\x00\u07d4\\\xdcG\b\xf1O@\xdc\xc1Zy_}\xc8\xcb\v\u007f\xaa\x9en\x89\x1d\x1c_>\xda \xc4\x00\x00\u07d4\\\u0d86,\u0391b\xe8~\bI\xe3\x87\xcb]\xf4\xf9\x11\x8c\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\\\xe2\xe7\u03aa\xa1\x8a\xf0\xf8\xaa\xfa\u007f\xba\xd7L\u021e<\xd46\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\\\xe4@h\xb8\xf4\xa3\xfey\x9ej\x83\x11\xdb\xfd\xed\xa2\x9d\xee\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u0794\\\xeb\xe3\v*\x95\xf4\xae\xfd\xa6ee\x1d\xc0\xcf~\xf5u\x81\x99\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\\\xf1\x8f\xa7\u0227\xc0\xa2\xb3\xd5\xef\u0459\x0fd\xdd\xc5i$,\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\\\xf4N\x10T\reqd#\xb1\xbc\xb5B\xd2\x1f\xf8:\x94\u034a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\\\xf8\xc0>\xb3\xe8r\xe5\x0f|\xfd\f/\x8d;?,\xb5\x18:\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\\\xfa\x8dV\x85ue\x8c\xa4\xc1\xa5\x93\xacL]\x0eD\xc6\aE\x89\x0f\xc6o\xae7F\xac\x00\x00\u07d4\\\xfa\x98w\xf7\x19\u01dd\x9eIJ\b\xd1\xe4\x1c\xf1\x03\xfc\x87\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4]\x1d\xc38{G\xb8E\x1eU\x10l\f\xc6}m\xc7+\u007f\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4]#\x1ap\xc1\xdf\xeb6\n\xbd\x97\xf6\x16\xe2\xd1\r9\xf3\u02b5\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4]$\xbd\xbc\x1cG\xf0\xeb\x83\xd1(\xca\xe4\x8a\xc3\xf4\xb5\x02bt\a\xda'/g\x81Jk\xec\u0509\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4]\x83\xb2\x1b\xd2q#`Ckg\xa5\x97\xee3x\xdb>z\xe4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94]\x87+\x12.\x99N\xf2|q\xd7\u07b4W\xbfeB\x9e\xcal\x8a\x01\xb1\xad\xed\x81\u04d4\x10\x80\x00\xe0\x94]\x8d1\xfa\xa8d\xe2!Y\xcdoQu\xcc\xec\xc5?\xa5Mr\x8a\x05\xb6\x96\xb7\r\xd5g\x10\x00\x00\xe0\x94]\x95\x8a\x9b\u0449\u0098_\x86\u014a\x8ci\xa7\xa7\x88\x06\xe8\u068a\x02(\xf1o\x86\x15x`\x00\x00\u07d4]\xa2\xa9\xa4\xc2\xc0\xa4\xa9$\xcb\xe0\xa5:\xb9\xd0\xc6'\xa1\u03e0\x89'\xbf8\xc6TM\xf5\x00\x00\u07d4]\xa4\u0288\x93\\'\xf5\\1\x10H\x84\x0eX\x9e\x04\xa8\xa0I\x89\x04V9\x18$O@\x00\x00\u07d4]\xa5G\x85\u027d0W\\\x89\u07b5\x9d A\xd2\n9\xe1{\x89j\xa2\t\xf0\xb9\x1de\x80\x00\xe0\x94]\xb6\x9f\xe9>o\xb6\xfb\xd4P\x96k\x97#\x8b\x11\n\xd8'\x9a\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4]\xb7\xbb\xa1\xf9W?$\x11]\x8c\x8cb\xe9\u0388\x95\x06\x8e\x9f\x89\x02\xb5\xaa\xd7,e \x00\x00\xe0\x94]\xb8D\x00W\x00i\xa9W<\xab\x04\xb4\u6d955\xe2\x02\xb8\x8a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4]\xc3m\xe55\x94P\xa1\xec\t\xcb\fD\xcf+\xb4+:\xe45\x89<\x94m\x89;3\x06\x00\x00\u07d4]\xc6\xf4_\xef&\xb0n3\x021?\x88M\xafH\xe2to\xb9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94]\u0376\xb8zP\xa9\xde\x02C\x80\x00\x00\u07d4^Q\xb8\xa3\xbb\t\xd3\x03\xea|\x86\x05\x15\x82\xfd`\x0f\xb3\xdc\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794^X\xe2U\xfc\x19\x87\n\x040_\xf2\xa0F1\xf2\xff)K\xb1\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4^ZD\x19t\xa8=t\u0187\xeb\xdcc?\xb1\xa4\x9e{\x1a\u05c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^eE\x8b\xe9d\xaeD\x9fqw7\x04\x97\x97f\xf8\x89\x87a\x89\x1c\xa7\xccs[o|\x00\x00\u07d4^g\u07c9i\x10\x1a\u06bd\x91\xac\xcdk\xb1\x99\x12t\xaf\x8d\xf2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4^n\x97G\xe1b\xf8\xb4\\en\x0fl\xaez\x84\xba\xc8\x0eN\x89lk\x93[\x8b\xbd@\x00\x00\u07d4^s\x1bU\xce\xd4R\xbb??\xe8q\xdd\xc3\xed~\xe6Q\n\x8f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^t\xed\x80\xe9eW\x88\xe1\xbb&\x97R1\x96g\xfeuNZ\x89\x03\t'\xf7L\x9d\xe0\x00\x00\u07d4^w.'\xf2\x88\x00\xc5\r\u0697;\xb3>\x10v.n\xea \x89a\t=|,m8\x00\x00\u07d4^{\x8cT\xdcW\xb0@ bq\x9d\xee~\xf5\xe3~\xa3]b\x89\x9b\xf9\x81\x0f\xd0\\\x84\x00\x00\u07d4^\u007fp7\x87uX\x9f\xc6j\x81\xd3\xf6S\xe9T\xf5U`\ub243\xf2\x89\x18\x1d\x84\xc8\x00\x00\xe0\x94^\x80n\x84W0\xf8\a>l\xc9\x01\x8e\xe9\x0f\\\x05\xf9\t\xa3\x8a\x02\x01\xe9m\xac\u03af \x00\x00\u07d4^\x8eM\xf1\x8c\xf0\xafw\tx\xa8\u07cd\xac\x90\x93\x15\x10\xa6y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4^\x90\xc8Xw\x19\x87V\xb06l\x0e\x17\xb2\x8eR\xb4FPZ\x89\x14JJ\x18\xef\xebh\x00\x00\u07d4^\x95\xfe_\xfc\xf9\x98\xf9\xf9\xac\x0e\x9a\x81\u06b8>\xadw\x00=\x89\x1dB\xc2\r2y\u007f\x00\x00\u07d4^\xad)\x03z\x12\x89dx\xb1)j\xb7\x14\xe9\u02d5B\x8c\x81\x89\x03\xe0C\a-@n\x00\x00\u07d4^\xb3q\xc4\a@lB{;}\xe2q\xad<\x1e\x04&\x95y\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^\u037a\xea\xb9\x10o\xfe]{Q\x96\x96`\x9a\x05\xba\ub16d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4^\xd0\xd63\x85Y\xefD\xdcza\xed\xeb\x89?\xa5\xd8?\xa1\xb5\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\xe0\x94^\u04fb\xc0R@\xe0\u04d9\xebm\xdf\xe6\x0fb\xdeM\x95\t\xaf\x8a)\x14\xc0$u\xf9\xd6\xd3\x00\x00\u0594^\xd3\xf1\xeb\xe2\xaegV\xb5\xd8\xdc\x19\xca\xd0,A\x9a\xa5w\x8b\x80\u07d4^\xd5a\x15\xbde\x05\xa8\x82s\xdf\\V\x83\x94p\xd2J-\xb7\x89\x03\x8ee\x91\xeeVf\x80\x00\xe0\x94^\xf8\xc9a\x86\xb3y\x84\xcb\xfe\x04\u0158@n;\n\xc3\x17\x1f\x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4^\xfb\xdf\xe58\x99\x99c<&`Z[\xfc,\x1b\xb5\x95\x93\x93\x89\x03\xc0W\xc9\\\xd9\b\x00\x00\xe0\x94_\x13\x15F1Fm\xcb\x13S\u0210\x93*|\x97\xe0\x87\x8e\x90\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4_\x16z\xa2B\xbcL\x18\x9a\xde\xcb:\u0127\xc4R\xcf\x19/\u03c9lkLM\xa6\u077e\x00\x00\xe0\x94_\x1c\x8a\x04\xc9\rs[\x8a\x15)\t\xae\xaeco\xb0\xce\x16e\x8a\x01{x'a\x8cZ7\x00\x00\u07d4_#\xba\x1f7\xa9lE\xbcI\x02YS\x8aT\u008b\xa3\xb0\u0549A\rXj \xa4\xc0\x00\x00\u07d4_&\xcf4Y\x9b\xc3n\xa6{\x9ez\x9f\x9bC0\xc9\xd5B\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4_)\xc9\xdev]\xde%\x85*\xf0}3\xf2\xceF\x8f\xd2\t\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_/\a\xd2\u0597\xe8\xc5g\xfc\xfd\xfe\x02\x0fI\xf3`\xbe!9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_2\x1b=\xaa\xa2\x96\xca\xdf)C\x9f\x9d\xab\x06*K\xff\xed\u0589\x04p%\x90>\xa7\xae\x00\x00\u07d4_3:;#\x10vZ\r\x182\xb9\xbeL\n\x03pL\x1c\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4_4K\x01\xc7\x19\x1a2\xd0v*\xc1\x88\xf0\xec-\xd4`\x91\x1d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94_6>\n\xb7G\xe0-\x1b;f\xab\xb6\x9e\xa5<{\xafR:\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\u07d4_7[\x86`\f@\u0328\xb2gkz\x1a\x1d\x16D\xc5\xf5,\x89\x04F\x18\xd7Lb?\x00\x00\u07d4_>\x1eg9\xb0\xc6\"\x00\xe0\n\x006\x91\xd9\xef\xb28\u061f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4_H?\xfb\x8fh\n\xed\xf2\xa3\x8fx3\xaf\xdc\xdeY\xb6\x1eK\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94_J\xceL\x1c\xc13\x91\xe0\x1f\x00\xb1\x98\xe1\xf2\v_\x91\xcb\xf5\x8a\x01\x0f\x0f\xa8\xb9\u04c1\x1a\x00\x00\xe0\x94_R\x12\x82\xe9\xb2x\u070c\x03Lr\xafS\xee)\xe5D=x\x8a\x01as-/\x8f:\xe0\x00\x00\u07d4_h\xa2L~\xb4\x11vgs{39?\xb3\xc2\x14\x8aS\xb6\x89\x02\xce\u0791\x8dE<\x00\x00\u07d4_p\x8e\xaf9\xd8#\x94lQ\xb3\xa3\u9df3\xc0\x03\xe2cA\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4_t.H~:\xb8\x1a\xf2\xf9J\xfd\xbe\x1b\x9b\x8f\\\u0301\xbc\x89u\xc4E\xd4\x11c\xe6\x00\x00\u07d4_t\xed\x0e$\xff\x80\u0672\u0124K\xaa\x99uB\x8c\u05b95\x89\xa1\x8b\xce\xc3H\x88\x10\x00\x00\u07d4_v\xf0\xa3\x06&\x9cx0k=e\r\xc3\xe9\xc3p\x84\xdba\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4_w\xa1\a\xab\x12&\xb3\xf9_\x10\ue0ee\xfcl]\xff>\u0709\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4_{;\xba\xc1m\xab\x83\x1aJ\x0f\xc5;\fT\x9d\xc3l1\u0289i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94_\x93\xff\x83't\xdbQ\x14\xc5[\xb4\xbfD\xcc\U000f53d0?\x8a(\xa9\xc9\x1a&4X)\x00\x00\u07d4_\x96\x16\xc4{Jg\xf4\x06\xb9Z\x14\xfeo\xc2h9o\x17!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4_\x98\x109\xfc\xf5\x02%\xe2\xad\xf7bu!\x12\xd1\xcc&\xb6\xe3\x89\x1b\x1aAj!S\xa5\x00\x00\u07d4_\x99\u070eI\xe6\x1dW\xda\xef`j\xcd\xd9\x1bMp\a2j\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4_\xa6\x1f\x15-\xe6\x125\x16\xc7Q$)y(_yj\u01d1\x89\v\x0f\x11\x97)c\xb0\x00\x00\u07d4_\xa7\xbf\xe0C\x88a'\xd4\x01\x1d\x83V\xa4~\x94yc\xac\xa8\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94_\xa8\xa5Nh\x17lO\xe2\xc0\x1c\xf6q\xc5\x15\xbf\xbd\xd5(\xa8\x8aE\xe1U\xfa\x01\x10\xfa@\x00\x00\u07d4_\xad\x96\x0fk,\x84V\x9c\x9fMG\xbf\x19\x85\xfc\xb2\xc6]\xa6\x8965f3\xeb\xd8\xea\x00\x00\u07d4_\xc6\xc1\x14&\xb4\xa1\xea\xe7\xe5\x1d\xd5\x12\xad\x10\x90\xc6\xf1\xa8[\x89\x93\xfe\\W\xd7\x10h\x00\x00\u07d4_\u0344Th\x96\xdd\b\x1d\xb1\xa3 \xbdM\x8c\x1d\xd1R\x8cL\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4_\u0368G\xaa\xf8\xd7\xfa\x8b\xca\b\x02\x9c\xa2\x84\x91f\xaa\x15\xa3\x89!\u02b8\x12Y\xa3\xbf\x00\x00\u07d4_\xd1\xc3\xe3\x17x'l\xb4.\xa7@\xf5\xea\xe9\xc6A\xdb\xc7\x01\x89\n\x84Jt$\xd9\xc8\x00\x00\u07d4_\xd3\xd6w~\xc2b\n\xe8:\x05R\x8e\xd4%\a-<\xa8\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_\xd9s\xaf6j\xa5\x15|Te\x9b\u03f2|\xbf\xa5\xac\x15\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4_\xe7w\x03\x80\x8f\x82>l9\x93R\x10\x8b\xdb,R|\xb8|\x89j@v\xcfy\x95\xa0\x00\x00\xe0\x94_\xecI\xc6e\xe6N\xe8\x9d\xd4A\xeet\x05n\x1f\x01\xe9(p\x8a\x01V\x9b\x9es4t\xc0\x00\x00\u07d4_\xf3&\xcd`\xfd\x13k$^)\xe9\bzj\u04e6R\u007f\r\x89e\xea=\xb7UF`\x00\x00\u07d4_\xf9=\xe6\xee\x05L\xadE\x9b-^\xb0\xf6\x87\x03\x89\xdf\xcbt\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\u07d4`\x06\xe3m\x92\x9b\xf4]\x8f\x16#\x1b\x12j\x01\x1a\xe2\x83\xd9%\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4`!\xe8Z\x88\x14\xfc\xe1\xe8*A\xab\xd1\u04f2\xda\xd2\xfa\xef\xe0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4`8t\n\xe2\x8df\xba\x93\xb0\xbe\bH+2\x05\xa0\xf7\xa0{\x89\x11!a\x85\u009fp\x00\x00\u07d4`?/\xabz\xfbn\x01{\x94v`i\xa4\xb4;8\x96I#\x89Y\xd2\xdb$\x14\u0699\x00\x00\u07d4`B'm\xf2\x98?\xe2\xbcGY\xdc\x19C\xe1\x8f\xdb\xc3Ow\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4`B\xc6D\xba\xe2\xb9o%\xf9M1\xf6x\xc9\r\xc9f\x90\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4`L\xdf\x18b\x8d\xbf\xa82\x91\x94\xd4x\xddR\x01\xee\xccK\xe7\x89\x01?0j$\t\xfc\x00\x00\u07d4`N\x94w\xeb\xf4r|t[\u02bb\xed\xcbl\xcf)\x99@\"\x8966\x9e\xd7t}&\x00\x00\u07d4`gm\x1f\xa2\x1f\xca\x05\"\x97\xe2K\xf9c\x89\u0171*p\u05c9\r\x17|Zzh\xd6\x00\x00\u07d4`gn\x92\u044b\x00\x05\t\xc6\x1d\xe5@\xe6\xc5\u0776v\xd5\t\x89A\rXj \xa4\xc0\x00\x00\u07d4`o\x17q!\xf7\x85\\!\xa5\x06#0\xc8v\"d\xa9{1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4`\x86B6\x93\r\x04\xd8@+]\xcb\xeb\x80\u007f<\xafa\x1e\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4`\xabq\xcd&\xeamnY\xa7\xa0\xf6'\xee\a\x9c\x88^\xbb\xf6\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4`\xaf\x0e\xe1\x18D<\x9b7\xd2\xfe\xadw\xf5\xe5!\u07be\x15s\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4`\xb3X\xcb=\xbe\xfa7\xf4}\xf2\xd76X@\u068e;\u024c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4`\xb8\u05b7;ySO\xb0\x8b\xb8\xcb\xce\xfa\xc7\xf3\x93\xc5{\xfe\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4`\xbeo\x95?*M%\xb6%o\xfd$#\xac\x148%.N\x89\b!\xab\rD\x14\x98\x00\x00\u0794`\xc3qO\xdd\xdbcFY\u48b1\xeaB\xc4r\x8c\u01f8\xba\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4`\xcc=D^\xbd\xf7j}z\xe5q\u0197\x1d\xffh\u0305\x85\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94`\xd5fq@\xd1&\x14\xb2\x1c\x8e^\x8a3\b.2\xdf\xcf#\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4`\xde\"\xa1Pt2\xa4{\x01\xcch\xc5*\v\xf8\xa2\xe0\u0418\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4`\xe0\xbd\u0422Y\xbb\x9c\xb0\x9d?7\xe5\u034b\x9d\xac\uafca\x89JD\x91\xbdm\xcd(\x00\x00\u07d4`\xe3\xccC\xbc\xdb\x02j\xadu\x9cpf\xf5U\xbb\xf2\xacf\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\x04+\x80\xfd`\x95\u0478{\xe2\xf0\x0f\x10\x9f\xab\xaf\xd1W\xa6\x89\x05k\xc7^-c\x10\x00\x00\u07d4a\a\xd7\x1d\xd6\xd0\xee\xfb\x11\xd4\xc9\x16@L\xb9\x8cu>\x11}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\x0f\xd6\xeeN\xeb\xab\x10\xa8\xc5]\vK\xd2\xe7\xd6\xef\x81qV\x89\x01\x15\x95a\x06]]\x00\x00\u07d4a\x14\xb0\xea\xe5Wi\x03\xf8\v\xfb\x98\x84-$\xed\x92#\u007f\x1e\x89\x05k\xc7^-c\x10\x00\x00\u07d4a!\xaf9\x8a[-\xa6\x9fe\xc68\x1a\xec\x88\u039c\xc6D\x1f\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4a&g\xf1r\x13[\x95\v,\xd1\xde\x10\xaf\xde\xcehW\xb8s\x8965\u026d\xc5\u07a0\x00\x00\u07d4a,\xed\x8d\xc0\u071e\x89\x9e\xe4oyb33\x15\xf3\xf5^D\x89\x12^5\xf9\xcd=\x9b\x00\x00\u07d4a4\xd9B\xf07\xf2\xcc=BJ#\f`=g\xab\xd3\xed\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a:\xc5;\xe5e\xd4e6\xb8 q[\x9b\x8d:\xe6\x8aK\x95\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\u07d4a?\xabD\xb1k\xbeUMD\xaf\xd1x\xab\x1d\x02\xf3z\ua949lk\x93[\x8b\xbd@\x00\x00\u07d4aN\x8b\xef=\xd2\u015bY\xa4\x14Vt@\x10\x185\x18\x84\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4aQ\x84d\xfd\u0637<\x1b\xb6\xacm\xb6\x00eI8\xdb\xf1z\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4aT}7nSi\xbc\xf9x\xfc\x16,1\xc9\b\"3\xb8%\xd0%\xbe?{\x10V\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4a\x91\xdd\u0276J\x8e\b\x90\xb427\t\u05e0|H\xb9*d\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4a\x96\xc3\xd3\xc0\x90\x8d%Cf\xb7\xbc\xa5WE\"-\x9dM\xb1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4a\x9f\x17\x14E\xd4+\x02\xe2\xe0p\x04\xad\x8a\xfeiO\xa5=j\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4a\xad\xf5\x92\x9a^)\x81hN\xa2C\xba\xa0\x1f}\x1f^\x14\x8a\x89\x05\xfa\xbfl\x98O#\x00\x00\u07d4a\xb1\xb8\xc0\x12\xcdLx\xf6\x98\xe4p\xf9\x02V\xe6\xa3\x0fH\u0749\n\u05ce\xbcZ\xc6 \x00\x00\u07d4a\xb3\xdf.\x9e\x9f\xd9h\x13\x1f\x1e\x88\xf0\xa0\xeb[\xd7eFM\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4a\xb9\x02\u0166s\x88X&\x82\r\x1f\xe1EI\xe4\x86_\xbd\u0089\x12$\xef\xed*\u1440\x00\u07d4a\xb9\x05\xdef?\xc1s\x86R;:(\xe2\xf7\xd07\xa6U\u0349\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4a\xba\x87\xc7~\x9bYm\xe7\xba\x0e2o\xdd\xfe\xec!c\xeff\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4a\xbf\x84\u056b\x02oX\xc8s\xf8o\xf0\xdf\u0282\xb5W3\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\xc4\xee|\x86LMk^7\xea\x131\xc2\x03s\x9e\x82k/\x89\x01\xa15;8*\x91\x80\x00\u07d4a\xc80\xf1eG\x18\xf0u\u032b\xa3\x16\xfa\xac\xb8[}\x12\v\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4a\xc8\xf1\xfaC\xbf\x84i\x99\xec\xf4{+2M\xfbkc\xfe:\x89+^:\xf1k\x18\x80\x00\x00\u07d4a\xc9\xdc\u8c98\x1c\xb4\x0e\x98\xb0@+\xc3\xeb(4\x8f\x03\xac\x89\n\xac\xac\u0679\xe2+\x00\x00\u07d4a\u03a7\x1f\xa4d\xd6*\a\x06?\x92\v\f\xc9\x17S\x973\u0609Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4a\xd1\x01\xa03\xee\x0e.\xbb1\x00\xed\xe7f\xdf\x1a\xd0$IT\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4a\xedU\x96\u0197 \u007f=U\xb2\xa5\x1a\xa7\xd5\x0f\a\xfa\t\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\xff\x8eg\xb3M\x9e\xe6\xf7\x8e\xb3o\xfe\xa1\xb9\xf7\xc1W\x87\xaf\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4b\x05\xc2\xd5dtp\x84\x8a8@\xf3\x88~\x9b\x01]4u\\\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4b(\xad\xe9^\x8b\xb1}\x1a\xe2;\xfb\x05\x18AMI~\x0e\xb8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94b)\xdc\xc2\x03\xb1\xed\xcc\xfd\xf0n\x87\x91\fE*\x1fMzr\x8a\x06\xe1\xd4\x1a\x8f\x9e\xc3P\x00\x00\u0794b+\xe4\xb4T\x95\xfc\xd91C\xef\xc4\x12\u0599\xd6\xcd\xc2=\u0148\xf0\x15\xf2W6B\x00\x00\u07d4b3\x1d\xf2\xa3\xcb\xee5 \xe9\x11\u07a9\xf7>\x90_\x89%\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4bVD\xc9Z\x87>\xf8\xc0l\u06de\x9fm\x8dv\x80\x04=b\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4be\xb2\xe7s\x0f6\xb7v\xb5-\f\x9d\x02\xad\xa5]\x8e<\xb6\x8965\u026d\xc5\u07a0\x00\x00\u07d4bh\n\x15\xf8\u0338\xbd\xc0/s`\xc2Z\xd8\u03f5{\x8c\u034965\u026d\xc5\u07a0\x00\x00\u07d4b\x94\xea\xe6\xe4 \xa3\xd5`\n9\xc4\x14\x1f\x83\x8f\xf8\xe7\xccH\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4b\x97\x1b\xf2cL\xee\v\xe3\u0249\x0fQ\xa5`\x99\u06f9Q\x9b\x89#\x8f\xd4,\\\xf0@\x00\x00\u07d4b\x9b\xe7\xab\x12jS\x98\xed\xd6\u069f\x18D~x\u0192\xa4\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4b\xb4\xa9\"nah\a\x1el\xbea\x11\xfe\xf0\xbcc\x8a\x03\xba\x19\x10\xbf4\x1b\x00\x00\x00\xe0\x94c\n\x91:\x901\xc9I*\xbdLA\u06f1PT\xcf\xecD\x16\x8a\x014X\xdbg\xaf5\xe0\x00\x00\xe0\x94c\fRs\x12mQ|\xe6q\x01\x81\x1c\xab\x16\xb8SL\xf9\xa8\x8a\x01\xfe\xcc\xc6%s\xbb\u04c0\x00\u07d4c\x100\xa5\xb2{\a(\x8aEio\x18\x9e\x11\x14\xf1*\x81\xc0\x89\x1b\x1azB\v\xa0\r\x00\x00\u07d4c\x10\xb0 \xfd\x98\x04IW\x99P\x92\t\x0f\x17\xf0NR\xcd\xfd\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4c+\x91I\xd7\x01x\xa7364'^\x82\u0555?'\x96{\x89%\xf2s\x93=\xb5p\x00\x00\u07d4c,\xec\xb1\f\xfc\xf3\x8e\u0246\xb4;\x87p\xad\xec\xe9 \x02!\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4c1\x02\x8c\xbbZ!H[\xc5\x1bVQB\x99;\xdb%\x82\xa9\x89\x1c\xfd\xd7F\x82\x16\xe8\x00\x00\u07d4c3O\xcf\x17E\x84\x0eK\tJ;\xb4\v\xb7o\x96\x04\xc0L\x89\u05e5\xd7\x03\xa7\x17\xe8\x00\x00\u07d4c4\nWqk\xfac\xebl\xd13r\x12\x02W[\xf7\x96\xf0\x89\va\xe0\xa2\f\x12q\x80\x00\u07d4cN\xfc$7\x11\a\xb4\xcb\xf0?y\xa9=\xfd\x93\xe41\xd5\xfd\x89B5\x82\xe0\x8e\xdc\\\x80\x00\xe0\x94c\\\x00\xfd\xf05\xbc\xa1_\xa3a\r\xf38N\x0f\xb7\x90h\xb1\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4ca.xb\xc2{X|\xfbm\xaf\x99\x12\xcb\x05\x1f\x03\n\x9f\x89\x02[\x19\u053f\xe8\xed\x00\x00\u07d4cfgU\xbdA\xb5\x98i\x97x<\x13\x040\b$+<\xb5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4c{\xe7\x1b:\xa8\x15\xffE=VB\xf70tE\vd\xc8*\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94c}g\xd8\u007fXo\nZG\x9e \xee\x13\xea1\n\x10\xb6G\x8a\n:Y&\xaf\xa1\xe70\x00\x00\u07d4c\u007fXi\xd6\xe4i_\x0e\xb9\xe2s\x11\u0107\x8a\xff33\x80\x89j\xc0Nh\xaa\xec\x86\x00\x00\u07d4c\x97|\xad}\r\xcd\xc5+\x9a\xc9\xf2\xff\xa16\xe8d(\x82\xb8\x89\x04\x10\u0546\xa2\nL\x00\x00\u07d4c\xa6\x1d\xc3\n\x8e;0\xa7c\xc4!<\x80\x1c\xbf\x98s\x81x\x8965\u026d\xc5\u07a0\x00\x00\u07d4c\xacT\\\x99\x12C\xfa\x18\xae\xc4\x1dOoY\x8eUP\x15\u0709 \x86\xac5\x10R`\x00\x00\u07d4c\xb9uMu\xd1-8@9\xeci\x06<\v\xe2\x10\xd5\xe0\u3252\v\x86\f\xc8\xec\xfd\x80\x00\u07d4c\xbbfO\x91\x17\x03v(YM\xa7\xe3\xc5\b\x9f\xd6\x18\xb5\xb5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4c\u00a3\xd25\xe5\xee\xab\xd0\u0526\xaf\u06c9\xd9F'9d\x95\x89CN\xf0[\x9d\x84\x82\x00\x00\u07d4c\xc8\xdf\xde\v\x8e\x01\xda\xdc.t\x8c\x82L\xc06\x9d\U00010cc9\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4c\xd5Z\u065b\x917\xfd\x1b \xcc+O\x03\xd4,\xba\xdd\xf34\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4c\xd8\x00H\x87u\x96\xe0\u0084\x89\xe6P\xcdJ\xc1\x80\tjI\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94c\xe4\x14`>\x80\xd4\xe5\xa0\xf5\xc1\x87t FB%\x82\b\xe4\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94c\xe8\x8e.S\x9f\xfbE\x03\x86\xb4\xe4g\x89\xb2#\xf5GlE\x8a\x01U\x17\nw\x8e%\xd0\x00\x00\u07d4c\xef/\xbc=\xaf^\xda\xf4\xa2\x95b\x9c\xcf1\xbc\xdf@8\xe5\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4c\xf0\xe5\xa7R\xf7\x9fg\x12N\xedc:\xd3\xfd'\x05\xa3\x97\u0509\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94c\xf5\xb5=y\xbf.A\x14\x89Re0\"8E\xfa\xc6\xf6\x01\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4c\xfc\x93\x00\x13\x05\xad\xfb\u0278])\xd9)\x1a\x05\xf8\xf1A\v\x8965\u026d\xc5\u07a0\x00\x00\u0794c\xfek\xccK\x8a\x98P\xab\xbeu\x8070\xc92%\x1f\x14[\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4d\x03\xd0bT\x96\x90\xc8\xe8\xb6>\xaeA\xd6\xc1\tGn%\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4d\x04+\xa6\x8b\x12\xd4\xc1Qe\x1c\xa2\x81;sR\xbdV\xf0\x8e\x89 \x86\xac5\x10R`\x00\x00\u0794d\x05\xdd\x13\xe9:\xbc\xff7~p\x0e<\x1a\x00\x86\xec\xa2})\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94d\n\xbam\xe9\x84\xd9E\x177x\x03p^\xae\xa7\t_J\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4d\v\xf8t\x15\xe0\xcf@s\x01\xe5Y\x9ah6m\xa0\x9b\xba\u0209\x1a\xbc\x9fA`\x98\x15\x80\x00\u07d4d \xf8\xbc\xc8\x16JaR\xa9\x9dk\x99i0\x05\xcc\xf7\xe0S\x8965f3\xeb\xd8\xea\x00\x00\u07d4d$\x1axD)\x0e\n\xb8U\xf1\u052au\xb5SE\x03\"$\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4d&J\xed\xd5-\xca\xe9\x18\xa0\x12\xfb\xcd\f\x03\x0e\xe6\xf7\x18!\x8965\u026d\xc5\u07a0\x00\x00\u07d4d7\x0e\x87 &E\x12Z5\xb2\a\xaf\x121\xfb`r\xf9\xa7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4d=\x9a\xee\u0531\x80\x94~\u04b9 |\xceL=\xdcU\xe1\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4dC\xb8\xaec\x9d\xe9\x1c\xf7\xf0p\xa5G\x03\xb7\x18NH'l\\\x00w\xefK4\x89\x11X\xe4`\x91=\x00\x00\x00\xe0\x94d\xe2\xde! \v\x18\x99\u00e0\xc0e;P@\x13m\r\xc8B\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4d\xec\x8a[t?4y\xe7\a\xda\xe9\xee \u076aO@\xf1\u0649\n\u05ce\xbcZ\xc6 \x00\x00\u07d4e\x03\x86\v\x19\x10\b\xc1U\x83\xbf\u0201X\t\x93\x01v((\x8965\u026d\xc5\u07a0\x00\x00\u07d4e\x051\x911\x9e\x06z%\xe66\x1dG\xf3\u007fc\x18\xf84\x19\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4e\t;#\x9b\xbf\xba#\xc7w\\\xa7\xdaZ\x86H\xa9\xf5L\xf7\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4e\t\xee\xb14~\x84/\xfbA>7\x15^,\xbcs\x82s\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94e\vBUU\xe4\xe4\xc5\x17\x18\x14h6\xa2\xc1\xeew\xa5\xb4!\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4e\f\xf6}\xb0`\xcc\xe1uh\xd5\xf2\xa4#h|Idv\t\x89\x05k\xc7^-c\x10\x00\x00\u07d4e\x10\xdfB\xa5\x99\xbc\xb0\xa5\x19\u0329a\xb4\x88u\x9aogw\x89lk\x93[\x8b\xbd@\x00\x00\u07d4e6u\xb8B\xd7\u0634a\xf7\"\xb4\x11|\xb8\x1d\xac\x8ec\x9d\x89\x01\xae6\x1f\xc1E\x1c\x00\x00\u07d4eK~\x80\x87\x99\xa8=r\x87\xc6w\x06\xf2\xab\xf4\x9aId\x04\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94eORHG\xb3\xa6\xac\xc0\xd3\xd5\xf1\xf3b\xb6\x03\xed\xf6_\x96\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4eY4\u068etN\xaa=\xe3M\xbb\xc0\x89LN\xda\va\xf2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4e]\\\xd7H\x96)\xe2ANIb?\xabb\xa1~M6\x11\x89\x05\fL\xb2\xa1\f`\x00\x00\u07d4e\xaf\x8d\x8b[\x1d\x1e\xed\xfaw\xbc\xbc\x96\xc1\xb13\xf83\x06\u07c9\x05P\x05\xf0\xc6\x14H\x00\x00\u07d4e\xaf\x90\x87\xe0QgqT\x97\u0265\xa7I\x18\x94\x89\x00M\xef\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u0794e\xb4/\xae\xcc\x1e\u07f1B\x83\u0297\x9a\xf5E\xf6;0\xe6\f\x88\xfc\x93c\x92\x80\x1c\x00\x00\u0794e\xd3>\xb3\x9c\xdadS\xb1\x9ea\xc1\xfeM\xb91p\xef\x9d4\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4e\xd8\xddN%\x1c\xbc\x02\x1f\x05\xb0\x10\xf2\xd5\xdcR\f8r\xe0\x89-CW\x9a6\xa9\x0e\x00\x00\u07d4e\xea&\xea\xbb\xe2\xf6L\xcc\xcf\xe0h)\xc2]F7R\x02%\x89%\xf2s\x93=\xb5p\x00\x00\u07d4e\xeag\xad?\xb5j\xd5\xfb\x948}\u04ce\xb3\x83\x00\x1d|h\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94e\xeb\xae\xd2~\u06dd\xcc\x19W\xae\xe5\xf4R\xac!\x05\xa6\\\x0e\x8a\t7\u07ed\xae%\u26c0\x00\u07d4e\xee \xb0m\x9a\u0549\xa7\xe7\xce\x04\xb9\xf5\xf7\x95\xf4\x02\xae\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4e\xf544m/\xfbx\u007f\xa9\xcf\x18]t[\xa4)\x86\xbdn\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94e\xf5\x87\x0f&\xbc\xe0\x89g}\xfc#\xb5\x00\x1e\xe4\x92H4(\x8a\x01\x12\xb1\xf1U\xaa2\xa3\x00\x00\u07d4e\xfd\x02\xd7\x04\xa1*M\xac\xe9G\x1b\x06E\xf9b\xa8\x96q\u0209\x01\x8d\x1c\xe6\xe4'\u0340\x00\u07d4e\xff\x87O\xaf\xceM\xa3\x18\xd6\xc9=W\xe2\u00ca\rs\xe8 \x8968\x02\x1c\xec\u06b0\x00\x00\xe0\x94f\x05W\xbbC\xf4\xbe:\x1b\x8b\x85\xe7\xdf{<[\xcdT\x80W\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4f\b,u\xa8\xde1\xa59\x13\xbb\xd4M\xe3\xa07O\u007f\xaaA\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4f\x11\xceY\xa9\x8b\a*\xe9Y\xdcI\xadQ\x1d\xaa\xaa\xa1\x9dk\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4f \x1b\xd2'\xaem\u01bd\xfe\xd5\xfb\u0781\x1f\xec\xfe^\x9d\u0649 >\x9e\x84\x92x\x8c\x00\x00\u07d4f#4\x81G$\x93[y1\xdd\xcaa\x00\xe0\rFw'\u0349\"\x88&\x9d\a\x83\xd4\x00\x00\u07d4f'O\xea\x82\xcd0\xb6\u009b#5\x0eOO=1\nX\x99\x89p7\x05P\xab\x82\x98\x00\x00\u07d4f,\xfa\x03\x8f\xab7\xa0\x17E\xa3d\u1e41'\xc5\x03tm\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4f5\xb4oq\x1d-\xa6\xf0\xe1cp\u034e\xe4>\xfb,-R\x89lk\x93[\x8b\xbd@\x00\x00\u07d4f6\x04\xb0P0F\xe6$\xcd&\xa8\xb6\xfbGB\xdc\xe0*o\x89\x03\x8b\x9by~\xf6\x8c\x00\x00\u07d4f6\u05ecczH\xf6\x1d8\xb1L\xfdHe\xd3m\x14(\x05\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4f@\xcc\xf0SU\\\x13\n\xe2\xb6Vd~\xa6\xe3\x167\xb9\xab\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4fBK\xd8x[\x8c\xb4a\x10*\x90\x02\x83\xc3]\xfa\a\xefj\x89\x02.-\xb2ff\xfc\x80\x00\u07d4fL\xd6}\xcc\u026c\x82(\xb4\\U\u06cdvU\ve\x9c\u0709\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4fNC\x11\x98p\xaf\x10zD\x8d\xb1'\x8b\x04H8\xff\u036f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4fQso\xb5\x9b\x91\xfe\xe9\xc9:\xa0\xbdn\xa2\xf7\xb2Pa\x80\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4f[\x00\x0f\vw'P\xcc\x89k\x91\x8a\xacIK\x16\x80\x00\xe0\x94g]\\\xaa`\x9b\xf7\n\x18\xac\xa5\x80F]\x8f\xb71\r\x1b\xbb\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4gc F\u0732ZT\x93i(\xa9oB?3 \xcb\ud489lk\x93[\x8b\xbd@\x00\x00\u07d4ge\xdf%(\x0e\x8eO8\u0531\xcfDo\xc5\xd7\xebe\x9e4\x89\x05k\xc7^-c\x10\x00\x00\u07d4gv\xe13\xd9\xdc5L\x12\xa9Q\b{c\x96P\xf59\xa43\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4g\x85Q<\xf72\xe4~\x87g\ap\xb5A\x9b\xe1\f\xd1\xfct\x89lk\x93[\x8b\xbd@\x00\x00\u07d4g\x947\xea\xcfCxx\xdc)=H\xa3\x9c\x87\xb7B\x1a!l\x89\x03\u007f\x81\x82\x1d\xb2h\x00\x00\u07d4g\x9b\x9a\x10\x990Q~\x89\x99\t\x9c\xcf*\x91LL\x8d\xd94\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4g\xa8\x0e\x01\x90r\x1f\x949\rh\x02r\x9d\xd1,1\xa8\x95\xad\x89lk\x13u\xbc\x91V\x00\x00\u07d4g\xb8\xa6\xe9\x0f\xdf\n\x1c\xacD\x17\x930\x1e\x87P\xa9\xfayW\x890\x84\x9e\xbe\x166\x9c\x00\x00\u07d4g\xbc\x85\xe8}\xc3LN\x80\xaa\xfa\x06k\xa8\u049d\xbb\x8eC\x8e\x89\x15\xd1\xcfAv\xae\xba\x00\x00\u07d4g\xc9&\t>\x9b\x89'\x938\x10\u0642\"\xd6.+\x82\x06\xbb\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4g\xcf\xdanp\xbfvW\u04d0Y\xb5\x97\x90\xe5\x14Z\xfd\xbea\x89#\x05\r\tXfX\x00\x00\u07d4g\u0582\xa2\x82\xefs\xfb\x8dn\x90q\xe2aOG\xab\x1d\x0f^\x8965\u026d\xc5\u07a0\x00\x00\u07d4g\u05a8\xaa\x1b\xf8\xd6\xea\xf78N\x99=\xfd\xf1\x0f\n\xf6\x8aa\x89\n\xbc\xbbW\x18\x97K\x80\x00\u07d4g\u0692.\xff\xa4r\xa6\xb1$\xe8N\xa8\xf8k$\xe0\xf5\x15\xaa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4g\xdf$-$\r\u0538\a\x1dr\xf8\xfc\xf3[\xb3\x80\x9dq\xe8\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4g\xee@n\xa4\xa7\xaej:8\x1e\xb4\xed\xd2\xf0\x9f\x17KI(\x898)c_\th\xb0\x00\x00\u07d4g\xf2\xbbx\xb8\xd3\xe1\x1f|E\x8a\x10\xb5\xc8\xe0\xa1\xd3tF}\x89a\t=|,m8\x00\x00\u07d4g\xfcR}\xce\x17\x85\xf0\xfb\x8b\xc7\xe5\x18\xb1\xc6i\xf7\xec\u07f5\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4h\x02}\x19U\x8e\xd73\x9a\b\xae\xe8\xde5Y\xbe\x06>\xc2\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4h\x06@\x83\x8b\xd0zD{\x16\x8dm\x92;\x90\xcflC\xcd\u0289]\u0212\xaa\x111\xc8\x00\x00\u07d4h\a\xdd\u020d\xb4\x89\xb03\xe6\xb2\xf9\xa8\x15SW\x1a\xb3\xc8\x05\x89\x01\x9f\x8euY\x92L\x00\x00\xe0\x94h\rY\x11\xed\x8d\xd9\xee\xc4\\\x06\f\"?\x89\xa7\xf6 \xbb\u054a\x04<3\xc1\x93ud\x80\x00\x00\u07d4h\x11\xb5L\u0456c\xb1\x1b\x94\xda\x1d\xe2D\x82\x85\u035fh\u0649;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4h\x19\f\xa8\x85\xdaB1\x87L\x1c\xfbB\xb1X\n!s\u007f8\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94h(\x97\xbcO\x8e\x89\x02\x91 \xfc\xff\xb7\x87\xc0\x1a\x93\xe6A\x84\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4h)^\x8e\xa5\xaf\xd9\t?\xc0\xa4e\xd1W\x92+]*\xe24\x89\x01\x15NS!}\xdb\x00\x00\u07d4h.\x96'oQ\x8d1\xd7\xe5n0\u07f0\t\xc1!\x82\x01\xbd\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4h5\xc8\xe8\xb7J,\xa2\xae?J\x8d\x0fk\x95J>*\x83\x92\x89\x03B\x9c3]W\xfe\x00\x00\u07d4h63\x01\n\x88hk\xeaZ\x98\xeaS\xe8y\x97\xcb\xf7>i\x89\x05k9Bc\xa4\f\x00\x00\u07d4h=\xba6\xf7\xe9O@\xeaj\xea\ry\xb8\xf5!\xdeU\an\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4hA\x9cm\xd2\xd3\xceo\u02f3\xc7>/\xa0y\xf0`Q\xbd\xe6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4hG;z}\x96Y\x04\xbe\u06e5V\u07fc\x17\x13l\xd5\xd44\x89\x05k\xc7^-c\x10\x00\x00\u07d4hG\x82[\xde\xe8$\x0e(\x04,\x83\xca\xd6B\U000868fd\u0709QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94hJD\xc0i3\x9d\b\xe1\x9auf\x8b\u06e3\x03\xbe\x85S2\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\u07d4hS\x1fM\u0680\x8fS vz\x03\x114(\xca\f\xe2\xf3\x89\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4hy'\xe3\x04\x8b\xb5\x16*\xe7\xc1\\\xf7k\xd1$\xf9I{\x9e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94h\x80\x9a\xf5\xd52\xa1\x1c\x1aMn2\xaa\xc7\\LR\xb0\x8e\xad\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4h\x86\xad\xa7\xbb\xb0a{\u0684!\x91\u018c\x92.\xa3\xa8\xac\x82\x89>\xe2;\xde\x0e} \x00\x00\xe0\x94h\x88>\x15.V`\xfe\xe5\x96&\xe7\xe3\xb4\xf0Q\x10\xe6\"/\x8a\v\x94c;\xe9u\xa6*\x00\x00\u07d4h\x8aV\x9e\x96U$\xeb\x1d\n\xc3\xd3s>\xab\x90\x9f\xb3\xd6\x1e\x89G\x8e\xae\x0eW\x1b\xa0\x00\x00\xe0\x94h\x8e\xb3\x85;\xbc\xc5\x0e\xcf\xee\x0f\xa8\u007f\n\xb6\x93\u02bd\xef\x02\x8a\x06\xb1\n\x18@\x06G\xc0\x00\x00\u07d4h\xa7B_\xe0\x9e\xb2\x8c\xf8n\xb1y>A\xb2\x11\xe5{\u058d\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4h\xa8l@#\x88\xfd\xdcY\x02\x8f\xecp!\u933f\x83\x0e\xac\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\xe0\x94h\xac\u06a9\xfb\x17\xd3\xc3\t\x91\x1aw\xb0_S\x91\xfa\x03N\xe9\x8a\x01\xe5.3l\xde\"\x18\x00\x00\u07d4h\xad\xdf\x01\x9dk\x9c\xabp\xac\xb1?\v1\x17\x99\x9f\x06.\x12\x89\x02\xb5\x12\x12\xe6\xb7\u0200\x00\u07d4h\xb3\x186\xa3\n\x01j\xda\x15{c\x8a\xc1]\xa7?\x18\xcf\u0789\x01h\u048e?\x00(\x00\x00\xe0\x94h\xb6\x85G\x88\xa7\xc6Il\xdb\xf5\xf8K\x9e\xc5\xef9+x\xbb\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4h\xc0\x84\x90\u021b\xf0\u05b6\xf3 \xb1\xac\xa9\\\x83\x12\xc0\x06\b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4h\xc7\xd1q\x1b\x01\x1a3\xf1o\x1fU\xb5\xc9\x02\xcc\xe9p\xbd\u05c9\b=lz\xabc`\x00\x00\u07d4h\xc8y\x1d\xc3B\xc3sv\x9e\xa6\x1f\xb7\xb5\x10\xf2Q\xd3 \x88\x8965\u026d\xc5\u07a0\x00\x00\u07d4h\u07d4|I[\ubbb8\u8273\xf9S\xd53\x87K\xf1\x06\x89\x1d\x99E\xab+\x03H\x00\x00\u07d4h\xe8\x02'@\xf4\xaf)\xebH\xdb2\xbc\xec\xdd\xfd\x14\x8d=\xe3\x8965\u026d\xc5\u07a0\x00\x00\u07d4h\xecy\u057eqUql@\x94\x1cy\u05cd\x17\u079e\xf8\x03\x89\x1b#8w\xb5 \x8c\x00\x00\u07d4h\xee\xc1\u222c1\xb6\xea\xba~\x1f\xbdO\x04\xadW\x9ak]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4h\xf5%\x92\x1d\xc1\x1c2\x9buO\xbf>R\x9f\xc7#\xc84\u0349WG=\x05\u06ba\xe8\x00\x00\u07d4h\xf7\x19\xae4+\xd7\xfe\xf1\x8a\x05\u02f0/pZ\u04ce\u0572\x898\xeb\xad\\\u0710(\x00\x00\xe0\x94h\xf7W<\xd4W\xe1L\x03\xfe\xa4>0-04|\x10p\\\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4h\xf8\xf4QU\xe9\x8cP)\xa4\xeb\u0175'\xa9.\x9f\xa81 \x89\xf0{D\xb4\a\x93 \x80\x00\u07d4h\xfe\x13W!\x8d\tXI\xcdW\x98B\u012a\x02\xff\x88\x8d\x93\x89lk\x93[\x8b\xbd@\x00\x00\u07d4i\x02(\xe4\xbb\x12\xa8\u0535\u09d7\xb0\xc5\xcf*u\t\x13\x1e\x89e\xea=\xb7UF`\x00\x00\u07d4i\x05\x94\xd3\x06a<\xd3\xe2\xfd$\xbc\xa9\x99J\u064a=s\xf8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94i\a2ir\x9ed\x14\xb2n\xc8\xdc\x0f\xd95\xc7;W\x9f\x1e\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x94i\x19\xdd^]\xfb\x1a\xfa@G\x03\xb9\xfa\xea\x8c\xee5\xd0\rp\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4i4\x92\xa5\xc5\x13\x96\xa4\x82\x88\x16i\xcc\xf6\xd8\xd7y\xf0\tQ\x89\x12\xbfPP:\xe3\x03\x80\x00\u07d4i=\x83\xbe\tE\x9e\xf89\v.0\xd7\xf7\u008d\xe4\xb4(N\x89lk\x93[\x8b\xbd@\x00\x00\u07d4iQp\x83\xe3\x03\xd4\xfb\xb6\xc2\x11E\x14!]i\xbcF\xa2\x99\x89\x05k\xc7^-c\x10\x00\x00\u07d4iUPel\xbf\x90\xb7]\x92\xad\x91\"\xd9\r#\xcah\xcaM\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94iX\xf8;\xb2\xfd\xfb'\xce\x04\t\xcd\x03\xf9\xc5\xed\xbfL\xbe\u074a\x04<3\xc1\x93ud\x80\x00\x00\u0794i[\x0fRBu7\x01\xb2d\xa6pq\xa2\u0708\b6\xb8\u06c8\u3601\x1b\xech\x00\x00\xe0\x94i[L\xce\bXV\xd9\xe1\xf9\xff>y\x94 #5\x9e_\xbc\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94if\x06:\xa5\xde\x1d\xb5\xc6q\xf3\xddi\x9dZ\xbe!>\xe9\x02\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4it\u0224\x14\u03ae\xfd<.M\xfd\xbe\xf40V\x8d\x9a\x96\v\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\xe0\x94iximQP\xa9\xa2cQ?\x8ft\u0196\xf8\xb19|\xab\x8a\x01g\xf4\x82\xd3\u0171\xc0\x00\x00\xe0\x94iy{\xfb\x12\u027e\u0582\xb9\x1f\xbcY5\x91\xd5\xe4\x027(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94i\u007fUSk\xf8Z\xdaQ\x84\x1f\x02\x87b:\x9f\x0e\u041a\x17\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4i\x82\xfe\x8a\x86~\x93\xebJ\v\xd0QX\x93\x99\xf2\xec\x9aR\x92\x89lk\x93[\x8b\xbd@\x00\x00\u07d4i\x8a\x8ao\x01\xf9\xabh/c|yi\xbe\x88_lS\x02\xbf\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4i\x8a\xb9\xa2\xf33\x81\xe0|\fGC=\r!\xd6\xf36\xb1'\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4i\x94\xfb21\xd7\xe4\x1dI\x1a\x9dh\xd1\xfaL\xae,\xc1Y`\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\x9c\x9e\xe4q\x95Q\x1f5\xf8b\xcaL\"\xfd5\xae\x8f\xfb\xf4\x89\x04V9\x18$O@\x00\x00\u07d4i\x9f\xc6\u058aGuW<\x1d\u036e\xc80\xfe\xfdP9|N\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4i\xaf(\xb0tl\xac\r\xa1p\x84\xb99\x8c^6\xbb:\r\xf2\x896w\x03n\xdf\n\xf6\x00\x00\u07d4i\xb8\x0e\xd9\x0f\x84\x83J\xfa?\xf8.\xb9dp;V\tw\u0589\x01s\x17\x90SM\xf2\x00\x00\xe0\x94i\xb8\x1dY\x81\x14\x1e\u01e7\x14\x10`\xdf\u03cf5\x99\xff\xc6>\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4i\xbc\xfc\x1dC\xb4\xba\x19\xde{'K\xdf\xfb5\x13\x94\x12\xd3\u05c95e\x9e\xf9?\x0f\xc4\x00\x00\u07d4i\xbd%\xad\xe1\xa34lY\xc4\xe90\xdb*\x9dq^\xf0\xa2z\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\xc0\x8dtGT\xdep\x9c\xe9n\x15\xae\r\x1d9[:\"c\x8965\u026d\xc5\u07a0\x00\x00\u07d4i\xc2\xd85\xf1>\xe9\x05\x80@\x8ej2\x83\xc8\u0326\xa44\xa2\x89#\x8f\xd4,\\\xf0@\x00\x00\u07d4i\xc9N\a\u0129\xbe3\x84\xd9]\xfa<\xb9)\x00Q\x87;{\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4i\xcb>!S\x99\x8d\x86\xe5\xee \xc1\xfc\u0466\xba\uec86?\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\u04ddQ\b\x89\xe5R\xa3\x96\x13[\xfc\xdb\x06\xe3~8v3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94i\u064f8\xa3\xba=\xbc\x01\xfa\\,\x14'\xd8b\x83//p\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94i\xe2\xe2\xe7\x040|\xcc[\\\xa3\xf1d\xfe\xce.\xa7\xb2\xe5\x12\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4i\xffB\x90t\u02dblc\xbc\x91B\x84\xbc\xe5\xf0\xc8\xfb\xf7\u0409\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4i\xff\x89\x01\xb5Av?\x81|_)\x98\xf0-\xcf\xc1\xdf)\x97\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4j\x02:\xf5}XM\x84^i\x876\xf10\u06dd\xb4\r\xfa\x9a\x89\x05[ \x1c\x89\x00\x98\x00\x00\u07d4j\x04\xf5\xd5?\xc0\xf5\x15\xbe\x94+\x8f\x12\xa9\xcbz\xb0\xf3\x97x\x89\xa9\xaa\xb3E\x9b\xe1\x94\x00\x00\u07d4j\x05\xb2\x1cO\x17\xf9\xd7?_\xb2\xb0\u02c9\xffSV\xa6\xcc~\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94j\x0f\x05`f\xc2\xd5f(\x85\x02s\xd7\xec\xb7\xf8\xe6\xe9\x12\x9e\x8a\x01\x0f\r)<\u01e5\x88\x00\x00\u07d4j\x13\xd5\xe3,\x1f\xd2m~\x91\xffn\x051`\xa8\x9b,\x8a\xad\x89\x02\xe6/ \xa6\x9b\xe4\x00\x00\u07d4j.\x86F\x9a[\xf3|\xee\x82\xe8\x8bL8c\x89](\xfc\xaf\x89\x1c\"\x92f8[\xbc\x00\x00\u07d4j6\x94BL|\u01b8\xbc\u067c\u02baT\f\xc1\xf5\xdf\x18\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94jB\u0297\x1cex\u056d\xe2\x95\xc3\xe7\xf4\xad3\x1d\xd3BN\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4jD\xaf\x96\xb3\xf02\xaed\x1b\xebg\xf4\xb6\xc83B\xd3|]\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4jL\x89\a\xb6\x00$\x80W\xb1\xe4cT\xb1\x9b\u0705\x9c\x99\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4jQNbB\xf6\xb6\x8c\x13~\x97\xfe\xa1\u73b5U\xa7\xe5\xf7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4jS\xd4\x1a\xe4\xa7R\xb2\x1a\xbe\xd57FI\x95:Q=\xe5\xe5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4jaY\aJ\xb5s\xe0\xeeX\x1f\x0f=\xf2\u05a5\x94b\x9bt\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4jc7\x83?\x8fjk\xf1\f\xa7\xec!\xaa\x81\x0e\xd4D\xf4\u02c97\xbd$4\\\xe8\xa4\x00\x00\u07d4jcS\xb9qX\x9f\x18\xf2\x95\\\xba(\xab\xe8\xac\xcejWa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4jc\xfc\x89\xab\xc7\xf3n(-\x80x{{\x04\xaf\xd6U>q\x89\b\xacr0H\x9e\x80\x00\x00\u07d4jg\x9e7\x8f\xdc\xe6\xbf\xd9\u007f\xe6/\x04)Z$\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4j\x8c\xea-\xe8J\x8d\xf9\x97\xfd?\x84\xe3\b=\x93\xdeW\u0369\x89\x05k\xe0<\xa3\xe4}\x80\x00\xe0\x94j\x97Xt;`>\xea:\xa0RKB\x88\x97#\xc4\x159H\x8a\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4j\xa5s/;\x86\xfb\x8c\x81\xef\xbek[G\xb5cs\v\x06\u020965\u026d\xc5\u07a0\x00\x00\u07d4j\xb3#\xaePV\xed\nE0r\u016b\xe2\xe4/\xcf]q9\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4j\xb5\xb4\xc4\x1c\u0778)i\f/\xda\u007f \xc8^b\x9d\xd5\u0549d\u052fqL2\x90\x00\x00\u07d4j\xc4\x0fS-\xfe\xe5\x11\x81\x17\u04ad5-\xa7}Om\xa2\u0209\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4j\xc4\u053e-\xb0\u065d\xa3\xfa\xaa\xf7RZ\xf2\x82\x05\x1dj\x90\x89\x04X\xcaX\xa9b\xb2\x80\x00\u07d4j\xcd\u0723\xcd+I\x90\xe2\\\xd6\\$\x14\x9d\t\x12\t\x9ey\x89\xa2\xa1\xe0|\x9fl\x90\x80\x00\u07d4j\xd9\v\xe2R\xd9\xcdFM\x99\x81%\xfa\xb6\x93\x06\v\xa8\xe4)\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4j\u0753!\x93\xcd8IJ\xa3\xf0:\xec\xccKz\xb7\xfa\xbc\xa2\x89\x04\xdbs%Gc\x00\x00\x00\xe0\x94j\xe5\u007f'\x91|V*\x13*M\x1b\xf7\xec\n\u01c5\x83)&\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4j\xeb\x9ftt.\xa4\x91\x81=\xbb\xf0\xd6\xfc\xde\x1a\x13\x1dM\xb3\x89\x17\xe5T0\x8a\xa00\x00\x00\u07d4j\xf25\u04bb\xe0P\xe6)\x16\x15\xb7\x1c\xa5\x82\x96X\x81\x01B\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4j\xf6\xc7\xee\x99\xdf'\x1b\xa1[\xf3\x84\xc0\xb7d\xad\xcbM\xa1\x82\x8965f3\xeb\xd8\xea\x00\x00\u07d4j\xf8\xe5Yih,q_H\xadO\xc0\xfb\xb6~\xb5\x97\x95\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4j\xf9@\xf6>\u0278\xd8v'*\u0296\xfe\xf6\\\xda\xce\xcd\ua262\xa1]\tQ\x9b\xe0\x00\x00\u07d4j\xf9\xf0\xdf\uebbb_d\xbf\x91\xabw\x16i\xbf\x05)US\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4j\xff\x14f\xc2b6u\xe3\xcb\x0eu\xe4#\xd3z%\xe4B\xeb\x89]\u0212\xaa\x111\xc8\x00\x00\xe0\x94k\r\xa2Z\xf2g\u05c3l\"k\xca\xe8\xd8r\xd2\xceR\xc9A\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4k\x10\xf8\xf8\xb3\xe3\xb6\r\xe9\n\xa1-\x15_\x9f\xf5\xff\xb2,P\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\x17Y\x8a\x8e\xf5Oyz\xe5\x15\u0336Q}\x18Y\xbf\x80\x11\x89\x05k\xc7^-c\x10\x00\x00\u07d4k \xc0\x80`jy\xc7;\xd8\xe7[\x11qzN\x8d\xb3\xf1\u00c9\x10?sX\x03\xf0\x14\x00\x00\u07d4k\"\x84D\x02!\xce\x16\xa88-\xe5\xff\x02)G\"i\xde\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4k0\xf1\x829\x10\xb8m:\xcbZj\xfc\x9d\xef\xb6\xf3\xa3\v\xf8\x89\u3bb5sr@\xa0\x00\x00\u07d4k8\u0784\x1f\xad\u007fS\xfe\x02\xda\x11[\xd8j\xaff$f\xbd\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4kK\x99\xcb?\xa9\xf7\xb7L\u3903\x17\xb1\xcd\x13\t\n\x1az\x89\x03\x1b2~i]\xe2\x00\x00\u07d4kZ\xe7\xbfx\xecu\xe9\f\xb5\x03\xc7x\xcc\u04f2KO\x1a\xaf\x89+^:\xf1k\x18\x80\x00\x00\u07d4kc\xa2\u07f2\xbc\xd0\xca\xec\x00\"\xb8\x8b\xe3\f\x14Q\xeaV\xaa\x89+\xdbk\xf9\x1f\u007fL\x80\x00\u07d4kew\xf3\x90\x9aMm\xe0\xf4\x11R-Ep8d\x004\\\x89e\xea=\xb7UF`\x00\x00\u07d4kr\xa8\xf0a\xcf\xe6\x99j\xd4G\xd3\xc7,(\xc0\xc0\x8a\xb3\xa7\x89\xe7\x8cj\u01d9\x12b\x00\x00\u07d4kv\rHw\xe6\xa6'\xc1\xc9g\xbe\xe4Q\xa8P}\xdd\u06eb\x891T\xc9r\x9d\x05x\x00\x00\u07d4k\x83\xba\xe7\xb5e$EXU[\xcfK\xa8\xda \x11\x89\x1c\x17\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\x92]\xd5\xd8\xeda2\xabm\b`\xb8,D\xe1\xa5\x1f\x1f\xee\x89P; >\x9f\xba \x00\x00\xe0\x94k\x94a]\xb7Pej\u00cc~\x1c\xf2\x9a\x9d\x13g\u007fN\x15\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4k\x95\x1aC'N\xea\xfc\x8a\t\x03\xb0\xaf.\xc9+\xf1\xef\xc89\x89\x05k\xc7^-c\x10\x00\x00\u07d4k\x99%!\xec\x85#p\x84\x8a\u0597\xcc-\xf6Nc\xcc\x06\xff\x8965\u026d\xc5\u07a0\x00\x00\u07d4k\xa8\xf7\xe2_\xc2\xd8qa\x8e$\xe4\x01\x84\x19\x917\xf9\xf6\xaa\x89\x15\xafd\x86\x9ak\xc2\x00\x00\u07d4k\xa9\xb2\x1b5\x10k\xe1Y\xd1\xc1\xc2ez\xc5l\u049f\xfdD\x89\xf2\xdc}G\xf1V\x00\x00\x00\u07d4k\xafz*\x02\xaex\x80\x1e\x89\x04\xadz\xc0Q\b\xfcV\xcf\xf6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94k\xb2\xac\xa2?\xa1bm\x18\xef\xd6w\u007f\xb9}\xb0-\x8e\n\xe4\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4k\xb4\xa6a\xa3:q\xd4$\u051b\xb5\xdf(b.\xd4\xdf\xfc\xf4\x89\",\x8e\xb3\xfff@\x00\x00\u07d4k\xb5\b\x13\x14j\x9a\xddB\xee\"\x03\x8c\x9f\x1fti\xd4\u007fG\x89\n\xdaUGK\x814\x00\x00\u07d4k\xbc?5\x8af\x8d\u0461\x1f\x03\x80\xf3\xf71\bBj\xbdJ\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4k\xbd\x1eq\x93\x90\xe6\xb9\x10C\xf8\xb6\xb9\u07c9\x8e\xa8\x00\x1b4\x89llO\xa6\xc3\xdaX\x80\x00\u07d4k\xc8Z\xcdY(r.\xf5\tS1\xee\x88\xf4\x84\xb8\u03c3W\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4k\xd3\xe5\x9f#\x9f\xaf\xe4wk\xb9\xbd\xddk\ue0fa]\x9d\x9f\x8965\u026d\xc5\u07a0\x00\x00\u07d4k\xd4W\xad\xe0Qy]\xf3\xf2F\\89\xae\xd3\xc5\xde\xe9x\x8964\xbf9\xab\x98x\x80\x00\u07d4k\xe1c\x13d>\xbc\x91\xff\x9b\xb1\xa2\xe1\x16\xb8T\xea\x93:E\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4k\xe7Y^\xa0\xf0hH\x9a'\x01\xecFI\x15\x8d\xdcC\xe1x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\xe9\x03\x0e\xe6\xe2\xfb\u0111\xac\xa3\xde@\"\xd3\x01w+{}\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94k\xec1\x1a\xd0P\b\xb4\xaf5<\x95\x8c@\xbd\x06s\x9a?\xf3\x8a\x03w\xf6*\x0f\nbp\x00\x00\u07d4k\xf7\xb3\xc0e\xf2\xc1\xe7\xc6\xeb\t+\xa0\xd1Pf\xf3\x93\u0478\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4k\xf8o\x1e/+\x802\xa9\\Mw8\xa1\t\xd3\xd0\xed\x81\x04\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4l\x05\xe3N^\xf2\xf4.\u041d\xef\xf1\x02l\xd6k\xcbi`\xbb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4l\b\xa6\xdc\x01s\xc74)U\xd1\xd3\xf2\xc0e\xd6/\x83\xae\u01c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4l\n\xe9\xf0C\xc84\xd4Bq\xf14\x06Y=\xfe\tO8\x9f\x89RD*\xe13\xb6*\x80\x00\u07d4l\f\xc9\x17\xcb\xee}|\t\x97c\xf1Nd\xdf}4\xe2\xbf\t\x89\r\x8drkqw\xa8\x00\x00\xe0\x94l\x0eq/@\\Yr_\xe8)\xe9wK\xf4\xdf\u007fM\xd9e\x8a\f(h\x88\x9c\xa6\x8aD\x00\x00\xe0\x94l\x10\x12\x05\xb3#\xd7uD\xd6\xdcR\xaf7\xac\xa3\xce\xc6\xf7\xf1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4l\x15\xec5 \xbf\x8e\xbb\xc8 \xbd\x0f\xf1\x97x7T\x94\u03dd\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\xe0\x94l\x1d\xdd3\xc8\x19f\u0706!w`q\xa4\x12\x94\x82\xf2\xc6_\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4l%2\u007f\x8d\u02f2\xf4^V\x1e\x86\xe3]\x88P\xe5:\xb0Y\x89;\xcd\xf9\xba\xfe\xf2\xf0\x00\x00\u07d4l.\x9b\xe6\u052bE\x0f\xd1%1\xf3?\x02\x8caFt\xf1\x97\x89\xc2\x12z\xf8X\xdap\x00\x00\u07d4l5\x9eX\xa1=Ex\xa93\x8e3\\g\xe7c\x9f_\xb4\u05c9\v\xd1[\x94\xfc\x8b(\x00\x00\u07d4l=\x18pA&\xaa\x99\xee3B\xce`\xf5\xd4\xc8_\x18g\u0349\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4lGK\xc6jTx\x00f\xaaOQ.\xef\xa7s\xab\xf9\x19\u01c9\x05\x18\x83\x15\xf7v\xb8\x00\x00\u07d4lNBn\x8d\xc0\x05\u07e3Ql\xb8\xa6\x80\xb0.\ua56e\x8e\x89Hz\x9a0E9D\x00\x00\u07d4lR\xcf\b\x95\xbb5\xe6V\x16\x1eM\xc4j\xe0\xe9m\xd3\xe6,\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4lT\"\xfbK\x14\xe6\u064b`\x91\xfd\xecq\xf1\xf0\x86@A\x9d\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4l\\:T\u0367\xc2\xf1\x18\xed\xbaCN\xd8\x1en\xbb\x11\xddz\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4lc\xf8EV\u0490\xbf\u0359\xe44\ue657\xbf\xd7yWz\x89lk\x93[\x8b\xbd@\x00\x00\u07d4lc\xfc\x85\x02\x9a&T\u05db+\xeaM\xe3I\xe4REw\u0149#\xc7W\a+\x8d\xd0\x00\x00\u07d4led\xe5\xc9\xc2N\xaa\xa7D\xc9\xc7\xc9h\xc9\xe2\xc9\xf1\xfb\xae\x89I\x9bB\xa2\x119d\x00\x00\xe0\x94lg\xd6\xdb\x1d\x03Ql\x12\x8b\x8f\xf24\xbf=I\xb2m)A\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4lg\xe0\u05f6.*\bPiE\xa5\xdf\xe3\x82c3\x9f\x1f\"\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4lj\xa0\xd3\vdr\x19\x90\xb9PJ\x86?\xa0\xbf\xb5\xe5}\xa7\x89\x92^\x06\xee\xc9r\xb0\x00\x00\u07d4lqJX\xff\xf6\xe9}\x14\xb8\xa5\xe3\x05\xeb$@eh\x8b\xbd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4l\x80\rKI\xba\a%\x04`\xf9\x93\xb8\xcb\xe0\v&j%S\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4l\x80\x8c\xab\xb8\xff_\xbbc\x12\xd9\xc8\xe8J\xf8\xcf\x12\xef\bu\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94l\x82 )!\x8a\xc8\xe9\x8a&\f\x1e\x06@)4\x889\x87[\x8a\x01\x0f\x97\xb7\x87\xe1\xe3\b\x00\x00\u07d4l\x84\u02e7|m\xb4\xf7\xf9\x0e\xf1=^\xe2\x1e\x8c\xfc\u007f\x83\x14\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94l\x86\x87\xe3Aw\x10\xbb\x8a\x93U\x90!\xa1F\x9ej\x86\xbcw\x8a\x02[-\xa2x\xd9k{\x80\x00\xe0\x94l\x88,'s,\xef\\|\x13\xa6\x86\xf0\xa2\xeawUZ\u0089\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4l\xa5\xde\x00\x81}\xe0\xce\xdc\xe5\xfd\x00\x01(\xde\xde\x12d\x8b<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4l\xa6\xa12\xce\x1c\u0488\xbe\xe3\x0e\xc7\xcf\xef\xfb\x85\xc1\xf5\nT\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94l\xb1\x1e\xcb2\xd3\u0382\x96\x011\x066\xf5\xa1\f\xf7\u03db_\x8a\x04?\u851c8\x01\xf5\x00\x00\u07d4l\xc1\xc8x\xfal\u078a\x9a\v\x83\x11$~t\x1eFB\xfem\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94l\xcb\x03\xac\xf7\xf5<\xe8z\xad\xcc!\xa9\x93-\xe9\x15\xf8\x98\x04\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4l\xd2\x12\xae\xe0N\x01?=*\xba\u04a0#`k\xfb\\j\u01c9lj\xccg\u05f1\xd4\x00\x00\u07d4l\xd2(\xdcq!i0\u007f\xe2|\xebtw\xb4\x8c\xfc\x82r\xe5\x89\x044\xea\x94\u06caP\x00\x00\u07d4l\xe1\xb0\xf6\xad\xc4pQ\xe8\xab8\xb3\x9e\xdbA\x86\xb0;\xab\u0309Ay\x97\x94\xcd$\xcc\x00\x00\u07d4l\xea\xe3s=\x8f\xa4=l\xd8\f\x1a\x96\xe8\xeb\x93\x10\x9c\x83\xb7\x89\x10'\x94\xad \xdah\x00\x00\u07d4m\x05i\xe5U\x8f\xc7\xdf'f\xf2\xba\x15\u070a\xef\xfc[\xebu\x89\xd8\xe6\x00\x1el0+\x00\x00\u07d4m\x12\x0f\f\xaa\xe4O\xd9K\xca\xfeU\xe2\xe2y\uf5ba\\z\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4m\x14V\xff\xf0\x10N\xe8D\xa31G7\x8438\xd2L\xd6l\x89\a\xb0l\xe8\u007f\xddh\x00\x00\u07d4m \xef\x97\x04g\nP\v\xb2i\xb5\x83.\x85\x98\x02\x04\x9f\x01\x89\a\f\x1c\xc7;\x00\xc8\x00\x00\xe0\x94m/\x97g4\xb9\xd0\a\r\x18\x83\xcfz\u02b8\xb3\xe4\x92\x0f\xc1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4m9\xa9\u93c1\xf7i\xd7:\xad,\xea\xd2v\xac\x13\x87\xba\xbe\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4m;x6\xa2\xb9\u0619r\x1aM#{R#\x85\xdc\xe8\xdf\u034966\xc2^f\xec\xe7\x00\x00\u07d4m?+\xa8V\u033b\x027\xfava\x15k\x14\xb0\x13\xf2\x12@\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94m@\b\xb4\xa8\x88\xa8&\xf2H\xeej\v\r\xfd\xe9\xf92\x10\xb9\x8a\x01'\xfc\xb8\xaf\xae \xd0\x00\x00\u07d4m@\xca'\x82m\x97s\x1b>\x86\xef\xfc\u05f9*Aa\xfe\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4mD\x97J1\u0447\xed\xa1m\xddG\xb9\xc7\xecP\x02\xd6\x1f\xbe\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94mK\\\x05\xd0j \x95~\x17H\xabm\xf2\x06\xf3C\xf9/\x01\x8a\x02\x1f6\x06\x99\xbf\x82_\x80\x00\xe0\x94mL\xbf=\x82\x84\x83:\xe9\x93D0>\b\xb4\xd6\x14\xbf\xda;\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4mY\xb2\x1c\xd0\xe2t\x88\x04\u066b\xe0d\xea\u00be\xf0\xc9_'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4mc\u04ce\xe8\xb9\x0e\x0en\xd8\xf1\x92\xed\xa0Q\xb2\u05a5\x8b\xfd\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4mf4\xb5\xb8\xa4\x01\x95\xd9I\x02z\xf4\x82\x88\x02\t,\ued89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94m}\x1c\x94\x95\x11\xf8\x83\x03\x80\x8c`\xc5\xea\x06@\xfc\xc0&\x83\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4m\x84m\xc1&W\xe9\x1a\xf2P\bQ\x9c>\x85\u007fQp}\u0589\xf8\xd3\v\xc9#B\xf8\x00\x00\u07d4m\x91\x93\x99k\x19F\x17!\x11\x06\xd1c^\xb2l\u0136ll\x89\x15\xaa\x1e~\x9d\xd5\x1c\x00\x00\u07d4m\x99\x97P\x98\x82\x02~\xa9G#\x14$\xbe\xde\xde)e\u043a\x89l\x81\u01f3\x11\x95\xe0\x00\x00\u07d4m\xa0\xed\x8f\x1di3\x9f\x05\x9f*\x0e\x02G\x1c\xb4O\xb8\u00fb\x892\xbc8\xbbc\xa8\x16\x00\x00\u07d4m\xb7+\xfdC\xfe\xf4e\xcaV2\xb4Z\xabra@N\x13\xbf\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94m\xbe\x8a\xbf\xa1t(\x06&9\x817\x1b\xf3\xd3U\x90\x80kn\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4m\xc3\xf9+\xaa\x1d!\u06b78+\x892a\xa05o\xa7\xc1\x87\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4m\xc7\x05:q\x86\x16\xcf\u01cb\xeec\x82\xeeQ\xad\xd0\xc7\x030\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94m\xcc~d\xfc\xaf\xcb\xc2\xdcl\x0e^f,\xb3G\xbf\xfc\xd7\x02\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4m\xda_x\x8alh\x8d\u07d2\x1f\xa3\x85.\xb6\xd6\xc6\xc6)f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4m\xdb`\x92w\x9dXB\xea\xd3x\xe2\x1e\x81 \xfdLk\xc12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4m\xdf\xefc\x91U\u06ab\n\\\xb4\x95:\xa8\u016f\xaa\x88\x04S\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4m\xe0/-\xd6~\xfd\xb794\x02\xfa\x9e\xaa\xcb\xcfX\x9d.V\x89@\x13\x8b\x91~\u07f8\x00\x00\u07d4m\u4d418\\\xf7\xfc\x9f\xe8\xc7}\x13\x1f\xe2\xeew$\xc7j\x89})\x97s=\xcc\xe4\x00\x00\u07d4m\xe4\xd1R\x19\x18/\xaf:\xa2\xc5\xd4\xd2Y_\xf20\x91\xa7'\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4m\xed\xf6.t?M,*K\x87\xa7\x87\xf5BJz\xeb9<\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4m\xf2Of\x85\xa6/y\x1b\xa37\xbf?\xf6~\x91\xf3\u053c:\x89ukI\xd4\nH\x18\x00\x00\u07d4m\xf5\xc8O{\x90\x9a\xab>a\xfe\x0e\xcb\x1b;\xf2`\"*\u0489\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4m\xff\x90\xe6\xdc5\x9d%\x90\x88+\x14\x83\xed\xbc\xf8\x87\xc0\xe4#\x8965\u026d\xc5\u07a0\x00\x00\u07d4n\x01\xe4\xadV\x9c\x95\xd0\a\xad\xa3\r^-\xb1(\x88I\"\x94\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\a;f\u0478\xc6gD\u0600\x96\xa8\u0759\xec~\x02(\u0689\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\x0e\xe7\x06\x12\xc9v(}I\x9d\u07e6\xc0\xdc\xc1,\x06\xde\xea\x89\a\v\u0579V!F\x00\x00\xe0\x94n\x12\xb5\x1e\"[JCr\xe5\x9a\u05e2\xa1\xa1>\xa3\u04e17\x8a\x03\x00F\xc8\xccw_\x04\x00\x00\u07d4n\x1a\x04l\xaf[JW\xf4\xfdK\xc1sb!&\xb4\xe2\xfd\x86\x89a\t=|,m8\x00\x00\u07d4n\x1e\xa4\xb1\x83\xe2R\u027bwg\xa0\x06\u05346\x96\u02ca\xe9\x89\x0f\xf3x<\x85\xee\u0400\x00\u07d4n%[p\n\xe7\x13\x8aK\xac\xf2(\x88\xa9\xe2\xc0\n(^\xec\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n'\n\xd5)\xf1\xf0\xb8\xd9\xcbm$'\xec\x1b~-\xc6Jt\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4n.\xab\x85\u0709\xfe)\xdc\n\xa1\x852G\u06b4:R=V\x89\x04V9\x18$O@\x00\x00\u07d4n:Q\xdbt=3M/\xe8\x82$\xb5\xfe|\x00\x8e\x80\xe6$\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4nL*\xb7\xdb\x02i9\xdb\u04fch8J\xf6`\xa6\x18\x16\xb2\x89\t\r\x97/22<\x00\x00\u07d4nM.9\u0203f)\u5d07\xb1\x91\x8af\x9a\xeb\u07556\x8965\u026d\xc5\u07a0\x00\x00\u07d4n\\-\x9b\x1cTj\x86\xee\xfd]\nQ \xc9\xe4\xe70\x19\x0e\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4n`\xae\u19cf\x8e\u068bBLs\xe3S5J\xe6|0B\x89\xbd5\xa4\x8d\x99\x19\xe6\x00\x00\u07d4nd\xe6\x12\x9f\"N7\x8c\x0ensj~z\x06\xc2\x11\xe9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4nm[\xbb\xb9\x05;\x89\xd7D\xa2s\x16\u00a7\xb8\xc0\x9bT}\x891Rq\n\x02>m\x80\x00\u07d4nr\xb2\xa1\x18j\x8e)\x16T;\x1c\xb3jh\x87\x0e\xa5\u0457\x89\n\x15D\xbe\x87\x9e\xa8\x00\x00\u07d4nv\x1e\xaa\x0f4_w{TA\xb7:\x0f\xa5\xb5k\x85\xf2-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4ny\xed\u0504[\anL\u060d\x18\x8bnC-\xd9?5\xaa\x893\xc5I\x901r\f\x00\x00\u07d4n\x82\x12\xb7\"\xaf\xd4\b\xa7\xa7>\xd3\xe29^\xe6EJ\x030\x89\b\x9e\x91y\x94\xf7\x1c\x00\x00\u07d4n\x84\x87m\xbb\x95\xc4\vfV\xe4+\xa9\xae\xa0\x8a\x99;T\u0709;\xbc`\xe3\xb6\u02fe\x00\x00\u07d4n\x84\xc2\xfd\x18\xd8\tW\x14\xa9h\x17\x18\x9c\xa2\x1c\xcab\xba\xb1\x89\x12{lp&!\u0340\x00\u07d4n\x86m\x03-@Z\xbd\xd6\\\xf6QA\x1d\x807\x96\xc2#\x11\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94n\x89\x9eY\xa9\xb4\x1a\xb7\xeaA\xdfu\x17\x86\x0f*\xcbY\xf4\xfd\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4n\x89\xc5\x1e\xa6\xde\x13\xe0l\xdct\x8bg\xc4A\x0f\u9f2b\x03\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\x8a&h\x9fz/\xde\xfd\x00\x9c\xba\xaaS\x10%4P\u06ba\x89o!7\x17\xba\xd8\xd3\x00\x00\u07d4n\x96\xfa\xed\xa3\x05C\x02\xc4_X\xf1a2L\x99\xa3\xee\xbbb\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4n\xb0\xa5\xa9\xae\x96\xd2,\xf0\x1d\x8f\xd6H;\x9f8\xf0\x8c,\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xb3\x81\x96\x17@@X&\x8f\f<\xff5\x96\xbf\xe9\x14\x8c\x1c\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94n\xb5W\x8ak\xb7\xc3!S\x19[\r\x80 \xa6\x91HR\xc0Y\x8a\x8b\u00ab\xf4\x02!\xf4\x80\x00\x00\u07d4n\xbb^iW\xaa\x82\x1e\xf6Y\xb6\x01\x8a9:PL\xaeDP\x89lk\x93[\x8b\xbd@\x00\x00\u07d4n\xbc\xf9\x95\u007f_\xc5\u916d\xd4u\";\x04\xb8\xc1Jz\xed\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4n\xc3e\x95q\xb1\x1f\x88\x9d\xd49\xbc\xd4\xd6u\x10\xa2[\xe5~\x89\x06\xaa\xf7\xc8Qm\f\x00\x00\u07d4n\u021b9\xf9\xf5'jU>\x8d\xa3\x0en\xc1z\xa4~\xef\u01c9\x18BO_\v\x1bN\x00\x00\u07d4n\xc9m\x13\xbd\xb2M\u01e5W)?\x02\x9e\x02\xddt\xb9zU\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xca\xef\xa6\xfc>\xe54bm\xb0,o\x85\xa0\u00d5W\x1ew\x89 \x86\xac5\x10R`\x00\x00\u07d4n\u04a1+\x02\xf8\u0188\u01f5\u04e6\xea\x14\xd66\x87\u06b3\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4n\u0604E\x9f\x80\x9d\xfa\x10\x16\xe7p\xed\xaf>\x9f\xefF\xfa0\x89\xb8R\xd6x \x93\xf1\x00\x00\xe0\x94n\xdf\u007fR\x83r\\\x95>\xe6C\x17\xf6a\x88\xaf\x11\x84\xb03\x8a\x01\xb4d1\x1dE\xa6\x88\x00\x00\u07d4n\xe8\xaa\xd7\xe0\xa0e\u0605-|;\x9an_\xdcK\xf5\f\x00\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4n\xef\u0705\x0e\x87\xb7\x15\xc7'\x91w<\x03\x16\xc3U\x9bX\xa4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xf9\xe8\u0276!}Vv\x9a\xf9}\xbb\x1c\x8e\x1b\x8b\xe7\x99\u0489\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4n\xfb\xa8\xfb*\u0176s\a)\xa9r\xec\"D&\xa2\x87\u00ed\x89\x0fY\x85\xfb\xcb\xe1h\x00\x00\xe0\x94n\xfd\x90\xb55\xe0\v\xbd\x88\x9f\xda~\x9c1\x84\xf8y\xa1Q\u06ca\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4o\x05\x16f\xcbO{\u04b1\x90r!\xb8)\xb5U\u05e3\xdbt\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4o\x0e\xdd#\xbc\xd8_`\x15\xf9(\x9c(\x84\x1f\xe0L\x83\xef\xeb\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4o\x13zq\xa6\xf1\x97\xdf,\xbb\xf0\x10\u073d\x89a\t=|,m8\x00\x00\u07d4p\x10\xbe-\xf5{\u042b\x9a\xe8\x19l\xd5\n\xb0\xc5!\xab\xa9\xf9\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4p#\xc7\tV\xe0J\x92\xd7\x00%\xaa\u0497\xb59\xaf5Xi\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p%\x96]+\x88\xda\x19}DY\xbe=\xc98cD\xcc\x1f1\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d4p(\x02\xf3m\x00%\x0f\xabS\xad\xbc\u0596\xf0\x17oc\x8aI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4pH\x19\xd2\xe4Mn\xd1\xda%\xbf\u0384\u011f\u0322V\x13\xe5\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4pJn\xb4\x1b\xa3O\x13\xad\xdd\xe7\xd2\xdb}\xf0I\x15\u01e2!\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4pJ\xb1\x15\r^\x10\xf5\xe3I\x95\b\xf0\xbfpe\x0f\x02\x8dK\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4pJ\xe2\x1dv-n\x1d\xde(\xc25\xd11\x04Yr6\xdb\x1a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4pM$<)x\xe4l,\x86\xad\xbe\xcd$n;)_\xf63\x89m\x12\x1b\xeb\xf7\x95\xf0\x00\x00\u07d4pM]\xe4\x84m9\xb5<\xd2\x1d\x1cI\xf0\x96\xdb\\\x19\xba)\x89\b=lz\xabc`\x00\x00\u07d4p]\xdd85T\x82\xb8\xc7\u04f5\x15\xbd\xa1P\r\xd7\u05e8\x17\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94pan(\x92\xfa&\x97\x05\xb2\x04k\x8f\xe3\xe7/\xa5X\x16\u04ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4pg\x0f\xbb\x05\xd30\x14DK\x8d\x1e\x8ew\x00%\x8b\x8c\xaam\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\x81\xfak\xaa\xd6\u03f7\xf5\x1b,\xca\x16\xfb\x89p\x99\x1ad\xba\x89\f\xae\xc0\x05\xf6\xc0\xf6\x80\x00\xe0\x94p\x85\xae~~M\x93!\x97\xb5\u01c5\x8c\x00\xa3gF&\xb7\xa5\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4p\x86\xb4\xbd\xe3\xe3]J\xeb$\xb8%\xf1\xa2\x15\xf9\x9d\x85\xf7E\x89lh\xcc\u041b\x02,\x00\x00\u07d4p\x8a*\xf4%\u03b0\x1e\x87\xff\xc1\xbeT\xc0\xf52\xb2\x0e\xac\u0589\aE\u0503\xb1\xf5\xa1\x80\x00\u07d4p\x8e\xa7\a\xba\xe45\u007f\x1e\xbe\xa9Y\u00e2P\xac\u05aa!\xb3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794p\x8f\xa1\x1f\xe3=\x85\xad\x1b\xef\u02ee8\x18\xac\xb7\x1fj}~\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4p\x9101\x16\xd5\xf28\x9b##\x8bMej\x85\x96\u0644\u04c9;N~\x80\xaaX3\x00\x00\u07d4p\x99\xd1/n\xc6V\x89\x9b\x04\x9avW\x06]b\x99h\x92\u0209\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4p\x9f\xe9\xd2\xc1\xf1\xceB |\x95\x85\x04J`\x89\x9f5\x94/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\xa05I\xaaah\xe9~\x88\xa5\b3\nZ\v\xeatq\x1a\x89Hz\x9a0E9D\x00\x00\u07d4p\xa4\x06}D\x8c\xc2]\xc8\xe7\x0ee\x1c\xea|\xf8N\x92\x10\x9e\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4p\xab4\xbc\x17\xb6o\x9c;c\xf1Q'O*r|S\x92c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\xc2\x13H\x8a\x02\f<\xfb9\x01N\xf5\xbad\x04rK\u02a3\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4p\xd2^\xd2\u022d\xa5\x9c\b\x8c\xf7\r\xd2+\xf2\u06d3\xac\xc1\x8a\x899GEE\u4b7c\x00\x00\u07d4p\xe5\xe9\xdas_\xf0w$\x9d\u02da\xaf=\xb2\xa4\x8d\x94\x98\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4p\xfe\xe0\x8b\x00\xc6\xc2\xc0Jp\xc0\xce=\x92\u03ca\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u0794q\v\xe8\xfd^)\x18F\x8b\u2abe\xa8\r\x82\x845\u05d6\x12\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4q\x13]\x8f\x05\x96<\x90ZJ\a\x92)\t#Z\x89jR\ua262\xa1]\tQ\x9b\xe0\x00\x00\u07d4q\x1e\xcfw\xd7\x1b=\x0e\xa9\\\xe4u\x8a\xfe\u0379\xc11\a\x9d\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4q!?\xca14\x04 N\u02e8q\x97t\x1a\xa9\xdf\xe9c8\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94q+vQ\x02\x14\xdcb\x0fl:\x1d\u049a\xa2+\xf6\xd2\x14\xfb\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4q/\xf77\n\x13\xed6\ts\xfe\u071f\xf5\xd2\xc9:P^\x9e\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4q3\x84:x\xd99\u019dD\x86\xe1\x0e\xbc{`*4\x9f\xf7\x89\x11\xd5\xca\xcc\xe2\x1f\x84\x00\x00\u07d4qH\xae\xf32a\xd8\x03\x1f\xac?q\x82\xff5\x92\x8d\xafT\u0649\xdeB\xee\x15D\u0750\x00\x00\u07d4qcu\x8c\xbblLR^\x04\x14\xa4\n\x04\x9d\xcc\xcc\xe9\x19\xbb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4qh\xb3\xbb\x8c\x16s!\u067d\xb0#\xa6\xe9\xfd\x11\xaf\u026f\u0649a\t=|,m8\x00\x00\u07d4qirN\xe7\"q\xc54\xca\xd6B\x0f\xb0N\xe6D\u02c6\xfe\x89\x16<+@\u06e5R\x00\x00\u07d4qj\xd3\xc3:\x9b\x9a\n\x18\x96sW\x96\x9b\x94\xee}*\xbc\x10\x89\x1a!\x17\xfeA*H\x00\x00\xe0\x94qk\xa0\x1e\xad*\x91'\x065\xf9_%\xbf\xaf-\xd6\x10\xca#\x8a\ty\xe7\x01 V\xaax\x00\x00\u07d4qmP\u0320\x1e\x93\x85\x00\xe6B\x1c\xc0p\xc3P|g\u04c7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4qv,cg\x8c\x18\xd1\xc67\x8c\xe0h\xe6f8\x13\x15\x14~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4qxL\x10Q\x17\xc1\xf6\x895y\u007f\xe1Y\xab\xc7NC\xd1j\x89l\x81\u01f3\x11\x95\xe0\x00\x00\xe0\x94qyro\\q\xae\x1bm\x16\xa6\x84(\x17Nk4\xb26F\x8a\x01\x8e\xa2P\t|\xba\xf6\x00\x00\xe0\x94q|\xf9\xbe\xab680\x8d\xed~\x19^\f\x86\x13-\x16?\xed\x8a\x032n\xe6\xf8e\xf4\"\x00\x00\u07d4q\x80\xb8>\xe5WC\x17\xf2\x1c\x80r\xb1\x91\u0615\xd4aS\u00c9\x18\xef\xc8J\xd0\u01f0\x00\x00\u07d4q\x94kq\x17\xfc\x91^\xd1\a8_B\u065d\xda\xc62I\u0089lk\x93[\x8b\xbd@\x00\x00\xe0\x94q\x9e\x89\x1f\xbc\xc0\xa3>\x19\xc1-\xc0\xf0 9\xca\x05\xb8\x01\u07ca\x01OU8F:\x1bT\x00\x00\u07d4q\xc7#\n\x1d5\xbd\u0581\x9e\u0539\xa8\x8e\x94\xa0\xeb\a\x86\u0749\uc80b5=$\x14\x00\x00\u07d4q\xd2\xccm\x02W\x8ce\xf7\r\xf1\x1bH\xbe\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4r\x83\xcdFu\xdaX\u0116UaQ\xda\xfd\x80\xc7\xf9\x95\xd3\x18\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4r\x86\xe8\x9c\xd9\u078fz\x8a\x00\xc8o\xfd\xb59\x92\u0752Q\u0449i*\xe8\x89p\x81\xd0\x00\x00\u07d4r\x8f\x9a\xb0\x80\x15}\xb3\a1V\xdb\xca\x1a\x16\x9e\xf3\x17\x94\a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4r\x94\xc9\x18\xb1\xae\xfbM%\x92~\xf9\u05d9\xe7\x1f\x93\xa2\x8e\x85\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94r\x94\uc763\x10\xbckK\xbd\xf5C\xb0\xefE\xab\xfc>\x1bM\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4r\x9a\xadF'tNS\xf5\xd6c\t\xaatD\x8b:\xcd\xf4o\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xa2\xfc\x86u\xfe\xb9r\xfaA\xb5\r\xff\u06fa\xe7\xfa*\u07f7\x89\x9a\xb4\xfcg\xb5(\xc8\x00\x00\u07d4r\xa8&\b&)G&\xa7[\xf3\x9c\u066a\x9e\a\xa3\xea\x14\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4r\xb0Yb\xfb*\u0549\xd6Z\xd1j\"U\x9e\xba\x14X\xf3\x87\x89\a?u\u0460\x85\xba\x00\x00\u07d4r\xb5c?\xe4w\xfeT.t/\xac\xfdi\f\x13xT\xf2\x16\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4r\xb7\xa0=\xda\x14\u029cf\x1a\x1dF\x9f\xd376\xf6s\xc8\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xb9\x04D\x0e\x90\xe7 \u05ac\x1c*\u05dc2\x1d\xcc\x1c\x1a\x86\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94r\xb9\nM\xc0\x97#\x94\x92\u0179w}\xcd\x1eR\xba+\xe2\u008a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4r\xbb'\u02d9\xf3\xe2\xc2\u03d0\xa9\x8fp}0\xe4\xa2\x01\xa0q\x89X\xe7\x92n\xe8X\xa0\x00\x00\xe0\x94r\xc0\x83\xbe\xad\xbd\xc2'\xc5\xfbC\x88\x15\x97\xe3.\x83\xc2`V\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4r\xcd\x04\x8a\x11\x05tH)\x83I-\xfb\x1b\xd2yB\xa6\x96\xba\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xd0=M\xfa\xb3P\f\xf8\x9b\x86\x86o\x15\xd4R\x8e\x14\xa1\x95\x89\xf3K\x82\xfd\x8e\x91 \x00\x00\u07d4r\u06bb[n\ud799\xbe\x91X\x88\xf6V\x80V8\x16\b\xf8\x89\vL\x96\xc5,\xb4\xfe\x80\x00\u07d4r\xfbI\u009d#\xa1\x89P\u0132\xdc\r\xdfA\x0fS-oS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xfe\xaf\x12EyR9Td[\u007f\xaf\xff\x03x\xd1\xc8$.\x8965\u026d\xc5\u07a0\x00\x00\u07d4s\x01\xdcL\xf2mq\x86\xf2\xa1\x1b\xf8\xb0\x8b\xf2)F?d\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s\x04G\xf9|\xe9\xb2_\"\xba\x1a\xfb6\xdf'\xf9Xk\ub6c9,s\xc97t,P\x00\x00\u07d4s\x06\xde\x0e(\x8bV\xcf\u07d8~\xf0\xd3\xcc)f\a\x93\xf6\u0749\x1b\x8a\xbf\xb6.\xc8\xf6\x00\x00\xe0\x94s\r\x87c\u01a4\xfd\x82J\xb8\xb8Y\x16\x1e\xf7\xe3\xa9j\x12\x00\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4s\x12\x81sH\x95(\x01.v\xb4\x1a^(\u018b\xa4\xe3\xa9\u050965\u026d\xc5\u07a0\x00\x00\u07d4s\x13F\x12\bETUFTE\xa4Y\xb0l7s\xb0\xeb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s/\xea\xd6\x0f{\xfd\u05a9\xde\u0101%\xe3s]\xb1\xb6eO\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4sB#\xd2\u007f\xf2>Y\x06\xca\xed\"YW\x01\xbb4\x83\f\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4sG>r\x11Q\x10\xd0\xc3\xf1\x17\b\xf8nw\xbe+\xb0\x98<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4sRXm\x02\x1a\xd0\xcfw\xe0\xe9(@JY\xf3t\xffE\x82\x89\xb8Pz\x82\a( \x00\x00\u07d4sU\v\xebs+\xa9\u076f\xdaz\xe4\x06\xe1\x8f\u007f\xeb\x0f\x8b\xb2\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4s[\x97\xf2\xfc\x1b\xd2K\x12\an\xfa\xf3\xd1(\x80s\xd2\f\x8c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4s^2\x86f\xedV7\x14+3\x06\xb7|\xccT`\xe7,=\x89j\xb8\xf3xy\u0251\x00\x00\u07d4sc\u0350\xfb\xab[\xb8\u011a\xc2\x0f\xc6,9\x8f\xe6\xfbtL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4skDP=\xd2\xf6\xddTi\xffL[-\xb8\xeaO\xece\u0409\x11\x04\xeeu\x9f!\xe3\x00\x00\xe0\x94sk\xf1@,\x83\x80\x0f\x89>X1\x92X*\x13N\xb52\xe9\x8a\x02\x1e\x19\u0493\xc0\x1f&\x00\x00\xe0\x94s\x8c\xa9M\xb7\u038b\xe1\xc3\x05l\u0598\x8e\xb3v5\x9f3S\x8a\x05f[\x96\xcf5\xac\xf0\x00\x00\u07d4s\x91K\"\xfc/\x13\x15\x84$}\x82\xbeO\ucfd7\x8a\u053a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s\x93'\t\xa9\u007f\x02\u024eQ\xb0\x911(e\x12#\x85\xae\x8e\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4s\x93\xcb\xe7\xf9\xba!e\xe5\xa7U5\x00\xb6\xe7]\xa3\xc3:\xbf\x89\x05k\xc7^-c\x10\x00\x00\u07d4s\xb4\u0519\xde?8\xbf5\xaa\xf7i\xa6\xe3\x18\xbcm\x126\x92\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94s\xbe\xddo\xda{\xa3'!\x85\b{cQ\xfc\x13=HN7\x8a\x01\x12&\xbf\x9d\xceYx\x00\x00\u07d4s\xbf\xe7q\x0f1\u02b9I\xb7\xa2`O\xbfR9\xce\xe7\x90\x15\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94s\u03c0\xae\x96\x88\xe1X\x0eh\xe7\x82\xcd\b\x11\xf7\xaaIM,\x8a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\xe0\x94s\xd7&\x9f\xf0l\x9f\xfd3uL\xe5\x88\xf7J\x96j\xbb\xbb\xba\x8a\x01e\xc9fG\xb3\x8a \x00\x00\u07d4s\xd8\xfe\xe3\u02c6M\xce\"\xbb&\u029c/\bm^\x95\xe6;\x8965\u026d\xc5\u07a0\x00\x00\u07d4s\xdf<>yU\xf4\xf2\xd8Y\x83\x1b\xe3\x80\x00\xb1\ak8\x84\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4s\u48b6\f\U0010e2ef+w~\x17Z[\x1eM\f-\x8f\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94t\n\xf1\xee\xfd3e\u05cb\xa7\xb1,\xb1\xa6s\xe0j\arF\x8a\x04+\xf0kx\xed;P\x00\x00\xe0\x94t\v\xfdR\xe0\x16g\xa3A\x9b\x02\x9a\x1b\x8eEWj\x86\xa2\u06ca\x03\x8e\xba\xd5\xcd\xc9\x02\x80\x00\x00\u07d4t\x0fd\x16\x14w\x9d\u03e8\x8e\xd1\xd4%\xd6\r\xb4*\x06\f\xa6\x896\"\xc6v\b\x10W\x00\x00\u07d4t\x12\u027c0\xb4\xdfC\x9f\x021\x00\xe69$\x06j\xfdS\xaf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4t\x16\x93\xc3\x03vP\x85\x13\b \xcc+c\xe9\xfa\x92\x13\x1b\x89A\rXj \xa4\xc0\x00\x00\u07d4t!\xce[\xe3\x81s\x8d\u0703\xf0&!\x97O\xf0hly\xb8\x89Xx\x8c\xb9K\x1d\x80\x00\x00\u07d4t1j\xdf%7\x8c\x10\xf5v\u0574\x1aoG\xfa\x98\xfc\xe3=\x89\x128\x13\x1e\\z\xd5\x00\x00\u07d4t6Q\xb5^\xf8B\x9d\xf5\f\xf8\x198\xc2P\x8d\xe5\u0207\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t=\xe5\x00&\xcag\xc9M\xf5O\x06b`\xe1\xd1J\xcc\x11\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tE /\ft)z\x00N\xb3rj\xa6\xa8-\xd7\xc0/\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tK\x03\xbb\xa8X*\xe5I\x8e-\xc2-\x19\x94\x94g\xabS\xfc\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4tL\fw\xba\u007f#i \xd1\xe44\xde]\xa3>H\xeb\xf0,\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4tP\xff\u007f\x99\xea\xa9\x11bu\u07ach\xe4(\xdf[\xbc\u0639\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tV\u0172\xc5Cn>W\x10\b\x93?\x18\x05\xcc\xfe4\xe9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4tZ\u04eb\xc6\xee\xeb$qh\x9bS\x9ex\x9c\xe2\xb8&\x83\x06\x89=A\x94\xbe\xa0\x11\x92\x80\x00\xe0\x94tZ\xec\xba\xf9\xbb9\xb7Jg\xea\x1c\xe6#\xde6\x84\x81\xba\xa6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4t\\\xcf-\x81\x9e\u06fd\u07a8\x11{\\I\xed<*\x06n\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4tb\u021c\xaa\x9d\x8dx\x91\xb2T]\xef!otd\u057b!\x89\x05\xea\xedT\xa2\x8b1\x00\x00\u07d4td\x8c\xaa\xc7H\xdd\x13\\\xd9\x1e\xa1L(\xe1\xbdM\u007f\xf6\xae\x89\xa8\r$g~\xfe\xf0\x00\x00\xe0\x94tq\xf7.\xeb0\x06$\xeb(.\xabM\x03r\x00\x00\x00\xe0\x94t\x84\xd2k\xec\xc1\xee\xa8\xc61^\xc3\xee\nE\x01\x17\u0706\xa0\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4t\x86:\xce\xc7]\x03\xd5>\x86\x0ed\x00/,\x16^S\x83w\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\x89\u030a\xbeu\u0364\xef\r\x01\xce\xf2`^G\xed\xa6z\xb1\x89\a?u\u0460\x85\xba\x00\x00\u07d4t\x8c(^\xf1#?\xe4\xd3\x1c\x8f\xb17\x833r\x1c\x12\xe2z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t\x90\x87\xac\x0fZ\x97\xc6\xfa\xd0!S\x8b\xf1\xd6\u0361\x8e\r\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\x95\xaex\xc0\xd9\x02a\xe2\x14\x0e\xf2\x061\x04s\x1a`\xd1\xed\x89\x01\xdbPq\x89%!\x00\x00\u07d4t\x9aJv\x8b_#rH\x93\x8a\x12\xc6#\x84{\xd4\xe6\x88\u0709\x03\xe73b\x87\x14 \x00\x00\u07d4t\x9a\xd6\xf2\xb5pk\xbe/h\x9aD\u0136@\xb5\x8e\x96\xb9\x92\x89\x05k\xc7^-c\x10\x00\x00\u07d4t\xa1\u007f\x06K4N\x84\xdbce\u0695\x91\xff\x16(%vC\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4t\xae\xec\x91]\xe0\x1c\u019b,\xb5\xa65o\xee\xa1FX\xc6\u0149\f\x9a\x95\xee)\x86R\x00\x00\u07d4t\xaf\xe5I\x02\xd6\x15x%v\xf8\xba\xac\x13\xac\x97\f\x05\x0fn\x89\t\xa1\xaa\xa3\xa9\xfb\xa7\x00\x00\u07d4t\xb7\xe0\"\x8b\xae\xd6YW\xae\xbbM\x91m3:\xae\x16O\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t\xbcJ^ E\xf4\xff\x8d\xb1\x84\xcf:\x9b\f\x06Z\xd8\a\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4t\xbc\xe9\xec86-l\x94\u032c&\xd5\xc0\xe1:\x8b;\x1d@\x8965&A\x04B\xf5\x00\x00\u07d4t\xbfzZ\xb5\x92\x93\x14\x9b\\`\xcf6Bc\xe5\xeb\xf1\xaa\r\x89\x06G\f>w\x1e<\x00\x00\xe0\x94t\xc7<\x90R\x8a\x15s6\xf1\xe7\xea b\n\xe5?\xd2G(\x8a\x01\xe6:.S\x8f\x16\xe3\x00\x00\u07d4t\u0464\xd0\xc7RN\x01\x8dN\x06\xed;d\x80\x92\xb5\xb6\xaf,\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94t\xd3f\xb0{/VG}|pw\xaco\xe4\x97\xe0\xebeY\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4t\xd3zQt{\xf8\xb7q\xbf\xbfC\x9493\xd1\x00\xd2\x14\x83\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\xd6q\u065c\xbe\xa1\xabW\x90cu\xb6?\xf4+PE\x1d\x17\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\xeb\xf4BVF\xe6\u03c1\xb1\t\xce{\xf4\xa2\xa6=\x84\x81_\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4t\xed3\xac\xf4?5\xb9\x8c\x920\xb9\xe6d.\xcbS0\x83\x9e\x89$\xf6\xdf\xfbI\x8d(\x00\x00\u07d4t\xef(i\xcb\xe6\b\x85`E\xd8\xc2\x04\x11\x18W\x9f\"6\xea\x89\x03<\xd6E\x91\x95n\x00\x00\u07d4t\xfcZ\x99\xc0\xc5F\x05\x03\xa1;\x05\tE\x9d\xa1\x9c\xe7\u0350\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4u\v\xbb\x8c\x06\xbb\xbf$\bC\xccux.\xe0/\b\xa9tS\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4u\x14\xad\xbd\xc6?H?0M\x8e\x94\xb6\u007f\xf30\x9f\x18\v\x82\x89!\u0120n-\x13Y\x80\x00\u0794u\x17\xf1l(\xd12\xbb@\xe3\xba6\u01ae\xf11\xc4b\xda\x17\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4u\x1a,\xa3Nq\x87\xc1c\u048e6\x18\xdb(\xb1<\x19m&\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94u\x1a\xbc\xb6\xcc\x030Y\x91\x18\x15\xc9o\u04516\n\xb0D-\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4u&\xe4\x82R\x9f\n\x14\xee\u0248q\xdd\xdd\x0er\x1b\f\u0662\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4u)\xf3y{\xb6\xa2\x0f~\xa6I$\x19\xc8L\x86vA\xd8\x1c\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94u*^\xe22a,\xd3\x00_\xb2n[Y}\xe1\x9fwk\xe6\x8a\x01'\xfc\xb8\xaf\xae \xd0\x00\x00\u07d4u,\x9f\xeb\xf4/f\xc4x{\xfa~\xb1|\xf53;\xbaPp\x89j\x99\xf2\xb5O\xddX\x00\x00\u07d4u930F\u07b1\xef\x8e\u07b9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94u\xc1\xad#\xd2?$\xb3\x84\xd0\xc3\x14\x91w\xe8f\x97a\r!\x8a\x01\\[\xcdl(\x8b\xbd\x00\x00\u07d4u\xc2\xff\xa1\xbe\xf5I\x19\xd2\t\u007fz\x14-.\x14\xf9\xb0JX\x89\x90\xf3XP@2\xa1\x00\x00\u07d4u\xd6|\xe1N\x8d)\xe8\xc2\xff\u3051{\x93\v\x1a\xff\x1a\x87\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4u\xde~\x93R\xe9\v\x13\xa5\x9aXx\xff\xec\u01c3\x1c\xacM\x82\x89\x94\x89#z\u06daP\x00\x00\u07d4u\xf7S\x9d0\x9e\x909\x98\x9e\xfe.\x8b-\xbd\x86Z\r\xf0\x88\x89\x85[[\xa6\\\x84\xf0\x00\x00\u07d4v\b\xf47\xb3\x1f\x18\xbc\vd\u04c1\xae\x86\xfd\x97\x8e\u05f3\x1f\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94v\x0f\xf35N\x0f\u0793\x8d\x0f\xb5\xb8,\xef[\xa1\\=)\x16\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4v\x1an6,\x97\xfb\xbd|Yw\xac\xba-\xa7F\x876_I\x89\t\xf7J\xe1\xf9S\xd0\x00\x00\u07d4v\x1el\xae\xc1\x89\xc20\xa1b\xec\x00e0\x19>g\u03dd\x19\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94v\x1f\x8a:*\U00028f7e\x1d\xa0\t2\x1f\xb2\x97d\xebb\xa1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4v)\x98\xe1\xd7R'\xfc\xedzp\xbe\x10\x9aL\vN\xd8d\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4v-o0\u06b9\x915\xe4\xec\xa5\x1dRC\xd6\xc8b\x11\x02\u0549\x0fI\x89A\xe6d(\x00\x00\u07d4v3\x1e0yl\xe6d\xb2p\x0e\rASp\x0e\u0706\x97w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v8\x86\xe33\xc5o\xef\xf8[\xe3\x95\x1a\xb0\xb8\x89\xce&.\x95\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v:|\xba\xb7\rzd\u0427\xe5)\x80\xf6\x81G%\x93I\f\x89 \x86\xac5\x10R`\x00\x00\u07d4v>\xec\u0c0a\u021e2\xbf\xa4\xbe\xcev\x95\x14\xd8\xcb[\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4v@\xa3\u007f\x80R\x98\x15\x15\xbc\xe0x\u0693\xaf\xa4x\x9bW4\x89lk\x93[\x8b\xbd@\x00\x00\u0794vA\xf7\xd2j\x86\xcd\xdb+\xe10\x81\x81\x0e\x01\xc9\xc8E\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94vO\xc4mB\x8bm\xbc\"\x8a\x0f_U\xc9P\x8cw.\xab\x9f\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4vPn\xb4\xa7\x80\xc9Q\xc7J\x06\xb0=;\x83b\xf0\x99\x9dq\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94v[\xe2\xe1/b\x9ecI\xb9}!\xb6*\x17\xb7\xc80\xed\xab\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94vb\x81P\xe2\x99[['\x9f\xc8>\r\xd5\xf1\x02\xa6q\xdd\x1c\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4vk7Y\xe8yN\x92m\xacG=\x91:\x8f\xb6\x1a\xd0\xc2\u0249\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4vp\xb0/,<\xf8\xfdOG0\xf38\x1aq\xeaC\x1c3\u01c9\x0e~\xeb\xa3A\vt\x00\x00\u07d4vz\x03eZ\xf3`\x84\x1e\x81\r\x83\xf5\xe6\x1f\xb4\x0fL\xd1\x13\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4vz\u0190y\x1c.#E\x10\x89\xfelp\x83\xfeU\u07b6+\x89,s\xc97t,P\x00\x00\u07d4v\u007f\xd7y}Qi\xa0_sd2\x1c\x19\x84:\x8c4\x8e\x1e\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u0794v\x84o\r\xe0;Zv\x97\x1e\xad)\x8c\xdd\b\x84:K\xc6\u0188\xd7\x1b\x0f\u088e\x00\x00\xe0\x94v\x84\x98\x93N7\xe9\x05\xf1\xd0\xe7{D\xb5t\xbc\xf3\xecJ\xe8\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4v\x8c\xe0\u06a0)\xb7\xde\xd0\"\xe5\xfcWM\x11\xcd\xe3\xec\xb5\x17\x89\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\xe0\x94v\x93\xbd\xebo\xc8+[\xcar\x13U\"1u\xd4z\bKM\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4v\xaa\xf8\xc1\xac\x01/\x87R\xd4\xc0\x9b\xb4f\a\xb6e\x1d\\\xa8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4v\xab\x87\xddZ\x05\xad\x83\x9aN/\xc8\xc8Z\xa6\xba\x05d\x170\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v\xaf\xc2%\xf4\xfa0}\xe4\x84U+\xbe\x1d\x9d?\x15\aLJ\x89\xa2\x90\xb5\u01ed9h\x00\x00\xe0\x94v\xbe\xca\xe4\xa3\x1d6\xf3\xcbW\u007f*CYO\xb1\xab\xc1\xbb\x96\x8a\x05C\xa9\xce\x0e\x132\xf0\x00\x00\u07d4v\xc2u5\xbc\xb5\x9c\xe1\xfa-\x8c\x91\x9c\xab\xebJk\xba\x01\u0449lk\x93[\x8b\xbd@\x00\x00\u07d4v\xca\"\xbc\xb8y\x9eS'\u012a*}\tI\xa1\xfc\xce_)\x89R\xa0?\"\x8cZ\xe2\x00\x00\u07d4v\xca\u0108\x11\x1aO\u0555\xf5h\xae:\x85\x87p\xfc\x91]_\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94v\u02dc\x8bi\xf48vu\u0102S\xe24\xcb~\rt\xa4&\x8a\x01\x90\xf4H.\xb9\x1d\xae\x00\x00\u07d4v\xf8:\xc3\xda0\xf7\t&(\xc73\x9f \x8b\xfc\x14,\xb1\ue25a\x18\xff\xe7B}d\x00\x00\xe0\x94v\xf9\xad=\x9b\xbd\x04\xae\x05\\\x14w\xc0\xc3^u\x92\xcb* \x8a\b\x83?\x11\xe3E\x8f \x00\x00\u07d4v\xff\xc1W\xadk\xf8\xd5m\x9a\x1a\u007f\u077c\x0f\xea\x01\n\xab\xf4\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\x02\x8e@\x9c\xc4:;\xd3=!\xa9\xfcS\xec`n\x94\x91\x0e\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4w\f/\xb2\u0128\x17S\xac\x01\x82\xeaF\x0e\xc0\x9c\x90\xa5\x16\xf8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4w\r\x98\xd3\x1bCS\xfc\xee\xe8V\fL\u03c0>\x88\xc0\xc4\xe0\x89 \x86\xac5\x10R`\x00\x00\xe0\x94w\x13\xab\x807A\x1c\t\xbah\u007fo\x93d\xf0\xd3#\x9f\xac(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4w\x15\a\xae\xeej%]\xc2\u035d\xf5QT\x06-\b\x97\xb2\x97\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4w\x19\x88\x87\x95\xadtY$\xc7W`\u0771\x82}\xff\xd8\u0368\x89lkLM\xa6\u077e\x00\x00\u07d4w'\xaf\x10\x1f\n\xab\xa4\xd2:\x1c\xaf\xe1|n\xb5\u06b1\xc6\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4w,)\u007f\n\u0454H.\xe8\xc3\xf06\xbd\xeb\x01\xc2\x01\xd5\u0309\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94w0o\xfe.J\x8f<\xa8&\xc1\xa2I\xf7!-\xa4:\xef\xfd\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4w1A\x12}\x8c\xf3\x18\xae\xbf\x886Z\xdd=U'\xd8[j\x8966\u05ef^\u024e\x00\x00\u07d4wF\xb6\xc6i\x9c\x8f4\xca'h\xa8 \xf1\xff\xa4\xc2\a\xfe\x05\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4wQ\xf3c\xa0\xa7\xfd\x053\x19\b\t\u076f\x93@\xd8\xd1\x12\x91\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4wW\xa4\xb9\xcc=\x02G\u032a\xeb\x99\t\xa0\xe5n\x1d\xd6\xdc\u0089\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4w\\\x10\xc9>\r\xb7 [&CE\x823\xc6O\xc3?\xd7[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4wa~\xbcK\xeb\xc5\xf5\xdd\xeb\x1bzp\xcd\xebj\xe2\xff\xa0$\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4wiC\xff\xb2\xef\\\xdd5\xb8<(\xbc\x04k\xd4\xf4gp\x98\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94wp\x1e,I=\xa4|\x1bX\xf4!\xb5I]\xeeE\xbe\xa3\x9b\x8a\x01H\xf6I\xcfaB\xa5\x80\x00\u07d4wy\x8f \x12W\xb9\xc3R\x04\x95pW\xb5Ft\xae\xfaQ\u07c9\b\x13\xcaV\x90m4\x00\x00\u07d4w\x8cC\xd1\x1a\xfe;Xo\xf3t\x19-\x96\xa7\xf2=+\x9b\u007f\x89\x8b\xb4\xfc\xfa;}k\x80\x00\u07d4w\x8cy\xf4\xde\x19S\xeb\u0398\xfe\x80\x06\xd5:\x81\xfbQ@\x12\x8963\x03\"\xd5#\x8c\x00\x00\u07d4w\x92t\xbf\x18\x03\xa36\xe4\u04f0\r\u0753\xf2\xd4\xf5\xf4\xa6.\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\xa1q\"\xfa1\xb9\x8f\x17\x11\xd3*\x99\xf0>\xc3&\xf3=\b\x89\\(=A\x03\x94\x10\x00\x00\u07d4w\xa3I\a\xf3\x05\xa5L\x85\xdb\t\xc3c\xfd\xe3\xc4~j\xe2\x1f\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4w\xa7i\xfa\xfd\xec\xf4\xa68v-[\xa3\x96\x9d\xf61 \xa4\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4w\xbekd\xd7\xc73\xa46\xad\xec^\x14\xbf\x9a\xd7@+\x1bF\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\xbf\xe9<\u0367P\x84~A\xa1\xaf\xfe\xe6\xb2\u0696\xe7!N\x89\x10CV\x1a\x88)0\x00\x00\u07d4w\u0126\x97\xe6\x03\xd4+\x12\x05l\xbb\xa7a\xe7\xf5\x1d\x04C\xf5\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4w\xcc\x02\xf6#\xa9\u03d8S\t\x97\xeag\xd9\\;I\x18Y\xae\x89Is\x03\xc3n\xa0\xc2\x00\x00\u07d4w\xd4?\xa7\xb4\x81\xdb\xf3\xdbS\f\xfb\xf5\xfd\xce\xd0\xe6W\x181\x89lk\x93[\x8b\xbd@\x00\x00\u07d4w\xda^lr\xfb6\xbc\xe1\xd9y\x8f{\xcd\xf1\u044fE\x9c.\x89\x016\x95\xbbl\xf9>\x00\x00\u07d4w\xf4\xe3\xbd\xf0V\x88<\xc8r\x80\xdb\xe6@\xa1\x8a\r\x02\xa2\a\x89\n\x81\x99:+\xfb[\x00\x00\u0794w\xf6\t\u0287 \xa0#&,U\xc4o-&\xfb90\xaci\x88\xf0\x15\xf2W6B\x00\x00\u07d4w\xf8\x1b\x1b&\xfc\x84\xd6\u0797\uf2df\xbdr\xa310\xccJ\x8965\u026d\xc5\u07a0\x00\x00\u07d4x\x19\xb0E\x8e1N+S\xbf\xe0\f8I_\u0539\xfd\xf8\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4x\x1b\x15\x01dz.\x06\xc0\xedC\xff\x19\u007f\xcc\xec5\xe1p\v\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4x/R\xf0\xa6v\xc7w\x16\xd5t\xc8\x1e\xc4hO\x9a\x02\n\x97\x89.\x14\xe2\x06\xb70\xad\x80\x00\u07d4x5]\xf0\xa20\xf8=\x03,p1TAM\xe3\xee\u06b5W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4x6\xf7\xefk\u01fd\x0f\xf3\xac\xafD\x9c\x84\xddk\x1e,\x93\x9f\x89\xe0\x8d\xe7\xa9,\xd9|\x00\x00\u07d4x7\xfc\xb8v\xda\x00\xd1\xeb;\x88\xfe\xb3\xdf?\xa4\x04/\xac\x82\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4x>\uc2a5\xda\xc7{.f#\xedQ\x98\xa41\xab\xba\xee\a\x89\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4x\\\x8e\xa7t\xd70D\xa74\xfay\n\x1b\x1et>w\xed|\x89\f\xf1Rd\f\\\x83\x00\x00\u07d4x`\xa3\xde8\xdf8*\xe4\xa4\xdc\xe1\x8c\f\a\xb9\x8b\xce=\xfa\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94xcCq\xe1s\x04\xcb\xf39\xb1E*L\xe48\xdcvL\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4xd\u0719\x9f\xe4\xf8\xe0\x03\xc0\xf4=\xec\u00da\xae\x15\"\xdc\x0f\x89\x05\x1e\x10+\xd8\xec\xe0\x00\x00\u07d4xtj\x95\x8d\xce\xd4\xc7d\xf8vP\x8cAJh4,\uce49\x02\xbe7O\xe8\xe2\xc4\x00\x00\xe0\x94x}1?\xd3k\x05>\xee\xae\xdb\xcet\xb9\xfb\x06x32\x89\x8a\x05\xc0X\xb7\x84'\x19`\x00\x00\u07d4x\x85\x9c[T\x8bp\r\x92\x84\xce\xe4\xb6c=GJ\x8a\x04{\x92\xc4\x15B$-\n\b\xc7\x0f\x99\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4x\u03c36\xb3(\xdb=\x87\x81:G+\x9e\x89\xb7^\f\xf3\xbc\x8965\u026d\xc5\u07a0\x00\x00\u07d4x\xd4\xf8\xc7\x1c\x1eh\xa6\x9a\x98\xf5/\xcbE\u068a\xf5n\xa1\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4x\xdf&\x81\xd6\xd6\x02\xe2!B\xd5A\x16\u07a1]EIW\xaa\x89\x10'\x94\xad \xdah\x00\x00\u07d4x\xe0\x8b\xc53A<&\u2473\x14?\xfa|\u026f\xb9{x\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4x\xe8?\x80\xb3g\x8cz\nN>\x8c\x84\xdc\xcd\xe0dBbw\x89a\t=|,m8\x00\x00\u07d4x\xf5\xc7G\x85\xc5f\x8a\x83\x80r\x04\x8b\xf8\xb4SYM\u06ab\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4y\x0f\x91\xbd]\x1c\\\xc4s\x9a\xe9\x13\x00\u06c9\xe1\xc10<\x93\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\x17\u5f42\xa9y\x0f\xd6P\xd0C\xcd\xd90\xf7y\x963\u06c9\xd8\xd4`,&\xbfl\x00\x00\u07d4y\x19\xe7b\u007f\x9b}T\xea;\x14\xbbM\xd4d\x9fO9\xde\xe0\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4y\x1f`@\xb4\xe3\xe5\r\xcf5S\xf1\x82\u0357\xa9\x060\xb7]\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4y0\xc2\xd9\xcb\xfa\x87\xf5\x10\xf8\xf9\x87w\xff\x8a\x84H\xcaV)\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4yE)\u041d\x01rq5\x970\x02pu\xb8z\xd8=\xaen\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4yKQ\u00deS\xd9\xe7b\xb0a;\x82\x9aD\xb4r\xf4\xff\xf3\x89$5\xe0dxA\u0300\x00\xe0\x94yU\x1c\xed\xe3v\xf7G\xe3ql\x8dy@\rvm.\x01\x95\x8a\t\xcb7\xaf\xa4\xffxh\x00\x00\u07d4y^\xbc&&\xfc9\xb0\xc8b\x94\xe0\xe87\xdc\xf5#U0\x90\x8965\u026d\xc5\u07a0\x00\x00\u07d4yn\xbb\xf4\x9b>6\xd6v\x94\xady\xf8\xff6vz\xc6\xfa\xb0\x89\x03K\xc4\xfd\xde'\xc0\x00\x00\u07d4yo\x87\xbaaz)0\xb1g\v\xe9.\xd1(\x1f\xb0\xb3F\xe1\x89\x06\xf5\xe8o\xb5((\x00\x00\u07d4yt'\xe3\xdb\xf0\xfe\xaez%\x06\xf1-\xf1\xdc@2n\x85\x05\x8965\u026d\xc5\u07a0\x00\x00\u07d4yu\x10\xe3\x86\xf5c\x93\xce\xd8\xf4w7\x8aDLHO}\xad\x8965\u026d\xc5\u07a0\x00\x00\u07d4y{\xb7\xf1W\xd9\xfe\xaa\x17\xf7m\xa4\xf7\x04\xb7M\xc1\x03\x83A\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4y\x88\x90\x131\xe3\x87\xf7\x13\xfa\u03b9\x00\\\xb9\xb6Q6\xeb\x14\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4y\x89\u041f8&\xc3\u5bccu*\x81\x15r:\x84\xd8\tp\x89\x16\x86\xf8aL\xf0\xad\x00\x00\xe0\x94y\x95\xbd\x8c\xe2\xe0\xc6{\xf1\u01e51\xd4w\xbc\xa1\xb2\xb9ua\x8a\x01BH\xd6\x17\x82\x9e\xce\x00\x00\u07d4y\xae\xb3Ef\xb9t\xc3ZX\x81\xde\xc0 \x92}\xa7\xdf]%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\xb1 \xeb\x88\x06s#!(\x8fgZ'\xa9\"_\x1c\xd2\ub245\xa0\xbf7\xde\xc9\xe4\x00\x00\u07d4y\xb4\x8d-a7\u00c5Ma\x1c\x01\xeaBBz\x0fY{\xb7\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4y\xb8\xaa\xd8y\xdd0V~\x87x\xd2\xd21\xc8\xf3z\xb8sN\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\xbf/{n2\x8a\xaf&\xe0\xbb\t?\xa2-\xa2\x9e\xf2\xf4q\x89a\t=|,m8\x00\x00\u07d4y\xc10\xc7b\xb8v[\x19\u04ab\u0260\x83\xab\x8f:\xady@\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4y\xc1\xbe\x19q\x1fs\xbe\xe4\xe61j\xe7T\x94Y\xaa\u03a2\xe0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4y\xc6\x00/\x84R\xca\x15\u007f\x13\x17\xe8\n/\xaf$GUY\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4y\xca\xc6IO\x11\xef'\x98t\x8c\xb52\x85\xbd\x8e\"\xf9|\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4y\u03e9x\n\xe6\xd8{,1\x88?\t'i\x86\u021ag5\x8965\u026d\xc5\u07a0\x00\x00\u07d4y\u06e2VG-\xb4\xe0X\xf2\xe4\xcd\xc3\xeaN\x8aBw83\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4y\xed\x10\xcf\x1fm\xb4\x82\x06\xb5\t\x19\xb9\xb6\x97\b\x1f\xbd\xaa\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u0794y\xf0\x8e\x01\xce\t\x88\xe6<\u007f\x8f)\b\xfa\xdeC\xc7\xf9\xf5\u0248\xfc\x93c\x92\x80\x1c\x00\x00\u07d4y\xfdmH1Pf\xc2\x04\xf9e\x18i\xc1\tl\x14\xfc\x97\x81\x89lk\x93[\x8b\xbd@\x00\x00\u0794y\xff\xb4\xac\x13\x81*\vx\u0123{\x82u\">\x17k\xfd\xa5\x88\xf0\x15\xf2W6B\x00\x00\u07d4z\x05\x89\xb1C\xa8\xe5\xe1\a\u026cf\xa9\xf9\xf8Yz\xb3\u7ac9Q\xe92\xd7n\x8f{\x00\x00\u07d4z\nx\xa9\xcc9?\x91\xc3\xd9\xe3\x9ak\x8c\x06\x9f\a^k\xf5\x89Hz\x9a0E9D\x00\x00\u07d4z\x13p\xa7B\xec&\x87\xe7a\xa1\x9a\u0167\x942\x9e\xe6t\x04\x89\xa2\xa12ga\xe2\x92\x00\x00\xe0\x94z-\xfcw\x0e$6\x811\xb7\x84w\x95\xf2\x03\xf3\xd5\r[V\x8a\x02i\xfe\xc7\xf06\x1d \x00\x00\u07d4z3\x83N\x85\x83s>-R\xae\xadX\x9b\u046f\xfb\x1d\xd2V\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94z6\xab\xa5\xc3\x1e\xa0\xca~'{\xaa2\xecF\u0393\xcfu\x06\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94z8\x11\"\xba\xday\x1az\xb1\xf6\x03}\xac\x80C'S\xba\xad\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94zH\xd8w\xb6:\x8f\x8f\x93\x83\xe9\xd0\x1eS\xe8\fR\x8e\x95_\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4zO\x9b\x85\x06\x90\xc7\xc9F\x00\xdb\xee\f\xa4\xb0\xa4\x11\xe9\xc2!\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4zc\x86\x9f\xc7g\xa4\u01b1\xcd\x0e\x06I\xf3cL\xb1!\xd2K\x89\x043\x87Oc,\xc6\x00\x00\u07d4zg\xdd\x04:PO\xc2\xf2\xfcq\x94\xe9\xbe\xcfHL\xec\xb1\xfb\x89\r\x8drkqw\xa8\x00\x00\xe0\x94zk&\xf48\u0663RD\x91U\xb8\x87l\xbd\x17\xc9\u065bd\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4zmx\x1cw\u013a\x1f\xca\xdfhsA\xc1\xe3\x17\x99\xe9='\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4zph\xe1\xc37\\\x0eY\x9d\xb1\xfb\xe6\xb2\xea#\xb8\xf4\a\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4zt\xce\xe4\xfa\x0fcp\xa7\x89O\x11l\xd0\f\x11G\xb8>Y\x89+^:\xf1k\x18\x80\x00\x00\u07d4zy\xe3\x0f\xf0W\xf7\n=\x01\x91\xf7\xf5?v\x157\xaf}\xff\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94zzO\x80sW\xa4\xbb\xe6\x8e\x1a\xa8\x0692\x10\xc4\x11\u0333\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4z\x85c\x86y\x01 o?+\xf0\xfa>\x1c\x81\t\u02bc\u0345\x89\amA\xc6$\x94\x84\x00\x00\xe0\x94z\x87\x97i\n\xb7{Tp\xbf|\f\x1b\xbaa%\b\xe1\xac}\x8a\x01\xe0\x92\x96\xc37\x8d\xe4\x00\x00\u07d4z\x8c\x89\xc0\x14P\x9dV\u05f6\x810f\x8f\xf6\xa3\xec\xecsp\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94z\x94\xb1\x99\x92\u03b8\xcec\xbc\x92\xeeKZ\xde\xd1\fM\x97%\x8a\x03\x8d\x1a\x80d\xbbd\xc8\x00\x00\u07d4z\xa7\x9a\xc0C\x16\u030d\b\xf2\x00e\xba\xa6\xd4\x14(\x97\xd5N\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4z\xadM\xbc\u04ec\xf9\x97\u07d3XiV\xf7+d\u062d\x94\xee\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94z\xb2V\xb2\x04\x80\n\xf2\x017\xfa\xbc\xc9\x16\xa22Xu%\x01\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4z\xbaV\xf6:H\xbc\b\x17\u05b9p9\x03\x9az\xd6/\xae.\x89 \x86\xac5\x10R`\x00\x00\xe0\x94z\xbb\x10\xf5\xbd\x9b\xc3;\x8e\xc1\xa8-d\xb5[k\x18wuA\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4z\u010d@\xc6d\u031am\x89\xf1\xc5\xf5\xc8\n\x1cp\xe7D\u6263\x10b\xbe\xee\xd7\x00\x00\x00\u07d4z\u014fo\xfcO\x81\a\xaen07\x8eN\x9f\x99\xc5\u007f\xbb$\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4z\xd3\xf3\aao\x19\u0731C\xe6DM\xab\x9c<3a\x1fR\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4z\xd8,\xae\xa1\xa8\xb4\xed\x051\x9b\x9c\x98p\x17<\x81N\x06\xee\x89!d\xb7\xa0J\u0220\x00\x00\u07d4z\xde]f\xb9D\xbb\x86\f\x0e\xfd\xc8bv\u054fFS\xf7\x11\x89lk\x93[\x8b\xbd@\x00\x00\u07d4z\xdf\xed\xb0m\x91\xf3\xccs\x90E\v\x85U\x02p\x88<{\xb7\x89\x11x\xfa@Q]\xb4\x00\x00\u07d4z\xe1\xc1\x9eS\xc7\x1c\xeeLs\xfa\xe2\xd7\xfcs\xbf\x9a\xb5\u348965\u026d\xc5\u07a0\x00\x00\u07d4z\xe6Y\xeb;\xc4hR\xfa\x86\xfa\xc4\xe2\x1cv\x8dP8\x89E\x89\x0f\x81\f\x1c\xb5\x01\xb8\x00\x00\u07d4z\xea%\xd4+&\x12(n\x99\xc56\x97\u01bcA\x00\xe2\u06ff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4z\xef{U\x1f\v\x9cF\xe7U\xc0\xf3\x8e[:s\xfe\x11\x99\xf5\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4{\v1\xffn$t^\xad\x8e\u067b\x85\xfc\v\xf2\xfe\x1dU\u0509+^:\xf1k\x18\x80\x00\x00\xe0\x94{\x0f\xea\x11v\xd5!Y3:\x14<)IC\xda6\xbb\u0774\x8a\x01\xfc}\xa6N\xa1L\x10\x00\x00\u07d4{\x11g<\xc0\x19bk)\f\xbd\xce&\x04o~m\x14\x1e!\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4{\x12!b\xc9\x13\xe7\x14l\xad\v~\xd3z\xff\xc9*\v\xf2\u007f\x89Q\xaf\tk#\x01\u0440\x00\u07d4{\x1b\xf5:\x9c\xbe\x83\xa7\u07a44W\x9f\xe7*\xac\x8d*\f\u0409\n\xd4\xc81j\v\f\x00\x00\u07d4{\x1d\xaf\x14\x89\x1b\x8a\x1e\x1b\xd4)\u0633k\x9aJ\xa1\u066f\xbf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4{\x1f\xe1\xabM\xfd\x00\x88\xcd\xd7\xf6\x01c\xefY\xec*\xee\x06\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4{%\xbb\x9c\xa8\xe7\x02!~\x933\"RP\xe5<6\x80MH\x89e\xea=\xb7UF`\x00\x00\u07d4{'\xd0\xd1\xf3\xdd<\x14\x02\x94\xd0H\x8bx>\xbf@\x15'}\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94{@\a\xc4^ZW?\u06f6\xf8\xbdtk\xf9J\xd0J<&\x8a\x038!\xf5\x13]%\x9a\x00\x00\u07d4{C\xc7\xee\xa8\xd6#U\xb0\xa8\xa8\x1d\xa0\x81\xc6Dk3\xe9\xe0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4{M*8&\x90i\xc1\x85Ww\rY\x1d$\xc5\x12\x1f^\x83\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94{au\xec\x9b\xef\xc78$\x955\xdd\xde4h\x8c\xd3n\xdf%\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94{f\x12hy\x84M\xfa4\xfee\xc9\xf2\x88\x11\u007f\xef\xb4I\xad\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4{j\x84q\x8d\xd8nc3\x84)\xac\x81\x1d|\x8a\x86\x0f!\xf1\x89a\t=|,m8\x00\x00\xe0\x94{q,z\xf1\x16v\x00jf\xd2\xfc\\\x1a\xb4\xc4y\xce`7\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4{s$-u\u029a\xd5X\xd6P)\r\xf1v\x92\xd5L\u0638\x89lnY\xe6|xT\x00\x00\u07d4{v\x1f\xeb\u007f\u03e7\xde\xd1\xf0\xeb\x05\x8fJ`\v\xf3\xa7\b\u02c9\xf9]\xd2\xec'\xcc\xe0\x00\x00\xe0\x94{\x82|\xae\u007f\xf4t\t\x18\xf2\xe00\xab&\u02d8\xc4\xf4l\xf5\x8a\x01\x94hL\v9\xde\x10\x00\x00\xe0\x94{\x892\x86B~r\xdb!\x9a!\xfcM\xcd_\xbfY(<1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4{\x92&\xd4o\xe7Q\x94\v\xc4\x16\xa7\x98\xb6\x9c\xcf\r\xfa\xb6g\x89\u3bb5sr@\xa0\x00\x00\u07d4{\x98\xe2<\xb9k\xee\xe8\n\x16\x80i\ube8f \xed\xd5\\\u03c9\v\xa0\xc9\x15\x87\xc1J\x00\x00\u07d4{\xb0\xfd\xf5\xa6c\xb5\xfb\xa2\x8d\x9c\x90*\xf0\xc8\x11\xe2R\xf2\x98\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4{\xb9W\x1f9K\v\x1a\x8e\xbaVd\xe9\u0635\xe8@g{\xea\x89\x01\x11du\x9f\xfb2\x00\x00\xe0\x94{\xb9\x84\xc6\u06f9\xe2y\x96j\xfa\xfd\xa5\x9c\x01\xd0&'\xc8\x04\x8a\x01\xb4d1\x1dE\xa6\x88\x00\x00\u07d4{\xbb\xec^p\xbd\xea\u063b2\xb4(\x05\x98\x8e\x96H\xc0\xaa\x97\x8966\u05ef^\u024e\x00\x00\u07d4{\xca\x1d\xa6\xc8\nf\xba\xa5\xdbZ\u0245A\u013e'kD}\x89$\xcf\x04\x96\x80\xfa<\x00\x00\u07d4{\u0772\xee\x98\xde\x19\xeeL\x91\xf6a\xee\x8eg\xa9\x1d\x05K\x97\x8965\u026d\xc5\u07a0\x00\x00\u0794{\xe2\xf7h\f\x80-\xa6\x15L\x92\xc0\x19J\xe72Qzqi\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4{\xe7\xf2Eiq\x88;\x9a\x8d\xbeL\x91\xde\xc0\x8a\xc3N\x88b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4{\xe8\u0334\xf1\x1bf\xcan\x1dW\xc0\xb59b!\xa3\x1b\xa5:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94{\xeb\x81\xfb/^\x91Rk*\xc9y^v\u019b\xcf\xf0K\xc0\x8a\x0e\xb2.yO\n\x8d`\x00\x00\u07d4|\b\x83\x05L-\x02\xbcz\x85+\x1f\x86\xc4'w\xd0\xd5\xc8V\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4|\x0f^\a C\xc9\xeet\x02B\x19~x\xccK\x98\xcd\xf9`\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|\x1d\xf2JO\u007f\xb2\u01f4r\xe0\xbb\x00l\xb2}\xcd\x16AV\x8965\u026d\xc5\u07a0\x00\x00\u07d4|)\xd4}W\xa73\xf5k\x9b!pc\xb5\x13\xdc;1Y#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4|+\x96\x03\x88JO.FN\u03b9}\x17\x93\x8d\x82\x8b\xc0,\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4|8,\x02\x96a.N\x97\xe4@\xe0-8q';U\xf5;\x89\n\xb6@9\x12\x010\x00\x00\u07d4|>\xb7\x13\xc4\xc9\xe08\x1c\xd8\x15L|\x9a}\xb8d\\\xde\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|D\x01\xae\x98\xf1.\xf6\xde9\xae$\u03df\xc5\x1f\x80\xeb\xa1k\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|E\xf0\xf8D*V\xdb\u04dd\xbf\x15\x99\x95A\\R\xedG\x9b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94|S-\xb9\xe0\xc0l&\xfd@\xac\xc5j\xc5\\\x1e\xe9-<:\x8a?\x87\bW\xa3\xe0\xe3\x80\x00\x00\u07d4|`\xa0_zJ_\x8c\xf2xC\x916.uZ\x83A\xefY\x89f\x94\xf0\x18*7\xae\x00\x00\u07d4|`\xe5\x1f\v\xe2(\xe4\xd5o\xdd)\x92\xc8\x14\xdaw@\u01bc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|i$\xd0|>\xf5\x89\x19f\xfe\nxV\xc8{\xef\x9d 4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94|\x8b\xb6Zo\xbbI\xbdA3\x96\xa9\xd7\xe3\x10S\xbb\xb3z\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94|\x9a\x11\f\xb1\x1f%\x98\xb2\xb2\x0e,\xa4\x002^A\xe9\xdb3\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4|\xbc\xa8\x8f\xcaj\x00`\xb9`\x98\\\x9a\xa1\xb0%4\xdc\"\b\x89\x19\x12z\x13\x91\xea*\x00\x00\u07d4|\xbe\xb9\x992\xe9~n\x02\x05\x8c\xfcb\u0432k\xc7\u0325+\x89lk\x93[\x8b\xbd@\x00\x00\u07d4|\xc2Jj\x95\x8c \xc7\xd1$\x96`\xf7Xb&\x95\v\r\x9a\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4|\xd2\x0e\u0335\x18\xb6\f\xab\t[r\x0fW\x15p\u02aaD~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4|\xd5\xd8\x1e\xab7\xe1\x1ebv\xa3\xa1\t\x12Q`~\r~8\x89\x03hM^\xf9\x81\xf4\x00\x00\u07d4|\xdft!9E\x95=\xb3\x9a\xd0\xe8\xa9x\x1a\xddy.M\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4|\xe4hdF\U000547be\xd6r\x15\xeb\rZ\x1d\xd7,\x11\xb8\x89x9\xd3!\xb8\x1a\xb8\x00\x00\u07d4|\xefMC\xaaA\u007f\x9e\xf8\xb7\x87\xf8\xb9\x9dS\xf1\xfe\xa1\ue209g\x8a\x93 b\xe4\x18\x00\x00\u07d4}\x03P\xe4\v3\x8d\xdasfa\x87+\xe3?\x1f\x97R\xd7U\x89\x02\xb4\xf5\xa6\U00051500\x00\xe0\x94}\x04\xd2\xed\xc0X\xa1\xaf\xc7a\xd9\u025a\xe4\xfc\\\x85\xd4\u0226\x8aB\xa9\xc4g\\\x94g\xd0\x00\x00\u07d4}\v%^\xfbW\xe1\x0fp\b\xaa\"\xd4\x0e\x97R\xdf\xcf\x03x\x89\x01\x9f\x8euY\x92L\x00\x00\xe0\x94}\x13\xd6pX\x84\xab!W\u074d\xccpF\xca\xf5\x8e\xe9K\xe4\x8a\x1d\r\xa0|\xbb>\xe9\xc0\x00\x00\u07d4}'>c~\xf1\xea\u0101\x11\x94\x13\xb9\x1c\x98\x9d\xc5\xea\xc1\"\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4}*R\xa7\xcf\f\x846\xa8\xe0\a\x97kl&\xb7\"\x9d\x1e\x15\x89\x17\xbf\x06\xb3*$\x1c\x00\x00\u07d4}4\x805i\xe0\v\u05b5\x9f\xff\b\x1d\xfa\\\n\xb4\x19zb\x89\\\xd8|\xb7\xb9\xfb\x86\x00\x00\u07d4}4\xffY\xae\x84\nt\x13\u01baL[\xb2\xba,u\xea\xb0\x18\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4}9(R\xf3\xab\xd9/\xf4\xbb[\xb2l\xb6\bt\xf2\xbeg\x95\x8966\xc2^f\xec\xe7\x00\x00\u07d4}DRg\u015a\xb8\u04a2\xd9\xe7\t\x99\x0e\th%\x80\u011f\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94}U\x13\x97\xf7\x9a)\x88\xb0d\xaf\xd0\xef\xeb\xee\x80,w!\xbc\x8a\bW\xe0\xd6\xf1\xdav\xa0\x00\x00\u07d4}Z\xa3?\xc1KQ\x84\x1a\x06\x90n\xdb+\xb4\x9c*\x11ri\x89\x10D\x00\xa2G\x0eh\x00\x00\xe0\x94}]/s\x94\x9d\xad\xda\bV\xb2\x06\x98\x9d\xf0\a\x8dQ\xa1\xe5\x8a\x02\xc4:H\x1d\xf0M\x01wb\xed\xcb\\\xaab\x9bZ\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4~\x8f\x96\xcc)\xf5{\tu\x12\f\xb5\x93\xb7\u0743=`kS\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4~\x97*\x8a|*D\xc9;!Cl8\xd2\x1b\x92R\xc3E\xfe\x89a\t=|,m8\x00\x00\u07d4~\x99\u07fe\x98\x9d;\xa5)\u0457Q\xb7\xf41\u007f\x89S\xa3\xe2\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4~\xa0\xf9n\xe0\xa5s\xa30\xb5h\x97v\x1f=L\x010\xa8\xe3\x89Hz\x9a0E9D\x00\x00\u0794~\xa7\x91\xeb\xab\x04E\xa0\x0e\xfd\xfcNJ\x8e\x9a~ue\x13m\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4~\xab\xa05\xe2\xaf7\x93\xfdtgK\x10%@\xcf\x19\n\u0779\x89E\x02l\x83[`D\x00\x00\xe0\x94~\xb4\xb0\x18\\\x92\xb6C\x9a\b\xe72!h\xcb5<\x8awJ\x8a\x02'\x19l\xa0I\x83\xca\x00\x00\xe0\x94~\xbd\x95\xe9\xc4p\xf7(5\x83\xdcn\x9d,M\xce\v\ua3c4\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4~\u0425\xa8G\xbe\xf9\xa9\xda|\xba\x1dd\x11\xf5\xc3\x161&\x19\x89\x02(\xeb7\xe8u\x1d\x00\x00\u07d4~\xda\xfb\xa8\x98K\xafc\x1a\x82\vk\x92\xbb\xc2\xc56U\xf6\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4~\xdb\x02\xc6\x1a\"r\x87a\x1a\xd9Pici\xccNdzh\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4~\xe5\u0280]\xce#\xaf\x89\xc2\xd4D\xe7\xe4\af\xc5Lt\x04\x89\r\v\xd4\x12\xed\xbd\x82\x00\x00\xe0\x94~\xe6\x04\u01e9\xdc)\t\xce2\x1d\u6e72OWgWuU\x8a\x01+\xf9\u01d8\\\xf6-\x80\x00\u07d4~\xf1o\xd8\xd1[7\x8a\x0f\xba0k\x8d\x03\u0758\xfc\x92a\x9f\x89%\xf2s\x93=\xb5p\x00\x00\u07d4~\xf9\x8bR\xbe\xe9S\xbe\xf9\x92\xf3\x05\xfd\xa0'\xf8\x91\x1cXQ\x89\x1b\xe7\" i\x96\xbc\x80\x00\u07d4~\xfc\x90vj\x00\xbcR7,\xac\x97\xfa\xbd\x8a<\x83\x1f\x8e\u0349\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4~\xfe\xc0\xc6%<\xaf9\u007fq(|\x1c\a\xf6\xc9X+[\x86\x89\x1a,\xbc\xb8O0\u0540\x00\u07d4\u007f\x01\xdc|7G\xca`\x8f\x98=\xfc\x8c\x9b9\xe7U\xa3\xb9\x14\x89\v8l\xad_zZ\x00\x00\u07d4\u007f\x06b\xb4\x10)\x8c\x99\xf3\x11\u04e1EJ\x1e\xed\xba/\xeav\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\u007f\x06\u021dY\x80\u007f\xa6\v\xc6\x016\xfc\xf8\x14\u02ef%C\xbd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u007f\v\x90\xa1\xfd\u050f'\xb2h\xfe\xb3\x83\x82\xe5]\xdbP\xef\x0f\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\u007f\x0e\xc3\u06c0F\x92\xd4\xd1\xea2E6Z\xab\x05\x90\a[\u0109\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u007f\x0f\x04\xfc\xf3zS\xa4\xe2N\xden\x93\x10Nx\xbe\x1d<\x9e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u007f\x13\xd7`I\x8dq\x93\xcahY\xbc\x95\xc9\x018d#\xd7l\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\u007f\x15\n\xfb\x1aw\u00b4Y(\xc2h\xc1\u9f74d\x1dG\u0609lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\x16\x19\x98\x8f7\x15\xe9O\xf1\xd2S&-\xc5X\x1d\xb3\xde\x1c\x890\xca\x02O\x98{\x90\x00\x00\u07d4\u007f\x1c\x81\xee\x16\x97\xfc\x14K|\v\xe5I;V\x15\xae\u007f\xdd\u0289\x1b\x1d\xaba\u04ead\x00\x00\u07d4\u007f#\x82\xff\xd8\xf89VFy7\xf9\xbar7F#\xf1\x1b8\x89 \x86\xac5\x10R`\x00\x00\u07d4\u007f7\t9\x1f?\xbe\xba5\x92\xd1u\xc7@\xe8z\tT\x1d\x02\x89\x1a\x05V\x90\xd9\u06c0\x00\x00\u07d4\u007f8\x9c\x12\xf3\xc6\x16OdFVlwf\x95\x03\xc2y%'\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\xe0\x94\u007f:\x1eE\xf6~\x92\u0200\xe5s\xb43y\xd7\x1e\xe0\x89\xdbT\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\u007f=r\x03\u0224G\xf7\xbf6\u060a\xe9\xb6\x06*^\xeex\xae\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\u007fF\xbb%F\r\xd7\xda\xe4!\x1c\xa7\xf1Z\xd3\x12\xfc}\xc7\\\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\u007fI\xe7\xa4&\x98\x82\xbd\x87\"\u0526\xf5f4v)b@y\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u007fI\xf2\a&G\x1a\xc1\u01e8>\xf1\x06\xe9w\\\xebf%f\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\u007fK^'\x85x\xc0F\xcc\xea\xf6W0\xa0\xe0h2\x9e\u0576\x89e\xea=\xb7UF`\x00\x00\u07d4\u007fOY;a\x8c3\v\xa2\xc3\xd5\xf4\x1e\xce\xeb\x92\xe2~Bl\x89\x96n\xdcuk|\xfc\x00\x00\u07d4\u007fT\x14\x91\u04ac\x00\xd2a/\x94\xaa\u007f\v\xcb\x01FQ\xfb\u0509\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\u007fZ\xe0Z\xe0\xf8\xcb\xe5\xdf\xe7!\xf0D\u05e7\xbe\xf4\xc2y\x97\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\u007f`:\xec\x17Y\xea_\a\xc7\xf8\xd4\x1a\x14(\xfb\xba\xf9\xe7b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u007falo\x00\x8a\u07e0\x82\xf3M\xa7\xd0e\x04`6\x80u\xfb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u007fa\xfal\xf5\xf8\x98\xb4@\xda\u016b\xd8`\rmi\x1f\xde\xf9\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94\u007fe\\g\x89\xed\xdfE\\\xb4\xb8\x80\x99r\x0698\x9e\ubb0a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u007fk(\u0204!\xe4\x85~E\x92\x81\u05c4ai$\x89\xd3\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007fn\xfboC\x18\x87m.\xe6$\xe2u\x95\xf4DF\xf6\x8e\x93\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u007fq\x92\xc0\xdf\x1c}\xb6\xd9\xede\xd7\x11\x84\xd8\xe4\x15Z\x17\xba\x89\x04Sr\x8d3\x94,\x00\x00\u07d4\u007fz:!\xb3\xf5\xa6]\x81\xe0\xfc\xb7\xd5-\xd0\n\x1a\xa3m\xba\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u007f\x8d\xbc\xe1\x80\xed\x9cV65\xaa\xd2\xd9{L\xbcB\x89\x06\u0649\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\u007f\x99=\xdb~\x02\u0082\xb8\x98\xf6\x15_h\x0e\xf5\xb9\xaf\xf9\a\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\u007f\x9f\x9bV\xe4(\x9d\xfbX\xe7\x0f\xd5\xf1*\x97\xb5m5\u01a5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u007f\xa3~\xd6x\x87u\x1aG\x1f\x0e\xb3\x06\xbeD\xe0\xdb\xcd`\x89\x899vt\u007f\xe1\x1a\x10\x00\x00\u07d4\u007f\xaa0\xc3\x15\x19\xb5\x84\xe9rP\xed*<\xf38^\xd5\xfdP\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xcf[\xa6fo\x96lTH\xc1{\xf1\xcb\v\xbc\xd8\x01\x9b\x06\x89\x05k\xc3\u042e\xbeI\x80\x00\xe0\x94\u007f\xd6y\xe5\xfb\r\xa2\xa5\xd1\x16\x19M\xcbP\x83\x18\xed\u0140\xf3\x8a\x01c\x9eI\xbb\xa1b\x80\x00\x00\u07d4\u007f\u06e01\u01cf\x9c\tmb\xd0Z6\x9e\uac3c\xccU\u5257\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\u007f\xdb\u00e8D\xe4\r\x96\xb2\xf3\xa652.`e\xf4\xca\x0e\x84\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xdf\u020dx\xbf\x1b(Z\xc6O\x1a\xdb5\xdc\x11\xfc\xb09Q\x89|\x06\xfd\xa0/\xb06\x00\x00\u07d4\u007f\xea\x19b\xe3]b\x05\x97h\xc7I\xbe\u0756\u02b90\xd3x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xef\x8c8w\x9f\xb3\a\xeco\x04K\xeb\xe4\u007f<\xfa\xe7\x96\xf1\x89\t#@\xf8l\xf0\x9e\x80\x00\u07d4\u007f\xf0\xc6?p$\x1b\xec\xe1\x9bs~SA\xb1+\x10\x901\u0609\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\u007f\xfa\xbf\xbc9\f\xbeC\u0389\x18\x8f\bh\xb2}\xcb\x0f\f\xad\x8a\x01YQ\x82\"K&H\x00\x00\xe0\x94\u007f\xfd\x02\xed7\fp`\xb2\xaeS\xc0x\xc8\x01!\x90\u07fbu\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794\x80\x02*\x12\a\xe9\x10\x91\x1f\xc9(I\xb0i\xab\f\xda\xd0C\u04c8\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x80\t\xa7\xcb\u0452\xb3\xae\u052d\xb9\x83\xd5(ER\xc1ltQ\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80\x0e}c\x1cnW:\x903/\x17\xf7\x1f_\u045bR\x8c\xb9\x89\b=lz\xabc`\x00\x00\u07d4\x80\x15m\x10\ufa320\u0254\x10c\r7\xe2i\xd4\t<\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\x172\xa4\x81\u00c0\xe5~\xd6-l)\u0799\x8a\xf3\xfa;\x13\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x1de\xc5\x18\xb1\x1d\x0e?OG\x02!Ap\x13\xc8\xe5>\u0149\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80&CZ\xacr\x8dI{\x19\xb3\xe7\xe5|(\xc5c\x95O+\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x80-\xc3\xc4\xff-}\x92^\u215fJ\x06\u05fa`\xf10\x8c\x89\x05P\x94\f\x8f\xd3L\x00\x00\u07d4\x800\xb1\x11\u0198?\x04\x85\u076c\xa7b$\xc6\x18\x064x\x9f\x89\x04V9\x18$O@\x00\x00\u07d4\x805\xbc\xff\xae\xfd\xee\xea5\x83\fI}\x14(\x9d6 #\u0789\x10CV\x1a\x88)0\x00\x00\u07d4\x805\xfeNkj\xf2z\u44a5xQ^\x9d9\xfao\xa6[\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80C\xed\"\xf9\x97\u58a4\xc1n6D\x86\xaed\x97V\x92\u0109=I\x04\xff\xc9\x11.\x80\x00\u07d4\x80C\xfd\u043cL\x97=\x16c\xd5_\xc15P\x8e\xc5\xd4\xf4\xfa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x80L\xa9IrcOc:Q\xf3V\v\x1d\x06\xc0\xb2\x93\xb3\xb1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x80R-\u07d4N\xc5.'\xd7$\xedL\x93\xe1\xf7\xbe`\x83\u0589\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x80Y\x1aB\x17\x9f4\xe6M\x9d\xf7]\xcdF;(hoUt\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x80\\\xe5\x12\x97\xa0y;\x81 g\xf0\x17\xb3\xe7\xb2\u07db\xb1\xf9\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x80]\x84o\xb0\xbc\x02\xa73r&\u0585\xbe\x9e\xe7s\xb9\x19\x8a\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x80c7\x9a{\xf2\u02d2:\x84\xc5\t>h\xda\xc7\xf7T\x81\u0149\x11v\x10.n2\xdf\x00\x00\u07d4\x80hTX\x8e\xcc\xe5AI_\x81\u008a)\x03s\xdf\x02t\xb2\x89\x1f\x8c\xdf\\n\x8dX\x00\x00\u07d4\x80oD\xbd\xebh\x807\x01^\x84\xff!\x80I\xe3\x823*3\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\x80tF\x18\xde9jT1\x97\xeeH\x94\xab\xd0c\x98\xdd|'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80w\xc3\xe4\xc4EXn\tL\xe1\x02\x93\u007f\xa0[s{V\x8c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x90\u007fY1H\xb5|F\xc1w\xe2=%\xab\u012a\xe1\x83a\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x97s\x16\x94NYB\xe7\x9b\x0e:\xba\u04cd\xa7F\be\x19\x89\x02\x1auJm\xc5(\x00\x00\xe0\x94\x80\xa0\xf6\xcc\x18l\xf6 \x14\x00sn\x06Z9\x1fR\xa9\xdfJ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x80\xab\xecZ\xa3n\\\x9d\t\x8f\x1b\x94(\x81\xbdZ\xca\u0196=\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xb2=8\v\x82\\F\xe098\x99\xa8UVF-\xa0\u1309lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xb4-\xe1p\xdb\xd7#\xf4T\xe8\x8fw\x16E-\x92\x98P\x92\x89\x10F#\xc0v-\xd1\x00\x00\u07d4\x80\xb7\x9f3\x83\x90\u047a\x1b77\xa2\x9a\x02W\xe5\xd9\x1e\a1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x80\xbf\x99^\u063a\x92p\x1d\x10\xfe\u011f\x9e}\x01M\xbe\xe0&\x89\x1f\x047\xca\x1a~\x12\x80\x00\u07d4\x80\xc0N\xfd1\x0fD\x04\x83\xc7?tK[\x9edY\x9c\xe3\xec\x89A\rXj \xa4\xc0\x00\x00\u07d4\x80\u00e9\xf6\x95\xb1m\xb1Yr\x86\u0473\xa8\xb7il9\xfa'\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\xc5>\xe7\xe35\u007f\x94\xce\rxh\x00\x9c \x8bJ\x13\x01%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xcc!\xbd\x99\xf3\x90\x05\u014f\xe4\xa4H\x90\x92 !\x8ff\u02c966\xc9yd6t\x00\x00\u07d4\x80\xd5\xc4\fY\xc7\xf5N\xa3\xa5_\xcf\xd1uG\x1e\xa3P\x99\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x80\xda/\u0762\x9a\x9e'\xf9\xe1\x15\x97^i\xae\x9c\xfb\xf3\xf2~\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x80\xe7\xb3 R0\xa5f\xa1\xf0a\xd9\"\x81\x9b\xb4\xd4\u04a0\xe1\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x80\xea\x1a\xcc\x13n\xcaKh\xc8B\xa9Z\xdfk\u007f\xee~\xb8\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80\xf0z\xc0\x9e{,<\n=\x1e\x94\x13\xa5D\xc7:A\xbe\u02c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x81\r\xb2Vu\xf4^\xa4\xc7\xf3\x17\u007f7\xce)\xe2-g\x99\x9c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81\x13\x9b\xfd\u0326V\xc40 ?r\x95\x8cT;e\x80\xd4\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x81\x14a\xa2\xb0\u0290\xba\xda\xc0j\x9e\xa1nx{3\xb1\x96\u0309\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\x81\x16M\xeb\x10\x81J\xe0\x83\x91\xf3,\bf{bH\xc2}z\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\x81\x18i1\x18A7\xd1\x19*\u020c\xd3\xe1\xe5\xd0\xfd\xb8jt\x89\x9d5\x95\xab$8\xd0\x00\x00\u0794\x81*U\xc4<\xae\xdcYr\x187\x90\x00\xceQ\rT\x886\xfd\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x81.\xa7\xa3\xb2\xc8n\xed2\xffO,sQL\xc6;\xac\xfb\u038965\u026d\xc5\u07a0\x00\x00\u07d4\x814\xdd\x1c\x9d\xf0\xd6\u0225\x81$&\xbbU\xc7a\u0283\x1f\b\x89\x06\xa2\x16\v\xb5|\xcc\x00\x00\u07d4\x81A5\u068f\x98\x11\aW\x83\xbf\x1a\xb6pb\xaf\x8d>\x9f@\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x81I\x8c\xa0{\x0f/\x17\xe8\xbb\xc7\xe6\x1a\u007fJ\xe7\xbef\xb7\x8b\x89\x05\x81\xfb\xb5\xb3;\xb0\x00\x00\u07d4\x81Um\xb2sI\xab\x8b'\x00ID\xedP\xa4n\x94\x1a\x0f_\x89\u063beI\xb0+\xb8\x00\x00\u07d4\x81U\xfalQ\xeb1\xd8\bA-t\x8a\xa0\x86\x10P\x18\x12/\x89e\xea=\xb7UF`\x00\x00\xe0\x94\x81V6\v\xbd7\ta\xce\xcakf\x91\xd7P\x06\xad L\xf2\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x81a\xd9@\xc3v\x01\x00\xb9\b\x05)\xf8\xa6\x03%\x03\x0fn\u0709\x10CV\x1a\x88)0\x00\x00\xe0\x94\x81d\xe7\x83\x14\xae\x16\xb2\x89&\xccU=,\xcb\x16\xf3V'\r\x8a\x01\xca\x13N\x95\xfb2\xc8\x00\x00\u07d4\x81e\u02b0\xea\xfbZ2\x8f\xc4\x1a\xc6M\xaeq[.\xef,e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81h\xed\xce\u007f)a\xcf)[\x9f\xcdZE\xc0l\xde\xdan\xf5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81m\x97r\xcf\x119\x91\x16\xcc\x1er\xc2lgt\xc9\xed\xd79\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81s\xc85djg.\x01R\xbe\x10\xff\xe8Ab\xdd%nL\x89\x1a\xab\xdf!E\xb40\x00\x00\u07d4\x81t\x93\u035b\xc6#p*$\xa5o\x9f\x82\xe3\xfdH\xf3\xcd1\x89\x9eK#\xf1-L\xa0\x00\x00\u07d4\x81y\xc8\tp\x18,\u0177\xd8*M\xf0n\xa9M\xb6:%\xf3\x89'o%\x9d\xe6k\xf4\x00\x00\u07d4\x81z\xc3;\xd8\xf8GVsr\x95\x1fJ\x10\u05e9\x1c\xe3\xf40\x89\n\xd7\xc4\x06\xc6m\xc1\x80\x00\xe0\x94\x81\x8f\xfe'\x1f\u00d75e\xc3\x03\xf2\x13\xf6\xd2\u0689\x89~\xbd\x8a\x016\xe0SB\xfe\u1e40\x00\u07d4\x81\x97\x94\x81!s.c\xd9\xc1H\x19N\xca\xd4n0\xb7I\u0209\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x81\x9a\xf9\xa1\xc2s2\xb1\xc3i\xbb\xda\x1b=\xe1\xc6\xe93\xd6@\x89\x11\t\xe6T\xb9\x8fz\x00\x00\xe0\x94\x81\x9c\u06a506x\xef|\xecY\u050c\x82\x16:\xcc`\xb9R\x8a\x03\x13QT_y\x81l\x00\x00\u07d4\x81\x9e\xb4\x99\vZ\xbaUG\t=\xa1+k<\x10\x93\xdfmF\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81\xa8\x81\x96\xfa\xc5\xf2<>\x12\xa6\x9d\xecK\x88\x0e\xb7\xd9s\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x81\xbc\xcb\xff\x8fD4~\xb7\xfc\xa9['\xce|\x95$\x92\xaa\xad\x89\b@\xc1!e\xddx\x00\x00\u07d4\x81\xbdu\xab\xd8e\xe0\xc3\xf0J\vO\xdb\xcbt\xd3@\x82\xfb\xb7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x81\xc1\x8c*#\x8d\xdcL\xba#\n\a-\xd7\xdc\x10\x1eb\x02s\x89Hz\x9a0E9D\x00\x00\u07d4\x81\xc9\xe1\xae\xe2\xd36]S\xbc\xfd\u0356\xc7\xc58\xb0\xfd~\xec\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\x81\u03edv\t\x13\xd3\xc3\"\xfc\xc7{I\u00ae9\a\xe7On\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x81\xd6\x19\xffW&\xf2@_\x12\x90Lr\xeb\x1e$\xa0\xaa\xeeO\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x81\xef\u25aev\xc8`\xd1\xc5\xfb\xd3=G\xe8\u0399\x96\xd1W\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81\xf8\xde,(=_\u052f\xbd\xa8]\xed\xf9v\x0e\xab\xbb\xb5r\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x82\f\x19)\x11\x96P[e\x05\x9d\x99\x14\xb7\t\v\xe1\u06c7\u0789\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x82\x1c\xb5\xcd\x05\xc7\uf41f\xe1\xbe`s=\x89c\xd7`\xdcA\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x82\x1dy\x8a\xf1\x99\x89\u00ee[\x84\xa7\xa7(<\xd7\xfd\xa1\xfa\xbe\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x82\x1e\xb9\t\x94\xa2\xfb\xf9K\xdc23\x91\x02\x96\xf7o\x9b\xf6\xe7\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x82$\x9f\xe7\x0fa\u01b1o\x19\xa3$\x84\x0f\xdc\x02\x021\xbb\x02\x8a\x02\x036\xb0\x8a\x93c[\x00\x00\u07d4\x82(\xeb\xc0\x87H\x0f\xd6EG\xca(\x1f^\xac\xe3\x04\x14S\xb9\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\x82)\u03b9\xf0\xd7\b9I\x8dD\xe6\xab\xed\x93\xc5\xca\x05\x9f]\x8a\x1a\x1c\x1b<\x98\x9a \x10\x00\x00\u07d4\x82.\xdf\xf66V:a\x06\xe5.\x9a%\x98\xf7\xe6\xd0\xef'\x82\x89\x01\xf4\xf9i=B\u04c0\x00\u07d4\x822\x19\xa2Yv\xbb*\xa4\xaf\x8b\xadA\xac5&\xb4\x936\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x822\xd1\xf9t.\u07cd\xd9'\xda5;*\xe7\xb4\xcb\xceu\x92\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\x824\xf4c\u0444\x85P\x1f\x8f\x85\xac\xe4\x97,\x9bc-\xbc\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\x827htg7\xcem\xa3\x12\xd5>TSN\x10o\x96|\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82;\xa7dr8\xd1\x13\xbc\xe9\x96JC\u0420\x98\x11\x8b\xfeM\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x82@t1(\x06\xdaGHCBf\xee\x00!@\u305a\u0089Q\xb1\u04c3\x92a\xac\x00\x00\u07d4\x82C\x8f\u04b3*\x9b\xddgKI\xd8\xcc_\xa2\xef\xf9x\x18G\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82HW(\xd0\xe2\x81V7X\xc7Z\xb2~\xd9\u80a0\x00-\x89\a\xf8\b\xe9)\x1el\x00\x00\u07d4\x82K<\x19)]~\xf6\xfa\xa7\xf3t\xa4y\x84\x86\xa8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82Q5\x8c\xa4\xe0`\u0775Y\xcaX\xbc\v\u077e\xb4\a\x02\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82Q5\xb1\xa7\xfc\x16\x05aL\x8a\xa4\u042cm\xba\u040fH\x0e\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\x82S\t\xa7\xd4]\x18\x12\xf5\x1en\x8d\xf5\xa7\xb9ol\x90\x88\x87\x89\x804\xf7\u0671f\xd4\x00\x00\u07d4\x82Z\u007fN\x10\x94\x9c\xb6\xf8\x96Bh\xf1\xfa_W\xe7\x12\xb4\u0109\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82a\xfa#\f\x90\x1dC\xffW\x9fG\x80\u04d9\xf3\x1e`v\xbc\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82b\x16\x9baXp\x13N\xb4\xacl_G\x1ck\xf2\xf7\x89\xfc\x89\x19\x12z\x13\x91\xea*\x00\x00\u07d4\x82c\xec\xe5\xd7\t\xe0\u05eeq\u0328h\xed7\xcd/\xef\x80{\x895\xab\x02\x8a\xc1T\xb8\x00\x00\xe0\x94\x82l\xe5y\x052\xe0T\x8ca\x02\xa3\r>\xac\x83k\xd68\x8f\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4\x82n\xb7\xcds\x19\xb8-\xd0z\x1f;@\x90q\xd9n9g\u007f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x82u1\xa6\u0141z\xe3_\x82\xb0\v\x97T\xfc\xf7LU\xe22\x89\xc3(\t>a\xee@\x00\x00\u0794\x82u\xcdhL6y\u0548}\x03fN3\x83E\xdc<\xdd\xe1\x88\xdbD\xe0I\xbb,\x00\x00\u07d4\x82\x84\x92;b\u62ff|+\x9f4\x14\xd1>\xf6\xc8\x12\xa9\x04\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\x82\x8b\xa6Q\u02d3\x0e\xd9xqV)\x9a=\xe4L\u040br\x12\x89Hz\x9a0E9D\x00\x00\u07d4\x82\xa1\\\xef\x1dl\x82`\xea\xf1Y\xea?\x01\x80\xd8g}\xce\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82\xa8\xb9kl\x9e\x13\xeb\xec\x1e\x9f\x18\xac\x02\xa6\x0e\xa8\x8aH\xff\x89lk\x8c@\x8es\xb3\x00\x00\u07d4\x82\xa8\u02ff\xdf\xf0+.8\xaeK\xbf\xca\x15\xf1\xf0\xe8;\x1a\xea\x89\x04\x9b\x99\x1c'\xefm\x80\x00\u07d4\x82\xe4F\x1e\xb9\xd8I\xf0\x04\x1c\x14\x04!\x9eBr\u0110\n\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82\xe5w\xb5\x15\xcb+\b`\xaa\xfe\x1c\xe0\x9aY\xe0\x9f\xe7\xd0@\x89 \x86\xac5\x10R`\x00\x00\u07d4\x82\xea\x01\xe3\xbf.\x83\x83nqpN\"\xa2q\x93w\xef\xd9\u00c9\xa4\xccy\x95c\u00c0\x00\x00\u07d4\x82\xf2\xe9\x91\xfd2L_]\x17v\x8e\x9fa3]\xb61\x9dl\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x82\xf3\x9b'X\xaeB'{\x86\u059fu\xe6(\xd9X\xeb\u02b0\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94\x82\xf8T\xc9\xc2\xf0\x87\xdf\xfa\x98Z\xc8 \x1ebl\xa5Fv\x86\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x82\xffqo\xdf\x03>\xc7\xe9B\xc9\t\u0643\x18g\xb8\xb6\xe2\xef\x89a\t=|,m8\x00\x00\u07d4\x83\b\xed\n\xf7\xf8\xa3\xc1u\x1f\xaf\xc8w\xb5\xa4*\xf7\xd3X\x82\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x83\x1cD\xb3\b@G\x18K*\xd2\x18h\x06@\x907P\xc4]\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x83!\x05\x83\xc1jN\x1e\x1d\xac\x84\xeb\xd3~=\x0f|W\ub909lk\x93[\x8b\xbd@\x00\x00\u07d4\x83,T\x17k\xdfC\xd2\u027c\u05f8\b\xb8\x95V\xb8\x9c\xbf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x833\x16\x98]Gt+\xfe\xd4\x10`J\x91\x95<\x05\xfb\x12\xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x834vK{9zNW\x8fP6M`\xceD\x89\x9b\xff\x94\x89\x05\x03\xb2\x03\xe9\xfb\xa2\x00\x00\xe0\x94\x83;j\x8e\xc8\xda@\x81\x86\xac\x8a}*m\xd6\x15#\xe7\u0384\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x83=?\xaeT*\xd5\xf8\xb5\f\xe1\x9b\xde+\xecW\x91\x80\u020c\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\x83=\xb4,\x14\x16<{\xe4\u02b8j\u0153\xe0bf\u0599\u054a$\xe4\r+iC\xef\x90\x00\x00\xe0\x94\x83V;\xc3d\ud060\xc6\xda;V\xffI\xbb\xf2g\x82z\x9c\x8a\x03\xab\x91\xd1{ \xdeP\x00\x00\u07d4\x83zd]\xc9\\IT\x9f\x89\x9cN\x8b\u03c7S$\xb2\xf5|\x89 \x8c9J\xf1\u0208\x00\x00\u07d4\x83\x8b\xd5e\xf9\x9f\xdeH\x05?y\x17\xfe3<\xf8J\xd5H\xab\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x83\x90\x8a\xa7G\x8am\x1c\x9b\x9b\x02\x81\x14\x8f\x8f\x9f$+\x9f\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\x83\x92\xe57vq5x\x01[\xffI@\xcfC\x84\x9d}\u02e1\x89\bM\xf05]V\x17\x00\x00\xe0\x94\x83\x97\xa1\xbcG\xac\xd6GA\x81Y\xb9\x9c\xeaW\xe1\xe6S-n\x8a\x01\xf1\x0f\xa8'\xb5P\xb4\x00\x00\u07d4\x83\x98\xe0~\xbc\xb4\xf7_\xf2\x11m\xe7|\x1c*\x99\xf3\x03\xa4\u03c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xa3\x14\x883\xd9dI\x84\xf7\xc4u\xa7\x85\a\x16\ufd00\xff\x89\xb8Pz\x82\a( \x00\x00\u07d4\x83\xa4\x02C\x8e\x05\x19w=TH2k\xfba\xf8\xb2\f\xf5-\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\x83\xa9;[\xa4\x1b\xf8\x87 \xe4\x15y\f\xdc\vg\xb4\xaf4\u0109\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x83\xc2=\x8aP!$\xee\x15\x0f\b\xd7\x1d\xc6rt\x10\xa0\xf9\x01\x8a\a3\x1f;\xfef\x1b\x18\x00\x00\u07d4\x83\u0217\xa8Ki^\xeb\xe4fy\xf7\xda\x19\xd7vb\x1c&\x94\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xd52\u04cdm\xee?`\xad\u018b\x93a3\u01e2\xa1\xb0\u0749\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xdb\xf8\xa1(S\xb4\n\xc6\x19\x96\xf8\xbf\x1d\xc8\xfd\xba\xdd\xd3)\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x83\xdb\xfd\x8e\xda\x01\xd0\u078e\x15\x8b\x16\u0413_\xc28\n]\u01c9 \x86\xac5\x10R`\x00\x00\u07d4\x83\xe4\x80U2|(\xb5\x93o\xd9\xf4D~s\xbd\xb2\xdd3v\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\x83\xfeZ\x1b2\x8b\xaeD\a\x11\xbe\xafj\xad`&\xed\xa6\xd2 \x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x84\x00\x8ar\xf8\x03o?\xeb\xa5B\xe3Px\xc0W\xf3*\x88%\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x84\x0e\xc8>\xa96!\xf04\xe7\xbb7b\xbb\x8e)\xde\xd4\xc4y\x89\x87\x86x2n\xac\x90\x00\x00\xe0\x94\x84\x11E\xb4H@\xc9F\xe2\x1d\xbc\x19\x02d\xb8\xe0\xd5\x02\x93i\x8a?\x87\bW\xa3\xe0\xe3\x80\x00\x00\u07d4\x84#!\a\x93+\x12\xe01\x86X5%\xce\x02:p>\xf8\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4\x84$O\xc9ZiW\xed|\x15\x04\xe4\x9f0\xb8\xc3^\xcaKy\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x841'}{\xdd\x10E}\xc0\x17@\x8c\x8d\xbb\xbdAJ\x8d\xf3\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4\x847Z\xfb\xf5\x9b:\x1da\xa1\xbe2\xd0u\xe0\xe1ZO\xbc\xa5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x84;\xd3P/E\xf8\xbcM\xa3p\xb3#\xbd\xac?\xcf_\x19\xa6\x89P\x03\x9dc\xd1\x1c\x90\x00\x00\u07d4\x84P34c\rw\xf7AG\xf6\x8b.\bf\x13\xc8\xf1\xad\xe9\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x84R\x03u\x0fqH\xa9\xaa&)!\xe8mC\xbfd\x19t\xfd\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x84a\xec\u0126\xa4^\xb1\xa5\xb9G\xfb\x86\xb8\x80i\xb9\x1f\xcdo\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84g^\x91wrmE\xea\xa4k9\x92\xa3@\xba\u007fq\f\x95\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x84hl{\xadv,T\xb6g\u055f\x90\x94<\xd1M\x11z&\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\x89\xf6\xad\x1d\x9a\x94\xa2\x97x\x91V\x89\x9d\xb6AT\xf1\u06f5\x89\x13t\a\xc0<\x8c&\x80\x00\u07d4\x84\x8c\x99Jy\x00?\xe7\xb7\xc2l\xc62\x12\xe1\xfc/\x9c\x19\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\x8f\xbd)\xd6|\xf4\xa0\x13\xcb\x02\xa4\xb1v\xef$N\x9e\u6349\x01\x17*ck\xbd\xc2\x00\x00\u07d4\x84\x94\x9d\xbaU\x9ac\xbf\xc8E\xde\xd0n\x9f-\x9b\u007f\x11\xef$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\x9a\xb8\a\x90\xb2\x8f\xf1\xff\u05ba9N\xfctc\x10\\6\xf7\x89\x01\xe0+\xe4\xael\x84\x00\x00\u07d4\x84\x9b\x11oYc\x01\xc5\u063bb\xe0\xe9z\x82H\x12n9\xf3\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x84\xa7L\xee\xcf\xf6\\\xb9;/\x94\x9dw>\xf1\xad\u007f\xb4\xa2E\x89\x05\n\x9bDF\x85\xc7\x00\x00\u07d4\x84\xaa\xc7\xfa\x19\u007f\xf8\\0\xe0;zS\x82\xb9W\xf4\x1f:\xfb\x89\b\x8b#\xac\xff\u0650\x00\x00\u07d4\x84\xaf\x1b\x15sB\xd5Ch&\r\x17\x87b0\xa54\xb5K\x0e\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x84\xb0\xeek\xb87\u04e4\xc4\xc5\x01\x1c:\"\x8c\x0e\u06b4cJ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\xb4\xb7Nf#\xba\x9d\x15\x83\xe0\u03feId?\x168AI\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\xb6\xb6\xad\xbe/[>-h,f\xaf\x1b\u0110S@\xc3\xed\x89!\x92\xf8\xd2\"\x15\x00\x80\x00\xe0\x94\x84\xb9\x1e.)\x02\xd0^+Y\x1bA\b;\u05fe\xb2\xd5,t\x8a\x02\x15\xe5\x12\x8bE\x04d\x80\x00\u07d4\x84\xbc\xbf\"\xc0\x96\a\xac\x844\x1d.\xdb\xc0;\xfb\x179\xd7D\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x84\xbf\xce\xf0I\x1a\n\xe0iK7\u03ac\x02E\x84\xf2\xaa\x04g\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x84\xcb}\xa0P-\xf4\\\xf5a\x81{\xbd#b\xf4Q\xbe\x02\u0689Hz\x9a0E9D\x00\x00\u07d4\x84\xccxx\xda`_\xdb\x01\x9f\xab\x9bL\xcf\xc1Wp\x9c\u0765\x89Hy\x85\x13\xaf\x04\xc9\x00\x00\u07d4\x84\xdb\x14Y\xbb\x00\x81.\xa6~\xcb=\xc1\x89\xb7!\x87\xd9\xc5\x01\x89\b\x11\xb8\xfb\u0685\xab\x80\x00\u07d4\x84\u9516\x80\xbe\xcehA\xb9\xa7\xe5%\r\b\xac\xd8}\x16\u0349\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x84\xe9\u03c1f\xc3j\xbf\xa4\x90S\xb7\xa1\xad@6 &\x81\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\xec\x06\xf2G\x00\xfeBAL\xb9\x89|\x15L\x88\xde/a2\x89Hz\x9a0E9D\x00\x00\xe0\x94\x84\xf5\"\xf0R\x0e\xbaR\xdd\x18\xad!\xfaK\x82\x9f+\x89\u02d7\x8a\x01\fQ\x06\xd5\x13O\x13\x00\x00\u07d4\x85\v\x9d\xb1\x8f\xf8K\xf0\xc7\xdaI\xea7\x81\xd9 \x90\xad~d\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\x85\x10\xee\x93O\f\xbc\x90\x0e\x10\a\xeb8\xa2\x1e*Q\x01\xb8\xb2\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4\x85\x16\xfc\xafw\u0213\x97\x0f\xcd\x1a\x95\x8b\xa9\xa0\x0eI\x04@\x19\x89\n\xa3\xeb\x16\x91\xbc\xe5\x80\x00\u07d4\x85\x1a\xa9\x1c\x82\xf4/\xad]\xd8\xe8\xbb^\xa6\x9c\x8f:Yw\u0449\b\x0eV\x1f%xy\x80\x00\u07d4\x85\x1c\rb\xbeF5\xd4w~\x805\xe3~K\xa8Q|a2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x85\x1d\u00ca\xdbE\x93r\x9av\xf3:\x86\x16\u06b6\xf5\xf5\x9aw\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x852I\b\x97\xbb\xb4\u038b\u007fk\x83~L\xba\x84\x8f\xbe\x99v\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x85>j\xba\xf4Di\xc7/\x15\x1dN\"8\x19\xac\xedN7(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x85F\x91\xceqO2\\\xedU\xceY(\u039b\xa1/\xac\u0478\x89\xedp\xb5\xe9\xc3\xf2\xf0\x00\x00\u07d4\x85L\fF\x9c$k\x83\xb5\u0473\xec\xa4C\xb3\x9a\xf5\xee\x12\x8a\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x85]\x9a\xef,9\xc6#\r\t\u025e\xf6II\x89\xab\u61c5\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\x85c\u0113a\xb6%\xe7hw\x1c\x96\x15\x1d\xbf\xbd\x1c\x90iv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x85fa\t\x01\xaa\xce8\xb82D\xf3\xa9\xc810jg\xb9\u0709\xb0\x82\x13\xbc\xf8\xff\xe0\x00\x00\xe0\x94\x85j\xa2<\x82\xd7![\xec\x8dW\xf6\n\xd7^\xf1O\xa3_D\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x85nZ\xb3\xf6L\x9a\xb5k\x00\x93\x93\xb0\x16d\xfc\x03$\x05\x0e\x89a\t=|,m8\x00\x00\u07d4\x85n\xb2\x04$\x1a\x87\x83\x0f\xb2)\x03\x13C\xdc0\x85OX\x1a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x85s,\x06\\\xbdd\x11\x99A\xae\xd40\xacYg\vlQ\u0109'\xa5sb\xab\n\x0e\x80\x00\xe0\x94\x85x\xe1\x02\x12\xca\x14\xff\a2\xa8$\x1e7F}\xb8V2\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x85y\xda\xdf\x1a9Z4q\xe2\vov=\x9a\x0f\xf1\x9a?o\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x85\u007f\x10\v\x1aY0\"^\xfc~\x90 \u05c3'\xb4\x1c\x02\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x85\x94mV\xa4\xd3q\xa93hS\x96\x90\xb6\x0e\xc8%\x10tT\x89]\u0212\xaa\x111\xc8\x00\x00\xe0\x94\x85\x99\xcb\u0566\xa9\xdc\u0539f\xbe8}iw]\xa5\xe3C'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86$_Yf\x91\t>\xce?=<\xa2&>\xac\xe8\x19A\u0649\n1\x06+\xee\xedp\x00\x00\u07d4\x86%i!\x1e\x8cc'\xb5A^:g\xe5s\x8b\x15\xba\xafn\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x86)}s\x0f\xe0\xf7\xa9\xee$\xe0\x8f\xb1\b{1\xad\xb3\x06\xa7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86D\xcc(\x1b\xe32\xcc\xce\xd3m\xa4\x83\xfb*\aF\u067a.\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x86I\x9a\x12(\xff-~\xe3\au\x93dPo\x8e\x8c\x83\a\xa5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x86K\xecPi\xf8U\xa4\xfdX\x92\xa6\xc4I\x1d\xb0|\x88\xff|\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86W\n\xb2Y\u0271\xc3,\x97) /w\xf5\x90\xc0}\xd6\x12\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86c\xa2A\xa0\xa8\x9ep\xe1\x82\xc8E\xe2\x10\\\x8a\xd7&K\u03ca\x03#\xb1=\x83\x98\xf3#\x80\x00\u07d4\x86g\xfa\x11U\xfe\xd72\u03f8\u0725\xa0\xd7e\xce\r\a\x05\xed\x89\x04n\xc9e\u00d3\xb1\x00\x00\u07d4\x86h\xaf\x86\x8a\x1e\x98\x88_\x93\u007f&\x15\xde\xd6u\x18\x04\xeb-\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x86t\nFd\x8e\x84Z]\x96F\x1b\x18\t\x1f\xf5{\xe8\xa1o\x8a\x14\xc0\x974\x85\xbf9@\x00\x00\xe0\x94\x86~\xbaVt\x8aY\x045\r,\xa2\xa5\u039c\xa0\vg\n\x9b\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x86\x80dt\xc3X\x04}\x94\x06\xe6\xa0\u007f@\x94[\xc82\x8eg\x8a\x01u.\xb0\xf7\x01=\x10\x00\x00\u07d4\x86\x88=T\xcd9\x15\xe5I\tU0\xf9\xab\x18\x05\xe8\xc5C-\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x86\x8c#\xbe\x874f\xd4\xc7L\"\n\x19\xb2E\xd1x~\x80\u007f\x89J\x13\xbb\xbd\x92\u020e\x80\x00\xe0\x94\x86\x92O\xb2\x11\xaa\xd2<\xf5\xce`\x0e\n\xae\x80c\x96D@\x87\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x86\x93\u9e3e\x94B^\xefyi\xbci\xf9\xd4/|\xadg\x1e\x8967\tlK\xcci\x00\x00\xe0\x94\x86\x9f\x1a\xa3\x0eDU\xbe\xb1\x82 \x91\xde\\\xad\xecy\xa8\xf9F\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x86\xa1\xea\xde\xeb0F\x13E\xd9\xefk\xd0R\x16\xfa$|\r\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86\xa5\xf8%\x9e\u0570\x9e\x18\x8c\xe3F\xee\x92\xd3J\xa5\u0753\xfa\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xb7\xbdV<\uad86\xf9bD\xf9\xdd\xc0*\u05f0\xb1K\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x86\u008bVx\xaf7\xd7'\xec\x05\xe4Dw\x90\xf1_q\xf2\xea\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xc4\xce\x06\u066c\x18[\xb1H\xd9o{z\xbes\xf4A\x00m\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x86\xc8\xd0\u0642\xb59\xf4\x8f\x980\xf9\x89\x1f\x9d`z\x94&Y\x8a\x02\xce\xd3wa\x82O\xb0\x00\x00\u07d4\x86\xc94\xe3\x8eS\xbe;3\xf2t\xd0S\x9c\xfc\xa1Y\xa4\xd0\u04494\x95tD\xb8@\xe8\x00\x00\xe0\x94\x86\xca\x01E\x95~k\r\xfe6\x87_\xbez\r\xecU\xe1z(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x86\u02af\xac\xf3*\xa01|\x03*\xc3k\xab\xed\x97G\x91\xdc\x03\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x86\u0377\xe5\x1a\xc4Gr\xbe6\x90\xf6\x1d\x0eYvn\x8b\xfc\x18\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x86\xdfs\xbd7\u007f,\t\xdec\xc4]g\xf2\x83\xea\xef\xa0\xf4\xab\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86\xe3\xfe\x86\xe9=\xa4\x86\xb1Bf\xea\xdf\x05l\xbf\xa4\xd9\x14C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86\xe8g\x0e'Y\x8e\xa0\x9c8\x99\xabw\x11\u04f9\xfe\x90\x1c\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xefd&!\x19I\xcc7\xf4\xc7^xP6\x9d\f\xf5\xf4y\x8a\x02\xd6_2\xea\x04Z\xf6\x00\x00\u07d4\x86\xf0]\x19\x06>\x93i\xc6\x00N\xb3\xf1#\x94:|\xffN\xab\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x86\xf2>\x9c\n\xaf\u01cb\x9c@M\xcd`3\x9a\x92[\xff\xa2f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x86\xf4\xf4\n\u0644\xfb\xb8\t3\xaebn\x0eB\xf93?\xddA\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x86\xf9\\[\x11\xa2\x93\x94\x0e5\xc0\xb8\x98\u0637_\b\xaa\xb0m\x8a\x06D\xe3\xe8u\xfc\xcft\x00\x00\u07d4\x86\xff\xf2 \xe5\x93\x05\xc0\x9fH8`\xd6\xf9N\x96\xfb\xe3/W\x89\x02S[j\xb4\xc0B\x00\x00\u07d4\x87\a\x96\xab\xc0\u06c4\xaf\x82\xdaR\xa0\xedhsM\xe7\xe66\xf5\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x87\x0f\x15\xe5\u07cb\x0e\xab\xd0%iSz\x8e\xf9;Vx\\B\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\x87\x181`\xd1r\xd2\xe0\x84\xd3'\xb8k\xcb|\x1d\x8eg\x84\xef\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94\x87\x1b\x8a\x8bQ\u07a1\x98\x9aY!\xf1>\xc1\xa9U\xa5\x15\xadG\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x87%\xe8\xc7S\xb3\xac\xbf\u0725_I\x13\\3\x91\x99\x10`)\n\xa7\xf6\u0338\xf8Zx\u06c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x87Pa\xee\x12\xe8 \x04\x1a\x01\x94,\xb0\xe6[\xb4'\xb0\x00`\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\x87XJ?a;\xd4\xfa\xc7L\x1ex\v\x86\xd6\xca\xeb\x89\f\xb2\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x87d\xd0'\"\x00\t\x96\xec\xd4u\xb43)\x8e\x9fT\v\x05\xbf\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x87l?!\x8bGv\xdf<\xa9\xdb\xfb'\r\xe1R\xd9N\xd2R\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x87u\xa6\x10\xc5\x02\xb9\xf1\xe6\xadL\xda\u06cc\xe2\x9b\xffu\xf6\xe4\x89 \x86\xac5\x10R`\x00\x00\u07d4\x87vN6w\xee\xf6\x04\xcb\u015a\xed$\xab\xdcVk\t\xfc%\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\x87\x87\xd1&w\xa5\xec)\x1eW\xe3\x1f\xfb\xfa\xd1\x05\xc32K\x87\x8a\x02\xa2N\xb52\b\xf3\x12\x80\x00\u07d4\x87\x94\xbfG\xd5E@\xec\xe5\xc7\"7\xa1\xff\xb5\x11\u0777Gb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x87\xa5>\xa3\x9fY\xa3[\xad\xa85%!dU\x94\xa1\xa7\x14\u02c9g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x87\xa7\xc5\b\xefqX-\u0665Cr\xf8\x9c\xb0\x1f%/\xb1\x80\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x87\xaf%\xd3\xf6\xf8\xee\xa1S\x13\xd5\xfeEW\xe8\x10\xc5$\xc0\x83\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x87\xb1\x0f\x9c(\x00\x98\x17\x9a+v\xe9\u0390\xbea\xfc\x84M\r\x89Hz\x9a0E9D\x00\x00\u07d4\x87\xbf|\xd5\u0629)\xe1\u01c5\xf9\xe5D\x91\x06\xac#$c\u0249\x047\xb1\x1f\xccEd\x00\x00\u07d4\x87\u0118\x17\t4\xb8#=\x1a\xd1\xe7i1}\\G_/@\x897\b\xba\xed=h\x90\x00\x00\u07d4\x87\xcf6\xad\x03\xc9\xea\xe9\x05:\xbbRB\u0791\x17\xbb\x0f*\v\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x87\u05ec\x06S\xcc\xc6z\xa9\xc3F\x9e\xefCR\x19?}\xbb\x86\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94\x87\xe3\x06+#!\xe9\u07f0\x87\\\u311c\x9b.5\"\xd5\n\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x87\xe6\x03N\xcf#\xf8\xb5c\x9d_\x0e\xa7\n\"S\x8a\x92\x04#\x89\x11\xc7\xea\x16.x \x00\x00\u07d4\x87\xefm\x8bj|\xbf\x9b\\\x8c\x97\xf6~\xe2\xad\u00a7;?w\x89\n\xdd\x1b\xd2<\x00L\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x88F\x92\x8dh2\x89\xa2\xd1\x1d\xf8\xdbz\x94t\x98\x8e\xf0\x13H\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x88I\x80\xebEe\xc1\x04\x83\x17\xa8\xf4\u007f\u06f4a\x96[\u4049\xd8\xd6\x11\x9a\x81F\x05\x00\x00\xe0\x94\x88Jz9\u0411n\x05\xf1\xc2B\xdfU`\u007f7\u07cc_\u068a\x04\xf4\x84<\x15|\x8c\xa0\x00\x00\u07d4\x88T\x93\xbd\xa3j\x042\x97eF\xc1\xdd\xceq\xc3\xf4W\x00!\x89\v\xbfQ\r\xdf\xcb&\x00\x00\xe0\x94\x88`\x9e\nF[n\x99\xfc\xe9\a\x16mW\xe9\xda\b\x14\xf5\u020a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x88m\n\x9e\x17\xc9\xc0\x95\xaf.\xa25\x8b\x89\xecpR\x12\ue509\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\x88y~Xg^\xd5\xccL\x19\x98\a\x83\xdb\xd0\xc9V\bQS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x88|\xacA\xcdpo3E\xf2\xd3J\xc3N\x01u*nY\t\x89 F\\\ue7617\x00\x00\u07d4\x88\x88\x8aW\xbd\x96\x87\xcb\xf9P\xae\xea\u03d7@\xdc\xc4\xd1\xefY\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u0794\x88\x89D\x83\x16\xcc\xf1N\xd8m\xf8\xe2\xf4x\xdcc\xc43\x83@\x88\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x88\x8c\x16\x14I3\x19|\xac&PM\xd7n\x06\xfdf\x00\u01c9\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x88\x8e\x94\x91p\x83\xd1R +S\x1699\x86\x9d'\x11u\xb4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x88\x90\x87\xf6o\xf2\x84\xf8\xb5\xef\xbd)I;pg3\xab\x14G\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\x88\x95\xebrb&\xed\xc3\xf7\x8c\u01a5\x15\a{2\x96\xfd\xb9^\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x88\x97Z_\x1e\xf2R\x8c0\v\x83\xc0\xc6\a\xb8\xe8}\u0593\x15\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\u07d4\x88\x9d\xa4\x0f\xb1\xb6\x0f\x9e\xa9\xbdzE>XL\xf7\xb1\xb4\xd9\xf7\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x88\x9d\xa6b\xebJ\n*\x06\x9d+\xc2K\x05\xb4\xee.\x92\xc4\x1b\x89Z,\x8cTV\xc9\xf2\x80\x00\u07d4\x88\xa1\"\xa28,R91\xfbQ\xa0\u032d;\xeb[rY\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x88\xa2\x15D0\xc0\xe4\x11G\xd3\xc1\xfe\u3cf0\x06\xf8Q\xed\xbd\x8965f3\xeb\xd8\xea\x00\x00\u07d4\x88\xb2\x17\u0337\x86\xa2T\xcfM\xc5\u007f]\x9a\xc3\xc4U\xa3\x04\x83\x892$\xf4'#\xd4T\x00\x00\xe0\x94\x88\xbcC\x01.\xdb\x0e\xa9\xf0b\xacCxC%\n9\xb7\x8f\xbb\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x88\xc2Qj|\xdb\t\xa6'mr\x97\xd3\x0fZM\xb1\xe8K\x86\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x88\xc3ad\rki7;\b\x1c\xe0\xc43\xbdY\x02\x87\xd5\xec\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x88\xd5A\xc8@\xceC\xce\xfb\xafm\x19\xafk\x98Y\xb5s\xc1E\x89\t79SM(h\x00\x00\u07d4\x88\xde\x13\xb0\x991\x87|\x91\rY1e\xc3d\u0221d\x1b\u04c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x88\xde\u017d?N\xba-\x18\xb8\xaa\xce\xfa{r\x15H\xc3\x19\xba\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\x88\xe6\xf9\xb2G\xf9\x88\xf6\xc0\xfc\x14\xc5o\x1d\xe5>\u019dC\u0309\x05k\xc7^-c\x10\x00\x00\u07d4\x88\xee\u007f\x0e\xfc\x8fw\x8ckh~\xc3+\xe9\xe7\xd6\xf0 \xb6t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x88\xf1\x04_\x19\xf2\xd3\x19\x18\x16\xb1\xdf\x18\xbbn\x145\xad\x1b8\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x89\x00\x9e\a\xe3\xfahc\xa7x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x89Wr~r\xcfb\x90 \xf4\xe0^\xdfy\x9a\xa7E\x80b\u0409wC\"\x17\xe6\x83`\x00\x00\u07d4\x89]iN\x88\v\x13\xcc\u0404\x8a\x86\xc5\xceA\x1f\x88Gk\xbf\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x89^\xc5TVD\u0dc30\xff\xfa\xb8\xdd\xea\xc9\xe83\x15l\x89 \x86\xac5\x10R`\x00\x00\u07d4\x89`\tRj,{\f\t\xa6\xf6:\x80\xbd\U0009d707\u079c\x89\xbb\xb8k\x82#\xed\xeb\x00\x00\u07d4\x89g\u05f9\xbd\xb7\xb4\xae\xd2.e\xa1]\xc8\x03\xcbz!?\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x89n3\\\xa4z\xf5yb\xfa\x0fM\xbf>E\xe6\x88\u02e5\x84\x89J/\xc0\xab`R\x12\x00\x00\u07d4\x89s\xae\xfd^\xfa\xee\x96\t]\x9e(\x8fj\x04l\x977KC\x89\a\xa4\u0120\xf32\x14\x00\x00\u07d4\x89\x8cr\xddseX\xef\x9eK\xe9\xfd\xc3O\xefT\xd7\xfc~\b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x89\x9b<$\x9f\fK\x81\xdfu\xd2\x12\x00M=m\x95/\xd2#\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x89\xab\x13\xee&mw\x9c5\xe8\xbb\x04\u034a\x90\xcc!\x03\xa9[\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\x89\xc43\xd6\x01\xfa\xd7\x14\xdaci0\x8f\xd2l\x1d\u0254+\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x89\xd7[\x8e\b1\xe4o\x80\xbc\x17A\x88\x18N\x00o\xde\x0e\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x89\u3d5a\x15\x86G7\u0513\xc1\xd2<\xc5=\xbf\x8d\xcb\x13b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x89\xfc\x8eM8k\r\v\xb4\xa7\a\xed\xf3\xbdV\r\xf1\xad\x8fN\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\x89\xfe\xe3\r\x17(\xd9l\xec\xc1\u06b3\xda.w\x1a\xfb\u03eaA\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x8a\x1c\u016c\x11\x1cI\xbf\xcf\xd8H\xf3}\xd7h\xaae\u0208\x02\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x8a \xe5\xb5\xce\xe7\xcd\x1fU\x15\xba\xce;\xf4\xf7\u007f\xfd\xe5\xcc\a\x89\x04V9\x18$O@\x00\x00\xe0\x94\x8a!}\xb3\x8b\xc3_!_\xd9)\x06\xbeBCo\xe7\xe6\xed\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x8a$:\n\x9f\xeaI\xb89TwE\xff-\x11\xaf?K\x05\"\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x8a$}\x18e\x10\x80\x9fq\xcf\xfcEYG\x1c9\x10\x85\x81!\x89a\t=|,m8\x00\x00\u07d4\x8a4p(-^**\xef\u05e7P\x94\xc8\"\xc4\xf5\xae\uf289\r(\xbc`dx\xa5\x80\x00\u07d4\x8a6\x86\x9a\xd4x\x99|\xbfm\x89$\xd2\n<\x80\x18\xe9\x85[\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8aC\x14\xfba\u0353\x8f\xc3>\x15\xe8\x16\xb1\x13\U000ac267\xfb\x89\x17vNz\xede\x10\x00\x00\u07d4\x8aOJ\u007fR\xa3U\xba\x10_\xca r\xd3\x06_\xc8\xf7\x94K\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8aX1(,\xe1Jezs\r\xc1\x88&\xf7\xf9\xb9\x9d\xb9h\x89\uaf8a[A\xc16\x00\x00\u07d4\x8a_\xb7W\x93\xd0C\xf1\xbc\xd48\x85\xe07\xbd0\xa5(\xc9'\x89\x13Snm.\x9a\xc2\x00\x00\u07d4\x8af\xab\xbc-0\xce!\xa83\xb0\u06ceV\x1dQ\x05\xe0\xa7,\x89%\xf1\xde\\v\xac\xdf\x00\x00\u07d4\x8atl]g\x06G\x11\xbf\xcah[\x95\xa4\xfe)\x1a'\x02\x8e\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8ax\n\xb8z\x91E\xfe\x10\xed`\xfaGjt\n\xf4\u02b1\u0489\x12\x1b.^ddx\x00\x00\u07d4\x8az\x06\xbe\x19\x9a:X\x01\x9d\x84j\xc9\xcb\xd4\xd9]\xd7W\u0789\xa2\xa4#\x94BV\xf4\x00\x00\u07d4\x8a\x81\x01\x14\xb2\x02]\xb9\xfb\xb5\x00\x99\xa6\xe0\u02de.\xfak\u0709g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x8a\x86\xe4\xa5\x1c\x01;\x1f\xb4\xc7k\xcf0f|x\xd5.\xed\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\x9e\u029cZ\xba\x8e\x13\x9f\x80\x03\xed\xf1\x16:\xfbp\xaa:\xa9\x89#\xc7W\a+\x8d\xd0\x00\x00\u07d4\x8a\xb89\xae\xaf*\xd3|\xb7\x8b\xac\xbb\xb63\xbc\xc5\xc0\x99\xdcF\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\u021b\u06780\x1ek\x06w\xfa%\xfc\xf0\xf5\x8f\f\u01f6\x11\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x8a\xdcS\xef\x8c\x18\xed0Qx]\x88\xe9\x96\xf3\xe4\xb2\x0e\xcdQ\x8a\b\xe4\xd3\x16\x82v\x86@\x00\x00\u07d4\x8a\xe6\xf8\vp\xe1\xf2<\x91\xfb\u0569f\xb0\xe4\x99\xd9]\xf82\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x8a\xe9\uf30a\x8a\u07e6\xaby\x8a\xb2\xcd\xc4\x05\b*\x1b\xbbp\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\xf6&\xa5\xf3'\xd7Pe\x89\xee\xb7\x01\x0f\xf9\xc9D` \u0489K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\x8b\x01\xda4\xd4p\xc1\xd1\x15\xac\xf4\xd8\x11\xe1\x01\xdb\x1e\x14\xec\xc7\xd3\"\xc7+\x8c\x04s\x89\x18\xb2j1>\x8a\xe9\x00\x00\xe0\x94\x8bH\xe1\x9d9\xdd5\xb6nn\x1b\xb6\xb9\xc6W\xcb,\xf5\x9d\x04\x8a\x03\xc7U\xac\x9c\x02J\x01\x80\x00\xe0\x94\x8bP^(q\xf7\u07b7\xa68\x95 \x8e\x82'\u072a\x1b\xff\x05\x8a\f\xf6\x8e\xfc0\x8dy\xbc\x00\x00\u07d4\x8bW\xb2\xbc\x83\u030dM\xe31 N\x89?/;\x1d\xb1\a\x9a\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8b\\\x91K\x12\x8b\xf1i\\\b\x89#\xfaF~y\x11\xf3Q\xfa\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\xe0\x94\x8b_)\xcc/\xaa&,\xde\xf3\x0e\xf5T\xf5\x0e\xb4\x88\x14n\xac\x8a\x01;hp\\\x97 \x81\x00\x00\u07d4\x8bpV\xf6\xab\xf3\xb1\x18\xd0&\xe9D\xd5\xc0sC<\xa4Q\u05c965\xc6 G9\u0640\x00\u07d4\x8bqE\"\xfa(9b\x04p\xed\xcf\fD\x01\xb7\x13f=\xf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x8bt\xa7\xcb\x1b\xb8\u014f\xce&tf\xa3\x03X\xad\xafR\u007fa\x8a\x02\xe2WxN%\xb4P\x00\x00\u07d4\x8b~\x9fo\x05\xf7\xe3dv\xa1n>q\x00\xc9\x03\x1c\xf4\x04\xaf\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8b\x81\x15ni\x869\x94<\x01\xa7Rr\xad=5\x85\x1a\xb2\x82\x89\x12\xb3\x16_e\xd3\xe5\x00\x00\u07d4\x8b\x95w\x92\x00S\xb1\xa0\x01\x890M\x88\x80\x10\xd9\xef,\xb4\xbf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8b\x98A\x86.w\xfb\xbe\x91\x94p\x93U\x83\xa9<\xf0'\xe4P\x89llS4B\u007f\x1f\x00\x00\u07d4\x8b\x99}\xbc\a\x8a\xd0)a5]\xa0\xa1Y\xf2\x92~\xd4=d\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x8b\x9f\xda}\x98\x1f\xe9\xd6B\x87\xf8\\\x94\xd8?\x90t\x84\x9f\u030a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x8b\xb0!/2\x95\xe0)\u02b1\xd9a\xb0A3\xa1\x80\x9e{\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8b\xbe\xac\xfc)\xcf\xe94\x02\xdb\xd6j\x1a\xcbvv\x85c7\xb9;\xf0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8b\xf3s\xd0v\x81L\xbcW\xe1\xc6\xd1j\x82\u017e\x13\xc7=7\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x8c\x10#\xfd\xe1WM\xb8\xbbT\xf1s\x96p\x15|\xa4}\xa6R\x8a\x01y\u03da\u00e1\xb1w\x00\x00\u07d4\x8c\x1f\xbe_\n\xea5\x9cZ\xa1\xfa\b\u0209T\x12\u028e\x05\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\"B`U\xb7o\x11\xf0\xa2\xde\x1a\u007f\x81\x9aa\x96\x85\xfe`\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4\x8c+}\x8b`\x8d(\xb7\u007f\\\xaa\x9c\xd6E$*\x82>L\u0649b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\x8c/\xbe\ue3ac\xc5\xc5\xd7|\x16\xab\xd4b\ue701E\xf3K\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x8c:\x9e\xe7\x1fr\x9f#l\xba8g\xb4\u05dd\x8c\xee\xe2]\xbc\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8cP\xaa*\x92\x12\xbc\xdeVA\x8a\xe2a\xf0\xb3^z\x9d\xbb\x82\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8cT\xc7\xf8\xb9\x89nu\xd7\xd5\xf5\xc7`%\x86\x99\x95qB\xad\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8c]\x16\xede\xe3\xed~\x8b\x96\u0297+\xc8as\xe3P\v\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8cj\xa8\x82\xee2,\xa8HW\x8c\x06\xcb\x0f\xa9\x11\xd3`\x83\x05\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\x8cj\xe7\xa0Z\x1d\xe5u\x82\xae'h Bv\xc0\xffG\xed\x03\x8a,\v\xb3\xdd0\xc4\xe2\x00\x00\x00\u07d4\x8co\x9fN[z\xe2v\xbfXI{\u05ff*}%$_d\x89\x93\xfe\\W\xd7\x10h\x00\x00\u07d4\x8cu\x95n\x8f\xedP\xf5\xa7\xdd|\xfd'\xda \x0fgF\xae\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c|\xb4\xe4\x8b%\x03\x1a\xa1\xc4\xf9)%\xd61\xa8\xc3\xed\xc7a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\u007f\xa5\xca\xe8/\xed\xb6\x9a\xb1\x89\xd3\xff'\xae \x92\x93\xfb\x93\x89\x15\xaf\x88\r\x8c\u06c3\x00\x00\xe0\x94\x8c\x81A\x0e\xa85L\xc5\xc6\\A\xbe\x8b\xd5\xdes<\v\x11\x1d\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\x8c\x83\xd4$\xa3\xcf$\xd5\x1f\x01\x92=\xd5J\x18\u05b6\xfe\xde{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8c\x90\n\x826\xb0\x8c+e@]9\xd7_ \x06*ua\xfd\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\x8c\x93\xc3\xc6\u06dd7q}\xe1e\u00e1\xb4\xfeQ\x95,\b\u0789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8c\x99\x95\x91\xfdr\xefq\x11\xef\xcaz\x9e\x97\xa25k;\x00\n\x89\xddd\xe2\xaa\ngP\x00\x00\u07d4\x8c\xa6\x98\x97F\xb0n2\xe2Hta\xb1\u0399j':\xcf\u05c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8c\xb3\xaa?\xcd!(T\xd7W\x8f\xcc0\xfd\xed\xe6t*1*\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x8c\xc0\xd7\xc0\x16\xfaz\xa9P\x11J\xa1\xdb\tH\x82\xed\xa2t\xea\x89\b\xa9\xab\xa5W\xe3l\x00\x00\u07d4\x8c\xc6R\xdd\x13\xe7\xfe\x14\u06bb\xb3m]2\r\xb9\xff\xee\x8aT\x89a\t=|,m8\x00\x00\u07d4\x8c\u02bf%\a\u007f:\xa4\x15E4MS\xbe\x1b+\x9c3\x90\x00\x89[\xe8f\xc5b\xc5D\x00\x00\u07d4\x8c\xcf:\xa2\x1a\xb7BWj\xd8\xc4\"\xf7\x1b\xb1\x88Y\x1d\ua28965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\xd0\xcd\"\xe6 \xed\xa7\x9c\x04a\xe8\x96\xc9\xd1b)\x12K_z\xfb\xec\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x8c\xe2/\x9f\xa3rD\x9aB\x06\x10\xb4z\xe0\xc8\xd5eH\x122\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x8c\u451d\x8a\x16T-B<\x17\x98Ng9\xfar\u03b1w\x8a\x05K@Y&\xf4\xa6=\x80\x00\u07d4\x8c\xe5\xe3\xb5\xf5\x91\xd5\uc8ca\xbf\"\x8f.<5\x13K\xda\xc0\x89}\xc3[\x84\x89|8\x00\x00\xe0\x94\x8c\xee8\xd6YW\x88\xa5n?\xb9F4\xb3\xff\xe1\xfb\xdb&\u058a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x8c\xee\xa1^\xec;\xda\xd8\x02?\x98\xec\xf2[+\x8f\xef'\xdb)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8c\xf3To\xd1\u0363=X\x84_\xc8\xfc\xfe\u02bc\xa7\xc5d*\x89\x1f\x1e9\x93,\xb3'\x80\x00\u07d4\x8c\xf6\xda\x02\x04\xdb\u0106\vF\xad\x97?\xc1\x11\x00\x8d\x9e\fF\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x8c\xfe\xde\xf1\x98\xdb\n\x91C\xf0\x91)\xb3\xfdd\u073b\x9bIV\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\x04\xa5\xeb\xfb]\xb4\t\xdb\x06\x17\xc9\xfaV1\xc1\x92\x86\x1fJ\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x8d\x06\xe4d$\\\xadaI9\xe0\xaf\bE\xe6\xd70\xe2\x03t\x89\n\u070a(\xf3\xd8}\x80\x00\u07d4\x8d\a\xd4-\x83\x1c-|\x83\x8a\xa1\x87+:\xd5\xd2w\x17h#\x89\x12\xee\x1f\x9d\xdb\xeeh\x00\x00\u07d4\x8d\v\x9e\xa5?\xd2cA^\xac\x119\x1f|\xe9\x12V\xb9\xfb\x06`\xf6\xf0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8dy\\_JV\x89\xadb\u0696\x16q\xf0(\x06R\x86\xd5T\x89o\x05\xb5\x9d; \x00\x00\x00\u07d4\x8d\u007f>a)\x9c-\xb9\xb9\xc0H|\xf6'Q\x9e\xd0\n\x91#\x89^t\xa8P^\x80\xa0\x00\x00\xe0\x94\x8d\x89\x17\v\x92\xb2\xbe,\b\xd5|H\xa7\xb1\x90\xa2\xf1Fr\x0f\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x8d\x93\xda\u01c5\xf8\x8f\x1a\x84\xbf\x92}Se+E\xa1T\xcc\u0749\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x8d\x99R\u043bN\xbf\xa0\xef\xd0\x1a:\xa9\xe8\xe8\u007f\x05%t.\x89\xbb\x91%T\"c\x90\x00\x00\u07d4\x8d\x9a\fp\xd2& B\xdf\x10\x17\xd6\xc3\x03\x13 $w'\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\x9e\xd7\xf4U0X\xc2ox6\xa3\x80-0d\xeb\x1b6=\x89\x04\xe1\x00;(\xd9(\x00\x00\u07d4\x8d\xa1\x17\x8fU\xd9wr\xbb\x1d$\x11\x1a@JO\x87\x15\xb9]\x89/\x9a\xc3\xf6\xde\x00\x80\x80\x00\u07d4\x8d\xa1\xd3Y\xbal\xb4\xbc\xc5}zCw \xd5]\xb2\xf0\x1cr\x89\x04V9\x18$O@\x00\x00\u07d4\x8d\xab\x94\x8a\xe8\x1d\xa3\x01\xd9r\xe3\xf6\x17\xa9\x12\xe5\xa7Sq.\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8d\xad\xdfR\xef\xbdt\u0695\xb9i\xa5GoO\xbb\xb5c\xbf\u0489-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4\x8d\xb1\x85\xfe\x1bp\xa9Jj\b\x0e~#\xa8\xbe\xdcJ\xcb\xf3K\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x8d\xb5\x8e@n -\xf9\xbcpl\xb43\xe1\x94\xf4\x0f\x82\xb4\x0f\xaa\xdb\x1f\x8b\x85a\x16\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x8d\xc1\xd5\x11\x1d\t\xaf%\xfd\xfc\xacE\\|\xec(>mgu\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\u0504\xff\x8a0sd\xebf\xc5%\xa5q\xaa\xc7\x01\xc5\xc3\x18\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\u05a9\xba\xe5\u007fQ\x85I\xad\xa6wFo\ua2b0O\u0674\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\xde<\xb8\x11\x85h\xefE\x03\xfe\x99\x8c\xcd\xf56\xbf\x19\xa0\x98\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\xde`\xeb\b\xa0\x99\xd7\u06a3V\u06aa\xb2G\r{\x02Zk\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x8d\xf39!Kj\u0472Fc\xceq`4t\x9dn\xf88\u064a\x02TO\xaaw\x80\x90\xe0\x00\x00\xe0\x94\x8d\xf5=\x96\x19\x14q\xe0Y\xdeQ\xc7\x18\xb9\x83\xe4\xa5\x1d*\xfd\x8a\x06\u01b95\xb8\xbb\xd4\x00\x00\x00\u07d4\x8d\xfb\xaf\xbc\x0e[\\\x86\xcd\x1a\u0597\xfe\xea\x04\xf41\x88\u0796\x89\x15%+\u007f_\xa0\xde\x00\x00\u07d4\x8e\a;\xad%\xe4\"\x18a_J\x0ek.\xa8\xf8\xde\"0\xc0\x89\x82=b\x9d\x02k\xfa\x00\x00\u07d4\x8e\x0f\xee8hZ\x94\xaa\xbc\xd7\u0385{k\x14\t\x82Ou\xb8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e#\xfa\xcd\x12\xc7e\xc3j\xb8\x1am\xd3M\x8a\xa9\xe6\x89\x18\xae\x89\t\x11\u418d\xba\x9b\x00\x00\xe0\x94\x8e/\x904\xc9%G\x19\u00ceP\u026ad0^\u0596\xdf\x1e\x8a\x01\x00N.E\xfb~\xe0\x00\x00\u07d4\x8e2@\xb0\x81\x0e\x1c\xf4\a\xa5\x00\x80G@\u03cdad2\xa4\x89\x02/fU\xef\v8\x80\x00\u07d4\x8eHj\x04B\xd1q\xc8`[\xe3H\xfe\xe5~\xb5\b^\xff\r\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8eaV3k\xe2\u037e2\x14\r\xf0\x8a+\xa5_\u0425\x84c\x89\x04\t\x9e\x1dcW\x18\x00\x00\u07d4\x8eg\b\x15\xfbg\xae\xae\xa5{\x86SN\xdc\x00\xcd\xf5d\xfe\u5272\xe4\xb3#\xd9\xc5\x10\x00\x00\u07d4\x8emt\x85\xcb\u942c\xc1\xad\x0e\xe9\xe8\xcc\xf3\x9c\f\x93D\x0e\x893\xc5I\x901r\f\x00\x00\xe0\x94\x8et\xe0\u0477~\xbc\x82:\xca\x03\xf1\x19\x85L\xb1 '\xf6\u05ca\x16\xb3R\xda^\x0e\xd3\x00\x00\x00\u07d4\x8ex\xf3QE}\x01oJ\xd2u^\xc7BN\\!\xbamQ\x89\a\xea(2uw\b\x00\x00\u07d4\x8ey6\u0552\x00\x8f\xdcz\xa0N\xde\xebuZ\xb5\x13\u06f8\x9d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8e\u007f\xd28H\xf4\xdb\a\x90j}\x10\xc0K!\x80;\xb0\x82'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8e\x92\xab\xa3\x8er\xa0\x98\x17\v\x92\x95\x92FSz.UV\xc0\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x8e\x98ve$\xb0\xcf'G\xc5\r\xd4;\x95gYM\x971\u0789lD\xb7\xc2a\x82(\x00\x00\u07d4\x8e\x9b5\xadJ\n\x86\xf7XDo\xff\xde4&\x9d\x94\f\xea\u0349\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8e\x9c\b\xf78f\x1f\x96v#n\xff\x82\xbaba\xdd?H\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8e\x9cB\x92f\xdf\x05~\xfax\xdd\x1d_w\xfc@t*\xd4f\x89\x10D.\u0475l|\x80\x00\u07d4\x8e\xa6V\xe7\x1e\xc6Q\xbf\xa1|ZWY\xd8`1\xcc5\x99w\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8e\xae)CU\x98\xba\x8f\x1c\x93B\x8c\xdb>+M1\a\x8e\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\xb1\xfb\xe4\xe5\xd3\x01\x9c\xd7\xd3\r\xae\x9c\r[Lv\xfbc1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\xb5\x17t\xaf k\x96k\x89\t\xc4Z\xa6r'H\x80,\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e\xb8\xc7\x19\x82\xa0\x0f\xb8Bu)2S\xf8\x04ED\xb6kI\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\x8e\xcb\u03ec\xbf\xaf\xe9\xf0\f9\"\xa2N,\xf0\x02gV\xca \x8a\x011\xbe\xb9%\xff\xd3 \x00\x00\u07d4\x8e\u03b2\xe1$Sl[_\xfcd\x0e\xd1O\xf1^\u0668\xcbq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\u042f\x11\xff(p\xda\x06\x81\x00J\xfe\x18\xb0\x13\xf7\xbd8\x82\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\x8e\xd1Cp\x1f/r(\x0f\xd0J{Ad(\x19y\xea\x87\u0248\xc2I\xfd\xd3'x\x00\x00\u07d4\x8e\xd1R\x8bD~\xd4)y\x02\xf69\xc5\x14\u0414J\x88\xf8\u0209\n\xc6\xe7z\xb6c\xa8\x00\x00\u07d4\x8e\xd4(L\x0fGD\x9c\x15\xb8\u0673$]\u8fb6\u0380\xbf\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\x8e\xde~=\xc5\aI\xc6\xc5\x0e.(\x16\x84x\xc3M\xb8\x19F\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x8e\xe5\x843}\xdb\xc8\x0f\x9e4\x98\xdfU\xf0\xa2\x1e\xac\xb5\u007f\xb1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8e\xeb\xec\x1ab\xc0\x8b\x05\xa7\xd1\u0551\x80\xaf\x9f\xf0\u044e?6\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e\xf4\u0622\xc2o\xf7\x14\xb6u\x89\x19\x80\x1c\x83\xb6\xc7\xc0\x00\x00\u07d4\x8fM\x1dAi>F,\xf9\x82\xfd\x81\u042ap\x1d:St\u0249\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8fM\x1e~Ea(J4\xfe\xf9g<\r4\xe1*\xf4\xaa\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8fO\xb1\xae\xa7\xcd\x0fW\x0e\xa5\xe6\x1b@\xa4\xf4Q\vbd\xe4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8fV\x1bA\xb2\t\xf2H\u0229\x9f\x85\x87\x887bP`\x9c\xf3\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94\x8fX\xd84\x8f\xc1\xdcN\r\xd84;eC\xc8W\x04^\xe9@\x8a\x02\xe3\x03\x8d\xf4s\x03(\x00\x00\u07d4\x8f`\x89_\xbe\xbb\xb5\x01\u007f\xcb\xff<\u0763\x97)+\xf2[\xa6\x89\x17D\x06\xff\x9fo\u0480\x00\u07d4\x8fd\xb9\xc1$m\x85x1d1\a\xd3U\xb5\xc7_\xef]O\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x8ff\x0f\x8b.L|\u00b4\xac\x9cG\xed(P\x8d_\x8f\x86P\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x8fi\xea\xfd\x023\xca\xdb@Y\xabw\x9cF\xed\xf2\xa0PnH\x89`\xf0f \xa8IE\x00\x00\xe0\x94\x8fq~\xc1U/LD\x00\x84\xfb\xa1\x15J\x81\xdc\x00>\xbd\xc0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x8f\x8a\xcb\x10v\a8\x84y\xf6K\xaa\xab\xea\x8f\xf0\a\xad\xa9}\x8a\x05\xc6\xf3\b\n\xd4#\xf4\x00\x00\u07d4\x8f\x8c\xd2n\x82\xe7\xc6\xde\xfd\x02\u07ed\a\x97\x90!\xcb\xf7\x15\f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x8f\x8f7\u042d\x8f3]*q\x01\xb4\x11V\xb6\x88\xa8\x1a\x9c\xbe\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\x8f\x92\x84O(*\x92\x99\x9e\u5d28\xd7s\xd0kiM\xbd\x9f\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x8f\xact\x8fxJ\x0f\xedh\u06e43\x19\xb4*u\xb4d\x9cn\x891T\xc9r\x9d\x05x\x00\x00\u07d4\x8f\u0665\xc3:}\x9e\xdc\xe0\x99{\xdfw\xab0d$\xa1\x1e\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8f\xef\xfa\xdb8z\x15G\xfb(M\xa9\xb8\x14\u007f>|m\xc6\u0689-b{\xe4S\x05\b\x00\x00\u07d4\x8f\xf4`Ehw#\xdc3\xe4\u0419\xa0i\x04\xf1\ubd44\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\x8f\xfa\x06!\"\xac0t\x18\x82\x1a\u06d3\x11\aZ7\x03\xbf\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8f\xfe2)\x97\xb8\xe4\x04B-\x19\xc5J\xad\xb1\x8f[\xc8\u9dc9\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x90\x01\x94\u0131\aC\x05\u045d\xe4\x05\xb0\xacx(\x0e\xca\xf9g\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x90\x03\xd2p\x89\x1b\xa2\xdfd=\xa84\x15\x83\x195E\xe3\xe0\x00\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x90\x05z\xf9\xaaf0~\xc9\xf03\xb2\x97$\u04f2\xf4\x1e\xb6\xf9\x8a\x19\xd1\u05aa\xdb,R\xe8\x00\x00\u07d4\x90\x0f\v\x8e5\xb6h\xf8\x1e\xf2R\xb18U\xaaP\a\xd0\x12\xe7\x89\x17\n\x0fP@\xe5\x04\x00\x00\u07d4\x90\x18\xcc\x1fH\xd20\x8e%*\xb6\b\x9f\xb9\x9a|\x1dV\x94\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x90\x1d\x99\xb6\x99\xe5\u0191\x15\x19\xcb v\xb4\xc7c0\xc5M\"\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x90-t\xa1W\xf7\u04b9\xa37\x8b\x1fVp70\xe0:\x17\x19\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x904\x13\x87\x8a\xea;\xc1\bc\t\xa3\xfev\x8beU\x9e\x8c\xab\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x90If\xcc\"\x13\xb5\xb8\xcb[\xd6\b\x9e\xf9\xcd\xdb\xef~\xdf\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\x90L\xaaB\x9ca\x9d\x94\x0f\x8egA\x82j\r\xb6\x92\xb1\x97(\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x90R\xf2\xe4\xa3\xe3\xc1-\xd1\xc7\x1b\xf7\x8aN\xc3\x04=\u020b~\x89\x0e~\xeb\xa3A\vt\x00\x00\u0794\x90U&V\x8a\xc1#\xaf\xc0\xe8J\xa7\x15\x12O\xeb\xe8=\xc8|\x88\xf8i\x93)g~\x00\x00\u07d4\x90\x92\x91\x87\a\xc6!\xfd\xbd\x1d\x90\xfb\x80\xebx\u007f\xd2osP\x89\x85[[\xa6\\\x84\xf0\x00\x00\u07d4\x90\x9b^v:9\xdc\u01d5\"=s\xa1\u06f7\xd9L\xa7Z\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\x90\xac\xce\xd7\xe4\x8c\b\u01b94dm\xfa\n\xdf)\u0714\aO\x89\x03\vK\x15{\xbdI\x00\x00\u07d4\x90\xb1\xf3p\xf9\xc1\xeb\v\xe0\xfb\x8e+\x8a\xd9jAcq\u074a\x890\xca\x02O\x98{\x90\x00\x00\u07d4\x90\xb6/\x13\x1a_)\xb4UqQ>\xe7\xa7J\x8f\v#\"\x02\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x90\xbdb\xa0P\x84Ra\xfaJ\x9f|\xf2A\xeac\v\x05\ufe09\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x90\xc4\x1e\xba\x00\x8e \xcb\xe9'\xf3F`?\u0206\x98\x12Yi\x89\x02F\xdd\xf9yvh\x00\x00\u07d4\x90\u0480\x9a\xe1\xd1\xff\xd8\xf6>\xda\x01\xdeI\xddU-\xf3\u047c\x89\u063beI\xb0+\xb8\x00\x00\u07d4\x90\xdc\t\xf7\x17\xfc*[i\xfd`\xba\b\xeb\xf4\v\xf4\xe8$l\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x90\xe3\x00\xacqE\x1e@\x1f\x88\u007fnw(\x85\x16G\xa8\x0e\a\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x90\xe3Z\xab\xb2\xde\xef@\x8b\xb9\xb5\xac\xefqDW\xdf\xdebr\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\x90\xe7\a\x0fM\x03?\xe6\x91\f\x9e\xfeZ'\x8e\x1f\xc6#M\xef\x89\x05q8\b\x19\xb3\x04\x00\x00\u07d4\x90\xe9>M\xc1q!HyR36\x14\x00+\xe4#VI\x8e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x90\u9a68.\u06a8\x14\u0084\xd22\xb6\u9e90p\x1dIR\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\x90\xf7t\xc9\x14}\u0790\x85=\xdcC\xf0\x8f\x16\xd4U\x17\x8b\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x90\xfcS{!\x06Xf\n\x83\xba\xa9\xacJ\x84\x02\xf6WF\xa8\x89e\xea=\xb7UF`\x00\x00\u07d4\x91\x05\n\\\xff\xad\xed\xb4\xbbn\xaa\xfb\xc9\xe5\x014(\xe9l\x80\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x91\x05\x17d\xafk\x80\x8eB\x12\xc7~0\xa5W.\xaa1pp\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x91\v}Wz~9\xaa#\xac\xf6*\xd7\xf1\xef4)4\xb9h\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x91\x0e\x99eC4Lh\x15\xfb\x97\u0367\xafK\x86\x98vZ[\x89\x05\x9a\xf6\x98)\xcfd\x00\x00\u07d4\x91\x1f\xee\xa6\x1f\xe0\xedP\u0179\xe5\xa0\xd6`q9\x9d(\xbd\u0189\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x91\x1f\xf23\xe1\xa2\x11\xc0\x17,\x92\xb4l\xf9\x97\x03\x05\x82\xc8:\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x91 \xe7\x11s\xe1\xba\x19\xba\x8f\x9fO\xdb\u072a4\xe1\u05bbx\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91!\x17\x12q\x9f+\bM;8u\xa8Pi\xf4f61A\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x91#\x04\x11\x8b\x80G=\x9e\x9f\xe3\xeeE\x8f\xbea\x0f\xfd\xa2\xbb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x91Tky\xec\xf6\x9f\x93kZV\x15\b\xb0\xd7\xe5\f\u0159/\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x91V\u0440)5\x0eG\x04\b\xf1_\x1a\xa3\xbe\x9f\x04\ng\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x91b\x0f>\xb3\x04\xe8\x13\u048b\x02\x97Ume\xdcN]\u5a89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\x91k\xf7\xe3\xc5E\x92\x1d2\x06\xd9\x00\xc2O\x14\x12|\xbd^p\x8a\x03\xd0\u077c}\xf2\xbb\x10\x00\x00\u0794\x91l\xf1}qA(\x05\xf4\xaf\xc3DJ\v\x8d\xd1\xd93\x9d\x16\x88\xc6s\xce<@\x16\x00\x00\u07d4\x91{\x8f\x9f:\x8d\t\xe9 ,R\u009erA\x96\xb8\x97\xd3^\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\x91\x89g\x91\x8c\u0617\xdd\x00\x05\xe3m\xc6\u0203\xefC\x8f\xc8\u01c9\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x91\x89\x8e\xab\x8c\x05\xc0\"(\x83\xcdM\xb2;w\x95\xe1\xa2J\u05c9lk\x93[\x8b\xbd@\x00\x00\u0794\x91\x91\xf9F\x98!\x05\x16\xcfc!\xa1B\a\x0e Yvt\xed\x88\xee\x9d[\xe6\xfc\x11\x00\x00\u07d4\x91\xa4\x14\x9a,{\x1b:g\xea(\xaf\xf3G%\u0fcdu$\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x91\xa7\x87\xbcQ\x96\xf3HW\xfe\f7/M\xf3v\xaa\xa7f\x13\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91\xa8\xba\xae\xd0\x12\xea.c\x80;Y=\r\f*\xabL[\n\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x91\xac\\\xfeg\xc5J\xa7\xeb\xfb\xa4HflF\x1a;\x1f\xe2\xe1\x89\x15\xc94\x92\xbf\x9d\xfc\x00\x00\u07d4\x91\xbb?y\x02+\xf3\xc4S\xf4\xff%n&\x9b\x15\xcf,\x9c\xbd\x89RX\\\x13\xfe:\\\x00\x00\u07d4\x91\xc7^<\xb4\xaa\x89\xf3F\x19\xa1d\xe2\xa4x\x98\xf5gM\x9c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91\xc8\f\xaa\b\x1b85\x1d*\x0e\x0e\x00\xf8\n4\xe5dt\xc1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x91\xccF\xaa7\x9f\x85jf@\xdc\xcdZd\x8ay\x02\xf8I\u0649\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x91\u04a9\xee\x1am\xb2\x0fS\x17\u0327\xfb\xe218\x95\u06ce\xf8\x8a\x01\xcc\u00e5/0n(\x00\x00\u07d4\x91\xd6n\xa6(\x8f\xaaK=`l*\xa4\\{k\x8a%'9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x91\u06f6\xaa\xad\x14\x95\x85\xbeG7\\]m\xe5\xff\t\x19\x15\x18\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x91\xe8\x81\x06R\xe8\xe6\x16\x15%\xd6;\xb7u\x1d\xc2\x0fg`v\x89'Mej\xc9\x0e4\x00\x00\u07d4\x91\xf5\x16\x14l\xda (\x17\x19\x97\x80`\u01beAI\x06|\x88\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x91\xf6$\xb2J\x1f\xa5\xa0V\xfeW\x12)\xe77\x9d\xb1K\x9a\x1e\x8a\x02\x8a\x85\x17\xc6i\xb3W\x00\x00\xe0\x94\x91\xfe\x8aLad\u07cf\xa6\x06\x99]k\xa7\xad\xca\xf1\u0213\u038a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4\x92\x1fRa\xf4\xf6\x12v\a\x06\x89&%\xc7^{\u0396\xb7\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92!\xc9\xce\x01#&et\x10\x96\xac\a#Y\x03\xad\x1f\xe2\xfc\x89\x06\xdbc3U\"b\x80\x00\u07d4\x92%\x988`\xa1\xcbF#\xc7$\x80\xac\x16'+\f\x95\xe5\xf5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x92%\xd4jZ\x80\x949$\xa3\x9e[\x84\xb9m\xa0\xacE\x05\x81\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x92* \u01da\x1d:&\xdd8)g{\xf1\xd4\\\x8fg+\xb6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x92C\x8eR\x03\xb64o\xf8\x86\xd7\xc3b\x88\xaa\xcc\xccx\xce\u028965\u026d\xc5\u07a0\x00\x00\u07d4\x92C\xd7v-w({\x12c\x86\x88\xb9\x85N\x88\xa7i\xb2q\x8965\u026d\xc5\u07a0\x00\x00\u0794\x92K\xcez\x85<\x97\v\xb5\xec{\xb7Y\xba\xeb\x9ct\x10\x85{\x88\xbe -j\x0e\xda\x00\x00\u07d4\x92N\xfam\xb5\x95\xb7\x93\x13'~\x881\x96%\akX\n\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92U\x82&\xb3\x84bl\xadH\xe0\x9d\x96k\xf19^\xe7\xea]\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4\x92`\x82\xcb~\xedK\x19\x93\xad$ZGrg\xe1\xc3<\xd5h\x89\x14Jt\xba\u07e4\xb6\x00\x00\u07d4\x92b\t\xb7\xfd\xa5N\x8d\u06dd\x9eM=\x19\xeb\u070e\x88\u009f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92h\xd6&FV6\x11\xdc;\x83*0\xaa#\x94\xc6F\x13\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92i\x8e4Sx\xc6-\x8e\xda\x18M\x946j\x14K\f\x10[\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x92y:\u0173rhwJq0\xde+\xbd3\x04\x05f\x17s\x89\x02,\xa3X|\xf4\xeb\x00\x00\xe0\x94\x92y\xb2\"\x8c\xec\x8f{M\xda?2\x0e\x9a\x04f\xc2\xf5\x85\u028a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x92|\xb7\xdc\x18p6\xb5B{\xc7\xe2\x00\xc5\xecE\f\x1d'\u0509\v\xb5\x9a'\x95<`\x00\x00\u07d4\x92|\u00bf\xda\x0e\b\x8d\x02\xef\xf7\v8\xb0\x8a\xa5<\xc3\tA\x89do`\xa1\xf9\x866\x00\x00\xe0\x94\x92\x84\xf9m\xdbG\xb5\x18n\xe5X\xaa12M\xf56\x1c\x0fs\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x92\x9d6\x8e\xb4j-\x1f\xbd\xc8\xff\xa0`~\xdeK\xa8\x8fY\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xa7\u0166Cb\xe9\xf8B\xa2=\xec\xa2\x105\x85\u007f\x88\x98\x00\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x92\xa8\x98\xd4o\x19q\x9c8\x12j\x8a<'\x86z\xe2\xce\u5589lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xa9q\xa79y\x9f\x8c\xb4\x8e\xa8G]r\xb2\xd2GAr\xe6\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x92\xaa\xe5\x97h\xed\xdf\xf8<\xfe`\xbbQ.s\n\x05\xa1a\u05c9\\\x97xA\fv\u0440\x00\u07d4\x92\xad\x1b=u\xfb\xa6}Tf=\xa9\xfc\x84\x8a\x8a\xde\x10\xfag\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xae[|~\xb4\x92\xff\x1f\xfa\x16\xddB\xad\x9c\xad@\xb7\xf8\u0709.\xe4IU\b\x98\xe4\x00\x00\u07d4\x92\xc0\xf5s\xec\xcfb\xc5H\x10\xeek\xa8\xd1\xf1\x13T+0\x1b\x89\xb7ro\x16\u0331\xe0\x00\x00\u07d4\x92\xc1?\xe0\xd6\u0387\xfdP\xe0=\uf7e6@\x05\t\xbdps\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x92\xc9L( \xdf\xcfqV\xe6\xf10\x88\xec\u754b6v\xfd\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4\x92\xcf\xd6\x01\x88\xef\u07f2\xf8\xc2\xe7\xb1i\x8a\xbb\x95&\xc1Q\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\u062d\x9aMah;\x80\u0526g.\x84\xc2\rbB\x1e\x80\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x92\u0725\xe1\x02\xb3\xb8\x1b`\xf1\xa5\x04cIG\xc3t\xa8\x8c\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xe454\x0e\x9d%<\x00%c\x89\xf5+\x06}U\x97Nv\x89\x0e\x87?D\x13<\xb0\x00\x00\xe0\x94\x92\xe49(\x16\xe5\xf2\xef_\xb6X7\xce\xc2\xc22\\\xc6I\"\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x92\xe6X\x1e\x1d\xa1\xf9\xb8F\xe0\x93G3=\xc8\x18\xe2\u04acf\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\x93\x1d\xf3M\x12%\xbc\xd4\"Nch\r\\L\t\xbc\xe75\xa6\x89\x03\xaf\xb0\x87\xb8v\x90\x00\x00\u07d4\x93\x1f\xe7\x12\xf6B\a\xa2\xfdP\"r\x88CT\x8b\xfb\x8c\xbb\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x93#_4\r(c\xe1\x8d/LR\x99e\x16\x13\x8d\"\x02g\x89\x04\x00.D\xfd\xa7\xd4\x00\x00\u07d4\x93%\x82U\xb3|\u007fX\xf4\xb1\x06s\xa92\xdd:\xfd\x90\xf4\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x93(\xd5\\\xcb?\xceS\x1f\x19\x93\x823\x9f\x0eWn\xe8@\xa3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x93)\xff\xdc&\x8b\xab\u0788t\xb3f@l\x81D[\x9b-5\x89\x16\xe6/\x8cs\f\xa1\x80\x00\u07d4\x93+\x9c\x04\xd4\r*\xc80\x83\xd9B\x98\x16\x9d\xae\x81\xab.\u0409lk\x93[\x8b\xbd@\x00\x00\u07d4\x9346\xc8G&U\xf6L:\xfa\xaf|Lb\x1c\x83\xa6+8\x8965\u026d\xc5\u07a0\x00\x00\u0794\x93;\xf3?\x82\x99p+:\x90&B\xc3>\v\xfa\xea\\\x1c\xa3\x88\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x93@4\\\xa6\xa3\uaf77sc\xf2X`C\xf2\x948\xce\v\x89\x1c\xc8\x05\xda\r\xff\xf1\x00\x00\xe0\x94\x93@\xb5\xf6x\xe4^\xe0^\xb7\b\xbbz\xbbn\xc8\xf0\x8f\x1bk\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x93J\xf2\x1b~\xbf\xa4g\xe2\xce\xd6Z\xa3N\xdd:\x0e\xc7\x132\x8a\a\x80\x1f>\x80\xcc\x0f\xf0\x00\x00\xe0\x94\x93PiDJj\x98M\xe2\bNFi*\xb9\x9fg\x1f\xc7'\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\xe0\x94\x93P~\x9e\x81\x19\xcb\xce\u068a\xb0\x87\xe7\xec\xb0q8=i\x81\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x93g\x8a\x00\x00\xe0\x94\x93m\xcf\x00\x01\x94\xe3\xbf\xf5\n\u0174$:;\xa0\x14\xd6a\u060a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x93o8\x13\xf5\xf6\xa1;\x8eO\xfe\xc8?\xe7\xf8&\x18jq\u0349\x1c0s\x1c\xec\x03 \x00\x00\u07d4\x93t\x86\x9dJ\x99\x11\xee\x1e\xafU\x8b\xc4\u00b6>\xc6:\xcf\u074965\u026d\xc5\u07a0\x00\x00\u07d4\x93uc\u0628\x0f\u05657\xb0\xe6m \xa0%%\xd5\u0606`\x89\x87\x86x2n\xac\x90\x00\x00\u07d4\x93v\xdc\xe2\xaf.\xc8\xdc\xdat\x1b~sEfF\x81\xd96h\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x93\x86\x8d\xdb*yM\x02\xeb\xda/\xa4\x80|v\xe3`\x98X\u0709m\xee\x15\xfc|$\xa7\x80\x00\xe0\x94\x93\x9cC\x13\xd2(\x0e\xdf^\a\x1b\xce\xd8F\x06?\n\x97]T\x8a\x19i6\x89t\xc0[\x00\x00\x00\xe0\x94\x93\xa6\xb3\xabB0\x10\xf9\x81\xa7H\x9dJ\xad%\xe2b\\WA\x8a\x04F\x80\xfej\x1e\xdeN\x80\x00\u07d4\x93\xaa\x8f\x92\xeb\xff\xf9\x91\xfc\x05^\x90ne\x1a\xc7h\xd3+\u02092\xf5\x1e\u06ea\xa30\x00\x00\u07d4\x93\xb4\xbf?\xdf\xf6\xde?NV\xbamw\x99\xdcK\x93\xa6T\x8f\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4\x93\xbc}\x9aJ\xbdD\u023b\xb8\xfe\x8b\xa8\x04\xc6\x1a\xd8\xd6Wl\x89\xd8\xd6\x11\x9a\x81F\x05\x00\x00\u07d4\x93\xc2\xe6N]\xe5X\x9e\xd2P\x06\xe8C\x19n\xe9\xb1\xcf\v>\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\x93\u020e-\x88b\x1e0\xf5\x8a\x95\x86\xbe\xd4\t\x89\x99\xebg\u074a\x06\x9bZ\xfa\xc7P\xbb\x80\x00\x00\u07d4\x93\xe0\xf3~\xcd\xfb\x00\x86\xe3\xe8b\xa9p4D{\x1eM\xec\x1a\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\xe0\x94\x93\xe3\x03A\x1a\xfa\xf6\xc1\a\xa4A\x01\u026c[6\xe9\xd6S\x8b\x8a\r\xf9\xdd\xfe\xcd\x03e@\x00\x00\u07d4\x93\xf1\x8c\xd2R`@v\x14\x88\xc5\x13\x17M\x1eycv\x8b,\x89\x82\xff\xac\x9a\u0553r\x00\x00\u07d4\x94\x0fqQ@P\x9f\xfa\xbf\x97EF\xfa\xb3\x90\"\xa4\x19R\u0489K\xe4\xe7&{j\xe0\x00\x00\u07d4\x94,k\x8c\x95[\xc0\u0608\x12g\x8a#g%\xb3'9\xd9G\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\x94=7\x86JJS}5\xc8\u0657#\xcdd\x06\xce%b\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94C\x9c\xa9\xcc\x16\x9ay\u0520\x9c\xae^gvJo\x87\x1a!\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x94D\x9c\x01\xb3*\u007f\xa5Z\xf8\x10OB\xcd\xd8D\xaa\x8c\xbc@\x8a\x03\x81\x11\xa1\xf4\xf0<\x10\x00\x00\xe0\x94\x94E\xba\\0\xe9\x89a\xb8`$a\xd08]@\xfb\xd8\x03\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x94O\a\xb9o\x90\xc5\xf0\xd7\xc0\u0140S1I\xf3\xf5\x85\xa0x\x89\x04\x02\xf4\xcf\xeeb\xe8\x00\x00\u07d4\x94T\xb3\xa8\xbf\xf9p\x9f\xd0\u1407~l\xb6\u0219t\xdb\u0589\x90\xf54`\x8ar\x88\x00\x00\u07d4\x94]\x96\xeaW>\x8d\xf7&+\xbf\xa5r\"\x9bK\x16\x01k\x0f\x89\vX\x9e\xf9\x14\xc1B\x00\x00\u07d4\x94^\x18v\x9d~\xe7'\xc7\x01?\x92\xde$\xd1\x17\x96\u007f\xf3\x17\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94a'\x81\x03;W\xb1F\xeet\xe7S\xc6r\x01\u007fS\x85\xe4\x89\xc3(\t>a\xee@\x00\x00\xe0\x94\x94dJ\xd1\x16\xa4\x1c\xe2\xca\u007f\xbe\xc6\t\xbd\xefs\x8a*\xc7\u01ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x94p\xcc6YE\x86\x82\x18!\xc5\u0256\xb6\xed\xc8;mZ2\x89\x01M\x11 \u05f1`\x00\x00\xe0\x94\x94u\xc5\x10\xec\x9a&\x97\x92GtL=\x8c;\x0e\v_D\u04ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x94~\x11\xe5\xea)\ro\u00f3\x80H\x97\x9e\f\xd4N\xc7\xc1\u007f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94\x83\u064f\x14\xa3?\xdc\x11\x8d@9U\u00995\xed\xfc_p\x89\x18\xea;4\xefQ\x88\x00\x00\u07d4\x94\x911\xf2\x89C\x92\\\xfc\x97\xd4\x1e\f\xea\v&)s\xa70\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\x94\x9f\x84\xf0\xb1\xd7\u0127\xcfI\xee\u007f\x8b,J\x13M\xe3(x\x89%\"H\u07b6\xe6\x94\x00\x00\u07d4\x94\x9f\x8c\x10{\xc7\xf0\xac\xea\xa0\xf1pR\xaa\xdb\xd2\xf9s+.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94\xa7\u0368\xf4\x81\xf9\u061dB\xc3\x03\xae\x162\xb3\xb7\t\xdb\x1d\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x94\xa9\xa7\x16\x911| d'\x1bQ\xc95?\xbd\xed5\x01\xa8\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x94\xadK\xad\x82K\xd0\ub7a4\x9cX\u03bc\xc0\xff^\b4k\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x94\xbb\xc6}\x13\xf8\x9e\xbc\xa5\x94\xbe\x94\xbcQp\x92\f0\xd9\xf3\x89\x04X\xff\xa3\x15\nT\x00\x00\u07d4\x94\xbe:\xe5Ob\xd6c\xb0\xd4\u031e\x1e\xa8\xfe\x95V\ua7bf\x89\x01C\x13,\xa8C\x18\x00\x00\xe0\x94\x94\xc0U\xe8X5z\xaa0\xcf A\xfa\x90Y\xce\x16J\x1f\x91\x8a\x04<%\xe0\xdc\xc1\xbd\x1c\x00\x00\xe0\x94\x94\xc7B\xfdz\x8by\x06\xb3\xbf\xe4\xf8\x90O\xc0\xbe\\v\x803\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x94\xcaV\xdew\u007f\xd4S\x17\u007f^\x06\x94\xc4x\xe6j\xff\x8a\x84\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x94\xd8\x10t\xdbZ\xe1\x97\u04bb\x13s\xab\x80\xa8}\x12\x1cK\u04ca\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x94\u06c0xs\x86\n\xac=Z\xea\x1e\x88^R\xbf\xf2\x86\x99T\x89\xae\x8ez\v\xb5u\xd0\x00\x00\u07d4\x94\xe1\xf5\u02db\x8a\xba\xce\x03\xa1\xa6B\x82VU;i\f#U\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x94\xef\x8b\xe4Pw\xc7\xd4\xc5e'@\u0794jbbOq?\x89\x05l\xf5Y:\x18\xf8\x80\x00\u07d4\x94\xf1?\x9f\b6\xa3\xee$7\xa8I\"\u0498M\xc0\xf7\xd5;\x89\xa2\xa02\x9b\u00ca\xbe\x00\x00\u07d4\x94\xf8\xf0W\xdb~`\xe6u\xad\x94\x0f\x15X\x85\u0464w4\x8e\x89\x15\xbeat\xe1\x91.\x00\x00\xe0\x94\x94\xfc\u03ad\xfe\\\x10\x9c^\xae\xafF-C\x871B\u020e\"\x8a\x01\x045a\xa8\x82\x93\x00\x00\x00\u07d4\x95\x03N\x16!\x86Q7\xcdG9\xb3F\xdc\x17\xda:'\xc3N\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\x95\fh\xa4\t\x88\x15M#\x93\xff\xf8\xda|\u0369\x96\x14\xf7,\x89\xf9AF\xfd\x8d\xcd\xe5\x80\x00\xe0\x94\x95\x0f\xe9\xc6\xca\xd5\f\x18\xf1\x1a\x9e\xd9\xc4W@\xa6\x18\x06\x12\u040a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x95!\x83\xcf\u04ce5.W\x9d6\xde\xce\u0171\x84P\xf7\xfb\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95'\x8b\b\xde\xe7\xc0\xf2\xc8\xc0\xf7\"\xf9\xfc\xbb\xb9\xa5$\x1f\u0689\x82\x93\t\xf6O\r\xb0\x00\x00\u07d4\x95,W\xd2\xfb\x19Q\a\xd4\xcd\\\xa3\x00wA\x19\u07ed/x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x955r\xf0\xeam\xf9\xb1\x97\xca\xe4\x0eK\x8e\xcc\x05lCq\u014965\u026d\xc5\u07a0\x00\x00\u07d4\x95>\xf6R\xe7\xb7i\xf5=nxjX\x95/\xa9>\xe6\xab\u725b\ny\x1f\x12\x110\x00\x00\u07d4\x95DpF1;/:^\x19\xb9H\xfd;\x8b\xed\xc8,q|\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x95]\xb3\xb7C`\xb9\xa2hg~s\u03a8!f\x8a\xf6\xfa\u038a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\x95`\xe8\xacg\x18\xa6\xa1\xcd\xcf\xf1\x89\xd6\x03\xc9\x06>A=\xa6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x95g\xa0\u0781\x1d\xe6\xff\t[~\xe6N\u007f\x1b\x83\xc2a[\x80\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x95h\x1c\xda\xe6\x9b I\xce\x10\x1e2\\u\x98\x92\xca\xc3\xf8\x11\x89\x9a\xe9*\x9b\xc9L@\x00\x00\xe0\x94\x95h\xb7\xdeuV(\xaf5\x9a\x84T=\xe25\x04\xe1^A\xe6\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x95i\xc6:\x92\x84\xa8\x05bm\xb3\xa3.\x9d#c\x93GaQ\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x95\x80\x9e\x8d\xa3\xfb\xe4\xb7\xf2\x81\xf0\xb8\xb1q_B\x0f}}c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95\x9fW\xfd\xedj\xe3y\x13\xd9\x00\xb8\x1e_H\xa7\x93\"\xc6'\x89\r\xdb&\x10GI\x11\x80\x00\u07d4\x95\x9f\xf1\u007f\x1dQ\xb4s\xb4@\x10\x05'U\xa7\xfa\x8cu\xbdT\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x95\xa5w\xdc.\xb3\xael\xb9\xdf\xc7z\xf6\x97\xd7\xef\xdf\xe8\x9a\x01\x89\a_a\x0fp\xed \x00\x00\u07d4\x95\xcbm\x8acy\xf9J\xba\x8b\x88ViV,MD\x8eV\xa7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95\xd5PB{ZQLu\x1ds\xa0\xf6\u049f\xb6]\"\xed\x10\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x95\u064d\f\x10i\x90\x8f\x06zR\xac\xac+\x8bSM\xa3z\xfd\x89oY\xb60\xa9)p\x80\x00\xe0\x94\x95\xdfN4E\xd7f&$\u010e\xbat\u03de\nS\xe9\xf72\x8a\v\xdb\xc4\x1e\x03H\xb3\x00\x00\x00\u07d4\x95\xe6\xa5K-_g\xa2JHu\xafu\x10|\xa7\xea\x9f\xd2\xfa\x89Hz\x9a0E9D\x00\x00\xe0\x94\x95\xe6\xf9=\xac\"\x8b\xc7XZ%sZ\xc2\xd0v\xcc:@\x17\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x95\xe7ad$\xcd\ta\xa7\x17'$t7\xf0\x06\x92r(\x0e\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x95\xe8\n\x82\xc2\f\xbe= `$,\xb9-sX\x10\xd04\xa2\x89\x01\xc3.F?\u0539\x80\x00\u07d4\x95\xf6-\x02C\xed\xe6\x1d\xad\x9a1e\xf59\x05'\rT\xe2B\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\x95\xfbZ\xfb\x14\xc1\uf6b7\xd1y\xc5\xc3\x00P?\xd6j^\xe2\x89\x01\xda\xf7\xa0+\r\xbe\x80\x00\u07d4\x96\x10Y\"\x02\u0082\xab\x9b\u0628\x84Q\x8b>\v\xd4u\x817\x89\x0e\x87?D\x13<\xb0\x00\x00\xe0\x94\x96\x1cY\xad\xc7E\x05\u0446M\x1e\xcf\u02ca\xfa\x04\x12Y<\x93\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x96,\r\xec\x8a=FK\xf3\x9b\x12\x15\xea\xfd&H\n\xe4\x90\u0349l\x82\xe3\xea\xa5\x13\xe8\x00\x00\u07d4\x96,\xd2*\x8e\xdf\x1eONU\xb4\xb1]\xdb\xfb]\x9dT\x19q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x963K\xfe\x04\xff\xfaY\x02\x13\xea\xb3e\x14\xf38\xb8d\xb76\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x967\xdc\x12r=\x9cxX\x85B\uac02fO?\x03\x8d\x9d\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x96N\xabK'kL\u0618>\x15\xcar\xb1\x06\x90\x0f\xe4\x1f\u0389\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x96b\xee\x02\x19&h+1\xc5\xf2\x00\xceEz\xbe\xa7ll\xe9\x89$Y\x0e\x85\x89\xebj\x00\x00\xe0\x94\x96l\x04x\x1c\xb5\xe6}\xde25\xd7\xf8b\x0e\x1a\xb6c\xa9\xa5\x8a\x10\r P\xdacQ`\x00\x00\u07d4\x96pv\xa8w\xb1\x8e\xc1ZA[\xb1\x16\xf0n\xf3&E\u06e3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96{\xfa\xf7bC\u0379@\t\xae<\x8d5\x05\xe9\xc0\x80EK\xe0\xe8\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x96\x92A\x91\xb7\xdfe[3\x19\xdcma7\xf4\x81\xa7:\x0f\xf3\x89\xd9\xec\xb4\xfd \x8eP\x00\x00\u07d4\x96\x96\x05!83\x8cr/\x11@\x81\\\xf7t\x9d\r;:t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x96\xa5_\x00\xdf\xf4\x05\xdcM\xe5\xe5\x8cW\xf6\xf6\xf0\xca\xc5]/\x89jf\x167\x9c\x87\xb5\x80\x00\u07d4\x96\xaaW?\xed/#4\x10\u06eeQ\x80\x14[#\xc3\x1a\x02\xf0\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x96\xadW\x9b\xbf\xa8\u06ce\xbe\xc9\u0486\xa7.Fa\xee\xd8\xe3V\x89:\v\xa4+\xeca\x83\x00\x00\u07d4\x96\xb44\xfe\x06W\xe4*\u0302\x12\xb6\x86Q9\xde\xde\x15\x97\x9c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x96\xb9\x06\xear\x9fFU\xaf\xe3\xe5}5'|\x96}\xfa\x15w\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x96\xd6-\xfdF\b\u007fb@\x9d\x93\xdd`a\x88\xe7\x0e8\x12W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96\xd9\u0328\xf5^\xea\x00@\xecn\xb3H\xa1wK\x95\xd9>\xf4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x96\xe7\xc0\xc9\u057f\x10\x82\x1b\xf1@\xc5X\xa1E\xb7\xca\xc2\x13\x97\x899>\xf1\xa5\x12|\x80\x00\x00\u07d4\x96\xeaj\u021a+\xac\x954{Q\u06e6=\x8b\xd5\xeb\xde\xdc\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96\xea\xfb\xf2\xfboM\xb9\xa46\xa7LE\xb5eDR\xe28\x19\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x96\xebR>\x83/P\n\x01}\xe1>\xc2\u007f]6lV\x0e\xff\x89\x10\xac\u03baC\xee(\x00\x00\u07d4\x96\xf0F*\xe6\xf8\xb9`\x88\xf7\xe9\u018ct\xb9\u062d4\xb3G\x89a\t=|,m8\x00\x00\u07d4\x96\xf8 P\vp\xf4\xa3\xe3#\x9da\x9c\xff\x8f\" u\xb15\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x96\xfeY\xc3\u06f3\xaa|\xc8\xcbbH\fe\xe5nb\x04\xa7\xe2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x96\xffoP\x99h\xf3l\xb4,\xbaH\xdb2\xf2\x1fVv\xab\xf8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x97\t8R*\xfb^\x8f\x99Hs\xc9\xfb\xdc&\xe3\xb3~1L\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x97\n\xbdS\xa5O\xcaJd) |\x18-MW\xbb9\u0520\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97\r\x8b\x8a\x00\x16\xd1C\x05O\x14\x9f\xb3\xb8\xe5P\xdc\a\x97\u01c965\u026d\xc5\u07a0\x00\x00\u07d4\x97,/\x96\xaa\x00\u03ca/ Z\xbc\xf8\x93|\fu\xf5\xd8\u0649\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97?N6\x1f\xe5\xde\u0358\x9dL\x8f}|\xc9y\x908]\xaf\x89\x15\x0f\x85C\xa3\x87B\x00\x00\u07d4\x97M\x05A\xabJG\xec\u007fu6\x9c\x00i\xb6J\x1b\x81w\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u0794\x97M/\x17\x89_)\x02\x04\x9d\xea\xae\xcf\t\xc3\x04e\a@-\x88\xcc\x19\u00947\xab\x80\x00\u07d4\x97R\xd1O^\x10\x93\xf0qq\x1c\x1a\xdb\xc4\xe3\xeb\x1e\\W\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97V\xe1v\xc9\xefi>\xe1\xee\u01b9\xf8\xb1Q\xd3\x13\xbe\xb0\x99\x89A\rXj \xa4\xc0\x00\x00\u07d4\x97_7d\xe9{\xbc\xcfv|\xbd;y[\xa8m\x8b\xa9\x84\x0e\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\x97j\x18Sj\xf4\x18tBc\b\x87\x1b\xcd\x15\x12\xa7u\xc9\xf8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x97n<\xea\xf3\xf1\xafQ\xf8\u009a\xff]\u007f\xa2\x1f\x03\x86\xd8\xee\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x97w\xcca\xcfuk\xe3\xb3\xc2\f\xd4I\x1ci\xd2u\xe7\xa1 \x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x97\x81\v\xaf\xc3~\x840c2\xaa\xcb5\xe9*\xd9\x11\xd2=$\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x97\x8cC\f\xe45\x9b\x06\xbc,\xdf\\)\x85\xfc\x95\x0eP\xd5\u0209\x1a\x05V\x90\xd9\u06c0\x00\x00\u07d4\x97\x95\xf6C\x19\xfc\x17\xdd\x0f\x82a\xf9\xd2\x06\xfbf\xb6L\xd0\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97\x99\xca!\xdb\xcfi\xbf\xa1\xb3\xf7+\xacQ\xb9\xe3\xcaX|\xf9\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x97\x9c\xbf!\xdf\xec\x8a\xce?\x1c\x19m\x82\u07d6%4\xdf9O\x89\x99\x91\xd4x\xddM\x16\x00\x00\u07d4\x97\x9dh\x1ca}\xa1o!\xbc\xac\xa1\x01\xed\x16\xed\x01Z\xb6\x96\x89e\xea=\xb7UF`\x00\x00\u07d4\x97\x9f0\x15\x8bWK\x99\x9a\xab4\x81\a\xb9\xee\xd8[\x1f\xf8\xc1\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x97\xa8o\x01\xce?|\xfdDA3\x0e\x1c\x9b\x19\xe1\xb1\x06\x06\xef\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x97\xb9\x1e\xfesP\xc2\xd5~~@k\xab\x18\xf3a{\xcd\xe1J\x8a\x02\x1e\x19\x99\xbb\xd5\u04be\x00\x00\u07d4\x97\xd0\xd9r^;p\xe6u\x841s\x93\x8e\xd3q\xb6,\u007f\xac\x89\t79SM(h\x00\x00\u07d4\x97\xd9\xe4jv\x04\u05f5\xa4\xeaN\xe6\x1aB\xb3\xd25\x0f\xc3\xed\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97\xdc&\xecg\n1\xe0\"\x1d*u\xbc]\xc9\xf9\f\x1fo\u0509\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x97\xde!\xe4!\xc3\u007f\xe4\xb8\x02_\x9aQ\xb7\xb3\x90\xb5\xdfx\x04\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\x97\xe2\x89s\xb8`\xc5g@(\x00\xfb\xb6<\xe3\x9a\x04\x8a=y\x89\x05B%:\x12l\xe4\x00\x00\u07d4\x97\xe5\xcca'\xc4\xf8\x85\xbe\x02\xf4KB\xd1\u0230\xac\x91\u44c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97\xf1\xfeL\x80\x83\xe5\x96!*\x18w(\xdd\\\xf8\n1\xbe\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x97\xf7v\x06W\xc1\xe2\x02u\x90\x86\x96>\xb4!\x1c_\x819\xb9\x8a\n\x8a\t\u007f\xcb=\x17h\x00\x00\xe0\x94\x97\xf9\x9bk\xa3\x13F\u0358\xa9\xfeL0\x8f\x87\u0165\x8cQQ\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x98\n\x84\xb6\x86\xfc1\xbd\xc8<\"\x10XTjq\xb1\x1f\x83\x8a\x89*AUH\xaf\x86\x81\x80\x00\u07d4\x98\x10\xe3J\x94\xdbn\xd1V\xd08\x9a\x0e+\x80\xf4\xfdk\n\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\x1d\xdf\x04\x04\xe4\xd2-\xdaUj\a&\xf0\v-\x98\xab\x95i\x8965f3\xeb\xd8\xea\x00\x00\xe0\x94\x98\x1fq'u\xc0\xda\xd9u\x18\xff\xed\xcbG\xb9\xad\x1dl'b\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\x984h!\x80\xb9\x82\xd1f\xba\u06dd\x9d\x1d\x9b\xbf\x01m\x87\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x986\xb4\xd3\x04sd\x1a\xb5j\xee\xe1\x92Bv\x1drrQx\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x989sB\xec_=L\xb8w\xe5N\xf5\xd6\xf1\xd3fs\x1b\u050a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\x98Fd\x886\xa3\a\xa0W\x18O\xd5\x1fb\x8a_\x8c\x12B|\x8a\x04\vi\xbfC\xdc\xe8\xf0\x00\x00\xe0\x94\x98Jy\x85\xe3\xcc~\xb5\xc96\x91\xf6\xf8\xcc{\x8f$]\x01\xb2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x98]p\xd2\a\x89+\xed9\x85\x90\x02N$!\xb1\xcc\x11\x93Y\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x98m\xf4~v\xe4\u05e7\x89\xcd\xee\x91<\u0243\x16P\x93l\x9d\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x98t\x80?\xe1\xf3\xa06^y\"\xb1Bp\xea\xeb\x03,\xc1\xb5\x89<\xf5\x92\x88$\xc6\xc2\x00\x00\u07d4\x98ub4\x95\xa4l\xdb\xf2YS\x0f\xf88\xa1y\x9e\u00c9\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98v\x18\xc8VV |{\xac\x15\a\xc0\xff\xef\xa2\xfbd\xb0\x92\x89\x03}\xfeC1\x89\xe3\x80\x00\u07d4\x98|\x9b\xcdn?9\x90\xa5+\xe3\xed\xa4q\f'Q\x8fOr\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x98\x82\x96|\xeeh\u04a89\xfa\u062bJ|=\xdd\xf6\xc0\xad\u0209Hx\xbe\x1f\xfa\xf9]\x00\x00\u07d4\x98\x85\\}\xfb\xee3SD\x90J\x12\xc4\fs\x17\x95\xb1:T\x899\xfb\xae\x8d\x04-\xd0\x00\x00\u07d4\x98\x9c\f\xcf\xf6T\xda\x03\xae\xb1\x1a\xf7\x01\x05Ea\xd6)~\x1d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x98\xa0\xe5Lm\x9d\u023e\x96'l\xeb\xf4\xfe\xc4`\xf6#]\x85\x89j\u0202\x10\tR\u01c0\x00\u07d4\x98\xb7i\xcc0\\\xec\xfbb\x9a\x00\xc9\a\x06\x9d~\xf9\xbc:\x12\x89\x01h\u048e?\x00(\x00\x00\xe0\x94\x98\xbaN\x9c\xa7/\xdd\xc2\fi\xb49ov\xf8\x18?z*N\x8a\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\x00\u07d4\x98\xbeimQ\xe3\x90\xff\x1cP\x1b\x8a\x0fc1\xb6(\xdd\u016d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\xbe\u04e7.\xcc\xfb\xaf\xb9#H\x92\x93\xe4)\xe7\x03\xc7\xe2[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\xbfJ\xf3\x81\v\x84#\x87\xdbp\xc1MF\t\x96&\x00=\x10\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x98\xc1\x0e\xbf,O\x97\u02e5\xa1\xab?*\xaf\xe1\xca\xc4#\xf8\u02c9\x10CV\x1a\x88)0\x00\x00\u07d4\x98\xc1\x9d\xba\x81\v\xa6\x11\xe6\x8f/\x83\xee\x16\xf6\xe7tO\f\x1f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x98\xc5IJ\x03\xac\x91\xa7h\xdf\xfc\x0e\xa1\xdd\u0b3f\x88\x90\x19\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\x98\xd2\x04\xf9\b_\x8c\x8e}\xe2>X\x9bd\xc6\xef\xf6\x92\xccc\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x98\xd3s\x19\x92\xd1\xd4\x0e\x12\x11\xc7\xf75\xf2\x18\x9a\xfa\a\x02\xe0\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x98\xe2\xb6\xd6\x06\xfd-i\x91\xc9\xd6\xd4\a\u007f\xdf?\xddE\x85\u06890\xdf\x1ao\x8a\xd6(\x00\x00\u07d4\x98\xe3\xe9\v(\xfc\xca\ue087y\xb8\xd4\nUh\xc4\x11n!\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x98\xe6\xf5G\u06c8\xe7_\x1f\x9c\x8a\xc2\xc5\xcf\x16'\xbaX\v>\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x98\xf4\xaf:\xf0\xae\xde_\xaf\xdcB\xa0\x81\xec\xc1\xf8\x9e<\xcf \x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x98\xf6\xb8\xe6!=\xbc\x9aU\x81\xf4\xcc\xe6e_\x95%+\xdb\a\x89\x11Xr\xb0\xbc\xa40\x00\x00\u07d4\x99\te\r\u05719{\x8b\x8b\x0e\xb6\x94\x99\xb2\x91\xb0\xad\x12\x13\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x99\x11s`\x19G\xc2\bJb\xd69R~\x96\x15\x12W\x9a\xf9\x89 \x86\xac5\x10R`\x00\x00\u07d4\x99\x12\x9d[<\f\xdeG\xea\r\xefM\xfc\a\r\x1fJY\x95'\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x99\x17\u058dJ\xf3A\xd6Q\xe7\xf0\a\\m\xe6\xd7\x14Nt\t\x8a\x012\xd4Gl\b\xe6\xf0\x00\x00\u07d4\x99\x1a\xc7\xcap\x97\x11_& ^\xee\x0e\xf7\xd4\x1e\xb4\xe3\x11\xae\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\x99#e\xd7d\xc5\xce5@9\xdd\xfc\x91.\x02:u\xb8\xe1h\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x99&F\xac\x1a\u02ab\xf5\u076b\xa8\xf9B\x9a\xa6\xa9Nt\x96\xa7\x8967Pz0\xab\xeb\x00\x00\u07d4\x99&\x83'\xc3s3.\x06\xc3\xf6\x16B\x87\xd4U\xb9\xd5\xfaK\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99(\xffqZ\xfc:+`\xf8\xebL\u013aN\xe8\u06b6\u5749\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4\x992\xef\x1c\x85\xb7Z\x9b*\x80\x05}P\x874\xc5\x10\x85\xbe\u0309\x02\xb8?\xa50\x1dY\x00\x00\xe0\x94\x99?\x14ax`^f\xd5\x17\xbex.\xf0\xb3\xc6\x1aN\x19%\x8a\x01|\x1f\x055\u05e5\x83\x00\x00\xe0\x94\x99A7\x04\xb1\xa3.p\xf3\xbc\ri\u0748\x1c8VkT\u02ca\x05\xcckiF1\xf7\x12\x00\x00\u07d4\x99AR\xfc\x95\xd5\xc1\u028b\x88\x11:\xbb\xadMq\x0e@\xde\xf6\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x99D\xfe\xe9\xd3JJ\x88\x00#\u01c92\xc0\vY\xd5\xc8*\x82\x89(\xa8\xa5k6\x90\a\x00\x00\u07d4\x99L\u00b5\"~\xc3\xcf\x04\x85\x12F|A\xb7\xb7\xb7H\x90\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99q\xdf`\xf0\xaef\xdc\xe9\xe8\xc8N\x17\x14\x9f\t\xf9\xc5/d\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x99v\x94~\xff_j\xe5\xda\b\xddT\x11\x92\xf3x\xb4(\xff\x94\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x99}e\x92\xa3\x15\x89\xac\xc3\x1b\x99\x01\xfb\xeb<\xc3\xd6[2\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\x82\xa5\x89\x0f\xfbT\x06\u04ec\xa8\u04bf\xc1\xddp\xaa\xa8\n\xe0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\x87\x8f\x9dn\n~\u066e\u01c2\x97\xb78y\xa8\x01\x95\xaf\xe0\x89\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d4\x99\x8c\x1f\x93\xbc\xdbo\xf2<\x10\xd0\u0712G(\xb7;\xe2\xff\x9f\x896[\xf3\xa43\xea\xf3\x00\x00\u07d4\x99\x91aL[\xaaG\xddl\x96\x87FE\xf9z\xdd,=\x83\x80\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x99\x92J\x98\x16\xbb}\xdf?\xec\x18D\x82\x8e\x9a\xd7\xd0k\xf4\xe6\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x99\x99vh\xf7\xc1\xa4\xff\x9e1\xf9\x97z\xe3\"K\u02c8z\x85\x89\x0f\xc969(\x01\xc0\x00\x00\u07d4\x99\x9cI\xc1t\xca\x13\xbc\x83l\x1e\n\x92\xbf\xf4\x8b'\x15C\u0289\xb1\xcf$\xdd\u0431@\x00\x00\u07d4\x99\xa4\xde\x19\xde\u05d0\b\xcf\xdc\xd4]\x01M.XK\x89\x14\xa8\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x99\xa9k\xf2$.\xa1\xb3\x9e\xceo\xcc\r\x18\xae\xd0\f\x01y\xf3\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x99\xb0\x18\x93+\xca\xd3U\xb6y+%]\xb6p-\xec\x8c\xe5\u0749\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x99\xb7C\xd1\xd9\xef\xf9\r\x9a\x194\xb4\xdb!\xd5\x19\u061bJ8\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x99\xb8\xc8$\x86\x9d\xe9\xed$\xf3\xbf\xf6\x85L\xb6\xddE\xcc?\x9f\x89e\xea=\xb7UF`\x00\x00\u07d4\x99\xc0\x17L\xf8N\a\x83\xc2 \xb4\xebj\xe1\x8f\xe7\x03\x85J\u04c9py\xa2W=\fx\x00\x00\u07d4\x99\xc1\xd9\xf4\fj\xb7\xf8\xa9/\xce/\xdc\xe4zT\xa5\x86\xc5?\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x99\xc26\x14\x1d\xae\xc87\xec\xe0O\xda\xee\x1d\x90\u03cb\xbd\xc1\x04\x89ve\x16\xac\xac\r \x00\x00\u07d4\x99\xc3\x1f\xe7HX7\x87\xcd\xd3\xe5%\xb2\x81\xb2\x18\x96\x179\xe3\x897\b\xba\xed=h\x90\x00\x00\u07d4\x99\xc4u\xbf\x02\xe8\xb9!J\xda_\xad\x02\xfd\xfd\x15\xba6\\\f\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\x99\u0203%\x85F\xcc~N\x97\x1fR.8\x99\x18\xda^\xa6:\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x99\xc9\xf9>E\xfe<\x14\x18\xc3S\xe4\u016c8\x94\xee\xf8\x12\x1e\x89\x05\x85\xba\xf1E\x05\v\x00\x00\xe0\x94\x99\xd1W\x9c\xd4&\x82\xb7dN\x1dOq(D\x1e\xef\xfe3\x9d\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x99\u0475\x85\x96_@jB\xa4\x9a\x1c\xa7\x0fv\x9evZ?\x98\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\x99\xdf\xd0PL\x06\xc7C\xe4e4\xfd{U\xf1\xf9\xc7\xec3)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\xf4\x14|\xcck\u02c0\u0304.i\xf6\xd0\x0e0\xfaA3\u0649\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x99\xf7\u007f\x99\x8b \xe0\xbc\xdc\xd9\xfc\x83\x86ARl\xf2Y\x18\xef\x89a\t=|,m8\x00\x00\u07d4\x99\xfa\xd5\x008\xd0\xd9\xd4\xc3\xfb\xb4\xbc\xe0V\x06\xec\xad\xcdQ!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\xfe\r \x12(\xa7S\x14VU\xd4(\xeb\x9f\xd9I\x85\xd3m\x89i \xbf\xf3QZ:\x00\x00\u07d4\x9a\a\x9c\x92\xa6)\xca\x15\xc8\xca\xfa.\xb2\x8d[\xc1z\xf8(\x11\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9a\r<\xee=\x98\x92\xea;7\x00\xa2\u007f\xf8A@\xd9\x02T\x93\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x9a$\u038dH\\\xc4\xc8nI\u07b3\x90\"\xf9,t0\xe6~\x89Fy\x1f\xc8N\a\xd0\x00\x00\u07d4\x9a,\xe4;]\x89\u0593k\x8e\x8c5G\x91\xb8\xaf\xff\x96$%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9a9\x01bS^9\x88w\xe4\x16x}b9\xe0uN\x93|\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9a=\xa6P#\xa10 \xd2!E\xcf\xc1\x8b\xab\x10\xbd\x19\xceN\x89\x18\xbfn\xa3FJ:\x00\x00\xe0\x94\x9a>+\x1b\xf3F\xdd\a\v\x02sW\xfe\xacD\xa4\xb2\xc9}\xb8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9aL\xa8\xb8!\x17\x89NC\xdbr\xb9\xfax\xf0\xb9\xb9:\xce\t\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x9aR.R\xc1\x95\xbf\xb7\xcf_\xfa\xae\u06d1\xa3\xbath\x16\x1d\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9aZ\xf3\x1c~\x063\x9a\u0234b\x8d|M\xb0\xce\x0fE\u0224\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\x9ac?\xcd\x11,\xce\xebv_\xe0A\x81ps*\x97\x05\u7708\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x9ac\u0445\xa7\x91)\xfd\xab\x19\xb5\x8b\xb61\xea6\xa4 TN\x89\x02F\xdd\xf9yvh\x00\x00\u07d4\x9ag\b\u0778\x90<(\x9f\x83\xfe\x88\x9c\x1e\xdc\xd6\x1f\x85D#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9ao\xf5\xf6\xa7\xaf{z\xe0\xed\x9c \xec\xecP#\u0481\xb7\x86\x89\x8a\x12\xb9\xbdjg\xec\x00\x00\xe0\x94\x9a\x82\x82m<)H\x1d\xcc+\u0495\x00G\xe8\xb6\x04\x86\xc38\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9a\x8e\xcaA\x89\xffJ\xa8\xff~\u0536\xb7\x03\x9f\t\x02!\x9b\x15\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9a\x95;[\xccp\x93y\xfc\xb5Y\u05f9\x16\xaf\u06a5\f\xad\u0309\x05k\xc7^-c\x10\x00\x00\u07d4\x9a\x99\v\x8a\xebX\x8d~\xe7\xec.\xd8\xc2\xe6Os\x82\xa9\xfe\xe2\x89\x01\xd1'\xdbi\xfd\x8b\x00\x00\u07d4\x9a\x9d\x1d\xc0\xba\xa7}n \xc3\xd8I\u01c8b\xdd\x1c\x05L\x87\x89/\xb4t\t\x8fg\xc0\x00\x00\xe0\x94\x9a\xa4\x8cf\xe4\xfbJ\u0419\x93N2\x02.\x82t'\xf2w\xba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9a\xa80\x8fB\x91\x0eZ\xde\t\xc1\xa5\xe2\x82\xd6\xd9\x17\x10\xbd\xbf\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9a\xaa\xfa\x00gd~\u0659\x06kzL\xa5\xb4\xb3\xf3\xfe\xaao\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9a\xb9\x88\xb5\x05\xcf\xee\x1d\xbe\x9c\u044e\x9bTs\xb9\xa2\xd4\xf56\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\x9a\xb9\x8dm\xbb\x1e\xaa\xe1mE\xa0EhT\x1a\xd3\xd8\xfe\x06\u0309\x0e\xc5\x04d\xfe#\xf3\x80\x00\xe0\x94\x9a\xba+^'\xffx\xba\xaa\xb5\xcd\u0248\xb7\xbe\x85\\\xeb\xbd\u038a\x02\x1e\f\x00\x13\a\n\xdc\x00\x00\u07d4\x9a\xc4\xdaQ\xd2x\"\xd1\xe2\b\xc9n\xa6J\x1e[U)\x97#\x89\x05lUy\xf7\"\x14\x00\x00\u0794\x9a\xc8S\x97y*i\u05cf(k\x86C*\a\xae\u03b6\x0ed\x88\xc6s\xce<@\x16\x00\x00\xe0\x94\x9a\xc9\a\xee\x85\xe6\xf3\xe2#E\x99\x92\xe2V\xa4?\xa0\x8f\xa8\xb2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9a\xd4\u007f\xdc\xf9\u0354-(\xef\xfd[\x84\x11[1\xa6X\xa1>\x89\xb2Y\xec\x00\xd5;(\x00\x00\u07d4\x9a\xdb\u04fc{\n\xfc\x05\xd1\xd2\xed\xa4\x9f\xf8c\x93\x9cH\xdbF\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x9a\xdfE\x8b\xff5\x99\xee\xe1\xa2c\x98\x85\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x9a\xf9\xdb\xe4t\"\xd1w\xf9E\xbd\xea\xd7\xe6\xd8)05b0\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x9a\xfaSkLf\xbc8\xd8u\u0133\x00\x99\xd9&\x1f\xdb8\xeb\x89\v*\x8f\x84*w\xbc\x80\x00\u07d4\x9b\x06\xad\x84\x1d\xff\xbeL\xcfF\xf1\x03\x9f\u00c6\xf3\xc3!Dn\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\x11h\u078a\xb6KGU/3\x89\x80\n\x9c\xc0\x8bFf\u03c9]\u0212\xaa\x111\xc8\x00\x00\u07d4\x9b\x18\x11\xc3\x05\x1fF\xe6d\xaeK\xc9\xc8$\u0445\x92\xc4WJ\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x9b\x18G\x86U\xa4\x85\x1c\xc9\x06\xe6`\xfe\xaca\xf7\xf4\u023f\xfc\x89\xe2G\x8d8\x90}\x84\x00\x00\u07d4\x9b\"\xa8\r\\{3t\xa0[D`\x81\xf9}\n4\a\x9e\u007f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x9b+\xe7\xf5gT\xf5\x05\xe3D\x1a\x10\xf7\xf0\xe2\x0f\xd3\xdd\xf8I\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\x9b2\xcfOQ\x15\xf4\xb3J\x00\xa6La}\xe0c\x875C#\x89\x05\xb8\x1e\u0608 |\x80\x00\u07d4\x9bC\u0739_\xde1\x80u\xa5g\xf1\xe6\xb5v\x17\x05^\xf9\xe8\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x9bDO\xd37\xe5\xd7R\x93\xad\xcf\xffp\xe1\xea\x01\xdb\x022\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9bH$\xff\x9f\xb2\xab\xdaUM\xeeO\xb8\xcfT\x91eW\x061\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9bL'\x15x\f\xa4\xe9\x9e`\xeb\xf2\x19\xf1Y\f\x8c\xadP\n\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x9bY\xeb!;\x1eue\xe4PG\xe0N\xa07O\x10v-\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\\9\xf7\xe0\xac\x16\x8c\x8e\xd0\xed4\x04w\x11}\x1bh.\xe9\x89\x05P\x05\xf0\xc6\x14H\x00\x00\u07d4\x9b^\xc1\x8e\x83\x13\x88}\xf4a\u0490.\x81\xe6z\x8f\x11;\xb1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9bd\xd3\u034d+s\xf6hA\xb5\xc4k\xb6\x95\xb8\x8a\x9a\xb7]\x89\x01 :Ov\f\x16\x80\x00\u07d4\x9be\x8f\xb3a\xe0F\xd4\xfc\xaa\x8a\xefm\x02\xa9\x91\x11\"6%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9bfA\xb1>\x17/\xc0r\xcaK\x83'\xa3\xbc(\xa1[f\xa9\x89\x06\x81U\xa46v\xe0\x00\x00\xe0\x94\x9bh\xf6t\x16\xa6;\xf4E\x1a1\x16L\x92\xf6r\xa6\x87Y\xe9\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\x9bw6i\xe8}v\x01\x8c\t\x0f\x82U\xe5D\t\xb9\u0728\xb2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9bw\xeb\xce\xd7\xe2\x15\xf0\x92\x0e\x8c+\x87\x00$\xf6\xec\xb2\xff1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9b|\x88\x10\xcc|\u021e\x80Nm>8\x12\x18PG(w\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\xa5=\xc8\xc9^\x9aG/\xeb\xa2\xc4\xe3,\x1d\xc4\xdd{\xabF\x89Hz\x9a0E9D\x00\x00\xe0\x94\x9b\xac\xd3\xd4\x0f;\x82\xac\x91\xa2d\xd9\u060d\x90\x8e\xac\x86d\xb9\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9b\xb7`\xd5\u0089\xa3\xe1\xdb\x18\xdb\tSE\xcaA;\x9aC\u0089\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x9b\xb7b\x04\x18j\xf2\xf6;\xe7\x91h`\x16\x87\xfc\x9b\xadf\x1f\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9b\xb9\xb0*&\xbf\xe1\xcc\xc3\xf0\xc6!\x9e&\x1c9\u007f\xc5\xcax\x89Hz\x9a0E9D\x00\x00\u07d4\x9b\xc5s\xbc\xda#\xb8\xb2o\x90s\xd9\f#\x0e\x8eq\xe0'\v\x896/u\xa40]\f\x00\x00\u07d4\x9b\xd7\u00caB\x100JMe>\xde\xff\x1b<\xe4_\xcexC\x89\x0fI\x89A\xe6d(\x00\x00\xe0\x94\x9b\u0600h\xe10u\xf3\xa8\xca\xc4d\xa5\xf9I\xd6\xd8\x18\xc0\xf6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9b\xd9\x05\xf1q\x9f\u01ec\xd0\x15\x9dM\xc1\xf8\xdb/!G#8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9b\xdb\u071b\x9741\xd1<\x89\xa3\xf9u~\x9b;bu\xbf\u01c9\x1b\x1a}\u03caD\u04c0\x00\u07d4\x9b\xe3\xc3)\xb6*(\xb8\xb0\x88l\xbd\x8b\x99\xf8\xbc\x93\f\xe3\xe6\x89\x04\t\xe5+H6\x9a\x00\x00\xe0\x94\x9b\xf5\x8e\xfb\xea\a\x84\xeb\x06\x8a\xde\u03e0\xbb!P\x84\xc7:5\x8a\x01:k+VHq\xa0\x00\x00\u07d4\x9b\xf6r\xd9y\xb3fR\xfcR\x82Tzjk\xc2\x12\xaeCh\x89#\x8f\xd4,\\\xf0@\x00\x00\xe0\x94\x9b\xf7\x03\xb4\x1c6$\xe1_@T\x96#\x90\xbc\xba0R\xf0\xfd\x8a\x01H>\x01S<.<\x00\x00\u07d4\x9b\xf7\x1f\u007f\xb57\xacT\xf4\xe5\x14\x94\u007f\xa7\xffg(\xf1m/\x89\x01\u03c4\xa3\n\n\f\x00\x00\u07d4\x9b\xf9\xb3\xb2\xf2<\xf4a\xebY\x1f(4\v\xc7\x19\x93\x1c\x83d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x9b\xfce\x9c\x9c`\x1e\xa4*k!\xb8\xf1p\x84\xec\x87\xd7\x02\x12\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9b\xff\xf5\r\xb3jxUU\xf0vR\xa1S\xb0\xc4+\x1b\x8bv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\x05\xe9\xd0\xf0u\x8eyS\x03q~1\xda!<\xa1W\u618965\u026d\xc5\u07a0\x00\x00\u07d4\x9c\x1bw\x1f\t\xaf\x88*\xf0d0\x83\xde*\xa7\x9d\xc0\x97\xc4\x0e\x89\x86p\xe9\xece\x98\xc0\x00\x00\u07d4\x9c(\xa2\xc4\b`\x91\xcb]\xa2&\xa6W\xce2H\xe8\xea{o\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\x9c/\xd5@\x89\xaff]\xf5\x97\x1ds\xb8\x04a`9dsu\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c4@\x98\xbaaZ9\x8f\x11\xd0\t\x90[\x17|D\xa7\xb6\x02\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c=\x06\x92\xce\xee\xf8\n\xa4\x96\\\xee\xd2b\xff\xc7\xf0i\xf2\u0709\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9c@\\\xf6\x97\x95a8\x06^\x11\xc5\xf7U\x9eg$[\u0465\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9cE *%\xf6\xad\x00\x11\xf1\x15\xa5\xa7\"\x04\xf2\xf2\x19\x88f\x8a\x01\x0f\xcf:b\xb0\x80\x98\x00\x00\xe0\x94\x9cI\xde\xffG\b_\xc0\x97\x04\u02a2\u0728\u0087\xa9\xa17\u068a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x9cK\xbc\xd5\xf1dJo\aX$\xdd\xfe\x85\xc5q\u05ab\xf6\x9c\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4\x9cRj\x14\x06\x83\xed\xf1C\x1c\xfa\xa1(\xa95\xe2\xb6\x14\u060b\x89\x06\x04o7\xe5\x94\\\x00\x00\xe0\x94\x9cT\xe4\xedG\x9a\x85h)\u01bbB\u069f\vi*u\xf7(\x8a\x01\x97\xa8\xf6\xddU\x19\x80\x00\x00\xe0\x94\x9cX\x1a`\xb6\x10(\xd94\x16y)\xb2-p\xb3\x13\xc3O\u040a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x9c\\\xc1\x11\t,\x12!\x16\xf1\xa8_N\xe3\x14\bt\x1a}/\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\x9ck\u0264k\x03\xaeT\x04\xf0C\xdf\xcf!\x88>A\x10\xcc3\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9cx\x96?\xbc&<\t\xbdr\xe4\xf8\xde\xf7J\x94u\xf7\x05\\\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4\x9cx\xfb\xb4\xdfv\x9c\xe2\xc1V\x92\f\xfe\xdf\xda\x03:\x0e%J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x9c{m\xc5\x19\x0f\xe2\x91)c\xfc\xd5yh>\xc79Q\x16\xb0\x89*\x11)\u0413g \x00\x00\u07d4\x9c\x80\xbc\x18\xe9\xf8\u0516\x8b\x18]\xa8\u01df\xa6\xe1\x1f\xfc>#\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x9c\x98\xfd\xf1\xfd\u034b\xa8\xf4\u0170L:\xe8X~\xfd\xf0\xf6\xe6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x9c\x99\xa1\u0691\u0552\v\xc1N\f\xb9\x14\xfd\xf6+\x94\u02c3X\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9c\x99\xb6&\x06(\x1b\\\xef\xab\xf3aV\xc8\xfeb\x83\x9e\xf5\xf3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x9c\x9a\a\xa8\xe5|1r\xa9\x19\xefdx\x94tI\x0f\r\x9fQ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9c\x9d\xe4G$\xa4\x05M\xa0\xea\xa6\x05\xab\u0300&hw\x8b\xea\x89\n\xd7\xd5\xca?\xa5\xa2\x00\x00\u07d4\x9c\x9f;\x8a\x81\x1b!\xf3\xff?\xe2\x0f\xe9p\x05\x1c\xe6j\x82O\x89>\xc2\u07bc\a\u053e\x00\x00\xe0\x94\x9c\x9f\x89\xa3\x91\x0fj*\xe8\xa9\x10G\xa1z\xb7\x88\xbd\xde\xc1p\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9c\xa0B\x9f\x87O\x8d\xce\xe2\xe9\xc0b\xa9\x02\n\x84*Xz\xb9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\xa4.\u7838\x98\xf6\xa5\xcc`\xb5\xa5\u05f1\xbf\xa3\xc321\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\xb2\x8a\xc1\xa2\n\x10o\u007f76\x92\xc5\xceLs\xf172\xa1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c\xcd\u0732\xcf\u00b2[\br\x9a\n\x98\xd9\xe6\xf0 .\xa2\xc1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9c\xe2\u007f$^\x02\xd1\xc3\x12\xc1\xd5\x00x\x8c\x9d\xefv\x90E;\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9c\xe56;\x13\xe8#\x8a\xa4\xdd\x15\xac\u0432\xe8\xaf\xe0\x872G\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9c\xf2\x92\x8b\xee\xf0\x9a@\xf9\xbf\xc9S\xbe\x06\xa2Q\x11a\x82\xfb\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9d\x06\x91\x97\xd1\xdeP\x04Z\x18o^\xc7D\xac@\u8bd1\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\x9d\x0e}\x92\xfb0XS\u05d8&;\xf1^\x97\xc7+\xf9\xd7\xe0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9d\x0f4~\x82k}\u03aa\xd2y\x06\n5\xc0\x06\x1e\xcf3K\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d u\x17B,\xc0\xd6\r\xe7\xc27\tzMO\xce \x94\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x9d%\n\xe4\xf1\x10\xd7\x1c\xaf\u01f0\xad\xb5.\x8d\x9a\xcbfy\xb8\x8a\x02\x15mn\x99r\x13\xc0\x00\x00\xe0\x94\x9d+\xfc6\x10o\x03\x82P\xc0\x18\x01hW\x85\xb1l\x86\xc6\r\x8aPw\xd7]\xf1\xb6u\x80\x00\x00\xe0\x94\x9d0\xcb#{\xc0\x96\xf1p6\xfc\x80\xdd!\xcah\x99,\xa2\u064a\x06n\xe71\x8f\u070f0\x00\x00\u07d4\x9d2\x96.\xa9\x97\x00\xd92(\xe9\xdb\xda\xd2\xcc7\xbb\x99\xf0~\x89\xb4c+\xed\xd4\xde\xd4\x00\x00\u07d4\x9d4\xda\xc2[\xd1X(\xfa\xef\xaa\xf2\x8fq\aS\xb3\x9e\x89\u0709;\x1cV\xfe\xd0-\xf0\x00\x00\u07d4\x9d6\x91e\xfbp\xb8\x1a:v_\x18\x8f\xd6\f\xbe^{\th\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9d@\xe0\x12\xf6\x04%\xa3@\xd8-\x03\xa1\xc7W\xbf\xab\xc7\x06\xfb\x89\t4o:\xdd\u020d\x80\x00\u07d4\x9dAt\xaaj\xf2\x84v\xe2)\xda\xdbF\x18\b\b\xc6u\x05\xc1\x89B\x1a\xfd\xa4.\u0597\x00\x00\u07d4\x9dB\x133\x9a\x01U\x18avL\x87\xa9<\xe8\xf8_\x87\x95\x9a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9dF\f\x1b7\x9d\xdb\x19\xa8\xc8[LgG\x05\r\xdf\x17\xa8u\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x9dG\xba[L\x85\x05\xad\x8d\xa4)4(\va\xa0\xe1\xe8\xb9q\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9dM2\x11w%n\xbd\x9a\xfb\xda0A5\xd5\x17\xc3\xdcV\x93\x89!d\xb7\xa0J\u0220\x00\x00\u07d4\x9dO\xf9\x89\xb7\xbe\u066b\x10\x9d\x10\xc8\xc7\xe5_\x02\xd7g4\xad\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x9dQ\x15C\xb3\xd9\xdc`\xd4\u007f\t\u051d\x01\xb6\u0118\xd8 x\x8a\x02a\x97\xb9Qo\u00d4\x00\x00\u07d4\x9dn\u03e0:\xf2\xc6\xe1D\xb7\xc4i*\x86\x95\x1e\x90.\x9e\x1f\x89\xa2\xa5\xaa`\xad$?\x00\x00\u07d4\x9dvU\xe9\xf3\xe5\xba]n\x87\xe4\x12\xae\xbe\x9e\xe0\u0512G\ue24e\t1\x1c\x1d\x80\xfa\x00\x00\u07d4\x9dx1\xe84\xc2\v\x1b\xaaiz\xf1\xd8\xe0\xc6!\u016f\xff\x9a\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4\x9dx\xa9u\xb7\xdb^M\x8e(\x84\\\xfb\xe7\xe3\x14\x01\xbe\r\u0649H\xa40k\xa2\u5e5c\x8ahX\u02f5,\f\xf75\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\x9d\u007f\xdapp\xbf>\xe9\xbb\u0664\x1fU\xca\u0505J\xe6\xc2,\x8a\x02U\u02e3\xc4o\xcf\x12\x00\x00\u07d4\x9d\x81\xae\xa6\x9a\xedj\xd0p\x89\xd6\x14E4\x8c\x17\xf3K\xfc[\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9d\x91\x1f6\x82\xf3/\xe0y.\x9f\xb6\xff<\xfcG\xf5\x89\xfc\xa5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\x91;]3\x9c\x95\xd8wEV%c\xfe\xa9\x8b#\xc6\f\u0109\tA0,\u007fM#\x00\x00\u07d4\x9d\x93\xfa\xb6\xe2(E\xf8\xf4Z\aIo\x11\xdeqS\r\xeb\u01c9lO\xd1\xee$nx\x00\x00\u07d4\x9d\x99\xb1\x89\xbb\u0664\x8f\xc2\xe1n\x8f\u0363;\xb9\x9a1{\xbb\x89=\x16\xe1\vm\x8b\xb2\x00\x00\u07d4\x9d\x9cN\xfe\x9fC9\x89\xe2;\xe9@I!S)\xfaU\xb4\u02c9\r\u3c89\x03\u01b5\x80\x00\u07d4\x9d\x9eW\xfd\xe3\x0ePh\xc0>I\x84\x8e\xdc\xe3C\xb7\x02\x83X\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x9d\xa30\"@\xaf\x05\x11\xc6\xfd\x18W\xe6\u07779Ow\xabk\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\x9d\xa4\xec@pw\xf4\xb9p{-\x9d.\xde^\xa5(+\xf1\u07c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\xa6\t\xfa:~l\xf2\xcc\x0ep\u036b\xe7\x8d\xc4\xe3\x82\xe1\x1e\x89A\rXj \xa4\xc0\x00\x00\xe0\x94\x9d\xa6\x1c\xcdb\xbf\x86\x06V\xe02]qW\xe2\xf1`\xd9;\xb5\x8a\x01\x0f\f\xa9V\xf8y\x9e\x00\x00\xe0\x94\x9d\xa6\xe0u\x98\x9ct\x19\tL\xc9\xf6\xd2\u44d3\xbb\x19\x96\x88\x8a\x02Y\xbbq\u056d\xf3\xf0\x00\x00\u07d4\x9d\xa8\xe2,\xa1\x0eg\xfe\xa4NR^GQ\xee\xac6\xa3\x11\x94\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\x9d\xb2\xe1\\\xa6\x81\xf4\xc6`H\xf6\xf9\xb7\x94\x1e\u040b\x1f\xf5\x06\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\xc1\x0f\xa3\x8f\x9f\xb0h\x10\xe1\x1f`\x17>\xc3\xd2\xfdju\x1e\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x9d\xd2\x19f$\xa1\xdd\xf1J\x9d7^_\a\x15+\xaf\"\xaf\xa2\x89A\xb0^$c\xa5C\x80\x00\u07d4\x9d\xd4k\x1cm?\x05\u279co\x03~\xed\x9aYZ\xf4\xa9\xaa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9d\xdd5^cN\xe9\x92~K\u007fl\x97\xe7\xbf:/\x1ehz\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x9d\xe2\n\xe7j\xa0\x82c\xb2\x05\xd5\x14$a\x96\x1e$\b\xd2f\x89\r\xa93\xd8\xd8\xc6p\x00\x00\u07d4\x9d\xe2\v\xc3~\u007fH\xa8\x0f\xfdz\xd8O\xfb\xf1\xa1\xab\xe1s\x8c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9d\xe78m\xde@\x1c\xe4\xc6{q\xb6U?\x8a\xa3N\xa5\xa1}\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x9d\xeb9\x02z\xf8w\x99+\x89\xf2\xecJ\x1f\x82.\xcd\xf1&\x93\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9d\xef\xe5j\x0f\xf1\xa1\x94}\xba\t#\xf7\xdd%\x8d\x8f\x12\xfaE\x8a\x05\xb1*\ufbe8\x04\x00\x00\x00\u07d4\x9d\xf0W\xcd\x03\xa4\xe2~\x8e\x03/\x85y\x85\xfd\u007f\x01\xad\xc8\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9d\xf3*P\x1c\vx\x1c\x02\x81\x02/B\xa1)?\xfd{\x89*\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4\x9e\x01vZ\xff\b\xbc\"\x05P\xac\xa5\xea.\x1c\xe8\u5c19#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9e \xe5\xfd6\x1e\xab\xcfc\x89\x1f[\x87\xb0\x92h\xb8\xeb7\x93\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9e#,\b\xc1M\xc1\xa6\xed\v\x8a;(h\x97{\xa5\xc1}\x10\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e#\xc5\u4dc2\xb0\n_\xad\U0006eb47\xda\xcf[\x03g\xa1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e59\x90q\xa4\xa1\x01\xe9\x19M\xaa?\t\xf0J\v_\x98p\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9e>\xb5\t'\x8f\xe0\xdc\xd8\xe0\xbb\xe7\x8a\x19N\x06\xb6\x809C\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\x9eBrrQk>g\xd4\xfc\xbf\x82\xf5\x93\x90\xd0L\x8e(\xe5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x9eL\xec5:\xc3\u3043^<\t\x91\xf8\xfa\xa5\xb7\u0428\xe6\x8a\x02\x1e\x18\xb9\xe9\xabE\xe4\x80\x00\u07d4\x9eX\x11\xb4\v\xe1\xe2\xa1\xe1\u048c;\at\xac\xde\n\t`=\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x9eZ1\x1d\x9fi\x89\x8a|j\x9dc`h\x048\xe6z{/\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x9e| P\xa2'\xbb\xfd`\x93~&\x8c\xea>h\xfe\xa8\xd1\xfe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9e\u007fe\xa9\x0e\x85\b\x86{\xcc\xc9\x14%j\x1e\xa5t\xcf\a\xe3\x89C8t\xf62\xcc`\x00\x00\xe0\x94\x9e\x81D\xe0\x8e\x89dx\x11\xfekr\xd4E\u05a5\xf8\n\xd2D\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9e\x8fd\xdd\xcd\u9e34Q\xba\xfa\xa25\xa9\xbfQ\x1a%\xac\x91\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\x9e\x95\x1fm\xc5\xe3R\xaf\xb8\xd0B\x99\xd2G\x8aE\x12Y\xbfV\x89\x03\xe7A\x98\x81\xa7:\x00\x00\u07d4\x9e\x96\r\xcd\x03\u057a\x99\xcb\x11]\x17\xffL\t$\x8a\xd4\u043e\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9e\xafj2\x8a@v\x02N\xfakg\xb4\x8b!\xee\xdc\xc0\xf0\xb8\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x9e\xb1\xffqy\x8f(\xd6\xe9\x89\xfa\x1e\xa0X\x8e'\xba\x86\xcb}\x89\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\x9e\xb2\x81\xc3'\x19\xc4\x0f\xdb>!m\xb0\xf3\u007f\xbcs\xa0&\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e\xb3\xa7\xcb^g&Bz:6\x1c\xfa\x8dad\xdb\u043a\x16\x89+\x95\xbd\xcc9\xb6\x10\x00\x00\u07d4\x9e\xb7\x83N\x17\x1dA\xe0i\xa7yG\xfc\xa8v\"\xf0\xbaNH\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x9e\xc0>\x02\u51f7v\x9d\xefS\x84\x13\xe9\u007f~U\xbeq\u060a\x04+\xf0kx\xed;P\x00\x00\u07d4\x9e\u02eb\xb0\xb2'\x82\xb3uD)\xe1uz\xab\xa0K\x81\x18\x9f\x89,\xa7\xbb\x06\x1f^\x99\x80\x00\u07d4\x9e\xce\x14\x00\x80\t6\xc7\xc6H_\xcd\xd3b`\x17\u041a\xfb\xf6\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4\x9e\xd4\xe6?ReB\xd4O\xdd\xd3MY\xcd%8\x8f\xfdk\u0689\u049b4\xa4cH\x94\x00\x00\u07d4\x9e\xd8\x0e\xda\u007fU\x05M\xb9\xfbR\x82E\x16\x88\xf2k\xb3t\xc1\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9e\u0710\xf4\xbe!\be!J\xb5\xb3^Z\x8d\xd7t\x15'\x9d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9e\u07acL\x02k\x93\x05M\u0171\xd6a\fo9`\xf2\xads\x89A\rXj \xa4\xc0\x00\x00\u07d4\x9e\xe9?3\x9eg&\xece\xee\xa4O\x8aK\xfe\x10\xda=2\x82\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9e\xe9v\f\xc2s\xd4pj\xa0\x83u\xc3\xe4o\xa20\xaf\xf3\u054a\x01\xe5.3l\xde\"\x18\x00\x00\u07d4\x9e\xeb\a\xbd+x\x90\x19^}F\xbd\xf2\a\x1bf\x17QM\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x9e\xefD-)\x1aD}t\xc5\xd2S\u011e\xf3$\xea\xc1\xd8\xf0\x89\xb9f\b\xc8\x10;\xf0\x00\x00\u07d4\x9e\xf1\x89k\x00|2\xa1Q\x14\xfb\x89\xd7=\xbdG\xf9\x12+i\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9f\x01w\x06\xb80\xfb\x9c0\ufc20\x9fPk\x91WEu4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x10\xf2\xa0F;e\xae0\xb0p\xb3\xdf\x18\xcfF\xf5\x1e\x89\xbd\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x9f\x19\xfa\u0223$7\xd8\n\u0183z\v\xb7\x84\x17)\xf4\x97.\x89#=\xf3)\x9far\x00\x00\u07d4\x9f\x1a\xa8\xfc\xfc\x89\xa1\xa52\x8c\xbdcD\xb7\x1f'\x8a,\xa4\xa0\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9f!0,\xa5\tk\xeat\x02\xb9\x1b\x0f\xd5\x06%O\x99\x9a=\x89C\x97E\x1a\x00=\xd8\x00\x00\u07d4\x9f'\x1d(U\x00\xd78F\xb1\x8fs>%\u074bO]J\x8b\x89'#\xc3F\xae\x18\b\x00\x00\u07d4\x9f4\x97\xf5\xef_\xe60\x95\x83l\x00N\xb9\xce\x02\xe9\x01;K\x89\"V\x86\x1b\xf9\xcf\b\x00\x00\xe0\x94\x9f:t\xfd^~\xdc\xc1\x16)\x93\x17\x13\x81\u02f62\xb7\xcf\xf0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9fF\xe7\xc1\xe9\a\x8c\xae\x860Z\xc7\x06\v\x01F}f\x85\xee\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\x9fIl\xb2\x06\x95c\x14M\b\x11g{\xa0\xe4q:\nAC\x89<\xd2\xe0\xbfc\xa4H\x00\x00\u07d4\x9fJq\x95\xac|\x15\x1c\xa2X\xca\xfd\xa0\u02b0\x83\xe0I\xc6\x02\x89SS\x8c2\x18\\\xee\x00\x00\u07d4\x9fJ\xc9\xc9\xe7\xe2L\xb2DJ\x04T\xfa[\x9a\xd9\xd9-8S\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4\x9f_D\x02kWjJ\xdbA\xe9YaV\x1dA\x03\x9c\xa3\x91\x89\r\x8drkqw\xa8\x00\x00\u07d4\x9f`{?\x12F\x9fDa!\u03bf4u5kq\xb42\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9fa\xbe\xb4o^\x85=\n\x85!\xc7Dnh\xe3L}\ts\x89\x1e[\x8f\xa8\xfe*\xc0\x00\x00\u07d4\x9fd\xa8\xe8\xda\xcfJ\xde0\xd1\x0fMY\xb0\xa3\u056b\xfd\xbft\x8966\x9e\xd7t}&\x00\x00\u07d4\x9ff.\x95'A!\xf1wVncm#\x96L\xf1\xfdho\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9fj2*mF\x99\x81Bj\xe8D\x86]~\xe0\xbb\x15\u01f3\x89\x02\xb5\xeeW\x92\x9f\u06c0\x00\u07d4\x9fy\x86\x92J\xeb\x02h|\xd6A\x89\x18\x9f\xb1g\xde\xd2\xdd\\\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x9fz\x03\x92\xf8Ws.0\x04\xa3u\xe6\xb1\x06\x8dI\xd801\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x82E\u00eb}\x171d\x86\x1c\u04d9\x1b\x94\xf1\xba@\xa9:\x89\x9b\ny\x1f\x12\x110\x00\x00\u07d4\x9f\x83\xa2\x93\xc3$\xd4\x10l\x18\xfa\xa8\x88\x8fd\u0499\x05L\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9f\x86\xa0f\xed\xb6\x1f\xcbXV\u0793\xb7\\\x8cy\x18d\xb9{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x98\xeb4\xd4iy\xb0\xa6\u078b\x05\xaaS:\x89\xb8%\xdc\xf1\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\xe0\x94\x9f\x9f\xe0\xc9_\x10\xfe\xe8z\xf1\xaf r6\xc8\xf3aN\xf0/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9f\xae\xa1\xc5\xe8\x1ez\xcb?\x17\xf1\xc3Q\xee.\u0649\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa0\b\x01\x98c\xc1\xa7|\x14\x99\xeb9\xbb\u05ff-\u05e3\x1c\xb9\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xa0\t\xbf\ao\x1b\xa3\xfaW\u04a7!r\x18\xbe\xd5VZzz\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa0\x1e\x94v\u07c4C\x18%\xc86\xe8\x80:\x97\xe2/\xa5\xa0\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\xa0\x1f\x12\xd7\x0fD\xaa{\x11;(\\\"\xdc\xdbE\x874T\xa7\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xa0\x1f\u0450j\x90\x85\x06\xde\xda\xe1\xe2\b\x12\x88r\xb5n\u7489\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa0\"\x82@\xf9\x9e\x1d\xe9\xcb2\xd8,\x0f/\xa9\xa3\xd4K\v\xf3\x89V\xbcu\xe2\xd61\x00\x00\x00\xe0\x94\xa0+\xdedahn\x19\xace\f\x97\r\x06r\xe7m\xcbO\u008a\x01\xe0\x92\x96\xc37\x8d\xe4\x00\x00\u07d4\xa0,\x1e4\x06O\x04u\xf7\xfa\x83\x1c\xcb%\x01L:\xa3\x1c\xa2\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xa0-\u01aa2\x8b\x88\r\u97acTh#\xfc\xcfw@G\xfb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa0.?\x8fYY\xa7\xaa\xb7A\x86\x12\x12\x9bp\x1c\xa1\xb8\x00\x10\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa04\u007f\n\x98wc\x90\x16\\\x16m2\x96;\xf7M\xcd\n/\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa05\xa3e$x\xf8-\xbdm\x11_\xaa\x8c\xa9F\xec\x9eh\x1d\x89\x05\xf4\xe4-\u052f\xec\x00\x00\u07d4\xa0:=\xc7\xc53\xd1tB\x95\xbe\x95]a\xaf?R\xb5\x1a\xf5\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa0E\x9e\xf3i:\xac\xd1d|\xd5\u0612\x989 L\xefS\xbe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa0O*\xe0*\xdd\x14\xc1/\xafe\xcb%\x90\"\u0403\n\x8e&\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xa0l\xd1\xf3\x969l\ndFFQ\xd7\xc2\x05\xef\xaf8|\xa3\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xa0ri\x1c\x8d\xd7\xcdB7\xffr\xa7\\\x1a\x95\x06\xd0\xce[\x9e\x89\x14\x0e\xc8\x0f\xa7\xee\x88\x00\x00\u07d4\xa0r\u03beb\xa9\xe9\xf6\x1c\xc3\xfb\xf8\x8a\x9e\xfb\xfe>\x9a\x8dp\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa0v\x82\x00\v\x1b\xcf0\x02\xf8\\\x80\xc0\xfa)I\xbd\x1e\x82\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa0z\xa1mt\xae\u8a63(\x8dR\xdb\x15Q\u0553\x882\x97\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa0\x8d![[j\xacHa\xa2\x81\xac~@\vx\xfe\xf0L\xbf\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa0\x95\x19p\xdf\u0403/\xb8;\xda\x12\xc25E\xe7\x90Aul\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa0\x9fM^\xaae\xa2\xf4\xcbu\nI\x924\x01\xda\u5410\xaf\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\xe0\x94\xa0\xa0\xe6R\x04T\x1f\u029b/\xb2\x82\u0355\x13\x8f\xae\x16\xf8\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa0\xaa_\x02\x01\xf0M;\xbe\xb8\x98\x13/|\x11g\x94f\xd9\x01\x89\x01\xfb\xedR\x15\xbbL\x00\x00\u07d4\xa0\xaa\xdb\xd9P\x97\"p_m#X\xa5\u01df7\x97\x0f\x00\xf6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa0\xb7q\x95\x1c\xe1\xde\xee6:\xe2\xb7q\xb7>\a\u0135\xe8\x00\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa0\xde\\`\x1eif5\u0198\xb7\xae\x9c\xa4S\x9f\u01f9A\xec\x89\x12\xc3\xcb\xd7\x04\xc9w\x00\x00\u07d4\xa0\xe8\xbaf\x1bH\x15L\xf8C\xd4\u00a5\xc0\xf7\x92\xd5(\xee)\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa0\xfc~S\xc5\xeb\xd2z*\xbd\xacE&\x1f\x84\xab;Q\xae\xfb\x89\xa3\x13\xda\xec\x9b\xc0\xd9\x00\x00\xe0\x94\xa0\xff[L\xf0\x16\x02~\x83#I}D(\xd3\xe5\xa8;\x87\x95\x8a\x01e\x98\xd3\xc8>\xc0B\x00\x00\u07d4\xa1\x06F[\xbd\x19\u1dbc\xe5\r\x1b\x11W\xdcY\tZ60\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa1\x06\xe6\x92>\xddS\u028e\xd6P\x96\x8a\x91\b\xd6\xcc\xfd\x96p\x8a\x02\x02\xfe\x15\x05\xaf\uc240\x00\u07d4\xa1\t\u12f0\xa3\x9c\x9e\xf8/\xa1\x95\x97\xfc^\xd8\xe9\xebmX\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\xa1\x1a\x03\u013b&\xd2\x1e\xffg}]U\\\x80\xb2TS\xeez\x89\x03\xcb'Y\xbcA\x0f\x80\x00\u07d4\xa1\x1e\xff\xabl\xf0\xf5\x97,\xff\xe4\xd5e\x96\xe9\x89h\x14J\x8f\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xa1 M\xad_V\a(\xa3\\\r\x8f\u01d4\x81\x05{\xf7s\x86\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa1&#\xe6)\u07d3\tg\x04\xb1`\x84\xbe,\u061dV-\xa4\x8a\x01\xcc\xc92E\x11\xe4P\x00\x00\xe0\x94\xa1*l-\x98]\xaf\x0eO_ z\xe8Q\xaa\xf7)\xb32\u034a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xa13m\xfb\x96\xb6\xbc\xbeK>\xdf2\x05\xbeW#\xc9\x0f\xadR\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa1;\x9d\x82\xa9\x9b<\x9b\xbaZ\xe7.\xf2\x19\x9e\xdc};\xb3l\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xa1<\xfe\x82mm\x18A\u072eD;\xe8\u00c7Q\x816\xb5\xe8\x8a\x1d\xa5jK\b5\xbf\x80\x00\x00\xe0\x94\xa1C.\xd2\u01b7wz\x88\xe8\xd4m8\x8epG\u007f \x8c\xa5\x8a\x01\xb1\xa7\xe4\x13\xa1\x96\xc5\x00\x00\u07d4\xa1D\xf6\xb6\x0fr\xd6J!\xe30\xda\xdbb\u0619\n\xde+\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa1P%\xf5\x95\xac\xdb\xf3\x11\x0fw\u017f$G~eH\xf9\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1X\x14\x8a.\x0f>\x92\xdc,\xe3\x8f\xeb\xc2\x01\a\xe3%<\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1a`\x85\x1d+\x9c4\x9b\x92\xe4o\x82\x9a\xbf\xb2\x10\x945\x95\x89a\t=|,m8\x00\x00\u07d4\xa1f\xf9\x11\xc6D\xac2\x13\u049e\x0e\x1a\xe0\x10\xf7\x94\u056d&\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1m\x9e=c\x98aY\xa8\x00\xb4h7\xf4^\x8b\xb9\x80\xee\v\x89n\x11u\xdaz\xd1 \x00\x00\u07d4\xa1pp\xc2\xe9\u0169@\xa4\xec\x0eIT\xc4\xd7\xd6C\xbe\x8fI\x89lk\x17\x03;6\x1c\x80\x00\u07d4\xa1|\x9eC#\x06\x95\x18\x18\x9dR\a\xa0r\x8d\u02d20j?\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa1\x83`\xe9\x85\xf2\x06.\x8f\x8e\xfe\x02\xad,\xbc\x91\xad\x9aZ\xad\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa1\x91\x14\x05\xcfn\x99\x9e\xd0\x11\xf0\xdd\xcd*O\xf7\u008f%&\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa1\x92i\x80\a\xcc\x11\xaa`=\"\x1d_\xee\xa0v\xbc\xf7\xc3\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\x92\xf0j\xb0R\xd5\xfd\u007f\x94\xee\xa81\x8e\x82x\x15\xfegz\x89\a\x1f\x8a\x93\xd0\x1eT\x00\x00\u07d4\xa1\x99\x81D\x96\x8a\\p\xa6AUT\xce\xfe\u0082F\x90\u0125\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa1\xa1\xf0\xfam \xb5\nyO\x02\xefR\b\\\x9d\x03j\xa6\u028965\u026d\xc5\u07a0\x00\x00\u07d4\xa1\xae\x8dE@\xd4\xdbo\xdd\xe7\x14oA[C\x1e\xb5\\y\x83\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xa1\xb4|M\x0e\xd6\x01\x88B\xe6\xcf\xc8c\n\u00e3\x14.^k\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xa1\xc4\xf4Z\x82\xe1\xc4x\xd8E\b.\xb1\x88u\xc4\xeae9\xab\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xa1\xdc\xd0\xe5\xb0Z\x97|\x96#\xe5\xae/Y\xb9\xad\xa2\xf3>1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa1\xe48\n;\x1ft\x96s\xe2p\"\x99\x93\xeeU\xf3Vc\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\xf1\x93\xa0Y/\x1f\xeb\x9f\xdf\xc9\n\xa8\x13xN\xb8\x04q\u0249K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa1\xf2\x85@P\xf8re\x8e\xd8.R\xb0\xad{\xbc\x1c\xb9!\xf6\x89m\x03\x17\xe2\xb3&\xf7\x00\x00\u07d4\xa1\xf5\xb8@\x14\rZ\x9a\xce\xf4\x02\xac<\u00c8jh\xca\xd2H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\xf7e\xc4O\xe4_y\x06w\x94HD\xbeO-B\x16_\xbd\x89\xc7\xe9\xcf\xdev\x8e\xc7\x00\x00\u07d4\xa1\xf7\xdd\xe1\xd78\xd8\xcdg\x9e\xa1\xee\x96[\xee\"K\xe7\xd0M\x89=\x18DP\xe5\xe9<\x00\x00\u07d4\xa1\xf8\u063c\xf9\x0ew\u007f\x19\xb3\xa6Iu\x9a\xd9P'\xab\xdf\u00c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\x02TrB\x80onp\xe7@X\xd6\xe5)-\xef\xc8\xc8\u0509l\x87T\xc8\xf3\f\b\x00\x00\u07d4\xa2\r\a\x1b\x1b\x000cI}y\x90\xe1$\x9d\xab\xf3l5\xf7\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa2\r\x8f\xf6\f\xaa\xe3\x1d\x02\xe0\xb6e\xfaC]v\xf7|\x94B\x89\x1a\x8a\x90\x9d\xfc\xef@\x00\x00\u07d4\xa2\x11\xda\x03\xcc\x0e1\xec\xceS\t\x99\x87\x18QU(\xa0\x90\u07c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\x14B\xab\x054\n\xdeh\xc9\x15\xf3\xc39\x9b\x99U\xf3\xf7\xeb\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xa2\"\"Y\u075c>=\xed\x12p\x84\xf8\b\xe9*\x18\x870,\x89\b\xc83\x9d\xaf\xedH\x00\x00\u07d4\xa2*\xde\r\xdb\\n\xf8\xd0\u034d\xe9M\x82\xb1\x10\x82\xcb.\x91\x897KW\xf3\xce\xf2p\x00\x00\u07d4\xa2L:\xb6!\x81\xe9\xa1[x\xc4b\x1eL|X\x81'\xbe&\x89\b\xcd\xe4:\x83\xd31\x00\x00\u07d4\xa2W\xadYK\u0603(\xa7\xd9\x0f\xc0\xa9\a\u07d5\xee\xca\xe3\x16\x89\x1c7\x86\xff8F\x93\x00\x00\u07d4\xa2[\bd7\xfd!\x92\u0420\xf6On\xd0D\xf3\x8e\xf3\xda2\x89\x12)\x0f\x15\x18\v\xdc\x00\x00\u07d4\xa2v\xb0X\u02d8\u060b\xee\xdbg\xe5CPl\x9a\r\x94p\u0609\x90\xaa\xfcv\xe0/\xbe\x00\x00\u07d4\xa2\x82\xe9i\xca\xc9\xf7\xa0\xe1\xc0\u0350\xf5\xd0\xc48\xacW\r\xa3\x89\"\a\xeb\x89\xfc'8\x00\x00\xe0\x94\xa2\x91\xe9\u01d9\rU-\u046e\x16\u03bc?\xca4,\xba\xf1\u044a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa2\x93\x19\xe8\x10i\xe5\xd6\r\xf0\x0f=\xe5\xad\xee5\x05\xec\xd5\xfb\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa2\x96\x8f\xc1\xc6K\xac\vz\xe0\u058b\xa9I\x87Mm\xb2S\xf4\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xa2\x9d[\xdat\xe0\x03GHr\xbdX\x94\xb8\x853\xffd\u00b5\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa2\x9df\x1acv\xf6m\vt\xe2\xfe\x9d\x8f&\xc0$~\xc8L\x89\xdf3\x04\a\x9c\x13\xd2\x00\x00\u07d4\xa2\xa45\xdeD\xa0\x1b\xd0\ucc9eD\xe4vD\xe4j\f\xdf\xfb\x89\x1b\x1dDZz\xff\xe7\x80\x00\u07d4\xa2\xac\xe4\u0253\xbb\x1eS\x83\xf8\xact\xe1y\x06n\x81O\x05\x91\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa2\xb7\x01\xf9\xf5\xcd\u041eK\xa6+\xae\xba\u3a02W\x10X\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa2\u0145O\xf1Y\x9f\x98\x89,W%\xd2b\xbe\x1d\xa9\x8a\xad\xac\x89\x11\t\xff30\x10\xe7\x80\x00\u07d4\xa2\xc7\xea\xff\xdc,\x9d\x93sE l\x90\x9aR\u07f1LG\x8f\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\xa2\u04aabk\t\xd6\xd4\xe4\xb1?\u007f\xfcZ\x88\xbdz\xd3gB\x89\xfb\x80xPuS\x83\x00\x00\u07d4\xa2\u04cd\xe1\xc79\x06\xf6\xa7\xcan\xfe\xb9|\xf6\xf6\x9c\xc4!\xbe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa2\xdce\xee%kY\xa5\xbdy)wO\x90K5\x8d\U000ed84a\x04\x83\xbc\xe2\x8b\xeb\t\xf8\x00\x00\u07d4\xa2\xe0h:\x80]\xe6\xa0^\xdb/\xfb\xb5\xe9o\x05p\xb67\u00c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa2\u1e2a\x90\x0e\x9c\x13\x9b?\xa1\"5OaV\xd9*\x18\xb1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa2\u2d54\x1e\f\x01\x94K\xfe\x1d_\xb4\xe8\xa3K\x92,\u03f1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\xe4`\xa9\x89\xcb\x15V_\x9e\u0327\xd1!\xa1\x8eN\xb4\x05\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa2\xec\xce,I\xf7*\t\x95\xa0\xbd\xa5z\xac\xf1\xe9\xf0\x01\xe2*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xa2\xf4r\xfeO\"\xb7}\xb4\x89!\x9e\xa4\x02=\x11X*\x93)\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xa2\xf7\x98\xe0w\xb0}\x86\x12N\x14\a\xdf2\x89\r\xbbKcy\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\xf8k\xc0a\x88N\x9e\xef\x05d\x0e\xddQ\xa2\xf7\xc0Yli\x89llD\xfeG\xec\x05\x00\x00\u07d4\xa2\xfa\x17\xc0\xfbPl\xe4\x94\x00\x8b\x95W\x84\x1c?d\x1b\x8c\xae\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa3\x04X\x8f\r\x85\f\xd8\u04cfv\xe9\xe8<\x1b\xf6>3>\u0789\x02(V\x01!l\x8c\x00\x00\u07d4\xa3\x05\x8cQszN\x96\xc5_.\xf6\xbd{\xb3X\x16~\u00a7\x89 \xdb:\xe4H\x1a\u0500\x00\u07d4\xa3\t\xdfT\u02bc\xe7\f\x95\xec03\x14\x9c\xd6g\x8ao\xd4\u03c9\f\x1f\x12\xc7Q\x01X\x00\x00\u07d4\xa3\nER\x0eR\x06\xd9\x00@p\xe6\xaf>{\xb2\xe8\xddS\x13\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa3\x0e\n\xcbSL\x9b0\x84\xe8P\x1d\xa0\x90\xb4\xeb\x16\xa2\xc0\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3 0\x95\xed\xb7\x02\x8ehq\xce\n\x84\xf5HE\x9f\x830\n\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa3!\t\x1d0\x18\x06By\xdb9\x9d+*\x88\xa6\xf4@\xae$\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4\xa3#-\x06\x8dP\x06I\x03\xc9\xeb\xc5c\xb5\x15\xac\u0237\xb0\x97\x89l\x87T\xc8\xf3\f\b\x00\x00\xe0\x94\xa3$\x1d\x89\n\x92\xba\xf5)\b\xdcJ\xa0Irk\xe4&\xeb\u04ca\x04<-\xa6a\xca/T\x00\x00\u07d4\xa3)F&\xec)\x84\xc4;C\xdaM]\x8eFi\xb1\x1dKY\x896\xa4\xcfcc\x19\xc0\x00\x00\u07d4\xa3,\xf7\xdd\xe2\f=\xd5g\x9f\xf5\xe3%\x84\\p\u0156&b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa39\xa3\xd8\xca(\x0e'\xd2A[&\xd1\xfcy2(\xb6`C\x896\U00086577\x8f\xf0\x00\x00\u07d4\xa3<\xb4P\xf9[\xb4n%\xaf\xb5\x0f\xe0_\xee\xe6\xfb\x8c\xc8\xea\x89*\x11)\u0413g \x00\x00\u07d4\xa3?p\xdaru\xef\x05q\x04\u07e7\xdbd\xf4r\xe9\xf5\xd5S\x89\x04YF\xb0\xf9\xe9\xd6\x00\x00\u07d4\xa3@v\xf8K\xd9\x17\xf2\x0f\x83B\u024b\xa7\x9eo\xb0\x8e\xcd1\x89\u3bb5sr@\xa0\x00\x00\u07d4\xa3C\x0e\x1fd\u007f2\x1e\xd3G9V##\xc7\xd6#A\vV\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xa3O\x9dV\x8b\xf7\xaf\xd9L*[\x8a_\xf5\\f\xc4\by\x99\x89\x84}P;\"\x0e\xb0\x00\x00\u07d4\xa3V\x06\xd5\x12 \xee\u007f!F\xd4\x11X.\xe4\xeeJEYn\x89\u062a\xbe\b\v\xc9@\x00\x00\u07d4\xa3VU\x1b\xb7}OE\xa6\xd7\xe0\x9f\n\b\x9ey\u0322I\u02c9\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xa3\\\x19\x13,\xac\x195Wj\xbf\xedl\x04\x95\xfb\a\x88\x1b\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3e\x91\x8b\xfe?&'\xb9\xf3\xa8gu\xd8un\x0f\u0629K\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa3n\r\x94\xb9Sd\xa8&q\xb6\b\xcb-72Ea)\t\x89\b!\xd2!\xb5)\x1f\x80\x00\u07d4\xa3u\xb4\xbc$\xa2N\x1fyu\x93\xcc0+/3\x10c\xfa\\\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa3v\"\xac\x9b\xbd\xc4\xd8+u\x01]t[\x9f\x8d\xe6Z(\uc25d\xc0\\\xce(\u00b8\x00\x00\xe0\x94\xa3y\xa5\a\fP=/\xac\x89\xb8\xb3\xaf\xa0\x80\xfdE\xedK\xec\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xa3\x80-\x8ae\x9e\x89\xa2\xc4~\x90T0\xb2\xa8'\x97\x89P\xa7\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa3\x83\x06\xcbp\xba\xa8\u4446\xbdh\xaap\xa8=$/)\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\x84vi\x1d4\x94.\xeak/v\x88\x92#\x04}\xb4az\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\x87\xceN\x96\x1axG\xf5`\a\\d\xe1YkVA\xd2\x1c\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xa3\x87\xec\xde\x0e\xe4\xc8\a\x94\x99\xfd\x8e\x03G;\u060a\xd7R*\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa3\x88:$\xf7\xf1f _\x1aj\x99I\al&\xa7nqx\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xa3\x8b[\xd8\x1a\x9d\xb9\u04b2\x1d^\xc7\xc6\x05R\xcd\x02\xedV\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa3\x90\xca\x12+\x85\x01\xee>^\a\xa8\xcaKA\x9f~M\xae\x15\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xa3\x93*1\xd6\xffu\xfb;\x12q\xac\xe7\u02a7\xd5\xe1\xff\x10Q\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xa3\x94\xadO\xd9\xe6S\x0eo\\S\xfa\xec\xbe\u0781\xcb\x17-\xa1\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xa3\x97\x9a\x92v\n\x13Z\xdfi\xd7/u\xe1gu_\x1c\xb8\u00c9\x05k\xc7^-c\x10\x00\x00\xe0\x94\xa3\x9b\xfe\xe4\xae\u027du\xbd\"\u01b6r\x89\x8c\xa9\xa1\xe9]2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa3\xa2b\xaf\u0493h\x19#\b\x92\xfd\xe8O-ZYJ\xb2\x83\x89e\xea=\xb7UF`\x00\x00\u07d4\xa3\xa2\xe3\x19\xe7\u04e1D\x8bZ\xa2F\x89S\x16\f-\xbc\xbaq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\xa5{\a\x16\x13(\x04\xd6\n\xac(\x11\x97\xff+=#{\x01\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa3\xa9>\xf9\xdb\xea&6&=\x06\xd8I/jA\u0790|\"\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\xa3\xae\x18y\x00}\x80\x1c\xb5\xf3RqjM\u063a'!\xde=\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xa3\xba\r:6\x17\xb1\xe3\x1bNB,\xe2i\xe8s\x82\x8d]i\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\xa3\xbc\x97\x9bp\x80\t/\xa1\xf9/n\x0f\xb3G\u2359PE\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xa3\xbf\xf1\u07e9\x97\x16h6\f\r\x82\x82\x842\xe2{\xf5Ng\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa3\xc1J\xce(\xb1\x92\u02f0b\x14_\u02fdXi\xc6rq\xf6\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa3\xc3:\xfc\x8c\xb4pN#\x15=\xe2\x04\x9d5\xaeq3$r\x89+X\xad\u06c9\xa2X\x00\x00\u07d4\xa3\u0430<\xff\xbb&\x9fyj\u009d\x80\xbf\xb0}\xc7\u01ad\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\u0543\xa7\xb6[#\xf6\vy\x05\xf3\xe4\xaab\xaa\xc8\u007fB'\x898\xbe\xfa\x12mZ\x9f\x80\x00\u07d4\xa3\xdb6J3-\x88K\xa9;&\x17\xaeM\x85\xa1H\x9b\xeaG\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xa3\xe0Q\xfbtJ\xa3A\f;\x88\xf8\x99\xf5\xd5\u007f\x16\x8d\xf1-\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\xa3\xe3\xa6\xeaP\x95s\xe2\x1b\xd0#\x9e\xce\x05#\xa7\xb7\u061b/\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa3\xf4\xad\x14\xe0\xbbD\xe2\xce,\x145\x9cu\xb8\xe72\xd3pT\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa3\xfa\xccP\x19\\\vI3\xc8X\x97\xfe\xcc[\xbd\x99\\4\xb8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\x03Z\xb1\xe5\x18\b!\xf0\xf3\x80\xf1\x13\x1bs\x87\xc8\u0641\u0349\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\n\xa2\xbb\xce\fr\xb4\xd0\xdf\xff\xccBq[+T\xb0\x1b\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4\x19\xa9\x84\x14#c&uuV`\x894\x0e\xea\x0e\xa2\b\x19\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xa4!\u06f8\x9b:\aA\x90\x84\xad\x10\xc3\xc1]\xfe\x9b2\xd0\u008a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa4\"\xe4\xbf\v\xf7AG\u0309[\xed\x8f\x16\xd3\xce\xf3BaT\x89\x12\xef?b\xee\x116\x80\x00\u07d4\xa4%\x9f\x83E\xf7\u3a37+\x0f\xec,\xf7^2\x1f\xdaM\u0089g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xa4)\b\xe7\xfeS\x98\n\x9a\xbf@D\xe9W\xa5Kp\u973e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4)\xfa\x88s\x1f\xdd5\x0e\x8e\xcdn\xa5B\x96\xb6HO\u6549j\xc5\xc6-\x94\x86\a\x00\x00\xe0\x94\xa40\x99]\xdb\x18[\x98e\xdb\xe6%9\xad\x90\xd2.Ks\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa46\xc7TS\xcc\xcaJ\x1f\x1bb\xe5\u0123\r\x86\xdd\xe4\xbeh\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa47\xfen\xc1\x03\u028d\x15\x8fc\xb34\"N\u032c[>\xa3\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4;m\xa6\xcbz\xacW\x1d\xff'\xf0\x9d9\xf8F\xf57i\xb1\x89\x14\x99\x8f2\xacxp\x00\x00\u07d4\xa4;\x81\xf9\x93V\xc0\xaf\x14\x1a\x03\x01\rw\xbd\x04,q\xc1\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4>\x19G\xa9$+5Ua\xc3\n\x82\x9d\xfe\uc881Z\xf8\x89\xd2=\x99\x96\x9f\u0591\x80\x00\u07d4\xa4H\x9aP\xea\xd5\xd5DZ{\xeeM-U6\u00a7lA\xf8\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa4O\xe8\x00\xd9o\xca\xd7;qp\xd0\xf6\x10\u02cc\x06\x82\xd6\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa4T2\xa6\xf2\xac\x9dVW{\x93\x8a7\xfa\xba\xc8\xcc|F\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4f\xd7p\u0618\xd8\xc9\xd4\x05\xe4\xa0\xe5Q\xef\xaf\xcd\xe5<\xf9\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\xe0\x94\xa4g\a1\x17X\x93\xbb\xcf\xf4\xfa\x85\u0397\xd9O\xc5\x1cK\xa8\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4kC\x87\xfbM\xcc\xe0\x11\xe7nMsT}D\x81\xe0\x9b\xe5\x89Hz\x9a0E9D\x00\x00\u07d4\xa4l\xd27\xb6>\xeaC\x8c\x8e;e\x85\xf6y\xe4\x86\b2\xac\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4wy\u063c\x1c{\xce\x0f\x01\x1c\xcb9\xefh\xb8T\xf8\u078f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4\x82kl8\x82\xfa\xd0\xed\\\x8f\xbb%\xcc@\xccO3u\x9f\x89p\x1bC\xe3D3\xd0\x00\x00\u07d4\xa4\x87Y(E\x8e\xc2\x00]\xbbW\x8c\\\xd35\x80\xf0\xcf\x14R\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4\x9fR:\xa5\x13d\xcb\xc7\u0655\x16=4\xebY\r\xed/\b\x89\x90'B\x1b*\x9f\xbc\x00\x00\u07d4\xa4\xa4\x9f\v\xc8h\x8c\xc9\xe6\xdc\x04\xe1\xe0\x8dR\x10&\xe6Ut\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa4\xa7\xd3\x06\xf5\x10\xcdX5\x94(\xc0\xd2\xf7\xc3`\x9dVt\u05c9\xb5\x8c\xb6\x1c<\xcf4\x00\x00\u07d4\xa4\xa8:\a8y\x9b\x97\x1b\xf2\xdep\x8c.\xbf\x91\x1c\xa7\x9e\xb2\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa4\xb0\x9d\xe6\xe7\x13\xdciTnv\xef\n\xcf@\xb9O\x02A\xe6\x89\x11}\xc0b~\xc8p\x00\x00\xe0\x94\xa4\u04b4)\xf1\xadSI\xe3\x17\x04\x96\x9e\xdc_%\xee\x8a\xca\x10\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xa4\xd6\xc8.\u076eYG\xfb\xe9\xcd\xfb\xd5H\xae3\xd9\x1aq\x91\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4\xda4E\r\"\xec\x0f\xfc\xed\xe0\x00K\x02\xf7\x87.\xe0\xb7:\x89\x05\x0fafs\xf0\x83\x00\x00\xe0\x94\xa4\xddY\xab^Q}9\x8eI\xfaS\u007f\x89\x9f\xedL\x15\xe9]\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa4\xe6#E\x1e~\x94\xe7\u86e5\xed\x95\u0228:b\xff\xc4\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\xed\x11\xb0r\u061f\xb16u\x9f\u019bB\x8cH\xaa]L\xed\x89\x0e?\x15'\xa0<\xa8\x00\x00\u07d4\xa4\xfb\x14@\x9ag\xb4V\x88\xa8Y>\\\xc2\xcfYl\xedo\x11\x89a\t=|,m8\x00\x00\xe0\x94\xa5\x14\xd0\x0e\xddq\b\xa6\xbe\x83\x9ac\x8d\xb2AT\x18\x17A\x96\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x94\xa5\"\xde~\xb6\xae\x12PR*Q13\xa9;\xd4(IG\\\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa5$\xa8\xcc\xccIQ\x8d\x17\n2\x82p\xa2\xf8\x813\xfb\xaf]\x89\x0f\xf7\x02-\xac\x10\x8a\x00\x00\u07d4\xa59\xb4\xa4\x01\xb5\x84\xdf\xe0\xf3D\xb1\xb4\"\xc6UC\x16~.\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa5>\xadT\xf7\x85\n\xf2\x148\xcb\xe0z\xf6\x86'\x9a1[\x86\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa5C\xa0f\xfb2\xa8f\x8a\xa0sj\f\x9c\xd4\rx\t\x87'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa5gw\vj\xe3 \xbd\xdeP\xf9\x04\xd6c\xe7F\xa6\x1d\xac\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5h\xdbMW\xe4\xd6tb\xd73\u019a\x9e\x0f\xe2n!\x83'\x89;k\xff\x92f\xc0\xae\x00\x00\u07d4\xa5i\x8059\x1eg\xa4\x90\x13\xc0\x00 yY1\x14\xfe\xb3S\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4\xa5p\":\xe3\u02a8QA\x8a\x98C\xa1\xacU\xdbH$\xf4\xfd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa5s`\xf0\x02\xe0\xd6M-tE}\x8c\xa4\x85~\xe0\v\xcd\u07c9\x123\xe22\xf6\x18\xaa\x00\x00\u07d4\xa5u\xf2\x89\x1d\xcf\u0368<\\\xf0\x14t\xaf\x11\xee\x01\xb7-\u0089\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\xa5x;\xf342\xff\x82\xacI\x89\x85\xd7\xd4`\xaeg\xec6s\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xa5\x87MuF5\xa7b\xb3\x81\xa5\xc4\u01d2H:\xf8\xf2=\x1d\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\xa5\xa4\"\u007fl\xf9\x88%\xc0\u057a\xffS\x15u,\xcc\x1a\x13\x91\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa5\xabK\xd3X\x8fF\xcb'.V\xe9=\xee\u04c6\xba\x8bu=\x89HB\xf0A\x05\x87,\x80\x00\xe0\x94\xa5\xba\xd8e\t\xfb\xe0\xe0\xe3\xc0\xe9?m8\x1f\x1a\xf6\xe9\u0501\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa5\xc36\b;\x04\xf9G\x1b\x8cn\xd76y\xb7Mf\xc3c\uc263e\nL\x9d \xe2\x00\x00\u07d4\xa5\xcd\x129\x92\x19K4\xc4x\x13\x140;\x03\xc5IH\xf4\xb9\x89l\xfc\xc3\xd9\x1d\xa5c\x00\x00\u07d4\xa5\u0578\xb6-\x00-\xef\x92A7\x10\xd1;o\xf8\xd4\xfc}\u04c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xa5\xd9ni}F5\x8d\x11\x9a\xf7\x81\x9d\xc7\b\u007fj\xe4\u007f\xef\x8a\x03\x17\xbe\xe8\xaf3\x15\xa7\x80\x00\u07d4\xa5\xde^CO\xdc\xddh\x8f\x1c1\xb6\xfbQ,\xb1\x96rG\x01\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xa5\xe0\xfc<:\xff\xed=\xb6q\tG\xd1\xd6\xfb\x01\u007f>'m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5\xe9;I\xea|P\x9d\xe7\xc4Ml\xfe\xdd\xefY\x10\u07aa\xf2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5\xe9\xcdKt%]\"\xb7\u0672z\xe8\xddC\xedn\xd0%+\x89)\x8d\xb2\xf5D\x11\u0640\x00\xe0\x94\xa5\xf0\a{5\x1flP\\\xd5\x15\u07e6\xd2\xfa\u007f\\L\u0487\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xa5\xf0u\xfd@\x135W{f\x83\u0081\xe6\xd1\x01C-\xc6\xe0\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xa5\xfe,\xe9\u007f\x0e\x8c8V\xbe\r\xe5\xf4\u0732\xce]8\x9a\x16\x89\x01=\xb0\xb8\xb6\x86>\x00\x00\u07d4\xa5\xffb\"-\x80\xc0\x13\xce\xc1\xa0\xe8\x85\x0e\xd4\xd3T\xda\xc1m\x89\vA\a\\\x16\x8b\x18\x00\x00\u07d4\xa6\t\xc2m\xd3P\xc25\xe4K+\x9c\x1d\xdd\xcc\u0429\xd9\xf87\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa6\f\x12\tuO]\x87\xb1\x81\xdaO\b\x17\xa8\x18Y\xef\x9f\u0609\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\xa6\x10\x1c\x96\x1e\x8e\x1c\x15y\x8f\xfc\xd0\xe3 \x1dw\x86\xec7:\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xa6\x13Ei\x96@\x8a\xf1\xc2\xe9>\x17w\x88\xabU\x89^+2\x8a\x01Y\x19\xffG|\x88\xb8\x00\x00\u07d4\xa6\x18\x87\x81\x8f\x91J \xe3\x10w)\v\x83qZk-n\xf9\x89e\xea=\xb7UF`\x00\x00\u07d4\xa6\x1aT\xdfxJD\xd7\x1bw\x1b\x871u\t!\x13\x81\xf2\x00\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa6\x1c\u06ed\xf0K\x1eT\u0203\xde`\x05\xfc\xdf\x16\xbe\xb8\xeb/\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa69\xac\xd9k1\xbaS\xb0\u0407c\"\x9e\x1f\x06\xfd\x10^\x9d\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa6BP\x10\x04\xc9\x0e\xa9\xc9\xed\x19\x98\xba\x14\nL\xd6,o_\x89\r\x94\xfb\x8b\x10\xf8\xb1\x80\x00\u07d4\xa6D\xed\x92,\xc27\xa3\xe5\u0117\x9a\x99Tw\xf3nP\xbcb\x89\x1f\xa7=\x84]~\x96\x00\x00\u07d4\xa6F\xa9\\moY\xf1\x04\xc6T\x1dw`uz\xb3\x92\xb0\x8c\x89\u3bb5sr@\xa0\x00\x00\xe0\x94\xa6HL\u0184\xc4\xc9\x1d\xb5>\xb6\x8aM\xa4Zjk\xda0g\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa6N_\xfbpL,\x919\xd7~\xf6\x1d\x8c\u07e3\x1dz\x88\xe9\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\xe0\x94\xa6T&\xcf\xf3x\xed#%5\x13\xb1\x9fIm\xe4_\xa7\u13ca\x01\x86P\x12|\xc3\u0700\x00\x00\u07d4\xa6jIc\xb2\u007f\x1e\xe1\x93+\x17+\xe5\x96N\r:\xe5KQ\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4\xa6\u007f8\x81\x95eB:\xa8_>:\xb6\x1b\xc7c\u02eb\x89\u0749sw\xb0\"\u01be\b\x00\x00\u07d4\xa6\x8c14E\xc2-\x91\x9e\xe4l\xc2\xd0\xcd\xff\x04:uX%\x89\x04\x13t\xfd!\xb0\u0600\x00\u07d4\xa6\x8e\f0\u02e3\xbcZ\x88>T\x03 \xf9\x99\xc7\xcdU\x8e\\\x89a\x9237b\xa5\x8c\x80\x00\u07d4\xa6\x90\xf1\xa4\xb2\n\xb7\xba4b\x86 \u079c\xa0@\xc4<\x19c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa6\x9d|\xd1}HB\xfe\x03\xf6*\x90\xb2\xfb\xf8\xf6\xaf{\xb3\x80\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa6\xa0\x82R\xc8YQw\xcc.`\xfc'Y>#y\xc8\x1f\xb1\x89\x01\x16Q\xac>zu\x80\x00\u07d4\xa6\xa0\xdeB\x1a\xe5Om\x17(\x13\b\xf5dm/9\xf7w]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa6\xb2\xd5s)s`\x10,\a\xa1\x8f\xc2\x1d\xf2\xe7I\x9f\xf4\xeb\x89\xd9o\u0390\u03eb\xcc\x00\x00\xe0\x94\xa6\xc9\x10\xceMIJ\x91\x9c\u036a\xa1\xfc;\x82\xaat\xba\x06\u03ca\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa6\u3ea3\x8e\x10J\x1e'\xa4\xd8(i\xaf\xb1\xc0\xaen\xff\x8d\x89\x01\x11@\ueb4bq\x00\x00\u07d4\xa6\xee\xbb\xe4d\u04d1\x87\xbf\x80\u029c\x13\xd7 '\xec[\xa8\xbe\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa6\xf6+\x8a=\u007f\x11\"\a\x01\xab\x9f\xff\xfc\xb3'\x95\x9a'\x85\x89\x1bn)\x1f\x18\u06e8\x00\x00\u07d4\xa6\xf93\a\xf8\xbc\xe01\x95\xfe\u0387 C\xe8\xa0?{\xd1\x1a\x89\x9csK\xadQ\x11X\x00\x00\u07d4\xa7\x01\xdfy\xf5\x94\x90\x1a\xfe\x14DH^k \u00fd\xa2\xb9\xb3\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa7\x02L\xfdt,\x1e\xc1<\x01\xfe\xa1\x8d0B\xe6_\x1d]\xee\x8a\x02c\x11\x9a(\xab\u0430\x80\x00\u07d4\xa7\x18\xaa\xadY\xbf9\\\xba+#\xe0\x9b\x02\xfe\f\x89\x81bG\x8960<\x97\xe4hx\x00\x00\u07d4\xa7$|S\xd0Y\xeb|\x93\x10\xf6(\xd7\xfclj\nw?\b\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xa7%7c\xcfJu\u07d2\xca\x1evm\xc4\xee\x8a'E\x14{\x8a\x02F7p\xe9\n\x8fP\x00\x00\u07d4\xa7.\xe6f\u0133^\x82\xa5\x06\x80\x8bD<\xeb\xd5\xc62\xc7\u0749+^:\xf1k\x18\x80\x00\x00\u07d4\xa7DD\xf9\x0f\xbbT\xe5o:\u0276\xcf\u032aH\x19\xe4aJ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa7GC\x9a\xd0\u04d3\xb5\xa08a\xd7r\x962m\u8edd\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa7`{BW;\xb6\xf6\xb4\xd4\xf2<~*&\xb3\xa0\xf6\xb6\xf0\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\xa7i)\x89\n{G\xfb\x85\x91\x96\x01lo\u0742\x89\u03b7U\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u0794\xa7kt?\x98\x1bi0r\xa11\xb2+\xa5\x10\x96\\/\xef\u05c8\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xa7m?\x15bQ\xb7,\f\xcfKG\xa39<\xbdoI\xa9\u0149Hz\x9a0E9D\x00\x00\u07d4\xa7t(\xbc\xb2\xa0\xdbv\xfc\x8e\xf1\xe2\x0eF\x1a\n2\u016c\x15\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xa7u\x8c\xec\xb6\x0e\x8faL\u0396\x13~\xf7+O\xbd\awJ\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa7w^J\xf6\xa2:\xfa \x1f\xb7\x8b\x91^Q\xa5\x15\xb7\xa7(\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\xa7\u007f>\u1793\x88\xbb\xbb\"\x15\xc6#\x97\xb9e`\x13#`\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa7\x85\x9f\xc0\u007fun\xa7\xdc\xeb\xbc\xcdB\xf0X\x17X-\x97?\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa7\x96lH\x9fLt\x8az\u902a'\xa5t%\x17g\xca\xf9\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa7\xa3\xbba9\xb0\xad\xa0\f\x1f\u007f\x1f\x9fV\u0654\xbaM\x1f\xa8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa7\xa3\xf1S\xcd\u00c8!\xc2\f]\x8c\x82A\xb2\x94\xa3\xf8+$\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa7\xa5\x17\u05ed5\x82\v\t\u0517\xfa~U@\xcd\xe9IXS\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa7\xc9\u04c8\xeb\xd8s\xe6k\x17\x13D\x83\x97\xd0\xf3\u007f\x8b\u04e8\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa7\u073b\xa9\xb9\xbfgb\xc1EAlPjq\u3d17 \x9c\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xa7\xe7O\v\xdb'\x8f\xf0\xa8\x05\xa6Ha\x8e\xc5+\x16o\xf1\xbe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa7\xe87r\xbc \x0f\x90\x06\xaa*&\r\xba\xa8H=\xc5+0\x89\vB\xd56f7\xe5\x00\x00\xe0\x94\xa7\xef5\u0387\xed\xa6\u008d\xf2HxX\x15\x05>\xc9zPE\x8a\x01\x0f\f\xe9I\xe0\x0f\x93\x00\x00\u07d4\xa7\xf9\"\f\x80G\x82k\xd5\xd5\x18?Ngjmw\xbf\xed6\x89\bPh\x97k\xe8\x1c\x00\x00\u07d4\xa8\a\x10O'\x03\xd6y\xf8\u07af\xc4B\xbe\xfe\x84\x9eB\x95\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\f\xb1s\x8b\xac\b\xd4\xf9\xc0\x8bM\xef\xf5\x15T_\xa8XO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa8\x19\xd2\xec\xe1\"\xe0(\xc8\xe8\xa0J\x06M\x02\xb9\x02\x9b\b\xb9\x8965\u026d\xc5\u07a0\x00\x00\u0794\xa8%\xfdZ\xbby&\xa6|\xf3k\xa2F\xa2K\xd2{\xe6\xf6\xed\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4\xa8(U9\x86\x9d\x88\xf8\xa9aS7Uq}~\xb6Uv\xae\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa83\x82\xb6\xe1Rg\x97J\x85P\xb9\x8fqv\xc1\xa3S\xf9\xbe\x89\xbf\xfd\xaf/\xc1\xb1\xa4\x00\x00\xe0\x94\xa8DlG\x81\xa77\xacC(\xb1\xe1[\x8a\v?\xbb\x0f\xd6h\x8a\x04\x87\x94\xd1\xf2F\x19*\x00\x00\u07d4\xa8E[A\x17e\u0590\x1e1\x1erd\x03\t\x1eB\xc5f\x83\x89\xb7:\xec;\xfe\x14P\x00\x00\xe0\x94\xa8f\x13\xe6\u0124\xc9\xc5_\\\x10\xbc\xda2\x17]\u02f4\xaf`\x8a\x02C\xd6\xc2\xe3k\xe6\xae\x00\x00\u07d4\xa8m\xb0}\x9f\x81/G\x96b-@\xe0=\x13Xt\xa8\x8at\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa8\u007fz\xbdo\xa3\x11\x94(\x96x\xef\xb6<\xf5\x84\xee^*a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xa8\x80\u2a3f\x88\xa1\xa8&H\xb4\x01W\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa8\x9d\xf3HY\xed\xd7\xc8 \u06c8w@\xd8\xff\x9e\x15\x15|{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\xa4<\x00\x91\x00al\xb4\xaeN\x03?\x1f\xc5\xd7\xe0\xb6\xf1R\x89\u0548\xd0x\xb4?M\x80\x00\u07d4\xa8\xa7\b\xe8O\x82\u06c6\xa3U\x02\x19;Ln\xe9\xa7n\xbe\x8f\x897\b\xba\xed=h\x90\x00\x00\xe0\x94\xa8\xa7\xb6\x8a\u06b4\xe3\xea\xdf\xf1\x9f\xfaX\xe3J?\xce\xc0\xd9j\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xa8\xa8\xdb\xdd\x1a\x85\u047e\xee%i\xe9\x1c\xccM\t\xae\u007fn\xa1\x8a\x01:k+VHq\xa0\x00\x00\u07d4\xa8\xac\xa7H\xf9\xd3\x12\xect\u007f\x8bex\x14&\x94\xc7\xe9\xf3\x99\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\xb6[\xa3\x17\x1a?w\xa65\v\x9d\xaf\x1f\x8dU\xb4\xd2\x01\xeb\x89(b\xf3\xb0\xd2\"\x04\x00\x00\u07d4\xa8\xbe\xb9\x1c+\x99\u0216J\xa9[kJ\x18K\x12i\xfc4\x83\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u0794\xa8\xc0\xb0/\xaf\x02\xcbU\x19\u0768\x84\xde{\xbc\x8c\x88\xa2\u0681\x88\xe7\xc2Q\x85\x05\x06\x00\x00\u07d4\xa8\xc1\u05aaA\xfe=e\xf6{\xd0\x1d\xe2\xa8f\xed\x1e\u066eR\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\xa8\xca\xfa\xc3\"\x80\xd0!\x02\v\xf6\xf2\xa9x(\x83\u05ea\xbe\x12\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa8\xdb\v\x9b \x14S3A<;\fb\xf5\xf5.\u0544\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa8\xf3\u007f\n\xb3\xa1\xd4H\xa9\xe3\xce@\x96_\x97\xa6F\b:4\x89\x11\xe0\xe4\xf8\xa5\v\xd4\x00\x00\u07d4\xa8\xf8\x9d\xd5\xccnd\u05f1\xee\xac\xe0\a\x02\x02,\xd7\xd2\xf0=\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\xa9\x04v\xe2\xef\xdf\xeeO8{\x0f2\xa5\x06x\xb0\xef\xb5s\xb5\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa9\x14PF\xfa6(\xcf_\xd4\xc6\x13\x92{\xe51\xe6\xdb\x1f\u0749\x06\x12O\xee\x99;\xc0\x00\x00\u07d4\xa9\x14\u0375q\xbf\xd9=d\xdaf\xa4\xe1\b\xea\x13NP\xd0\x00\x89M\x878\x99G\x13y\x80\x00\xe0\x94\xa9\x1aZ{4\x1f\x99\xc55\x14N \xbe\x9ck;\xb4\u008eM\x8a\x01&u:\xa2$\xa7\v\x00\x00\u07d4\xa9%%Q\xa6$\xaeQ7\x19\u06beR\a\xfb\xef\xb2\xfdwI\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa9'\u050b\xb6\u02c1K\xc6\t\xcb\u02a9\x15\x1f]E\x9a'\xe1\x89\x0e\xb95\t\x00d\x18\x00\x00\u07d4\xa9)\u023dq\xdb\f0\x8d\xac\x06\b\n\x17G\xf2\x1b\x14e\xaa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa9K\xbb\x82\x14\u03cd\xa0\xc2\xf6h\xa2\xacs\xe8bHR\x8dK\x894\n\xad!\xb3\xb7\x00\x00\x00\u07d4\xa9Q\xb2D\xffP\u03eeY\x1d^\x1a\x14\x8d\xf6\xa98\xef*\x1a\x89^\x00\x15\x84\xdf\xcfX\x00\x00\u07d4\xa9`\xb1\xca\xdd;\\\x1a\x8el\xb3\xab\xca\xf5.\xe7\xc3\xd9\xfa\x88\x89R\x8b\xc3T^Rh\x00\x00\u07d4\xa9a\x17\x1fSB\xb1s\xddp\xe7\xbf\xe5\xb5\xca#\x8b\x13\xbc\u0749\xb8'\x94\xa9$O\f\x80\x00\u07d4\xa9u\xb0w\xfc\xb4\u030e\xfc\xbf\x83\x84Y\xb6\xfa$:AY\u0589\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xa9{\xeb:H\xc4_\x15((L\xb6\xa9_}\xe4S5\x8e\u018a\x06\x90\x83l\n\xf5\xf5`\x00\x00\u07d4\xa9~\a!DI\x9f\xe5\xeb\xbd5J\xcc~~\xfbX\x98]\b\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xa9\x86v/zO)O.\v\x172y\xad,\x81\xa2\"4X\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa9\x8f\x10\x985\xf5\xea\xcd\x05Cd|4\xa6\xb2i\xe3\x80/\xac\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa9\x97\xdf\u01d8j'\x05\bH\xfa\x1cd\u05e7\xd6\xe0z\u0322\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\xa9\x99\x91\u03bd\x98\xd9\xc88\xc2_zt\x16\xd9\xe2D\xca%\r\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa9\xa1\xcd\xc3;\xfd7o\x1c\rv\xfbl\x84\xb6\xb4\xac'Mh\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa9\xa8\xec\xa1\x1a#\xd6F\x89\xa2\xaa>A}\xbb=3k\xb5\x9a\x89\x0e4S\xcd;g\xba\x80\x00\u07d4\xa9\xac\xf6\x00\b\x1b\xb5[\xb6\xbf\xba\xb1\x81_\xfcN\x17\xe8Z\x95\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa9\xad\x19&\xbcf\xbd\xb31X\x8e\xa8\x197\x88SM\x98,\x98\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xa9\xaf!\xac\xbeH/\x811\x89j\"\x806\xbaQ\xb1\x94S\u00c9\x02\xb5\xe0!\x98\f\xc1\x80\x00\xe0\x94\xa9\xb2\xd2\xe0IN\xab\x18\xe0}7\xbb\xb8V\xd8\x0e\x80\xf8L\u04ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa9\xbaoA;\x82\xfc\xdd\xf3\xaf\xfb\xbd\u0412\x87\xdc\xf5\x04\x15\u0289\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa9\xbe\x88\xad\x1eQ\x8b\v\xbb\x02J\xb1\xd8\xf0\xe7?y\x0e\fv\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xa9\xbf\xc4\x10\xdd\xdb q\x1eE\xc0s\x87\xea\xb3\n\x05N\x19\xac\x89>\x99`\x1e\xdfNS\x00\x00\u07d4\xa9\u0522\xbc\xbe[\x9e\bi\xd7\x0f\x0f\xe2\xe1\u05aa\xcdE\xed\u0149\n\xc6\xe7z\xb6c\xa8\x00\x00\xe0\x94\xa9\xd6KO;\xb7\x85\a\"\xb5\x8bG\x8b\xa6\x917^\"NB\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa9\xd6\xf8q\xcax\x1au\x9a \xac:\u06d7,\xf1()\xa2\b\x892$\xf4'#\xd4T\x00\x00\xe0\x94\xa9\xdc\x04$\u0196\x9dy\x83X\xb3\x93\xb1\x93:\x1fQ\xbe\xe0\n\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa9\xe1\x94f\x1a\xacpN\xe9\u07a0C\x97N\x96\x92\xde\xd8J]\x89\x1a&\xa5\x14\"\xa0p\x00\x00\u07d4\xa9\xe2\x837\xe65q\x93\xd9\xe2\xcb#k\x01\xbeD\xb8\x14'\u07c9wC\"\x17\xe6\x83`\x00\x00\u07d4\xa9\xe6\xe2^ekv%Xa\x9f\x14z!\x98[\x88t\xed\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa9\xe9\xdb\xcez,\xb06\x94y\x98\x97\xbe\xd7\xc5M\x15_\u06a8\x89\n\xb5\xae\x8f\u025de\x80\x00\u07d4\xa9\xed7{}n\xc2Yq\xc1\xa5\x97\xa3\xb0\xf3\xbe\xadW\u024f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xaa\x02\x00\xf1\xd1~\x9cT\xda\x06G\xbb\x969]W\xa7\x858\u06099>\xf1\xa5\x12|\x80\x00\x00\u07d4\xaa\f\xa3ss7\x17\x8a\f\xaa\xc3\t\x9cXK\x05lV0\x1c\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xaa\x13kG\x96+\xb8\xb4\xfbT\r\xb4\xcc\xf5\xfd\xd0B\xff\xb8\u03c9\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\xaa\x14B-o\n\xe5\xa7X\x19N\xd1W\x80\xc88\xd6\u007f\x1e\xe1\x8a\x06\t2\x05lD\x9d\xe8\x00\x00\u07d4\xaa\x16&\x9a\xac\x9c\r\x800h\xd8/\u01d1Q\xda\xdd3Kf\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xaa\x16p&\u04da\xb7\xa8V5\x94N\xd9\xed\xb2\xbf\xeb\xa1\x18P\x8a\x01\xc1\xd5\xe2\x1bO\xcfh\x00\x00\u07d4\xaa\x1b7h\xc1m\x82\x1fX\x0ev\xc8\xe4\xc8\xe8m}\u01c8S\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xaa\x1d\xf9.Q\xdf\xf7\v\x19s\xe0\xe9$\xc6b\x87\xb4\x94\xa1x\x89\x1c\xf8J0\xa0\xa0\xc0\x00\x00\u07d4\xaa,g\x00\x96\xd3\xf990S%B~\xb9U\xa8\xa6\r\xb3\u0149l\x95Y\x06\x99#-\x00\x00\u07d4\xaa15\xcbT\xf1\x02\xcb\xef\xe0\x9e\x96\x10:\x1ayg\x18\xffT\x89\x03\"\"\xd9\xc31\x94\x00\x00\u07d4\xaa2\x1f\xdb\xd4I\x18\r\xb8\xdd\xd3O\x0f\xe9\x06\xec\x18\xee\t\x14\x89%\"H\u07b6\xe6\x94\x00\x00\xe0\x94\xaa9%\xdc\"\v\xb4\xae!w\xb2\x880x\xb6\xdc4l\xa1\xb2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xaa?)`\x1a\x131t^\x05\xc4(0\xa1^q\x93\x8ab7\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94\xaaG\xa4\xff\xc9y622\u025b\x99\xfa\xda\x0f'4\xb0\xae\xee\x8a\x01\xb8H\x9d\xf4\xdb\xff\x94\x00\x00\u07d4\xaaI=?O\xb8fI\x1c\xf8\xf8\x00\xef\xb7\xe22N\xd7\xcf\xe5\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xaaV\xa6]\u012b\xb7/\x11\xba\xe3+o\xbb\aDG\x91\xd5\u0249(\x94\xe9u\xbfIl\x00\x00\xe0\x94\xaaZ\xfc\xfd\x83\t\xc2\u07dd\x15\xbe^jPN}pf$\u014a\x01<\xf4\"\xe3\x05\xa17\x80\x00\u07d4\xaa\x8e\xb0\x82;\a\xb0\xe6\xd2\n\xad\xda\x0e\x95\xcf85\xbe\x19.\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xaa\x91#~t\r%\xa9/\u007f\xa1F\xfa\xa1\x8c\xe5m\xc6\xe1\xf3\x892$\xf4'#\xd4T\x00\x00\u07d4\xaa\x96\x0e\x10\xc5#\x91\xc5N\x158|\xc6z\xf8'\xb51m\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\x9b\xd4X\x955\xdb'\xfa+\xc9\x03\xca\x17\xd6y\xddeH\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\xa8\xde\xfe\x11\xe3a?\x11\x06\u007f\xb9\x83bZ\b\x99Z\x8d\xfc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaa\xaa\xe6\x8b2\x14\x02\xc8\xeb\xc14h\xf3A\xc6<\f\xf0?\u0389Rf<\u02b1\xe1\xc0\x00\x00\u07d4\xaa\xad\x1b\xaa\xdeZ\xf0N+\x17C\x9e\x93Y\x87\xbf\x8c+\xb4\xb9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xaa\xb0\n\xbfX(\xd7\xeb\xf2kG\u03ac\u0378\xba\x032Qf\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xaa\xbd\xb3\\\x15\x14\x98J\x03\x92\x13y?3E\xa1h\xe8\x1f\xf1\x89\x10\xca\u0216\xd29\x00\x00\x00\u07d4\xaa\xca`\xd9\xd7\x00\u7156\xbb\xbb\xb1\xf1\xe2\xf7\x0fF'\xf9\u060965\xbbw\xcbK\x86\x00\x00\u07d4\xaa\xce\u0629V;\x1b\xc3\x11\xdb\xdf\xfc\x1a\xe7\xf5u\x19\xc4D\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\u04b7\xf8\x10f\x95\a\x8el\x13\x8e\xc8\x1at\x86\xaa\xca\x1e\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaa\xe6\x1eC\xcb\r\f\x96\xb3\x06\x99\xf7~\x00\xd7\x11\u0423\x97\x9b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaa\xe72\xed\xa6Y\x88\u00e0\f\u007fG/5\x1cF;\x1c\x96\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\xf0#\xfe\U0009091b\xb7\x8b\xb7\xab\xc9]f\x9cP\xd5(\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xaa\xf5\xb2\a\xb8\x8b\r\xe4\xac@\xd7G\xce\xe0n\x17-\xf6\xe7E\x8a\x06\xa7\xb7\x1d\u007fQ\u0410\x00\x00\u07d4\xaa\xf9\xeeK\x88lm\x1e\x95Io\xd2t#[\xf4\xec\xfc\xb0}\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xaa\xfb{\x01:\xa1\xf8T\x1c~2{\xf6P\xad\xbd\x19L \x8f\x89I\x9e\t-\x01\xf4x\x00\x00\xe0\x94\xab\t\x863\xee\xee\f\xce\xfd\xf62\xf9WTV\xf6\u0740\xfc\x86\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xab\f\xedv.\x16a\xfa\xe1\xa9*\xfb\x14\b\x88\x94\x13yH%\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xab\x14\xd2!\xe3=TF)\x19\x8c\u0416\xedc\u07e2\x8d\x9fG\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xab \x9f\u0729y\u0426G\x01\n\xf9\xa8\xb5/\xc7\xd2\r\x8c\u044a\x01\xee\xe2S,|-\x04\x00\x00\u07d4\xab'\xbax\xc8\xe5\xe3\xda\xef1\xad\x05\xae\xf0\xff\x03%r\x1e\b\x89\x19^\xce\x00n\x02\xd0\x00\x00\u07d4\xab(q\xe5\a\u01fe9eI\x8e\x8f\xb4b\x02Z\x1a\x1cBd\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xab8a\"o\xfe\xc1(\x91\x87\xfb\x84\xa0\x8e\xc3\xed\x042d\xe8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab=\x86\xbc\x82\x92~\f\xd4!\xd1F\xe0\u007f\x91\x93'\xcd\xf6\xf9\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xab>b\xe7z\x8b\"^A\x15\x92\xb1\xaf0\aR\xfeA$c\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xab>x)K\xa8\x86\xa0\xcf\xd5\xd3H\u007f\xb3\xa3\a\x8d3\x8dn\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xab@\x04\xc0@?~\xab\xb0\xeaXo!!V\xc4 =g\xf1\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xabAo\xe3\rX\xaf\xe5\xd9EL\u007f\xce\u007f\x83\v\xccu\x03V\x89\x0657\x01\xc6\x05\u06c0\x00\u07d4\xabEr\xfb\xb1\xd7+W]i\xecj\xd1s3\x87>\x85R\xfc\x89lj\xc5L\xdahG\x00\x00\u07d4\xabZy\x01av2\ts\xe8\xcd8\xf67U0\x02%1\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab]\xfc\x1e\xa2\x1a\xdcB\u03cc?n6\x1e$?\xd0\xdaa\xe5\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xabke\xea\xb8\xdf\xc9\x17\xec\x02Q\xb9\xdb\x0e\u03e0\xfa\x03(I\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xabp\x91\x93.K\u00dd\xbbU#\x80\u0293O\xd7\x16m\x1en\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\xabt\x16\xff2%IQ\u02fcbN\xc7\xfbE\xfc~\u02a8r\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xab|B\xc5\xe5-d\x1a\a\xadu\t\x9cb\x92\x8b\u007f\x86b/\x89\x126\x1a\xa2\x1d\x14\xba\x00\x00\u07d4\xab}T\xc7\xc6W\x0e\xfc\xa5\xb4\xb8\xcep\xf5*Ws\xe5\xd5;\x89\x0f(:\xbe\x9d\x9f8\x00\x00\u07d4\xab~\v\x83\xed\x9aBLm\x1ejo\x87\xa4\xdb\xf0d\t\xc7\u0589\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xab\x84\xa0\xf1G\xad&T\x00\x00+\x85\x02\x9aA\xfc\x9c\xe5\u007f\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab\x93\xb2n\xce\n\n\xa2\x13e\xaf\xed\x1f\xa9\xae\xa3\x1c\xd5Dh\x89W+{\x98sl \x00\x00\u07d4\xab\x94\x8aJ\xe3y\\\xbc\xa11&\xe1\x92S\xbd\xc2\x1d:\x85\x14\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xab\x9a\xd3n\\t\xce.\x969\x9fW\x83\x941\xd0\u77d6\xab\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\xab\xb2\xe6\xa7*@\xban\xd9\b\u037c\xec\x91\xac\xfdwx0\xd1dcG\x8a\xe0\xfcw \x89\a?u\u0460\x85\xba\x00\x00\xe0\x94\xab\u071f\x1b\xcfM\x19\xee\x96Y\x100\xe7r\xc340/}\x83\x8a\b~^\x11\xa8\x1c\xb5\xf8\x00\x00\u07d4\xab\xde\x14{*\xf7\x89\ua946T~f\xc4\xfa&d\xd3(\xa4\x89\rk`\x81\xf3L\x12\x80\x00\xe0\x94\xab\xe0|\xedj\xc5\xdd\xf9\x91\xef\xf6\xc3\xda\"jt\x1b\xd2C\xfe\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xab\xf1/\xa1\x9e\x82\xf7lq\x8f\x01\xbd\xca\x00\x03gE#\xef0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xab\xf7(\u03d3\x12\xf2!(\x02NpF\xc2Q\xf5\xdcY\x01\xed\x8a\x06A\xe8\xa15c\xd8\xf8\x00\x00\u07d4\xab\xf8\xff\xe0p\x8a\x99\xb5(\xcc\x1e\xd4\xe9\xceK\r\x060\xbe\x8c\x89z\xb5\u00ae\xee\xe68\x00\x00\u07d4\xab\xfc\xf5\xf2P\x91\xceW\x87_\xc6t\xdc\xf1\x04\xe2\xa7=\xd2\xf2\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xab\xfe\x93d%\xdc\u01f7K\x95P\x82\xbb\xaa\xf2\xa1\x1dx\xbc\x05\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xac\x02OYO\x95X\xf0ICa\x8e\xb0\xe6\xb2\xeeP\x1d\xc2r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\x12*\x03\xcd\x05\x8c\x12._\xe1{\x87/Hw\xf9\u07d5r\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xac\x14.\xda\x11W\xb9\xa9\xa6C\x90\xdf~j\xe6\x94\xfa\u0249\x05\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\x1d\xfc\x98Kq\xa1\x99)\xa8\x1d\x81\xf0J|\xbb\x14\a7\x03\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xac!\xc1\xe5\xa3\xd7\xe0\xb5\x06\x81g\x9d\xd6\u01d2\xdb\u0287\xde\u02ca\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xac(\x89\xb5\x96o\f\u007f\x9e\xdbB\x89\\\xb6\x9d\x1c\x04\xf9#\xa2\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xac(\xb5\xed\xea\x05\xb7o\x8c_\x97\bEA'|\x96ijL\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xac,\x8e\t\xd0d\x93\xa68XC{\xd2\v\xe0\x19bE\x03e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xac.vm\xac?d\x8fcz\xc6q?\u0770h\xe4\xa4\xf0M\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\xac9\x00)\x8d\xd1M|\xc9mJ\xbbB\x8d\xa1\xba\xe2\x13\xff\xed\x8a\x05<\xa1)t\x85\x1c\x01\x00\x00\u07d4\xac=\xa5&\xcf\u0388)s\x02\xf3LI\xcaR\r\xc2q\xf9\xb2\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xacD`\xa7nm\xb2\xb9\xfc\xd1R\xd9\xc7q\x8d\x9a\xc6\xed\x8co\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xacJ\xcf\xc3n\xd6\tJ'\xe1\x18\xec\xc9\x11\xcdG>\x8f\xb9\x1f\x89a\x91>\x14@<\f\x00\x00\u07d4\xacL\xc2V\xaet\xd6$\xac\xe8\r\xb0x\xb2 \u007fW\x19\x8fk\x89lyt\x12?d\xa4\x00\x00\u07d4\xacN\xe9\xd5\x02\xe7\xd2\xd2\xe9\x9eY\xd8\xca}_\x00\xc9KM\u058965\u026d\xc5\u07a0\x00\x00\u07d4\xacR\xb7~\x15fH\x14\xf3\x9eO'\x1b\xe6A0\x8d\x91\xd6\u0309\v\xed\x1d\x02c\xd9\xf0\x00\x00\u07d4\xacY\x99\xa8\x9d-\u0486\u0568\fm\xee~\x86\xaa\xd4\x0f\x9e\x12\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xac_br1H\r\r\x950.m\x89\xfc2\xcb\x1dO\xe7\xe3\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xac`\x8e+\xac\x9d\xd2\a(\u0494~\xff\xbb\xbf\x90\n\x9c\xe9K\x8a\x01EK\r\xb3uh\xfc\x00\x00\u07d4\xacm\x02\xe9\xa4k7\x9f\xacJ\u0271\u05f5\xd4{\xc8P\xce\x16\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xacoh\xe87\xcf\x19a\xcb\x14\xabGDm\xa1h\xa1m\u0789\x89Hz\x9a0E9D\x00\x00\u07d4\xacw\xbd\xf0\x0f\u0558[]\xb1+\xbe\xf8\x008\n\xbc*\x06w\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xac~\x03p'#\xcb\x16\xee'\xe2-\u0438\x15\xdc-\\\xae\x9f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xac\x8bP\x9a\xef\xea\x1d\xbf\xaf+\xb35\x00\xd6W\vo\xd9mQ\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xac\x8e\x87\xdd\xda^x\xfc\xbc\xb9\xfa\u007f\xc3\xce\x03\x8f\x9f}.4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xac\x9f\xffh\xc6\x1b\x01\x1e\xfb\xec\xf08\xedr\u06d7\xbb\x9er\x81\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xac\xa1\xe6\xbcd\xcc1\x80\xf6 \xe9M\u0171\xbc\xfd\x81X\xe4]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\xa2\xa883\v\x170-\xa71\xd3\r\xb4\x8a\x04\xf0\xf2\a\xc1\x89Hz\x9a0E9D\x00\x00\u07d4\xac\xaa\xdd\xcb\xf2\x86\xcb\x0e!]\xdaUY\x8f\u007f\xf0\xf4\xad\xa5\u018965\u026d\xc5\u07a0\x00\x00\u07d4\xac\xb9C8UK\u0108\u0308\xae-\x9d\x94\b\rk\u07c4\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xac\xbc-\x19\xe0l;\xab\xbb[o\x05+k\xf7\xfc7\xe0r)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\xbd\x18U\x89\xf7\xa6\x8ag\xaaK\x1b\xd6Pw\xf8\xc6NN!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\xc0bp,Ya]4D\xefb\x14\xb8\x86+\x00\x9a\x02\xed\x89QO\xcb$\xff\x9cP\x00\x00\u07d4\xac\xc0\x90\x9f\xda.\xa6\xb7\xb7\xa8\x8d\xb7\xa0\xaa\xc8h\t\x1d\xdb\xf6\x89\x013v_\x1e&\u01c0\x00\u07d4\xac\xc1\u01c7\x86\xabM+;'q5\xb5\xba\x12>\x04\x00Hk\x89\x04E\x91\xd6\u007f\xec\xc8\x00\x00\u07d4\xac\xc4j*U\\t\xde\u0522\xbd\tN\x82\x1b\x97\x84;@\xc0\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xac\u015f;0\xce\xff\xc5da\xcc[\x8d\xf4\x89\x02$\x0e\x0e{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\xce\x01\xe0\xa7\x06\x10\xdcp\xbb\x91\xe9\x92o\xa9\x95\u007f7/\xba\x89\x1d\x1c_>\xda \xc4\x00\x00\u07d4\xac\xd8\u0751\xf7\x14vLEg|c\xd8R\xe5n\xb9\xee\xce.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\u2af6;\x06\x04@\x9f\xbd\xe3\xe7\x16\u0487mD\xe8\xe5\u0749\b=lz\xabc`\x00\x00\xe0\x94\xac\xec\x91\xefiA\xcfc\v\xa9\xa3\u71e0\x12\xf4\xa2\xd9\x1d\u050a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xad\nJ\xe4x\xe9cn\x88\xc6\x04\xf2B\xcfT9\xc6\xd4V9\x89\xbe\xd1\xd0&=\x9f\x00\x00\x00\u07d4\xad\x17\x99\xaa\xd7`+E@\u0343/\x9d\xb5\xf1\x11P\xf1hz\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xad\x1dh\xa08\xfd%\x86\x06~\xf6\xd15\xd9b\x8ey\xc2\xc9$\x89\xfe\t\xa5'\x9e*\xbc\x00\x00\u07d4\xad*\\\x00\xf9#\xaa\xf2\x1a\xb9\xf3\xfb\x06n\xfa\n\x03\xde/\xb2\x8965\xbbw\xcbK\x86\x00\x00\u07d4\xad5e\xd5+h\x8a\xdd\xed\b\x16\x8b-8r\xd1}\n&\xae\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xad7|\xd2^\xb5>\x83\xae\t\x1a\n\x1d+E\x16\xf4\x84\xaf\u0789i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xadAM)\xcb~\xe9s\xfe\xc5N\"\xa3\x88I\x17\x86\xcfT\x02\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xadD5~\x01~$OGi1\u01f8\x18\x9e\xfe\xe8\n]\n\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xadW\xaa\x9d\x00\xd1\fC\x9b5\xef\xcc\v\xec\xac.9U\xc3\x13\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xadY\xa7\x8e\xb9\xa7J\u007f\xbd\xae\xfa\xfa\x82\xea\u0684u\xf0\u007f\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xadZ\x8dV\x01L\xfc\xb3`\xf4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xadr\x81!\x87?\x04V\xd0Q\x8b\x80\xabe\x80\xa2\x03pe\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xads,\x97e\x93\xee\xc4x;N.\xcdy9yx\v\xfe\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xad}\xd0S\x85\x9e\xdf\xf1\xcbo\x9d*\xcb\xedm\xd5\xe32Bo\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xad\x80\xd8e\xb8\\4\xd2\xe6IK.z\xef\xeak\x9a\xf1\x84\u06c9\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xad\x8b\xfe\xf8\u018aH\x16\xb3\x91o5\xcb{\xfc\xd7\xd3\x04\tv\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xad\x8eH\xa3wi]\xe0\x146:R:(\xb1\xa4\fx\xf2\b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xad\x91\n#\u0585\x06\x13eJ\xf7\x863z\u04a7\bh\xacm\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xad\x92~\x03\xd1Y\x9ax\xca+\xf0\xca\u04a1\x83\xdc\xebq\xea\xc0\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xad\x92\xca\x06n\xdb|q\x1d\xfc[\x16a\x92\xd1\xed\xf8\xe7q\x85\x8a\a\x9f\x90\\o\xd3N\x80\x00\x00\u07d4\xad\x94#_\u00f3\xf4z$\x13\xaf1\u8111I\b\xef\fE\x89\x1b\x1b\x01B\xd8\x15\x84\x00\x00\u07d4\xad\x9e\x97\xa0H/5:\x05\xc0\xf7\x92\xb9w\xb6\xc7\xe8\x11\xfa_\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xad\x9fL\x89\n;Q\x1c\xeeQ\xdf\xe6\xcf\xd7\xf1\t;vA,\x89\x1bv|\xbf\xeb\f\xe4\x00\x00\u07d4\xad\xaa\x0eT\x8c\x03Z\xff\xedd\xcag\x8a\x96?\xab\xe9\xa2k\xfd\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\xad\xb9H\xb1\xb6\xfe\xfe }\xe6^\x9b\xbc-\xe9\x8e`]\vW\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xad\xc1\x9e\xc85\xaf\xe3\u5347\u0713\xa8\xa9!<\x90E\x13&\x89j\xdb\xe54\"\x82\x00\x00\x00\u07d4\xad\xc8\"\x8e\xf9(\xe1\x8b*\x80}\x00\xfb1\xfcgX\x15\xaa\x00\x00\u07d4\xad\xff\r\x1d\v\x97G\x1ev\u05c9\xd2\u470at\xf9\xbdT\xff\x89e\xea=\xb7UF`\x00\x00\u07d4\xae\x06,D\x86\x18d0u\xdez\x0004-\xce\xd6=\xba\u05c9,\xc6\u034c\u0082\xb3\x00\x00\xe0\x94\xae\x10\xe2z\x01O\r0k\xaf&mH\x97\u021a\xee\xe2\xe9t\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xae\x12k8,\xf2W\xfa\xd7\xf0\xbc}\x16)~T\xccrg\u0689\x10CV\x1a\x88)0\x00\x00\u07d4\xae\x13\xa0\x85\x11\x11\x0f2\xe5;\xe4\x12xE\xc8C\xa1\xa5|{\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xae\x17\x9aF\r\xb6c&t=$\xe6u#\xa5{$m\xaf\u007f\x8a\x01\x00\a\xae|\xe5\xbb\xe4\x00\x00\u07d4\xae\"(ey\x90y\xaa\xf4\xf0gJ\f\u06ab\x02\xa6\xd5p\xff\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xae#\x9a\xcf\xfdN\xbe.\x1b\xa5\xb4\x17\x05r\xdcy\xcce3\xec\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xae/\x9c\x19\xacv\x13e\x94C#\x93\xb0G\x1d\b\x90!d\u04c9%\xdf\x05\u01a8\x97\xe4\x00\x00\u07d4\xae4\x86\x1d4\"S\x19O\xfcfR\xdf\xdeQ\xabD\xca\xd3\xfe\x89\x19F\bhc\x16\xbd\x80\x00\u07d4\xae6\xf7E!!\x91>\x80\x0e\x0f\xcd\x1ae\xa5G\x1c#\x84o\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\xae?\x98\xa4C\xef\xe0\x0f>q\x1dR]\x98\x94\u071aa\x15{\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\xe0\x94\xaeG\xe2`\x9c\xfa\xfe6\x9df\xd4\x15\xd99\xde\x05\b\x1a\x98r\x8a\x05\xba\xec\xf0%\xf9\xb6P\x00\x00\u07d4\xaeO\x12.5\xc0\xb1\xd1\xe4\x06\x92\x91E|\x83\xc0\u007f\x96_\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaePU\x81L\xb8\xbe\f\x11{\xb8\xb1\xc8\u04b6;F\x98\xb7(\x89\x01\xbc\x93.\xc5s\xa3\x80\x00\u07d4\xaeS\x8cs\u0173\x8d\x8dXM~\xbd\xad\xef\xb1\\\xab\xe4\x83W\x896'\xe8\xf7\x127<\x00\x00\u07d4\xaeW\xcc\x12\x9a\x96\xa8\x99\x81\xda\xc6\r/\xfb\x87}]\xc5\xe42\x89<:#\x94\xb3\x96U\x00\x00\u07d4\xaeZ\xa1\xe6\u00b6\x0fo\xd3\xef\xe7!\xbbJq\x9c\xbe=o]\x89+$\u01b5Z^b\x00\x00\u07d4\xae\\\x9b\xda\xd3\xc5\u0221\"\x04D\xae\xa5\xc2)\xc1\x83\x9f\x1dd\x89\x19\xe2\xa4\xc8\x18\xb9\x06\x00\x00\u07d4\xae\\\xe35Z{\xa9\xb32v\f\tP\u00bcE\xa8_\xa9\xa0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xae]\"\x1a\xfc\xd3\u0493U\xf5\b\xea\xdf\xca@\x8c\xe3<\xa9\x03\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xaec[\xf781\x11\x9d-)\xc0\xd0O\xf8\xf8\xd8\u0425zF\x89Hz\x9a0E9D\x00\x00\xe0\x94\xaed\x81U\xa6X7\x0f\x92\x9b\xe3\x84\xf7\xe0\x01\x04~I\xddF\x8a\x02\xdf$\xae2\xbe D\x00\x00\xe0\x94\xaeo\fs\xfd\xd7|H\x97'Q!t\u0675\x02\x96a\x1cL\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xaep\xe6\x9d,J\n\xf8\x18\x80{\x1a'\x05\xf7\x9f\u0435\xdb\u01095e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xaew9\x12N\xd1S\x05%\x03\xfc\x10\x14\x10\xd1\xff\xd8\xcd\x13\xb7\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xaex\xbb\x84\x919\xa6\xba8\xae\x92\xa0\x9ai`\x1c\xc4\xcbb\u0449\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xae\x84\"\x10\xf4M\x14\u0124\u06d1\xfc\x9d;;P\x01O{\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xae\x84.\x81\x85\x8e\xcf\xed\xf6Plhm\xc2\x04\xac\x15\xbf\x8b$\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xae\x89T\xf8\xd6\x16m\xe5\a\xcfa)}\x0f\xc7\xcak\x9eq(\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xae\x9e\xcdk\u0755.\xf4\x97\xc0\x05\n\u0aca\x82\xa9\x18\x98\u0389\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\xae\x9f\\?\xbb\xe0\u027c\xbf\x1a\xf8\xfft\xea(\v:]\x8b\b\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xae\xad\x88\u0589Ak\x1c\x91\xf26D!7[}\x82\xd0RR\n\xfb\\Wm\x9f~\xb9>\u048a\r\xd0A \xba\t\xcf\xe6\x00\x00\u07d4\xae\xc2\u007f\xf5\xd7\xf9\xdd\u0691\x18?F\xf9\xd5%C\xb6\xcd+/\x89\x18e\x01'\xcc=\xc8\x00\x00\u07d4\xae\xe4\x9dh\xad\xed\xb0\x81\xfdCpZ_x\xc7x\xfb\x90\xdeH\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xae\xf5\xb1\"X\xa1\x8d\xec\a\xd5\xec.1et\x91\x9dy\xd6\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\xae\xfc\xfe\x88\xc8&\xcc\xf11\xd5N\xb4\ua7b8\x0ea\xe1\xee%\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xaf\x06\xf5\xfam\x12\x14\xecC\x96}\x1b\xd4\xdd\xe7J\xb8\x14\xa98\x89\x04\xc5>\xcd\xc1\x8a`\x00\x00\u07d4\xaf\x11H\xefl\x8e\x10=u0\xef\xc9\x16y\u026c'\x00\t\x93\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaf >\"\x9d~mA\x9d\xf47\x8e\xa9\x87\x15Q_c\x14\x85\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xaf X\xc7(,\xf6|\x8c<\xf90\x13<\x89a|\xe7])\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4\xaf&\xf7\u01bfE> x\xf0\x89S\u4c80\x04\xa2\xc1\xe2\t\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xaf0\x87\xe6.\x04\xbf\x90\rZT\xdc>\x94bt\u0692B;\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf6\x14\u0736\x8a6\xe4ZN\x91\x1ebybG\"-Y[\x89z\x81\x06_\x11\x03\xbc\x00\x00\u07d4\xaf6\x15\u01c9\u0431\x15*\xd4\xdb%\xfe]\xcf\"(\x04\xcfb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaf<\xb5\x96Y3\xe7\xda\u0603i;\x9c>\x15\xbe\xb6\x8aHs\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xafD\x93\xe8R\x1c\xa8\x9d\x95\xf5&|\x1a\xb6?\x9fEA\x1e\x1b\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xafL\xf4\x17\x85\x16\x1fW\x1d\f\xa6\x9c\x94\xf8\x02\x1fA)N\u028a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xafR\x9b\xdbE\x9c\xc1\x85\xbe\xe5\xa1\u014b\xf7\xe8\xcc\xe2\\\x15\r\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xafg\xfd>\x12\u007f\xd9\xdc6\xeb?\xcdj\x80\u01feOu2\xb2\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xafw\x1094Z40\x01\xbc\x0f\x8aY#\xb1&\xb6\rP\x9c\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\xaf\u007fy\xcbAZ\x1f\xb8\u06fd\tF\a\xee\x8dA\xfb|Z;\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xaf\x87\xd27\x1e\xf3x\x95\u007f\xbd\x05\xba/\x1df\x93\x1b\x01\u2e09%\xf2s\x93=\xb5p\x00\x00\u07d4\xaf\x88\x0f\xc7V}U\x95\xca\xcc\xe1\\?\xc1L\x87B\xc2l\x9e\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xaf\x8e\x1d\xcb1L\x95\r6\x87CM0\x98X\xe1\xa8s\x9c\u0509\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xaf\x99-\xd6i\xc0\x88>U\x15\xd3\xf3\x11*\x13\xf6\x17\xa4\xc3g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaf\xa1\u056d8\xfe\xd4GY\xc0[\x89\x93\xc1\xaa\r\xac\xe1\x9f@\x89\x04V9\x18$O@\x00\x00\xe0\x94\xaf\xa59XnG\x19\x17J;F\xb9\xb3\xe6c\xa7\u0475\xb9\x87\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xaf\xa6\x94n\xff\xd5\xffS\x15O\x82\x01\x02S\xdfG\xae(\f\u0309j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xaf\xc8\xeb\u860b\xd4\x10Z\xccL\x01\x8eTj\x1e\x8f\x9cx\x88\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xaf\xcc}\xbb\x83V\xd8B\xd4:\xe7\xe2<\x84\"\xb0\"\xa3\b\x03\x8a\x06o\xfc\xbf\xd5\xe5\xa3\x00\x00\x00\u07d4\xaf\xd0\x19\xff6\xa0\x91U4ki\x97H\x15\xa1\xc9\x12\xc9\n\xa4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaf\xda\xc5\xc1\xcbV\xe2E\xbfp3\x00f\xa8\x17\uabecL\u0449\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf\xdd\x1bxab\xb81~ \xf0\xe9y\xf4\xb2\xceHmv]\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf\xf1\x04Z\xdf'\xa1\xaa2\x94a\xb2M\xe1\xba\u950ai\x8b\x89\x01\u03c4\xa3\n\n\f\x00\x00\u07d4\xaf\xf1\a\x96\v~\xc3N\u0590\xb6e\x02M`\x83\x8c\x19\x0fp\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xaf\xf1\x1c\xcfi\x93\x04\xd5\xf5\x86*\xf8`\x83E\x1c&\xe7\x9a\xe5\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xaf\xf1at\nm\x90\x9f\xe9\x9cY\xa9\xb7yE\xc9\x1c\xc9\x14H\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\xaf\xfc\x99\xd5\ubd28O\xe7x\x8d\x97\xdc\xe2t\xb08$\x048\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xaf\xfe\xa0G7\"\xcb\u007f\x0e\x0e\x86\xb9\xe1\x18\x83\xbfB\x8d\x8dT\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xb0\t\x96\xb0Vn\xcb>rC\xb8\"y\x88\u0733R\xc2\x18\x99\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xb0\x1e8\x9b(\xa3\x1d\x8eI\x95\xbd\xd7\xd7\xc8\x1b\xee\xab\x1eA\x19\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb0-\x06(s3EE\u03a2\x92\x18\xe4\x05w`Y\x0ft#\x89\xac\xb6\xa1\xc7\xd9:\x88\x00\x00\u07d4\xb0/\xa2\x93\x87\xec\x12\xe3\u007fi\"\xacL\xe9\x8c[\t\xe0\xb0\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb06\x91k\xda\u03d4\xb6\x9eZ\x8ae`)u\xeb\x02a\x04\u0749\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb0A1\x0f\xe9\xee\u0586L\xed\u053e\xe5\x8d\xf8\x8e\xb4\xed<\xac\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb0U\xafL\xad\xfc\xfd\xb4%\xcfe\xbad1\a\x8f\a\xec\u056b\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xb0W\x11S\xdb\x1cN\u05ec\xae\xfe\x13\xec\xdf\xdbr\xe7\xe4\xf0j\x8a\x11\f\xffyj\xc1\x95 \x00\x00\u07d4\xb0n\xab\t\xa6\x10\u01a5=V\xa9F\xb2\xc44\x87\xac\x1d[-\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb0rI\xe0U\x04J\x91U5\x9a@)7\xbb\xd9T\xfeH\xb6\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xb0v\x182\x8a\x90\x13\a\xa1\xb7\xa0\xd0X\xfc\xd5xn\x9er\xfe\x8a\x06gI]JC0\xce\x00\x00\u07d4\xb0y\xbbM\x98f\x14:m\xa7*\xe7\xac\x00\"\x06)\x811\\\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4\xb0{\xcc\bZ\xb3\xf7)\xf2D\x00Ah7\xb6\x996\xba\x88s\x89lm\x84\xbc\xcd\xd9\xce\x00\x00\u07d4\xb0{\xcf\x1c\xc5\xd4F.Q$\xc9e\xec\xf0\xd7\r\xc2z\xcau\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\xb0|\xb9\xc1$\x05\xb7\x11\x80uC\u0113De\xf8\u007f\x98\xbd-\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb0\u007f\u07af\xf9\x1dD`\xfel\xd0\u8870\xbd\x8d\"\xa6.\x87\x8a\x01\x1d%)\xf3SZ\xb0\x00\x00\xe0\x94\xb0\x9f\xe6\xd44\x9b\x99\xbc7\x93\x80T\x02-T\xfc\xa3f\xf7\xaf\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94\xb0\xaa\x00\x95\f\x0e\x81\xfa2\x10\x17>r\x9a\xaf\x16:'\xcdq\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xb0\xacN\xfff\x80\xee\x14\x16\x9c\xda\xdb\xff\xdb0\x80Om%\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb0\xb3j\xf9\xae\xee\u07d7\xb6\xb0\"\x80\xf1\x14\xf19\x84\xea2`\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xb0\xb7y\xb9K\xfa<.\x1fX{\u031c~!x\x92\"7\x8f\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xb0\xba\xeb0\xe3\x13wlLm$t\x02\xbaAg\xaf\u0361\u0309j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb0\xbb)\xa8a\xea\x1dBME\xac\u053f\u0112\xfb\x8e\xd8\t\xb7\x89\x04V9\x18$O@\x00\x00\xe0\x94\xb0\xc1\xb1w\xa2 \xe4\x1f|t\xd0|\u0785i\xc2\x1cu\xc2\xf9\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xb0\xc7\xceL\r\xc3\u00bb\xb9\x9c\xc1\x85{\x8aE_a\x17\x11\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb0\xce\xf8\xe8\xfb\x89\x84\xa6\x01\x9f\x01\xc6y\xf2r\xbb\xe6\x8f\\w\x89\b=lz\xabc`\x00\x00\xe0\x94\xb0\xd3+\xd7\xe4\u6577\xb0\x1a\xa3\xd0Ao\x80U}\xba\x99\x03\x8a\x03s\x9f\xf0\xf6\xe6\x130\x00\x00\xe0\x94\xb0\xd3\u0247+\x85\x05n\xa0\xc0\xe6\xd1\xec\xf7\xa7~<\u6ac5\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\xb0\xe4i\u0206Y8\x15\xb3IV8Y]\xae\xf0f_\xaeb\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xb0\xe7`\xbb\a\xc0\x81wsE\xe0W\x8e\x8b\u0218\"mN;\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1\x040\x04\xec\x19A\xa8\xcfO+\x00\xb1W\x00\u076co\xf1~\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb1\x05\xdd=\x98|\xff\xd8\x13\xe9\xc8P\n\x80\xa1\xad%}V\u0189lj\xccg\u05f1\xd4\x00\x00\u07d4\xb1\x0f\u04a6G\x10/\x88\x1ft\xc9\xfb\xc3}\xa62\x94\x9f#u\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xb1\x15\xee:\xb7d\x1e\x1a\xa6\xd0\x00\xe4\x1b\xfc\x1e\xc7!\f/2\x8a\x02\xc0\xbb=\xd3\fN \x00\x00\u07d4\xb1\x17\x8a\xd4s\x83\xc3\x1c\x814\xa1\x94\x1c\xbc\xd4t\xd0bD\xe2\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xb1\x17\x95\x89\u1779\xd4\x15W\xbb\xec\x1c\xb2L\xcc-\xec\x1c\u007f\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xb1\x19\u76a9\xb9\x16Re\x81\xcb\xf5!\xefGJ\xe8M\xcf\xf4\x89O\xba\x10\x01\xe5\xbe\xfe\x00\x00\u07d4\xb1\x1f\xa7\xfb'\n\xbc\xdfZ.\xab\x95\xaa0\u013566\uffc9+^:\xf1k\x18\x80\x00\x00\u07d4\xb1$\xbc\xb6\xff\xa40\xfc\xae.\x86\xb4_'\xe3\xf2\x1e\x81\xee\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1)\xa5\xcbq\x05\xfe\x81\v\u0615\xdcr\x06\xa9\x91\xa4TT\x88\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\xe0\x94\xb1.\xd0{\x8a8\xadU\x066?\xc0z\vmy\x996\xbd\xaf\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb14\xc0\x049\x1a\xb4\x99(x3zQ\xec$/B(WB\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1?\x93\xaf0\xe8\xd7fs\x81\xb2\xb9[\xc1\xa6\x99\xd5\xe3\xe1)\x89\x16\u012b\xbe\xbe\xa0\x10\x00\x00\u07d4\xb1E\x92\x85\x86>\xa2\xdb7Y\xe5F\u03b3\xfb7a\xf5\x90\x9c\x89<\xd7*\x89@\x87\xe0\x80\x00\u07d4\xb1F\xa0\xb9%U<\xf0o\xca\xf5J\x1bM\xfe\xa6!)\aW\x89lnY\xe6|xT\x00\x00\xe0\x94\xb1Jz\xaa\x8fI\xf2\xfb\x9a\x81\x02\u05bb\xe4\u010a\xe7\xc0o\xb2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb1K\xbe\xffpr\tu\xdca\x91\xb2\xa4O\xf4\x9f&r\x87<\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\xe0\x94\xb1L\xc8\xde3\xd63\x826S\x9aH\x90 \xceFU\xa3+\u018a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb1M\xdb\x03\x86\xfb`c\x98\xb8\xccGVZ\xfa\xe0\x0f\xf1\xd6j\x89\xa1*\xff\b>f\xf0\x00\x00\u07d4\xb1S\xf8(\xdd\amJ|\x1c%t\xbb-\xee\x1aD\xa3\x18\xa8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb1T\x0e\x94\xcf\xf3F\\\xc3\u0447\xe7\xc8\u3f6f\x98FY\u2262\x15\xe4C\x90\xe33\x00\x00\u07d4\xb1X\xdbC\xfab\xd3\x0ee\xf3\u041b\xf7\x81\u01f6sr\uba89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xb1ar_\xdc\xed\xd1yR\xd5{#\xef([~K\x11i\xe8\x89\x02\xb6\xdf\xed6d\x95\x80\x00\u07d4\xb1dy\xba\x8e}\xf8\xf6>\x1b\x95\xd1I\u0345)\xd75\xc2\u0689-\xe3:j\xac2T\x80\x00\u07d4\xb1f\xe3}.P\x1a\xe7<\x84\x14+_\xfbZ\xa6U\xddZ\x99\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xb1\x83\xeb\xeeO\xcbB\xc2 \xe4wt\xf5\x9dlT\xd5\xe3*\xb1\x89V\xf7\xa9\xc3<\x04\xd1\x00\x00\u07d4\xb1\x88\a\x84D\x02~8g\x98\xa8\xaehi\x89\x19\xd5\xcc#\r\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xb1\x89j7\xe5\u0602Z-\x01vZ\xe5\xdeb\x99w\u0783R\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb1\x8eg\xa5\x05\n\x1d\xc9\xfb\x19\t\x19\xa3=\xa88\xefDP\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb1\xa2\xb4:t3\xdd\x15\v\xb8\"'\xedQ\x9c\u05b1B\u04c2\x89\x94mb\rtK\x88\x00\x00\u07d4\xb1\xc0\u040b6\xe1\x84\xf9\x95*@7\xe3\xe5:f}\a\nN\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb1\xc3(\xfb\x98\xf2\xf1\x9a\xb6do\n|\x8cVo\xdaZ\x85@\x89\x87\x86x2n\xac\x90\x00\x00\xe0\x94\xb1\xc7Qxi9\xbb\xa0\xd6q\xa6w\xa1X\u01ab\xe7&^F\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xb1\xcdK\xdf\xd1\x04H\x9a\x02n\u025dYs\a\xa0By\xf1s\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb1\u03d4\xf8\t\x15\x05\x05_\x01\n\xb4\xba\u0196\xe0\xca\x0fg\xa1\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xb1\u05b0\x1b\x94\xd8T\xfe\x8b7J\xa6^\x89\\\xf2*\xa2V\x0e\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94\xb1\u06e5%\v\xa9bWU$n\x06yg\xf2\xad/\a\x91\u078a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xb1\xe2\u0755\xe3\x9a\xe9w\\U\xae\xb1?\x12\xc2\xfa#0S\xba\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1\xe6\xe8\x10\xc2J\xb0H\x8d\xe9\xe0\x1eWH7\x82\x9f|w\u0409\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb1\xe9\xc5\xf1\xd2\x1eauzk.\xe7Y\x13\xfcZ\x1aA\x01\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\x03\u049elV\xb9&\x99\u0139-\x1fo\x84d\x8d\xc4\u03fc\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\x16\xdcY\xe2|=ry\xf5\xcd[\xb2\xbe\u03f2`n\x14\u0649\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\x1byy\xbf|\\\xa0\x1f\xa8-\xd6@\xb4\x1c9\xe6\u01bcu\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xb2#\xbf\x1f\xbf\x80H\\\xa2\xb5V}\x98\xdb{\xc3SM\xd6i\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb2-PU\xd9b15\x96\x1ej\xbd'<\x90\xde\xea\x16\xa3\xe7\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xb2-\xad\xd7\xe1\xe0R2\xa927\xba\xed\x98\xe0\u07d2\xb1\x86\x9e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb24\x03_uDF<\xe1\xe2+\xc5S\x06F\x84\xc5\x13\xcdQ\x89\r\x89\xfa=\u010d\xcf\x00\x00\u07d4\xb2G\u03dcr\xecH*\xf3\xea\xa7Ye\x8fy=g\nW\f\x891p\x8a\xe0\x04T@\x00\x00\u07d4\xb2ghA\xee\x9f-1\xc1r\xe8#\x03\xb0\xfe\x9b\xbf\x9f\x1e\t\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb2y\xc7\xd3U\u0088\x03\x92\xaa\u046a!\xee\x86|;5\a\u07c9D[\xe3\xf2\uf1d4\x00\x00\u07d4\xb2|\x1a$ L\x1e\x11\x8du\x14\x9d\xd1\t1\x1e\a\xc0s\xab\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb2\x81\x81\xa4X\xa4@\xf1\u01bb\x1d\xe8@\x02\x81\xa3\x14\x8fL5\x89\x14b\fW\xdd\xda\xe0\x00\x00\xe0\x94\xb2\x82E\x03|\xb1\x92\xf7W\x85\u02c6\xcb\xfe|\x93\r\xa2X\xb0\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xb2\x87\xf7\xf8\xd8\u00c7,\x1bXk\xcd}\n\xed\xbf~s'2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb2\x8b\xb3\x9f4fQ|\xd4o\x97\x9c\xf5\x96S\xee}\x8f\x15.\x89\x18e\x01'\xcc=\xc8\x00\x00\u07d4\xb2\x8d\xbf\xc6I\x98\x94\xf7:q\xfa\xa0\n\xbe\x0fK\xc9\u045f*\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xb2\x96\x8f}5\xf2\b\x87\x161\xc6h{?=\xae\xab\xc6al\x89\bu\xc4\u007f(\x9fv\x00\x00\u07d4\xb2\x9f[|\x190\xd9\xf9z\x11^\x06pf\xf0\xb5M\xb4K;\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb2\xa1D\xb1\xeag\xb9Q\x0f\"g\xf9\xda9\xd3\xf9=\xe2fB\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xa2\xc2\x11\x16\x12\xfb\x8b\xbb\x8e}\xd97\x8dg\xf1\xa3\x84\xf0P\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb2\xa4\x98\xf0;\xd7\x17\x8b\u0627\x89\xa0\x0fR7\xafy\xa3\xe3\xf8\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\xb2\xaa/\x1f\x8e\x93\xe7\x97\x13\xd9,\xea\x9f\xfc\xe9\xa4\n\xf9\xc8-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xb5\x16\xfd\u045e\u007f8d\xb6\xd2\xcf\x1b%*AV\xf1\xb0;\x89\x02\xe9\x83\xc7a\x15\xfc\x00\x00\u07d4\xb2\xb7\u0374\xffKa\u0577\xce\v\"p\xbb\xb5&\x97C\xec\x04\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xbd\xbe\u07d5\x90\x84v\xd7\x14\x8a7\f\u0193t6(\x05\u007f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb2\xbf\xaaX\xb5\x19l\\\xb7\xf8\x9d\xe1_G\x9d\x188\xdeq=\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4\xb2\xc5>\xfa3\xfeJ:\x1a\x80 \\s\xec;\x1d\xbc\xad\x06\x02\x89h\x01\u06b3Y\x18\x93\x80\x00\xe0\x94\xb2\xd06\x05\x15\xf1}\xab\xa9\x0f\u02ec\x82\x05\xd5i\xb9\x15\u05ac\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xb2\xd1\xe9\x9a\xf9\x121\x85\x8epe\xdd\x19\x183\r\xc4\xc7G\u054a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\xb2\u066b\x96d\xbc\xf6\xdf <4o\u0192\xfd\x9c\xba\xb9 ^\x89\x17\xbex\x97`e\x18\x00\x00\u07d4\xb2\u0777\x86\xd3yN'\x01\x87\xd0E\x1a\xd6\u0237\x9e\x0e\x87E\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\xe0\x85\xfd\xdd\x14h\xba\aA['NsN\x11#\u007f\xb2\xa9\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xb2\xe9\xd7k\xf5\x0f\xc3k\xf7\u04d4Kc\xe9\u0288\x9bi\x99h\x89\x902\xeab\xb7K\x10\x00\x00\xe0\x94\xb2\xf9\xc9r\xc1\xe9swU\xb3\xff\x1b0\x88s\x83\x969[&\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xb2\xfc\x84\xa3\xe5\nP\xaf\x02\xf9M\xa08>\u055fq\xff\x01\u05ca\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xb3\x05\v\xef\xf9\xde3\xc8\x0e\x1f\xa1R%\xe2\x8f,A:\xe3\x13\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\xb3\x11\x96qJH\xdf\xf7&\xea\x943\xcd)\x12\xf1\xa4\x14\xb3\xb3\x89\x91Hx\xa8\xc0^\xe0\x00\x00\xe0\x94\xb3\x14[tPm\x1a\x8d\x04|\xdc\xdcU9*{SPy\x9a\x8a\x1bb)t\x1c\r=]\x80\x00\u07d4\xb3 \x83H6\xd1\xdb\xfd\xa9\xe7\xa3\x18M\x1a\xd1\xfdC \xcc\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3#\u073f.\xdd\xc58.\u4efb \x1c\xa3\x93\x1b\xe8\xb48\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb3$\x00\xfd\x13\xc5P\t\x17\xcb\x03{)\xfe\"\xe7\xd5\"\x8f-\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xb3%gL\x01\xe3\xf7)\rR&3\x9f\xbe\xacg\xd2!'\x9f\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xb3(%\xd5\xf3\xdb$\x9e\xf4\xe8\\\xc4\xf31S\x95\x89v\u8f09\x1b-\xf9\xd2\x19\xf5y\x80\x00\u07d4\xb3*\xf3\xd3\xe8\xd0u4I&To.2\x88{\xf9;\x16\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb3/\x1c&\x89\xa5\xcey\xf1\xbc\x97\v1XO\x1b\xcf\"\x83\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb3<\x03#\xfb\xf9\xc2l\x1d\x8a\xc4N\xf7C\x91\u0400F\x96\u0689\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb3O\x04\xb8\xdbe\xbb\xa9\xc2n\xfcL\xe6\xef\xc5\x04\x81\xf3\xd6]\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb3U}9\xb5A\x1b\x84D__T\xf3\x8fb\xd2qM\x00\x87\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xb3X\xe9|p\xb6\x05\xb1\xd7\xd7)\u07f6@\xb4<^\xaf\xd1\xe7\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xb3^\x8a\x1c\r\xac~\x0ef\u06ecsjY*\xbdD\x01%a\x88\xcf\xceU\xaa\x12\xb3\x00\x00\xe0\x94\xb3fx\x94\xb7\x86<\x06\x8a\xd3D\x87?\xcf\xf4\xb5g\x1e\x06\x89\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb3qw1\xda\xd6Q2\xday-\x87`0\xe4j\xc2'\xbb\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3s\x1b\x04l\x8a\u0195\xa1'\xfdy\u0425\xd5\xfaj\xe6\xd1.\x89lO\xd1\xee$nx\x00\x00\u07d4\xb3|+\x9fPc{\xec\xe0\u0295\x92\b\xae\xfe\xe6F;\xa7 \x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb3\x88\xb5\xdf\xec\xd2\xc5\u4d56W|d%V\xdb\xfe'xU\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb3\x8cNS{]\xf90\xd6Zt\xd0C\x83\x1dkH[\xbd\xe4\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xb3\x919Wa\x94\xa0\x86a\x95\x15\x1f3\xf2\x14\n\xd1\u0306\u03ca\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xb3\x9fL\x00\xb2c\f\xab}\xb7)^\xf4=G\xd5\x01\xe1\u007f\u05c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb3\xa6K\x11vrOT\t\xe1AJ5#f\x1b\xae\xe7KJ\x89\x01ch\xffO\xf9\xc1\x00\x00\u07d4\xb3\xa6\xbdA\xf9\xd9\xc3 \x1e\x05\v\x87\x19\x8f\xbd\xa3\x994\"\x10\x89\xc4a\xe1\xdd\x10)\xb5\x80\x00\u07d4\xb3\xa8\xc2\xcb}5\x8eW9\x94\x1d\x94[\xa9\x04Z\x02:\x8b\xbb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3\xaeT\xfb\xa0\x9d>\xe1\u05bd\xd1\xe9W\x929\x19\x02L5\xfa\x89\x03\x8d,\xeee\xb2*\x80\x00\u07d4\xb3\xb7\xf4\x93\xb4J,\x8d\x80\xecx\xb1\xcd\xc7Ze+s\xb0l\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb3\xc2(s\x1d\x18m-\xed[_\xbe\x00Lfl\x8eF\x9b\x86\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4\xb3\xc2``\x9b\x9d\xf4\t^l]\xff9\x8e\xeb^-\xf4\x99\x85\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xb3\xc6[\x84Z\xbal\xd8\x16\xfb\xaa\xe9\x83\xe0\xe4l\x82\xaa\x86\"\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3\xc9H\x11\xe7\x17[\x14\x8b(\x1c\x1a\x84[\xfc\x9b\xb6\xfb\xc1\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb3\xe2\x0e\xb4\xde\x18\xbd\x06\x02!h\x98\x94\xbe\u5bb2SQ\xee\x89\x03\xfc\x80\xcc\xe5\x16Y\x80\x00\u07d4\xb3\xe3\xc49\x06\x98\x80\x15f\x00\u0089.D\x8dA6\xc9-\x9b\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\xe0\x94\xb3\xf8*\x87\xe5\x9a9\xd0\u0480\x8f\aQ\xebr\xc22\x9c\xdc\u014a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb3\xfc\x1dh\x81\xab\xfc\xb8\xbe\xcc\v\xb0!\xb8\xb7;r3\u0751\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xb4\x05\x94\xc4\xf3fN\xf8I\u0326\"{\x8a%\xaai\t%\xee\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb4\x1e\xaf]Q\xa5\xba\x1b\xa3\x9b\xb4\x18\u06f5O\xabu\x0e\xfb\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4$\u058d\x9d\r\x00\xce\xc1\x93\x8c\x85N\x15\xff\xb8\x80\xba\x01p\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb4%bs\x96+\xf61\xd0\x14U\\\xc1\xda\r\xcc1akI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb40g\xfep\u0675Ys\xbaX\xdcd\xdd\u007f1\x1eUBY\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb46W\xa5\x0e\xec\xbc0w\xe0\x05\xd8\xf8\xd9O7xv\xba\u0509\x01\xec\x1b:\x1f\xf7Z\x00\x00\u07d4\xb4<'\xf7\xa0\xa1\"\bK\x98\xf4\x83\x92%A\u0203l\xee,\x89&\u009eG\u0104L\x00\x00\xe0\x94\xb4A5v\x86\x9c\b\xf9Q*\xd3\x11\xfe\x92Y\x88\xa5-4\x14\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb4F\x05U$q\xa6\xee\xe4\u06abq\xff;\xb4\x13&\xd4s\xe0\x89-~=Q\xbaS\xd0\x00\x00\u07d4\xb4GW\x1d\xac\xbb>\u02f6\xd1\xcf\v\f\x8f88\xe5#$\xe2\x89\x01\xa3\x18f\u007f\xb4\x05\x80\x00\u07d4\xb4G\x83\xc8\xe5{H\a\x93\xcb\u059aE\xd9\f{O\fH\xac\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb4H\x15\xa0\xf2\x8eV\x9d\x0e\x92\x1aJ\u078f\xb2d%&Iz\x89\x03\x027\x9b\xf2\xca.\x00\x00\u07d4\xb4Im\xdb'y\x9a\"$W\xd79y\x11g(\u8844[\x89\x8d\x81\x9e\xa6_\xa6/\x80\x00\xe0\x94\xb4RL\x95\xa7\x86\x0e!\x84\x02\x96\xa6\x16$@\x19B\x1cJ\xba\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb4\\\xca\r6\x82fbh<\xf7\u0432\xfd\xach\u007f\x02\xd0\u010965\u026d\xc5\u07a0\x00\x00\u0794\xb4d@\u01d7\xa5V\xe0L}\x91\x04f\x04\x91\xf9k\xb0v\xbf\x88\xce\xc7o\x0eqR\x00\x00\u07d4\xb4j\u0386^,P\xeaF\x98\xd2\x16\xabE]\xffZ\x11\xcdr\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4m\x11\x82\xe5\xaa\xca\xff\r&\xb2\xfc\xf7/<\x9f\xfb\xcd\xd9}\x89\xaa*`<\xdd\u007f,\x00\x00\u07d4\xb4\x89!\xc9h}U\x10tE\x84\x93n\x88\x86\xbd\xbf-\xf6\x9b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\x98\xbb\x0fR\x00\x05\xb6!jD%\xb7Z\xa9\xad\xc5-b+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xb4\xb1\x1d\x10\x9f`\x8f\xa8\xed\xd3\xfe\xa9\xf8\xc3\x15d\x9a\xeb=\x11\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb4\xb1K\xf4TU\u042b\b\x035\x8bu$\xa7+\xe1\xa2\x04[\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb4\xb1\x85\xd9C\xee+Xc\x1e3\xdf\xf5\xafhT\xc1y\x93\xac\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\xbf$\u02c3hk\xc4i\x86\x9f\xef\xb0D\xb9\tqi\x93\xe2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb4\xc2\x00@\xcc\u0661\xa3(=\xa4\u0522\xf3e\x82\bC\xd7\xe2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\xc8\x17\x0f{*\xb56\xd1\u0662[\xdd :\xe1(\x8d\xc3\u0549\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb4\xd8/.i\x94?}\xe0\xf5\xf7t8y@o\xac.\x9c\xec\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xb4\xddF\f\xd0\x16rZd\xb2.\xa4\xf8\xe0n\x06gN\x03>\x8a\x01#\x1b\xb8t\x85G\xa8\x00\x00\u07d4\xb4\xddT\x99\xda\xeb%\a\xfb-\xe1\"\x97s\x1dLr\xb1k\xb0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb5\x04l\xb3\xdc\x1d\xed\xbd6E\x14\xa2\x84\x8eD\xc1\xdeN\xd1G\x8a\x03{}\x9b\xb8 @^\x00\x00\xe0\x94\xb5\b\xf9\x87\xb2\xde4\xaeL\xf1\x93\u0785\xbf\xf6\x13\x89b\x1f\x88\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xb5\tU\xaan4\x15q\x98f\b\xbd\u0211\xc2\x13\x9fT\f\u07c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb5\f\x14\x9a\x19\x06\xfa\xd2xo\xfb\x13Z\xabP\x177\xe9\xe5o\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\xb5\f\x9fW\x89\xaeD\xe2\xdc\xe0\x17\xc7\x14\xca\xf0\f\x83\x00\x84\u0089\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xb5\x14\x88,\x97\x9b\xb6B\xa8\r\u04c7T\u0578\xc8)m\x9a\a\x893\xc5I\x901r\f\x00\x00\u07d4\xb5\x1d\u0734\xddN\x8a\xe6\xbe3m\xd9eIq\xd9\xfe\xc8kA\x89\x16\xd4d\xf8=\u2500\x00\u07d4\xb5\x1eU\x8e\xb5Q/\xbc\xfa\x81\xf8\u043d\x93\x8cy\xeb\xb5$+\x89&\u009eG\u0104L\x00\x00\u07d4\xb5#\xff\xf9t\x98q\xb3S\x88C\x887\xf7\xe6\xe0\u07a9\xcbk\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb5-\xfbE\xde]t\xe3\xdf \x832\xbcW\x1c\x80\x9b\x8d\xcf2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xb55\xf8\u06c7\x9f\xc6\u007f\xecX\x82J\\\xbenT\x98\xab\xa6\x92\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xb57\xd3jp\xee\xb8\xd3\xe5\xc8\r\xe8\x15\"\\\x11X\u02d2\u0109QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xb5;\xcb\x17L%\x184\x8b\x81\x8a\xec\xe0 6E\x96Fk\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5I>\xf1srDE\xcf4\\\x03]'\x9b\xa7Y\xf2\x8dQ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb5S\xd2]kT!\xe8\x1c*\xd0^\v\x8b\xa7Q\xf8\xf0\x10\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5Tt\xbaX\xf0\xf2\xf4\x0el\xba\xbe\xd4\xea\x17n\x01\x1f\xca\u0589j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb5U\xd0\x0f\x91\x90\xcc6w\xae\xf3\x14\xac\xd7?\xdc99\x92Y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5W\xab\x949\xefP\xd27\xb5S\xf0%\b6JFj\\\x03\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5jx\x00(\x03\x9c\x81\xca\xf3{gu\xc6 \u7195Gd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5j\u04ae\xc6\xc8\xc3\xf1\x9e\x15\x15\xbb\xb7\u0751(RV\xb69\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5t\x13\x06\n\xf3\xf1N\xb4y\x06_\x1e\x9d\x19\xb3uz\xe8\u0309\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xb5uI\xbf\xbc\x9b\xdd\x18\xf76\xb2&P\xe4\x8as`\x1f\xa6\\\x89\x18-~L\xfd\xa08\x00\x00\xe0\x94\xb5w\xb6\xbe\xfa\x05N\x9c\x04\x04a\x85P\x94\xb0\x02\xd7\xf5{\u05ca\x18#\xf3\xcfb\x1d#@\x00\x00\u07d4\xb5{\x04\xfa#\xd1 ?\xae\x06\x1e\xacEB\xcb`\xf3\xa5v7\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xb5\x87\f\xe3B\xd43C36s\x03\x8bGd\xa4n\x92_>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5\x87\xb4J,\xa7\x9eK\xc1\u074b\xfd\xd4: qP\xf2\xe7\xe0\x89\",\x8e\xb3\xfff@\x00\x00\u07d4\xb5\x89gm\x15\xa0DH4B0\xd4\xff'\xc9^\xdf\x12,I\x8965\u026d\xc5\u07a0\x00\x00\u0794\xb5\x8bR\x86^\xa5]\x806\xf2\xfa\xb2`\x98\xb3R\u0283~\x18\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xb5\x90k\n\u9881X\xe8\xacU\x0e9\xda\bn\xe3\x15v#\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5\xa4g\x96\x85\xfa\x14\x19l.\x920\xc8\xc4\xe3;\xff\xbc\x10\xe2\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xb5\xa5\x89\u075f@q\u06f6\xfb\xa8\x9b?]]\xae}\x96\xc1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5\xa6\x06\xf4\xdd\u02f9G\x1e\xc6\u007fe\x8c\xaf+\x00\xees\x02^\x89\xeaun\xa9*\xfct\x00\x00\u07d4\xb5\xadQW\u0769!\xe6\xba\xfa\u0350\x86\xaes\xae\x1fa\x1d?\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5\xad\xd1\u701f}\x03\x06\x9b\xfe\x88;\n\x93\"\x10\xbe\x87\x12\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5\xba)\x91|x\xa1\xd9\xe5\xc5\xc7\x13fl\x1eA\x1d\u007fi:\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb5\xc8\x16\xa8(<\xa4\xdfh\xa1\xa7=c\xbd\x80&\x04\x88\xdf\b\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5\xca\xc5\xed\x03G}9\v\xb2g\xd4\xeb\xd4a\x01\xfb\xc2\xc3\u0689\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xb5\u037cA\x15@oR\u5a85\xd0\xfe\xa1p\u0497\x9c\u01fa\x89Hz\x9a0E9D\x00\x00\u0794\xb5\u0653M{)+\xcf`;(\x80t\x1e\xb7`(\x83\x83\xa0\x88\xe7\xc2Q\x85\x05\x06\x00\x00\u07d4\xb5\xddP\xa1]\xa3Ih\x89\nS\xb4\xf1?\xe1\xaf\b\x1b\xaa\xaa\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb5\xfa\x81\x84\xe4>\xd3\u0e2b\x91!da\xb3R\x8d\x84\xfd\t\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xb5\xfb~\xa2\xdd\xc1Y\x8bfz\x9dW\xdd9\xe8Z8\xf3]V\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb6\x00B\x97R\xf3\x99\xc8\r\a4tK\xae\n\x02.\xcag\u0189\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb6\x00\xfe\xabJ\xa9lSu\x04\xd9`W\"1Ai,\x19:\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb6\x04|\u07d3-\xb3\xe4\x04_Iv\x12#AS~\u0556\x1e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb6\x15\xe9@\x14>\xb5\u007f\x87X\x93\xbc\x98\xa6\x1b=a\x8c\x1e\x8c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb6\x1c4\xfc\xac\xdap\x1aZ\xa8p$Y\u07b0\u4b83\x8d\xf8\x8a\aiZ\x92\xc2\ro\xe0\x00\x00\xe0\x94\xb60d\xbd3U\xe6\xe0~-7p$\x12Z3wlJ\xfa\x8a\b7Z*\xbc\xca$@\x00\x00\u07d4\xb65\xa4\xbcq\xfb(\xfd\xd5\xd2\xc3\"\x98:V\u0084Bni\x89\t79SM(h\x00\x00\u07d4\xb6F\u07d8\xb4\x94BtkaR\\\x81\xa3\xb0K\xa3\x10bP\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb6YA\xd4LP\xd2Ffg\r6Gf\xe9\x91\xc0.\x11\u0089 \x86\xac5\x10R`\x00\x00\xe0\x94\xb6[\u05c0\xc7CA\x15\x16 'VR#\xf4NT\x98\xff\x8c\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\xb6d\x11\xe3\xa0-\xed\xb7&\xfay\x10}\xc9\v\xc1\xca\xe6MH\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb6fu\x14.1\x11\xa1\xc2\xea\x1e\xb2A\x9c\xfaB\xaa\xf7\xa24\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xb6o\x92\x12K^c\x03XY\xe3\x90b\x88i\xdb\u07a9H^\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xb6rsJ\xfc\xc2$\xe2\xe6\t\xfcQ\xd4\xf0Ys'D\xc9H\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\xe0\x94\xb6w\x1b\v\xf3B\u007f\x9a\xe7\xa9>|.a\xeec\x94\x1f\xdb\b\x8a\x03\xfb&i)T\xbf\xc0\x00\x00\u07d4\xb6z\x80\xf1p\x19}\x96\xcd\xccJ\xb6\u02e6'\xb4\xaf\xa6\xe1,\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xb6\x88\x99\xe7a\rL\x93\xa255\xbc\xc4H\x94[\xa1fo\x1c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb6\xa8)3\xc9\xea\u06bd\x98\x1e]m`\xa6\x81\x8f\xf8\x06\xe3k\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xb6\xaa\u02cc\xb3\v\xab*\xe4\xa2BF&\xe6\xe1+\x02\xd0F\x05\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb6\xb3J&?\x10\xc3\xd2\xec\xeb\n\xccU\x9a{*\xb8\\\xe5e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb6\xbf\xe1\xc3\xef\x94\xe1\x84o\xb9\xe3\xac\xfe\x9bP\xc3\xe9\x06\x923\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xb6\xcdt2\xd5\x16\x1b\xe7\x97h\xadE\xde>Dz\a\x98 c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb6\xceM\xc5`\xfcs\xdci\xfbzb\xe3\x88\xdb~r\xeavO\x894]\xf1i\xe9\xa3X\x00\x00\u07d4\xb6\xde\u03c2\x96\x98\x19\xba\x02\xde)\xb9\xb5\x93\xf2\x1bd\xee\xda\x0f\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\xe0\x94\xb6\xe6\xc3\"+ko\x9b\xe2\x87]*\x89\xf1'\xfbd\x10\x0f\xe2\x8a\x01\xb2\x1dS#\xcc0 \x00\x00\u07d4\xb6\xe8\xaf\xd9=\xfa\x9a\xf2\u007f9\xb4\xdf\x06\ag\x10\xbe\xe3\u07eb\x89\x01Z\xf1\u05cbX\xc4\x00\x00\xe0\x94\xb6\xf7\x8d\xa4\xf4\xd0A\xb3\xbc\x14\xbc[\xa5\x19\xa5\xba\f2\xf1(\x8a$}\xd3,?\xe1\x95\x04\x80\x00\xe0\x94\xb6\xfb9xbP\b\x14&\xa3B\xc7\rG\xeeR\x1e[\xc5c\x8a\x03-&\xd1.\x98\v`\x00\x00\u07d4\xb7\r\xba\x93\x91h+J6Nw\xfe\x99%c\x01\xa6\xc0\xbf\x1f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\x16#\xf3Q\a\xcft1\xa8?\xb3\xd2\x04\xb2\x9e\u0c67\xf4\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xb7\x1a\x13\xba\x8e\x95\x16{\x803\x1bR\u059e7\x05O\xe7\xa8&\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\x1bb\xf4\xb4H\xc0+\x12\x01\xcb^9J\xe6'\xb0\xa5`\xee\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb7\" \xad\xe3d\xd06\x9f--\xa7\x83\xcaGM{\x9b4\u0389\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\xe0\x94\xb7#\r\x1d\x1f\xf2\xac\xa3f\x969\x14\xa7\x9d\xf9\xf7\xc5\xea,\x98\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\xb7$\n\U000af433<\b\xae\x97d\x10>5\xdc\xe3c\x84(\x8a\x01\xca\xdd/\xe9hnc\x80\x00\u07d4\xb7'\xa9\xfc\x82\xe1\xcf\xfc\\\x17_\xa1HZ\x9b\xef\xa2\u037d\u04496'\xe8\xf7\x127<\x00\x00\u07d4\xb7,*\x01\x1c\r\xf5\x0f\xbbn(\xb2\n\xe1\xaa\xd2\x17\x88g\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb78-7\xdb\x03\x98\xacrA\f\xf9\x81=\xe9\xf8\xe1\uc36d\x8966\xc2^f\xec\xe7\x00\x00\u07d4\xb7;O\xf9\x9e\xb8\x8f\u061b\vmW\xa9\xbc3\x8e\x88o\xa0j\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xb7=jwU\x9c\x86\xcfet$)\x039K\xac\xf9n5p\x89\x04\xf1\xa7|\xcd;\xa0\x00\x00\u07d4\xb7Cr\xdb\xfa\x18\x1d\xc9$/9\xbf\x1d71\xdf\xfe+\xda\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7G\x9d\xabP\"\xc4\xd5\u06ea\xf8\xde\x17\x1bN\x95\x1d\u0464W\x89\x04V9\x18$O@\x00\x00\u07d4\xb7I\xb5N\x04\u0571\x9b\xdc\xed\xfb\x84\xdaw\x01\xabG\x8c'\xae\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xb7N\xd2f`\x01\xc1c3\xcfz\xf5\x9eJ=H`6;\x9c\x89\n~\xbd^Cc\xa0\x00\x00\u07d4\xb7QI\xe1\x85\xf6\xe3\x92pWs\x90s\xa1\x82*\xe1\xcf\r\xf2\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xb7S\xa7_\x9e\xd1\v!d:\n=\xc0Qz\xc9k\x1a@h\x89\x15\xc8\x18[,\x1f\xf4\x00\x00\xe0\x94\xb7V\xadR\xf3\xbft\xa7\xd2LgG\x1e\b\x87Ci6PL\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb7Wn\x9d1M\xf4\x1e\xc5Pd\x94):\xfb\x1b\xd5\xd3\xf6]\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb7X\x89o\x1b\xaa\x86O\x17\xeb\xed\x16\xd9S\x88o\xeeh\xaa\xe6\x8965\u026d\xc5\u07a0\x00\x00\u0794\xb7h\xb5#N\xba:\x99h\xb3Mm\xdbH\x1c\x84\x19\xb3e]\x88\xcf\xceU\xaa\x12\xb3\x00\x00\u07d4\xb7\x82\xbf\xd1\xe2\xdep\xf4gdo\x9b\xc0\x9e\xa5\xb1\xfc\xf4P\xaf\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xb7\xa2\xc1\x03r\x8bs\x05\xb5\xaen\x96\x1c\x94\xee\x99\xc9\xfe\x8e+\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xb7\xa3\x1a|8\xf3\xdb\t2.\xae\x11\xd2'!A\xea\"\x99\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7\xa6y\x1c\x16\xebN!b\xf1Ke7\xa0+=c\xbf\xc6\x02\x89*Rc\x91\xac\x93v\x00\x00\u07d4\xb7\xa7\xf7|4\x8f\x92\xa9\xf1\x10\fk\xd8)\xa8\xacm\u007f\u03d1\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xb7\xc0w\x94ft\xba\x93A\xfbLtz]P\xf5\xd2\xdad\x15\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb7\xc0\xd0\xcc\vM4-@b\xba\xc6$\xcc\xc3\xc7\f\xc6\xda?\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb7\xc9\xf1+\x03\x8esCm\x17\xe1\xc1/\xfe\x1a\xec\u0373\xf5\x8c\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\xb7\xcck\x1a\xcc2\u0632\x95\xdfh\xed\x9d^`\xb8\xf6L\xb6{\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xb7\xcehK\t\xab\xdaS8\x9a\x87Si\xf7\x19X\xae\xac;\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7\xd1.\x84\xa2\xe4\u01264Z\xf1\xdd\x1d\xa9\xf2PJ*\x99n\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\xd2R\xee\x94\x02\xb0\xee\xf1D)_\x0ei\xf0\xdbXl\bq\x89#\xc7W\a+\x8d\xd0\x00\x00\xe0\x94\xb7\u0541\xfe\n\xf1\xec8?;\xce\x00\xaf\x91\x99\xf3\xcf_\xe0\xcc\xe2\x8c\xd1J\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\xb8R\x18\xf3B\xf8\x01.\u069f'Nc\xce!R\xb2\xdc\xfd\xab\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb8UP\x10wn<\\\xb3\x11\xa5\xad\xee\xfe\x9e\x92\xbb\x9ad\xb9\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xb8_&\xdd\x0er\xd9\u009e\xba\xf6\x97\xa8\xafwG,+X\xb5\x8a\x02\x85\x19\xac\xc7\x19\fp\x00\x00\u07d4\xb8_\xf0>{_\xc4\"\x98\x1f\xae^\x99A\xda\xcb\u06bau\x84\x89Hz\x9a0E9D\x00\x00\xe0\x94\xb8f\a\x02\x1bb\xd3@\xcf&R\xf3\xf9_\xd2\xdcgi\x8b\u07ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb8}\xe1\xbc\u0492i\xd5!\xb8v\x1c\u00dc\xfbC\x19\xd2\xea\u054965\u026d\xc5\u07a0\x00\x00\u07d4\xb8\u007fSv\xc2\xde\vl\xc3\xc1y\xc0`\x87\xaaG=kFt\x89Hz\x9a0E9D\x00\x00\u07d4\xb8\x84\xad\u060d\x83\xdcVJ\xb8\xe0\xe0,\xbd\xb69\x19\xae\xa8D\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\x8a7\xc2\u007fx\xa6\x17\xd5\xc0\x91\xb7\u0577:7a\xe6_*\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\x94x\"\u056c\u79ad\x83&\xe9T\x96\"\x1e\v\xe6\xb7=\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb8\x9c\x03n\xd7\u0112\x87\x99!\xbeA\xe1\f\xa1i\x81\x98\xa7L\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xb8\x9fF2\xdfY\t\xe5\x8b*\x99d\xf7O\xeb\x9a;\x01\xe0\u014a\x04\x88u\xbc\xc6\xe7\xcb\xeb\x80\x00\u07d4\xb8\xa7\x9c\x84\x94^G\xa9\xc3C\x86\x83\u05b5\x84,\xffv\x84\xb1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\xa9y5'Y\xba\t\xe3Z\xa5\x93]\xf1u\xbf\xf6x\xa1\b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb8\xab9\x80[\xd8!\x18Ol\xbd=$s4{\x12\xbf\x17\\\x89\x06hZ\xc1\xbf\xe3,\x00\x00\xe0\x94\xb8\xac\x11}\x9f\r\xba\x80\x90\x14E\x82:\x92\x11\x03\xa51o\x85Zew\x9d\x1b\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4\xb9\xe9\f\x11\x92\xb3\xd5\xd3\xe3\xab\a\x00\xf1\xbfe_]\xd44z\x89\x1b\x19\xe5\vD\x97|\x00\x00\u07d4\xb9\xfd83\xe8\x8e|\xf1\xfa\x98y\xbd\xf5Z\xf4\xb9\x9c\xd5\xce?\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xba\x02I\xe0\x1d\x94[\xef\x93\xee^\xc6\x19%\xe0<\\\xa5\t\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xba\x0f9\x02;\xdb)\xeb\x18b\xa9\xf9\x05\x9c\xab]0nf/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\x10\xf2vB\x90\xf8uCCr\xf7\x9d\xbfq8\x01\u02ac\x01\x893\xc5I\x901r\f\x00\x00\u07d4\xba\x151\xfb\x9ey\x18\x96\xbc\xf3\xa8\x05X\xa3Y\xf6\xe7\xc1D\xbd\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xba\x17m\xbe2I\xe3E\xcdO\xa9g\xc0\xed\x13\xb2LG\u5189\x15\xae\xf9\xf1\xc3\x1c\u007f\x00\x00\xe0\x94\xba\x1f\x0e\x03\u02da\xa0!\xf4\xdc\xeb\xfa\x94\xe5\u0209\xc9\u01fc\x9e\x8a\x06\u0450\xc4u\x16\x9a \x00\x00\u07d4\xba\x1f\xca\xf2#\x93~\xf8\x9e\x85gU\x03\xbd\xb7\xcaj\x92\x8bx\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\xe0\x94\xba$\xfcCgS\xa79\xdb,\x8d@\xe6\xd4\xd0LR\x8e\x86\xfa\x8a\x02\xc0\xbb=\xd3\fN \x00\x00\u07d4\xbaB\xf9\xaa\xceL\x18E\x04\xab\xf5BWb\xac\xa2oq\xfb\u0709\x02\a\a}\u0627\x9c\x00\x00\u07d4\xbaF\x9a\xa5\u00c6\xb1\x92\x95\u0521\xb5G;T\x03S9\f\x85\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbad@\xae\xb3s{\x8e\xf0\xf1\xaf\x9b\f\x15\xf4\xc2\x14\xff\xc7\u03c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbam1\xb9\xa2a\xd6@\xb5\u07a5\x1e\xf2\x16,1\t\xf1\uba0a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xbap\xe8\xb4u\x9c\f<\x82\xcc\x00\xacN\x9a\x94\xdd[\xaf\xb2\xb8\x890C\xfa3\xc4\x12\xd7\x00\x00\u07d4\xba\x8ac\xf3\xf4\r\u4a03\x88\xbcP!/\xea\x8e\x06O\xbb\x86\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\x8eF\u059d.#C\xd8l`\xd8,\xf4, A\xa0\xc1\u0089\x05k\xc7^-c\x10\x00\x00\u07d4\xba\xa4\xb6L+\x15\xb7\x9f_ BF\xfdp\xbc\xbd\x86\xe4\xa9*\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xba\u0212,J\xcc},\xb6\xfdY\xa1N\xb4\\\xf3\xe7\x02!K\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xba\xd25\xd5\b]\u01f0h\xa6|A&w\xb0>\x186\x88L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\xd4B^\x17\x1c>r\x97^\xb4j\xc0\xa0\x15\xdb1Z]\x8f\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xba\xdc*\xef\x9fYQ\xa8\u05cak5\xc3\u0433\xa4\xe6\xe2\xe79\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xba\xdeCY\x9e\x02\xf8OL0\x14W\x1c\x97k\x13\xa3le\xab\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xba\xe9\xb8/r\x99c\x14\be\x9d\xd7N\x89\x1c\xb8\xf3\x86\x0f\xe5\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xbb\x03f\xa7\u03fd4E\xa7\r\xb7\xfeZ\xe3H\x85uO\xd4h\x8a\x01M\xef,B\xeb\xd6@\x00\x00\u07d4\xbb\aj\xac\x92 \x80i\xea1\x8a1\xff\x8e\xeb\x14\xb7\xe9\x96\xe3\x89\b\x13\xcaV\x90m4\x00\x00\u07d4\xbb\bW\xf1\xc9\x11\xb2K\x86\u0227\x06\x81G?\u6aa1\xcc\xe2\x89\x05k\xc7^-c\x10\x00\x00\u0794\xbb\x19\xbf\x91\u02edt\xcc\xeb_\x81\x1d\xb2~A\x1b\xc2\xea\x06V\x88\xf4?\xc2\xc0N\xe0\x00\x00\xe0\x94\xbb'\u01a7\xf9\x10uGZ\xb2)a\x90@\xf8\x04\xc8\xeczj\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xbb7\x1cr\xc9\xf01l\xea+\xd9\xc6\xfb\xb4\a\x9ewT)\xef\x89_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94\xbb;\x01\v\x18\xe6\xe2\xbe\x115\x87\x10&\xb7\xba\x15\xea\x0f\xde$\x8a\x02 |\x800\x9bwp\x00\x00\xe0\x94\xbb;\x90\x05\xf4o\xd2\xca;0\x16%\x99\x92\x8cw\xd9\xf6\xb6\x01\x8a\x01\xb1\xae\u007f+\x1b\xf7\xdb\x00\x00\u07d4\xbb?\xc0\xa2\x9c\x03Mq\b\x12\xdc\xc7u\xc8\u02b9\u048diu\x899\xd4\xe8D\xd1\xcf_\x00\x00\u07d4\xbbH\xea\xf5\x16\xce-\xec>A\xfe\xb4\xc6y\xe4\x95vA\x16O\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\xbbKJKT\x80p\xffAC,\x9e\b\xa0\xcao\xa7\xbc\x9fv\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\xbbV\xa4\x04r<\xff \xd0hT\x88\xb0Z\x02\xcd\xc3Z\xac\xaa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbba\x8e%\"\x1a\u0667@\xb2\x99\xed\x14\x06\xbc94\xb0\xb1m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbba\xa0K\xff\xd5|\x10G\rE\u00d1\x03\xf6FP4v\x16\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbbh#\xa1\xbd\x81\x9f\x13QU8&J-\xe0R\xb4D\"\b\x89\x01ch\xffO\xf9\xc1\x00\x00\u07d4\xbbl(J\xac\x8ai\xb7\\\u0770\x0f(\xe1EX;V\xbe\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xbbu\xcbPQ\xa0\xb0\x94KFs\xcau*\x97\x03\u007f|\x8c\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbb\x99;\x96\xee\x92Z\xda}\x99\u05c6W=?\x89\x18\f\u3a89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xa3\u0180\x04$\x8eH\x95s\xab\xb2t6w\x06k$\u0227\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xa4\xfa\xc3\xc4 9\xd8(\xe7B\xcd\xe0\xef\xff\xe7t\x94\x1b9\x89lj\u04c2\xd4\xfba\x00\x00\u07d4\xbb\xa8\xab\"\xd2\xfe\xdb\xcf\xc6?hL\b\xaf\xdf\x1c\x17P\x90\xb5\x89\x05_)\xf3~N;\x80\x00\u07d4\xbb\xa9v\xf1\xa1!_u\x12\x87\x18\x92\xd4_pH\xac\xd3V\u0209lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbb\xab\x00\v\x04\b\xed\x01Z7\xc0GG\xbcF\x1a\xb1N\x15\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xbb\xab\xf6d;\xebK\xd0\x1c\x12\v\xd0Y\x8a\t\x87\xd8)g\u0449\xb52\x81x\xad\x0f*\x00\x00\u07d4\xbb\xb4\xee\x1d\x82\xf2\xe1VD,\xc938\xa2\xfc(o\xa2\x88d\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\xbb\xb5\xa0\xf4\x80,\x86H\x00\x9e\x8ai\x98\xaf5,\u0787TO\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4\xbb\xb6C\xd2\x18{6J\xfc\x10\xa6\xfd6\x8d}U\xf5\r\x1a<\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbb\xb8\xff\xe4?\x98\u078e\xae\x18F#\xaeRd\xe4$\u0438\u05c9\x05\xd5?\xfd\xe9(\b\x00\x00\u07d4\xbb\xbdn\u02f5u(\x91\xb4\u03b3\xcc\xe7:\x8fGpY7o\x89\x01\xf3\x99\xb1C\x8a\x10\x00\x00\u07d4\xbb\xbf9\xb1\xb6y\x95\xa4\"APO\x97\x03\u04a1JQV\x96\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xbb\xc8\xea\xffc~\x94\xfc\u014d\x91\xdb\\\x89\x12\x1d\x06\xe1/\xff\x98\x80\x00\u07d4\xbc\u065e\xdc!`\xf2\x10\xa0^:\x1f\xa0\xb0CL\xed\x00C\x9b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbc\u07ec\xb9\xd9\x02<4\x17\x18.\x91\x00\xe8\xea\x1d73\x93\xa3\x89\x034-`\xdf\xf1\x96\x00\x00\u07d4\xbc\xe1>\"2*\u03f3U\xcd!\xfd\r\xf6\f\xf9:\xdd&\u0189\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xbc\xe4\x04u\xd3E\xb0q-\xeep=\x87\xcdvW\xfc\u007f;b\x8a\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\xbc\xed\xc4&|\u02c9\xb3\x1b\xb7d\xd7!\x11q\x00\x8d\x94\xd4M\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbc\xfc\x98\xe5\xc8+j\xdb\x18\n?\xcb\x12\v\x9av\x90\xc8j?\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbd\x04;g\xc6>`\xf8A\xcc\xca\x15\xb1)\xcd\xfee\x90\xc8\xe3\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbd\x04\u007f\xf1\xe6\x9c\u01b2\x9a\xd2d\x97\xa9\xa6\xf2z\x90?\xc4\u0749.\xe4IU\b\x98\xe4\x00\x00\u07d4\xbd\b\xe0\xcd\xde\xc0\x97\xdby\x01\ua05a=\x1f\xd9\u0789Q\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbd\t\x12l\x89\x1cJ\x83\x06\x80Y\xfe\x0e\x15ylFa\xa9\xf4\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xbd\f\\\u05d9\xeb\u0106B\xef\x97\xd7N\x8eB\x90d\xfe\u4489\x11\xac(\xa8\xc7)X\x00\x00\u07d4\xbd\x17\xee\xd8+\x9a%\x92\x01\x9a\x1b\x1b<\x0f\xba\xd4\\@\x8d\"\x89\r\x8drkqw\xa8\x00\x00\u07d4\xbd\x18\x037\v\u0771)\xd29\xfd\x16\xea\x85&\xa6\x18\x8a\u5389\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbd+p\xfe\xcc7d\x0fiQO\xc7\xf3@IF\xaa\xd8k\x11\x89A\rXj \xa4\xc0\x00\x00\u07d4\xbd0\x97\xa7\x9b<\r.\xbf\xf0\xe6\xe8j\xb0\xed\xad\xbe\xd4p\x96\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xbd2]@)\xe0\xd8r\x9fm9\x9cG\x82$\xae\x9ez\xe4\x1e\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xbdC*9\x16$\x9bG$):\xf9\x14nI\xb8(\n\u007f*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbdG\xf5\xf7n;\x93\x0f\xd9HR\t\xef\xa0\xd4v=\xa0uh\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbdK`\xfa\xect\n!\xe3\a\x13\x91\xf9j\xa54\xf7\xc1\xf4N\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xbdK\u0571\"\xd8\xef{|\x8f\x06gE\x03 \xdb!\x16\x14.\x89 \x86\xac5\x10R`\x00\x00\u07d4\xbdQ\xee.\xa1C\u05f1\u05b7~~D\xbd\xd7\xda\x12\U00105b09G~\x06\u0332\xb9(\x00\x00\u07d4\xbdY\tN\aO\x8dy\x14*\xb1H\x9f\x14\x8e2\x15\x1f \x89\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbdZ\x8c\x94\xbd\x8b\xe6G\x06D\xf7\f\x8f\x8a3\xa8\xa5\\cA\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbd^G:\xbc\xe8\xf9zi2\xf7|/\xac\xaf\x9c\xc0\xa0\x05\x14\x89<\x92X\xa1\x06\xa6\xb7\x00\x00\u07d4\xbd_F\u02ab,=K(\x93\x96\xbb\xb0\u007f *\x06\x11>\xd4\xc3\xfb\xa1\xa8\x91;\x19@~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\x9eV\xe9\x02\xf4\xbe\x1f\xc8v\x8d\x808\xba\xc6>*\u02ff\x8e\x8965f3\xeb\xd8\xea\x00\x00\u07d4\xbd\xa4\xbe1~~K\xed\x84\xc0I^\xee2\xd6\a\xec8\xcaR\x89}2'yx\xefN\x80\x00\u07d4\xbd\xb6\v\x82:\x11s\xd4Z\a\x92$_\xb4\x96\xf1\xfd3\x01\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xba\xf6CM@\xd65[\x1e\x80\xe4\f\u012b\x9ch\xd9a\x16\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbd\xc0,\xd43\f\x93\xd6\xfb\xdaOm\xb2\xa8]\xf2/C\xc23\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xc4aF+c\"\xb4b\xbd\xb3?\"y\x9e\x81\b\xe2A}\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xbd\xc79\xa6\x99p\v.\x8e,JL{\x05\x8a\x0eQ=\u07be\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xc7Hs\xaf\x92+\x9d\xf4t\x85;\x0f\xa7\xff\v\xf8\xc8&\x95\x89\xd8\xc9F\x00c\xd3\x1c\x00\x00\u07d4\xbd\xca*\x0f\xf3E\x88\xafb_\xa8\xe2\x8f\xc3\x01Z\xb5\xa3\xaa\x00\x89~\xd7?w5R\xfc\x00\x00\u07d4\xbd\xd3%N\x1b:m\xc6\xcc,i}Eq\x1a\xca!\xd5\x16\xb2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbd\u07e3M\x0e\xbf\x1b\x04\xafS\xb9\x9b\x82IJ\x9e=\x8a\xa1\x00\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xbd\xe4\xc7?\x96\x9b\x89\xe9\u03aef\xa2\xb5\x18DH\x0e\x03\x8e\x9a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbd\xe9xj\x84\xe7[H\xf1\x8erm\u05cdp\xe4\xaf>\xd8\x02\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4\xbd\xed\x11a/\xb5\xc6\u0699\xd1\xe3\x0e2\v\xc0\x99Tf\x14\x1e\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xbd\xed~\a\xd0q\x1ehM\xe6Z\u0232\xabW\xc5\\\x1a\x86E\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\xbd\xf6\x93\xf83\xc3\xfeG\x17S\x18G\x88\xebK\xfeJ\xdc?\x96\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbd\xf6\xe6\x8c\f\xd7X@\x80\xe8G\xd7,\xbb#\xaa\xd4j\xeb\x1d\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbe\n/8_\t\xdb\xfc\xe9g2\xe1+\xb4\n\xc3I\x87\x1b\xa8\x89WL\x11^\x02\xb8\xbe\x00\x00\u07d4\xbe\f*\x80\xb9\xde\bK\x17(\x94\xa7l\xf4szOR\x9e\x1a\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xbe\x1c\xd7\xf4\xc4r\a\th\xf3\xbd\xe2h6k!\xee\xea\x83!\x89\xe9\x1a|\u045f\xa3\xb0\x00\x00\u07d4\xbe#F\xa2\u007f\xf9\xb7\x02\x04OP\r\xef\xf2\xe7\xff\xe6\x82EA\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbe$q\xa6\u007f`G\x91\x87r\xd0\xe3h9%^\xd9\u0591\xae\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbe+\"\x80R7h\xea\x8a\xc3\\\xd9\xe8\x88\xd6\nq\x93\x00\u0509lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbe+2nx\xed\x10\xe5P\xfe\xe8\xef\xa8\xf8\a\x03\x96R/Z\x8a\bW\xe0\xd6\xf1\xdav\xa0\x00\x00\xe0\x94\xbe0Zyn3\xbb\xf7\xf9\xae\xaee\x12\x95\x90f\xef\xda\x10\x10\x8a\x02M\xceT\xd3J\x1a\x00\x00\x00\u07d4\xbeG\x8e\x8e=\xdek\xd4\x03\xbb-\x1ce|C\x10\xee\x19'#\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\xbeN}\x98?.*ck\x11\x02\xecp9\xef\xeb\xc8B\u9349\x03\x93\xef\x1aQ'\xc8\x00\x00\u07d4\xbeO\xd0sap\"\xb6\u007f\\\x13I\x9b\x82\u007fv69\xe4\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbeRZ3\xea\x91aw\xf1r\x83\xfc\xa2\x9e\x8b5\v\u007fS\v\x89\x8f\x01\x9a\xafF\xe8x\x00\x00\u07d4\xbeS2/C\xfb\xb5\x84\x94\xd7\xcc\xe1\x9d\xda'+$P\xe8'\x89\n\xd7\u03afB\\\x15\x00\x00\u07d4\xbeS\x82F\xddNo\f \xbfZ\xd17<;F:\x13\x1e\x86\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbeZ`h\x99\x98c\x9a\xd7[\xc1\x05\xa3qt>\xef\x0fy@\x89\x1b2|s\xe1%z\x00\x00\u07d4\xbe\\\xba\x8d7By\x86\xe8\xca&\x00\xe8X\xbb\x03\xc3YR\x0f\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\xbe`\x03~\x90qJK\x91~a\xf1\x93\xd84\x90g\x03\xb1:\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xbec:77\xf6\x849\xba\xc7\xc9\nR\x14 X\ue38ao\x894\n\xad!\xb3\xb7\x00\x00\x00\xe0\x94\xbee\x9d\x85\xe7\xc3O\x883\xea\u007fH\x8d\xe1\xfb\xb5\xd4\x14\x9b\xef\x8a\x01\xeb\xd2:\xd9\u057br\x00\x00\u07d4\xbes'M\x8cZ\xa4J<\xbe\xfc\x82c\xc3{\xa1!\xb2\n\u04c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbe\x86\u0430C\x84\x19\u03b1\xa081\x927\xbaR\x06\xd7.F\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xbe\x8d\u007f\x18\xad\xfe]l\xc7u9I\x89\xe1\x93\f\x97\x9d\x00}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbe\x91\x86\xc3JRQJ\xbb\x91\a\x86\x0fgO\x97\xb8!\xbd[\x89\x1b\xa0\x1e\xe4\x06\x03\x10\x00\x00\u07d4\xbe\x93W\x93\xf4[p\xd8\x04]&T\xd8\xdd:\xd2K[a7\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xbe\x98\xa7\u007f\xd4\x10\x97\xb3OY\xd7X\x9b\xaa\xd0!e\x9f\xf7\x12\x890\xca\x02O\x98{\x90\x00\x00\u07d4\xbe\x9b\x8c4\xb7\x8e\xe9G\xff\x81G.\xdaz\xf9\xd2\x04\xbc\x84f\x89\b!\xab\rD\x14\x98\x00\x00\u07d4\xbe\xa0\r\xf1pg\xa4:\x82\xbc\x1d\xae\xca\xfbl\x140\x0e\x89\xe6\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xbe\xa0\xaf\xc9:\xae!\b\xa3\xfa\xc0Yb;\xf8o\xa5\x82\xa7^\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xbe\xb35\x8cP\u03dfu\xff\xc7mD<,\u007fU\aZ\x05\x89\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xbe\xb4\xfd1UYC`E\u0739\x9dI\xdc\xec\x03\xf4\fB\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\xbe\xc2\xe6\xde9\xc0|+\xaeUj\u03fe\xe2\xc4r\x8b\x99\x82\xe3\x89\x1f\x0f\xf8\xf0\x1d\xaa\xd4\x00\x00\u07d4\xbe\xc6d\x0fI\t\xb5\x8c\xbf\x1e\x80cB\x96\x1d`u\x95\tl\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xbe\xc8\xca\xf7\xeeIF\x8f\xeeU.\xff:\xc5#N\xb9\xb1}B\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbe\xce\xf6\x1c\x1cD+\xef|\xe0Ks\xad\xb2I\xa8\xba\x04~\x00\x896;V\u00e7T\xc8\x00\x00\u0794\xbe\xd4d\x9d\xf6F\u2052)\x03-\x88hUo\xe1\xe0S\u04c8\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\xbe\xd4\xc8\xf0\x06\xa2|\x1e_|\xe2\x05\xdeu\xf5\x16\xbf\xb9\xf7d\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xbe\xe8\u0430\bB\x19T\xf9-\x00\r9\x0f\xb8\xf8\xe6X\xea\xee\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbe\xec\u05af\x90\f\x8b\x06J\xfc\xc6\a?-\x85\u055a\xf1\x19V\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbe\xef\x94!8y\xe0&\"\x14+\xeaa)\tx\x93\x9a`\u05ca\x016\x85{2\xad\x86\x04\x80\x00\xe0\x94\xbe\xf0}\x97\xc3H\x1f\x9dj\xee\x1c\x98\xf9\xd9\x1a\x18\n2D+\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xbe\xfbD\x8c\f_h?\xb6~\xe5p\xba\xf0\xdbV\x86Y\x97Q\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbf\x05\a\f,4!\x93\x11\xc4T\x8b&\x14\xa48\x81\r\xedm\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbf\x05\xff^\xcf\r\xf2\u07c8wY\xfb\x82t\xd928\xac&}\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\xbf\t\xd7pH\xe2p\xb6b3\x0e\x94\x86\xb3\x8bC\xcdx\x14\x95\x8a\\S\x9b{\xf4\xff(\x80\x00\x00\u07d4\xbf\x17\xf3\x97\xf8\xf4o\x1b\xaeE\u0447\x14\x8c\x06\xee\xb9Y\xfaM\x896I\u0156$\xbb0\x00\x00\u07d4\xbf\x186A\xed\xb8\x86\xce`\xb8\x19\x02a\xe1OB\xd9<\xce\x01\x89\x01[5W\xf1\x93\u007f\x80\x00\u07d4\xbf*\xeaZ\x1d\xcfn\u04f5\xe829D\xe9\x83\xfe\xdf\u046c\xfb\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xbf@\x96\xbcT}\xbf\xc4\xe7H\t\xa3\x1c\x03\x9e{8\x9d^\x17\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xbfI\xc1H\x981eg\u0637\t\xc2\xe5\x05\x94\xb3f\xc6\u04cc\x89'\xbf8\xc6TM\xf5\x00\x00\u07d4\xbfLs\xa7\xed\xe7\xb1d\xfe\a!\x14\x846T\xe4\xd8x\x1d\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xbfP\xce.&K\x9f\xe2\xb0h0az\xed\xf5\x02\xb25\x1bE\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbfY\xae\xe2\x81\xfaC\xfe\x97\x19CQ\xa9\x85~\x01\xa3\xb8\x97\xb2\x89 \x86\xac5\x10R`\x00\x00\u07d4\xbfh\u048a\xaf\x1e\xee\xfe\xf6F\xb6^\x8c\xc8\u0450\xf6\xc6\u069c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbfi%\xc0\aQ\x00\x84@\xa6s\x9a\x02\xbf+l\u06ab^:\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbfw\x01\xfcb%\u0561x\x15C\x8a\x89A\xd2\x1e\xbc]\x05\x9d\x89e\xea=\xb7UF`\x00\x00\u07d4\xbf\x8b\x80\x05\xd66\xa4\x96d\xf7Bu\xefBC\x8a\xcde\xac\x91\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbf\x92A\x8a\fl1$M\"\x02`\xcb>\x86}\u05f4\xefI\x89\x05i\x00\xd3<\xa7\xfc\x00\x00\u07d4\xbf\x9a\xcdDE\xd9\xc9UF\x89\u02bb\xba\xb1\x88\x00\xff\x17A\u008965\u026d\xc5\u07a0\x00\x00\u07d4\xbf\x9f'\x1fz~\x12\xe3m\xd2\xfe\x9f\xac\xeb\xf3\x85\xfeaB\xbd\x89\x03f\xf8O{\xb7\x84\x00\x00\u07d4\xbf\xa8\xc8X\xdf\x10,\xb1$!\x00\x8b\n1\xc4\xc7\x19\n\xd5`\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbf\xae\xb9\x10ga}\u03cbD\x17+\x02\xafaVt\x83]\xba\x89\b\xb5\x9e\x88H\x13\b\x80\x00\xe0\x94\xbf\xb0\xea\x02\xfe\xb6\x1d\xec\x9e\"\xa5\a\tY3\x02\x99\xc40r\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xbf\xbc\xa4\x18\xd3R\x9c\xb3\x93\b\x10b\x03*n\x11\x83\u01b2\u070a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xbf\xbe\x05\u831c\xbb\xcc\x0e\x92\xa4\x05\xfa\xc1\xd8]\xe2H\xee$\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xbf\xbf\xbc\xb6V\u0099+\xe8\xfc\u0782\x19\xfb\xc5J\xad\u055f)\x8a\x02\x1e\x18\xd2\xc8!\xc7R\x00\x00\u07d4\xbf\xc5z\xa6f\xfa\u239f\x10zI\xcbP\x89\xa4\xe2!Q\u074965\u026d\xc5\u07a0\x00\x00\u07d4\xbf\u02d70$c\x04p\r\xa9\vAS\xe7\x11Ab.\x1cA\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbf\xd9<\x90\u009c\a\xbc_\xb5\xfcI\xae\xeaU\xa4\x0e\x13O5\x8a\x05\xed\xe2\x0f\x01\xa4Y\x80\x00\x00\xe0\x94\xbf\xe3\xa1\xfcn$\xc8\xf7\xb3%\x05`\x99\x1f\x93\u02e2\u03c0G\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xbf\u6f30\xf0\xc0xRd3$\xaa]\xf5\xfdb%\xab\xc3\u0289\x04\t\xe5+H6\x9a\x00\x00\u07d4\xbf\xf5\xdfv\x994\xb8\x94<\xa9\x13}\x0e\xfe\xf2\xfen\xbb\xb3N\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbf\xfbi)$\x1fx\x86\x93'>p\"\xe6\x0e>\xab\x1f\xe8O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\x06O\x1d\x94t\xab\x91]V\x90l\x9f\xb3 \xa2\xc7\t\x8c\x9b\x89\x13h?\u007f<\x15\xd8\x00\x00\u07d4\xc0\a\xf0\xbd\xb6\xe7\x00\x92\x02\xb7\xaf>\xa9\t\x02i|r\x14\x13\x89\xa2\xa0\xe4>\u007f\xb9\x83\x00\x00\u07d4\xc0\n\xb0\x80\xb6C\xe1\u00ba\xe3c\xe0\u0455\xde.\xff\xfc\x1cD\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\xc0 wD\x9a\x13Jz\xd1\xef~M\x92z\xff\xec\ueb75\xae\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xc0$q\xe3\xfc.\xa0S&\x15\xa7W\x1dI2\x89\xc1<6\xef\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0-n\xad\xea\xcf\x1bx\xb3\u0285\x03\\c{\xb1\xce\x01\xf4\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc03\xb12Z\n\xf4Tr\xc2U'\x85;\x1f\x1c!\xfa5\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xc03\xbe\x10\xcbHa;\xd5\xeb\xcb3\xedI\x02\xf3\x8bX0\x03\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc04[3\xf4\x9c\xe2\u007f\xe8,\xf7\xc8M\x14\x1ch\xf5\x90\xcev\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc0=\xe4*\x10\x9bezd\xe9\"$\xc0\x8d\xc1'^\x80\u0672\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0@i\u07f1\x8b\tlxg\xf8\xbe\xe7zm\xc7Gz\xd0b\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xc0A?Z|-\x9aK\x81\b(\x9e\xf6\xec\xd2qx\x15$\xf4\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xc0C\xf2E-\u02d6\x02\xefb\xbd6\x0e\x03=\xd29q\xfe\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc0OK\xd4\x04\x9f\x04F\x85\xb8\x83\xb6)Y\xaec\x1df~5\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\xc0V\u053dk\xf3\u02ec\xace\xf8\xf5\xa0\xe3\x98\v\x85'@\xae\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xc0[t\x06 \xf1s\xf1nRG\x1d\u00cb\x9cQJ\v\x15&\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\xc0i\xef\x0e\xb3B\x99\xab\xd2\xe3-\xab\xc4yD\xb2r3H$\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\xc0l\xeb\xbb\xf7\xf5\x14\x9af\xf7\xeb\x97k>G\xd5e\x16\xda/\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc0r^\u00bd\xc3:\x1d\x82`q\u07a2\x9db\xd48Z\x8c%\x8a\b\xa0\x85\x13F:\xa6\x10\x00\x00\u07d4\xc0~8g\xad\xa0\x96\x80z\x05\x1al\x9c4\xcc;?J\xd3J\x89`\xf0f \xa8IE\x00\x00\u07d4\xc0\x89^\xfd\x05m\x9a:\x81\xc3\xdaW\x8a\xda1\x1b\xfb\x93V\u03c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc0\x90\xfe#\xdc\xd8k5\x8c2\xe4\x8d*\xf9\x10$%\x9fef\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc0\x9af\x17*\xea7\r\x9ac\xda\x04\xffq\xff\xbb\xfc\xff\u007f\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\x9e<\xfc\x19\xf6\x05\xff>\xc9\xc9\xc7\x0e%@\xd7\xee\x97Cf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc0\xa0*\xb9N\xbeV\xd0E\xb4\x1bb\x9b\x98F.:\x02J\x93\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xc0\xa3\x93\b\xa8\x0e\x9e\x84\xaa\xaf\x16\xac\x01\xe3\xb0\x1dt\xbdk-\x89\afM\xddL\x1c\v\x80\x00\u07d4\xc0\xa6\u02edwi*=\x88\xd1A\xefv\x9a\x99\xbb\x9e<\x99Q\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xc0\xa7\xe8C]\xff\x14\xc2Uws\x9d\xb5\\$\u057fW\xa3\u064a\nm\xd9\f\xaeQ\x14H\x00\x00\u07d4\xc0\xae\x14\xd7$\x83./\xce'x\xde\u007f{\x8d\xaf{\x12\xa9>\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0\xaf\xb7\u0637\x93p\xcf\xd6c\u018c\u01b9p*7\u035e\xff\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc0\xb0\xb7\xa8\xa6\xe1\xac\xdd\x05\xe4\u007f\x94\xc0\x96\x88\xaa\x16\u01ed\x8d\x89\x03{m\x02\xacvq\x00\x00\xe0\x94\xc0\xb3\xf2D\xbc\xa7\xb7\xde[H\xa5>\u06dc\xbe\xab\vm\x88\xc0\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\xc0\xc0M\x01\x06\x81\x0e>\xc0\xe5J\x19\U000ab157\xe6\x9aW=\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xc0\xca2w\x94.tE\x87K\xe3\x1c\xeb\x90)rqO\x18#\x89\r\x8drkqw\xa8\x00\x00\u07d4\xc0\u02ed<\xcd\xf6T\xda\"\xcb\xcf\\xe\x97\xca\x19U\xc1\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\xcb\xf6\x03/\xa3\x9e|F\xffw\x8a\x94\xf7\xd4E\xfe\"\xcf0\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4\xc0\xe0\xb9\x03\b\x8e\fc\xf5=\xd0iWTR\xaf\xf5$\x10\u00c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc0\xe4W\xbdV\xec6\xa1$k\xfa20\xff\xf3\x8eY&\xef\"\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xc0\xed\rJ\xd1\r\xe045\xb1S\xa0\xfc%\xde;\x93\xf4R\x04\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\xc0\xf2\x9e\xd0\af\x11\xb5\xe5^\x13\x05G\xe6\x8aH\xe2m\xf5\u4262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc1\x13(x#\\]\u06e5\xd9\xf3\"\x8bR6\xe4p \xdco\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc1\x17\r\xba\xad\xb3\xde\xe6\x19\x8e\xa5D\xba\xec\x93%\x18`\xfd\xa5\x89A\rXj \xa4\xc0\x00\x00\xe0\x94\xc1&W=\x87\xb0\x17ZR\x95\xf1\xdd\a\xc5u\u03cc\xfa\x15\xf2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc1'\xaa\xb5\x90e\xa2\x86D\xa5k\xa3\xf1^.\xac\x13\xda)\x95\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xc1+\u007f@\u07da/{\xf9\x83f\x14\"\xab\x84\xc9\xc1\xf5\bX\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xc1,\xfb{=\xf7\x0f\xce\xca\x0e\xde&5\x00\xe2xs\xf8\xed\x16\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc1/\x88\x1f\xa1\x12\xb8\x19\x9e\xcb\xc7>\xc4\x18W\x90\xe6\x14\xa2\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc18Lnq~\xbeK#\x01NQ\xf3\x1c\x9d\xf7\xe4\xe2[1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc1C\x8c\x99\xddQ\xef\x1c\xa88j\xf0\xa3\x17\xe9\xb0AEx\x88\x89\f\x1d\xaf\x81\u0623\xce\x00\x00\u07d4\xc1c\x12(\xef\xbf*.:@\x92\xee\x89\x00\xc69\xed4\xfb\u02093\xc5I\x901r\f\x00\x00\u07d4\xc1u\xbe1\x94\xe6iB-\x15\xfe\xe8\x1e\xb9\xf2\xc5lg\xd9\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc1\x82v\x86\xc0\x16\x94\x85\xec\x15\xb3\xa7\xc8\xc0\x15\x17\xa2\x87M\xe1\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xc1\x8a\xb4g\xfe\xb5\xa0\xaa\xdf\xff\x91#\x0f\xf0VFMx\xd8\x00\x89lk\x93[\x8b\xbd@\x00\x00\u0794\xc1\x95\x05CUM\x8aq0\x03\xf6b\xbba,\x10\xadL\xdf!\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xc1\xa4\x1aZ'\x19\x92&\xe4\xc7\xeb\x19\x8b\x03\x1bY\x19o\x98B\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xc1\xb2\xa0\xfb\x9c\xadE\xcdi\x91\x92\xcd'T\v\x88\xd38By\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc1\xb2\xaa\x8c\xb2\xbfb\xcd\xc1:G\xec\xc4e\u007f\xac\xaa\x99_\x98\x8967\x93\xfa\x96\u6980\x00\u07d4\xc1\xb5\x00\x01\x1c\xfb\xa9]|\xd66\xe9^l\xbfagFK%\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc1\xb9\xa5pM5\x1c\xfe\x98?y\xab\xee\xc3\u06fb\xae;\xb6)\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc1\xcb\xd2\xe23*RL\xf2\x19\xb1\r\x87\x1c\xcc \xaf\x1f\xb0\xfa\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xc1\xcd\xc6\x01\xf8\x9c\x04(\xb3\x13\x02\u0447\xe0\xdc\b\xad}\x1cW\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xc1\u052f8\xe9\xbay\x90@\x89HI\xb8\xa8!\x93u\xf1\xacx\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc1\xe1@\x9c\xa5,%CQ4\xd0\x06\u00a6\xa8T-\xfbrs\x89\x01\xdd\x1eK\xd8\xd1\xee\x00\x00\u07d4\xc1\xeb\xa5hJ\xa1\xb2L\xbac\x15\x02c\xb7\xa9\x13\x1a\xee\u008d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc1\xec\x81\xdd\x12=K|-\u0674\xd48\xa7\a,\x11\u0707L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc1\xf3\x9b\xd3]\xd9\xce\xc37\xb9oG\xc6w\x81\x81`\xdf7\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\xc1\xff\xad\a\u06d6\x13\x8cK*S\x0e\xc1\xc7\xde)\xb8\xa0Y,\x88\xf4?\xc2\xc0N\xe0\x00\x00\xe0\x94\xc2\x1f\xa6d:\x1f\x14\xc0)\x96\xadqD\xb7Y&\xe8~\xcbK\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc24\nL\xa9L\x96x\xb7IL<\x85%(\xed\xe5\xeeR\x9f\x89\x02\xa3k\x05\xa3\xfd|\x80\x00\u07d4\xc29\xab\u07ee>\x9a\xf5E\u007fR\xed+\x91\xfd\n\xb4\xd9\xc7\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2;/\x92\x1c\xe4\xa3z%\x9e\u4b4b!X\xd1]fOY\x89\x01`\x89\x95\xe8\xbd?\x80\x00\u07d4\xc2C\x99\xb4\xbf\x86\xf73\x8f\xbfd^;\"\xb0\u0dd79\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2L\u03bc#D\xcc\xe5d\x17\xfbhL\xf8\x16\x13\xf0\xf4\xb9\xbd\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xc2Rf\xc7gf2\xf1>\xf2\x9b\xe4U\ud50a\xddVw\x92\x89Hz\x9a0E9D\x00\x00\u07d4\xc2\\\xf8&U\f\x8e\xaf\x10\xaf\"4\xfe\xf9\x04\u0779R\x13\xbe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc2f?\x81E\xdb\xfe\xc6\xc6F\xfc\\I\x96\x13E\xde\x1c\x9f\x11\x89%g\xacp9+\x88\x00\x00\u07d4\xc2pEh\x854+d\vL\xfc\x1bR\x0e\x1aTN\xe0\xd5q\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xc2sv\xf4]!\xe1^\xde;&\xf2e_\xce\xe0,\xcc\x0f*\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xc2w\x97q\xf0Smy\xa8p\x8fi1\xab\xc4K05\u964a\x047\u04ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xc2\xc1>r\xd2h\xe7\x15\r\u01d9\xe7\xc6\xcf\x03\u0209T\xce\u05c9%\xf2s\x93=\xb5p\x00\x00\u07d4\xc2\xcb\x1a\xda]\xa9\xa0B8s\x81G\x93\xf1aD\xef6\xb2\xf3\x89HU~;p\x17\xdf\x00\x00\u07d4\xc2\xd1w\x8e\xf6\xee_\xe4\x88\xc1E\xf3Xkn\xbb\xe3\xfb\xb4E\x89>\x1f\xf1\xe0;U\xa8\x00\x00\xe0\x94\xc2\xd9\xee\xdb\xc9\x01\x92c\xd9\xd1l\u016e\a-\x1d=\xd9\xdb\x03\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc2\xe0XJq4\x8c\xc3\x14\xb7; )\xb6#\v\x92\u06f1\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2\xe2\u0518\xf7\r\xcd\bY\xe5\v\x02:q\nmK!3\xbd\x8989\x11\xf0\f\xbc\xe1\x00\x00\u07d4\xc2\xed_\xfd\u046d\xd8U\xa2i/\xe0b\xb5\xd6\x18t#`\u0509A\rXj \xa4\xc0\x00\x00\u07d4\xc2\xee\x91\xd3\xefX\xc9\u0465\x89\x84N\xa1\xae1%\xd6\u017ai\x894\x95tD\xb8@\xe8\x00\x00\u07d4\xc2\xfa\xfd\xd3\n\xcbmg\x06\xe9)<\xb0&A\xf9\xed\xbe\a\xb5\x89Q\x00\x86\vC\x0fH\x00\x00\u07d4\xc2\xfd\v\xf7\xc7%\xef>\x04~Z\xe1\u009f\xe1\x8f\x12\xa7)\x9c\x89Hz\x9a0E9D\x00\x00\u07d4\xc2\xfe}us\x1fcm\xcd\t\xdb\xda\x06q9;\xa0\xc8*}\x89wC\"\x17\xe6\x83`\x00\x00\u07d4\xc3\x10z\x9a\xf32-R8\xdf\x012A\x911b\x959W}\x89\x1a\xb4\xe4d\xd4\x141\x00\x00\xe0\x94\xc3\x11\v\xe0\x1d\xc9sL\xfcn\x1c\xe0\u007f\x87\xd7}\x13E\xb7\xe1\x8a\x01\x0f\f\xe9I\xe0\x0f\x93\x00\x00\u07d4\xc3 8\xcaR\xae\xe1\x97E\xbe\\1\xfc\xdcT\x14\x8b\xb2\xc4\u0409\x02\xb5\xaa\xd7,e \x00\x00\u07d4\xc3%\xc3R\x80\x1b\xa8\x83\xb3\"l_\xeb\r\xf9\xea\xe2\xd6\xe6S\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xc3.\xc7\xe4*\xd1l\xe3\xe2UZ\xd4\xc5C\x06\xed\xa0\xb2gX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc32\xdfP\xb1<\x014\x90\xa5\xd7\xc7]\xbf\xa3f\u0687\xb6\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc3:\u0373\xba\x1a\xab'P{\x86\xb1]g\xfa\xf9\x1e\xcfb\x93\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3>\u0393Z\x8fN\xf98\xea~\x1b\xac\x87\u02d2]\x84\x90\u028a\a\x03\x8c\x16x\x1fxH\x00\x00\u07d4\xc3@\xf9\xb9\x1c&r\x8c1\xd1!\xd5\xd6\xfc;\xb5m=\x86$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3F\xcb\x1f\xbc\xe2\xab(]\x8eT\x01\xf4-\xd7#M7\xe8m\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\xe0\x94\xc3H=n\x88\xac\x1fJ\xe7<\xc4@\x8dl\x03\xab\xe0\xe4\x9d\u028a\x03\x99\x92d\x8a#\u0220\x00\x00\xe0\x94\xc3H\xfcZF\x13#\xb5{\xe3\x03\u02c96\x1b\x99\x19\x13\xdf(\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xc3N;\xa12.\xd0W\x11\x83\xa2O\x94 N\xe4\x9c\x18fA\x89\x03'\xaf\uf927\xbc\x00\x00\xe0\x94\xc3[\x95\xa2\xa3s|\xb8\xf0\xf5\x96\xb3E$\x87+\xd3\r\xa24\x8a\x01\x98\xbe\x85#^-P\x00\x00\xe0\x94\xc3c\x1cv\x98\xb6\xc5\x11\x19\x89\xbfE''\xb3\xf99Zm\xea\x8a\x02C'X\x96d\x1d\xbe\x00\x00\u07d4\xc3l\vc\xbf\xd7\\/\x8e\xfb\x06\b\x83\xd8h\xcc\xcdl\xbd\xb4\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xc3uk\xcd\xcc~\xect\xed\x89j\xdf\xc35'Y0&n\b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u00c4\xacn\xe2|9\xe2\xf2x\xc2 \xbd\xfa[\xae\xd6&\xd9\u04c9 \x86\xac5\x10R`\x00\x00\u07d4\u00e0F\xe3\u04b2\xbfh\x14\x88\x82n2\xd9\xc0aQ\x8c\xfe\x8c\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\u00e9\"j\xe2u\xdf,\xab1+\x91\x10@cJ\x9c\x9c\x9e\xf6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u00f9(\xa7o\xadex\xf0O\x05U\xe69R\xcd!\xd1R\n\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3\xc2)s)\xa6\xfd\x99\x11~T\xfcj\xf3y\xb4\xd5VT~\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc3\xc3\xc2Q\rg\x80 HZcs]\x13\a\xecL\xa60+\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc3\xcbk6\xafD?,n%\x8bJ9U:\x81\x87G\x81\x1f\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\xc3\xdbVW\xbbr\xf1\rX\xf21\xfd\xdf\x11\x98\n\xffg\x86\x93\x8a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\xc3\u06df\xb6\xf4lH\n\xf3De\u05d7S\xb4\xe2\xb7Jg\u038a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc3\xddX\x908\x860;\x92\x86%%z\xe1\xa0\x13\xd7\x1a\xe2\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\xe0G\x1cd\xff5\xfaR2\xcc1!\xd1\u04cd\x1a\x0f\xb7\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\xe2\f\x96\u07cdN8\xf5\v&Z\x98\xa9\x06\xd6\x1b\xc5\x1aq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\u31f0<\xe9\\\xcf\xd7\xfaQ\u0744\x01\x83\xbcCS(\t\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3\xf8\xf6r\x95\xa5\xcd\x04\x93d\xd0]#P&#\xa3\xe5.\x84\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc4\x01\xc4'\xcc\xcf\xf1\r\xec\xb8d /6\xf5\x80\x83\"\xa0\xa8\x89\xb4{Q\xa6\x9c\xd4\x02\x00\x00\u07d4\xc4\b\x8c\x02_>\x85\x01?T9\xfb4@\xa1s\x01\xe5D\xfe\x89~\t\xdbM\x9f?4\x00\x00\u07d4\xc4\x14a\xa3\u03fd2\u0246UU\xa4\x8117\xc0v1#`\x8965\xc6 G9\u0640\x00\u07d4\xc4 8\x8f\xbe\xe8J\xd6V\xddh\xcd\xc1\xfb\xaa\x93\x92x\v4\x89\n-\xcac\xaa\xf4\u0140\x00\u07d4\xc4\"P\xb0\xfeB\xe6\xb7\xdc\xd5\u0210\xa6\xf0\u020f__\xb5t\x89\b\x1e\xe4\x82SY\x84\x00\x00\u07d4\xc4-j\xebq\x0e:P\xbf\xb4Ml1\t)i\xa1\x1a\xa7\xf3\x89\b\"c\xca\xfd\x8c\xea\x00\x00\xe0\x94\xc4@\xc7\xca/\x96Kir\xeffJ\"a\xdd\xe8\x92a\x9d\x9c\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xc4K\xde\xc8\xc3l\\h\xba\xa2\xdd\xf1\xd41i2)rlC\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xc4OJ\xb5\xbc`9|s~\xb0h3\x91\xb63\xf8\xa2G\x1b\x12\x1c\xa4\x89 .h\xf2\u00ae\xe4\x00\x00\u07d4\xc4h\x1es\xbb\x0e2\xf6\xb7& H1\xffi\xba\xa4\x87~2\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xc4k\xbd\xefv\xd4\xca`\xd3\x16\xc0\u007f]\x1ax\x0e;\x16_~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc4}a\v9\x92P\xf7\x0e\xcf\x13\x89\xba\xb6),\x91&O#\x89\x0f\xa7\xe7\xb5\xdf<\xd0\x00\x00\u07d4\u0100;\xb4\a\xc7b\xf9\vu\x96\xe6\xfd\u1513\x1ev\x95\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0106Q\xc1\xd9\xc1k\xffL\x95T\x88l??&C\x1foh\x89#\xab\x95\x99\xc4?\b\x00\x00\u07d4\u0109\xc8?\xfb\xb0%*\xc0\xdb\xe3R\x12\x17c\x0e\x0fI\x1f\x14\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u010bi<\xac\xef\xdb\xd6\xcb]x\x95\xa4.1\x962~&\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0113H\x9eV\u00fd\xd8)\x00}\xc2\xf9VA)\x06\xf7k\xfa\x89\x02\xa7\x91H\x8eqT\x00\x00\u07d4\u0116\u02f0E\x9aj\x01`\x0f\u0149\xa5Z2\xb4T!\u007f\x9d\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4\u011c\xfa\xa9g\xf3\xaf\xbfU\x03\x10a\xfcL\xef\x88\xf8]\xa5\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0136\xe5\xf0\x9c\xc1\xb9\r\xf0x\x03\xce=M\x13vj\x9cF\xf4\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\u013e\xc9c\b\xa2\x0f\x90\u02b1\x83\x99\u0113\xfd=\x06Z\xbfE\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xc4\xc0\x1a\xfc>\x0f\x04R!\xda\x12\x84\u05c7\x85tD/\xb9\xac\x8a\x01\x92\xb5\u0249\x02J\x19\xc1\xbdo\x12\x80\x00\xe0\x94\xc5\x00\xb7 sN\xd2)8\u05cc^H\xb2\xba\x93g\xa5u\xba\x8a\a\x12\x9e\x1c\xdf7>\xe0\x00\x00\u07d4\xc5\x0f\xe4\x15\xa6A\xb0\x85lNu\xbf\x96\x05\x15D\x1a\xfa5\x8d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5\x13L\xfb\xb1\xdfz \xb0\xedpWb.\xee\u0480\x94}\xad\x89\xcd\xff\x97\xfa\xbc\xb4`\x00\x00\xe0\x94\xc5\x17\xd01\\\x87\x88\x13\xc7\x17\u132f\xa1\xea\xb2eN\x01\u068a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc5\x18y\x9aY%Wb\x13\xe2\x18\x96\xe0S\x9a\xbb\x85\xb0Z\xe3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc5\"\xe2\x0f\xbf\x04\xed\u007fk\x05\xa3{G\x18\xd6\xfc\xe0\x14.\x1a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc5$\bmF\xc8\x11+\x12\x8b/\xafo|}\x81`\xa88l\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc5-\x1a\fs\u00a1\xbe\x84\x91Q\x85\xf8\xb3O\xaa\n\xdf\x1d\xe3\x89K\xe4\xea\xb3\xfa\x0f\xa6\x80\x00\xe0\x94\xc55\x94\xc7\u03f2\xa0\x8f(L\xc9\u05e6;\xbd\xfc\v1\x972\x8a\nk#(\xff:b\xc0\x00\x00\u07d4\xc57I(\xcd\xf1\x93pTC\xb1L\xc2\r\xa4#G<\xd9\u03c9\a}\x10P\x9b\xb3\xaf\x80\x00\u07d4\xc58\xa0\xff(*\xaa_Ku\u03f6,p\x03~\xe6}O\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5;P\xfd;+r\xbclC\v\xaf\x19JQU\x85\u04d8m\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xc5=y\xf7\u02dbp\x95/\xd3\x0f\xceX\xd5K\x9f\vY\xf6G\x8a\x01\x13\xe2\xd6tCE\xf8\x00\x00\u07d4\xc5I\u07c3\xc6\xf6^\xec\x0f\x1d\u0260\x93J\\_:P\xfd\x88\x89\x9d\xc0\\\xce(\u00b8\x00\x00\u07d4\xc5P\x05\xa6\xc3~\x8c\xa7\xe5C\xce%\x99s\xa3\xca\u0396\x1aJ\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5U\xb91V\xf0\x91\x01#\x80\x00\xe0\x94\u0166)\xa3\x96%R\u02ce\xde\u0609cj\xaf\xbd\f\x18\xcee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u016e\x86\xb0\xc6\xc7\xe3\x90\x0f\x13h\x10\\VS\u007f\xaf\x8dt>\x89\n1\x06+\xee\xedp\x00\x00\u07d4\u0170\t\xba\xea\xf7\x88\xa2v\xbd5\x81:\xd6[@\v\x84\x9f;\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0175l\xd24&|(\xe8\x9cok\"f\xb0\x86\xa1/\x97\f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xc5\u01a4\x99\x8a3\xfe\xb7dCz\x8b\xe9)\xa7;\xa3J\ad\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\xe0\x94\xc5\xc7=a\xcc\xe7\xc8\xfeL\x8f\xce)\xf3\x90\x92\xcd\x19>\x0f\xff\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xc5\xc7Y\vV!\xec\xf85\x85\x88\u079bh\x90\xf2baC\U000498a1]\tQ\x9b\xe0\x00\x00\u07d4\xc5\xcd\xce\xe0\xe8]\x11}\xab\xbfSj?@i\xbfD?T\xe7\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xc5\u050c\xa2\xdb/\x85\xd8\xc5U\xcb\x0e\x9c\xfe\x82i6x?\x9e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc5\xde\x12\x03\xd3\xcc,\xea1\xc8.\xe2\xdeY\x16\x88\a\x99\xea\xfd\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xc5\xe4\x88\xcf+Vw\x939q\xf6L\xb8 -\xd0WR\xa2\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc5\xe8\x12\xf7o\x15\xf2\xe1\xf2\xf9\xbcH#H<\x88\x04cog\x89\x03\xf5\x14\x19:\xbb\x84\x00\x00\u07d4\xc5\u94d34\xf1%.\u04ba&\x81D\x87\xdf\u0498+1(\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\xc5\xebB)^\x9c\xad\xea\xf2\xaf\x12\xde\u078a\x8dS\xc5y\xc4i\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\xc5\xed\xbb\xd2\xca\x03WeJ\xd0\xeaG\x93\xf8\xc5\xce\xcd0\xe2T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc5\xf6K\xab\xb7\x031B\xf2\x0eF\u05eab\x01\xed\x86\xf6q\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5\xf6\x87qrF\u068a \r \xe5\u9f2c`\xb6\u007f8a\x89\x01\x8d\x99?4\xae\xf1\x00\x00\u07d4\xc6\x04[<5\vL\xe9\xca\fkuO\xb4\x1ai\xb9~\x99\x00\x892$\xf4'#\xd4T\x00\x00\u07d4\xc6\v\x04eN\x00;F\x83\x04\x1f\x1c\xbdk\u00cf\xda|\xdb\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\x14F\xb7T\xc2N;\x16B\xd9\xe5\x17e\xb4\xd3\xe4k4\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\x18R\x13!\xab\xaf[&Q:J\x95(\bo\"\n\xdco\x89\x01v\xb3D\xf2\xa7\x8c\x00\x00\u07d4\xc6#FW\xa8\a8A&\xf8\x96\x8c\xa1p\x8b\xb0{\xaaI<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc6%\xf8\u024d'\xa0\x9a\x1b\u02bdQ(\xb1\u00a9HV\xaf0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc65^\xc4v\x8cp\xa4\x9a\xf6\x95\x13\u0343\xa5\xbc\xa7\xe3\xb9\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc6:\xc4\x17\x99.\x9f\x9b`8n\xd9S\xe6\xd7\xdf\xf2\xb0\x90\xe8\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xc6<\u05c8!\x18\xb8\xa9\x1e\aML\x8fK\xa9\x18Q0;\x9a\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xc6R\x87\x1d\x19$\"\u01bc#_\xa0c\xb4J~\x1dC\u3149\bg\x0e\x9e\xc6Y\x8c\x00\x00\xe0\x94\xc6gD\x1e\u007f)y\x9a\xbaadQ\xd5;?H\x9f\x9e\x0fH\x8a\x02\xf2\x9a\xceh\xad\u0740\x00\x00\u07d4\xc6j\xe4\xce\xe8\u007f\xb352\x19\xf7\u007f\x1dd\x86\u0140(\x032\x89\x01\x9a\x16\xb0o\xf8\xcb\x00\x00\u07d4\xc6t\xf2\x8c\x8a\xfd\a?\x8by\x96\x91\xb2\xf0XM\xf9B\xe8D\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0197\xb7\x04w\u02b4.+\x8b&f\x81\xf4\xaesu\xbb%A\x8a\x01.W2\xba\xba\\\x98\x00\x00\u07d4\u019b\x85U9\xce\x1b\x04qG(\xee\xc2Z7\xf3g\x95\x1d\xe7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u019b\xe4@\x13Mb\x80\x98\x01D\xa9\xf6M\x84t\x8a7\xf3I\x89&\u009eG\u0104L\x00\x00\u07d4\u019df<\x8d`\x90\x83\x91\xc8\xd26\x19\x153\xfd\xf7wV\x13\x89\x1aJ\xba\"\\ t\x00\x00\u0794\u01a2\x86\xe0e\xc8_:\xf7H\x12\xed\x8b\u04e8\xce]%\xe2\x1d\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\u01a3\x0e\xf5\xbb3 \xf4\r\xc5\xe9\x81#\rR\xae:\xc1\x93\"\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\u01ae(}\xdb\xe1\x14\x9b\xa1m\xdc\xcaO\xe0j\xa2\uaa48\xa9\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc6\xc7\xc1\x917\x98\x97\u075c\x9d\x9a3\x83\x9cJ_b\xc0\x89\r\x89\xd8\xd8T\xb2$0h\x80\x00\xe0\x94\xc6\xcdh\xec56,Z\xd8L\x82\xadN\xdc#!%\x91-\x99\x8a\x05\xe0T\x9c\x962\xe1\xd8\x00\x00\u07d4\xc6\u0615N\x8f?\xc53\xd2\xd20\xff\x02\\\xb4\xdc\xe1O4&\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc6\xdb\u06de\xfd^\xc1\xb3xn\x06q\xeb\"y\xb2S\xf2\x15\xed\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc6\xdf u\xeb\xd2@\xd4Hi\u00bek\u07c2\xe6=N\xf1\xf5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc6\xe2\xf5\xaf\x97\x9a\x03\xfdr:\x1bn\xfar\x83\x18\u03dc\x18\x00\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xc6\xe3$\xbe\xeb[6v^\xcdFB`\xf7\xf2`\x06\xc5\xc6.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\xe4\xcc\fr\x83\xfc\x1c\x85\xbcH\x13\xef\xfa\xafr\xb4\x98#\xc0\x89\x0f\x03\x1e\xc9\xc8}\xd3\x00\x00\xe0\x94\xc6\xee5\x93B)i5)\xdcA\u067bq\xa2IfX\xb8\x8e\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xc6\xfb\x1e\xe3t\x17\u0400\xa0\xd0H\x92;\u06ba\xb0\x95\xd0w\u0189\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc7\x05'\xd4D\u0110\xe9\xfc?\\\xc4Nf\xebO0k8\x0f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7\r\x85mb\x1e\xc1E0<\nd\x00\xcd\x17\xbb\xd6\xf5\xea\xf7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc7\x0f\xa4Uv\xbf\x9c\x86_\x988\x93\x00,AI&\xf6\x10)\x89\x15\xb4\xaa\x8e\x97\x02h\x00\x00\u07d4\xc7\x11E\xe5)\u01e7\x14\xe6y\x03\xeeb\x06\xe4\xc3\x04+g'\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xc7\x1b*=q5\u04a8_\xb5\xa5q\u073ei^\x13\xfcC\u034965\u026d\xc5\u07a0\x00\x00\u07d4\xc7\x1f\x1du\x87?3\u0732\xddK9\x87\xa1-\a\x91\xa5\xce'\x897\b\xba\xed=h\x90\x00\x00\u07d4\xc7\x1f\x92\xa3\xa5J{\x8c/^\xa4C\x05\xfc\u02c4\xee\xe21H\x89\x02\xb5\x9c\xa11\xd2\x06\x00\x00\u07d4\xc7!\xb2\xa7\xaaD\xc2\x12\x98\xe8P9\xd0\x0e.F\x0eg\v\x9c\x89\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\xc7,\xb3\x01%\x8e\x91\xbc\b\x99\x8a\x80]\u0452\xf2\\/\x9a5\x89 \t\xc5\u023fo\xdc\x00\x00\xe0\x94\xc76\x8b\x97\t\xa5\xc1\xb5\x1c\n\xdf\x18ze\xdf\x14\xe1+}\xba\x8a\x02\x02o\xc7\u007f\x03\u5b80\x00\u07d4\xc79%\x9e\u007f\x85\xf2e\x9b\xef_`\x9e\xd8k=Yl \x1e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc7>!\x12(\"\x15\xdc\ab\xf3+~\x80}\xcd\x1az\xae>\x8a\x01v\f\xbcb;\xb3P\x00\x00\xe0\x94\xc7If\x80B\xe7\x11#\xa6H\x97^\b\xedc\x82\xf8>\x05\xe2\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xc7J9\x95\xf8\a\xde\x1d\xb0\x1a.\xb9\xc6.\x97\xd0T\x8fio\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc7Pl\x10\x19\x12\x1f\xf0\x8a,\x8c\x15\x91\xa6^\xb4\xbd\xfbJ?\x89 \x86\xac5\x10R`\x00\x00\u07d4\xc7\\7\xce-\xa0k\xbc@\b\x11Y\u01ba\x0f\x97n9\x93\xb1\x89:y#\x15\x1e\xcfX\x00\x00\u07d4\xc7]\"Y0j\xec}\xf0\"v\x8ci\x89\x9ae!\x85\xdb\u0109\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7`\x97\x1b\xbc\x18\x1cj|\xf7tA\xf2BG\u045c\xe9\xb4\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xc7a0\xc7<\xb9!\x028\x02\\\x9d\xf9]\v\xe5J\xc6\u007f\xbe\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xc7e\xe0\x04v\x81\tG\x81j\xf1B\xd4m.\u7f28\xccO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc7g^VG\xb9\xd8\xda\xf4\xd3\xdf\xf1\xe5R\xf6\xb0qT\xac8\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xc7{\x01\xa6\xe9\x11\xfa\x98\x8d\x01\xa3\xab3dk\xee\xf9\xc18\xf3\x89'\x1bo\xa5\xdb\xe6\xcc\x00\x00\u07d4\u01c3z\u0420\xbf\x14\x18i7\xac\xe0lUF\xa3j\xa5OF\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u01d8\x06\x03+\xc7\xd8(\xf1\x9a\u01a6@\u018e=\x82\x0f\xa4B\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u01d9\xe3N\x88\xff\x88\xbe}\xe2\x8e\x15\xe4\xf2\xa6=\v3\xc4\u02c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\u01ddPb\u01d6\xddwa\xf1\xf1>U\x8ds\xa5\x9f\x82\xf3\x8b\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\u01e0\x18\xf0\x96\x8aQ\xd1\xf6`<\\I\xdcT[\xcb\x0f\xf2\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u01ef\xf9\x19)yt\x89UZ/\xf1\xd1M\\iZ\x10\x83U\x8965\u026d\xc5\u07a0\x00\x00\u0794\u01f1\xc8>c ?\x95G&>\xf6(.}\xa3;n\xd6Y\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\u01f3\x9b\x06\x04Q\x00\f\xa1\x04\x9b\xa1T\xbc\xfa\x00\xff\x8a\xf2b\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\u01ff\x17\xc4\xc1\x1f\x98\x94\x1fP~w\bO\xff\xbd-\xbd=\xb5\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u01ff.\xd1\xed1)@\xeej\xde\xd1Qn&\x8eJ`HV\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc7\xd4O\xe3,\u007f\x8c\xd5\xf1\xa9t'\xb6\xcd:\xfc\x9eE\x02>\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xc7\xd5\xc7\x05@\x81\xe9\x18\xech{Z\xb3n\x97=\x18\x13)5\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xc7\xde^\x8e\xaf\xb5\xf6+\x1a\n\xf2\x19\\\xf7\x93\u01c9L\x92h\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc7\xe30\xcd\f\x89\n\u025f\xe7q\xfc\xc7\xe7\xb0\t\xb7A=\x8a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7\xea\xc3\x1a\xbc\xe6\xd5\xf1\u07a4\"\x02\xb6\xa6t\x15=\xb4z)\x89 \t\xc5\u023fo\xdc\x00\x00\xe0\x94\xc7\xecb\xb8\x04\xb1\xf6\x9b\x1e0p\xb5\xd3b\xc6/\xb3\t\xb0p\x8a\x02\xc4k\xf5A`f\x11\x00\x00\u07d4\xc7\xf7+\xb7X\x01k7G\x14\u0509\x9b\xce\"\xb4\xae\xc7\n1\x89:&\xc9G\x8f^-\x00\x00\u0794\xc8\v6\u047e\xaf\xba_\xccdM`\xacnF\xed)'\xe7\u0708\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xc8\x11\xc2\xe9\xaa\x1a\xc3F.\xba^\x88\xfc\xb5\x12\x0e\x9fn,\xa2\x89K\xe6\u0607\xbd\x87n\x00\x00\u07d4\xc8\x17\xdf\x1b\x91\xfa\xf3\x0f\xe3%\x15qr|\x97\x11\xb4]\x8f\x06\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xc8\x1f\xb7\xd2\x0f\u0480\x01\x92\xf0\xaa\xc1\x98\xd6\u05a3}?\xcb}\x89\x0e\x11I3\x1c-\xde\x00\x00\u07d4\xc8 \xc7\x11\xf0w\x05'8\a\xaa\xaam\xe4M\x0eKH\xbe.\x89\bg\x0e\x9e\xc6Y\x8c\x00\x00\u07d4\xc8#\x1b\xa5\xa4\x11\xa1>\"+)\xbf\xc1\b?v1X\xf2&\x8967\tlK\xcci\x00\x00\u07d4\xc86\xe2Jo\xcf)\x94;6\b\xe6b)\n!_e)\xea\x89\x0f\xd4Pd\xea\xee\x10\x00\x00\xe0\x94\xc8;\xa6\u0755I\xbe\x1d2\x87\xa5\xa6T\xd1\x06\xc3Lk]\xa2\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xc8>\x9djX%;\uefb7\x93\xe6\xf2\x8b\x05JXI\x1bt\x89\x0fF\u00b6\xf5\xa9\x14\x00\x00\u07d4\xc8A\x88O\xa4x_\xb7s\xb2\x8e\x97\x15\xfa\xe9\x9aQ40]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc8M\x9b\xea\n{\x9f\x14\x02 \xfd\x8b\x90\x97\u03ff\xd5\xed\xf5d\x89\x06\xab\x9e\u0091\xad}\x80\x00\u07d4\xc8RB\x8d+Xd\x97\xac\xd3\fV\xaa\x13\xfbU\x82\xf8D\x02\x893B\xd6\r\xff\x19`\x00\x00\u07d4\xc8S![\x9b\x9f-,\xd0t\x1eX^\x98{_\xb8\f!.\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xc8S%\uaca5\x9b>\xd8c\xc8j_)\x06\xa0B)\xff\xa9\x89\x19=\u007f}%=\xe0\x00\x00\u07d4\xc8^\xf2}\x82\x04\x03\x80_\xc9\xed%\x9f\xffd\xac\xb8\xd64j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc8akN\xc0\x91(\xcd\xff9\xd6\u4e6c\x86\xee\xc4q\xd5\xf2\x89\x01\r:\xa56\xe2\x94\x00\x00\xe0\x94\xc8a\x90\x90K\x8d\a\x9e\xc0\x10\xe4b\xcb\xff\xc9\b4\xff\xaa\\\x8a\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4\xc8q\r~\x8bZ;\u059aB\xfe\x0f\xa8\xb8|5\u007f\xdd\xcd\u0209\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc8sR\u06e5\x82\xee f\xb9\xc0\x02\xa9b\xe0\x03\x13Ox\xb1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xc8|w\xe3\xc2J\xde\xcd\xcd\x108\xa3\x8bV\xe1\x8d\xea\u04f7\x02\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\xc8}:\xe3\u0607\x04\u066b\x00\t\xdc\xc1\xa0\x06q1\xf8\xba<\x89j\xc5\xc6-\x94\x86\a\x00\x00\xe0\x94\u0201N4R>8\xe1\xf9'\xa7\xdc\xe8FjDz\t6\x03\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0202U\xed\xdc\xf5!\xc6\xf8\x1d\x97\xf5\xa4!\x81\xc9\a=N\xf1\x89\x0f\u00d0D\xd0\n*\x80\x00\u07d4\u0205\xa1\x8a\xab\xf4T\x1b{{~\xcd0\xf6\xfa\u619d\x95i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u020c\xa1\xe6\xe5\xf4\xd5X\xd17\x80\xf4\x88\xf1\rJ\xd3\x13\r4\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u020e\xecT\xd3\x05\xc9(\xcc(H\xc2\xfe\xe251\xac\xb9mI\x89lj\u04c2\xd4\xfba\x00\x00\xe0\x94\u021c\xf5\x04\xb9\xf3\xf85\x18\x1f\xd8BO\\\xcb\xc8\xe1\xbd\xdf}\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0222\xc4\xe5\x9e\x1c\u007f\xc5H\x05X\x048\xae\xd3\xe4J\xfd\xf0\x0e\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4\u022aI\u301f\b\x99\xf2\x8a\xb5~gCp\x9dXA\x903\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\u022b\x1a<\xf4l\xb8\xb0d\xdf.\"-9`s\x94 2w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0231\x85\x05%\xd9F\xf2\xae\x84\xf3\x17\xb1Q\x88\xc56\xa5\u0706\x89\x91\x8d\xdc:B\xa3\xd4\x00\x00\u07d4\xc8\xd4\xe1Y\x9d\x03\xb7\x98\t\xe0\x13\n\x8d\u00c4\b\xf0^\x8c\u04c9\x9f\xad\x06$\x12y\x16\x00\x00\u07d4\xc8\xdd'\xf1k\xf2$P\xf5w\x1b\x9f\xe4\xedO\xfc\xb3\t6\xf4\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xc8\xdezVL\u007f@\x12\xa6\xf6\xd1\x0f\u040fG\x89\x0f\xbf\a\u0509\x10CV\x1a\x88)0\x00\x00\u07d4\xc8\xe2\xad\xebT^I\x9d\x98,\f\x11sc\u03b4\x89\u0171\x1f\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\xc8\xe5X\xa3\xc5i~o\xb2:%\x94\u0200\xb7\xa1\xb6\x8f\x98`\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc8\xf2\xb3 \xe6\xdf\xd7\t\x06\u0157\xba\xd2\xf9P\x13\x12\u01c2Y\x89Q\x93K\x8b:W\xd0\x00\x00\u07d4\xc9\x03\x00\xcb\x1d@w\xe6\xa6\xd7\xe1i\xa4`F\x8c\xf4\xa4\x92\u05c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9\f7e\x15k\u028eH\x97\xab\x80$\x19\x15<\xbeR%\xa9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc9\x10\xa9pUl\x97\x16\xeaS\xaff\xdd\xef\x93\x141$\x91=\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\xe0\x94\xc9\x12{\u007ff)\xee\x13\xfc?`\xbc/Dg\xa2\aE\xa7b\x8a\x03|\x9a\xa4\xe7\xceB\x1d\x80\x00\u07d4\xc9\x1b\xb5b\xe4+\xd4a0\xe2\u04eeFR\xb6\xa4\ub1bc\x0f\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\xe0\x94\xc90\x88y\x05m\xfe\x13\x8e\xf8 \x8fy\xa9\x15\u01bc~p\xa8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xc94\xbe\xca\xf7\x1f\"_\x8bJK\xf7\xb1\x97\xf4\xac\x9604\\\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc9?\xbd\xe8\xd4m+\xcc\x0f\xa9\xb3;\u063a\u007f\x80B\x12Ue\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xc9@\x89U:\xe4\xc2,\xa0\x9f\xbc\x98\xf5pu\xcf.\u0155\x04\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc9A\x10\xe7\x1a\xfeW\x8a\xa2\x18\xe4\xfc(d\x03\xb03\n\u038d\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc9F\u056c\xc14n\xba\nry\xa0\xac\x1dF\\\x99m\x82~\x8a\x03x=T_\xdf\n\xa4\x00\x00\u07d4\xc9J(\xfb20\xa9\xdd\xfa\x96Nw\x0f,\xe3\xc2S\xa7\xbeO\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc9JXR\x03\xda{\xba\xfd\x93\xe1X\x84\xe6`\u0531\xea\xd8T\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xc9O|5\xc0'\xd4}\xf8\xefO\x9d\xf8Z\x92H\xa1}\xd2;\x89\x01\x9f\x8euY\x92L\x00\x00\u07d4\xc9Q\x90\f4\x1a\xbb\xb3\xba\xfb\xf7\xee )7pq\xdb\xc3j\x89\x11\xc2]\x00M\x01\xf8\x00\x00\u07d4\xc9S\xf94\xc0\xeb-\x0f\x14K\u06b0\x04\x83\xfd\x81\x94\x86\\\xe7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9f&r\x8a\xaaLO\xb3\xd3\x1c&\xdf:\xf3\x10\b\x17\x10\u0449\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\xc9gQel\n\x8e\xf45{sD2!4\xb9\x83PJ\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\u0240Hh\u007f+\xfc\u027d\x90\xed\x18slW\xed\xd3R\xb6]\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0241\xd3\x12\u0487\xd5X\x87\x1e\u0757:\xbbv\xb9y\xe5\xc3^\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0242Xmc\xb0\xd7L \x1b\x1a\xf8A\x83r\xe3\fv\x16\xbe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u0249CO\x82Z\xaf\x9cU/h^\xba|\x11\xdbJ_\xc7:\x89\x1b(\u014d\x96\x96\xb4\x00\x00\u07d4\u0249\xee\xc3\a\u80db\x9dr7\xcf\xda\b\x82)b\xab\u41c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u0252\xbeY\xc6r\x1c\xafN\x02\x8f\x9e\x8f\x05\xc2\\UQ[\u0509\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0255{\xa9L\x1b)\xe5'~\xc3f\"pI\x04\xc6=\xc0#\x89h>\xfcg\x82d,\x00\x00\xe0\x94\u025a\x9c\xd6\xc9\xc1\xbe54\xee\u0352\xec\xc2/\\8\xe9Q[\x8a\x01\x05Y;:\x16\x9dw\x00\x00\xe0\x94\u026c\x01\xc3\xfb\t)\x03?\f\xcc~\x1a\xcf\uaae7\x94]G\x8a\x02\xa3j\x9e\x9c\xa4\xd2\x03\x80\x00\u07d4\u0276\x98\xe8\x98\xd2\rMO@\x8eNM\x06\x19\"\xaa\x85c\a\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u0276\xb6\x86\x11\x16\x91\xeej\xa1\x97\xc7#\x1a\x88\xdc`\xbd)]\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc9\u01ec\v\u0753B\xb5\xea\xd46\t#\xf6\x8cr\xa6\xbac:\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc9\xc8\r\xc1.{\xab\x86\xe9I\xd0\x1eL>\xd3_+\x9b\xba_\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9\xd7dF\u056a\xdf\xf8\vh\xb9\x1b\b\u035b\xc8\xf5U\x1a\xc1\x89&\xb4\xbd\x91\x10\xdc\xe8\x00\x00\xe0\x94\xc9\u073b\x05oM\xb7\xd9\xda9\x93b\x02\u017d\x820\xb3\xb4w\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc9\xe0&\b\x06h(\x84\x8a\xeb(\xc76r\xa1)%\x18\x1fM\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xca\x042\xcb\x15{Qy\xf0.\xbb\xa5\xc9\u0475O\xecM\x88\u028965\u026d\xc5\u07a0\x00\x00\u07d4\xca\x12,\xf0\U00094216\xb7HC\xf4\x9a\xfe\u043a\x16\x18\xee\u05c9\x1e[\x8f\xa8\xfe*\xc0\x00\x00\xe0\x94\xca\"\u0363`m\xa5\xca\xd0\x13\xb8\aG\x06\xd7\xe9\xe7!\xa5\f\x8a\x01q\x81\xc6\xfa9\x81\x94\x00\x00\u07d4\xca#\xf6-\xff\rd`\x03lb\xe8@\xae\xc5W~\v\xef\u0489\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\xca%\xff4\x93L\x19B\xe2*N{\xd5o\x14\x02\x1a\x1a\xf0\x88\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xca7?\xe3\xc9\x06\xb8\xc6U\x9e\xe4\x9c\xcd\a\xf3|\xd4\xfbRf\x89a\t=|,m8\x00\x00\u07d4\xcaA\u032c0\x17 R\xd5\"\xcd//\x95}$\x81S@\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xcaB\x88\x01N\xdd\xc5c/_\xac\xb5\xe3\x85\x17\xa8\xf8\xbc]\x98\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xcaB\x88c\xa5\xca06\x98\x92\xd6\x12\x18>\xf9\xfb\x1a\x04\xbc\xea\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\xcaI\xa5\xf5\x8a\xdb\xef\xae#\xeeY\xee\xa2A\xcf\x04\x82b.\xaa\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xcaL\xa9\xe4w\x9dS\x0e\u02ec\xd4~j\x80X\xcf\xdee\u064f\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xcae~\xc0o\xe5\xbc\t\xcf#\xe5*\xf7\xf8\f\xc3h\x9en\u07890\xca\x02O\x98{\x90\x00\x00\u07d4\xcaf\xb2(\x0f\xa2\x82\u0176v1\xceU+b\xeeU\xad\x84t\x89j\xc4\"\xf54\x92\x88\x00\x00\xe0\x94\xcal\x81\x8b\xef\xd2Q6\x1e\x02t@h\xbe\x99\u062a`\xb8J\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcap\xf4\u077f\x06\x9d!C\xbdk\xbc\u007fikRx\x9b2\u7262\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xcatuvDjL\x8f0\xb0\x83@\xfe\xe1\x98\xdec\xec\x92\u03ca\x01|\x8e\x12\x06r*0\x00\x00\u07d4\xca{\xa3\xffSl~_\x0e\x158\x00\xbd8=\xb81)\x98\xe0\x89\t1\xac=k\xb2@\x00\x00\xe0\x94\u0282v\xc4w\xb4\xa0{\x80\x10{\x845\x94\x18\x96\a\xb5;\xec\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u0284\t\b>\x01\xb3\x97\xcf\x12\x92\x8a\x05\xb6\x84U\xceb\x01\u07c9V\xbcu\xe2\xd61\x00\x00\x00\u07d4\u0298\u01d8\x8e\xfa\b\xe9%\uf719ER\x03&\xe9\xf4;\x99\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u029a\x04*j\x80o\xfc\x92\x17\x95\x00\xd2D)\xe8\xabR\x81\x17\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\u029d\xec\x02\x84\x1a\xdf\\\xc9 WjQ\x87\xed\u04bdCJ\x18\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u029f\xaa\x17T/\xaf\xbb8\x8e\xab!\xbcL\x94\u89f3G\x88\x89lk\x8f\xce\r\x18y\x80\x00\xe0\x94\u02aah\xeel\xdf\r4EJv\x9b\r\xa1H\xa1\xfa\xaa\x18e\x8a\x01\x87.\x1d\xe7\xfeR\xc0\x00\x00\u07d4\u02ad\x9d\xc2\rX\x9c\xe4(\xd8\xfd\xa3\xa9\xd5:`{y\x88\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u02b0\xd3,\xf3v\u007f\xa6\xb3S|\x842\x8b\xaa\x9fPE\x816\x8a\x01\xe5\xb8\xfa\x8f\xe2\xac\x00\x00\x00\u07d4\u02b9\xa3\x01\xe6\xbdF\xe9@5P(\xec\xcd@\xceMZ\x1a\u00c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u02b9\xa9z\xda\x06\\\x87\x81nh`\xa8\xf1Bo\xe6\xb3\xd7u\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u02ba\xb6'N\xd1P\x89s~({\xe8x\xb7W\x93Hd\xe2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\u02bd\xaf5OG \xa4f\xa7d\xa5(\xd6\x0e:H*9<\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xca\xcbg^\t\x96#T\x04\ufbfb.\u02c1R'\x1bU\xe0\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\xca\xd1O\x9e\xbb\xa7f\x80\xeb\x83k\a\x9c\u007f{\xaa\xf4\x81\xedm\x89\f\xef={\xd7\xd04\x00\x00\xe0\x94\xca\xe3\xa2S\xbc\xb2\xcfN\x13\xba\x80\u0098\xab\x04\x02\xda|*\xa0\x8a\x01$\xbc\r\u0752\xe5`\x00\x00\u07d4\xca\xef\x02{\x1a\xb5\x04\xc7?A\xf2\xa1\ty\xb4t\xf9~0\x9f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xca\xf4H\x1d\x9d\xb7\x8d\xc4\xf2_{J\u023d;\x1c\xa0\x10k1\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xca\xfd\xe8U\x86L%\x98\xda<\xaf\xc0Z\u064d\U00089380H\x8a\x03\x00\xa8\xed\x96\xffJ\x94\x00\x00\xe0\x94\xcb\r\xd7\xcfN]\x86a\xf6\x02\x89C\xa4\xb9\xb7\\\x91D6\xa7\x8a\x19i6\x89t\xc0[\x00\x00\x00\u07d4\xcb\x1b\xb6\xf1\xda^\xb1\rH\x99\xf7\xe6\x1d\x06\xc1\xb0\x0f\u07f5-\x898E$\xccp\xb7x\x00\x00\u07d4\xcb=vl\x98?\x19+\xce\xca\xc7\x0fN\xe0=\xd9\xffqMQ\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xcbB\xb4N\xb5\xfd`\xb5\x83~O\x9e\xb4rgR=\x1a\"\x9c\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xcbG\xbd0\u03e8\xecTh\xaa\xa6\xa9FB\xce\xd9\xc8\x19\xc8\u0509\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcbH\xfe\x82e\u066fU\xebp\x06\xbc3VE\xb0\xa3\xa1\x83\xbe\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xcbJ\x91M+\xb0)\xf3._\xef\\#LO\xec--\xd5w\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xcbJ\xbf\u0082\xae\xd7n]W\xaf\xfd\xa5B\xc1\xf3\x82\xfc\xac\xf4\x8a\x01\xb9\x0f\x11\xc3\x18?\xaa\x00\x00\u07d4\xcbJ\xd0\xc7#\xdaF\xabV\xd5&\xda\f\x1d%\xc7=\xaf\xf1\n\x89\x1b\xa5\xab\xf9\xe7y8\x00\x00\u07d4\xcbK\xb1\xc6#\xba(\xdcB\xbd\xaa\xa6\xe7N\x1d*\xa1%l*\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xcbPXt\x12\x82#\x04\xeb\u02e0}\xab:\x0f\t\xff\xfe\u4189JD\x91\xbdm\xcd(\x00\x00\u07d4\xcbX\x99\v\u0350\u03ffm\x8f\t\x86\xf6\xfa`\x02v\xb9N-\x8964\xbf9\xab\x98x\x80\x00\u07d4\xcbh\xaeZ\xbe\x02\xdc\xf8\xcb\u016aq\x9c%\x81FQ\xaf\x8b\x85\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xcbty\x10\x9bC\xb2fW\xf4F_M\x18\xc6\xf9t\xbe_B\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xcb}+\x80\x89\xe91,\u026e\xaa's\xf3S\b\xecl*{\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u02c6\xed\xbc\x8b\xbb\x1f\x911\x02+\xe6IV^\xbd\xb0\x9e2\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u02d3\x19\x9b\x9c\x90\xbcI\x15\xbd\x85\x9e=B\x86m\xc8\xc1\x87I\x89\f\x90\xdf\a\xde\xf7\x8c\x00\x00\u07d4\u02d4\xe7o\xeb\xe2\b\x11g3\xe7n\x80]H\xd1\x12\xec\x9f\u028965\u026d\xc5\u07a0\x00\x00\u07d4\u02dbQ\x03\xe4\u0389\xafOd\x91aP\xbf\xf9\xee\u02df\xaa\\\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u02e2\\zP<\xc8\xe0\xd0Iq\xca\x05\xc7b\xf9\xb7b\xb4\x8b\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u02e2\x88\xcd<\x1e\xb4\u055d\xdb\x06\xa6B\x1c\x14\xc3E\xa4{$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u02f3\x18\x9eK\xd7\xf4_\x17\x8b\x1c0\xc7n&1MJK\n\x89\x0f\xfe\vg|e\xa9\x80\x00\xe0\x94\u02f7\xbe\x17\x95?,\u0313\u1f19\x80[\xf4U\x11CNL\x8a\n\xae[\x9d\xf5m/ \x00\x00\xe0\x94\xcb\xc0KM\x8b\x82\xca\xf6p\x99o\x16\f6)@\xd6o\xcf\x1a\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcb\u07974\xb8\xe6\xaaS\x8c)\x1dm\u007f\xac\xed\xb0\xf38\xf8W\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xcb\xe1\xb9H\x86M\x84t\xe7e\x14XX\xfc\xa4U\x0fxK\x92\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xe5/\xc53\xd7\xdd`\x8c\x92\xa2`\xb3|?E\u07b4\xeb3\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcb\xe8\x10\xfe\x0f\xec\xc9dGJ\x1d\xb9w(\xbc\x87\xe9s\xfc\xbd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xf1j\x0f\xe2tRX\xcdR\xdb+\xf2\x19T\xc9u\xfcj\x15\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\xcb\xf3\u007f\xf8T\xa2\xf1\xceS\x93D\x94wx\x92\xd3\xeceW\x82\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xfaj\xf6\u0083\xb0F\xe2w,`c\xb0\xb2\x15S\xc4\x01\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcb\xfav\xdb\x04\xce8\xfb ]7\xb8\xd3w\xcf\x13\x80\xda\x03\x17\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xcc\x03I\x85\xd3\xf2\x8c-9\xb1\xa3K\xce\xd4\u04f2\xb6\xca#N\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xcc\x04\x8d\u01f9]\xca%\xdf&\xee\xfac\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcc+_D\x8f5(\xd3\xfeA\xcc}\x1f\xa9\xc0\xdcv\xf1\xb7v\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xcc-\x04\xf0\xa4\x01q\x89\xb3@\xcaw\x19\x86A\xdc\xf6Ek\x91\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xccA\x9f\u0651+\x85\x13VY\xe7z\x93\xbc=\xf1\x82\xd4Q\x15\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xccE\xfb:U[\xad\x80{8\x8a\x03W\xc8U _|u\xe8\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xccHAM*\xc4\xd4*Yb\xf2\x9e\xeeD\x97\t/C\x13R\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\xccJ/,\xf8l\xf3\xe43u\xf3`\xa4sF\x91\x19_\x14\x90\x89I\x15\x05;\xd1)\t\x80\x00\u07d4\xccO\x0f\xf2\xae\xb6}T\xce;\xc8\xc6Q\v\x9a\xe8>\x9d2\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xccO\xaa\xc0\v\xe6b\x8f\x92\xefk\x8c\xb1\xb1\xe7j\xac\x81\xfa\x18\x89\v\"\xa2\xea\xb0\xf0\xfd\x00\x00\xe0\x94\xccO\xebr\u07d8\xff5\xa18\xe0\x17a\xd1 ?\x9b~\xdf\n\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xcc`oQ\x13\x97\xa3\x8f\u01c7+\u04f0\xbd\x03\xc7\x1b\xbdv\x8b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xcc`\xf86\xac\xde\xf3T\x8a\x1f\xef\u0321>\u01a97\xdbD\xa0\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4\xccl\x03\xbd`>\t\xdeT\xe9\xc4\u056cmA\xcb\xceqW$\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xccl-\xf0\x0e\x86\xec\xa4\x0f!\xff\xda\x1ag\xa1i\x0fG|e\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94\xccm{\x12\x06\x1b\xc9m\x10M`me\xff\xa3+\x006\xeb\a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xccs\xdd5kIy\xb5y\xb4\x01\xd4\xccz1\xa2h\xdd\xceZ\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xccu\x8d\a\x1d%\xa62\n\xf6\x8c]\xc9\xc4\xf6\x95[\xa9E \x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcc{\x04\x81\xcc2\xe6\xfa\xef#\x86\xa0p\"\xbc\xb6\xd2\u00f4\xfc\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94\u0314;\xe1\",\xd1@\n#\x99\xdd\x1bE\x94E\xcfmT\xa9\x8a\x02\xa7@\xaee6\xfc\x88\x00\x00\u07d4\u0315\x19\xd1\xf3\x98_k%^\xad\xed\x12\xd5bJ\x97'!\xe1\x8965\u026d\xc5\u07a0\x00\x00\u0794\u031a\xc7\x15\xcdo&\x10\xc5+XgdV\x88B\x97\x01\x8b)\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\u0320{\xb7\x94W\x1dJ\xcf\x04\x1d\xad\x87\xf0\xd1\xef1\x85\xb3\x19\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u032b\xc6\x04\x8aSFD$\xfc\xf7n\xeb\x9en\x18\x01\xfa#\u0509\x02\xab{&\x0f\xf3\xfd\x00\x00\u07d4\u032e\r=\x85*}\xa3\x86\x0f\x066\x15L\nl\xa3\x16(\u0509\x05\xc6\xd1+k\xc1\xa0\x00\x00\u07d4\xcc\xca$\xd8\xc5mn,\a\xdb\bn\xc0~X[\xe2g\xac\x8d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xcc\xd5!\x13-\x98l\xb9hi\x84&\"\xa7\u0762l>\xd0W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcc\xf49u\xb7k\xfes_\xec<\xb7\xd4\xdd$\xf8\x05\xba\tb\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xcc\xf6*f?\x13S\xba.\xf8\xe6R\x1d\xc1\xec\xb6s\xec\x8e\xf7\x89\b=lz\xabc`\x00\x00\u07d4\xcc\xf7\x11\r\x1b\u0667K\xfd\x1d}}-\x9dU`~{\x83}\x890\xca\x02O\x98{\x90\x00\x00\u07d4\xcc\xfdrW`\xa6\x88#\xff\x1e\x06/L\xc9~\x13`\xe8\u0657\x89\x15\xacV\xed\xc4\xd1,\x00\x00\u07d4\xcd\x02\x0f\x8e\xdf\xcfRG\x98\xa9\xb7:d\x034\xbb\xf7/\x80\xa5\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xcd\x06\xf8\xc1\xb5\u037d(\xe2\xd9kcF\xc3\xe8Z\x04\x83\xba$\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcd\a.n\x183\x13y\x95\x19m{\xb1r_\xef\x87a\xf6U\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcd\n\x16\x1b\xc3g\xae\t'\xa9*\xac\x9c\xf6\xe5\bg\x14\xef\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\xcd\n\xf3GN\"\xf0i\xec4\a\x87\r\xd7pD=[\x12\xb0\x89\x8e^\xb4\xeew\xb2\xef\x00\x00\u07d4\xcd\v\x02W\u70e3\xd2\xc2\u3e9dny\xb7^\xf9\x80$\u0509\x9f\xad\x06$\x12y\x16\x00\x00\u07d4\xcd\x10,\xd6\xdb=\xf1J\u05af\x0f\x87\xc7$y\x86\x1b\xfc=$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcd\x1ef\xedS\x9d\xd9/\xc4\v\xba\xa1\xfa\x16\u078c\x02\xc1ME\x89\fw\xe4%hc\xd8\x00\x00\u07d4\xcd\x1e\xd2c\xfb\xf6\xf6\xf7\xb4\x8a\xef\x8fs=2\x9dC\x82\xc7\u01c9\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4\xcd*6\xd7S\xe9\xe0\xed\x01*XMqh\aX{A\xd5j\x89\x0e+\xa7[\v\x1f\x1c\x00\x00\u07d4\xcd2\xa4\xa8\xa2\u007f\x1c\xc69T\xaacOxW\x05s4\u01e3\x89:\xd1fWlr\xd4\x00\x00\u07d4\xcd5\xff\x01\x0e\xc5\x01\xa7!\xa1\xb2\xf0z\x9c\xa5\x87}\xfc\xf9Z\x89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4\xcdC\x06\xd7\xf6\x94z\xc1tMN\x13\xb8\xef2\xcbe~\x1c\x00\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4\xcdC%\x8bs\x92\xa90\x83\x9aQ\xb2\xef\x8a\xd24\x12\xf7Z\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcdI\xbf\x18^p\xd0E\a\x99\x9f\x92\xa4\xdeDU1('\u040965\u026d\xc5\u07a0\x00\x00\u07d4\xcdU\x10\xa2B\u07f0\x18=\xe9%\xfb\xa8f\xe3\x12\xfa\xbc\x16W\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xcdVj\u05f8\x83\xf0\x1f\u04d9\x8a\x9aX\xa9\xde\xe4rM\u0725\x89\x030\xae\x185\xbe0\x00\x00\xe0\x94\xcdY\xf3\xdd\xe7~\t\x94\v\xef\xb6\xeeX\x03\x19e\xca\xe7\xa36\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcdr]p\xbe\x97\xe6w\xe3\xc8\xe8\\\v&\xef1\xe9\x95PE\x89Hz\x9a0E9D\x00\x00\xe0\x94\xcd~G\x90\x94d\xd8q\xb9\xa6\xdcv\xa8\xe9\x19]\xb3H^z\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xcd~\xce\bkKa\x9b;6\x93R\xee8\xb7\x1d\xdb\x06C\x9a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xcd\u007f\t\xd7\xedf\xd0\u00cb\u016dN2\xb7\xf2\xb0\x8d\xc1\xb3\r\x89>;\xb3M\xa2\xa4p\x00\x00\u07d4\u0355)I+\\)\xe4u\xac\xb9A@+=;\xa5\x06\x86\xb0\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0355\xfaB=o\xc1 'J\xac\xde\x19\xf4\xee\xb7f\xf1\x04 \x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u035bL\xefs9\f\x83\xa8\xfdq\u05f5@\xa7\xf9\u03cb\x8c\x92\x89\x04\xe1\x00;(\xd9(\x00\x00\u07d4\u0361t\x11\t\xc0&[?\xb2\xbf\x8d^\xc9\u00b8\xa34kc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0361\xb8\x86\u39d5\u027aw\x91N\n/\xe5go\x0f\\\u03c9\x05\xbf`\xeaB\xc2\x04\x00\x00\u07d4\u0364S\x0fK\x9b\xc5\t\x05\xb7\x9d\x17\u008f\xc4o\x954\x9b\u07c93\x10\xe0I\x11\xf1\xf8\x00\x00\u07d4\u036bF\xa5\x90 \x80do\xbf\x95B\x04 J\xe8\x84\x04\x82+\x89\x1d\x8a\x96\xe5\xc6\x06\xeb\x00\x00\u07d4\u0375\x97)\x900\x18?n-#\x853\xf4d*\xa5\x87T\xb6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xcd\xd5\u0601\xa76,\x90p\a;\u07fcu\xe7$S\xacQ\x0e\x89-\xa5\x18\xea\xe4\x8e\xe8\x00\x00\u07d4\xcd\xd6\rs\xef\xaa\xd8s\u027b\xfb\x17\x8c\xa1\xb7\x10Z\x81\xa6\x81\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xcd\xd9\xef\xacMm`\xbdq\xd9U\x85\xdc\xe5\u0557\x05\xc15d\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xcd\xe3m\x81\xd1(\u015d\xa1Ee!\x93\xee\u00bf\xd9e\x86\xef\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcd\xea8o\x9d\x0f\xd8\x04\xd0(\x18\xf27\xb7\xd9\xfavF\xd3^\x89\xa3I\xd3m\x80\xecW\x80\x00\u07d4\xcd\xec\xf5gT3\u0370\xc2\xe5Zh\xdb]\x8b\xbexA\x9d\u0489\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xcd\xfd\x82\x173\x97%\xd7\xeb\xac\x11\xa66U\xf2e\xef\xf1\xcc=\x8a\x01\x0f\fid\x10\xe3\xa9\x00\x00\u07d4\xce\a\x9fQ\x88wt\xd8\x02\x1c\xb3\xb5u\xf5\x8f\x18\xe9\xac\xf9\x84\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xce\x18\x84\u077b\xb8\xe1\x0eM\xbanD\xfe\xee\u00a7\xe5\xf9/\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xce\x1b\f\xb4j\xae\xcf\u05db\x88\f\xad\x0f-\u068a\x8d\xed\u0431\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xce&\xf9\xa50_\x83\x81\tCT\xdb\xfc\x92fN\x84\xf9\x02\xb5\x89\fz\xaa\xb0Y\x1e\xec\x00\x00\u07d4\xce-\xea\xb5\x1c\n\x9a\xe0\x9c\xd2\x12\xc4\xfaL\xc5+S\xcc\r\xec\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xce.\r\xa8\x93F\x99\xbb\x1aU>U\xa0\xb8\\\x16\x945\xbe\xa3\x8a\x01\x0f\fid\x10\xe3\xa9\x00\x00\u07d4\xce:a\xf0F\x1b\x00\x93^\x85\xfa\x1e\xad\x82\xc4^Zd\u0508\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xceK\x06]\xbc\xb20G 2b\xfbH\xc1\x18\x83d\x97tp\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xceS\xc8\xcd\xd7B\x96\xac\xa9\x87\xb2\xbc\x19\u00b8u\xa4\x87I\u0409\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xce^\x04\xf0\x18Ci\xbc\xfa\x06\xac\xa6o\xfa\x91\xbfY\xfa\x0f\xb9\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xce^\xb6:{\xf4\xfb\xc2\xf6\u4ea0\u018a\xb1\xcbL\xf9\x8f\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xceb\x12Z\xde\xc37\n\xc5!\x10\x95:Nv\v\xe9E\x1e;\x89\b=lz\xabc`\x00\x00\xe0\x94\xceq\bmL`%T\xb8-\xcb\xfc\xe8\x8d cMS\xccM\x8a\t(\x96R\x9b\xad\u0708\x00\x00\u07d4\u038akmP3\xb1I\x8b\x1f\xfe\xb4\x1aAU\x04\x05\xfa\x03\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0397\x86\xd3q/\xa2\x00\xe9\xf6\x857\xee\xaa\x1a\x06\xa6\xf4ZK\x89a\t=|,m8\x00\x00\u07d4\u039d!\u0192\xcd<\x01\xf2\x01\x1fP_\x87\x006\xfa\x8fl\u0489\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\u03a2\x89f#\xf4\x91\x02\x87\xa2\xbd\u017e\x83\xae\xa3\xf2\xe6\xde\b\x8a\x01\xfbZ7Q\xe4\x90\xdc\x00\x00\u07d4\u03a3JM\xd9=\u066e\xfd9\x90\x02\xa9}\x99z\x1bK\x89\u0349QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\u03a4?pu\x81k`\xbb\xfc\u62d9:\xf0\x88\x12p\xf6\u0109lk\x93[\x8b\xbd@\x00\x00\u07d4\u03a8t3AS<\xb2\xf0\xb9\xc6\xef\xb8\xfd\xa8\rw\x16(%\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u03b0\x89\xec\x8ax3~\x8e\xf8\x8d\xe1\x1bI\xe3\u0751\x0ft\x8f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u03b3=x\xe7Tz\x9d\xa2\xe8}Q\xae\xc5\xf3D\x1c\x87\x92:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u03b3\x898\x1dH\xa8\xaeO\xfcH:\u043b^ L\xfd\xb1\xec\x89('\xe6\xe4\xddb\xba\x80\x00\u07d4\xce\xc6\xfce\x85?\x9c\xce_\x8e\x84Fv6.\x15y\x01_\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xce\xd3\u01fe\x8d\xe7XQ@\x95*\xebP\x1d\xc1\xf8v\ucbf0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xce\xd8\x1e\xc3S?\xf1\xbf\xeb\xf3\xe3\x84>\xe7@\xad\x11u\x8d>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xce\u0733\xa1\u0584?\xb6\xbe\xf6Ca}\xea\U000cf398\xdd_\x89\x19\xe2\xa4\xc8\x18\xb9\x06\x00\x00\u07d4\xce\xe6\x99\xc0pzx6%+)/\x04|\xe8\xad(\x9b/U\x89\x11\x9a\x1e!\xaaiV\x00\x00\u07d4\xce\xedG\xca[\x89\x9f\xd1b?!\xe9\xbdM\xb6Z\x10\u5c1d\x89\a8w@L\x1e\xee\x00\x00\u07d4\xce\xf7tQ\u07e2\xc6C\xe0\v\x15mlo\xf8N#s\xebf\x89\n1\x06+\xee\xedp\x00\x00\u07d4\xcf\x11i\x04\x1c\x17E\xe4[\x17$5\xa2\xfc\x99\xb4\x9a\xce+\x00\x89\x01\xbb\x88\xba\xab-|\x00\x00\xe0\x94\xcf\x15v\x12vN\x0f\u0596\xc8\xcb_\xba\x85\xdfL\r\xdc<\xb0\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u0794\xcf\x1b\xdby\x9b.\xa6<\xe14f\x8b\xdc\x19\x8bT\x84\x0f\x18\v\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xcf\"\x88\xefN\xbf\x88\xe8m\xb1=\x8a\x0e\v\xf5*\x05e\x82\u00c9\x89Po\xbf\x97@t\x00\x00\u07d4\xcf&Ni%\x13\t\x06\xc4\xd7\xc1\x85\x91\xaaA\xb2\xa6\u007foX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf&\xb4{\xd04\xbcP\x8elK\xcf\xd6\xc7\xd3\x004\x92Wa\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xcf.*\xd65\xe9\x86\x1a\xe9\\\xb9\xba\xfc\xca\x03kR\x81\xf5\u038a\at2!~h6\x00\x00\x00\u07d4\xcf.s@B\xa3U\xd0_\xfb.9\x15\xb1h\x11\xf4Zi^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf4\x8f/\xe4{~A<\az{\xaf:u\xfb\xf8B\x86\x92\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xcf?\x91(\xb0r\x03\xa3\xe1\r}WU\xc0\u012b\xc6\xe2\xca\u008a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xcf?\xbf\xa1\xfd2\u05e6\xe0\xe6\xf8\xefN\xabW\xbe4\x02\\L\x899\xa1\xc0\xf7YMH\x00\x00\u07d4\xcfAftn\x1d;\xc1\xf8\xd0qK\x01\xf1~\x8ab\xdf\x14d\x896w\x03n\xdf\n\xf6\x00\x00\u07d4\xcfO\x118\xf1\xbdk\xf5\xb6\u0505\xcc\xe4\xc1\x01\u007f\u02c5\xf0}\x89/\u043cw\xc3+\xff\x00\x00\u07d4\xcfZo\x9d\xf7Uy\xc6D\xf7\x94q\x12\x15\xb3\rw\xa0\xce@\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf^\x0e\xac\u0473\x9d\x06U\xf2\xf7u5\xeff\b\xeb\x95\v\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcfhM\xfb\x83\x04r\x93U\xb5\x83\x15\xe8\x01\x9b\x1a\xa2\xad\x1b\xac\x89\x17r$\xaa\x84Lr\x00\x00\u07d4\xcfi@\x81\xc7m\x18\xc6L\xa7\x13\x82\xbe\\\xd6;<\xb4v\xf8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xcfnR\xe6\xb7t\x80\xb1\x86~\xfe\xc6Dm\x9f\xc3\xcc5w\xe8\x89\f\t\x01\xf6\xbd\x98y\x00\x00\u07d4\u03c8: 2\x96g\xea\"j\x1e\x9a\x92*\x12\xf2\x1f\xaa\x03\x81V\x91\x8cO\u02dc\x89\x04E\x91\xd6\u007f\xec\xc8\x00\x00\u07d4\xcf\xf7\xf8\x9aMB\x19\xa3\x82\x95%\x131V\x82\x10\xff\xc1\xc14\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xcf\xf8\xd0k\x00\xe3\xf5\f\x19\x10\x99\xadV\xbaj\xe2eq\u0348\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcf\xfcI\xc1x~\ubcb5l\xab\xe9$\x04\xb66\x14}EX\x8a\x013\xe00\x8f@\xa3\u0680\x00\u07d4\xd0\bQ;'`J\x89\xba\x17c\xb6\xf8L\u6233F\x94[\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\x0f\x06r\x86\xc0\xfb\u0402\xf9\xf4\xa6\x10\x83\xecv\u07b3\xce\xe6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\x15\xf6\xfc\xb8M\xf7\xbbA\x0e\x8c\x8f\x04\x89J\x88\x1d\xca\xc27\x898E$\xccp\xb7x\x00\x00\u07d4\xd0\x1a\xf9\x13O\xafRW\x17N\x8by\x18oB\xee5Nd-\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0!\b\u04ae<\xab\x10\xcb\xcf\x16W\xaf\">\x02|\x82\x10\xf6\x89lm\x84\xbc\xcd\xd9\xce\x00\x00\u07d4\xd0*\xfe\u03ce.\u00b6*\u022d Aa\xfd\x1f\xaew\x1d\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd01\x919\xfb\xab.\x8e*\xcc\xc1\xd9$\u0531\x1d\xf6ilZ\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd07\xd2\x15\xd1\x1d\x1d\xf3\xd5O\xbd2\x1c\u0495\xc5F^';\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xd0:-\xa4\x1e\x86\x8e\xd3\xfe\xf5t[\x96\xf5\xec\xa4b\xffo\u0689\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xd0?\xc1eWj\xae\xd5%\xe5P,\x8e\x14\x0f\x8b.\x86\x969\x8a\x01sV\u0633%\x01\xc8\x00\x00\u07d4\xd0C\xa0\x11\xecBp\xee~\u0239hsu\x15\xe5\x03\xf80(\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd0K\x86\x1b=\x9a\xccV:\x90\x16\x89\x94\x1a\xb1\xe1\x86\x11a\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd0ZD|\x91\x1d\xbb'[\xfb.Z7\xe5\xa7\x03\xa5o\x99\x97\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd0_\xfb+t\xf8g O\xe51e;\x02H\xe2\x1c\x13TN\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0bX\x81q\u03d9\xbb\xebX\xf1&\xb8p\xf9\xa3r\x8da\xec\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\u07d4\xd0c\x8e\xa5q\x89\xa6\xa6\x99\x02J\u05ccq\xd99\xc1\xc2\xff\x8c\x89\x8e\xaeVg\x10\xfc \x00\x00\xe0\x94\xd0d\x8aX\x1b5\b\xe15\xa2\x93]\x12\xc9epE\xd8q\u028a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4\xd0q\x19)f\xebi\xc3R\x0f\xca:\xa4\xdd\x04)~\xa0KN\x89\x05\xf6\x8e\x811\xec\xf8\x00\x00\u07d4\xd0q\x85 \xea\xe0\xa4\xd6-p\xde\x1b\xe0\xcaC\x1c^\xea$\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd0w]\xba*\xf4\xc3\n:x6Y9\xcdq\xc2\xf9\u0795\u0489i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xd0{\xe0\xf9\t\x97\xca\xf9\x03\u022c\x1dS\xcd\xe9\x04\xfb\x19\aA\x8968\x908\xb6\x99\xb4\x00\x00\u07d4\xd0~Q\x18d\xb1\u03d9i\xe3V\x06\x02\x82\x9e2\xfcNq\xf5\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\u0400\x94\x98\xc5H\x04z\x1e**\xa6\xa2\x9c\xd6\x1a\x0e\xe2h\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0402'_tZ,\xac\x02v\xfb\xdb\x02\u0532\xa3\xab\x17\x11\xfe\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\u040f\xc0\x9a\x000\xfd\t(\xcd2\x11\x98X\x01\x82\xa7j\xae\x9f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0413\xe8)\x81\x9f\xd2\xe2[\x978\x00\xbb=XA\xdd\x15-\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0414J\xa1\x85\xa13pa\xae \u071d\xd9l\x83\xb2\xbaF\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u0416V[|t\a\xd0e6X\x03U\xfd\xd6\xd29\x14J\xa1\x89\r\x8drkqw\xa8\x00\x00\u07d4\u041c\xb2\xe6\b-i:\x13\xe8\xd2\xf6\x8d\xd1\u0744a\xf5X@\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0426\xc6\xf9\xe9\u0133\x83\xd7\x16\xb3\x1d\xe7\x8dVAM\xe8\xfa\x91\x89\x10CV\x1a\x88)0\x00\x00\u07d4\u0427 \x9b\x80\xcf`\xdbb\xf5}\n]}R\x1ai`fU\x89\b\xacr0H\x9e\x80\x00\x00\xe0\x94\u0428\xab\xd8\n\x19\x9bT\xb0\x8be\xf0\x1d \x9c'\xfe\xf0\x11[\x8a\x01a\xc6&\xdca\xa2\xef\x80\x00\xe0\x94\u042b\xccp\xc0B\x0e\x0e\x17/\x97\xd4;\x87\xd5\xe8\f3n\xa9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u042es]\x91^\x94hf\xe1\xfe\xa7~^\xa4f\xb5\xca\xdd\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0431\x1do+\u0394^\fjP \u00f5'S\xf8\x03\xf9\u0449\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xd0\xc1\x01\xfd\x1f\x01\xc6?k\x1d\x19\xbc\x92\r\x9f\x93#\x14\xb16\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd0\xc5Z\xbf\x97o\xdc=\xb2\xaf\u9f99\u0519HMWl\x02\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\u0422\xadE\xf5\x9a\x9d\xcc\u0195\xd8_%\xcaF\xed1\xa5\xa3\x89-\x89W}}@ \x00\x00\u07d4\xd0\xd6,G\xea`\xfb\x90\xa3c\x92\t\xbb\xfd\xd4\xd93\x99\x1c\u0189\n\x84Jt$\xd9\xc8\x00\x00\u07d4\xd0\xdbEax o\\D0\xfe\x00Pc\x90<=zI\xa7\x89&I\x1eE\xa7S\xc0\x80\x00\u07d4\xd0\xe1\x94\xf3K\x1d\xb6\t(\x85\t\xcc\xd2\xe7;a1\xa2S\x8b\x8965f3\xeb\xd8\xea\x00\x00\u07d4\xd0\xe3^\x04vF\xe7Y\xf4Qp\x93\xd6@\x86BQ\u007f\bM\x89\u054f\xa4h\x18\xec\u02c0\x00\u07d4\xd0\xeeM\x02\xcf$8,0\x90\xd3\xe9\x95`\xde6xs\\\u07c9\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xd0\xf0OR\x10\x9a\xeb\xec\x9a{\x1e\x932v\x1e\x9f\xe2\xb9{\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd0\xf9Yx\x11\xb0\xb9\x92\xbb}7W\xaa%\xb4\xc2V\x1d2\xe2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\x03\x02\xfa\xa1\x92\x9a2i\x04\xd3v\xbf\v\x8d\xc9:\xd0LL\x89a\t=|,m8\x00\x00\xe0\x94\xd1\x10\r\xd0\x0f\xe2\xdd\xf1\x81c\xad\x96M\vi\xf1\xf2\xe9e\x8a\x8a\x01C\x12\tU\xb2Pk\x00\x00\u07d4\xd1\x16\xf3\xdc\xd5\xdbtK\xd0\b\x88v\x87\xaa\x0e\xc9\xfdr\x92\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd1\x19A|Fs,\xf3M\x1a\x1a\xfby\xc3\xe7\xe2\u034e\xec\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1-w\xae\x01\xa9-5\x11{\xacpZ\xac\u0642\xd0.t\xc1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd15yK\x14\x9a\x18\xe1G\xd1nb\x1ai1\xf0\xa4\n\x96\x9a\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xd1C%8\xe3[vd\x95j\u4563*\xbd\xf0A\xa7\xa2\x1c\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xd1C\x82g#\x17\x04\xfcr\x80\xd5c\xad\xf4v8D\xa8\a\"\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd1S\x8e\x9a\x87\u5729\xec\x8eX&\xa5\xb7\x93\xf9\x9f\x96\xc4\u00c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd1d\x85\x03\xb1\xcc\u0178\xbe\x03\xfa\x1e\xc4\xf3\xee&~j\xdf{\x8a\x01;\xef\xbfQ\xee\xc0\x90\x00\x00\xe0\x94\xd1h,!Y\x01\x8d\xc3\xd0\u007f\b$\n\x8c`m\xafe\xf8\xe1\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xd1q\xc3\xf2%\x8a\xef5\xe5\x99\xc7\xda\x1a\xa0s\x00#M\xa9\xa6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1w\x8c\x13\xfb\xd9h\xbc\b<\xb7\xd1\x02O\xfe\x1fI\xd0,\xaa\x89\xd9\xec\xb4\xfd \x8eP\x00\x00\u07d4\xd1\u007f\xbe\"\xd9\x04b\xed7(\x06p\xa2\xea\v0\x86\xa0\xd6\u0589\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\u0441\x1cU\x97i\x80\xf0\x83\x90\x1d\x8a\r\xb2i\"-\xfb\\\xfe\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u044e\xb9\xe1\u0485\u06be\x93\xe5\u053a\xe7k\xee\xfeC\xb5!\xe8\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\u0453\xe5\x83\xd6\a\x05c\xe7\xb8b\xb9aJG\u9509\xf3\xe5\x8965f3\xeb\xd8\xea\x00\x00\u07d4\u0457\x8f.4@\u007f\xab\x1d\xc2\x18=\x95\xcf\xdab`\xb3Y\x82\x89*\xb7\xb2`\xff?\xd0\x00\x00\u07d4\u045c\xaf9\xbb7\u007f\xdf,\xf1\x9b\xd4\xfbRY\x1c&1\xa6<\x8965\u026d\xc5\u07a0\x00\x00\u0794\u0463\x96\xdc\u06b2\xc7IA0\xb3\xfd0x 4\r\xfd\x8c\x1f\x88\xf9\"P\xe2\xdf\xd0\x00\x00\xe0\x94\u0467\x1b-\bX\xe82p\b]\x95\xa3\xb1T\x96P\x03^#\x8a\x03'\xbb\t\xd0j\xa8P\x00\x00\u07d4\u046c\xb5\xad\xc1\x189s%\x8dk\x85$\xff\xa2\x8f\xfe\xb2=\xe3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0473\u007f\x03\xcb\x10t$\xe9\xc4\xddW\\\xcdOL\xeeW\xe6\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4\u0475\xa4T\xac4\x05\xbbAy \x8cl\x84\xde\x00k\u02db\xe9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\xc4YT\xa6+\x91\x1a\xd7\x01\xff.\x90\x13\x1e\x8c\xeb\x89\xc9\\\x89K\x91\xa2\xdeE~\x88\x00\x00\u07d4\xd1\xc9np\xf0Z\xe0\xe6\xcd`!\xb2\b7P\xa7q|\xdeV\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\u0571\u007f\xfe-{\xbby\xcc}y0\xbc\xb2\xe5\x18\xfb\x1b\xbf\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd1\xda\f\x8f\xb7\xc2\x10\xe0\xf2\xeca\x8f\x85\xbd\xae}>sK\x1c\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd1\xddy\xfb\x15\x81`\xe5\xb4\xe8\xe2?1.j\x90\u007f\xbcMN\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\xdeZ\xad:_\xd8\x03\U00071bb6\x10<\xb8\xe1O\xe7#\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd1\xe1\xf2\xb9\xc1l0\x98t\xde\xe7\xfa\xc3&u\xaf\xf1)\u00d8\x89\x03\xf2M\x8eJ\x00p\x00\x00\xe0\x94\xd1\xe5\xe24\xa9\xf4Bf\xa4\xa6$\x1a\x84\u05e1\xa5Z\u0567\xfe\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd1\xeaMr\xa6{[>\x0f1UY\xf5+\xd0aMq0i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1\xee\x90YW\xfe|\xc7\x0e\xc8\xf2\x86\x8bC\xfeG\xb1?\xeb\xff\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4\xd1\xf1iM\"g\x1bZ\xadj\x94\x99\\6\x9f\xbea3go\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd1\xf4\xdc\x1d\u06ca\xbb\x88H\xa8\xb1N%\xf3\xb5Z\x85\x91\xc2f\x89\r\x8drkqw\xa8\x00\x00\u07d4\xd1\xfe\u042e\xe6\xf5\xdf\xd7\xe2Wi%L<\xfa\xd1Z\xde\u032a\x89'\x92\xc8\xfcKS(\x00\x00\u07d4\xd2\x05\x1c\xb3\xcbg\x04\xf0T\x8c\u0210\xab\n\x19\xdb4\x15\xb4*\x89\x12\x1b.^ddx\x00\x00\u07d4\xd2\x06\xaa\u07736\xd4^yr\xe9<\xb0uG\x1d\x15\x89{]\x89 \x86\xac5\x10R`\x00\x00\u07d4\xd2\tH+\xb5I\xab\xc4w{\xeam\u007fe\x00b\xc9\xc5z\x1c\x89\x11e\x1a\xc3\xe7\xa7X\x00\x00\u07d4\xd2\r\xcb\vxh+\x94\xbc0\x00(\x14H\xd5W\xa2\v\xfc\x83\x890\x84\x9e\xbe\x166\x9c\x00\x00\u07d4\xd2\x10{57&\u00e2\xb4ef\xea\xa7\xd9\xf8\v]!\xdb\xe3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd2\x11\xb2\x1f\x1b\x12\xb5\ta\x81Y\r\xe0~\xf8\x1a\x89S~\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd2\x18\xef\xb4\u06d8\x1c\xddjy\u007fK\u050c|&)<\xeb@\x89\xa1Fk1\xc6C\x1c\x00\x00\xe0\x94\xd2\x1asA\xeb\x84\xfd\x15\x10T\xe5\u31fb%\xd3nI\x9c\t\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xd2$\xf8\x80\xf9G\x9a\x89\xd3/\t\xe5+\u9432\x88\x13\\\xef\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xd2/\f\xa4\xcdG\x9ef\x17u\x05;\xccI\xe3\x90\xf6p\u074a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd21\x92\x975\x13!\x02G\x1b\xa5\x90\a\xb6dL\xc0\xc1\xde>\x8967\tlK\xcci\x00\x00\u07d4\xd25\xd1\\\xb5\xec\xee\xbba)\x9e\x0e\x82\u007f\xa8'H\x91\x1d\x89\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd2:$\xd7\xf9F\x83C\xc1C\xa4\x1ds\xb8\x8f|\xbec\xbe^\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd2=z\xff\xac\xdc>\x9f=\xaez\xfc\xb4\x00oX\xf8\xa4F\x00\x89\xc3(\t>a\xee@\x00\x00\u07d4\xd2C\x18L\x80\x1e]y\xd2\x06?5x\u06ee\x81\u7ce9\u02c9k\u0722h\x1e\x1a\xba\x00\x00\u07d4\xd2KfD\xf49\xc8\x05\x1d\xfcd\u04c1\xb8\xc8lu\xc1u8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd2K\xf1--\xdfE}\xec\xb1xt\xef\xde R\xb6\\\xbbI\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xd2Q\xf9\x03\xae\x18rrY\xee\xe8A\xa1\x89\xa1\xf5i\xa5\xfdv\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd2R\x96\v\v\xf6\xb2\x84\x8f\u07ad\x80\x13m\xb5\xf5\a\xf8\xbe\x02\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd2X\x1aU\xce#\xab\x10\u062d\x8cD7\x8fY\a\x9b\xd6\xf6X\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\xd2Z\xec\xd7\xeb\x8b\xd64[\x06;]\xbd'\x1cw\xd3QD\x94\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xd2|#O\xf7\xac\xca\xce=\x99g\b\xf8\xf9\xb0Ip\xf9}6\x89Hz\x9a0E9D\x00\x00\u07d4\u0482\x98RM\xf5\xecK$\xb0\xff\xb9\u07c5\x17\n\x14Z\x9e\xb5\x89\x0f\x98\xa3\xb9\xb37\xe2\x00\x00\xe0\x94\u0483\xb8\xed\xb1\n%R\x8aD\x04\xde\x1ce\xe7A\r\xbc\xaag\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\u0484\xa5\x03\x82\xf8:am9\xb8\xa9\xc0\xf3\x96\xe0\ubfe9]\x8966\xc2^f\xec\xe7\x00\x00\u07d4\u0488\xe7\xcb{\xa9\xf6 \xab\x0ftR\xe5\bc=\x1cZ\xa2v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u049d\xc0\x8e\xfb\xb3\xd7.&?x\xabv\x10\xd0\"m\xe7k\x00\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\u04a00\xac\x89R2_\x9e\x1d\xb3x\xa7\x14\x85\xa2N\x1b\a\xb2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u04a4y@CG\xc5T:\xab)*\xe1\xbbJo\x15\x83W\xfa\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u04a5\xa0$#\nW\xcc\xc6fv\v\x89\xb0\xe2l\xaf\u0449\u01ca\n\x96YZ\\n\x8a?\x80\x00\u07d4\u04a8\x03'\xcb\xe5\\L{\xd5\x1f\xf9\xdd\xe4\xcad\x8f\x9e\xb3\xf8\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\u04a8Oug\\b\xd8\f\x88ulB\x8e\xee+\xcb\x18T!\x89A\rXj \xa4\xc0\x00\x00\u07d4\u04ab\xd8J\x18\x10\x93\xe5\xe2)\x13oB\xd85\xe8#]\xe1\t\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\u04ac\r:X`^\x1d\x0f\x0e\xb3\xde%\xb2\xca\xd1)\xed`X\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u04bfg\xa7\xf3\xc6\xceV\xb7\xbeAg]\xbb\xad\xfe~\xa9:3\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd2\xdb\xeb\xe8\x9b\x03W\xae\xa9\x8b\xbe\x8eIc8\u07bb(\xe8\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd2\xe2\x1e\xd5hh\xfa\xb2\x8e\tG\x92z\xda\xf2\x9f#\xeb\xadl\x89l\x18O\x13U\xd0\xe8\x00\x00\u07d4\xd2\xe8\x17s\x8a\xbf\x1f\xb4\x86X?\x80\xc3P1\x8b\xed\x86\f\x80\x89\r\x02\xce\xcf_]\x81\x00\x00\u07d4\xd2\xed\xd1\xdd\xd6\xd8m\xc0\x05\xba\xebT\x1d\"\xb6@\xd5\xc7\xca\xe5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd2\xf1\x99\x8e\x1c\xb1X\f\xecOl\x04}\xcd=\xce\xc5L\xf7<\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd2\xf2A%]\xd7\xc3\xf7<\a\x040q\xec\b\xdd\xd9\xc5\xcd\xe5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd2\xffg \x16\xf6;/\x859\x8fJo\xed\xbb`\xa5\r<\u0389\x12\x91$o[sJ\x00\x00\u07d4\xd3\rLC\xad\xcfU\xb2\xcbS\u0583#&A4I\x8d\x89\u038965\u026d\xc5\u07a0\x00\x00\u07d4\xd3\x0e\xe9\xa1+Mh\xab\xac\xe6\xba\u029a\u05ff\\\xd1\xfa\xf9\x1c\x89QO\xcb$\xff\x9cP\x00\x00\u07d4\xd3\x11\x8e\xa3\xc85\x05\xa9\u0613\xbbg\xe2\xde\x14-Sz>\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd3\x11\xbc\u05eaN\x9bO8?\xf3\xd0\u05b6\xe0~!\xe3p]\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd3\x15\xde\xea\x1d\x8c\x12q\xf9\xd11\x12c\xabG\xc0\a\xaf\xb6\xf5\x89\x03\xc8\x1dNeK@\x00\x00\u07d4\xd3+,y\xc3dx\xc5C\x19\x01\xf6\xd7\x00\xb0M\xbe\x9b\x88\x10\x89\x15w\x9a\x9d\xe6\xee\xb0\x00\x00\u07d4\xd3+EVF\x14Ql\x91\xb0\u007f\xa9\xf7-\xcfx|\xceN\x1c\x89\x0f\xc6o\xae7F\xac\x00\x00\u07d4\xd30r\x811\xfe\x8e:\x15Hz4W<\x93E~*\xfe\x95\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd31\xc8#\x82Z\x9eRc\xd0R\u0611]M\xcd\xe0z\\7\x89\x1e\x93\x12\x83\xcc\xc8P\x00\x00\u07d4\xd33btE\xf2\u05c7\x90\x1e\xf3;\xb2\xa8\xa3g^'\xff\xec\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd3<\xf8+\xf1LY&@\xa0\x86\b\x91L#py\u057e4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd3Mp\x8ds\x98\x02E3\xa5\xa2\xb20\x9b\x19\xd3\xc5Qq\xbb\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd3N\x03\xd3j+\xd4\u045a_\xa1b\x18\xd1\xd6\x1e?\xfa\v\x15\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\xd3Pu\xcaa\xfeY\xd1#\x96\x9c6\xa8-\x1a\xb2\xd9\x18\xaa8\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xd3g\x00\x9a\xb6X&;b\xc23:\x1c\x9eA@I\x8e\x13\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd3g\x9aG\xdf-\x99\xa4\x9b\x01\u024d\x1c>\f\x98|\xe1\xe1X\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94\u04cf\xa2\xc4\xcc\x14z\xd0j\u0562\xf7Uy(\x1f\"\xa7\xcc\x1f\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\u04da]\xa4`9+\x94\v\u01ee8\xf1e\u007f\x8a\x01f\xc5H\b\x89\xdbw\x00\x00\xe0\x94\xd3\xd6\xe9\xfb\x82T/\u049e\xd9\xea6\t\x89\x1e\x15\x13\x96\xb6\xf7\x8a\voX\x8a\xa7\xbc\xf5\xc0\x00\x00\xe0\x94\xd3\xda\u0476\u040dE\x81\u032ee\xa8s-\xb6\xaci\xf0\u019e\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xd3\xdf;S\xcb;GU\xdeT\xe1\x80E\x1c\xc4L\x9e\x8a\u0a89#\u0114\t\xb9w\x82\x80\x00\u07d4\xd3\xf8s\xbd\x99V\x13W\x89\xab\x00\xeb\xc1\x95\xb9\"\xe9K%\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\x02\xb4\xf6\xa0\x99\xeb\xe7\x16\xcb\x14\xdfOy\xc0\xcd\x01\xc6\a\x1b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd4\r\x00U\xfd\x9a8H\x8a\xff\x92?\xd0=5\xecF\xd7\x11\xb3\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\xd4\x0e\xd6j\xb3\xce\xff$\xca\x05\xec\xd4q\ufd12\xc1__\xfa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd4\x18\x87\v\xc2\xe4\xfa{\x8aa!\xae\br\xd5RG\xb6%\x01\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xd4\x1d\u007f\xb4\x9f\xe7\x01\xba\xac%qpBl\u0273\x8c\xa3\xa9\xb2\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4\xd4 U\x92\x84@U\xb3\u01e1\xf8\f\xef\xe3\xb8\xebP\x9b\xcd\xe7\x89\t\xb3\xbf\xd3B\xa9\xfc\x80\x00\u07d4\xd4+ \xbd\x03\x11`\x8bf\xf8\xa6\xd1[*\x95\xe6\xde'\u017f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd44O}\\\xade\xd1~\\-\x0es#\x94=ob\xfe\x92\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xd4>\xe48\xd8=\xe9\xa3ub\xbbN(l\xb1\xbd\x19\xf4\x96M\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd4C4\xb4\xe2:\x16\x9a\f\x16\xbd!\xe8f\xbb\xa5-\x97\x05\x87\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\xe0\x94\xd4M\x81\xe1\x8fF\xe2\u03f5\xc1\xfc\xf5\x04\x1b\xc8V\x97g\xd1\x00\x8a\a\xb4B\xe6\x84\xf6Z\xa4\x00\x00\u07d4\xd4OJ\xc5\xfa\xd7k\xdc\x157\xa3\xb3\xafdr1\x9bA\r\x9d\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\xd4O^\xdf+\xcf$3\xf2\x11\xda\xdd\f\xc4P\xdb\x1b\x00\x8e\x14\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xd4Oj\u00d2;_\xd71\xa4\xc4YD\xecO~\xc5*j\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd4[3A\xe8\xf1\\\x802\x93 \u00d7~;\x90\xe7\x82j~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd4]]\xaa\x13\x8d\xd1\xd3t\xc7\x1b\x90\x19\x91h\x11\xf4\xb2\nN\x89\x1f9\x9b\x148\xa1\x00\x00\x00\u07d4\xd4`\xa4\xb9\b\xdd+\x05gY\xb4\x88\x85\vf\xa88\xfcw\xa8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd4g\xcf\x06L\bq\x98\x9b\x90\u0632\xeb\x14\xcc\xc6;6\b#\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd4k\xaea\xb0'\xe5\xbbB.\x83\xa3\xf9\xc9?<\x8f\xc7}'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4o\x82#E)\x82\xa1\xee\xa0\x19\xa8\x81n\xfc-o\xc0\ah\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xd4uG\u007f\xa5c\x90\xd30\x17Q\x8dg\x11\x02\u007f\x05\U0008dfc9k\x11\x133\xd4\xfdL\x00\x00\u07d4\xd4|$.\xdf\xfe\xa0\x91\xbcT\xd5}\xf5\xd1\xfd\xb91\x01Gl\x89\x9d\xf7\u07e8\xf7`H\x00\x00\u07d4\xd4}\x86\x85\xfa\xee\x14|R\x0f\u0646p\x91u\xbf/\x88k\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\u007fP\u07c9\xa1\xcf\xf9e\x13\xbe\xf1\xb2\xae:)q\xac\xcf,\x89-\x89W}}@ \x00\x00\u07d4\u0502\xe7\xf6\x8eA\xf28\xfeQx)\xde\x15G\u007f\xe0\xf6\xdd\x1d\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u0507\x9f\xd1+\x1f:'\xf7\xe1\tv\x1b#\xca4\xfa#\x06K\x1c\xaf\x00Qn(pJ\x82\xa4\xf8\x89Hz\x9a0E9D\x00\x00\u07d4\xd5\x00\xe4\xd1\u0242K\xa9\xf5\xb65\u03e3\xa8\xc2\u00cb\xbdL\xed\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd5\b\u04dcp\x91oj\xbcL\xc7\xf9\x99\xf0\x11\xf0w\x10X\x02\x89\x05rM$\xaf\xe7\u007f\x00\x00\u07d4\xd5\x0f\u007f\xa0>8\x98v\u04d0\x8b`\xa57\xa6pc\x04\xfbV\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd5\x13\xa4P\x80\xff/\xeb\xe6,\u0545J\xbe)\xeeDg\xf9\x96\x89\bN\x13\xbcO\xc5\xd8\x00\x00\u07d4\xd5'o\f\xd5\xff\xd5\xff\xb6?\x98\xb5p=U\x94\xed\xe0\x83\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd5)KfbB0;m\xf0\xb1\u020d7B\x9b\xc8\xc9e\xaa\x89\x10M\r\x00\u04b7\xf6\x00\x00\u07d4\xd5*\xec\xc6I98\xa2\x8c\xa1\xc3g\xb7\x01\xc2\x15\x98\xb6\xa0.\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\xd5\x99x\xee \xa3\x8c?I\x8dc\xd5\u007f1\xa3\x9fj\x06\x8a\x022\xb3o\xfcg*\xb0\x00\x00\u07d4\u05568\xd3\xc5\xfa\xa7q\x1b\xf0\x85t_\x9d[\xdc#\u0518\u0609lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u055d\x92\xd2\xc8p\x19\x80\xcc\a<7]r\n\xf0dt<\f\x8a\x04\x05\xfd\xf7\u5bc5\xe0\x00\x00\u07d4\u0567\xbe\xc32\xad\xde\x18\xb3\x10KW\x92Tj\xa5\x9b\x87\x9bR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0571\x17\xec\x11n\xb8FA\x89a\xeb~\xdbb\x9c\xd0\xddi\u007f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\u0572\x84\x04\x010\xab\xf7\xc1\xd1cq#q\xcc~(\xadf\u0689j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0579\xd2w\u062a\xd2\x06\x97\xa5\x1fv\xe2\tx\x99k\xff\xe0U\x89\a\xc3\xfe<\aj\xb5\x00\x00\u07d4\u057d^\x84U\xc10\x16\x93W\xc4q\xe3\u6077\x99jrv\x89-\x9e(\x8f\x8a\xbb6\x00\x00\u07d4\xd5\u02e5\xb2k\xea]s\xfa\xbb\x1a\xba\xfa\xcd\xef\x85\xde\xf3h\u0309\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd5\xceU\u0476/YC\xc0?\x89\b\xe3\x1f\xe1h\x9d\x8a\x00\x00\u07d4\xd6\x06Q\xe3\x93x4#\xe5\xcc\x1b\xc5\xf8\x89\xe4N\xf7\xea$>\x89\x15\x9ev7\x11)\xc8\x00\x00\u07d4\xd6\t\xbfO\x14n\xeak\r\xc8\xe0m\xdc\xf4D\x8a\x1f\xcc\xc9\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\t\xec\v\xe7\r\n\xd2ong\xc9\xd4v+R\xeeQ\x12,\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd6\nRX\a(R\r\xf7Tk\xc1\xe2\x83)\x17\x88\u06ee\f\x8964\x89\xef?\xf0\xd7\x00\x00\u07d4\xd6\v$s!\xa3*Z\xff\xb9k\x1e'\x99'\xccXM\xe9C\x89z\xd0 \xd6\xdd\xd7v\x00\x00\u07d4\xd6\x11\x02v\xcf\xe3\x1eB\x82ZW\u007fkC]\xbc\xc1\f\xf7d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd6\x12Y{\xc3\x17C\u01c63\xf63\xf29\xb1\xe9Bk\xd9%\x8a\x10\x17\xf7\u07d6\xbe\x17\x80\x00\x00\u07d4\xd6#J\xafE\xc6\xf2.f\xa2%\xff\xb9:\xddb\x9bN\xf8\x0f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd6.\u06d6\xfc\u259a\xaflT^\x96|\xf1\xc0\xbc\x80R\x05\x89\x04\xa5eSjZ\u0680\x00\u07d4\xd60\v2\x15\xb1\x1d\xe7b\xec\xdeKp\xb7\x92}\x01)\x15\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd69]\xb5\xa4\xbbf\xe6\x0fL\xfb\xcd\xf0\x05{\xb4\xd9xb\xe2\x891T\xc9r\x9d\x05x\x00\x00\xe0\x94\xd6J-P\xf8\x85\x857\x18\x8a$\xe0\xf5\r\xf1h\x1a\xb0~\u05ca\b7Z*\xbc\xca$@\x00\x00\u07d4\xd6X\n\xb5\xedL}\xfaPo\xa6\xfed\xad\\\xe1)pw2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd6Y\x8b\x13\x86\xe9<\\\u02d6\x02\xffK\xbb\xec\xdb\xd3p\x1d\u0109\f%\xf4\xec\xb0A\xf0\x00\x00\u07d4\xd6dM@\xe9\v\xc9\u007f\xe7\xdf\xe7\u02bd2i\xfdW\x9b\xa4\xb3\x89\b\x9e\x91y\x94\xf7\x1c\x00\x00\xe0\x94\xd6g\f\x03m\xf7T\xbeC\xda\u074fP\xfe\xea(\x9d\x06\x1f\u058a\x01D\xa2\x904H\xce\xf7\x80\x00\u07d4\xd6hR:\x90\xf0)=e\xc58\xd2\xddlWg7\x10\x19n\x89\x02$,0\xb8S\xee\x00\x00\u07d4\xd6j\xb7\x92\x94\aL\x8bb}\x84-\xabA\xe1}\xd7\f]\xe5\x8965\u026d\xc5\u07a0\x00\x00\u0794\xd6j\xcc\r\x11\xb6\x89\u03a6\xd9\xea_\xf4\x01L\"J]\xc7\u0108\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xd6m\xdf\x11Y\xcf\"\xfd\x8czK\xc8\u0540wV\xd43\xc4>\x89wC\"\x17\xe6\x83`\x00\x00\u07d4\u0587\xce\xc0\x05\x90\x87\xfd\xc7\x13\xd4\xd2\xd6^w\xda\xef\xed\xc1_\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\u0588\xe7\x85\u024f\x00\xf8K:\xa1S3U\u01e2X\xe8yH\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u05a2.Y\x8d\xab\u04ce\xa6\xe9X\xbdy\u050d\u0756\x04\xf4\u07c965\u026d\xc5\u07a0\x00\x00\u07d4\u05a7\xacM\xe7\xb5\x10\xf0\xe8\xdeQ\x9d\x97?\xa4\xc0\x1b\xa84\x00\x89e\xea=\xb7UF`\x00\x00\u07d4\u05ac\xc2 \xba.Q\xdf\xcf!\xd4C6\x1e\xeav\\\xbd5\u0609\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u05ac\xff\u043f\u065c8.{\xd5o\xf0\xe6\x14J\x9eR\xb0\x8e\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xd6\xc0\u043c\x93\xa6.%qtp\x0e\x10\xf0$\u0232?\x1f\x87\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd6\xcf\\\x1b\u03dd\xa6b\xbc\xea\"U\x90P\x99\xf9\xd6\xe8M\u030a\x01\u011eB\x01W\xd9\xc2\x00\x00\u07d4\xd6\xd05r\xa4RE\xdb\xd46\x8cO\x82\xc9W\x14\xbd!g\xe2\x89?\x00\xc3\xd6f\x86\xfc\x00\x00\u07d4\xd6\xd6wiX\xee#\x14:\x81\xad\xad\xeb\b8 \t\xe9\x96\u0089\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd6\xd9\xe3\x0f\bB\x01*qv\xa9\x17\xd9\xd2\x04\x8c\xa0s\x87Y\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd6\xe0\x9e\x98\xfe\x13\x003!\x04\xc1\xca4\xfb\xfa\xc5T6N\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\xe8\xe9z\u90db\x9e\xe5\a\xee\xdb(\xed\xfbtw\x03\x149\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\uea18\u052e+q\x80'\xa1\x9c\xe9\xa5\xebs\x00\xab\xe3\u0289\x01}J\xce\xeec\u06c0\x00\xe0\x94\xd6\xf1\xe5[\x16\x94\b\x9e\xbc\xb4\xfe}x\x82\xaaf\u0217av\x8a\x04<#\xbd\xbe\x92\x9d\xb3\x00\x00\u07d4\xd6\xf4\xa7\xd0N\x8f\xaf \xe8\xc6\ub15c\xf7\xf7\x8d\xd2=z\x15\x89\a$\xde\xd1\xc7H\x14\x00\x00\u07d4\xd6\xfc\x04F\u01a8\xd4\n\xe3U\x1d\xb7\xe7\x01\xd1\xfa\x87nJI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\x03\u01a4\xf1\x1d`\x19Ey\u054c'f\xa7\xef\x16\xc3\n)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\x05%\x19uj\xf4%\x90\xf1S\x91\xb7#\xa0?\xa5d\xa9Q\x89\xfa61H\r\x01\xfd\x80\x00\u07d4\xd7\na+\xd6\u0769\xea\xb0\xdd\xdc\xffJ\xafA\"\u04cf\xea\xe4\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\xd7\n\xd2\xc4\xe9\uefe67\xefV\xbdHj\u04a1\xe5\xbc\xe0\x93\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\x14\f\x8eZC\a\xfa\xb0\xcc'\xba\u0752\x95\x01\x8b\xf8yp\x89\x05\xf1\x01kPv\xd0\x00\x00\u07d4\xd7\x16J\xa2a\xc0\x9a\u0672\xb5\x06\x8dE>\xd8\xebj\xa10\x83\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd7\x1eC\xa4Qw\xadQ\xcb\xe0\xf7!\x84\xa5\xcbP9\x17(Z\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\x1f\xb10\xf0\x15\fVRi\xe0\x0e\xfbC\x90+R\xa4U\xa6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\"W8\xdc\xf3W\x848\xf8\xe7\u0233\x83~B\xe0J&/\x89\x18+\x8c\ubec3\xaa\x00\x00\u07d4\xd7'MP\x80M\x9cw\u0693\xfaH\x01V\xef\xe5{\xa5\x01\u0789i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xd71\xbbk_<79^\t\u03ac\xcd\x14\xa9\x18\xa6\x06\a\x89\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xd7>\xd2\u0645\xb5\xf2\x1bU\xb2td;\xc6\xda\x03\x1d\x8e\u074d\x8a\nm\xd9\f\xaeQ\x14H\x00\x00\u07d4\xd7D\xac~S\x10\xbeijc\xb0\x03\xc4\v\xd097\x05a\u0189Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\xd7Jn\x8dj\xab4\u0385\x97h\x14\xc12{\xd6\xea\a\x84\u048a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xd7ZP*[gr\x87G\x0fe\u016aQ\xb8|\x10\x15\x05r\x8910\xb4dc\x85t\x00\x00\u07d4\xd7m\xba\xeb\xc3\rN\xf6{\x03\xe6\xe6\xec\xc6\xd8N\x00MP-\x89mv\xb9\x18\x8e\x13\x85\x00\x00\u07d4\xd7q\xd9\xe0\u028a\b\xa1\x13wW1CN\xb3'\x05\x99\xc4\r\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd7x\x8e\xf2\x86X\xaa\x06\xccS\xe1\xf3\xf0\xdeX\xe5\xc3q\xbex\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xd7x\x92\xe2';#]v\x89\xe40\xe7\xae\ud73c\xe8\xa1\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u05c1\xf7\xfc\t\x18F\x11V\x85p\xb4\x98n,r\x87+~\u0409\x01\x15\x95a\x06]]\x00\x00\u07d4\u05c5\xa8\xf1\x8c8\xb9\xbcO\xfb\x9b\x8f\xa8\xc7r{\xd6B\xee\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u05ce\xcd%\xad\xc8k\xc2\x05\x1d\x96\xf6Sd\x86kB\xa4&\xb7\x89\xd20X\xbf/&\x12\x00\x00\xe0\x94\u05cf\x84\xe3\x89D\xa0\xe0%_\xae\xceH\xbaIP\u053d9\u048a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\u05d4\x83\xf6\xa8DO%I\xd6\x11\xaf\xe0,C-\x15\xe1\x10Q\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u05d85\xe4\x04\xfb\x86\xbf\x84_\xba\t\rk\xa2^\f\x88f\xa6\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\u05da\xff\x13\xba-\xa7]F$\f\xac\n$g\xc6V\x94\x98#\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\u05dd\xb5\xabCb\x1az=\xa7\x95\xe5\x89)\xf3\xdd%\xafg\u0649lj\xccg\u05f1\xd4\x00\x00\u07d4\u05e1C\x1e\xe4S\xd1\xe4\x9a\x05P\xd1%hy\xb4\xf5\xd1\x02\x01\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\u05ed\t\xc6\xd3&WhSU\xb5\xc6\uc39fW\xb4\ube42\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u05f7@\xdf\xf8\xc4Wf\x8f\xdft\xf6\xa2f\xbf\xc1\u0737#\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd7\u0080>\u05f0\xe0\x83sQA\x1a\x8ef7\xd1h\xbc[\x05\x8a\x06A\xda\xf5\xc9\x1b\xd95\x80\x00\u07d4\xd7\xc6&]\xea\x11\x87l\x90;q\x8eL\u062b$\xfe&[\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xca\u007f\xdc\xfe\xbeE\x88\xef\xf5B\x1d\x15\"\xb6\x13(\xdf{\xf3\x89\xd8\xe6\x00\x1el0+\x00\x00\u07d4\xd7\u037dA\xff\xf2\r\xf7'\xc7\vbU\xc1\xbav\x06\x05Th\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\xd1W\xe4\xc0\xa9d7\xa6\u0485t\x1d\xd2>\xc46\x1f\xa3k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xd2\xc6\xfc\xa8\xad\x1fu9R\x10\xb5}\xe5\xdf\xd6s\x939\t\x89\x12nr\xa6\x9aP\xd0\x00\x00\xe0\x94\xd7\xd3\xc7Y Y\x048\xb8,>\x95\x15\xbe.\xb6\xedz\x8b\x1a\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\xd7\xd7\xf2\u02a4b\xa4\x1b;0\xa3J\xeb;\xa6\x10\x10\xe2bo\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xe7J\xfd\xba\xd5^\x96\u03bcZ7O,\x8bv\x86\x80\xf2\xb0\x89\x05]\xe6\xa7y\xbb\xac\x00\x00\xe0\x94\xd7\xeb\x901b'\x1c\x1a\xfa5\xfei\xe3s\"\u0224\u049b\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd7\xeb\u0779\xf99\x87w\x9bh\x01U7T8\xdbe\xaf\xcbj\x89\x05t\x1a\xfe\xff\x94L\x00\x00\u07d4\xd7\xef4\x0ef\xb0\u05ef\xcc\xe2\n\x19\xcb{\xfc\x81\xda3\xd9N\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd7\xf3p\u053e\xd9\xd5|oI\u0259\xder\x9e\xe5i\xd3\xf4\xe4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\xfa_\xfb`H\xf9o\xb1\xab\xa0\x9e\xf8{\x1c\x11\xddp\x05\xe4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd8\x06\x9f\x84\xb5!I?G\x15\x03\u007f2&\xb2_3\xb6\x05\x86\x89g\x8a\x93 b\xe4\x18\x00\x00\u0794\xd8\x15\xe1\xd9\xf4\xe2\xb5\xe5~4\x82k|\xfd\x88\x81\xb8Th\x90\x88\xf0\x15\xf2W6B\x00\x00\u07d4\xd8\x1b\xd5K\xa2\xc4Jok\xeb\x15a\u058b\x80\xb5DNm\u0189?\x17\r~\xe4\"\xf8\x9c\x80-1({\x96q\xe8\x1c\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xd8K\x92/xA\xfcWt\xf0\x0e\x14`J\xe0\xdfB\xc8U\x1e\x89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4\xd8U\xb0<\xcb\x02\x9awG\xb1\xf0s\x03\xe0\xa6dy59\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\xd8_\u07af*a\xf9]\xb9\x02\xf9\xb5\xa5<\x9b\x8f\x92f\u00ec\x89l\xf6Z~\x90G(\x00\x00\u07d4\xd8q^\xf9\x17o\x85\v.0\xeb\x8e8'\a\xf7w\xa6\xfb\xe9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd8t\xb9\u07eeEj\x92\x9b\xa3\xb1\xa2~W,\x9b,\xec\u07f3\x89\t79SM(h\x00\x00\u07d4\u0613\n9\xc7sW\xc3\n\u04e0`\xf0\v\x06\x04c1\xfdb\x89,s\xc97t,P\x00\x00\u07d4\u061b\xc2q\xb2{\xa3\xabib\xc9JU\x90\x06\xae8\xd5\xf5j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0637}\xb9\xb8\x1b\xbe\x90B{b\xf7\x02\xb2\x01\xff\u009f\xf6\x18\x892m\x1eC\x96\xd4\\\x00\x00\u07d4\xd8\xcdd\xe0(N\xecS\xaaF9\xaf\xc4u\b\x10\xb9\u007f\xabV\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd8\xd6C\x84$\x9bwg\x94\x06;V\x98x\xd5\xe3\xb50\xa4\xb2\x89\t\xa0C\u0432\xf9V\x80\x00\u07d4\xd8\xd6T \xc1\x8c#'\xccZ\xf9t%\xf8W\xe4\xa9\xfdQ\xb3\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xd8\xe5\xc9g^\xf4\xde\xed&k\x86\x95o\xc4Y\x0e\xa7\u0522}\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd8\xe8GB\x92\xe7\xa0Q`L\xa1d\xc0pw\x83\xbb(\x85\xe8\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\u07d4\xd8\xebxP>\xc3\x1aT\xa9\x016x\x1a\xe1\t\x00Lt2W\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd8\xee\xf4\xcfK\xeb\x01\xee \xd1\x11t\x8ba\xcbM?d\x1a\x01\x89\x94\x89#z\u06daP\x00\x00\u07d4\xd8\xf4\xba\xe6\xf8M\x91\rm}Z\xc9\x14\xb1\xe6\x83r\xf9A5\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd8\xf6 6\xf0;v5\xb8X\xf1\x10?\x8a\x1d\x90\x19\xa8\x92\xb6\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xd8\xf6e\xfd\x8c\xd5\u00bc\xc6\xdd\xc0\xa8\xaeR\x1eM\u01aa``\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xd8\xf9$\fU\xcf\xf05RB\x80\xc0\x9e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd8\xfe\b\x8f\xff\u0394\x8fQ7\xee#\xb0\x1d\x95\x9e\x84\xacB#\x89\f[T\xa9O\xc0\x17\x00\x00\u07d4\xd9\x0f0\t\xdbC~N\x11\u01c0\xbe\u0209os\x8de\xef\r\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd9\x10;\xb6\xb6zU\xa7\xfe\xce-\x1a\xf6-E|!x\x94m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd9\x13\xf0w\x19Iu\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd9D\u0226\x9f\xf2\xca\x12Ii\f\x12)\xc7\x19/6%\x10b\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd9JW\x88*Rs\x9b\xbe*\x06G\xc8\f$\xf5\x8a+O\x1c\x89H\xb5N*\xdb\xe1+\x00\x00\xe0\x94\xd9SB\x95<\x8a!\xe8\xb65\xee\xfa\u01c1\x9b\xea0\xf1pG\x8a\x13\xf0l\u007f\xfe\xf0]@\x00\x00\u07d4\xd9\\\x90\xff\xbeT\x84\x86G\x80\xb8gIJ\x83\u0212V\xd6\xe4\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\xd9g\x11T\x0e.\x99\x83C\xd4\xf5\x90\xb6\xfc\x8f\xac;\xb8\xb3\x1d\x89_Z@h\xb7\x1c\xb0\x00\x00\u07d4\xd9j\xc2Pt\t\u01e3\x83\xab.\xee\x18\"\xa5\xd78\xb3kV\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd9m\xb3;{Z\x95\f>\xfa-\xc3\x1b\x10\xba\x10\xa52\uf1c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd9wYe\xb7\x16Gfu\xa8\xd5\x13\xeb\x14\xbb\xf7\xb0|\xd1J\x8a\x01\x13.m-#\xc5\xe4\x00\x00\u07d4\xd9{\xc8J\xbdG\xc0[\xbfE{.\xf6Y\xd6\x1c\xa5\xe5\u43c9\x06\x9d\x17\x11\x9d\u0168\x00\x00\u07d4\xd9\u007fE&\u07a9\xb1c\xf8\xe8\xe3:k\u03d2\xfb\x90}\xe6\xec\x89\x0feJ\xafM\xb2\xf0\x00\x00\u07d4\xd9\u007f\xe6\xf5?*X\xf6\xd7mu*\xdft\xa8\xa2\xc1\x8e\x90t\x89\x10\xcd\xf9\xb6\x9aCW\x00\x00\u07d4\u0659\x99\xa2I\r\x94\x94\xa50\xca\xe4\xda\xf3\x85T\xf4\xddc>\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\u065d\xf7B\x1b\x93\x82\xe4,\x89\xb0\x06\xc7\xf0\x87p*\aW\xc0\x89\x1a\x05V\x90\xd9\u06c0\x00\x00\xe0\x94\u0677\x83\xd3\x1d2\xad\xc5\x0f\xa3\xea\u02a1]\x92\xb5h\xea\xebG\x8a\a3\xaf\x907L\x1b(\x00\x00\u07d4\xd9\xd3p\xfe\xc65v\xab\x15\xb3\x18\xbf\x9eX6M\u00a3U*\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xd9\xd4/\xd1>\xbdK\xf6\x9c\xac^\x9c~\x82H:\xb4m\xd7\xe9\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xd9\xe2~\xb0}\xfcq\xa7\x06\x06\f\u007f\a\x928\u0293\xe8\x859\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd9\xe3\x85~\xfd\x1e *D\x17p\xa7w\xa4\x9d\xccE\xe2\xe0\u04c9\f\x1d\xaf\x81\u0623\xce\x00\x00\u07d4\xd9\xec.\xfe\x99\xff\\\xf0\r\x03\xa81{\x92\xa2J\xefD\x1f~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd9\xec\x8f\xe6\x9bw\x16\xc0\x86Z\xf8\x88\xa1\x1b+\x12\xf7 \xed3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd9\xf1\xb2d\b\xf0\xecg\xad\x1d\ro\xe2.\x85\x15\xe1t\x06$\x89\x01M\x11 \u05f1`\x00\x00\u07d4\xd9\xf5G\xf2\xc1\xde\x0e\u064aS\xd1a\xdfWc]\xd2\x1a\x00\xbd\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xd9\xff\x11]\x01&l\x9fs\xb0c\xc1\xc28\xef5e\xe6;6\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\xda\x06\x04N)#&\xffil\x0091h\xceF\xff\xac9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda*\x14\xf9r@\x15\u05d0\x14\xed\x8eY\th\x1dYaH\xf1\x89\x02\xa1\x0f\x0f\x8a\x91\xab\x80\x00\u07d4\xda*\u054ew\xde\xdd\xed\xe2\x18vF\xc4e\x94Z\x8d\xc3\xf6A\x89#\xc7W\a+\x8d\xd0\x00\x00\u07d4\xda0\x17\xc1P\xdd\r\xce\u007f\u03c8\x1b\nH\xd0\xd1\xc7V\xc4\u01c9\x05k\xf9\x1b\x1ae\xeb\x00\x00\u07d4\xda4\xb2\xea\xe3\v\xaf\xe8\xda\xec\xcd\xe8\x19\xa7\x94\u0349\xe0\x95I\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdaJ_U\u007f;\xab9\n\x92\xf4\x9b\x9b\x90\n\xf3\fF\xae\x80\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdaPU7S\u007f\xfb3\xc4\x15\xfe\xc6Ni\xba\xe0\x90\xc5\xf6\x0f\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xdai\x8dd\xc6\\\u007f+,rS\x05\x9c\xd3\u0441\u0619\xb6\xb7\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\u07d4\xdaw2\xf0/.'.\xaf(\u07d7.\xcc\r\xde\xed\x9c\xf4\x98\x89\v \xbf\xbfig\x89\x00\x00\u07d4\xdaz\xd0%\xeb\xde%\xd2\"C\u02c3\x0e\xa1\xd3\xf6JVc#\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\u0685]SG\u007fP^\xc4\xc8\xd5\u8ed1\x80\u04c6\x81\x11\x9c\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\u0687^N/<\xab\xe4\xf3~\x0e\xae\xd7\xd1\xf6\xdc\xc6\xff\xefC\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u068b\xbe\xe1\x82\xe4U\xd2\t\x8a\xcb3\x8amE\xb4\xb1~\u0636\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0698.\x96C\xff\xec\xe7#\aZ@\xfewnZ\xce\x04\xb2\x9b\x89\b\xb8\xb6\u0259\x9b\xf2\x00\x00\u07d4\u069fUF\tF\u05ff\xb5p\xdd\xecu|\xa5w;XB\x9a\x89\x1b\x84]v\x9e\xb4H\x00\x00\u07d4\u06a1\xbdz\x91H\xfb\x86\\\xd6\x12\xdd5\xf1b\x86\x1d\x0f;\u0709\xa68\xabr\xd9,\x13\x80\x00\xe0\x94\u06a6<\xbd\xa4]\u0507\xa3\xf1\xcdJtj\x01\xbb^\x06\v\x90\x8a\x01\x04\x16\u0670*\x89$\x00\x00\u07d4\u06a7v\xa6uDi\u05f9&z\x89\xb8g%\xe7@\xda\x0f\xa0\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u06ac\x91\xc1\xe8Y\xd5\xe5~\xd3\bKP \x0f\x97f\xe2\xc5+\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u06ac\xda\xf4\"&\xd1\\\xb1\u03d8\xfa\x15\x04\x8c\u007fL\xee\xfei\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\u06b6\xbc\u06c3\xcf$\xa0\xae\x1c\xb2\x1b;[\x83\xc2\xf3\x82I'\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\u06bb\b\x89\xfc\x04)&\xb0^\xf5{% \x91\n\xbcKAI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06bc\"PB\xa6Y,\xfa\x13\xeb\xe5N\xfaA\x04\bx\xa5\xa2\x89\x0e\x11\xfa\xd5\xd8\\\xa3\x00\x00\u07d4\xda\xc0\xc1w\xf1\x1c\\>>x\xf2\xef\xd6c\xd12!H\x85t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\xd16\xb8\x81x\xb4\x83zlx\x0f\xeb\xa2&\xb9\x85i\xa9L\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xda\xdb\xfa\xfd\x8bb\xb9*$\xef\xd7RV\u0743\xab\xdb\u05fb\u06c9\x01\x11du\x9f\xfb2\x00\x00\u07d4\xda\xdc\x00\xaby'`\xaa4\x15i\xfa\x9f\xf5\x98&\x84\x85JJ2\x8an\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\xe7 \x1e\xab\x8c\x063\x02\x93\ri9)\xd0\u007f\x95\xe7\x19b\x89\x91\xae\xc0(\xb4\x19\x81\x00\x00\u07d4\xda\xed\u052d\x10{'\x1e\x89Hl\xbf\x80\xeb\xd6!\u0757Ex\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x04\xfa\xd9\u011f\x9e\x88\v\xeb\x8f\xcf\x1d:8\x90\u4cc4o\x89CZ\xe6\xcc\fX\xe5\x00\x00\u07d4\xdb\f\u01cft\u0642{\u070ads'n\xb8O\u0717b\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x12\x93\xa5\x06\xe9\f\xad*Y\xe1\xb8V\x1f^f\x96\x1ag\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x19\xa3\x98\"06\x8f\x01w!\x9c\xb1\f\xb2Y\u0372%|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb#\xa6\xfe\xf1\xaf{X\x1ew,\xf9\x18\x82\u07b2Qo\xc0\xa7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb$O\x97\xd9\xc4K\x15\x8a@\xed\x96\x06\xd9\xf7\xbd8\x9131\x89\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xdb(\x8f\x80\xff\xe22\u00baG\u0314\xc7c\xcfo\u0278+\r\x89\x04\x9b\x9c\xa9\xa6\x944\x00\x00\u07d4\xdb*\f\x9a\xb6M\xf5\x8d\u07f1\u06ec\xf8\xba\r\x89\xc8[1\xb4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdb4t^\u0785v\xb4\x99\xdb\x01\xbe\xb7\xc1\xec\u0685\xcfJ\xbe\x89\x04V9\x18$O@\x00\x00\u07d4\xdb?%\x8a\xb2\xa3\xc2\xcf3\x9cD\x99\xf7ZK\xd1\xd3G.\x9e\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xdbK\xc8;\x0ek\xaa\xdb\x11V\xc5\xcf\x06\xe0\xf7!\x80\x8cR\u01c9/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xdbc\x12-\xe7\x03}\xa4\x97\x151\xfa\u9bc5\x86x\x86\u0192\x89\x0f\x04%\xb0d\x1f4\x00\x00\u07d4\xdbl*s\xda\xc7BJ\xb0\xd01\xb6ga\x12%f\xc0\x10C\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xdbnV\f\x9b\xc6 \u053e\xa3\xa9MG\xf7\x88\v\xf4\u007f-_\x89\x04\xda\x0f\xdf\xcf\x05v\x00\x00\u07d4\xdbo\xf7\x1b=\xb0\x92\x8f\x83\x9e\x05\xa72;\xfbW\u049c\x87\xaa\x891T\xc9r\x9d\x05x\x00\x00\u07d4\xdbsF\vY\xd8\xe8PE\xd5\xe7R\xe6%Y\x87^BP.\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\xdbw\xb8\x8d\xcbq/\xd1~\xe9\x1a[\x94t\x8dr\f\x90\xa9\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb}@7\b\x1fle\xf9Gk\x06\x87\xd9\u007f\x1e\x04M\n\x1d\x89#\xc7W\a+\x8d\xd0\x00\x00\xe0\x94\u06c8.\xac\xed\xd0\xef\xf2cQ\x1b1*\u06fcY\u01b8\xb2[\x8a\x01\xedO\xdez\"6\xb0\x00\x00\u07d4\u06d3q\xb3\fL\x84NY\xe0>\x92K\xe6\x06\xa98\xd1\xd3\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06e4ym\f\xebM:\x83k\x84\xc9o\x91\n\xfc\x10?[\xa0\x89\t\b\xf4\x93\xf77A\x00\x00\u07d4\u06ed\xc6\x1e\xd5\xf0F\n\u007f\x18\xe5\x1b/\xb2aM\x92d\xa0\xe0\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u06f6\xacH@'\x04\x16B\xbb\xfd\x8d\x80\xf9\xd0\xc1\xcf3\xc1\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06fc\xbby\xbfG\x9aB\xadq\xdb\u02b7{Z\u07ea\x87,X\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xdb\xc1\xce\x0eI\xb1\xa7\x05\xd2. 7\xae\xc8x\xee\ru\xc7\x03\x89\r\x8drkqw\xa8\x00\x00\u07d4\xdb\xc1\xd0\xee+\xabS\x11@\xde\x13w\"\xcd6\xbd\xb4\xe4q\x94\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb\u015e\u0609s\u07ad1\b\x84\":\xf4\x97c\xc0P0\xf1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\xdb\xc6ie\xe4&\xff\x1a\xc8z\xd6\xebx\xc1\xd9Rq\x15\x8f\x9f\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xdb\xcb\xcdzW\ua7724\x9b\x87\x8a\xf3K\x1a\xd6B\xa7\xf1\u0449\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb\xd5\x1c\xdf,;\xfa\xcd\xff\x10b!\xde.\x19\xadmB\x04\x14\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xdb\xd7\x1e\xfaK\x93\u0209\xe7e\x93\xde`\x9c;\x04\u02ef\xbe\b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xdb\xf5\xf0a\xa0\xf4\x8e^ia\x879\xa7}.\xc1\x97h\xd2\x01\x89\b=lz\xabc`\x00\x00\u07d4\xdb\xf8\xb19g\xf5Q%'-\xe0V%6\xc4P\xbaVU\xa0\x89n\xf5x\xf0n\f\xcb\x00\x00\u07d4\xdb\xfb\x1b\xb4d\xb8\xa5\x8eP\r.\xd8\u0797,E\xf5\xf1\xc0\xfb\x89V\xbcu\xe2\xd61\x00\x00\x00\xe0\x94\xdc\x06~\xd3\xe1-q\x1e\xd4u\xf5\x15n\xf7\xe7\x1a\x80\xd94\xb9\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xdc\b\u007f\x93\x90\xfb\x9e\x97j\xc2:\xb6\x89TJ\tB\xec !\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xdc\x1e\xb9\xb6\xe6CQ\xf5d$P\x96E\xf8>y\xee\xe7l\xf4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdc\x1f\x19ya_\b!@\xb8\xbbx\xc6{'\xa1\x94'\x13\xb1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xdc#\xb2`\xfc\xc2n}\x10\xf4\xbd\x04J\xf7\x94W\x94`\xd9\u0689\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xdc)\x11\x97E\xd23s \xdaQ\xe1\x91\x00\xc9H\u0640\xb9\x15\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xdc-\x15\xa6\x9fk\xb3;$j\xef@E\aQ\xc2\xf6uj\u0489l4\x10\x80\xbd\x1f\xb0\x00\x00\u07d4\xdc=\xaeY\xed\x0f\xe1\x8bXQ\x1eo\xe2\xfbi\xb2\x19h\x94#\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xdc?\x0evr\xf7\x1f\xe7R[\xa3\v\x97U\x18: \xb9\x16j\x8a\x02\b\x9c\xf5{[>\x96\x80\x00\xe0\x94\xdcCE\u0581.\x87\n\xe9\fV\x8cg\xd2\xc5g\u03f4\xf0<\x8a\x01k5-\xa5\xe0\xed0\x00\x00\u07d4\xdcD'[\x17\x15\xba\xea\x1b\x03EsZ)\xacB\xc9\xf5\x1bO\x89?\x19\xbe\xb8\xdd\x1a\xb0\x00\x00\u07d4\xdcF\xc13%\u034e\xdf\x020\xd0h\x89d\x86\xf0\a\xbfN\xf1\x89Hz\x9a0E9D\x00\x00\u07d4\xdcQ\xb2\u071d$z\x1d\x0e[\xc3l\xa3\x15oz\xf2\x1f\xf9\xf6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xdcS\x05\xb4\x02\n\x06\xb4\x9de||\xa3L5\xc9\x1c_,V\x8a\x01}\xf6\xc1\r\xbe\xba\x97\x00\x00\u07d4\xdcW4[8\xe0\xf0g\u0263\x1d\x9d\xea\xc5'Z\x10\x94\x93!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdcWG}\xaf\xa4/p\\\u007f\xe4\x0e\xae\x9c\x81un\x02%\xf1\x89\x1b\x1b\x81(\xa7An\x00\x00\u07d4\xdc_Z\xd6c\xa6\xf2c2}d\xca\xc9\xcb\x13=,\x96\x05\x97\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdcp:_7\x94\xc8Ml\xb3TI\x18\xca\xe1J5\u00fdO\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94\xdcs\x8f\xb2\x17\u03ad/iYL\b\x17\r\xe1\xaf\x10\xc4\x19\xe3\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xdcv\xe8[\xa5\v\x9b1\xec\x1e& \xbc\xe6\xe7\xc8\x05\x8c\x0e\xaf\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0703\xb6\xfd\rQ!1 G\a\xea\xf7.\xa0\xc8\u027e\xf9v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u070c)\x12\xf0\x84\xa6\u0444\xaasc\x85\x13\u033c2n\x01\x02\x89F3\xbc6\xcb\xc2\xdc\x00\x00\u07d4\u0711\x1c\xf7\xdc]\u04016Vg\x05(\xe93\x8eg\x03G\x86\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0730;\xfal\x111#NV\xb7\xea|Or\x14\x87Tkz\x89Hz\x9a0E9D\x00\x00\xe0\x94\u0736M\xf47X\xc7\u03d7O\xa6`HO\xbbq\x8f\x8cg\xc1\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xdc\xc5-\x8f\x8d\x9f\xc7B\xa8\xb8'g\xf0US\x87\xc5c\xef\xff\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xdc\xcb7\x0e\u058a\xa9\"(0C\xef|\xad\x1b\x9d@?\xc3J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdc\u0324 E\xec>\x16P\x8b`?\xd96\xe7\xfd}\xe5\xf3j\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xdc\xd1\fU\xbb\x85OuD4\xf1!\x9c,\x9a\x98\xac\xe7\x9f\x03\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xdc\u057c\xa2\x00S\x95\xb6u\xfd\xe5\x03VY\xb2k\xfe\xfcI\xee\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xdc\u06fdN&\x04\xe4\x0e\x17\x10\xccg0(\x9d\xcc\xfa\u04c9-\x89\xf9]\xd2\xec'\xcc\xe0\x00\x00\u07d4\xdc\xe3\f1\xf3\xcafr\x1e\xcb!<\x80\x9a\xabV\x1d\x9bR\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdc\xf39eS\x13\x80\x161h\xfc\x11\xf6~\x89\xc6\xf1\xbc\x17\x8a\x89\x12'v\x854\x06\xb0\x80\x00\u07d4\xdc\xf6\xb6W&n\x91\xa4\xda\xe6\x03=\xda\xc1S2\u074d+4\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xdc\xf9q\x9b\xe8|oFum\xb4\x89\x1d\xb9\xb6\x11\xd2F\x9cP\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xdc\xff\xf3\xe8\xd2<*4\xb5k\u0473\xbdE\u01d3tC\"9\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xdd\x04\xee\xe7N\v\xf3\f?\x8dl,\u007fR\xe0Q\x92\x10\u07d3\x89\x04V9\x18$O@\x00\x00\xe0\x94\xdd&\xb4)\xfdC\xd8N\xc1y\x82S$\xba\u057f\xb9\x16\xb3`\x8a\x01\x16\xbf\x95\xbc\x842\x98\x00\x00\u07d4\xdd*#:\xde\xdef\xfe\x11&\xd6\xc1h#\xb6*\x02\x1f\xed\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xdd+\u07e9\x17\xc1\xf3\x10\xe6\xfa5\xaa\x8a\xf1i9\xc23\xcd}\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xdd5\xcf\xdb\u02d93\x95Sz\xec\xc9\xf5\x90\x85\xa8\xd5\u0776\xf5\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xddG\x18\x9a>d9qg\xf0b\x0eHEe\xb7b\xbf\xbb\xf4\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xddM\xd6\xd3`3\xb0co\u030d\t8`\x9fM\xd6OJ\x86\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xddO_\xa2\x11\x1d\xb6\x8fk\xde5\x89\xb60)9[i\xa9-\x89\b\x96=\xd8\xc2\xc5\xe0\x00\x00\xe0\x94\xddc\x04/%\xed2\x88J\xd2n:\xd9Y\xeb\x94\xea6\xbfg\x8a\x04\x84\xd7\xfd\xe7\u0553\xf0\x00\x00\u07d4\xdde\xf6\xe1qc\xb5\xd2\x03d\x1fQ\xcc{$\xb0\x0f\x02\xc8\xfb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xddl\x06!\x93\xea\xc2=/\xdb\xf9\x97\xd5\x06:4k\xb3\xb4p\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdd{\u0366Y$\xaa\xa4\x9b\x80\x98J\xe1su\x02X\xb9(G\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdd\u007f\xf4A\xbao\xfe6q\xf3\xc0\u06bb\xff\x18#\xa5\x043p\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0742T\x12\x1an\x94/\xc9\b(\xf2C\x1fQ\x1d\xad\u007f2\u6263\x9b)\xe1\xf3`\xe8\x00\x00\xe0\x94\u074a\xf9\xe7vR#\xf4DoD\xd3\xd5\t\x81\x9a==\xb4\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\u0755\xdb\xe3\x0f\x1f\x18w\xc5\xddv\x84\xae\xef0*\xb6\x88Q\x92\x8a\x01\xc5\xd8\xd6\xeb>2P\x00\x00\xe0\x94\u0756|L_\x8a\xe4~&o\xb4\x16\xaa\u0456N\xe3\xe7\xe8\u00ca\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\u075bHZ;\x1c\xd3:j\x9cb\xf1\xe5\xbe\xe9'\x01\x85m%\x89\f3\x83\xed\x03\x1b~\x80\x00\xe0\x94\u0763q\xe6\x00\xd3\x06\x88\xd4q\x0e\b\x8e\x02\xfd\xf2\xb9RM_\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4\u0764\xed*X\xa8\xdd \xa72u4{X\rq\xb9[\xf9\x9a\x89\x15\xa1<\xc2\x01\xe4\xdc\x00\x00\xe0\x94\u0764\xff}\xe4\x91\u0187\xdfEt\xdd\x1b\x17\xff\x8f$k\xa3\u044a\x04&\x84\xa4\x1a\xbf\xd8@\x00\x00\u07d4\u076bkQ\xa9\x03\v@\xfb\x95\xcf\vt\x8a\x05\x9c$\x17\xbe\u01c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u076bu\xfb/\xf9\xfe\u02c8\xf8\x94vh\x8e+\x00\xe3g\xeb\xf9\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\xe0\x94\u076b\xf1<<\x8e\xa4\xe3\xd7=x\xecqz\xfa\xfaC\x0eTy\x8a\b\xcf#\xf9\t\xc0\xfa\x00\x00\x00\u07d4\u076c1*\x96UBj\x9c\f\x9e\xfa?\xd8%Y\xefE\x05\xbf\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\u076ck\xf4\xbb\xdd}Y}\x9chm\x06\x95Y;\xed\xcc\xc7\xfa\x89.\xe4IU\b\x98\xe4\x00\x00\xe0\x94\u077d+\x93,v;\xa5\xb1\xb7\xae;6.\xac>\x8d@\x12\x1a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u077d\xdd\x1b\xbd8\xff\xad\xe00]0\xf0 (\xd9.\x9f:\xa8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u077e\xe6\xf0\x94\xea\xe64 \xb0\x03\xfbGW\x14*\xeal\xd0\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdd\u059c[\x9b\xf5\xebZ9\xce\xe7\xc34\x1a\x12\r\x97?\xdb4\x89k\xc1K\x8f\x8e\x1b5\x00\x00\xe0\x94\xdd\xdd{\x9en\xab@\x9b\x92&:\xc2r\u0680\x1bfO\x8aW\x8ai\xe1\r\xe7fv\u0400\x00\x00\u07d4\xdd\xe6p\xd0\x169fuv\xa2-\xd0]2F\xd6\x1f\x06\xe0\x83\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94\xdd\xe7zG@\xba\b\xe7\xf7?\xbe:\x16t\x91)1t.\xeb\x8a\x044\xfeMC\x82\xf1\u0500\x00\u07d4\xdd\xe8\xf0\xc3\x1bt\x15Q\x1d\xce\xd1\xcd}F2>K\xd1\"2\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\xdd\xe9i\xae\xf3N\xa8z\u0099\xb7Y~)+J\x01U\u030a\x89\x102\xf2YJ\x01s\x80\x00\u07d4\xdd\xf0\xcc\xe1\xfe\x99m\x91v5\xf0\a\x12\xf4\x05 \x91\xdf\xf9\xea\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdd\xf3\xadv58\x10\xbej\x89\xd71\xb7\x87\xf6\xf1q\x88a+\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xdd\xf5\x81\n\x0e\xb2\xfb.22;\xb2\u0255\t\xab2\x0f$\xac\x8a\x03\xca\\f\u067cD0\x00\x00\xe0\x94\xdd\xf9\\\x1e\x99\xce/\x9fV\x98\x05|\x19\xd5\xc9@'\xeeJn\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\xdd\xfa\xfd\xbc|\x90\xf12\x0eT\xb9\x8f7F\x17\xfb\xd0\x1d\x10\x9f\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xdd\xfc\xca\x13\xf94\xf0\u03fe#\x1d\xa109\xd7\x04u\xe6\xa1\u040968\"\x16`\xa5\xaa\x80\x00\u07d4\xde\x02~\xfb\xb3\x85\x03\"n\xd8q\t\x9c\xb3\v\xdb\x02\xaf\x135\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xde\x06\xd5\xeawzN\xb1G^`]\xbc\xbfCDN\x807\xea\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xde\a\xfb[zFN;\xa7\xfb\xe0\x9e\x9a\xcb'\x1a\xf53\x8cX\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xde\x11!\x82\x9c\x9a\b(@\x87\xa4?\xbd/\xc1\x14*23\xb4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xde\x17kR\x84\xbc\xee:\x83\x8b\xa2Og\xfc|\xbfg\u05ce\xf6\x89\x02\t\xce\b\xc9b\xb0\x00\x00\u07d4\xde!\"\x93\xf8\xf1\xd21\xfa\x10\xe6\tG\rQ,\xb8\xff\xc5\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde0\xe4\x9eZ\xb3\x13!M/\x01\u072b\u0389@\xb8\x1b\x1cv\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xde3\xd7\b\xa3\xb8\x9e\x90\x9e\xafe;0\xfd\u00e5\xd5\u0334\xb3\x89\t\x9c\x88\"\x9f\xd4\xc2\x00\x00\u07d4\xde7B\x99\xc1\xd0}ySs\x85\x19\x0fD.\xf9\xca$\x06\x1f\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xdeB\xfc\xd2L\xe4#\x93\x830CgY_\x06\x8f\fa\a@\x89\x02r*p\xf1\xa9\xa0\x00\x00\u07d4\xdeP\x86\x8e\xb7\xe3\xc7\x197\xecs\xfa\x89\u074b\x9e\xe1\rE\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xdeU\xde\x04X\xf8P\xb3~Mx\xa6A\xdd.\xb2\u074f8\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xde[\x00_\xe8\u06ae\x8d\x1f\x05\xde>\xda\x04 f\xc6\xc4i\x1c\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\xdea-\a$\xe8N\xa4\xa7\xfe\xaa=!B\xbd^\xe8-2\x01\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdem61\x06\xccb8\xd2\xf0\x92\xf0\xf07!6\xd1\xcdP\u018a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xde}\xee\"\x0f\x04W\xa7\x18}V\xc1\xc4\x1f.\xb0\n\xc5`!\x89\"%\xf3\x9c\x85\x05*\x00\x00\u07d4\u0782\u030dJ\x1b\xb1\xd9CC\x92\x96[>\x80\xba\xd3\xc0=O\x89P\x18nu\u0797\xa6\x00\x00\u07d4\u0797\xf43\a\x00\xb4\x8cImC|\x91\xca\x1d\xe9\u0130\x1b\xa4\x89\x9d\xcc\x05\x15\xb5n\f\x00\x00\u07d4\u079e\xffLy\x88\x11\xd9h\xdc\xcbF\r\x9b\x06\x9c\xf3\x02x\xe0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u07b1\xbc4\xd8mJM\xde%\x80\u063e\xaf\aN\xb0\xe1\xa2D\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\u07b2I]j\xca{*j-\x13\x8bn\x1aB\xe2\xdc1\x1f\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\u07b9rTGL\r/Zyp\xdc\xdb/R\xfb\x10\x98\xb8\x96\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u07b9\xa4\x9aC\x870 \xf0u\x91\x85\xe2\v\xbbL\U000c1ecf\x89\vx\xed\xb0\xbf.^\x00\x00\u07d4\u07bb\u0743\x1e\x0f \xaen7\x82R\xde\xcd\xf9/|\xf0\xc6X\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde\xc3\xee\xc2d\nu,Fn+~~\u616f\xe9\xacA\xf4\x89G\u0257SYk(\x80\x00\u07d4\xde\xc8#s\xad\xe8\xeb\xcf*\xcbo\x8b\xc2AM\u05eb\xb7\rw\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xde\u0221\xa8\x98\xf1\xb8\x95\xd80\x1f\xe6J\xb3\xad]\xe9A\xf6\x89\x89*\xb4\xf6~\x8as\x0f\x80\x00\u07d4\xde\u025e\x97/\xcaqwP\x8c\x8e\x1aG\xac\"\xd7h\xac\xab|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde\xd8w7\x84\a\xb9Nx\x1cN\xf4\xaf|\xfc[\xc2 \xb5\x16\x89\x141y\xd8i\x11\x02\x00\x00\u07d4\xde\xe9B\xd5\xca\xf5\xfa\xc1\x14!\xd8k\x01\vE\x8e\\9)\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xde\xee&\x89\xfa\x90\x06\xb5\x9c\xf2\x85#}\xe5;:\u007f\xd0\x148\x89\x18ey\xf2\x9e %\x00\x00\u07d4\xde\xfd\xdf\u055b\x8d,\x15N\xec\xf5\xc7\xc1g\xbf\v\xa2\x90]>\x89\x05\x12\xcb^&GB\x00\x00\u07d4\xde\xfe\x91A\xf4pE\x99\x15\x9d{\"=\xe4+\xff\xd8\x04\x96\xb3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xdf\t\x8f^N=\xff\xa5\x1a\xf27\xbd\xa8e,Os\ud726\x89\x1b6\xa6DJ>\x18\x00\x00\xe0\x94\xdf\r\ba{\xd2R\xa9\x11\u07cb\xd4\x1a9\xb8=\u07c0\x96s\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdf\x0f\xf1\xf3\xd2z\x8e\xc9\xfb\x8fk\f\xb2T\xa6;\xba\x82$\xa5\x89\xec\xc5 )E\xd0\x02\x00\x00\u07d4\xdf\x1f\xa2\xe2\x0e1\x98^\xbe,\x0f\f\x93\xb5L\x0f\xb6z&K\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdf!\x1c\xd2\x12\x88\xd6\xc5o\xaef\xc3\xffTb]\u0531T'\x89\x87\x86\xcdvN\x1f,\x00\x00\u07d4\xdf#k\xf6\xab\xf4\xf3)7\x95\xbf\f(q\x8f\x93\u3c73k\x89Hz\x9a0E9D\x00\x00\u07d4\xdf1\x02_VI\xd2\xc6\xee\xa4\x1e\u04fd\xd3G\x1ay\x0fu\x9a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdf7\xc2.`:\xed\xb6\nbrS\xc4}\x8b\xa8f\xf6\xd9r\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4\xdf;r\u017dq\u0501N\x88\xa6#!\xa9=@\x11\xe3W\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdf?W\xb8\xeed4\xd0G\"=\xeft\xb2\x0fc\xf9\xe4\xf9U\x89\r\x94b\xc6\xcbKZ\x00\x00\u07d4\xdfD\xc4\u007f\xc3\x03\xacv\xe7O\x97\x19L\xcag\xb5\xbb<\x02?\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\xdfG\xa6\x1brSQ\x93\xc5a\xcc\xccu\xc3\xf3\xce\b\x04\xa2\x0e\x89\x15\x93\\\vN=x\x00\x00\u07d4\xdfG\xa8\xef\x95\xf2\xf4\x9f\x8eoX\x18AT\x14]\x11\xf7'\x97\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xdfS\x003F\xd6\\^zdk\xc04\xf2\xb7\xd3/\xcb\xe5j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdfW5:\xaf\xf2\xaa\xdb\n\x04\xf9\x01N\x8d\xa7\x88N\x86X\x9c\x89\bH\x86\xa6nO\xb0\x00\x00\u07d4\xdf`\xf1\x8c\x81*\x11\xedN'v\xe7\xa8\x0e\xcf^S\x05\xb3\u05890\xca\x02O\x98{\x90\x00\x00\u07d4\xdfd\x85\xc4)z\xc1R\xb2\x89\xb1\x9d\xde2\xc7~\xc4\x17\xf4}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xdff\n\x91\u06b9\xf70\xf6\x19\rP\xc89\x05aP\aV\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\xdfn\xd6\x00jj\xbe\x88n\xd3=\x95\xa4\xde(\xfc\x12\x189'\x891T\xc9r\x9d\x05x\x00\x00\u07d4\u07c5\x10y>\xee\x81\x1c-\xab\x1c\x93\xc6\xf4G?0\xfb\xef[\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u07cdH\xb1\xeb\a\xb3\xc2\x17y\x0el-\xf0M\xc3\x19\xe7\xe8H\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u07e6\xb8\xb8\xad1\x84\xe3W\xda()Q\u05d1a\u03f0\x89\xbc\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\u07ef1\xe6\"\xc0=\x9e\x18\xa0\u0778\xbe`\xfb\xe3\xe6a\xbe\n\x8a\x02\x1e\x17\x1a>\xc9\xf7,\x00\x00\u07d4\u07f1bn\xf4\x8a\x1d}uR\xa5\xe0)\x8f\x1f\xc2:;H-\x89\\\xe8\x95\u0754\x9e\xfa\x00\x00\xe0\x94\u07f4\u052d\xe5/\u0301\x8a\xccz,k\xb2\xb0\x02$e\x8fx\x8a\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\u07fdB2\xc1|@z\x98\r\xb8\u007f\xfb\u036060\xe5\xc4Y\x89\x1d\xfc\u007f\x92I#S\x00\x00\u07d4\xdf\xcb\xdf\tEN\x1a^J@\xd3\xee\xf7\xc5\xcf\x1c\xd3\u0794\x86\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdf\xdb\xce\xc1\x01K\x96\xda!X\xcaQ>\x9c\x8d;\x9a\xf1\xc3\u0409lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdf\xde\xd2WK'\xd1a:}\x98\xb7\x15\x15\x9b\r\x00\xba\xab(\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xdf\xdfC9P\x8b\x0fnZ\xb1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe0\x06\x04b\xc4\u007f\xf9g\x9b\xae\xf0qY\xca\xe0\x8c)\xf2t\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\r\x15;\x106\x91C\xf9\u007fT\xb8\xd4\xca\"\x9e\xb3\xe8\xf3$\x89\b=lz\xabc`\x00\x00\u07d4\xe0\x12\xdbE8'\xa5\x8e\x16\xc16V\b\xd3n\xd6Xr\x05\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x15G\xbaB\xfc\xaf\xaf\x93\x93\x8b\xec\xf7i\x9ft)\n\xf7O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x16\xdc\x13\x8e%\x81[\x90\xbe?\xe9\xee\xe8\xff\xb2\xe1\x05bO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0\x18Y\xf2B\xf1\xa0\xec`/\xa8\xa3\xb0\xb5v@\xec\x89\a^\x89\x1e\x16,\x17{\xe5\xcc\x00\x00\xe0\x94\xe0 \xe8cb\xb4\x87u(6\xa6\xde\v\xc0,\xd8\u061a\x8bj\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xe0#\xf0\x9b(\x87a,|\x9c\xf1\x98\x8e::`+3\x94\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0'\"\x13\xe8\xd2\xfd>\x96\xbdb\x17\xb2KK\xa0\x1bapy\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe0+t\xa4v(\xbe1[\x1fv\xb3\x15\x05J\xd4J\xe9qo\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xe02 \u0197\xbc\u048f&\xef\vt@J\x8b\xeb\x06\xb2\xba{\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe05/\u07c1\x9b\xa2e\xf1L\x06\xa61\\J\xc1\xfe\x13\x1b.\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe08\x8a\xed\xdd?\xe2\xadV\xf8WH\xe8\x0eq\n4\xb7\xc9.\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0<\x00\xd0\x03\x88\xec\xbfO&=\n\xc7x\xbbA\xa5z@\u064966\xc9yd6t\x00\x00\u07d4\xe0I \xdcn\xcc\x1dn\xcc\bO\x88\xaa\n\xf5\u06d7\xbf\x89:\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xe0Ir\xa8<\xa4\x11+\xc8q\xc7-J\xe1al/\a(\u06c9\x0e\x81\xc7\u007f)\xa3/\x00\x00\u07d4\xe0O\xf5\xe5\xa7\u2bd9]\x88W\xce\x02\x90\xb5:+\x0e\xda]\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xe0P)\xac\xeb\axg[\xef\x17A\xab,\u0493\x1e\xf7\xc8K\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4\xe0V\xbf?\xf4\x1c&%o\xefQqf\x12\xb9\u04da\u0799\x9c\x89\x05k\xe7W\xa1.\n\x80\x00\u07d4\xe0a\xa4\xf2\xfcw\xb2\x96\u045a\xda#\x8eI\xa5\u02ce\xcb\xfap\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe0f>\x8c\xd6g\x92\xa6A\xf5nP\x03f\x01G\x88\x0f\x01\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0f\x8f\xa8,\x14\xd6\xe8\xd9:S\x11>\xf2\x86/\xa8\x15\x81\xbc\x89//9\xfclT\x00\x00\x00\u07d4\xe0i\xc0\x173R\xb1\v\xf6\x83G\x19\xdb[\xed\x01\xad\xf9{\xbc\x89\x01\x064\xf8\xe52;\x00\x00\u07d4\xe0l)\xa8\x15\x17\xe0\u0507\xb6\u007f\xb0\xb6\xaa\xbcOW6\x83\x88\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xe0l\xb6)G\x04\xee\xa7C|/\xc3\xd3\as\xb7\xbf8\x88\x9a\x89\x01\x16\xdc:\x89\x94\xb3\x00\x00\u07d4\xe0q7\xae\r\x11m\x0353\xc4\uad16\xf8\xa9\xfb\tV\x9c\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\xe0v\xdb0\xabHoy\x19N\xbb\xc4]\x8f\xab\x9a\x92B\xf6T\x8a\x01\x06`~4\x94\xba\xa0\x00\x00\u07d4\xe0~\xbb\xc7\xf4\xdaAnB\xc8\xd4\xf8B\xab\xa1b3\xc1%\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x81\xca\x1fH\x82\xdb`C\u0569\x19\a\x03\xfd\xe0\xab;\xf5m\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\x83\xd3Hc\xe0\xe1\u007f\x92ky(\xed\xff1~\x99\x8e\x9cK\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\x8b\x9a\xbak\xd9\u048b\xc2\x05gy\xd2\xfb\xf0\xf2\x85Z=\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x8b\u009c+H\xb1i\xff+\xdc\x16qLXnl\xb8\\\u03c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe0\x8c`11\x06\xe3\xf93O\xe6\xf7\xe7bM!\x110\xc0w\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xe0\x9ch\xe6\x19\x98\xd9\xc8\x1b\x14\xe4\xee\x80+\xa7\xad\xf6\xd7L\u06c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe0\x9f\xeauZ\xee\x1aD\xc0\xa8\x9f\x03\xb5\u07b7b\xba3\x00o\x89;\xa2\x89\xbc\x94O\xf7\x00\x00\xe0\x94\xe0\xa2T\xac\t\xb9r[\xeb\xc8\xe4`C\x1d\xd0s.\xbc\xab\xbf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xe0\xaai6UU\xb7?(#3\xd1\xe3\f\x1b\xbd\a(T\xe8\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xe0\xba\u064e\ue598\xdb\xf6\xd7`\x85\xb7\x92=\xe5uN\x90m\x89\t\r\x97/22<\x00\x00\u07d4\xe0\u012b\x90r\xb4\xe6\xe3eJI\xf8\xa8\xdb\x02jK3\x86\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\u0380\xa4a\xb6H\xa5\x01\xfd\v\x82F\x90\u0206\x8b\x0eM\xe8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0\xcfi\x8a\x053'\xeb\xd1k}w\x00\t/\xe2\xe8T$F\x89\x05*4\u02f6\x1fW\x80\x00\xe0\x94\xe0\xd21\xe1D\xec\x91\a8l|\x9b\x02\xf1p,\xea\xa4\xf7\x00\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4\xe0\xd7kqf\xb1\xf3\xa1+@\x91\xee+)\u078c\xaa}\a\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\xe0\xb2\xe2\x9d\xdes\xafu\x98~\xe4Dl\x82\x9a\x18\x9c\x95\xbc\x89\b\x13\xcaV\x90m4\x00\x00\xe0\x94\xe0\xe9xu=\x98/\u007f\x9d\x1d#\x8a\x18\xbdH\x89\xae\xfeE\x1b\x8a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4\xe0\xf3r4|\x96\xb5_}C\x06\x03K\xeb\x83&o\xd9\tf\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\xf9\x03\xc1\xe4\x8a\xc4!\xabHR\x8f=J&H\b\x0f\xe0C\x897\b\xba\xed=h\x90\x00\x00\u07d4\xe0\xff\v\xd9\x15D9\u0125\xb7#>)\x1d}\x86\x8a\xf5?3\x89\x15y!jQ\xbb\xfb\x00\x00\xe0\x94\xe1\n\xc1\x9cTo\xc2T|a\xc19\xf5\xd1\xf4Zff\u0570\x8a\x01\x02\xdao\xd0\xf7:<\x00\x00\xe0\x94\xe1\fT\x00\x88\x11?\xa6\xec\x00\xb4\xb2\u0202O\x87\x96\xe9n\u010a2\x0fE\t\xab\x1e\xc7\xc0\x00\x00\xe0\x94\xe1\x17:$})\xd8#\x8d\xf0\x92/M\xf2Z\x05\xf2\xafw\u00ca\bx\xc9]V\x0f0G\x80\x00\xe0\x94\xe1 >\xb3\xa7#\xe9\x9c\" \x11|\xa6\xaf\xebf\xfaBOa\x8a\x02\x00\uf49e2V\xfe\x00\x00\xe0\x94\xe11\xf8~\xfc^\xf0~C\xf0\xf2\xf4\xa7G\xb5Q\xd7P\xd9\xe6\x8a\x04<%\xe0\xdc\xc1\xbd\x1c\x00\x00\u07d4\xe13N\x99\x83y\xdf\xe9\x83\x17pby\x1b\x90\xf8\x0e\xe2-\x8d\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe15@\xec\xee\x11\xb2\x12\xe8\xb7u\u070eq\xf3t\xaa\xe9\xb3\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1;=+\xbf\u073c\x87r\xa23\x15rL\x14%\x16|V\x88\x897\xf3y\x14\x1e\xd0K\x80\x00\u07d4\xe1D=\xbd\x95\xccA#\u007fa:HEi\x88\xa0Oh2\x82\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xe1F\x17\xf6\x02%\x01\xe9~{>-\x886\xaaa\xf0\xff-\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe1I\xb5rl\xafm^\xb5\xbf*\xccA\xd4\xe2\xdc2\x8d\u1089i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xe1T\xda\xea\xdbTX8\xcb\u01aa\fUu\x19\x02\xf5(h*\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4\xe1l\xe3Ya\xcdt\xbdY\r\x04\u012dJ\x19\x89\xe0V\x91\u0189\a\xea(2uw\b\x00\x00\u07d4\xe1r\xdf\xc8\xf8\f\xd1\xf8\u03459\xdc&\b \x14\xf5\xa8\xe3\u8262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xe1w\xe0\xc2\x01\xd35\xba9V\x92\x9cW\x15\x88\xb5\x1cR#\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1x\x12\xf6l^e\x94\x1e\x18lF\x92+n{/\x0e\xebF\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xe1\x80\u079e\x86\xf5{\xaf\xac\u05d0O\x98&\xb6\xb4\xb2c7\xa3\x89-\x04\x1dpZ,`\x00\x00\xe0\x94\xe1\x92H\x9b\x85\xa9\x82\xc1\x882F\xd9\x15\xb2)\xcb\x13 \u007f8\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xe1\x95\xbb\xc6,{tD\x04\x0e\xb9\x96#\x96Ovg\xb3v\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe2\x06\xfbs$\xe9\u07b7\x9e\x19\x904\x96\u0596\x1b\x9b\xe5f\x03\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe2\aW\x8e\x1fM\u06cf\xf6\u0546{9X-q\xb9\x81*\u0149\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xe2\b\x81*h@\x98\xf3\xdaN\xfej\xba%bV\xad\xfe?\xe6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe2\tT\xd0\xf4\x10\x8c\x82\xd4\u0732\x14\x8d&\xbb\xd9$\xf6\xdd$\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe2\v\xb9\xf3\x96d\x19\xe1K\xbb\xaa\xaag\x89\xe9$\x96\u03e4y\x89\xbb\xd8%\x03\aRv\x00\x00\u07d4\xe2\r\x1b\xcbq(m\xc7\x12\x8a\x9f\xc7\xc6\xed\u007fs8\x92\xee\xf5\x896d\xf8\xe7\xc2J\xf4\x00\x00\u0794\xe2\x19\x12\x15\x98?3\xfd3\xe2,\u0522I\x00T\xdaS\xfd\u0708\xdbD\xe0I\xbb,\x00\x00\u07d4\xe2\x19\x8c\x8c\xa1\xb3\x99\xf7R\x15a\xfdS\x84\xa7\x13/\xbaHk\x897\b\xba\xed=h\x90\x00\x00\xe0\x94\xe2\x1cw\x8e\xf2\xa0\xd7\xf7Q\xea\x8c\aM\x1f\x81\"C\x86>N\x8a\x01\x1f\xc7\x0e,\x8c\x8a\xe1\x80\x00\xe0\x94\xe2)\xe7F\xa8?,\xe2S\xb0\xb0>\xb1G$\x11\xb5~W\x00\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4\xe2+ \xc7x\x94F;\xafwL\xc2V\u057d\u06ff}\xdd\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe20\xfe\x1b\xff\x03\x18m\x02\x19\xf1]LH\x1b}Y\xbe(j\x89\x01\xfdt\x1e\x80\x88\x97\x00\x00\u07d4\xe27\xba\xa4\xdb\u0252n2\xa3\xd8]\x12d@-T\xdb\x01/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2A\t\xbe/Q=\x87I\x8e\x92j(d\x99uO\x9e\u051e\x890\x0e\xa8\xad\x1f'\xca\x00\x00\u07d4\xe2Fh<\u025d\xb7\u0125+\u02ec\xaa\xb0\xb3/k\xfc\x93\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe2Z\x16{\x03\x1e\x84am\x0f\x01?1\xbd\xa9]\xcccP\xb9\x8a\x02\x8c*\xaa\u0243\xd0]\u0187st\xa8\xf4F\xee\xe9\x89\n\xb6@9\x12\x010\x00\x00\u07d4\xe2\x8b\x06\"Y\xe9n\xeb<\x8dA\x04\x94?\x9e\xb3%\x89<\xf5\x89Hz\x9a0E9D\x00\x00\xe0\x94\u237c\x8e\xfd^Ajv.\xc0\xe0\x18\x86K\xb9\xaa\x83({\x8a\x051\xf2\x00\xab>\x03\n\x80\x00\u07d4\xe2\x90K\x1a\xef\xa0V9\x8bb4\xcb5\x81\x12\x88\xd76\xdbg\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u274a\xe4R\xdc\xf3\xb6\xacd^c\x04\t8UQ\xfa\xae\n\x89\x04Z\r\xa4\xad\xf5B\x00\x00\u07d4\xe2\xbb\xf8FA\xe3T\x1fl3\xe6\xedh:cZp\xbd\xe2\xec\x89\x1bA<\xfc\xbfY\xb7\x80\x00\u07d4\xe2\xcf6\n\xa22\x9e\xb7\x9d+\xf7\xca\x04\xa2z\x17\xc52\xe4\u0609\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xe2\xdf#\xf6\xea\x04\xbe\xcfJ\xb7\x01t\x8d\xc0\x961\x84U\\\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2\xe1\\`\xdd8\x1e:K\xe2Pq\xab$\x9aL\\Rd\u0689\u007fk\u011b\x81\xb57\x00\x00\u07d4\xe2\xe2nN\x1d\xcf0\xd0H\xccn\u03ddQ\xec\x12\x05\xa4\xe9&\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xe2\xeei\x1f#~\xe6R\x9beW\xf2\xfc\xdd=\xcf\fY\xecc\x8a\x01'r\x9c\x14h| \x00\x00\u07d4\xe2\xef\xa5\xfc\xa7\x958\xce`h\xbf1\xd2\xc5\x16\xd4\xd5<\b\xe5\x89\a\x1c\xc4\b\xdfc@\x00\x00\xe0\x94\xe2\xef\u0429\xbc@~\xce\x03\xd6~\x8e\xc8\xe9\u0483\xf4\x8d*I\x8a\x02\x99\xb3;\xf9\u0144\xe0\x00\x00\u07d4\xe2\xf4\r5\x8f^?\xe7F>\xc7\x04\x80\xbd.\u04d8\xa7\x06;\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe2\xf98=X\x10\xea{C\x18+\x87\x04\xb6+'\xf5\x92]9\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe2\xff\x9e\xe4\xb6\xec\xc1AA\xcct\xcaR\xa9\xe7\xa2\xee\x14\xd9\b\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\xe3\x02\x12\xb2\x01\x1b\xb5k\xdb\xf1\xbc5i\x0f:N\x0f\xd9\x05\xea\x8a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4\xe3\x03\x16\u007f=I`\xfe\x88\x1b2\x80\n+J\xef\xf1\xb0\x88\u0509lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3\x04\xa3/\x05\xa87btJ\x95B\x97o\xf9\xb7#\xfa1\xea\x89Ur\xf2@\xa3F \x00\x00\u07d4\xe3\bCR\x04y7d\xf5\xfc\xbee\xebQ\x0fZtJeZ\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3\t\x97L\xe3\x9d`\xaa\xdf.ig2Q\xbf\x0e\x04v\n\x10\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xe3\x1bN\xef\x18L$\xab\t\x8e6\xc8\x02qK\xd4t=\xd0\u0509\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3!\xbbJ\x94j\xda\xfd\xad\xe4W\x1f\xb1\\\x00C\u04de\xe3_\x89Udu8+L\x9e\x00\x00\u07d4\xe3&<\xe8\xafm\xb3\xe4gXE\x02\xedq\t\x12^\xae\"\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3+\x1cG%\xa1\x87TI\u93d7\x0e\xb3\xe5@b\xd1X\x00\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3/\x95vmW\xb5\xcdK\x172\x89\u0587o\x9edU\x81\x94\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe38@\u063c\xa7\u0698\xa6\xf3\u0416\xd8=\xe7\x8bp\xb7\x1e\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe38\xe8Y\xfe.\x8c\x15UHH\xb7\\\xae\u0368w\xa0\xe82\x89a\xac\xff\x81\xa7\x8a\xd4\x00\x00\u07d4\xe3=\x98\x02 \xfa\xb2Y\xafj\x1fK8\xcf\x0e\xf3\xc6\xe2\xea\x1a\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe3=\xf4\u0380\u0336*v\xb1+\xcd\xfc\xec\xc4b\x89\x97:\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xe3?\xf9\x87T\x1d\xde\\\xde\u0a29m\xcc?3\xc3\xf2L\u008a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xe3A\v\xb7U|\xf9\x1dy\xfai\xd0\xdf\xea\n\xa0u@&Q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3Ad-@\u04af\xce.\x91\a\xc6py\xacz&`\bl\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe3TS\xee\xf2\xcc2\x89\x10CR\x8d\t\x84i\x80\x00\xe0\x94\xe5\x10\xd6y\u007f\xba=f\x93\x83Z\x84N\xa2\xadT\x06\x91\x97\x1b\x8a\x03\xae9\xd4s\x83\xe8t\x00\x00\u07d4\xe5\x14!\xf8\xee\"\x10\xc7\x1e\xd8p\xfea\x82v\u0215J\xfb\xe9\x89Hz\x9a0E9D\x00\x00\u07d4\xe5\x1e\xb8~\u007f\xb71\x1fR(\xc4y\xb4\x8e\u0247\x881\xacL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5!V1\xb1BH\xd4Z%R\x96\xbe\xd1\xfb\xfa\x030\xff5\x89G\x03\xe6\xebR\x91\xb8\x00\x00\xe0\x94\xe5(\xa0\xe5\xa2g\xd6g\xe99:e\x84\xe1\x9b4\u071b\xe9s\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xe54%\xd8\xdf\x1f\x11\xc3A\xffX\xae_\x148\xab\xf1\xcaS\u03c9\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\u07d4\xe5No\x9c\xffV\xe1\x9cF\x1e\xb4T\xf9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5A\x02SM\xe8\xf2>\xff\xb0\x93\xb3\x12B\xad;#?\xac\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe5E\xee\x84\xeaH\xe5d\x16\x1e\x94\x82\u055b\xcf@j`,\xa2\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xe5H\x1a\u007f\xedB\xb9\x01\xbb\xed x\x9b\u052d\xe5\r_\x83\xb9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5Y\xb5\xfd3{\x9cUr\xa9\xbf\x9e\x0f%!\xf7\xd4F\xdb\xe4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe5\\\x80R\n\x1b\x0fu[\x9a,\xd3\xce!Ov%e>\x8a\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe5mC\x13$\xc9)\x11\xa1t\x9d\xf2\x92p\x9c\x14\xb7ze\u034a\x01\xbc\x85\xdc*\x89\xbb \x00\x00\u07d4\xe5})\x95\xb0\xeb\xdf?<\xa6\xc0\x15\xeb\x04&\r\xbb\x98\xb7\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\u51f1j\xbc\x8at\b\x1e6\x13\xe1CB\xc03u\xbf\bG\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\x89\xfav\x98M\xb5\xec@\x04\xb4n\u8954\x92\xc3\aD\u0389\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xe5\x8d\xd228\xeen\xa7\xc2\x13\x8d8]\xf5\x00\xc3%\xf3v\xbe\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xe5\x95?\xeaIq\x04\xef\x9a\xd2\xd4\xe5\x84\x1c'\x1f\a5\x19\u0089&)\xf6n\fS\x00\x00\x00\xe0\x94\u5587\x97F\x8e\xf7g\x10\x1bv\x1dC\x1f\xce\x14\xab\xff\u06f4\x8a\x01\xb3\xd9i\xfaA\x1c\xa0\x00\x00\u07d4\xe5\x97\xf0\x83\xa4i\xc4Y\x1c=+\x1d,w'\x87\xbe\xfe'\xb2\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xe5\x9b;\xd3\x00\x89?\x97#>\xf9G\xc4or\x17\xe3\x92\xf7\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5\xa3e4<\xc4\xeb\x1ew\x03h\xe1\xf1\x14Jw\xb82\xd7\xe0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe5\xa3\xd7\xeb\x13\xb1\\\x10\x01w#m\x1b\xeb0\xd1~\xe1T \x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xaa\v\x83;\xb9\x16\xdc\x19\xa8\xddh?\x0e\xde$\x1d\x98\x8e\xba\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\u5def\x14i\x86\xc0\xff\x8f\x85\xd2.l\xc34\a}\x84\xe8$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xb8&\x19l\x0e\x1b\xc1\x11\x9b\x02\x1c\xf6\xd2Y\xa6\x10\u0256p\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe5\xb9o\u026c\x03\xd4H\xc1a:\xc9\x1d\x15\x97\x81E\xdb\xdf\u0449\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u5e40\u048e\xec\xe2\xc0o\xcal\x94s\x06\x8b7\u0526\xd6\xe9\x89%\xaf\u058c\xac+\x90\x00\x00\u07d4\u5eb4\xf0\xaf\u0629\u0463\x81\xb4Wa\xaa\x18\xf3\xd3\xcc\xe1\x05\x89Q\xbf\xd7\xc18x\xd1\x00\x00\u07d4\xe5\xbc\u020c;%on\xd5\xfeU\x0eJ\x18\x19\x8b\x943V\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xbd\xf3OL\xccH>L\xa50\xcc|\xf2\xbb\x18\xfe\xbe\x92\xb3\x89\x06\xd85\xa1\v\xbc\xd2\x00\x00\u07d4\xe5\u0713I\xcbR\xe1a\x19a\"\u03c7\xa3\x896\xe2\xc5\u007f4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xe38\x00\xa1\xb2\xe9k\xde\x101c\n\x95\x9a\xa0\a\xf2nQ\x89Hz\x9a0E9D\x00\x00\u07d4\xe5\xe3~\x19@\x8f,\xfb\xec\x834\x9d\u0501S\xa4\xa7\x95\xa0\x8f\x89\u3bb5sr@\xa0\x00\x00\u07d4\xe5\xed\xc7>bo]4A\xa4U9\xb5\xf7\xa3\x98\u0153\xed\xf6\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xe5\xed\xf8\x12?$\x03\xce\x1a\x02\x99\xbe\xcfz\xactM\a_#\x89\n\xdaUGK\x814\x00\x00\u07d4\xe5\xf8\xefm\x97\x066\xb0\u072aO \x0f\xfd\xc9\xe7Z\xf1t\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xfb1\xa5\xca\xeej\x96\xde9;\xdb\xf8\x9f\xbee\xfe\x12[\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5\xfb\xe3I\x84\xb67\x19o3\x1cg\x9d\f\fG\xd84\x10\xe1\x89llD\xfeG\xec\x05\x00\x00\u07d4\xe6\tU\xdc\v\xc1V\xf6\xc4\x18I\xf6\xbdwk\xa4K\x0e\xf0\xa1\x89\x10C\x16'\xa0\x93;\x00\x00\u07d4\xe6\nU\xf2\u07d9m\u00ee\xdbil\b\xdd\xe09\xb2d\x1d\xe8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe6\x11[\x13\xf9y_~\x95e\x02\xd5\aEg\u06b9E\xcek\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xe6\x1f(\t\x15\xc7t\xa3\x1d\"<\xf8\f\x06\x92f\xe5\xad\xf1\x9b\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xe6/\x98e\a\x12\xeb\x15\x87S\xd8)r\xb8\u9723\xf6\x18w\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe6/\x9d|d\xe8\xe2cZ\xeb\x88=\xd7;\xa6\x84\xee|\x10y\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe6>xt\x14\xb9\x04\x84x\xa5\a35\x9e\xcd\xd7\xe3dz\xa6\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xe6FfXr\xe4\v\rz\xa2\xff\x82r\x9c\xaa\xba[\xc3\u8789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe6N\xf0\x12e\x8dT\xf8\xe8`\x9cN\x90#\xc0\x9f\xe8e\xc8;\x89\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\xe6On\x1dd\x01\xb5l\akd\xa1\xb0\x86}\v/1\rN\x89\x02\u02edq\xc5:\xe5\x00\x00\u07d4\xe6g\xf6R\xf9W\u008c\x0ef\u04364\x17\xc8\f\x8c\x9d\xb8x\x89 \x9d\x92/RY\xc5\x00\x00\xe0\x94\xe6w\xc3\x1f\xd9\xcbr\x00u\u0724\x9f\x1a\xbc\xcdY\xec3\xf74\x8a\x01\xa6\u05be\xb1\xd4.\xe0\x00\x00\u07d4\xe6|,\x16e\u02038h\x81\x87b\x9fI\xe9\x9b`\xb2\u04fa\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xe6\x9al\xdb:\x8a}\xb8\xe1\xf3\f\x8b\x84\xcds\xba\xe0+\xc0\xf8\x8a\x03\x94\xfd\xc2\xe4R\xf6q\x80\x00\u07d4\xe6\x9d\x1c7\x8bw\x1e\x0f\xef\xf0Q\xdbi\xd9f\xacgy\xf4\xed\x89\x1d\xfaj\xaa\x14\x97\x04\x00\x00\u07d4\xe6\x9f\xcc&\xed\"_{.7\x984\xc5$\xd7\f\x175\u5f09lk\x93[\x8b\xbd@\x00\x00\u07d4\xe6\xa3\x01\x0f\x02\x01\xbc\x94\xffg\xa2\xf6\x99\xdf\xc2\x06\xf9\xe7gB\x89/\xa7\xcb\xf6dd\x98\x00\x00\u07d4\xe6\xa6\xf6\xddop\xa4V\xf4\xec\x15\xefz\xd5\xe5\u06f6\x8b\xd7\u0709\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe6\xb2\x0f\x98\n\xd8S\xad\x04\xcb\xfc\x88|\xe6`\x1ck\xe0\xb2L\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u6cec?]M\xa5\xa8\x85}\v?0\xfcK+i+w\u05c9O%\x91\xf8\x96\xa6P\x00\x00\u07d4\xe6\xb9T_~\u0406\xe5R\x92F9\xf9\xa9\xed\xbb\xd5T\v>\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\xe0\x94\xe6\xbc\xd3\n\x8f\xa18\xc5\xd9\xe5\xf6\xc7\xd2\u0680i\x92\x81-\u034a7\x0e\xa0\xd4|\xf6\x1a\x80\x00\x00\u07d4\xe6\xc8\x1f\xfc\xec\xb4~\xcd\xc5\\\vq\xe4\x85_>^\x97\xfc\x1e\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4\xe6\xcb&\vqmL\n\xb7&\xee\xeb\a\xc8pr\x04\xe2v\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe6\xcb?1$\xc9\xc9\xcc84\xb1'K\xc33dV\xa3\x8b\xac\x89\x17+\x1d\xe0\xa2\x13\xff\x00\x00\xe0\x94\xe6\xd2\"\t\xff\u0438u\t\xad\xe3\xa8\xe2\xefB\x98y\u02c9\xb5\x8a\x03\xa7\xaa\x9e\x18\x99\xca0\x00\x00\u07d4\xe6\u051f\x86\xc2(\xf4sg\xa3^\x88l\xaa\xcb'\x1eS\x94)\x89\x16^\xc0\x9d\xa7\xa1\x98\x00\x00\u07d4\xe6\xe6!\xea\xab\x01\xf2\x0e\xf0\x83k|\xadGFL\xb5\xfd<\x96\x89\x11!\x93B\xaf\xa2K\x00\x00\u07d4\xe6\xe8\x861{jf\xa5\xb4\xf8\x1b\xf1d\xc58\xc2d5\x17e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe6\u98ddu\x0f\xe9\x949N\xb6\x82\x86\xe5\xeab\xa6\x99x\x82\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xe6\xec\\\xf0\u011b\x9c1~\x1epc\x15\uf7b7\xc0\xbf\x11\xa7\x8a\x03\xa4i\xf3F~\x8e\xc0\x00\x00\u07d4\xe6\xf5\xebd\x9a\xfb\x99Y\x9cAK'\xa9\xc9\xc8U5\u007f\xa8x\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xe6\xfe\n\xfb\x9d\xce\xdd7\xb2\xe2,E\x1b\xa6\xfe\xabg4\x803\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe7\x10\xdc\u041b\x81\x01\xf9C{\xd9}\xb9\ns\xef\x99=\v\xf4\x89\x14\xee6\xc0Z\xc2R\x00\x00\u07d4\xe7'\xe6~\xf9\x11\xb8\x1fl\xf9\xc7?\xcb\xfe\xbc+\x02\xb5\xbf\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7.\x1d3\\\u009a\x96\xb9\xb1\xc0/\x00:\x16\xd9q\xe9\v\x9d\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xe71\x1c\x953\xf0\t,rH\xc9s\x9b[,\x86J4\xb1\u0389\x97\xf9}l\xc2m\xfe\x00\x00\u07d4\xe7;\xfe\xad\xa6\xf0\xfd\x01o\xbc\x84>\xbc\xf6\xe3p\xa6[\xe7\f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xe7<\xcfCg%\xc1Q\xe2U\xcc\xf5!\f\xfc\xe5\xa4?\x13\xe3\x89\x01\x15NS!}\xdb\x00\x00\u07d4\xe7B\xb1\xe6\x06\x9a\x8f\xfc'\f\xc6\x1f\xa1d\xac\x15SE\\\x10]\x04\x88~\x14\x89\x06\x96\xd8Y\x00 \xbb\x00\x00\u07d4\xe7\\\x1f\xb1w\b\x9f>X\xb1\x06y5\xa6Yn\xf1s\u007f\xb5\x89\x05j\x87\x9f\xa7uG\x00\x00\u07d4\xe7\\;8\xa5\x8a?3\xd5V\x90\xa5\xa5\x97f\xbe\x18^\x02\x84\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe7a\xd2\u007f\xa3P,\xc7k\xb1\xa6\bt\x0e\x14\x03\u03dd\xfci\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xe7f\xf3O\xf1o<\xfc\xc9s!r\x1fC\xdd\xf5\xa3\x8b\f\xf4\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xe7m\x94Z\xa8\x9d\xf1\xe4W\xaa4+1\x02\x8a^\x910\xb2\u03897\b\xba\xed=h\x90\x00\x00\u07d4\xe7s^\xc7e\x18\xfcj\xa9-\xa8qZ\x9e\xe3\xf6%x\x8f\x13\x89lM\x16\v\xaf\xa1\xb7\x80\x00\xe0\x94\xe7z\x89\xbdE\xdc\x04\xee\xb4\xe4\x1d{Ykp~nQ\xe7L\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xe7}}\uac96\u0234\xfa\a\xca;\xe1\x84\x16=Zm`l\x89\x05\x049\x04\xb6q\x19\x00\x00\u07d4\xe7\u007f\xeb\xab\xdf\b\x0f\x0f]\xca\x1d?Wf\xf2\xa7\x9c\x0f\xfa|\x89K\"\x9d(\xa8Ch\x00\x00\xe0\x94\u7025c\x06\xba\x1ek\xb31\x95,\"S\x9b\x85\x8a\xf9\xf7}\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xe7\x81\xecs-@\x12\x02\xbb\x9b\xd18`\x91\r\xd6\u009a\xc0\xb6\x89C8t\xf62\xcc`\x00\x00\u07d4\xe7\x84\xdc\xc8s\xaa\x8c\x15\x13\xec&\xff6\xbc\x92\xea\xc6\xd4\xc9h\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe7\x91-L\xf4V,W=\xdc[q\xe3s\x10\xe3x\xef\x86\u0249\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xe7\x91\u0545\xb8\x996\xb2])\x8f\x9d5\xf9\xf9\xed\xc2Z)2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7\x924\x9c\xe9\xf6\xf1O\x81\xd0g@\x96\xbe\xfa\x1f\x92!\xcd\xea\x89[]#J\r\xb48\x80\x00\u07d4\xe7\x96\xfdN\x83\x9bL\x95\xd7Q\x0f\xb7\xc5\xc7+\x83\xc6\xc3\xe3\u01c9\x1b\xc43\xf2?\x83\x14\x00\x00\xe0\x94\xe7\xa4/Y\xfe\xe0t\xe4\xfb\x13\xea\x9eW\xec\xf1\xccH(\"I\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xe7\xa4V\f\x84\xb2\x0e\x0f\xb5LIg\f)\x03\xb0\xa9lB\xa4\x89 j\xea\u01e9\x03\x98\x00\x00\u07d4\xe7\xa8\xe4q\xea\xfby\x8fET\xccnRg0\xfdV\xe6,}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u7f82\xc6Y<\x1e\xed\xdd*\xe0\xb1P\x01\xff \x1a\xb5{/\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4\xe7\u01b5\xfc\x05\xfct\x8e[C\x81rdI\xa1\xc0\xad\x0f\xb0\xf1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7\xd1u$\xd0\v\xad\x82I|\x0f'\x15jd\u007f\xf5\x1d'\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe7\xd2\x13\x94\u007f\u02d0J\xd78H\v\x1e\xed/\\2\x9f'\xe8\x89\x01\x03\u00f1\xd3\xe9\xc3\x00\x00\u07d4\xe7\xd6$\x06 \xf4,^\u06f2\xed\xe6\xae\xc4=\xa4\xed\x9bWW\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe7\xda`\x9d@\xcd\xe8\x0f\x00\xce[O\xfbj\xa9\u04304\x94\xfc\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe7\xf0oi\x9b\xe3\x1cD\vC\xb4\xdb\x05\x01\xec\x0e%&\x16D\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe7\xf4\xd7\xfeoV\x1f\u007f\xa1\xda0\x05\xfd6TQ\xad\x89\u07c9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe7\xfd\x8f\xd9Y\xae\xd2v~\xa7\xfa\x96\f\xe1\xdbS\xaf\x80%s\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe8\x0e\u007f\xef\x18\xa5\xdb\x15\xb0\x14s\xf3\xadkx\xb2\xa2\xf8\xac\u0649\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe8\x13\u007f\xc1\xb2\xec|\xc7\x10:\xf9!\x89\x9bJ9\xe1\xd9Y\xa1\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\xe8\x1c-4l\n\xdfL\xc5g\b\xf69K\xa6\xc8\u0226J\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8,X\xc5yC\x1bg5F\xb5:\x86E\x9a\xca\xf1\u079b\x93\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe84\xc6C\x18 \\\xa7\xddJ!\xab\xcb\b&l\xb2\x1f\xf0,\x8965\xc6 G9\u0640\x00\u07d4\xe86\x04\xe4\xffk\xe7\xf9o`\x18\xd3\xec0r\xecR]\xffk\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\xe0\x94\xe8E\xe3\x87\xc4\xcb\u07d8\"\x80\xf6\xaa\x01\xc4\x0eK\xe9X\u0772\x8a\x05K@\xb1\xf8R\xbd\xa0\x00\x00\u07d4\xe8H\xca~\xbf\xf5\xc2O\x9b\x9c1g\x97\xa4;\xf7\xc3V)-\x89\x06.\x11\\\x00\x8a\x88\x00\x00\u07d4\xe8KU\xb5%\xf1\x03\x9etK\x91\x8c\xb33$\x92\xe4^\xcaz\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe8O\x80v\xa0\xf2\x96\x9e\xcd3>\xef\x8d\xe4\x10B\x98b\x91\xf2\x89\x17k4O*x\xc0\x00\x00\u07d4\xe8d\xfe\xc0~\xd1!Je1\x1e\x11\xe3)\xde\x04\r\x04\xf0\xfd\x89Y\u0283\xf5\xc4\x04\x96\x80\x00\u07d4\xe8}\xba\xc66\xa3w!\xdfT\xb0\x8a2\xefIY\xb5\xe4\xff\x82\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe8~\x9b\xbf\xbb\xb7\x1c\x1at\ft\xc7#Bm\xf5]\x06=\u064a\x01\xb1\x92\x8c\x00\u01e68\x00\x00\u07d4\xe8~\xacm`+A\t\xc9g\x1b\xf5{\x95\f,\xfd\xb9\x9dU\x89\x02\xb4\xf2\x19r\xec\xce\x00\x00\xe0\x94\u807b\xbeir-\x81\xef\xec\xaaH\u0455*\x10\xa2\xbf\xac\x8f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xe8\x92Is\x8b~\xce\xd7\xcbfjf\xe4s\xbcv\x82/U\t\x8d\x89\xb9\x1c\u0149lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\xc3\u04f0\xe1\u007f\x97\xd1\xe7V\xe6\x84\xf9N\x14p\xf9\x9c\x95\xa1\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xe8\xc3\xf0E\xbb}8\xc9\xd2\U000d5c3a\x84\x92\xb2S#\t\x01\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4\xe8\xccC\xbcO\x8a\xcf9\xbf\xf0N\xbf\xbfB\xaa\xc0j2\x84p\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe8\xd9B\xd8/\x17^\xcb\x1c\x16\xa4\x05\xb1\x01C\xb3\xf4k\x96:\x89\x1e\xd2\xe8\xffm\x97\x1c\x00\x00\u07d4\xe8\u077e\xd72\xeb\xfeu@\x96\xfd\xe9\bk\x8e\xa4\xa4\xcd\xc6\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\xder^\xca]\xef\x80_\xf7\x94\x1d1\xac\x1c.4-\xfe\x95\x89\x85~\ro\x1d\xa7j\x00\x00\u07d4\xe8\xe9\x85\x05\x86\xe9OR\x99\xabIK\xb8!\xa5\xf4\f\x00\xbd\x04\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\xe8\xea\u047b\x90\xcc\u00ee\xa2\xb0\xdc\u0175\x80VUFU\xd1\u054a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\u07d4\xe8\xea\xf1)D\t-\xc3Y\x9b9S\xfa|\xb1\xc9v\x1c\xc2F\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xe8\xedQ\xbb\xb3\xac\xe6\x9e\x06\x02K3\xf8hD\xc4sH\u06de\x8a\"\xf9\xea\x89\xf4\xa7\xd6\xc4\x00\x00\u07d4\xe8\xef\x10\r|\xe0\x89X2\xf2g\x8d\xf7-J\u03cc(\xb8\xe3\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xe8\xf2\x99i\xe7\\e\xe0\x1c\xe3\xd8aT }\n\x9e|v\xf2\x89\xa2/\xa9\xa7:'\x19\x80\x00\u07d4\xe8\xfc6\xb0\x13\x1e\xc1 \xac\x9e\x85\xaf\xc1\f\xe7\vV\u0636\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9\n5L\xec\x04\u059e]\x96\xdd\xc0\xc5\x13\x8d=3\x15\n\xa0\x89\x1b\x1a}\u03caD\u04c0\x00\xe0\x94\xe9\x13>}1\x84]_+f\xa2a\x87\x92\xe8i1\x1a\xcff\x8a\x05\x17\xc0\xcb\xf9\xa3\x90\x88\x00\x00\u07d4\xe9\x1d\xac\x01\x95\xb1\x9e7\xb5\x9bS\xf7\xc0\x17\xc0\xb29[\xa4L\x89e\xea=\xb7UF`\x00\x00\u07d4\xe9\x1f\xa0\xba\xda\u0779\xa9~\x88\xd3\xf4\xdb|U\u05bbt0\xfe\x89\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\xe9#\xc0aw\xb3B~\xa4H\xc0\xa6\xff\x01\x9bT\xccT\x8d\x95\x89\x01\xf7\x80\x01Fg\xf2\x80\x00\xe0\x94\xe9=G\xa8\u0288]T\fNRo%\xd5\xc6\xf2\xc1\b\u0138\x8a\x17\xda:\x04\u01f3\xe0\x00\x00\x00\u07d4\xe9E\x8fh\xbb',\xb5g:\x04\xf7\x81\xb4\x03Uo\u04e3\x87\x89\x03N\x8b\x88\xce\xe2\xd4\x00\x00\u07d4\xe9IA\xb6\x03`\x19\xb4\x01j0\xc1\x03}Zi\x03\xba\xba\xad\x89*H\xac\xabb\x04\xb0\x00\x00\u07d4\xe9I[\xa5\x84'(\xc0\ud5fe7\xd0\xe4\"\xb9\x8di ,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe9M\xed\x99\u0735r\xb9\xbb\x1d\u02e3/m\xee\x91\xe0W\x98N\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\xe0\x94\xe9QyR}\uc951l\xa9\xa3\x8f!\\\x1e\x9c\xe77\xb4\u024a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xe9U\x91\x85\xf1f\xfc\x95\x13\xccq\x11aD\xce-\xeb\x0f\x1dK\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xe9^\x92\xbb\xc6\xde\a\xbf:f\x0e\xbf_\xeb\x1c\x8a5'\xe1\u0148\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\xe9e\u06a3@9\xf7\xf0\xdfb7Z7\u5acar\xb3\x01\xe7\x8a\x01\x03\xfd\xde\u0373\xf5p\x00\x00\u07d4\xe9i\xea\x15\x95\xed\xc5\u0127\a\xcf\xde8\t)c2Q\xa2\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9k\x18N\x1f\x0fT\x92J\xc8t\xf6\v\xbfDptF\xb7+\x89\x9d\xcc\x05\x15\xb5n\f\x00\x00\xe0\x94\xe9m}L\xdd\x15U:NM1mmd\x80\xca<\xea\x1e8\x8a\x02\x95]\x02\xe1\xa15\xa0\x00\x00\u07d4\xe9n-8\x13\xef\xd1\x16_\x12\xf6\x02\xf9\u007fJb\x90\x9d\x1b;\xc0\xe9\xaa\"\u007f\x90\x89'\xcaK\xd7\x19\xf0\xb8\x00\x00\u07d4\xea,\x19}&\xe9\x8b\r\xa8>\x1br\u01c7a\x8c\x97\x9d=\xb0\x89\x01\x11du\x9f\xfb2\x00\x00\xe0\x94\xea7y\xd1J\x13\xf6\u01c5f\xbc\xde@5\x91A:b9\u06ca)\xb7d2\xb9DQ \x00\x00\u07d4\xeaN\x80\x9e&j\xe5\xf1<\xdb\u33dd\x04V\xe68m\x12t\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\xe0\x94\xeaS\xc9T\xf4\xed\x97\xfdH\x10\x11\x1b\u06b6\x9e\xf9\x81\xef%\xb9\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xeaS\xd2ed\x85\x9d\x9e\x90\xbb\x0eS\xb7\xab\xf5`\xe0\x16,8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xea`Ci\x12\xdek\xf1\x87\u04e4r\xff\x8fS3\xa0\xf7\xed\x06\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xea`T\x9e\xc7U?Q\x1d!I\xf2\xd4fl\xbd\x92C\xd9<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xeaf\xe7\xb8M\u037f6\xee\xa3\xe7[\x858*u\xf1\xa1]\x96\x89]\xbc\x91\x91&o\x11\x80\x00\u07d4\xeahlPW\t<\x17\x1cf\u06d9\xe0\x1b\x0e\xce\xcb0\x86\x83\x89\x14\u0768],\xe1G\x80\x00\u07d4\xeaj\xfe,\xc9(\xac\x83\x91\xeb\x1e\x16_\xc4\x00@\xe3t!\u7262\u007f\xa0c\xb2\xe2\xe6\x80\x00\u07d4\xeay\x05}\xab\xef^d\xe7\xb4O\u007f\x18d\x8e~S7\x18\u0489\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xea|Mm\xc7)\xcdk\x15|\x03\xad#|\xa1\x9a \x93F\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\x81h\xfb\xf2%\xe7\x86E\x9c\xa6\xbb\x18\xd9c\xd2kPS\t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xea\x81\u02868T\f\xd9\xd4\xd7=\x06\x0f,\xeb\xf2$\x1f\xfc>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xea\x83\x17\x19yYB@A\xd9\xd7\xc6z>\xce\x1d\xbbx\xbbU\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xea\x85'\xfe\xbf\xa1\xad\xe2\x9e&A\x93)\u04d3\xb9@\xbb\xb7\u0709lj\xccg\u05f1\xd4\x00\x00\u07d4\xea\x8f0\xb6\xe4\xc5\xe6R\x90\xfb\x98d%\x9b\u0159\x0f\xa8\ue289\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xea\x94\xf3(\b\xa2\uf29b\xf0\x86\x1d\x1d$\x04\xf7\xb7\xbe%\x8a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xea\xa4\\\xea\x02\xd8},\xc8\xfd\xa9CN-\x98[\xd4\x03\x15\x84\x89h\x1f\xc2\xccn+\x8b\x00\x00\xe0\x94\uac3d\x14\x83\t\x18l\xf8\xcb\xd1;r2\xd8\tZ\u02c3:\x8a\x02C\x9a\x88\x1cjq|\x00\x00\u07d4\uaed0\xd3y\x89\xaa\xb3\x1f\xea\xe5G\xe0\xe6\xf3\x99\x9c\xe6\xa3]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xc0\x82~\xff\fn?\xf2\x8a}JT\xf6\\\xb7h\x9d{\x99\x89\x9a\xd9\u67ddGR\x00\x00\u07d4\xea\xc1H(&\xac\xb6\x11\x1e\x19\xd3@\xa4_\xb8QWk\xed`\x89\x01\xbe\x8b\xab\x04\u067e\x80\x00\xe0\x94\xea\xc1{\x81\xedQ\x91\xfb\b\x02\xaaT3s\x13\x83A\a\xaa\xa4\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xea\u00efW\x84\x92\u007f\u9958\xfcN\xec8\xb8\x10/7\xbcX\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xea\u01b9\x88BT.\xa1\v\xb7O&\xd7\xc7H\x8fi\x8bdR\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xea\xc7h\xbf\x14\xb8\xf9C.i\xea\xa8*\x99\xfb\xeb\x94\xcd\f\x9c\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4\xea\xd2\x1c\x1d\xec\u03ff\x1c\\\xd9f\x88\xa2Gki\xba\a\xceJ\x89\x03\xf2M\x8eJ\x00p\x00\x00\u07d4\xea\xd4\xd2\xee\xfbv\xab\xaeU3\x96\x1e\xdd\x11@\x04\x06\xb2\x98\xfc\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xea\xd6Rb\xed]\x12-\xf2\xb2u\x14\x10\xf9\x8c2\xd1#\x8fQ\x89\x05\x83\x17\xedF\xb9\xb8\x00\x00\u07d4\xea\xd7P\x16\u3801Pr\xb6\xb1\b\xbc\xc1\xb7\x99\xac\xf08>\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xea#\xaa\x05r\x00\xe7\xc9\xc1^\x8f\xf1\x90\xd0\xe6l\f\x0e\x83\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xed\x16\xea\xf5\u06ab[\xf0)^^\a\u007fY\xfb\x82U\x90\v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xea\xed\xcck\x8bib\xd5\xd9(\x8c\x15lW\x9dG\xc0\xa9\xfc\xff\x89\x04\x9b\x9c\xa9\xa6\x944\x00\x00\u07d4\xea\xf5#\x88Tn\xc3Z\xcaolc\x93\xd8\xd6\t\xde:K\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xeb\x10E\x8d\xac\xa7\x9eJk$\xb2\x9a\x8a\x8a\xdaq\x1b\u007f.\xb6\x89\u063beI\xb0+\xb8\x00\x00\u07d4\xeb\x1c\xea{E\u047dM\x0e*\x00{\u04ff\xb3Tu\x9e,\x16\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\xeb%H\x1f\u035c\"\x1f\x1a\xc7\xe5\xfd\x1e\u0353\a\xa1b\x15\xb8\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\xeb.\xf3\u04cf\xe6R@<\xd4\xc9\xd8^\xd7\xf0h,\xd7\xc2\u078a\t\x0fSF\b\xa7(\x80\x00\x00\xe0\x94\xeb;\xddY\xdc\u0765\xa9\xbb*\xc1d\x1f\xd0!\x80\xf5\xf3e`\x8a\x01e\xc9fG\xb3\x8a \x00\x00\u07d4\xeb<\xe7\xfc8\x1cQ\xdb}_\xbdi/\x8f\x9e\x05\x8aLp=\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xebE?Z:\xdd\u074a\xb5gP\xfa\xdb\x0f\xe7\xf9M\x9c\x89\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xebO\x00\xe2\x836\xea\t\x94%\x88\ueb12\x18\x11\xc5\"\x14<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xebR\xab\x10U4\x922\x9c\x1cT\x83:\xe6\x10\xf3\x98\xa6[\x9d\x89\b=lz\xabc`\x00\x00\u07d4\xebW\r\xba\x97R'\xb1\xc4-n\x8d\xea,V\u026d\x96\x06p\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xebc\x94\xa7\xbf\xa4\u0489\x11\u0565\xb2>\x93\xf3^4\f\"\x94\x89\x04:w\xaa\xbd\x00x\x00\x00\u07d4\xebh\x10i\x1d\x1a\xe0\u045eG\xbd\"\u03be\u0cfa'\xf8\x8a\x89\x87\x85c\x15\xd8x\x15\x00\x00\u07d4\xebvBL\x0f\u0557\xd3\xe3A\xa9d*\xd1\xee\x11\x8b+W\x9d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xeb| +F+|\u0145]t\x84u_n&\xefC\xa1\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xeb\x83\\\x1a\x91\x18\x17\x87\x8a3\xd1gV\x9e\xa3\xcd\u04c7\xf3(\x8965\u026d\xc5\u07a0\x00\x00\u07d4\ub268\x82g\t\t\xcf7~\x9ex(n\xe9{\xa7\x8dF\u0089+|\xc2\xe9\xc3\"\\\x00\x00\xe0\x94\xeb\x90\u01d3\xb3S\x97a\xe1\xc8\x14\xa2\x96q\x14\x86\x92\x19>\xb4\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xeb\x9c\xc9\xfe\bi\xd2\u06b5,\u01ea\xe8\xfdW\xad\xb3_\x9f\xeb\x89j\x93\xbb\x17\xaf\x81\xf8\x00\x00\xe0\x94\ub8c8\xb0\xda'\xc8{\x1c\xc0\xea\xc6\xc5{,Z\vE\x9c\x1a\x8a\x01p\xa0\xf5\x04\x0eP@\x00\x00\u07d4\xeb\xaa!m\xe9\xccZC\x03\x17\a\xd3o\xe6\u057e\xdc\x05\xbd\xf0\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xeb\xac+D\b\xefT1\xa1;\x85\b\xe8bP\x98!\x14\xe1E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xeb\xb6,\xf8\xe2,\x88K\x1b(\xc6\xfa\x88\xfb\xbc\x17\x93\x8a\xa7\x87\x89+By\x84\x03\u0278\x00\x00\u07d4\xeb\xb7\xd2\xe1\x1b\u01b5\x8f\n\x8dE\xc2\xf6\xde0\x10W\n\u0211\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4\xeb\xbbO,=\xa8\xbe>\xb6-\x1f\xfb\x1f\x95\x02a\u03d8\xec\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4\xeb\xbdM\xb9\x01\x99R\u058b\x1b\x0fm\x8c\xf0h<\x008{\xb5\x89\x12\x04\x01V=}\x91\x00\x00\u07d4\xeb\xbe\xeb%\x91\x84\xa6\xe0\x1c\xcc\xfc\"\a\xbb\u0603xZ\xc9\n\x89!\x9b\xc1\xb0G\x83\xd3\x00\x00\u07d4\xeb\xd3V\x15j81#4=H\x84;\xff\xeda\x03\xe8f\xb3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xeb\xd3{%ec\xe3\fo\x92\x89\xa8\xe2p/\bR\x88\b3\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xeb\xe4l\xc3\xc3L2\xf5\xad\xd6\xc3\x19[\xb4\x86\xc4q>\xb9\x18\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xeb\xff\x84\xbb\xefB0q\xe6\x04\xc3a\xbb\xa6w\xf5Y=\xefN\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xec\t'\xba\xc7\xdc6f\x9c(5J\xb1\xbe\x83\xd7\xee\xc3\t4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\x0e\x18\xa0\x1d\xc4\xdc]\xaa\xe5g\xc3\xfaL\u007f\x8f\x9bY\x02\x05\x89\x11\x1f\xfe@JA\xe6\x00\x00\xe0\x94\xec\x116,\xec\x81\t\x85\xd0\xeb\xbd{sE\x14D\x98[6\x9f\x8a\x06ZNIWpW1\x80\x00\u07d4\xec,\xb8\xb97\x8d\xff1\xae\xc3\xc2.\x0em\xad\xff1J\xb5\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\xec0\xad\u0749[\x82\xee1\x9eT\xfb\x04\xcb+\xb09q\xf3k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec;\x8bX\xa1'\x03\xe5\x81\xce_\xfd~!\xc5}\x1e\\f?\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xecHg\xd2\x17Z\xb5\xb9F\x93aYUFUF\x84\u0364`\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xecM\b\xaa.GIm\u0287\"]\xe3?+@\xa8\xa5\xb3o\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\xecX\xbc\r\f \xd8\xf4\x94efAS\xc5\xc1\x96\xfeY\u6f89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xec[\x19\x8a\x00\u03f5Z\x97\xb5\xd56D\xcf\xfa\x8a\x04\u04abE\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec]\xf2'\xbf\xa8]z\xd7kBn\x1c\xee\x96;\xc7\xf5\x19\u074965\u026d\xc5\u07a0\x00\x00\xe0\x94\xec_\xea\xfe!\f\x12\xbf\u0265\xd0Y%\xa1#\xf1\xe7?\xbe\xf8\x8a`\x8f\xcf=\x88t\x8d\x00\x00\x00\u07d4\xeci\x04\xba\xe1\xf6\x97\x90Y\x17\t\xb0`\x97\x83s?%s\xe3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xecs\x11L^@o\u06fe\t\xb4\xfab\x1b\xd7\x0e\xd5N\xa1\xef\x8a\x050%\xcd!o\xceP\x00\x00\u07d4\xecs\x83=\xe4\xb8\x10\xbb\x02x\x10\xfc\x8fi\xf5D\xe8<\x12\u044965\u026d\xc5\u07a0\x00\x00\u07d4\xecu\xb4\xa4u\x13\x12\v\xa5\xf8`9\x81O\x19\x98\xe3\x81z\u00c9\t\xb0\xbc\xe2\xe8\xfd\xba\x00\x00\u07d4\xecv\xf1.W\xa6U\x04\x03?,\v\xceo\xc0;\xd7\xfa\n\u0109\xc2\x12z\xf8X\xdap\x00\x00\u0794\xec\x80\x14\xef\xc7\xcb\xe5\xb0\xceP\xf3V,\xf4\xe6\u007f\x85\x93\xcd2\x88\xf0\x15\xf2W6B\x00\x00\u07d4\xec\x82\xf5\r\x06G_hM\xf1\xb3\x92\xe0\r\xa3A\xaa\x14TD\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xec\x83\xe7\x98\u00d6\xb7\xa5^*\"$\xab\u0343K'\xeaE\x9c\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xec\x89\xf2\xb6x\xa1\xa1[\x914\xec^\xb7\fjb\a\x1f\xba\xf9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xec\x8c\x1d{j\xac\xcdB\x9d\xb3\xa9\x1e\xe4\xc9\xeb\x1c\xa4\xf6\xf7<\x89\xe6d\x99\"\x88\xf2(\x00\x00\xe0\x94\xec\x98Q\xbd\x91rpa\x02g\xd6\x05\x18\xb5M<\xa2\xb3[\x17\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xec\x99\xe9]\xec\xe4o\xff\xfb\x17^\xb6@\x0f\xbe\xbb\b\ue6d5\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xec\xa5\xf5\x87\x92\xb8\xc6-*\xf5Vq~\xe3\xee0(\xbeM\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\xabZ\xba[\x82\x8d\xe1pS\x81\xf3\x8b\xc7D\xb3+\xa1\xb47\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\xec\xaf3P\xb7\xce\x14M\x06\x8b\x18`\x10\x85,\x84\xdd\f\xe0\xf0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\xb9LV\x8b\xfeY\xad\xe6Pd_O&0lsl\xac\xe4\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xec\xbeB^g\r9\tN \xfbVC\xa9\xd8\x18\xee\xd26\u078a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xec\xbe^\x1c\x9a\u04b1\xdc\xcf\n0_\xc9R/Fi\xdd:\xe7\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xec\xcfz\x04W\xb5f\xb3F\xcag:\x18\x0fDA0!j\u00c9\x05k\xc7^-c\x10\x00\x00\u07d4\xec\u0466(\x025\x1aAV\x8d#\x030\x04\xac\xc6\xc0\x05\xa5\u04c9\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xec\xd2v\xafd\u01dd\x1b\u0669+\x86\xb5\u835a\x95\xeb\x88\xf8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xec\u0506\xfc\x19g\x91\xb9,\xf6\x12\xd3HaO\x91VH\x8b~\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xec\xda\xf92)\xb4^\xe6r\xf6]\xb5\x06\xfb^\xca\x00\xf7\xfc\xe6\x89W\x01\xf9m\xcc@\xee\x80\x00\u07d4\xec\xe1\x11g\vV<\u037e\xbc\xa5#\x84)\x0e\xcdh\xfe\\\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xec\xe1\x15&\x82\xb7Y\x8f\xe2\xd1\xe2\x1e\xc1U3\x88T5\xac\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xec\xe1)\bw\xb5\x83\xe3a\xa2\xd4\x1b\x00\x93F\xe6'N%8\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xec\xf0]\a\xea\x02n~\xbfIA\x00#5\xba\xf2\xfe\xd0\xf0\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xec\xf2L\xdd|\"\x92\x8cD\x1eiM\xe4\xaa1\xb0\xfa\xb5\x97x\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xec\xfd\x00M\x02\xf3l\xd4\u0634\xa8\xc1\xa9S;j\xf8\\\xd7\x16\x8a\x01\x0fA\xac\xb4\xbb;\x9c\x00\x00\xe0\x94\xed\x02\x06\xcb#1Q(\xf8\xca\xff&\xf6\xa3\v\x98Tg\xd0\"\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xed\x10e\xdb\u03dds\xc0O\xfcy\b\x87\r\x88\x14h\xc1\xe12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xed\x12vQ;o\u0186(\xa7A\x85\xc2\xe2\f\xbb\xcax\x17\xbf\x89\nZ\xa8P\t\xe3\x9c\x00\x00\xe0\x94\xed\x12\xa1\xba\x1f\xb8\xad\xfc\xb2\r\xfa\x19X.RZ\xa3\xb7E$\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xed\x16\xce9\xfe\xef;\xd7\xf5\xd1b\x04^\x0fg\xc0\xf0\x00F\xbb\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xed\x1a\\C\xc5t\xd4\xe94)\x9b$\xf1G,\u071f\xd6\xf0\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xed\x1b$\xb6\x91-Q\xb34\xac\r\xe6\xe7q\xc7\xc0EF\x95\xea\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xed\x1f\x1e\x11Z\r`\xce\x02\xfb%\xdf\x01M(\x9e:\f\xbe}\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xed10\\1\x9f\x92s\u04d3m\x8f[/q\u9c72)c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xed2z\x14\xd5\u03ed\u0641\x03\xfc\t\x99q\x8d~\xd7\x05(\xea\x89N\x10\x03\xb2\x8d\x92\x80\x00\x00\u07d4\xed<\xbc7\x82\u03bdg\x98\x9b0\\A3\xb2\xcd\xe3\"\x11\xeb\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xed@\x14S\x8c\xeefJ/\xbc\xb6\xdcf\x9fz\xb1m\v\xa5|\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xedA\u188f\\\xaa\x848\x80\xefN\x8b\b\xbdl3\x14\x1e\u07c9*\xd5\xdd\xfaz\x8d\x83\x00\x00\xe0\x94\xedK\xe0J\x05-z\u0333\xdc\u03901\x9d\xba@ \xab,h\x8a\a\xf3zp\xea\xf3b\x17\x80\x00\xe0\x94\xedR\xa2\xcc\bi\u071e\x9f\x84+\u0415|G\xa8\xe9\xb0\xc9\xff\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xed[LA\xe7b\xd9B@Cs\xca\xf2\x1e\xd4a]%\xe6\xc1\x89m-O=\x95%\xb4\x00\x00\u07d4\xed`\u012bnT\x02\x061~5\x94zc\xa9\xcak\x03\xe2\u02c9\x03\x1a\u066d\vF\u007f\x80\x00\u07d4\xedd\x1e\x066\x8f\xb0\xef\xaa\x17\x03\xe0\x1f\xe4\x8fJhS\t\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xedfC\xc0\xe8\x88K-2\x11\x857\x85\xa0\x8b\xf8\xf3>\u049f\x89Hz\x9a0E9D\x00\x00\xe0\x94\xedp\xa3|\xdd\x1c\xbd\xa9tm\x93\x96X\xae*a\x81(\x85x\x8a\x02\bj\xc3Q\x05&\x00\x00\x00\u07d4\xedsFvn\x1agm\r\x06\xec\x82\x18g\xa2v\xa0\x83\xbf1\x89\u064a\t1\xcc-I\x00\x00\u07d4\xed\x86&\x16\xfc\xbf\xb3\xbe\xcbt\x06\xf7<\\\xbf\xf0\f\x94\aU\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xed\x9e\x03\f\xa7\\\xb1\u049e\xa0\x1d\rL\xdf\xdc\xcd8D\xb6\xe4\x89\x01\xac\xc1\x16\u03ef\xb1\x80\x00\xe0\x94\ud7bc\u02e4/\x98\x15\xe7\x823&m\xd6\xe85\xb6\xaf\xc3\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\ud7f1\xf5\xaf/\xbf\u007f\xfcP)\xce\xe4+p\xff\\'[\xf5\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xed\xa4\xb2\xfaY\u0584\xb2z\x81\r\xf8\x97\x8as\xdf0\x8ac\u0089\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xed\xb4s59y\xa2\x06\x87\x9d\xe1D\xc1\n:\xcf\x12\xa7'OV9a\xf57R\x9d\x89\xc7\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xeer\x88\xd9\x10\x86\xd9\xe2\xeb\x91\x00\x14\u066b\x90\xa0-x\u00a0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xee|=\xed|(\xf4Y\xc9/\xe1;M\x95\xba\xfb\xab\x026}\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\xee\x86} \x91k\xd2\xe9\xc9\xec\xe0\x8a\xa0C\x85\xdbf|\x91.\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\ue25b\x02\xcb\xcb99\xcda\xde\x13B\xd5\x04\x82\xab\xb6\x852\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xee\x90m}_\x17H%\x81t\xbeL\xbc8\x93\x03\x02\xab{B\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\ue5ea\x8a\u019e\xdfz\x98}mp\x97\x9f\x8e\xc1\xfb\xcaz\x94\x89\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\xee\xa1\xe9y\x88\xdeu\xd8!\xcd(\xadh\"\xb2,\u0398\x8b1\x89\x1c0s\x1c\xec\x03 \x00\x00\xe0\x94\xee\u048c?\x06\x8e\tJ0K\x85<\x95\nh\t\xeb\xcb\x03\xe0\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xee\u04c4\xef-A\xd9\xd2\x03\x97NW\xc1#(\xeav\x0e\b\xea\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xee\xdflB\x80\xe6\xeb\x05\xb94\xac\xe4(\xe1\x1dB1\xb5\x90[\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xee\xe7a\x84~3\xfda\u0653\x87\xee\x14b\x86\x94\u047f\xd5%\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xee\xe9\xd0Rn\xda\x01\xe41\x16\xa3\x952-\u0689pW\x8f9\x8a\x02\x1e\x19\x99\xbb\xd5\u04be\x00\x00\u07d4\xee\xf1\xbb\xb1\xe5\xa8?\u0782H\xf8\x8e\xe3\x01\x8a\xfa-\x132\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xee\xfb\xa1-\xfc\x99gB\xdby\x04d\xca}';\xe6\xe8\x1b>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xee\xfd\x05\xb0\xe3\xc4\x17\xd5[3C\x06\x04\x86\xcd\xd5\xe9*\xa7\xa6\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xef\r\xc7\xddzS\xd6\x12r\x8b\xcb\u04b2|\x19\xddM}fo\x89&A\x1c[5\xf0Z\x00\x00\u07d4\xef\x11RR\xb1\xb8E\u0345\u007f\x00-c\x0f\x1bo\xa3zNP\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xef\x1c\x04w\xf1\x18M`\xac\u02b3t\xd3tUz\n>\x10\xf3\x89\b=lz\xabc`\x00\x00\u07d4\xef,4\xbbH}7b\xc3\u0327\x82\xcc\xddz\x8f\xbb\n\x991\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xef5\xf6\u0531\a^j\xa19\x15\x1c\x97K/FX\xf7\x058\x89<;\xc3?\x94\xe5\r\x80\x00\u07d4\xef9\u0291s\xdf\x15S\x1ds\xe6\xb7*hKQ\xba\x0f+\xb4\x89V\xa0\xb4un\xe28\x00\x00\u07d4\xefF<&y\xfb'\x91d\xe2\f=&\x915\x87s\xa0\xad\x95\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xefG\xcf\a>6\xf2q\xd5\"\xd7\xfaNq \xadP\a\xa0\xbc\x89\x87\x86x2n\xac\x90\x00\x00\u07d4\xefa\x15[\xa0\t\xdc\u07be\xf1\v(\xd9\xda=\x1b\xc6\xc9\xce\u0509\x034-`\xdf\xf1\x96\x00\x00\u0794\xefix\x1f2\xff\xce34o,\x9a\xe3\xf0\x84\x93\xf3\xe8/\x89\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xefv\xa4\u034f\xeb\xcb\u0278\x18\xf1x(\xf8\xd94s\xf3\xf3\u02c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\uf4c1\x8fhM\xb0\xc3g^\xc8\x132\xb3\x18>\xcc(\xa4\x95\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94\xef\x9fY\xae\xdaA\x8c\x14\x94h-\x94\x1a\xabI$\xb5\xf4\x92\x9a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\uf9b1\xf0\xdb`57\x82h\x91\xb8\xb4\xbc\x169\x84\xbb@\u03495e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xef\xbdR\xf9}\xa5\xfd:g:F\xcb\xf30D{~\x8a\xad\\\x89\x05l<\x9b\x80\xa0\xa6\x80\x00\xe0\x94\xef\xc8\xcf\x19c\u0269Rg\xb2(\xc0\x86#\x98\x89\xf4\xdf\xd4g\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xef\u02ae\x9f\xf6M,\xd9[RI\xdc\xff\xe7\xfa\xa0\xa0\xc0\xe4M\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xef\xcc\xe0k\xd6\b\x9d\x0eE\x8e\xf5a\xf5\xa6\x89H\n\xfep\x00\x89 \x86\xac5\x10R`\x00\x00\u07d4\xef\xe0g]\xa9\x8a]\xdap\u0356\x19k\x87\xf4\xe7&\xb43H\x89?\x19\xbe\xb8\xdd\x1a\xb0\x00\x00\u07d4\xef\xe8\xff\x87\xfc&\x0e\agc\x8d\xd5\xd0/\xc4g.\x0e\xc0m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xef\xeb\x19\x97\xaa\xd2w\xcc3C\x0ea\x11\xed\tCY@H\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xef\xee\xa0\x10uo\x81\xdaK\xa2[r\x17\x87\xf0X\x17\v\uff49\x01\u009c\x9c\xf7p\xef\x00\x00\u07d4\xef\xf5\x1dr\xad\xfa\xe1C\xed\xf3\xa4+\x1a\xecU\xa2\xcc\xdd\v\x90\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xef\xf8kQ#\xbc\xdc\x17\xedL\xe8\xe0[~\x12\xe5\x13\x93\xa1\xf7\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xef\xfc\x15\u41f1\xbe\xda\n\x8d\x13%\xbd\xb4\x17\"@\xdcT\n\x89\x03\x8599\xee\xe1\xde\x00\x00\xe0\x94\xf0\x11\x95\xd6W\xef<\x94.l\xb89I\xe5\xa2\v\\\xfa\x8b\x1e\x8a\x05ts\xd0]\xab\xae\x80\x00\x00\u07d4\xf0'\x96)Q\x01gB\x88\xc1\xd94g\x05=\x04\"\x19\xb7\x94\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\u07d4\xf09h={=\"[\xc7\xd8\u07ed\xefc\x164A\xbeA\xe2\x89\x01\xdd\x1eK\xd8\xd1\xee\x00\x00\u07d4\xf0Jj7\x97\b\xb9B\x8dr*\xa2\xb0kw\xe8\x895\u03c9\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xf0M,\x91\xef\xb6\xe9\xc4_\xfb\xe7KCL\x8c_+\x02\x8f\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf0W\xaaf\xcav~\xde\x12J\x1c[\x9c\xc5\xfc\x94\xef\v\x017\x89p\xa2K\u02b6\xf4]\x00\x00\u07d4\xf0[\xa8\u05f6\x859\xd930\v\xc9(\x9c=\x94t\xd0A\x9e\x89\x06\xda'\x02M\xd9`\x00\x00\u07d4\xf0\\\xee\xabeA\x05dp\x99Qw<\x84E\xad\x9fN\u01d7\x89\x10C\x16'\xa0\x93;\x00\x00\xe0\x94\xf0_\xcdL\rs\xaa\x16~US\xc8\xc0\xd6\xd4\xf2\xfa\xa3\x97W\x8a\x02\xd2\xd6l1p\xb2\x98\x00\x00\u07d4\xf0g\xe1\xf1\u0583UjL\xc4\xfd\f\x03\x13#\x9f2\xc4\xcf\u060965\u026d\xc5\u07a0\x00\x00\u07d4\xf0g\xfb\x10\u07f2\x93\u962b\xe5d\xc0U\xe34\x8f\x9f\xbf\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0h\xdf\xe9]\x15\xcd:\u007f\x98\xff\xa6\x88\xb44hB\xbe&\x90\x89D\n\xd8\x19\xe0\x97L\x00\x00\xe0\x94\xf0j\x85J<]\xc3m\x1cI\xf4\xc8}m\xb33\xb5~J\u074a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf0y\xe1\xb1&_P\xe8\u0229\x8e\xc0\u01c1^\xb3\xae\xac\x9e\xb4\x89\x01\x16\xdc:\x89\x94\xb3\x00\x00\xe0\x94\xf0{\xd0\xe5\xc2\xcei\xc7\u0127$\xbd&\xbb\xfa\x9d*\x17\xca\x03\x8a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\xf0\x83*k\xb2U\x03\xee\xcaC[\xe3\x1b\v\xf9\x05\xca\x1f\xcfW\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf0\x9b>\x87\xf9\x13\xdd\xfdW\xae\x80I\xc71\u06e9\xb66\xdf\u00c9 \xf5\xb1\uab4d\x80\x00\x00\u07d4\xf0\xb14\v\x99oo\v\xf0\xd9V\x1c\x84\x9c\xaf\u007fD0\xbe\xfa\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf0\xb1\xf9\xe2x2\xc6\xdei\x14\xd7\n\xfc#\x8ct\x99\x95\xac\xe4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf0\xb4i\xea\xe8\x9d@\f\xe7\xd5\xd6j\x96\x95\x03p6\xb8\x89\x03\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf0\xb9\u0583\u03a1+\xa6\x00\xba\xac\xe2\x19\xb0\xb3\xc9~\x8c\x00\xe4\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf0\xbe\x0f\xafMy#\xfcDF\"\u0458\f\xf2\u0650\xaa\xb3\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0\xc0\x81\xdaR\xa9\xae6d*\xdf^\b _\x05\xc5Ah\xa6\x89\x06\x04o7\xe5\x94\\\x00\x00\u07d4\xf0\xc7\r\rm\xabvc\xaa\x9e\xd9\xce\xeaV~\xe2\u01b0'e\x89qC\x8a\u0167\x91\xa0\x80\x00\u07d4\xf0\xcb\xef\x84\xe1ic\x00\x98\xd4\xe3\x01\xb2\x02\b\xef\x05\x84j\u0249\x0e\v\x83EPkN\x00\x00\u07d4\xf0\xd2\x16c\u0630\x17n\x05\xfd\xe1\xb9\x0e\xf3\x1f\x850\xfd\xa9_\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xf0\xd5\xc3\x1c\xcbl\xbe0\xc7\xc9\xea\x19\xf2h\xd1Y\x85\x1f\x8c\x9c\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\xf0\xd6L\xf9\xdf\tt\x113\xd1pH_\xd2K\x00P\x11\xd5 \x89\x1b\b\x93A\xe1O\xcc\x00\x00\u07d4\xf0\xd8X\x10^\x1bd\x81\x01\xac?\x85\xa0\xf8\"+\xf4\xf8\x1dj\x89 \x86\xac5\x10R`\x00\x00\u07d4\xf0\xdcC\xf2\x05a\x91'P{+\x1c\x1c\xfd\xf3-(1\t \x89\x10^\xb7\x9b\x94\x17\b\x80\x00\u07d4\xf0\xe1\u07e4*\u07ac/\x17\xf6\xfd\xf5\x84\xc9Hb\xfdV3\x93\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf0\xe2d\x9c~j?,]\xfe3\xbb\xfb\xd9'\xca<5\nX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0\xe7\xfb\x9eB\nS@\xd56\xf4\x04\b4O\xea\xef\xc0j\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf1\x04b\xe5\x8f\xcc\a\U000d5121\x87c\x94Q\x16~\x85\x92\x01\x89\t4\xdd]3\xbc\x97\x00\x00\xe0\x94\xf1\x06a\xff\x94\x14\x0f >zH%rCy8\xbe\xc9\xc3\xf7\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xf1\x14\xff\r\x0f$\xef\xf8\x96\xed\xdeTq\u07a4\x84\x82J\x99\xb3\x88\xbe -j\x0e\xda\x00\x00\u07d4\xf1\x16\xb0\xb4h\x0fS\xabr\xc9h\xba\x80.\x10\xaa\x1b\xe1\x1d\u0209\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xf1\x1c\xf5\xd3cto\xeehd\xd3\xca3m\xd8\x06y\xbb\x87\xae\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xf1\x1e\x01\u01e9\xd1$\x99\x00_M\xaew\x16\tZ4\x17bw\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf1;\b0\x93\xbaVN-\xc61V\x8c\xf7T\r\x9a\x0e\xc7\x19\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf1O\x0e\xb8m\xb0\xebhu?\x16\x91\x8e]K\x80t7\xbd>\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf1Qx\xff\xc4:\xa8\a\x0e\xce2~\x93\x0f\x80\x9a\xb1\xa5O\x9d\x89\n\xb6@9\x12\x010\x00\x00\u07d4\xf1V\xdc\v*\x98\x1e[U\xd3\xf2\xf0;\x814\xe31\u06ed\xb7\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf1]\x9dZ!\xb1\x92\x9ey\x03q\xa1\u007f\x16\xd9_\fie\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1^\x18,O\xbb\xady\xbd\x934\"B\xd4\xdc\xcf+\xe5\x89%\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf1bM\x98\ve3o\xea\u0166\xd5A%\x00\\\xfc\xf2\xaa\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1g\xf5\x86\x8d\xcfB3\xa7\x83\x06\th,\xaf-\xf4\xb1\xb8\a\x89\x81\xe5B\xe1\xa78?\x00\x00\u07d4\xf1m\xe1\x89\x1d\x81\x96F\x13\x95\xf9\xb16&[;\x95F\xf6\xef\x89\x01\xb2\x8e\x1f\x98\xbb\u0380\x00\u07d4\xf1z\x92\xe06\x1d\xba\xce\xcd\xc5\xde\r\x18\x94\x95Z\xf6\xa9\xb6\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1z\xdbt\x0fE\u02fd\xe3\tN~\x13qo\x81\x03\xf5c\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1\x8b\x14\xcb\xf6iC6\xd0\xfe\x12\xac\x1f%\xdf-\xa0\xc0]\xbb\x89\xd8\xd4`,&\xbfl\x00\x00\u07d4\xf1\x9b98\x9dG\xb1\x1b\x8a,?\x1d\xa9\x12M\xec\xff\xbe\xfa\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1\x9f\x195\b9>M*\x12{ \xb2\x03\x1f9\xc8%\x81\u0189\xbd\xbdz\x83\xbd/l\x00\x00\u07d4\xf1\xa1\xf3 @yd\xfd<\x8f.,\u0224X\r\xa9O\x01\xea\x89ll!wU|D\x00\x00\u07d4\xf1\xb4\xec\xc65%\xf7C,=\x83O\xfe+\x97\x0f\xbe\xb8r\x12\x89\xa2\xa2@h\xfa\u0340\x00\x00\u07d4\U000753ef\xfa\x87\x94\xf5\n\xf8\xe8\x83\t\u01e6&TU\xd5\x1a\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\xf1\xc8\u0129A\xb4b\x8c\rl0\xfd\xa5dR\u065c~\x1bd\x89N\x8c\xea\x1e\xdeu\x04\x00\x00\u07d4\xf1\xda@so\x99\xd5\xdf;\x06\x8a]t_\xaf\xc6F?\u0271\x89\x06\x96\xca#\x05\x8d\xa1\x00\x00\u07d4\xf1\u070a\xc8\x10B\xc6z\x9c\\c2!\xa8\xf76>e\x87\f\x9f(t$\u04a9`\x89J\xcfX\xe0rW\x10\x00\x00\u07d4\xf2B\u0684]B\u053fw\x9a\x00\xf2\x95\xb4\aP\xfeI\xea\x13\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf2RY\xa5\xc99\xcd%\x96l\x9bc\x03\xd3s\x1cS\u077cL\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf2^Lp\xbcFV2\u021eV%\xa82\xa7r/k\xff\xab\x89\xf3K\x82\xfd\x8e\x91 \x00\x00\u07d4\xf2k\xce\xdc\xe3\xfe\xad\u03a3\xbc>\x96\xeb\x10@\xdf\xd8\xff\u1809*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xf2py%v\xf0]QD\x93\xff\xd1\xf5\xe8K\xecK-\xf8\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf2s,\xf2\xc1;\x8b\xb8\xe7I*\x98\x8f_\x89\xe3\x82s\xdd\u0209 \x86\xac5\x10R`\x00\x00\xe0\x94\xf2t.hY\xc5i\xd5\xf2\x10\x83Q\xe0\xbfM\xca5*H\xa8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf2\x81:d\xc5&]\x02\x025\u02dc1\x9bl\x96\xf9\x06\xc4\x1e\x89\x12\xf99\u025e\u06b8\x00\x00\u07d4\xf2\x87\xffR\xf4a\x11z\xdb>\x1d\xaaq\x93-\x14\x93\xc6_.\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\xf2\xab\x11au\x02D\xd0\xec\xd0H\xee\r>Q\xab\xb1C\xa2\xfd\x89B\xfe+\x90ss\xbc\x00\x00\u07d4\xf2\xb4\xab,\x94'\xa9\x01^\xf6\xee\xff\xf5\xed\xb6\x019\xb7\x19\u0449&\u06d9*;\x18\x00\x00\x00\u07d4\xf2\xc0>*8\x99\x8c!d\x87`\xf1\xe5\xae~\xa3\a}\x85\"\x89\x8f?q\x93\xab\a\x9c\x00\x00\u0794\xf2\u0090N\x9f\xa6d\xa1\x1e\xe2VV\xd8\xfd,\xc0\u0665\"\xa0\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xf2\xc3b\xb0\xef\x99\x1b\xc8/\xb3nf\xffu\x93*\xe8\u0742%\x89\x04\x02\xf4\xcf\xeeb\xe8\x00\x00\u07d4\xf2\xd0\xe9\x86\xd8\x14\xea\x13\xc8\xf4f\xa0S\x8cS\u0712&Q\xf0\x89J\xcfX\xe0rW\x10\x00\x00\xe0\x94\xf2\u04775w$\xecL\x03\x18[\x87\x9bc\xf5~&X\x91S\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf2\xd5v<\xe0s\x12~,\xed\xdeo\xab\xa7\x86\xc7<\xa9AA\x8a\x01\xacB\x86\x10\x01\x91\xf0\x00\x00\xe0\x94\xf2\u055c\x89#u\x90s\xd6\xf4\x15\xaa\xf8\xeb\x06_\xf2\U000f614a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4\xf2\xe9\x9f\\\xbb\x83kz\xd3bGW\x1a0,\xbeKH\x1ci\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf2\xed>w%J\u02c3#\x1d\xc0\x86\x0e\x1a\x11$+\xa6'\u06c9kV\x05\x15\x82\xa9p\x00\x00\xe0\x94\xf2\xed\xde7\xf9\xa8\u00dd\u07a2My\xf4\x01WW\xd0k\xf7\x86\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xf2\xef\xe9e`\xc9\xd9{r\xbd6DxC\x88\\\x1d\x90\xc21\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf2\xfb\xb6\u0607\xf8\xb8\xcc:\x86\x9a\xba\x84\u007f=\x1fd\xfcc\x97\xaae\xfbS\xa8\xf0z\x0f\x89:\xae0\xe8\xbc\xee\x89|\xf28\x1fa\x9f\x15\x00\x00\u07d4\xf3@\x83\xec\xea8P\x17\xaa@\xbd\xd3^\xf7\xef\xfbL\xe7v-\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf3F\xd7\u0792t\x1c\b\xfcX\xa6M\xb5[\x06-\xde\x01-\x14\x89\x0f\xffk\x1fv\x1em\x00\x00\xe0\x94\xf3U\xd3\xec\f\xfb\x90}\x8d\xbb\x1b\xf3FNE\x81(\x19\v\xac\x8a\x01\v\x04n\u007f\r\x80\x10\x00\x00\u07d4\xf3m\xf0/\xbd\x89`sG\xaf\xce)i\xb9\xc4#jX\xa5\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3s\xe9\u06ac\f\x86u\xf5;yz\x16\x0fo\xc04\xaek#\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf3{BeG\xa1d-\x8032H\x14\xf0\xed\xe3\x11O\xc2\x12\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xf3{\xf7\x8cXu\x15G\x11\xcbd\r7\xeam(\xcf\xcb\x12Y\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf3\x82\xdfX1U\xd8T\x8f?\x93D\f\xd5\xf6\x8c\xb7\x9d`&\x8a8u}\x02\u007f\xc1\xfd\\\x00\x00\xe0\x94\xf3\x82\xe4\xc2\x04\x10\xb9Q\b\x9e\x19\xba\x96\xa2\xfe\xe3\xd9\x1c\xce~\x8a\x01\x11\xfaV\xee\u00a88\x00\x00\xe0\x94\xf3\x8al\xa8\x01hS~\x97M\x14\xe1\xc3\xd19\x90\xa4L,\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xf3\x9a\x9dz\xa3X\x1d\xf0~\xe4'\x9a\xe6\xc3\x12\xef!\x036X\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf3\xb6h\xb3\xf1M\x92\x0e\xbc7\x90\x92\u06d8\x03\x1bg\xb2\x19\xb3\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\U000fe679\x10<\xe7U\n\xa7O\xf1\xdb\x18\xe0\x9d\xfe2\xe0\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xc1\xab\u049d\xc5{A\xdc\x19-\x0e8M\x02\x1d\xf0\xb4\xf6\u0509\x97\xae\f\u07cf\x86\xf8\x00\x00\u07d4\xf3\xc4qm\x1e\xe5'\x9a\x86\xd0\x16:\x14a\x81\x81\xe1a6\u01c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf3\u030b\xcbU\x94e\xf8\x1b\xfeX;\u05eb\n#\x06E;\x9e\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf3\u0588\xf0k\xbd\xbfP\xf9\x93,AE\xcb\xe4\x8e\xcd\xf6\x89\x04\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf3\xdb\xcf\x13Z\u02dd\xee\x1aH\x9cY<\x02O\x03\u00bb\xae\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xde_&\xefj\xde\xd6\xf0m;\x91\x13F\xeep@\x1d\xa4\xa0\x89\x13:\xb3}\x9f\x9d\x03\x00\x00\u07d4\xf3\xdfc\xa9q\x99\x93308;>\xd7W\v\x96\u0101#4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xe7OG\f}:?\x003x\x0fv\xa8\x9f>\xf6\x91\xe6\u02c9\xa3\xcf\xe61\xd1Cd\x00\x00\u07d4\xf3\xeb\x19H\xb9Q\xe2-\xf1ax)\xbf;\x8d\x86\x80\xeckh\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf3\xf1\xfa9\x18\xca4\xe2\xcf~\x84g\v\x1fM\x8e\xca\x16\r\xb3\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\xf3\xf2O\u009e @?\xc0\xe8\xf5\xeb\xbbU4&\xf7\x82p\xa2\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf3\xfar5R\xa5\xd0Q.+b\xf4\x8d\xca{+\x81\x050[\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xf3\xfeQ\xfd\xe3D\x13\xc73\x18\xb9\xc8T7\xfe~\x82\x0fV\x1a\x896b2\\\u044f\xe0\x00\x00\u07d4\xf4\x00\xf9=_\\~?\xc3\x03\x12\x9a\xc8\xfb\f/xd\a\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\v\x13O\xea\"\u01b2\x9c\x84W\xf4\x9f\x00\x0f\x9c\xdax\x9a\u06c9 \x86\xac5\x10R`\x00\x00\u07d4\xf4\x15W\xdf\u07f1\xa1\xbd\xce\xfe\xfe.\xba\x1e!\xfe\nJ\x99B\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\x17z\r\x85\u050b\x0e&B\x11\xce*\xa2\xef\xd3\xf1\xb4\u007f\b\x89\xc2\xcc\xca&\xb7\xe8\x0e\x80\x00\u07d4\xf4/\x90R1\xc7p\xf0\xa4\x06\xf2\xb7h\x87\u007f\xb4\x9e\xee\x0f!\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xf42\xb9\u06ef\x11\xbd\xbds\xb6Q\x9f\xc0\xa9\x04\x19\x87q\xaa\u0189\b=lz\xabc`\x00\x00\u07d4\xf4=\xa3\xa4\xe3\xf5\xfa\xb1\x04\u029b\xc1\xa0\xf7\xf3\xbbJV\xf3Q\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xf4G\x10\x8b\x98\xdfd\xb5~\x87\x103\x88\\\x1a\xd7\x1d\xb1\xa3\xf9\x8a\x01v\xf4\x9e\xad4\x83P\x80\x00\u07d4\xf4O\x85Q\xac\xe93r\a\x12\xc5\u0111\u0376\xf2\xf9Qsl\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\xf4V\x05Z\x11\xab\x91\xfff\x8e.\xc9\"\x96\x1f*#\xe3\xdb%\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xf4V\xa7[\xb9\x96U\xa7A,\xe9}\xa0\x81\x81m\xfd\xb2\xb1\xf2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf4[\x1d\xcb.A\xdc'\xff\xa0$\u06ad\xf6\x19\xc1\x11u\xc0\x87\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xf4c\xa9\f\xb3\xf1>\x1f\x06CB66\xbe\xab\x84\xc1#\xb0m\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf4h\x90n~\xdffJ\xb0\u063e=\x83\xebz\xb3\xf7\xff\xdcx\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xf4i\x80\u3929\u049ajn\x90`E7\xa3\x11K\xcb(\x97\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf4kk\x9c|\xb5R\x82\x9c\x1d=\xfd\x8f\xfb\x11\xaa\xba\xe7\x82\xf6\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4\xf4v\xe1&\u007f\x86$|\xc9\b\x81o.z\xd58\x8c\x95-\xb0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf4v\xf2\xcbr\b\xa3.\x05\x1f\xd9N\xa8f)\x92c\x82\x87\xa2\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xf4{\xb14\xda0\xa8\x12\xd0\x03\xaf\x8d\u0338\x88\xf4K\xbfW$\x8a\x01\x19Y\xb7\xfe3\x95X\x00\x00\u07d4\xf4\x83\xf6\a\xa2\x1f\xcc(\x10\n\x01\x8cV\x8f\xfb\xe1@8\x04\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf4\x8e\x1f\x13\xf6\xafM\x84\xb3q\xd7\xdeK'=\x03\xa2c'\x8e\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xf4\x9cG\xb3\xef\xd8knj[\xc9A\x8d\x1f\x9f\xec\x81Ki\xef\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xf4\x9fo\x9b\xaa\xbc\x01\x8c\x8f\x8e\x11\x9e\x01\x15\xf4\x91\xfc\x92\xa8\xa4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf4\xa3g\xb1f\u0499\x1a+\xfd\xa9\xf5dc\xa0\x9f%,\x1b\x1d\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\xa5\x1f\xceJ\x1d[\x94\xb0q\x83\x89\xbaNx\x14\x13\x9c\xa78\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xf4\xa9\xd0\f\xef\xa9{zX\xef\x94\x17\xfcbg\xa5\x06\x909\xee\x89\x01.\x89(\u007f\xa7\x84\x00\x00\u07d4\xf4\xaa\xa3\xa6\x16>7\x06W{I\xc0v~\x94\x8ah\x1e\x16\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\xb1bn$\xf3\v\xca\xd9'!\xb2\x93r\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xf5U\xa2{\xb1\xe2\xfdN,\u01c4\xca\ue493\x9f\xc0n/\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\xf5X\xa2\xb2\xdd&\u0755\x93\xaa\xe0E1\xfd<<\u00c5Kg\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\xf5`H\xdd!\x81\u0523od\xfc\xec\xc6!T\x81\xe4*\xbc\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf5dB\xf6\x0e!i\x13\x95\u043f\xfa\xa9\x19M\xca\xff\x12\u2dc9\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xf5yqJE\xeb\x8fR\xc3\xd5{\xbd\xef\xd2\xc1[./\x11\u07c9T\x91YV\xc4\t`\x00\x00\u07d4\xf5\x93\xc6R\x85\xeek\xbdf7\U000fe3c9\xad@\u0509\xf6U\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf5\x98\xdb.\t\xa8\xa5\xee}r\r+\\C\xbb\x12m\x11\xec\u0089\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf5\x9d\xab\x1b\xf8\xdf\x112~a\xf9\xb7\xa1KV:\x96\xec5T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf5\x9f\x9f\x02\xbb\u024e\xfe\t~\xab\xb7\x82\x10\x97\x90!\x89\x8b\xfd\x8a\x02\x1e\x17\x1a>\xc9\xf7,\x00\x00\u07d4\xf5\xa5E\x9f\xcd\xd5\xe5\xb2s\x83\r\xf8\x8e\xeaL\xb7}\xda\u07f9\x89\x04\t\xe5+H6\x9a\x00\x00\u07d4\xf5\xa7gj\xd1H\xae\x9c\x1e\xf8\xb6\xf5\xe5\xa0\xc2\xc4s\xbe\x85\v\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf5\xb0h\x98\x9d\xf2\x9c%5w\xd0@Z\xden\x0eu(\xf8\x9e\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\xf5\xb6\xe9\x06\x1aN\xb0\x96\x16\aw\xe2gb\xcfH\xbd\u0635]\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xf5\xcf\xfb\xbabN~\xb3!\xbc\x83\xc6\f\xa6\x81\x99\xb4\xe3fq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf5\xd1ER\xb1\xdc\xe0\xd6\xdc\x1f2\r\xa6\xff\u02231\xcdo\f\x89Hz\x9a0E9D\x00\x00\xe0\x94\xf5\xd6\x1a\xc4\u0295G^[{\xff\xd5\xf2\xf6\x90\xb3\x16u\x96\x15\x8a\x06\x92\xae\x88\x97\b\x1d\x00\x00\x00\u07d4\xf5\xd9\xcf\x00\xd6X\xddEQzH\xa9\xd3\xf5\xf63T\x1aS=\x89\x06O_\xdfIOx\x00\x00\u07d4\xf5\xea\xdc\xd2\u0478ez\x12\x1f3\xc4X\xa8\xb1>v\xb6U&\x89\r\x8b\x0fZZ\xc2J\x00\x00\u07d4\xf6\a\xc2\x15\r>\x1b\x99\xf2O\xa1\xc7\xd5@\xad\xd3\\N\xbe\x1e\x89\xa7\xf1\xaa\a\xfc\x8f\xaa\x00\x00\u07d4\xf6\v\xd75T>k\xfd.\xa6\xf1\x1b\xffbs@\xbc\x03Z#\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf6\f\x1bE\xf1d\xb9X\x0e 'Z\\9\xe1\xd7\x1e5\xf8\x91\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf6\x0fb\xd797\x95?\xef5\x16\x9e\x11\xd8r\xd2\xea1~\xec\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xf6\x12\x83\xb4\xbd\x85\x04\x05\x8c\xa3`\u94d9\x9bb\xcb\xc8\xcdg\x89\r\xd2\xd5\xfc\xf3\xbc\x9c\x00\x00\u07d4\xf6\x17\xb9g\xb9\xbdH_v\x95\xd2\xefQ\xfbw\x92\u0618\xf5\x00\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf6\x18\u0671\x04A\x14\x80\xa8c\xe6#\xfcU#-\x1aOH\xaa\x89\x0eh\x9emD\xb1f\x80\x00\u07d4\xf6\"\u5126b>\xaa\xf9\x9f+\xe4\x9eS\x80\xc5\xcb\xcf\\\u0609\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf62\xad\xffI\r\xa4\xb7-\x126\xd0KQ\x0ft\xd2\xfa\xa3\u0349K\xe4\xe7&{j\xe0\x00\x00\u07d4\xf69\xac1\u069fg'\x1b\xd1\x04\x02\xb7eN\\\xe7c\xbdG\x89\x15\xaf\x0fB\xba\xf9&\x00\x00\u07d4\xf6:W\x9b\xc3\xea\u00a9I\x04\x10\x12\x8d\xbc\xeb\xe6\xd9\u0782C\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\xf6E\xdd|\x89\x00\x93\xe8\xe4\u022a\x92\xa6\xbb55\"\xd3\u0718\x89\aC\x9f\xa2\t\x9eX\x00\x00\xe0\x94\xf6H\xea\x89\xc2u%q\x01r\x94Ny\xed\xff\x84x\x03\xb7u\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xf6JJ\xc8\xd5@\xa9(\x9ch\xd9`\xd5\xfb|\xc4Zw\x83\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf6N\xcf!\x17\x93\x1cmSZ1\x1eO\xfe\xae\xf9\u0514\x05\xb8\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xf6O\xe0\x93\x9a\x8d\x1e\xea*\x0e\u035a\x970\xfdyX\xe31\t\x89\x01\x1d\xe1\xe6\xdbE\f\x00\x00\u07d4\xf6V\x16\xbe\x9c\x8by~t\x15\"|\x918\xfa\xa0\x89\x17B\u05c9*\xd3s\xcef\x8e\x98\x00\x00\u07d4\xf6W\xfc\xbeh.\xb4\xe8\xdb\x15.\u03c9$V\x00\vQ=\x15\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf6X\x19\xacL\xc1L\x13\u007f\x05\xddyw\xc7\xda\xe0\x8d\x1aJ\xb5\x89\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xf6{\xb8\xe2\x11\x8b\xbc\u0550'fn\xed\xf6\x94>\xc9\xf8\x80\xa5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf6\x84d\xbfd\xf2A\x13V\xe4\xd3%\x0e\xfe\xfe\\P\xa5\xf6[\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf6\x86x[\x89r\va\x14_\ua017\x8dj\u030e\v\xc1\x96\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf6\x8c^3\xfa\x97\x13\x9d\xf5\xb2\xe68\x86\xce4\xeb\xf3\u45dc\x89\xb3\xfaAi\xe2\xd8\xe0\x00\x00\u07d4\xf6\xa8cWW\xc5\xe8\xc14\xd2\r\x02\x8c\xf7x\u03c6\t\xe4j\x89O\x1dw/\xae\xc1|\x00\x00\u07d4\xf6\xb7\x82\xf4\xdc\xd7E\xa6\xc0\xe2\xe00`\x0e\x04\xa2K%\xe5B\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xf6\xbc7\xb1\u04a3x\x8dX\x9bm\xe2\x12\xdc\x17\x13\xb2\xf6\u738a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xf6\xc3\u010a\x1a\xc0\xa3G\x99\xf0M\xb8n\u01e9u\xfewh\xf3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf6\xd2]?=\x84m#\x9fR_\xa8\xca\xc9{\xc45x\u06ec\x890\x92\u007ft\xc9\xde\x00\x00\x00\u07d4\xf6\xea\xacp2\u0512\xef\x17\xfd`\x95\xaf\xc1\x1dcOV\xb3\x82\x89\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\xf6\xea\xd6}\xbf[~\xb13X\xe1\x0f6\x18\x9dS\xe6C\xcf\u03ca\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xf6\xf1\xa4C\t\x05\x1ck%\xe4}\xff\x90\x9b\x17\x9b\xb9\xabY\x1c\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf7\x03(\xef\x97b_\xe7E\xfa\xa4\x9e\xe0\xf9\u052a;\r\xfbi\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf7\n\x99\x8aq{3\x8d\x1d\u0658T@\x9b\x1a3\x8d\ue930\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7\rcz\x84\\\x06\xdbl\u0711\xe67\x1c\xe7\xc48\x8ab\x8e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\x15R\x13D\x98\x92tK\xc6\x0f.\x04@\a\x88\xbd\x04\x1f\u0749\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\xe0\x94\xf7\x1bE4\xf2\x86\xe40\x93\xb1\xe1^\xfe\xa7I\xe7Y{\x8bW\x8a\x16\x1c\x13\xd34\x1c\x87(\x00\x00\u07d4\xf74\xec\x03rM\xde\xe5\xbbRy\xaa\x1a\xfc\xf6\x1b\f\xb4H\xa1\x89\xe5\xbf,\u0270\x97\x80\x00\x00\u07d4\xf76\u0716v\x00\x128\x8f\xe8\x8bf\xc0n\xfeW\xe0\xd7\xcf\n\x89q\xd7Z\xb9\xb9 P\x00\x00\u07d4\xf7:\xc4l ;\xe1S\x81\x11\xb1Q\xec\x82 \u01c6\xd8AD\x89\x0f\xf77x\x17\xb8+\x80\x00\u07d4\xf7=\xd9\xc1B\xb7\x1b\xce\x11\xd0n0\xe7\xe7\xd02\xf2\uc71e\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf7A\x8a\xa0\xe7\x13\xd2H\"\x87v\xb2\xe7CB\"\xaeu\u3949lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7Nn\x14S\x82\xb4\u06c2\x1f\xe0\xf2\u0643\x88\xf4V\t\u019f\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf7P\f\x16o\x8b\xea/\x824v\x06\xe5\x02K\xe9\xe4\xf4\u0399\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7W\xfc\x87 \xd3\xc4\xfaRw\a^`\xbd\\A\x1a\xeb\xd9w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7[\xb3\x9cy\x97y\xeb\xc0J3m&\r\xa61F\xed\x98\u0409\x01Z\xf1\u05cbX\xc4\x00\x00\xe0\x94\xf7h\xf3!\xfdd3\xd9kO5M<\xc1e,\x172\xf5\u007f\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf7oi\xce\xe4\xfa\xa0\xa6;0\xae\x1ex\x81\xf4\xf7\x15ep\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf7w6\x1a=\u062bb\xe5\xf1\xb9\xb0GV\x8c\xc0\xb5UpL\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf7|{\x84QI\xef\xba\x19\xe2a\xbc|u\x15y\b\xaf\xa9\x90\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7\u007f\x95\x87\xffz-r\x95\xf1\xf5q\u0206\xbd3\x92jR|\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xf7\x82X\xc1$\x81\xbc\xdd\u06f7*\x8c\xa0\xc0C\tra\xc6\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\x98\xd1m\xa4\xe4`\xc4`\xcdH_\xae\x0f\xa0Y\x97\b\ub08965\u026d\xc5\u07a0\x00\x00\u07d4\xf7\xa1\xad\xe2\xd0\xf5)\x12=\x10U\xf1\x9b\x17\x91\x9fV!Ng\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf7\xac\xff\x93K\x84\xda\ti\xdc7\xa8\xfc\xf6C\xb7\xd7\xfb\xedA\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf7\xb1Q\xcc^W\x1c\x17\xc7e9\xdb\xe9\x96L\xbbo\xe5\xdey\x89tq|\xfbh\x83\x10\x00\x00\u07d4\xf7\xb2\x9b\x82\x19\\\x88-\xabx\x97\u00ae\x95\xe7w\x10\xf5xu\x89w5Aa2\xdb\xfc\x00\x00\u07d4\xf7\xbcLD\x91\rZ\xed\xd6n\xd25U8\xa6\xb1\x93\xc3a\xec\x89\x05A\xde,-\x8db\x00\x00\u07d4\xf7\xc0\f\xdb\x1f\x02\x03\x10\u056c\xab{Ij\xaaD\xb7y\b^\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xf7\xc1\xb4C\x96\x8b\x11{]\u0677UW/\xcd9\xca^\xc0K\x89\x18\xb9h\u0092\xf1\xb5\x00\x00\xe0\x94\xf7\xc5\x0f\x92*\xd1ka\xc6\u047a\xa0E\xed\x81h\x15\xba\u010f\x8a\x02\xa99j\x97\x84\xad}\x00\x00\u07d4\xf7\xc7\b\x01Pq\xd4\xfb\n:*\t\xa4]\x15c\x96\xe34\x9e\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf7\xcb\u06e6\xbel\xfeh\xdb\xc2<+\x0f\xf50\xee\x05\"o\x84\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\xd0\xd3\x10\xac\xea\x18@a8\xba\xaa\xbb\xfe\x05q\xe8\r\xe8_\x89Hz\x9a0E9D\x00\x00\u07d4\xf7\u05ef LV\xf3\x1f\xd9C\x98\xe4\r\xf1\x96K\u063f\x12<\x89\b!\xd2!\xb5)\x1f\x80\x00\u07d4\xf7\xdc%\x11\x96\xfb\u02f7|\x94}|\x19F\xb0\xffe\x02\x1c\xea\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf7\xe4Z\x12\xaaq\x1cp\x9a\xce\xfe\x95\xf3;xa-*\xd2*\x8a\x0e\x06U\xe2\xf2k\xc9\x18\x00\x00\u07d4\xf7\xf4\x89\x8cLRm\x95_!\xf0U\xcbnG\xb9\x15\xe5\x19d\x89|\b`\xe5\xa8\r\xc0\x00\x00\u07d4\xf7\xf9\x1ez\xcb[\x81)\xa3\x06\x87|\xe3\x16\x8eoC\x8bf\xa1\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4\xf7\xfcE\xab\xf7oP\x88\xe2\u5d68\xd12\xf2\x8aMN\xc1\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\x06:\xf4\xcc\x1d\xd9a\x9a\xb5\u063f\xf3\xfc\xd1\xfa\xa8H\x82!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\bnBf\x1e\xa9)\xd2\u0761\xablt\x8c\xe3\x05]\x11\x1e\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf8\bw\x86\xb4-\xa0N\xd6\xd1\xe0\xfe&\xf6\xc0\xee\xfe\x1e\x9fZ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf8\r6\x19p/\xa5\x83\x8cH9\x18Y\xa89\xfb\x9c\xe7\x16\x0f\x89l\a\xa7\u0471np\x00\x00\u07d4\xf8\x14y\x9fm\xdfM\xcb)\xc7\xee\x87\x0eu\xf9\xcc-52m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf8\x15\xc1\n\x03-\x13\xc3K\x89v\xfan;\xd2\xc9\x13\x1a\x8b\xa9\x89Hz\x9a0E9D\x00\x00\u07d4\xf8\x16\"\xe5WW\xda\xeafu\x97]\xd958\xda}\x16\x99\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8$\xee3\x1eJ\xc3\xccXv\x939[W\xec\xf6%\xa6\xc0\u0089V\xc9]\xe8\xe8\xca\x1d\x00\x00\u07d4\xf8'\xd5n\xd2\xd3' \u052b\xf1\x03\xd6\xd0\xefM;\xcdU\x9b\x89\x01l\x80\x06W\x91\xa2\x80\x00\u07d4\xf8)\x85\x91R>P\xb1\x03\xf0\xb7\x01\xd6#\xcb\xf0\xf7EV\xf6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf8H\xfc\xe9\xaba\x1c}\x99 n#\xfa\u019a\u0508\xb9O\xe1\x89\x02\xa1\x12\x9d\t6r\x00\x00\u07d4\xf8O\t\n\xdf?\x8d\xb7\u1533P\xfb\xb7u\x00i\x9ff\xfd\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf8Q\xb0\x10\xf63\xc4\n\xf1\xa8\xf0js\ubeabe\az\xb5\x89\xee\x86D/\xcd\x06\xc0\x00\x00\u07d4\xf8X\x17\x1a\x04\xd3W\xa1;IA\xc1n~U\xdd\u0514\x13)\x89\x02F\xa5!\x8f*\x00\x00\x00\u07d4\xf8[\xab\x1c\xb3q\x0f\xc0_\xa1\x9f\xfa\xc2.gR\x1a\v\xa2\x1d\x89l\x955\u007f\xa6\xb3l\x00\x00\u07d4\xf8j>\xa8\a\x1fp\x95\xc7\u06ca\x05\xaePz\x89)\u06f8v\x89\x126\xef\xcb\u02f3@\x00\x00\u07d4\xf8pL\x16\xd2\xfd[\xa3\xa2\xc0\x1d\x0e\xb2\x04\x84\xe6\xec\xfa1\t\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf8p\x99_\xe1\xe5\"2\x1duC7\xa4\\\f\x9d{8\x95\x1c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf8s\xe5ze\xc9;n\x18\xcbu\xf0\xdc\a}[\x893\xdc\\\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xf8ua\x9d\x8a#\xe4]\x89\x98\u0444\u0500\xc0t\x89p\x82*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf8{\xb0{(\x9d\xf70\x1eT\xc0\xef\xdaj,\xf2\x91\xe8\x92\x00\x89K\xe4\xe7&{j\xe0\x00\x00\u0794\xf8\x89\x00\xdbsyU\xb1Q\x9b\x1a}\x17\n\x18\x86L\xe5\x90\xeb\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xf8\x8bX\xdb7B\vFL\v\xe8\x8bE\xee+\x95)\x0f\x8c\xfa\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf8\x96+u\xdb]$\xc7\xe8\xb7\xce\xf1\x06\x8c>g\u03bb0\xa5\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xf8\xa0e\xf2\x87\xd9\x1dw\xcdbj\xf3\x8f\xfa\"\r\x9bU*+\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xf8\xa4\x9c\xa29\f\x1fm\\\x0ebQ;\a\x95qt?|\u0189\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf8\xa5\f\xee.h\x8c\xee\u3b24\u0522\x97%\xd4\a,\u0103\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xacJ9\xb5<\x110x \x97;D\x13e\xcf\xfeYof\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xae\x85{g\xa4\xa2\x89:?\xbe|z\x87\xff\x1c\x01\u01a6\xe7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf8\xbf\x9c\x04\x87NZw\xf3\x8fL8R~\x80\xc6v\xf7\xb8\x87\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xc7\xf3J8\xb3\x18\x01\xdaC\x064w\xb1+'\xd0\xf2\x03\xff\x89\x1a\u04ba\xbao\xefH\x00\x00\u07d4\xf8\xca3l\x8e\x91\xbd \xe3\x14\xc2\v-\xd4`\x8b\x9c\x8b\x94Y\x89-\u071b\u0173,x\x00\x00\u07d4\xf8\xd1t$\xc7g\xbe\xa3\x12\x05s\x9a+W\xa7'r\x14\uef89\x02F\xdd\xf9yvh\x00\x00\u07d4\xf8\xd5-\xcc_\x96\xcc(\x00{>\u02f4\t\xf7\xe2*dl\xaa\x89\b\x16\x90\xe1\x81(H\x00\x00\u07d4\xf8\xdc\xe8g\xf0\xa3\x9c[\xef\x9e\xeb\xa6\t\"\x9e\xfa\x02g\x8bl\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xf2&\x14*B\x844\xab\x17\xa1\x86J%\x97\xf6J\xab/\x06\x89\tY\x8b/\xb2\xe9\xf2\x80\x00\u07d4\xf8\xf6d^\r\xeedK=\xad\x81\xd5q\uf6ef\x84\x00!\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\x01\xc0\x0f\xc1\u06c8\xb6\x9cK\xc3%+\\\xa7\x0e\xa6\xee\\\xf6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9=[\xcb\x06D\xb0\xcc\xe5\xfc\u0763C\xf5\x16\x8f\xfa\xb2\x87}\x89\vb\a\xb6}&\xf9\x00\x00\u07d4\xf9W\x0e\x92L\x95\u07bbpa6\x97\x92\xcf.\xfe\u00a8-^\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf9d \x86\xb1\xfb\xaea\xa6\x80M\xbe_\xb1^\xc2\u04b57\xf4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9d\x88i\x85\x90\xdc;,UVB\xb8q4\x8d\xfa\x06z\u0549\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9d\u064d(\x170\xba5\xb2\xe3\xa3\x14yn{B\xfe\xdfg\x89S\xb0\x87`\x98\xd8\f\x00\x00\u07d4\xf9e\ri\x89\xf1\x99\xab\x1c\xc4ycm\xed0\xf2A\x02\x1fe\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\xe0\x94\xf9h\x83X$Y\x90\x8c\x82v'\xe8o(\xe6F\xf9\xc7\xfcz\x8a\x01\u0127\x877\xcd\u03f8\x00\x00\u07d4\xf9kL\x00voSsj\x85t\xf8\"\xe6GL/!\xda-\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9r\x9dH(,\x9e\x87\x16m^\xef-\x01\xed\xa9\xdb\xf7\x88!\x89\x05k\x83\xdd\xc7(T\x80\x00\u07d4\xf9v~N\xcbJY\x80Ru\b\u05fe\xc3\xd4^Ld\x9c\x13\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xf9x\xb0%\xb6B3U\\\xc3\xc1\x9a\xda\u007fA\x99\xc94\x8b\xf7\x8aT\xb4\v\x1f\x85+\xda\x00\x00\x00\u07d4\xf9{V\xeb\u0577z\xbc\x9f\xba\u02eb\u0514\xb9\xd2\xc2!\xcd\x03\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf9\x81\x1f\xa1\x9d\xad\xbf\x02\x9f\x8b\xfeV\x9a\xdb\x18\"\x8c\x80H\x1a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf9\x82Ps\fLa\xc5\u007f\x12\x985\xf2h\b\x94yEB\xf3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xf9\x894gr\x99^\xc1\x90o\xaf\xfe\xba*\u007f\xe7\u079ck\xab\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf9\x98\xca4\x11s\nl\xd1\x0etU\xb0A\x0f\xb0\xf6\xd3\xff\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\x9a\xeeDKW\x83\xc0\x93\xcf\xff\xd1\xc4c,\xf9\x90\x9f\xbb\x91\x1d/\x81\x92\xf8B\t\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xf9\xbf\xb5\x9dS\x8a\xfcHt\xd4\xf5\x94\x1b\b\xc9s\x0e8\xe2K\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf9\xdd#\x90\b\x18/\xb5\x19\xfb0\xee\xdd \x93\xfe\xd1c\x9b\xe8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9\u07ba\xec\xb5\xf39\xbe\xeaH\x94\xe5 K\xfa4\r\x06\u007f%\x89ZB\x84Fs\xb1d\x00\x00\xe0\x94\xf9\xe3tG@lA!\x97\xb2\u2bbc\x00\x1dn0\u024c`\x8a\x01\xc4y\xbbCI\xc0\xee\x00\x00\u07d4\xf9\xe7\"/\xaa\xf0\xf4\xda@\xc1\u0124\x0607:\t\xbe\u05f6\x89\x9bO\u0730\x94V$\x00\x00\u07d4\xf9\xec\xe0\"\xbc\xcd,\x924i\x11\xe7\x9d\xd5\x03\x03\xc0\x1e\x01\x88\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfa\x00\xc3v\xe8\x9c\x05\u81c1z\x9d\xd0t\x8d\x96\xf3A\xaa\x89\x89\x10M\r\x00\u04b7\xf6\x00\x00\u07d4\xfa\f\x1a\x98\x8c\x8a\x17\xad5(\xeb(\xb3@\x9d\xaaX\"_&\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xfa\x10_\x1a\x11\xb6\xe4\xb1\xf5`\x12\xa2y\"\xe2\xac-\xa4\x81/\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xfa\x14/\xe4~\u0697\xe6P;8k\x18\xa2\xbe\xdds\u0335\xb1\x89.\x15:\xd8\x15H\x10\x00\x00\u07d4\xfa\x14\xb5f#J\xbe\xe70B\xc3\x1d!qq\x82\u02e1J\xa1\x89\x11\xc7\xea\x16.x \x00\x00\u07d4\xfa\x19\xd6\xf7\xa5\x0fO\a\x98\x93\xd1g\xbf\x14\xe2\x1d\x00s\u0456\x89\x1c\xbb:?\xf0\x8d\b\x00\x00\u07d4\xfa\x1f\x19q\xa7u\xc3PO\xefPy\xf6@\xc2\u013c\xe7\xac\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfa'\x9b\xfd\x87g\xf9V\xbf\u007f\xa0\xbdV`\x16\x8d\xa7V\x86\xbd\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xfa'\xccI\xd0\vl\x98s6\xa8u\xae9\xdaX\xfb\x04\x1b.\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xfa(2\x99`=\x87X\xe8\u02b0\x82\x12],\x8f}DT)\x8a\x01[\xca\xcb\x1e\x05\x01\xae\x80\x00\u07d4\xfa+\xbc\xa1]?\u37ca2\x8e\x91\xf9\r\xa1Oz\xc6%=\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xfa/\u049d\x03\xfe\xe9\xa0x\x93\xdf:&\x9fV\xb7/.\x1ed\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xfa3U2\x85\xa9sq\x9a\r_\x95o\xf8a\xb2\u061e\xd3\x04\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfa:\fK\x90?n\xa5.\xa7\xab{\x88c\xb6\xa6\x16\xadfP\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfa:\x1a\xa4H\x8b5\x1a\xa7V\f\xf5\xeec\n/\xd4\\2\"\x89/\xa4~j\xa74\r\x00\x00\u07d4\xfaA\tq\xad\"\x9c06\xf4\x1a\u03c5/*\u0259(\x19P\x89\u0633\x11\xa8\xdd\xfa|\x00\x00\u07d4\xfaD\xa8U\xe4\x04\xc8m\f\xa8\xef3$%\x1d\xfb4\x9cS\x9e\x89T\"S\xa1&\xce@\x00\x00\xe0\x94\xfaR\x01\xfe\x13B\xaf\x110{\x91B\xa0A$<\xa9./\t\x8a 8\x11j:\xc0C\x98\x00\x00\xe0\x94\xfa`\x86\x8a\xaf\xd4\xffL\\W\x91K\x8e\u054bBWs\u07e9\x8a\x01\xcf\xe5\xc8\b\xf3\x9f\xbc\x00\x00\u07d4\xfag\xb6{O7\xa0\x15\t\x15\x11\x0e\xde\a;\x05\xb8S\xbd\xa2\x89#\x19\xba\x94sq\xad\x00\x00\u07d4\xfah\xe0\xcb>\xdfQ\xf0\xa6\xf2\x11\u0272\xcb^\a<\x9b\xff\xe6\x89\x0f\xc969(\x01\xc0\x00\x00\xe0\x94\xfaj7\xf0\x18\xe9yg\x93\u007f\xc5\xe8a{\xa1\u05c6\xdd_w\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\xfav\x06C[5l\xee%{\xd2\xfc\xd3\xd9\xea\xcb<\xd1\xc4\xe1\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xfaz\xdff\v\x8d\x99\xce\x15\x93=|_\a/<\xbe\xb9\x9d3\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\xfa\x86\xca'\xbf(T\u0648p\x83\u007f\xb6\xf6\xdf\xe4\xbfdS\xfc\x89\x11u~\x85%\xcf\x14\x80\x00\u07d4\xfa\x8c\xf4\xe6'i\x8c]W\x88\xab\xb7\x88\x04\x17\xe7P#\x13\x99\x89\xe6\x1a6\x96\xee\xf6\x10\x00\x00\u07d4\xfa\x8e;\x1f\x13C9\x00s}\xaa\xf1\xf6)\x9cH\x87\xf8[_\x89&\u009eG\u0104L\x00\x00\u07d4\xfa\x9e\xc8\xef\xe0\x86\x86\xfaX\xc1\x813Xr\xbai\x85`\ucac9lj\xccg\u05f1\xd4\x00\x00\u07d4\xfa\xad\x90]\x84|{#A\x8a\xee\xcb\xe3\xad\u06cd\xd3\xf8\x92J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xfa\xae\xba\x8f\xc0\xbb\xdaU<\xa7.0\xef=s.&\xe8 A\x89H\x8d(*\xaf\xc9\xf6\x80\x00\u07d4\xfa\xb4\x87P\r\xf2\x0f\xb8>\xbe\xd9\x16y\x1dV\x17r\xad\xbe\xbf\x89lkLM\xa6\u077e\x00\x00\u07d4\xfa\xc5\u0294u\x80x\xfb\xfc\xcd\x19\xdb5X\xda~\u8827h\x897(\xa6+\r\xcf\xf6\x00\x00\u07d4\xfa\xd9j\xb6\xacv\x8a\xd5\t\x94R\xacGw\xbd\x1aG\xed\u010f\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xfa\xe7g\x19\xd9~\xacA\x87\x04(\xe9@'\x9d\x97\xddW\xb2\xf6\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4\xfa\u8053pG\x89Zf\f\xf2)v\x0f'\xe6h(\xd6C\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xfa\xe9,\x13p\xe9\u115a]\xf8;V\xd0\xf5\x86\xaa;@L\x89\x05\u0174\xf3\xd8C\x98\x00\x00\xe0\x94\xfa\xf5\xf0\xb7\xb6\xd5X\xf5\t\r\x9e\xa1\xfb-B%\x9cX`x\x8a\x01Z\xff\xb8B\fkd\x00\x00\xe0\x94\xfb\x12o\x0e\xc7i\xf4\x9d\xce\xfc\xa2\xf2\x00(dQX0\x84\xb8\x8a\x01\x0f\xcb\xc25\x03\x96\xbf\x00\x00\xe0\x94\xfb\x13^\xb1Z\x8b\xacr\xb6\x99\x154*`\xbb\xc0k~\a|\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xfb\"<\x1e\"\xea\xc1&\x9b2\xee\x15jS\x85\x92.\xd3o\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb7\xcfkO\x81\xa9\xe2\"\xfb\xa2.\x9b\xd2KP\x98\xb73\u03c9\x02\x1auJm\xc5(\x00\x00\u07d4\xfb8`\xf4\x12\x1cC.\xbd\xc8\xecj\x031\xb1\xb7\ty.\x90\x89 \x8c9J\xf1\u0208\x00\x00\u07d4\xfb9\x18\x9a\xf8v\xe7b\xc7\x1dl>t\x18\x93\xdf\"l\xed\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfb:\v\rkjq\x8fo\xc0)*\x82]\xc9$z\x90\xa5\u0409\n\xd6\xdd\x19\x9e\x97[\x00\x00\xe0\x94\xfb?\xa1\xac\b\xab\xa9\xcc;\xf0\xfe\x9dH8 h\x8fe\xb4\x10\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xfb?\xe0\x9b\xb86\x86\x15)\xd7Q\x8d\xa2v5\xf58PV\x15\x89K\xe3\x92\x16\xfd\xa0p\x00\x00\xe0\x94\xfbQ%\xbf\x0f^\xb0\xb6\xf0 \xe5k\xfc/\xdf=@,\t~\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\xfbU\x18qL\xef\xc3m\x04\x86]\xe5\x91^\xf0\xffG\xdf\xe7C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb_\xfa\xa0\xf7aW&5x\x91GX\x18\x93\x9d 7\u03d6\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfbh\\\x15\xe49\x96^\xf6&\xbf\r\x83L\u0468\x9f+V\x95\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xfbtK\x95\x1d\tK1\x02b\xc8\xf9\x86\xc8`\u07da\xb1\xdee\x89\x02\xd1\xc5\x15\xf1\xcbJ\x80\x00\u07d4\xfby\xab\u06d2\\U\xb9\xf9\x8e\xfe\xefd\xcf\xc9\xeba\xf5\x1b\xb1\x89a@\xc0V\xfb\n\xc8\x00\x00\u07d4\xfb\x81\x13\xf9M\x91s\xee\xfdZ0s\xf5\x16\x80:\x10\xb2\x86\xae\x89\x04V9\x18$O@\x00\x00\u07d4\xfb\x84,\xa2\xc5\xef\x139\x17\xa26\xa0\u052c@i\x01\x10\xb08\x89\x10\x96\x9ab\xbe\x15\x88\x00\x00\u07d4\xfb\x91\xfb\x1aiUS\xf0\u018e!'m\xec\xf0\xb89\t\xb8m\x89\x05l\x006\x17\xafx\x00\x00\u07d4\xfb\x94s\xcfw\x125\n\x1f\xa09Rs\xfc\x80V\aR\xe4\xfb\x89\x06\xaf!\x98\xba\x85\xaa\x00\x00\xe0\x94\xfb\x94\x9cd\u007f\xdc\xfd%\x14\xc7\u054e1\xf2\x8aS-\x8cX3\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xfb\xa5HmS\xc6\xe2@IBA\xab\xf8~C\xc7`\rA:\x89k\xbfaIIH4\x00\x00\u07d4\xfb\xb1a\xfe\x87_\t)\nK&+\xc6\x01\x10\x84\x8f\r\"&\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb\xbb\xeb\u03fe#^W\xdd#\x06\xad\x1a\x9e\u0141\xc7\xf9\xf4\x8f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xfb\xc0\x1d\xb5NG\xcd\xc3\xc48iJ\xb7\x17\xa8V\xc2?\xe6\xe9\x8a\x01\xcaqP\xab\x17OG\x00\x00\xe0\x94\xfb\xcf\xccJ{\x0f&\xcf&\xe9\xf33!2\xe2\xfcj#\af\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfb\xe7\x16\"\xbc\xbd1\xc1\xa3iv\xe7\xe5\xf6p\xc0\u007f\xfe\x16\u0789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfb\xed\xe3,4\x9f3\x00\xefL\xd3;M\xe7\xdc\x18\xe4C\xd3&\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\xfb\xf2\x04\xc8\x13\xf86\xd89b\u01c7\fx\b\xca4\u007f\xd3>\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xfb\xf7Y3\xe0\x1bu\xb1T\xef\x06i\ak\xe8\u007fb\xdf\xfa\xe1\x8a\x10\x84cr\xf2I\xd4\xc0\x00\x00\u07d4\xfc\x00\x96\xb2\x1e\x95\xac\xb8\xd6\x19\xd1v\xa4\xa1\xd8\xd5)\xba\xdb\xef\x89\x14\xd9i;\xcb\xec\x02\x80\x00\xe0\x94\xfc\x00\xa4 \xa3a\a\xdf\xd5\xf4\x95\x12\x8a_\u5af2\xdb\x0f4\x8a\x01C\x17\x9d\x86\x91\x10 \x00\x00\xe0\x94\xfc\x01\x8ai\n\xd6tm\xbe:\u03d7\x12\xdd\xcaR\xb6%\x009\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xfc\x02s@3\xe5\u007fpQ~\n\xfc~\xe6$a\xf0o\xad\x8e\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xfc\x0e\xe6\xf7\u00b3qJ\xe9\x91lEVf\x05\xb6V\xf3$A\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xfc\x10\xb7\xa6{2h\xd53\x1b\xfbj\x14\xde\xf5\xeaJ\x16,\xa3\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfc\x15\u02d9\xa8\xd1\x03\v\x12w\n\xdd\x03:y\xee\r\f\x90\x8c\x89\x12\xfa\x00\xbdR\xe6$\x00\x00\u07d4\xfc)R\xb4\u011f\xed\u043c\x05(\xa3\bI^mj\x1cq\u0589lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xfc,\x1f\x88\x96\x1d\x01\x9c>\x9e\xa30\t\x15.\x06\x93\xfb\xf8\x8a\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\xfc6\x11\x05\u0750\xf9\xed\xe5fI\x9di\xe9\x13\x03\x95\xf1*\u020aS\xa4\xfe/ N\x80\xe0\x00\x00\u07d4\xfc7/\xf6\x92|\xb3\x96\xd9\xcf)\x805\x00\x11\r\xa62\xbcR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc9\xbeA\tK\x19\x97\xd2\x16\x9e\x82d\xc2\u00fa\xa6\u025b\u0109lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc=\"k\xb3jX\xf5&V\x88W\xb0\xbb\x12\xd1\t\xec\x93\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfcC\x82\x9a\u01c7\xff\x88\xaa\xf1\x83\xba5*\xad\xbfZ\x15\xb1\x93\x89\u05ac\n+\x05R\xe0\x00\x00\u07d4\xfcI\xc1C\x9aA\u05b3\xcf&\xbbg\xe06R$\xe5\xe3\x8f_\x8966\u05ef^\u024e\x00\x00\u07d4\xfcU\x00\x82Q\x05\xcfq*1\x8a^\x9c;\xfci\u021d\f\x12\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xfcf\xfa\xba'\u007fK]\xe6J\xd4^\xb1\x9c1\xe0\f\xed>\u054a\x011\xbe\xb9%\xff\xd3 \x00\x00\xe0\x94\xfc~\"\xa5\x03\xecZ\xbe\x9b\b\xc5\v\xd1I\x99\xf5 \xfaH\x84\x8a\x01ZG}\xfb\xe1\xea\x14\x80\x00\u07d4\xfc\x82\x15\xa0\xa6\x99\x13\xf6*C\xbf\x1c\x85\x90\xb9\xdd\xcd\r\x8d\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc\x98\x9c\xb4\x87\xbf\x1a}\x17\xe4\xc1\xb7\u0137\xaa\xfd\xdak\n\x8d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xfc\x9b4td\xb2\xf9\x92\x9d\x80~\x03\x9d\xaeH\xd3\u064d\xe3y\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xfc\xa4;\xbc#\xa0\xd3!\xba\x9eF\xb9)s\\\xe7\xd8\xef\f\x18\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfc\xa7>\xff\x87q\xc0\x10;\xa3\xcc\x1a\x9c%\x94H\xc7*\xbf\v\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfc\xad\xa3\x00(?k\xcc\x13J\x91Eg`\xb0\xd7}\xe4\x10\xe0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xfc\xbc\\q\xac\xe7\x97AE\v\x01,\xf6\xb8\xd3\xf1}\xb6\x8ap\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xfc\xbd\x85\xfe\xeajuO\xcf4ID\x9e7\xff\x97\x84\xf7w<\x89\xa7J\xdai\xab\xd7x\x00\x00\xe0\x94\xfc\xc9\u0524&.z\x02z\xb7Q\x91\x10\xd8\x02\u0115\xce\xea9\x8a\x01YQ\x82\"K&H\x00\x00\xe0\x94\xfc\xcd\r\x1e\xce\xe2z\xdd\xea\x95\xf6\x85z\xee\xc8\u01e0K(\xee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xfc\u0434\x82|\xd2\b\xff\xbf^u\x9d\xba\x8c<\xc6\x1d\x8c,<\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfc\xe0\x89c\\\xe9z\xba\xc0kD\x81\x9b\xe5\xbb\n>.\v7\x89\x05\x03\x92\nv0\xa7\x80\x00\u07d4\xfc\xf1\x99\xf8\xb8T\"/\x18.N\x1d\t\x9dN2>*\xae\x01\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfc\xfc:P\x04\xd6xa?\v6\xa6B&\x9a\u007f7\x1c?j\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfd\x19\x1a5\x15}x\x13s\xfbA\x1b\xf9\xf2R\x90\x04|^\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfd\x1f\xaa4{\x0f\u0300L-\xa8l6\xd5\xf1\u044bp\x87\xbb\x89\x02\xd6\xeb$z\x96\xf6\x00\x00\u07d4\xfd\x1f\xb5\xa8\x9a\x89\xa7!\xb8yph\xfb\xc4\u007f>\x9dR\xe1I\x89\f\u0435\x83\u007f\xc6X\x00\x00\u07d4\xfd OOJ\xba%%\xbar\x8a\xfd\xf7\x87\x92\xcb\u07b75\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd'W\xcc5Q\xa0\x95\x87\x8d\x97\x87V\x15\xfe\fj2\xaa\x8a\x89 m\xb1R\x99\xbe\xac\x00\x00\u07d4\xfd(r\u045eW\x85<\xfa\x16\xef\xfe\x93\u0431\xd4{O\x93\xfb\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfd))'\x1e\x9d \x95\xa2dv~{\r\xf5.\xa0\xd1\xd4\x00\x89\xa2\xa1\xeb%\x1bZ\xe4\x00\x00\u07d4\xfd7z8Rr\x90\f\xb46\xa3\xbbyb\xcd\xff\xe9?]\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd@$+\xb3Jp\x85^\xf0\xfd\x90\xf3\x80-\xec!6\xb3'\x89h\xa8u\a>)$\x00\x00\xe0\x94\xfdE,9i\xec\xe3\x80\x1cT \xf1\xcd\u02a1\xc7\x1e\xd2=\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xfdKU\x1fo\xdb\u0366\xc5\x11\xb5\xbb7\"P\xa6\xb7\x83\xe54\x89\x01\x1d\xe1\xe6\xdbE\f\x00\x00\u07d4\xfdK\x98\x95X\xae\x11\xbe\f;6\xe2\xd6\xf2\xa5J\x93C\xca.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfdM\xe8\xe3t\x8a(\x9c\xf7\xd0`Q}\x9d88\x8d\xb0\x1f\xb8\x89\r\x8drkqw\xa8\x00\x00\u07d4\xfdZc\x15\u007f\x91O\u04d8\uac5c\x13}\xd9U\v\xb7q\\\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xfd`\u04b5\xaf=5\xf7\xaa\xf0\u00d3\x05.y\xc4\xd8#\u0645\x89\x03\x0e\xb5\r.\x14\b\x00\x00\u07d4\xfdhm\xe5?\xa9\u007f\x99c\x9e%hT\x97 \xbcX\x8c\x9e\xfc\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xfd~\u078fR@\xa0eA\xebi\x9dx,/\x9a\xfb!p\xf6\x89Hz\x9a0E9D\x00\x00\u07d4\xfd\x81+\u019f\xb1p\xefW\xe22~\x80\xaf\xfd\x14\xf8\xe4\xb6\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\x88\xd1\x14\"\x0f\b\x1c\xb3\xd5\xe1[\xe8\x15*\xb0sfWj\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xfd\x91\x856\xa8\xef\xa6\xf6\xce\xfe\x1f\xa1\x159\x95\xfe\xf5\xe3=;\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xfd\x92\x0fr&\x82\xaf\xb5\xafE\x1b\x05D\xd4\xf4\x1b;\x9dWB\x89~R\x05j\x12?<\x00\x00\u07d4\xfd\x95y\xf1\x19\xbb\xc8\x19\xa0+a\u3348\x03\xc9B\xf2M2\x89\x05\xb9~\x90\x81\xd9@\x00\x00\u07d4\xfd\xa0\xce\x153\a\a\xf1\v\xce2\x01\x17- \x18\xb9\xdd\xeat\x89\x02\xd0A\xd7\x05\xa2\xc6\x00\x00\xe0\x94\xfd\xa3\x04(\x19\xaf>f)\x00\xe1\xb9+CX\xed\xa6\xe9%\x90\x8a\x19\a\xa2\x84\u054fc\xe0\x00\x00\u07d4\xfd\xa6\x81\x0e\xa5\xac\x98]o\xfb\xf1\xc5\x11\xf1\xc1B\xed\xcf\xdd\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfd\xb39D\xf26\x06\x15\xe5\xbe#\x95w\u0221\x9b\xa5-\x98\x87\x89 \x9d\x92/RY\xc5\x00\x00\u07d4\xfd\xbaSY\xf7\xec;\xc7p\xacI\x97]\x84N\xc9qbV\xf1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xfd\xc4\xd4vZ\x94/[\xf9i1\xa9\xe8\xccz\xb8\xb7W\xffL\x8a\x12lG\x8a\x0e>\xa8`\x00\x00\xe0\x94\xfd\xcd]\x80\xb1\x05\x89zW\xab\xc4xev\x8b)\x00RB\x95\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u0794\xfd\xd1\x19_y}O5q}\x15\xe6\xf9\x81\n\x9a?\xf5T`\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xfd\xd5\x02\xa7N\x81;\u03e3U\xce\xda<\x17ojhq\xaf\u007f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfd\u357c\vm\\\xbbL\x1d\x8f\xea>\vK\xffc^\x9d\xb7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\xea\xac*\xcf\x1d\x13\x8e\x19\xf2\xfc?\x9f\xb7E\x92\xe3\ud04a\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xfd\xec\xc8-\xdf\xc5a\x92\xe2oV<=h\xcbTJ\x96\xbf\xed\x89\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4\xfd\xf4#C\x01\x9b\v\fk\xf2`\xb1s\xaf\xab~E\xb9\xd6!\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xfd\xf4I\xf1\b\xc6\xfbOZ+\b\x1e\xed~E\u645eM%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\xfda4\xc0J\x8a\xb7\xeb\x16\xf0\x06C\xf8\xfe\xd7\u06aa\ucc89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfe\x00\xbfC\x99\x11\xa5S\x98-\xb68\x03\x92E\xbc\xf02\xdb\u0709\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xfe\x01n\xc1~\xc5\xf1\x0e;\xb9\x8f\xf4\xa1\xed\xa0E\x15v\x82\xab\x89\x14_T\x02\xe7\xb2\xe6\x00\x00\u07d4\xfe\x0e0\xe2\x14)\rt=\xd3\x0e\xb0\x82\xf1\xf0\xa5\"Z\xdea\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfe!\v\x8f\x04\xdcmOv!j\xcf\xcb\u055b\xa8;\xe9\xb60\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfe\"\xa0\xb3\x88f\x8d\x1a\xe2d>w\x1d\xac\xf3\x8aCB#\u0309\xd8\xdb^\xbd{&8\x00\x00\u07d4\xfe6&\x88\x84_\xa2D\u0300~K\x110\xeb7A\xa8\x05\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfe8'\xd5v0\u03c7a\xd5\x12y{\v\x85\x8eG\x8b\xbd\x12\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfeA\x8bB\x1a\x9cm76\x02y\x04u\xd20>\x11\xa7Y0\x897\b\xba\xed=h\x90\x00\x00\u07d4\xfeBI\x12yP\xe2\xf8\x96\xec\x0e~.=\x05Z\xab\x10U\x0f\x89$=M\x18\"\x9c\xa2\x00\x00\xe0\x94\xfeM\x84\x03!o\xd5qW+\xf1\xbd\xb0\x1d\x00W\x89x\u0588\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xfeS\xb9I\x89\u0619d\xda aS\x95&\xbb\xe9y\xdd.\xa9\x89h\xa8u\a>)$\x00\x00\u07d4\xfeT\x9b\xbf\xe6G@\x18\x98\x92\x93%8\u06afF\u04b6\x1dO\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xfea]\x97\\\b\x87\xe0\xc9\x11>\xc7)\x84 \xa7\x93\xaf\x8b\x96\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfee\xc4\x18\x8dy\"Wi\td D\xfd\xc5#\x95V\x01e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfei\u007f\xf2,\xa5G\xbf\xc9^3\xd9`\xda`\\gc\xf3[\x89G\xd4\x11\x9f\xd9`\x94\x00\x00\u07d4\xfej\x89[y\\\xb4\xbf\x85\x90=<\xe0\x9cZ\xa49S\u04ff\x89\xb8Pz\x82\a( \x00\x00\u07d4\xfeo_B\xb6\x19;\x1a\xd1b\x06\u4bf5#\x9dM}\xb4^\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xfep\x11\xb6\x98\xbf3q\x13-tE\xb1\x9e\xb5\xb0\x945j\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x80\xe9#-\xea\xff\x19\xba\xf9\x98i\x88:K\xdf\x00\x04\xe5<\x89.b\xf2\ni\xbe@\x00\x00\u07d4\xfe\x8en6eW\r\xffz\x1b\xdaiz\xa5\x89\xc0\xb4\xe9\x02J\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x8f\x1f\u072b\u007f\xbe\u0266\xa3\xfc\xc5\aa\x96\x00P\\6\xa3\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xfe\x91\xec\xcf+\xd5f\xaf\xa1\x16\x96\xc5\x04\x9f\xa8Lic\nR\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xfe\x96\xc4\xcd8\x15b@\x1a\xa3*\x86\xe6[\x9dR\xfa\x8a\xee'\x89\x8f\x1d\\\x1c\xae7@\x00\x00\u07d4\xfe\x98\xc6d\xc3\xe4G\xa9^i\xbdX!q\xb7\x17n\xa2\xa6\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfe\x9a\xd1.\xf0]m\x90&\x1f\x96\xc84\n\x03\x81\x97M\xf4w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x9c\x0f\xff\xef\xb8\x03\b\x12V\xc0\xcfMfY\xe6\xd3>\xb4\xfb\x89R\xd5B\x80O\x1c\xe0\x00\x00\u07d4\xfe\x9c\xfc;\xb2\x93\u0772\x85\xe6%\xf3X/t\xa6\xb0\xa5\xa6\u0349j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xfe\x9e\x11\x97\u05d7JvH\xdc\u01e01\x12\xa8\x8e\xdb\xc9\x04]\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\xe0\x94\xfe\xac\xa2\xactbK\xf3H\xda\u0258QC\xcf\xd6R\xa4\xbeU\x8a\x05\x89\u007f\u02f0)\x14\b\x80\x00\u07d4\xfe\xad\x18\x03\xe5\xe77\xa6\x8e\x18G-\x9a\xc7\x15\xf0\x99L\u00be\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xfe\xb8\xb8\xe2\xafqj\xe4\x1f\xc7\xc0K\xcf)T\x01VF\x1ek\x89TQt\xa5(\xa7z\x00\x00\u07d4\xfe\xb9-0\xbf\x01\xff\x9a\x19\x01flUsS+\xfa\a\xee\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfe\xbc1s\xbc\x90r\x13cT\x00+{O\xb3\xbf\xc5?\"\xf1\x89\x14\x0e\xc8\x0f\xa7\xee\x88\x00\x00\u07d4\xfe\xbdH\xd0\xff\xdb\xd5el\xd5\xe6\x866:a\x14R(\xf2y\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xfe\xbd\x9f\x81\xcfx\xbd_\xb6\u0139\xa2K\xd4\x14\xbb\x9b\xfaLN\x89k\xe1\x0f\xb8\xedn\x13\x80\x00\u07d4\xfe\xc0o\xe2{D\u01c4\xb29n\xc9/{\x92:\xd1~\x90w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\xc1NT\x85\xde+>\xef^t\xc4aF\u06ceEN\x035\x89\t\xb4\x1f\xbf\x9e\n\xec\x00\x00\u07d4\xfe\xd8Gm\x10\u0544\xb3\x8b\xfag7`\x0e\xf1\x9d5\xc4\x1e\u0609b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xfe\xef;n\xab\xc9J\xff\xd31\f\x1cM\x0ee7^\x13\x11\x19\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfe\xf0\x9dp$?9\xed\x8c\xd8\x00\xbf\x96QG\x9e\x8fJ\xca<\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfe\xf3\xb3\u07ad\x1ai&\u051a\xa3+\x12\xc2*\xf5M\x9f\xf9\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xff\v|\xb7\x1d\xa9\xd4\xc1\xean\xcc(\xeb\xdaPLc\xf8/\u04498\x8a\x88]\xf2\xfcl\x00\x00\u07d4\xff\f\xc6\u73c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xff'&)AH\xb8lx\xa97$\x97\xe4Y\x89\x8e\xd3\xfe\xe3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xff=\xedz@\u04ef\xf0\u05e8\xc4_\xa6\x13j\xa0C=\xb4W\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xff>\xeeW\xc3Mm\xae\x97\r\x8b1\x11\x17\xc55\x86\xcd5\x02\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xff>\xf6\xba\x15\x1c!\xb5\x99\x86\xaed\xf6\xe8\"\x8b\u0262\xc73\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xffA\xd9\xe1\xb4\xef\xfe\x18\u0630\xd1\xf6?\xc4%_\xb4\xe0l=\x89Hz\x9a0E9D\x00\x00\u07d4\xffE\xcb4\xc9(6M\x9c\xc9\u063b\x0074ta\x8f\x06\xf3\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xffI\xa7u\x81N\xc0\x00Q\xa7\x95\xa8u\xde$Y.\xa4\x00\u050a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xffJ@\x8fP\xe9\xe7!F\xa2\x8c\xe4\xfc\x8d\x90'\x1f\x11n\x84\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xffM\x9c\x84\x84\xc4\x10T\x89H\xa4\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\x7f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\u0791i\x16\xa8{\x823?BE\x04f#\xb27\x94\xc6\\\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94\v\xe9I\x92\x8f\xf1\x99\xc9\xeb\xa9\xe1\x10\xdb!\n\xa5\xc9N\xfa\u040b|\x13\xbcK,\x13\x8e\u0344h\xa0\x03\x7f\x05\x8a\x9d\xaf\xady\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xf9!2\x94BBBBBBBBBBBBBBBBBBBB\x80\xf9!\x19\x80\xb9\x18\xd6`\x80`@R`\x046\x10a\x00?W`\x005`\xe0\x1c\x80c\x01\xff\u0267\x14a\x00DW\x80c\"\x89Q\x18\x14a\x00\xa4W\x80cb\x1f\xd10\x14a\x01\xbaW\x80c\xc5\xf2\x89/\x14a\x02DW[`\x00\x80\xfd[4\x80\x15a\x00PW`\x00\x80\xfd[Pa\x00\x90`\x04\x806\x03` \x81\x10\x15a\x00gW`\x00\x80\xfd[P5\x7f\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16a\x02kV[`@\x80Q\x91\x15\x15\x82RQ\x90\x81\x90\x03` \x01\x90\xf3[a\x01\xb8`\x04\x806\x03`\x80\x81\x10\x15a\x00\xbaW`\x00\x80\xfd[\x81\x01\x90` \x81\x01\x815d\x01\x00\x00\x00\x00\x81\x11\x15a\x00\xd5W`\x00\x80\xfd[\x82\x01\x83` \x82\x01\x11\x15a\x00\xe7W`\x00\x80\xfd[\x805\x90` \x01\x91\x84`\x01\x83\x02\x84\x01\x11d\x01\x00\x00\x00\x00\x83\x11\x17\x15a\x01\tW`\x00\x80\xfd[\x91\x93\x90\x92\x90\x91` \x81\x01\x905d\x01\x00\x00\x00\x00\x81\x11\x15a\x01'W`\x00\x80\xfd[\x82\x01\x83` \x82\x01\x11\x15a\x019W`\x00\x80\xfd[\x805\x90` \x01\x91\x84`\x01\x83\x02\x84\x01\x11d\x01\x00\x00\x00\x00\x83\x11\x17\x15a\x01[W`\x00\x80\xfd[\x91\x93\x90\x92\x90\x91` \x81\x01\x905d\x01\x00\x00\x00\x00\x81\x11\x15a\x01yW`\x00\x80\xfd[\x82\x01\x83` \x82\x01\x11\x15a\x01\x8bW`\x00\x80\xfd[\x805\x90` \x01\x91\x84`\x01\x83\x02\x84\x01\x11d\x01\x00\x00\x00\x00\x83\x11\x17\x15a\x01\xadW`\x00\x80\xfd[\x91\x93P\x91P5a\x03\x04V[\x00[4\x80\x15a\x01\xc6W`\x00\x80\xfd[Pa\x01\xcfa\x10\xb5V[`@\x80Q` \x80\x82R\x83Q\x81\x83\x01R\x83Q\x91\x92\x83\x92\x90\x83\x01\x91\x85\x01\x90\x80\x83\x83`\x00[\x83\x81\x10\x15a\x02\tW\x81\x81\x01Q\x83\x82\x01R` \x01a\x01\xf1V[PPPP\x90P\x90\x81\x01\x90`\x1f\x16\x80\x15a\x026W\x80\x82\x03\x80Q`\x01\x83` \x03a\x01\x00\n\x03\x19\x16\x81R` \x01\x91P[P\x92PPP`@Q\x80\x91\x03\x90\xf3[4\x80\x15a\x02PW`\x00\x80\xfd[Pa\x02Ya\x10\xc7V[`@\x80Q\x91\x82RQ\x90\x81\x90\x03` \x01\x90\xf3[`\x00\x7f\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x82\x16\x7f\x01\xff\u0267\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x80a\x02\xfeWP\x7f\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x82\x16\x7f\x85d\t\a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14[\x92\x91PPV[`0\x86\x14a\x03]W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`&\x81R` \x01\x80a\x18\x05`&\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[` \x84\x14a\x03\xb6W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`6\x81R` \x01\x80a\x17\x9c`6\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[``\x82\x14a\x04\x0fW`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`)\x81R` \x01\x80a\x18x`)\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[g\r\u0db3\xa7d\x00\x004\x10\x15a\x04pW`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`&\x81R` \x01\x80a\x18R`&\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[c;\x9a\xca\x004\x06\x15a\x04\xcdW`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`3\x81R` \x01\x80a\x17\xd2`3\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[c;\x9a\xca\x004\x04g\xff\xff\xff\xff\xff\xff\xff\xff\x81\x11\x15a\x055W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`'\x81R` \x01\x80a\x18+`'\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[``a\x05@\x82a\x14\xbaV[\x90P\x7fd\x9b\xbcb\xd0\xe3\x13B\xaf\xeaN\\\xd8-@I\xe7\xe1\xee\x91/\xc0\x88\x9a\xa7\x90\x80;\xe3\x908\u0149\x89\x89\x89\x85\x8a\x8aa\x05u` Ta\x14\xbaV[`@\x80Q`\xa0\x80\x82R\x81\x01\x89\x90R\x90\x81\x90` \x82\x01\x90\x82\x01``\x83\x01`\x80\x84\x01`\xc0\x85\x01\x8e\x8e\x80\x82\x847`\x00\x83\x82\x01R`\x1f\x01\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x16\x90\x91\x01\x87\x81\x03\x86R\x8c\x81R` \x01\x90P\x8c\x8c\x80\x82\x847`\x00\x83\x82\x01\x81\x90R`\x1f\x90\x91\x01\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x16\x90\x92\x01\x88\x81\x03\x86R\x8cQ\x81R\x8cQ` \x91\x82\x01\x93\x91\x8e\x01\x92P\x90\x81\x90\x84\x90\x84\x90[\x83\x81\x10\x15a\x06HW\x81\x81\x01Q\x83\x82\x01R` \x01a\x060V[PPPP\x90P\x90\x81\x01\x90`\x1f\x16\x80\x15a\x06uW\x80\x82\x03\x80Q`\x01\x83` \x03a\x01\x00\n\x03\x19\x16\x81R` \x01\x91P[P\x86\x81\x03\x83R\x88\x81R` \x01\x89\x89\x80\x82\x847`\x00\x83\x82\x01\x81\x90R`\x1f\x90\x91\x01\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x16\x90\x92\x01\x88\x81\x03\x84R\x89Q\x81R\x89Q` \x91\x82\x01\x93\x91\x8b\x01\x92P\x90\x81\x90\x84\x90\x84\x90[\x83\x81\x10\x15a\x06\xefW\x81\x81\x01Q\x83\x82\x01R` \x01a\x06\xd7V[PPPP\x90P\x90\x81\x01\x90`\x1f\x16\x80\x15a\a\x1cW\x80\x82\x03\x80Q`\x01\x83` \x03a\x01\x00\n\x03\x19\x16\x81R` \x01\x91P[P\x9dPPPPPPPPPPPPPP`@Q\x80\x91\x03\x90\xa1`\x00`\x02\x8a\x8a`\x00`\x80\x1b`@Q` \x01\x80\x84\x84\x80\x82\x847\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x90\x94\x16\x91\x90\x93\x01\x90\x81R`@\x80Q\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x81\x84\x03\x01\x81R`\x10\x90\x92\x01\x90\x81\x90R\x81Q\x91\x95P\x93P\x83\x92P` \x85\x01\x91P\x80\x83\x83[` \x83\x10a\a\xfcW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\a\xbfV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\bYW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\bnW`\x00\x80\xfd[PQ\x90P`\x00`\x02\x80a\b\x84`@\x84\x8a\x8ca\x16\xfeV[`@Q` \x01\x80\x83\x83\x80\x82\x847\x80\x83\x01\x92PPP\x92PPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\b\xf8W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\b\xbbV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\tUW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\tjW`\x00\x80\xfd[PQ`\x02a\t{\x89`@\x81\x8da\x16\xfeV[`@Q`\x00\x90` \x01\x80\x84\x84\x80\x82\x847\x91\x90\x91\x01\x92\x83RPP`@\x80Q\x80\x83\x03\x81R` \x92\x83\x01\x91\x82\x90R\x80Q\x90\x94P\x90\x92P\x82\x91\x84\x01\x90\x80\x83\x83[` \x83\x10a\t\xf4W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\t\xb7V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\nQW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\nfW`\x00\x80\xfd[PQ`@\x80Q` \x81\x81\x01\x94\x90\x94R\x80\x82\x01\x92\x90\x92R\x80Q\x80\x83\x03\x82\x01\x81R``\x90\x92\x01\x90\x81\x90R\x81Q\x91\x92\x90\x91\x82\x91\x84\x01\x90\x80\x83\x83[` \x83\x10a\n\xdaW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\n\x9dV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\v7W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\vLW`\x00\x80\xfd[PQ`@\x80Q` \x81\x01\x85\x81R\x92\x93P`\x00\x92`\x02\x92\x83\x92\x87\x92\x8f\x92\x8f\x92\x01\x83\x83\x80\x82\x847\x80\x83\x01\x92PPP\x93PPPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\v\xd9W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\v\x9cV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\f6W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\fKW`\x00\x80\xfd[PQ`@Q\x86Q`\x02\x91\x88\x91`\x00\x91\x88\x91` \x91\x82\x01\x91\x82\x91\x90\x86\x01\x90\x80\x83\x83[` \x83\x10a\f\xa9W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\flV[`\x01\x83` \x03a\x01\x00\n\x03\x80\x19\x82Q\x16\x81\x84Q\x16\x80\x82\x17\x85RPPPPPP\x90P\x01\x83g\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16g\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x81R`\x18\x01\x82\x81R` \x01\x93PPPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\rNW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\r\x11V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\r\xabW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\r\xc0W`\x00\x80\xfd[PQ`@\x80Q` \x81\x81\x01\x94\x90\x94R\x80\x82\x01\x92\x90\x92R\x80Q\x80\x83\x03\x82\x01\x81R``\x90\x92\x01\x90\x81\x90R\x81Q\x91\x92\x90\x91\x82\x91\x84\x01\x90\x80\x83\x83[` \x83\x10a\x0e4W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\r\xf7V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x0e\x91W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x0e\xa6W`\x00\x80\xfd[PQ\x90P\x85\x81\x14a\x0f\x02W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`T\x81R` \x01\x80a\x17H`T\x919``\x01\x91PP`@Q\x80\x91\x03\x90\xfd[` Tc\xff\xff\xff\xff\x11a\x0f`W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`!\x81R` \x01\x80a\x17'`!\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[` \x80T`\x01\x01\x90\x81\x90U`\x00[` \x81\x10\x15a\x10\xa9W\x81`\x01\x16`\x01\x14\x15a\x0f\xa0W\x82`\x00\x82` \x81\x10a\x0f\x91W\xfe[\x01UPa\x10\xac\x95PPPPPPV[`\x02`\x00\x82` \x81\x10a\x0f\xafW\xfe[\x01T\x84`@Q` \x01\x80\x83\x81R` \x01\x82\x81R` \x01\x92PPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\x10%W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x0f\xe8V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x10\x82W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x10\x97W`\x00\x80\xfd[PQ\x92P`\x02\x82\x04\x91P`\x01\x01a\x0fnV[P\xfe[PPPPPPPV[``a\x10\xc2` Ta\x14\xbaV[\x90P\x90V[` T`\x00\x90\x81\x90\x81[` \x81\x10\x15a\x12\xf0W\x81`\x01\x16`\x01\x14\x15a\x11\xe6W`\x02`\x00\x82` \x81\x10a\x10\xf5W\xfe[\x01T\x84`@Q` \x01\x80\x83\x81R` \x01\x82\x81R` \x01\x92PPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\x11kW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x11.V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x11\xc8W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x11\xddW`\x00\x80\xfd[PQ\x92Pa\x12\xe2V[`\x02\x83`!\x83` \x81\x10a\x11\xf6W\xfe[\x01T`@Q` \x01\x80\x83\x81R` \x01\x82\x81R` \x01\x92PPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\x12kW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x12.V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x12\xc8W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x12\xddW`\x00\x80\xfd[PQ\x92P[`\x02\x82\x04\x91P`\x01\x01a\x10\xd1V[P`\x02\x82a\x12\xff` Ta\x14\xbaV[`\x00`@\x1b`@Q` \x01\x80\x84\x81R` \x01\x83\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\x13ZW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x13\x1dV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x95\x90\x95\x16\x92\x01\x91\x82RP`@\x80Q\x80\x83\x03\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x01\x81R`\x18\x90\x92\x01\x90\x81\x90R\x81Q\x91\x95P\x93P\x83\x92\x85\x01\x91P\x80\x83\x83[` \x83\x10a\x14?W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x14\x02V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x14\x9cW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x14\xb1W`\x00\x80\xfd[PQ\x92PPP\x90V[`@\x80Q`\b\x80\x82R\x81\x83\x01\x90\x92R``\x91` \x82\x01\x81\x806\x837\x01\x90PP\x90P`\xc0\x82\x90\x1b\x80`\a\x1a`\xf8\x1b\x82`\x00\x81Q\x81\x10a\x14\xf4W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x06\x1a`\xf8\x1b\x82`\x01\x81Q\x81\x10a\x157W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x05\x1a`\xf8\x1b\x82`\x02\x81Q\x81\x10a\x15zW\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x04\x1a`\xf8\x1b\x82`\x03\x81Q\x81\x10a\x15\xbdW\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x03\x1a`\xf8\x1b\x82`\x04\x81Q\x81\x10a\x16\x00W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x02\x1a`\xf8\x1b\x82`\x05\x81Q\x81\x10a\x16CW\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x01\x1a`\xf8\x1b\x82`\x06\x81Q\x81\x10a\x16\x86W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x00\x1a`\xf8\x1b\x82`\a\x81Q\x81\x10a\x16\xc9W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SPP\x91\x90PV[`\x00\x80\x85\x85\x11\x15a\x17\rW\x81\x82\xfd[\x83\x86\x11\x15a\x17\x19W\x81\x82\xfd[PP\x82\x01\x93\x91\x90\x92\x03\x91PV\xfeDepositContract: merkle tree fullDepositContract: reconstructed DepositData does not match supplied deposit_data_rootDepositContract: invalid withdrawal_credentials lengthDepositContract: deposit value not multiple of gweiDepositContract: invalid pubkey lengthDepositContract: deposit value too highDepositContract: deposit value too lowDepositContract: invalid signature length\xa2dipfsX\"\x12 \x1d\xd2o7\xa6!p0\t\xab\xf1nw\u6713\xdcP\u01dd\xb7\xf6\xcc7T>>\x0e=\xec\u0717dsolcC\x00\x06\v\x003\xf9\b<\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\xa0\xf5\xa5\xfdB\xd1j 0'\x98\xefn\xd3\t\x97\x9bC\x00=# \xd9\xf0\xe8\xea\x981\xa9'Y\xfbK\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00#\xa0\xdbV\x11N\x00\xfd\xd4\xc1\xf8\\\x89+\xf3Z\u0268\x92\x89\xaa\xec\xb1\xeb\u0429l\xde`jt\x8b]q\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\xa0\u01c0\t\xfd\xf0\x7f\xc5j\x11\xf1\"7\x06X\xa3S\xaa\xa5B\xedc\xe4LK\xc1_\xf4\xcd\x10Z\xb3<\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%\xa0Sm\x98\x83\x7f-\xd1e\xa5]^\xea\xe9\x14\x85\x95Dr\xd5o$m\xf2V\xbf<\xae\x195*\x12<\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&\xa0\x9e\xfd\xe0R\xaa\x15B\x9f\xae\x05\xba\xd4\u0431\xd7\xc6M\xa6M\x03\u05e1\x85JX\x8c,\xb8C\f\r0\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\xa0\u060d\xdf\xee\xd4\x00\xa8uU\x96\xb2\x19B\xc1I~\x11L0.a\x18)\x0f\x91\xe6w)v\x04\x1f\xa1\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00(\xa0\x87\xeb\r\u06e5~5\xf6\u0486g8\x02\xa4\xafYu\xe2%\x06\xc7\xcfLd\xbbk\xe5\xee\x11R\x7f,\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00)\xa0&\x84dv\xfd_\xc5J]C8Qg\xc9QD\xf2d?S<\xc8[\xb9\xd1kx/\x8d}\xb1\x93\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00*\xa0Pm\x86X-%$\x05\xb8@\x01\x87\x92\xca\u04bf\x12Y\xf1\xefZ\xa5\xf8\x87\xe1<\xb2\xf0\tOQ\xe1\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+\xa0\xff\xff\n\xd7\xe6Yw/\x954\xc1\x95\xc8\x15\xef\xc4\x01N\xf1\xe1\xda\xedD\x04\xc0c\x85\xd1\x11\x92\xe9+\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,\xa0l\xf0A'\xdb\x05D\x1c\xd83\x10zR\xbe\x85(h\x89\x0eC\x17\xe6\xa0*\xb4v\x83\xaau\x96B \xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-\xa0\xb7\xd0_\x87_\x14\x00'\xefQ\x18\xa2${\xbb\x84\u038f/\x0f\x11#b0\x85\xda\xf7\x96\f2\x9f_\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\xa0\xdfj\xf5\xf5\xbb\xdbk\xe9\uf2a6\x18\u4fc0s\x96\bg\x17\x1e)go\x8b(M\xeaj\b\xa8^\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00/\xa0\xb5\x8d\x90\x0f^\x18.\x01t\u0285\x18.\xec\x9f:\t\xf6\xa6\xc0\xdfcw\xa5\x10\xd7\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x009\xa01 o\xa8\nP\xbbj\xbe)\bPX\xf1b\x12!*`\xee\xc8\xf0I\xfe\u02d2\xd8\xc8\xe0\xa8K\xc0\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:\xa0!5+\xfe\xcb\xed\xdd\u94c3\x9faL=\xac\n>\xe3uC\xf9\xb4\x12\xb1a\x99\xdc\x15\x8e#\xb5D\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\xa0a\x9e1'$\xbbm|1S\xed\x9d\xe7\x91\xd7d\xa3f\xb3\x89\xaf\x13\u014b\xf8\xa8\xd9\x04\x81\xa4ge\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\xa0|\xdd)\x86&\x82Pb\x8d\f\x10\xe3\x85\u014ca\x91\xe6\xfb\xe0Q\x91\xbc\xc0O\x13?,\xear\xc1\xc4\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\xa0\x84\x890\xbd{\xa8\xca\xc5Fa\a!\x13\xfb'\x88i\xe0{\xb8X\x7f\x919)37M\x01{\xcb\xe1\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>\xa0\x88i\xff,\"\xb2\x8c\xc1\x05\x10\u06452\x92\x803(\xbeO\xb0\xe8\x04\x95\u8ecd'\x1f[\x88\x966\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xa0\xb5\xfe(\xe7\x9f\x1b\x85\x0f\x86X$l\u9da1\u7d1f\xc0m\xb7\x14>\x8f\xe0\xb4\xf2\xb0\xc5R:\\\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\xa0\x98^\x92\x9fp\xaf(\u043d\u0469\n\x80\x8f\x97\x7fY||w\x8cH\x9e\x98\u04fd\x89\x10\xd3\x1a\xc0\xf7\xe1\x94F#\x96\u677f\xa4U\xf4\x05\xf4\u0742\xf3\x01J\xf8\x00;r\x8b\xa5o\xa5\xb9\x90\x19\xa5\xc8\x00\x00\x00\xe0\x94I\xdf<\xca&p\xeb\rY\x11F\xb1cY\xfe3nGo)\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94K\xc6V\xb3M\xe28\x96\xfa`i\u0246/5[t\x04\x01\xaf\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe0\x94M\v\x04\xb4\x05\u01b6,|\xfc:\xe5GYt~,\vFb\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94MIl\xcc(\x05\x8b\x1dt\xb7\xa1\x95Af>!\x15O\x9c\x84\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94P\x9avg\xac\x8d\x03 \xe3ar\xc1\x92Pja\x88\xaa\x84\xf6\x8b|\x13\xbcK,\x13\xfa<]\xc1\xaa\x19;\xc6\x03=\xfd\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94jz\xa9\xb8\x82\xd5\v\xb7\xbc]\xa1\xa2Dq\x9c\x99\xf1/\x06\xa3\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94l\xc99|;8s\x9d\xac\xbf\xaah\xea\xd5\xf5\xd7{\xa5\xf4U\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94s\xb2\xe0\xe5E\x10#\x9e\"\u0313o\vJm\xe1\xac\xf0\xab\u078bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94v,\xa6,\xa2T\x9a\xd8\x06v;:\xa1\xea1|B\x9b\xdb\u068a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94w\x8f_\x13\u013ex\xa3\xa4\xd7\x14\x1b\xcb&\x99\x97\x02\xf4\a\u03cbR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\x83M\xbfZ\x03\xe2\x9c%\xbcUE\x9c\u039c\x02\x1e\xeb\xe6v\xad\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\x87]%\xeeK\xc6\x04\xc7\x1b\xafb6\xa8H\x8f\"9\x9b\xedK\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\u150d\xf7\x87\x8d5q\xbe\xf5\xe5\xa7D\xf9b\x87\xc8\xd2\x03\x86\xd7Z\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\x9eAZ\to\xf7vP\u0712]\xeaTe\x85\xb4\xad\xb3\"\xb6\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xa0vke\xa4\xf7\xb1\xday\xa1\xafy\xaciTV\uf886D\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xa2\x9b\x14JD\x9eAJG,`\u01ea\xf1\xaa\xff\xe3)\x02\x1d\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xa5S\x95Vk\vT9[2F\xf9j\v\xdcK\x8aH=\xf9\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xac\x9b\xa7/\xb6\x1a\xa7\xc3\x1a\x95\xdf\n\x8bn\xbaoA\xef\x87^\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xb0I\x8c\x15\x87\x9d\xb2\xeeTq\u0512l_\xaa%\u0260\x96\x83\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xb0J\xef*=-\x86\xb0\x10\x06\xcc\xd43\x9a.\x94=\x9cd\x80\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\u1531\x9f\xb4\xc1\xf2\x802~`\xed7\xb1\xdcn\xe7u3S\x93\x14\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\xbb\x97{.\xe8\xa1\x11\u05c8\xb3G}$ x\u04387\xe7+\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xc2\x1c\xb9\u025c1m\x18c\x14/}\xd8m\xd5Im\x81\xa8\u058a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xc4s\xd4\x12\xdcR\xe3I\x86\"\t\x92L\x89\x81\xb2\xeeB\ah\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94\u010e#\xc5\xf6\xe1\xea\v\xae\xf6S\a4\xed\u00d6\x8fy\xaf.\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94\xc6\xe2E\x99\x91\xbf\xe2|\xcam\x86r/5\xda#\xa1\xe4\u02d7\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\xc9\xca+\xa9\xa2}\xe1\xdbX\x9d\x8c3\xab\x8e\u07e2\x11\x1b1\xfb\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xd1\xf7~L\x1cE\x18n\x86S\u0109\xf9\x0e\x00\x8asYr\x96\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe2\x94\u04d9NM2\x02\xdd#\xc8I}\x7fu\xbf\x16G\xd1\xda\x1b\xb1\x8c\x01\x9d\x97\x1eO\xe8@\x1et\x00\x00\x00\xe0\x94\u0726\u9d0e\xa8j\xeb\xfd\xf9\x92\x99I\x12@B)kn4\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xe0\x99\x1e\x84@A\xbeo\x11\xb9\x9d\xa5\xb1\x14\xb6\xbc\xf8N\xbdW\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94\u08bdBX\xd2v\x887\xba\xa2j(\xfeq\xdc\a\x9f\x84\u01cbR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\xea(\xd0\x02\x04/\u0649\x8d\r\xb0\x16\xbe\x97X\xee\xaf\xe3\\\x1e\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xef\xa7EO\x11\x16\x80yu\xa4u\vFi^\x96xP\xde]\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94\xfb\xfdo\xa9\xf7:\u01a0X\xe0\x12Y\x03L(\x00\x1b\xef\x82G\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00" +const hoodiAllocData = "\xf93\x9d\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x01\xc2\v\x01\xc2\f\x01\xc2\r\x01\xc2\x0e\x01\xc2\x0f\x01\xc2\x10\x01\xc2\x11\x01\xc2\x12\x01\xc2\x13\x01\xc2\x14\x01\xc2\x15\x01\xc2\x16\x01\xc2\x17\x01\xc2\x18\x01\xc2\x19\x01\xc2\x1a\x01\xc2\x1b\x01\xc2\x1c\x01\xc2\x1d\x01\xc2\x1e\x01\xc2\x1f\x01\xc2 \x01\xc2!\x01\xc2\"\x01\xc2#\x01\xc2$\x01\xc2%\x01\xc2&\x01\xc2'\x01\xc2(\x01\xc2)\x01\xc2*\x01\xc2+\x01\xc2,\x01\xc2-\x01\xc2.\x01\xc2/\x01\xc20\x01\xc21\x01\xc22\x01\xc23\x01\xc24\x01\xc25\x01\xc26\x01\xc27\x01\xc28\x01\xc29\x01\xc2:\x01\xc2;\x01\xc2<\x01\xc2=\x01\xc2>\x01\xc2?\x01\xc2@\x01\xc2A\x01\xc2B\x01\xc2C\x01\xc2D\x01\xc2E\x01\xc2F\x01\xc2G\x01\xc2H\x01\xc2I\x01\xc2J\x01\xc2K\x01\xc2L\x01\xc2M\x01\xc2N\x01\xc2O\x01\xc2P\x01\xc2Q\x01\xc2R\x01\xc2S\x01\xc2T\x01\xc2U\x01\xc2V\x01\xc2W\x01\xc2X\x01\xc2Y\x01\xc2Z\x01\xc2[\x01\xc2\\\x01\xc2]\x01\xc2^\x01\xc2_\x01\xc2`\x01\xc2a\x01\xc2b\x01\xc2c\x01\xc2d\x01\xc2e\x01\xc2f\x01\xc2g\x01\xc2h\x01\xc2i\x01\xc2j\x01\xc2k\x01\xc2l\x01\xc2m\x01\xc2n\x01\xc2o\x01\xc2p\x01\xc2q\x01\xc2r\x01\xc2s\x01\xc2t\x01\xc2u\x01\xc2v\x01\xc2w\x01\xc2x\x01\xc2y\x01\xc2z\x01\xc2{\x01\xc2|\x01\xc2}\x01\xc2~\x01\xc2\x7f\x01\u00c1\x80\x01\u00c1\x81\x01\u00c1\x82\x01\u00c1\x83\x01\u00c1\x84\x01\u00c1\x85\x01\u00c1\x86\x01\u00c1\x87\x01\u00c1\x88\x01\u00c1\x89\x01\u00c1\x8a\x01\u00c1\x8b\x01\u00c1\x8c\x01\u00c1\x8d\x01\u00c1\x8e\x01\u00c1\x8f\x01\u00c1\x90\x01\u00c1\x91\x01\u00c1\x92\x01\u00c1\x93\x01\u00c1\x94\x01\u00c1\x95\x01\u00c1\x96\x01\u00c1\x97\x01\u00c1\x98\x01\u00c1\x99\x01\u00c1\x9a\x01\u00c1\x9b\x01\u00c1\x9c\x01\u00c1\x9d\x01\u00c1\x9e\x01\u00c1\x9f\x01\u00c1\xa0\x01\u00c1\xa1\x01\u00c1\xa2\x01\u00c1\xa3\x01\u00c1\xa4\x01\u00c1\xa5\x01\u00c1\xa6\x01\u00c1\xa7\x01\u00c1\xa8\x01\u00c1\xa9\x01\u00c1\xaa\x01\u00c1\xab\x01\u00c1\xac\x01\u00c1\xad\x01\u00c1\xae\x01\u00c1\xaf\x01\u00c1\xb0\x01\u00c1\xb1\x01\u00c1\xb2\x01\u00c1\xb3\x01\u00c1\xb4\x01\u00c1\xb5\x01\u00c1\xb6\x01\u00c1\xb7\x01\u00c1\xb8\x01\u00c1\xb9\x01\u00c1\xba\x01\u00c1\xbb\x01\u00c1\xbc\x01\u00c1\xbd\x01\u00c1\xbe\x01\u00c1\xbf\x01\u00c1\xc0\x01\u00c1\xc1\x01\u00c1\xc2\x01\u00c1\xc3\x01\u00c1\xc4\x01\u00c1\xc5\x01\u00c1\xc6\x01\u00c1\xc7\x01\u00c1\xc8\x01\u00c1\xc9\x01\u00c1\xca\x01\u00c1\xcb\x01\u00c1\xcc\x01\u00c1\xcd\x01\u00c1\xce\x01\u00c1\xcf\x01\u00c1\xd0\x01\u00c1\xd1\x01\u00c1\xd2\x01\u00c1\xd3\x01\u00c1\xd4\x01\u00c1\xd5\x01\u00c1\xd6\x01\u00c1\xd7\x01\u00c1\xd8\x01\u00c1\xd9\x01\u00c1\xda\x01\u00c1\xdb\x01\u00c1\xdc\x01\u00c1\xdd\x01\u00c1\xde\x01\u00c1\xdf\x01\u00c1\xe0\x01\u00c1\xe1\x01\u00c1\xe2\x01\u00c1\xe3\x01\u00c1\xe4\x01\u00c1\xe5\x01\u00c1\xe6\x01\u00c1\xe7\x01\u00c1\xe8\x01\u00c1\xe9\x01\u00c1\xea\x01\u00c1\xeb\x01\u00c1\xec\x01\u00c1\xed\x01\u00c1\xee\x01\u00c1\xef\x01\u00c1\xf0\x01\u00c1\xf1\x01\u00c1\xf2\x01\u00c1\xf3\x01\u00c1\xf4\x01\u00c1\xf5\x01\u00c1\xf6\x01\u00c1\xf7\x01\u00c1\xf8\x01\u00c1\xf9\x01\u00c1\xfa\x01\u00c1\xfb\x01\u00c1\xfc\x01\u00c1\xfd\x01\u00c1\xfe\x01\u00c1\xff\x01\xf9!.\x90!\x9a\xb5@5l\xbb\x83\x9c\xbe\x050=w\x05\xfa\x80\xf9!\x19\x80\xb9\x18\xd6`\x80`@R`\x046\x10a\x00?W`\x005`\xe0\x1c\x80c\x01\xff\u0267\x14a\x00DW\x80c\"\x89Q\x18\x14a\x00\xa4W\x80cb\x1f\xd10\x14a\x01\xbaW\x80c\xc5\xf2\x89/\x14a\x02DW[`\x00\x80\xfd[4\x80\x15a\x00PW`\x00\x80\xfd[Pa\x00\x90`\x04\x806\x03` \x81\x10\x15a\x00gW`\x00\x80\xfd[P5\x7f\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16a\x02kV[`@\x80Q\x91\x15\x15\x82RQ\x90\x81\x90\x03` \x01\x90\xf3[a\x01\xb8`\x04\x806\x03`\x80\x81\x10\x15a\x00\xbaW`\x00\x80\xfd[\x81\x01\x90` \x81\x01\x815d\x01\x00\x00\x00\x00\x81\x11\x15a\x00\xd5W`\x00\x80\xfd[\x82\x01\x83` \x82\x01\x11\x15a\x00\xe7W`\x00\x80\xfd[\x805\x90` \x01\x91\x84`\x01\x83\x02\x84\x01\x11d\x01\x00\x00\x00\x00\x83\x11\x17\x15a\x01\tW`\x00\x80\xfd[\x91\x93\x90\x92\x90\x91` \x81\x01\x905d\x01\x00\x00\x00\x00\x81\x11\x15a\x01'W`\x00\x80\xfd[\x82\x01\x83` \x82\x01\x11\x15a\x019W`\x00\x80\xfd[\x805\x90` \x01\x91\x84`\x01\x83\x02\x84\x01\x11d\x01\x00\x00\x00\x00\x83\x11\x17\x15a\x01[W`\x00\x80\xfd[\x91\x93\x90\x92\x90\x91` \x81\x01\x905d\x01\x00\x00\x00\x00\x81\x11\x15a\x01yW`\x00\x80\xfd[\x82\x01\x83` \x82\x01\x11\x15a\x01\x8bW`\x00\x80\xfd[\x805\x90` \x01\x91\x84`\x01\x83\x02\x84\x01\x11d\x01\x00\x00\x00\x00\x83\x11\x17\x15a\x01\xadW`\x00\x80\xfd[\x91\x93P\x91P5a\x03\x04V[\x00[4\x80\x15a\x01\xc6W`\x00\x80\xfd[Pa\x01\xcfa\x10\xb5V[`@\x80Q` \x80\x82R\x83Q\x81\x83\x01R\x83Q\x91\x92\x83\x92\x90\x83\x01\x91\x85\x01\x90\x80\x83\x83`\x00[\x83\x81\x10\x15a\x02\tW\x81\x81\x01Q\x83\x82\x01R` \x01a\x01\xf1V[PPPP\x90P\x90\x81\x01\x90`\x1f\x16\x80\x15a\x026W\x80\x82\x03\x80Q`\x01\x83` \x03a\x01\x00\n\x03\x19\x16\x81R` \x01\x91P[P\x92PPP`@Q\x80\x91\x03\x90\xf3[4\x80\x15a\x02PW`\x00\x80\xfd[Pa\x02Ya\x10\xc7V[`@\x80Q\x91\x82RQ\x90\x81\x90\x03` \x01\x90\xf3[`\x00\x7f\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x82\x16\x7f\x01\xff\u0267\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x80a\x02\xfeWP\x7f\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x82\x16\x7f\x85d\t\a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14[\x92\x91PPV[`0\x86\x14a\x03]W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`&\x81R` \x01\x80a\x18\x05`&\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[` \x84\x14a\x03\xb6W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`6\x81R` \x01\x80a\x17\x9c`6\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[``\x82\x14a\x04\x0fW`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`)\x81R` \x01\x80a\x18x`)\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[g\r\u0db3\xa7d\x00\x004\x10\x15a\x04pW`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`&\x81R` \x01\x80a\x18R`&\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[c;\x9a\xca\x004\x06\x15a\x04\xcdW`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`3\x81R` \x01\x80a\x17\xd2`3\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[c;\x9a\xca\x004\x04g\xff\xff\xff\xff\xff\xff\xff\xff\x81\x11\x15a\x055W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`'\x81R` \x01\x80a\x18+`'\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[``a\x05@\x82a\x14\xbaV[\x90P\x7fd\x9b\xbcb\xd0\xe3\x13B\xaf\xeaN\\\xd8-@I\xe7\xe1\xee\x91/\xc0\x88\x9a\xa7\x90\x80;\xe3\x908\u0149\x89\x89\x89\x85\x8a\x8aa\x05u` Ta\x14\xbaV[`@\x80Q`\xa0\x80\x82R\x81\x01\x89\x90R\x90\x81\x90` \x82\x01\x90\x82\x01``\x83\x01`\x80\x84\x01`\xc0\x85\x01\x8e\x8e\x80\x82\x847`\x00\x83\x82\x01R`\x1f\x01\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x16\x90\x91\x01\x87\x81\x03\x86R\x8c\x81R` \x01\x90P\x8c\x8c\x80\x82\x847`\x00\x83\x82\x01\x81\x90R`\x1f\x90\x91\x01\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x16\x90\x92\x01\x88\x81\x03\x86R\x8cQ\x81R\x8cQ` \x91\x82\x01\x93\x91\x8e\x01\x92P\x90\x81\x90\x84\x90\x84\x90[\x83\x81\x10\x15a\x06HW\x81\x81\x01Q\x83\x82\x01R` \x01a\x060V[PPPP\x90P\x90\x81\x01\x90`\x1f\x16\x80\x15a\x06uW\x80\x82\x03\x80Q`\x01\x83` \x03a\x01\x00\n\x03\x19\x16\x81R` \x01\x91P[P\x86\x81\x03\x83R\x88\x81R` \x01\x89\x89\x80\x82\x847`\x00\x83\x82\x01\x81\x90R`\x1f\x90\x91\x01\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x16\x90\x92\x01\x88\x81\x03\x84R\x89Q\x81R\x89Q` \x91\x82\x01\x93\x91\x8b\x01\x92P\x90\x81\x90\x84\x90\x84\x90[\x83\x81\x10\x15a\x06\xefW\x81\x81\x01Q\x83\x82\x01R` \x01a\x06\xd7V[PPPP\x90P\x90\x81\x01\x90`\x1f\x16\x80\x15a\a\x1cW\x80\x82\x03\x80Q`\x01\x83` \x03a\x01\x00\n\x03\x19\x16\x81R` \x01\x91P[P\x9dPPPPPPPPPPPPPP`@Q\x80\x91\x03\x90\xa1`\x00`\x02\x8a\x8a`\x00`\x80\x1b`@Q` \x01\x80\x84\x84\x80\x82\x847\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x90\x94\x16\x91\x90\x93\x01\x90\x81R`@\x80Q\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x81\x84\x03\x01\x81R`\x10\x90\x92\x01\x90\x81\x90R\x81Q\x91\x95P\x93P\x83\x92P` \x85\x01\x91P\x80\x83\x83[` \x83\x10a\a\xfcW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\a\xbfV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\bYW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\bnW`\x00\x80\xfd[PQ\x90P`\x00`\x02\x80a\b\x84`@\x84\x8a\x8ca\x16\xfeV[`@Q` \x01\x80\x83\x83\x80\x82\x847\x80\x83\x01\x92PPP\x92PPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\b\xf8W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\b\xbbV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\tUW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\tjW`\x00\x80\xfd[PQ`\x02a\t{\x89`@\x81\x8da\x16\xfeV[`@Q`\x00\x90` \x01\x80\x84\x84\x80\x82\x847\x91\x90\x91\x01\x92\x83RPP`@\x80Q\x80\x83\x03\x81R` \x92\x83\x01\x91\x82\x90R\x80Q\x90\x94P\x90\x92P\x82\x91\x84\x01\x90\x80\x83\x83[` \x83\x10a\t\xf4W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\t\xb7V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\nQW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\nfW`\x00\x80\xfd[PQ`@\x80Q` \x81\x81\x01\x94\x90\x94R\x80\x82\x01\x92\x90\x92R\x80Q\x80\x83\x03\x82\x01\x81R``\x90\x92\x01\x90\x81\x90R\x81Q\x91\x92\x90\x91\x82\x91\x84\x01\x90\x80\x83\x83[` \x83\x10a\n\xdaW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\n\x9dV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\v7W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\vLW`\x00\x80\xfd[PQ`@\x80Q` \x81\x01\x85\x81R\x92\x93P`\x00\x92`\x02\x92\x83\x92\x87\x92\x8f\x92\x8f\x92\x01\x83\x83\x80\x82\x847\x80\x83\x01\x92PPP\x93PPPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\v\xd9W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\v\x9cV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\f6W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\fKW`\x00\x80\xfd[PQ`@Q\x86Q`\x02\x91\x88\x91`\x00\x91\x88\x91` \x91\x82\x01\x91\x82\x91\x90\x86\x01\x90\x80\x83\x83[` \x83\x10a\f\xa9W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\flV[`\x01\x83` \x03a\x01\x00\n\x03\x80\x19\x82Q\x16\x81\x84Q\x16\x80\x82\x17\x85RPPPPPP\x90P\x01\x83g\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16g\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x81R`\x18\x01\x82\x81R` \x01\x93PPPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\rNW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\r\x11V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\r\xabW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\r\xc0W`\x00\x80\xfd[PQ`@\x80Q` \x81\x81\x01\x94\x90\x94R\x80\x82\x01\x92\x90\x92R\x80Q\x80\x83\x03\x82\x01\x81R``\x90\x92\x01\x90\x81\x90R\x81Q\x91\x92\x90\x91\x82\x91\x84\x01\x90\x80\x83\x83[` \x83\x10a\x0e4W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\r\xf7V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x0e\x91W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x0e\xa6W`\x00\x80\xfd[PQ\x90P\x85\x81\x14a\x0f\x02W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`T\x81R` \x01\x80a\x17H`T\x919``\x01\x91PP`@Q\x80\x91\x03\x90\xfd[` Tc\xff\xff\xff\xff\x11a\x0f`W`@Q\x7f\b\xc3y\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81R`\x04\x01\x80\x80` \x01\x82\x81\x03\x82R`!\x81R` \x01\x80a\x17'`!\x919`@\x01\x91PP`@Q\x80\x91\x03\x90\xfd[` \x80T`\x01\x01\x90\x81\x90U`\x00[` \x81\x10\x15a\x10\xa9W\x81`\x01\x16`\x01\x14\x15a\x0f\xa0W\x82`\x00\x82` \x81\x10a\x0f\x91W\xfe[\x01UPa\x10\xac\x95PPPPPPV[`\x02`\x00\x82` \x81\x10a\x0f\xafW\xfe[\x01T\x84`@Q` \x01\x80\x83\x81R` \x01\x82\x81R` \x01\x92PPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\x10%W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x0f\xe8V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x10\x82W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x10\x97W`\x00\x80\xfd[PQ\x92P`\x02\x82\x04\x91P`\x01\x01a\x0fnV[P\xfe[PPPPPPPV[``a\x10\xc2` Ta\x14\xbaV[\x90P\x90V[` T`\x00\x90\x81\x90\x81[` \x81\x10\x15a\x12\xf0W\x81`\x01\x16`\x01\x14\x15a\x11\xe6W`\x02`\x00\x82` \x81\x10a\x10\xf5W\xfe[\x01T\x84`@Q` \x01\x80\x83\x81R` \x01\x82\x81R` \x01\x92PPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\x11kW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x11.V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x11\xc8W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x11\xddW`\x00\x80\xfd[PQ\x92Pa\x12\xe2V[`\x02\x83`!\x83` \x81\x10a\x11\xf6W\xfe[\x01T`@Q` \x01\x80\x83\x81R` \x01\x82\x81R` \x01\x92PPP`@Q` \x81\x83\x03\x03\x81R\x90`@R`@Q\x80\x82\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\x12kW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x12.V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x12\xc8W=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x12\xddW`\x00\x80\xfd[PQ\x92P[`\x02\x82\x04\x91P`\x01\x01a\x10\xd1V[P`\x02\x82a\x12\xff` Ta\x14\xbaV[`\x00`@\x1b`@Q` \x01\x80\x84\x81R` \x01\x83\x80Q\x90` \x01\x90\x80\x83\x83[` \x83\x10a\x13ZW\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x13\x1dV[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x95\x90\x95\x16\x92\x01\x91\x82RP`@\x80Q\x80\x83\x03\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x01\x81R`\x18\x90\x92\x01\x90\x81\x90R\x81Q\x91\x95P\x93P\x83\x92\x85\x01\x91P\x80\x83\x83[` \x83\x10a\x14?W\x80Q\x82R\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x90\x92\x01\x91` \x91\x82\x01\x91\x01a\x14\x02V[Q\x81Q` \x93\x84\x03a\x01\x00\n\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x19\x90\x92\x16\x91\x16\x17\x90R`@Q\x91\x90\x93\x01\x94P\x91\x92PP\x80\x83\x03\x81\x85Z\xfa\x15\x80\x15a\x14\x9cW=`\x00\x80>=`\x00\xfd[PPP`@Q=` \x81\x10\x15a\x14\xb1W`\x00\x80\xfd[PQ\x92PPP\x90V[`@\x80Q`\b\x80\x82R\x81\x83\x01\x90\x92R``\x91` \x82\x01\x81\x806\x837\x01\x90PP\x90P`\xc0\x82\x90\x1b\x80`\a\x1a`\xf8\x1b\x82`\x00\x81Q\x81\x10a\x14\xf4W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x06\x1a`\xf8\x1b\x82`\x01\x81Q\x81\x10a\x157W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x05\x1a`\xf8\x1b\x82`\x02\x81Q\x81\x10a\x15zW\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x04\x1a`\xf8\x1b\x82`\x03\x81Q\x81\x10a\x15\xbdW\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x03\x1a`\xf8\x1b\x82`\x04\x81Q\x81\x10a\x16\x00W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x02\x1a`\xf8\x1b\x82`\x05\x81Q\x81\x10a\x16CW\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x01\x1a`\xf8\x1b\x82`\x06\x81Q\x81\x10a\x16\x86W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SP\x80`\x00\x1a`\xf8\x1b\x82`\a\x81Q\x81\x10a\x16\xc9W\xfe[` \x01\x01\x90~\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x19\x16\x90\x81`\x00\x1a\x90SPP\x91\x90PV[`\x00\x80\x85\x85\x11\x15a\x17\rW\x81\x82\xfd[\x83\x86\x11\x15a\x17\x19W\x81\x82\xfd[PP\x82\x01\x93\x91\x90\x92\x03\x91PV\xfeDepositContract: merkle tree fullDepositContract: reconstructed DepositData does not match supplied deposit_data_rootDepositContract: invalid withdrawal_credentials lengthDepositContract: deposit value not multiple of gweiDepositContract: invalid pubkey lengthDepositContract: deposit value too highDepositContract: deposit value too lowDepositContract: invalid signature length\xa2dipfsX\"\x12 \xdc\xec\xa8pk)\xe9\x17\xda\xcf%\xfc\xee\xf9Z\xca\xc8\xd9\rvZ\xc9&f<\xe4\ta\x95\x95+adsolcC\x00\x06\v\x003\xf9\b<\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\xa0\xf5\xa5\xfdB\xd1j 0'\x98\xefn\xd3\t\x97\x9bC\x00=# \xd9\xf0\xe8\xea\x981\xa9'Y\xfbK\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00#\xa0\xdbV\x11N\x00\xfd\xd4\xc1\xf8\\\x89+\xf3Z\u0268\x92\x89\xaa\xec\xb1\xeb\u0429l\xde`jt\x8b]q\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\xa0\u01c0\t\xfd\xf0\x7f\xc5j\x11\xf1\"7\x06X\xa3S\xaa\xa5B\xedc\xe4LK\xc1_\xf4\xcd\x10Z\xb3<\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%\xa0Sm\x98\x83\x7f-\xd1e\xa5]^\xea\xe9\x14\x85\x95Dr\xd5o$m\xf2V\xbf<\xae\x195*\x12<\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&\xa0\x9e\xfd\xe0R\xaa\x15B\x9f\xae\x05\xba\xd4\u0431\xd7\xc6M\xa6M\x03\u05e1\x85JX\x8c,\xb8C\f\r0\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\xa0\u060d\xdf\xee\xd4\x00\xa8uU\x96\xb2\x19B\xc1I~\x11L0.a\x18)\x0f\x91\xe6w)v\x04\x1f\xa1\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00(\xa0\x87\xeb\r\u06e5~5\xf6\u0486g8\x02\xa4\xafYu\xe2%\x06\xc7\xcfLd\xbbk\xe5\xee\x11R\x7f,\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00)\xa0&\x84dv\xfd_\xc5J]C8Qg\xc9QD\xf2d?S<\xc8[\xb9\xd1kx/\x8d}\xb1\x93\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00*\xa0Pm\x86X-%$\x05\xb8@\x01\x87\x92\xca\u04bf\x12Y\xf1\xefZ\xa5\xf8\x87\xe1<\xb2\xf0\tOQ\xe1\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+\xa0\xff\xff\n\xd7\xe6Yw/\x954\xc1\x95\xc8\x15\xef\xc4\x01N\xf1\xe1\xda\xedD\x04\xc0c\x85\xd1\x11\x92\xe9+\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,\xa0l\xf0A'\xdb\x05D\x1c\xd83\x10zR\xbe\x85(h\x89\x0eC\x17\xe6\xa0*\xb4v\x83\xaau\x96B \xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-\xa0\xb7\xd0_\x87_\x14\x00'\xefQ\x18\xa2${\xbb\x84\u038f/\x0f\x11#b0\x85\xda\xf7\x96\f2\x9f_\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\xa0\xdfj\xf5\xf5\xbb\xdbk\xe9\uf2a6\x18\u4fc0s\x96\bg\x17\x1e)go\x8b(M\xeaj\b\xa8^\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00/\xa0\xb5\x8d\x90\x0f^\x18.\x01t\u0285\x18.\xec\x9f:\t\xf6\xa6\xc0\xdfcw\xa5\x10\xd7\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x009\xa01 o\xa8\nP\xbbj\xbe)\bPX\xf1b\x12!*`\xee\xc8\xf0I\xfe\u02d2\xd8\xc8\xe0\xa8K\xc0\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:\xa0!5+\xfe\xcb\xed\xdd\u94c3\x9faL=\xac\n>\xe3uC\xf9\xb4\x12\xb1a\x99\xdc\x15\x8e#\xb5D\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\xa0a\x9e1'$\xbbm|1S\xed\x9d\xe7\x91\xd7d\xa3f\xb3\x89\xaf\x13\u014b\xf8\xa8\xd9\x04\x81\xa4ge\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\xa0|\xdd)\x86&\x82Pb\x8d\f\x10\xe3\x85\u014ca\x91\xe6\xfb\xe0Q\x91\xbc\xc0O\x13?,\xear\xc1\xc4\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\xa0\x84\x890\xbd{\xa8\xca\xc5Fa\a!\x13\xfb'\x88i\xe0{\xb8X\x7f\x919)37M\x01{\xcb\xe1\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>\xa0\x88i\xff,\"\xb2\x8c\xc1\x05\x10\u06452\x92\x803(\xbeO\xb0\xe8\x04\x95\u8ecd'\x1f[\x88\x966\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xa0\xb5\xfe(\xe7\x9f\x1b\x85\x0f\x86X$l\u9da1\u7d1f\xc0m\xb7\x14>\x8f\xe0\xb4\xf2\xb0\xc5R:\\\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\xa0\x98^\x92\x9fp\xaf(\u043d\u0469\n\x80\x8f\x97\x7fY||w\x8cH\x9e\x98\u04fd\x89\x10\xd3\x1a\xc0\xf7\u0791i\x16\xa8{\x823?BE\x04f#\xb27\x94\xc6\\\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xf9\x02Y\x92\ta\xefH\x0e\xb5^\x80\u045a\xd85y\xa6L\x00p\x02\x80\xf9\x02B\x01\xb9\x01\xf83s\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x14`\xcbW`\x11_T\x80\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x14a\x01\xf4W`\x01\x82\x02`\x01\x90_[_\x82\x11\x15`hW\x81\x01\x90\x83\x02\x84\x83\x02\x90\x04\x91`\x01\x01\x91\x90`MV[\x90\x93\x90\x04\x92PPP6`8\x14`\x88W6a\x01\xf4W4a\x01\xf4W_R` _\xf3[4\x10a\x01\xf4W`\x01T`\x01\x01`\x01U`\x03T\x80`\x03\x02`\x04\x013\x81U`\x01\x01_5\x81U`\x01\x01` 5\x90U3``\x1b_R`8_`\x147`L_\xa0`\x01\x01`\x03U\x00[`\x03T`\x02T\x80\x82\x03\x80`\x10\x11`\xdfWP`\x10[_[\x81\x81\x14a\x01\x83W\x82\x81\x01`\x03\x02`\x04\x01\x81`L\x02\x81T``\x1b\x81R`\x14\x01\x81`\x01\x01T\x81R` \x01\x90`\x02\x01T\x80\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x82R\x90`\x10\x01\x90`@\x1c\x90\x81`8\x1c\x81`\a\x01S\x81`0\x1c\x81`\x06\x01S\x81`(\x1c\x81`\x05\x01S\x81` \x1c\x81`\x04\x01S\x81`\x18\x1c\x81`\x03\x01S\x81`\x10\x1c\x81`\x02\x01S\x81`\b\x1c\x81`\x01\x01SS`\x01\x01`\xe1V[\x91\x01\x80\x92\x14a\x01\x95W\x90`\x02Ua\x01\xa0V[\x90P_`\x02U_`\x03U[_T\x80\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x14\x15a\x01\xcdWP_[`\x01T`\x02\x82\x82\x01\x11a\x01\xe2WPP_a\x01\xe8V[\x01`\x02\x90\x03[_U_`\x01U`L\x02_\xf3[__\xfd\xf8D\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf9\x01\xff\x92\xbb\xdd\xc7\xceH\x86B\xfbW\x9f\x8b\x00\xf3\xa5\x90\x00rQ\x80\xf9\x01\xe8\x01\xb9\x01\x9e3s\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x14`\xd3W`\x11_T\x80\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x14a\x01\x9aW`\x01\x82\x02`\x01\x90_[_\x82\x11\x15`hW\x81\x01\x90\x83\x02\x84\x83\x02\x90\x04\x91`\x01\x01\x91\x90`MV[\x90\x93\x90\x04\x92PPP6``\x14`\x88W6a\x01\x9aW4a\x01\x9aW_R` _\xf3[4\x10a\x01\x9aW`\x01T`\x01\x01`\x01U`\x03T\x80`\x04\x02`\x04\x013\x81U`\x01\x01_5\x81U`\x01\x01` 5\x81U`\x01\x01`@5\x90U3``\x1b_R``_`\x147`t_\xa0`\x01\x01`\x03U\x00[`\x03T`\x02T\x80\x82\x03\x80`\x02\x11`\xe7WP`\x02[_[\x81\x81\x14a\x01)W\x82\x81\x01`\x04\x02`\x04\x01\x81`t\x02\x81T``\x1b\x81R`\x14\x01\x81`\x01\x01T\x81R` \x01\x81`\x02\x01T\x81R` \x01\x90`\x03\x01T\x90R`\x01\x01`\xe9V[\x91\x01\x80\x92\x14a\x01;W\x90`\x02Ua\x01FV[\x90P_`\x02U_`\x03U[_T\x80\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x14\x15a\x01sWP_[`\x01T`\x01\x82\x82\x01\x11a\x01\x88WPP_a\x01\x8eV[\x01`\x01\x90\x03[_U_`\x01U`t\x02_\xf3[__\xfd\xf8D\xf8B\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8m\x92\xf9\b'\xf1\xc5:\x10\xcbz\x023[\x17S \x00)5\x80\xf8W\x01\xb8S3s\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x14`FW` 6\x03`BW_5`\x01C\x03\x81\x11`BWa\x1f\xff\x81C\x03\x11`BWa\x1f\xff\x90\x06T_R` _\xf3[__\xfd[_5a\x1f\xff`\x01C\x03\x06U\x00\xc0\xf8|\x93\x0f=\xf6\xd72\x80~\xf11\x9f\xb7\xb8\xbb\x85\"\u043e\xac\x02\x80\xf8e\x01\xb8a3s\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x14`MW` 6\x14`$W__\xfd[_5\x80\x15`IWb\x00\x1f\xff\x81\x06\x90\x81T\x14`\x8e\u0344h\xa0\x03\x7f\x05\x8a\x9d\xaf\xady\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94@O\xfd\x9d\xd6T(\xe0\x96\u0667\xb87\x92Bc\xef@\xfb1\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94E\u0720to\xc5k@\v)\x1f]\x96W\xa7\x94d\xe8\xf1R\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94F#\x96\u677f\xa4U\xf4\x05\xf4\u0742\xf3\x01J\xf8\x00;r\x8b\xa5o\xa5\xb9\x90\x19\xa5\xc8\x00\x00\x00\xe0\x94I\xdf<\xca&p\xeb\rY\x11F\xb1cY\xfe3nGo)\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94M\v\x04\xb4\x05\u01b6,|\xfc:\xe5GYt~,\vFb\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94MIl\xcc(\x05\x8b\x1dt\xb7\xa1\x95Af>!\x15O\x9c\x84\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94P\x9avg\xac\x8d\x03 \xe3ar\xc1\x92Pja\x88\xaa\x84\xf6\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94Q\x80\xdb\x027)\x1adI\u0769\xed3\xad\x90\xa3\x87\x87b\x1c\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94Rs\x0f4}\xefk\xa0\x9a\xdf\xf6.\xac`\xd5\xfe\xe8 [\u010a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94^\xac\x0f\xbd=\xfe\xf8\xae>\xfa<]\xc1\xaa\x19;\xc6\x03=\xfd\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94_\x10\u069bh\xf0lv\xf3\xc6$\xf8\xb2\xee;*&\x985\x1b\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94a\bf\xc6\b\x97h\u0695RK\xccL\xe7\xdba\xed\xa3\x93\x1c\x8b\xa5o\xa5\xb9\x90\x19\xa5\xc8\x00\x00\x00\xe1\x94jz\xa9\xb8\x82\xd5\v\xb7\xbc]\xa1\xa2Dq\x9c\x99\xf1/\x06\xa3\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94l\xc99|;8s\x9d\xac\xbf\xaah\xea\xd5\xf5\xd7{\xa5\xf4U\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94s\xb2\xe0\xe5E\x10#\x9e\"\u0313o\vJm\xe1\xac\xf0\xab\u078bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94v,\xa6,\xa2T\x9a\xd8\x06v;:\xa1\xea1|B\x9b\xdb\u068a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94v\x7fuv\x94M2\x13t\x92\x1d\xf18X\x9a0\xe3\xc5\x03\r\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94w\x8f_\x13\u013ex\xa3\xa4\xd7\x14\x1b\xcb&\x99\x97\x02\xf4\a\u03cbR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\x83M\xbfZ\x03\xe2\x9c%\xbcUE\x9c\u039c\x02\x1e\xeb\xe6v\xad\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\x87]%\xeeK\xc6\x04\xc7\x1b\xafb6\xa8H\x8f\"9\x9b\xedK\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\x8d\xf7\x87\x8d5q\xbe\xf5\xe5\xa7D\xf9b\x87\xc8\xd2\x03\x86\xd7Z\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\x90)\xc7r\xdd\xe8Gb-\xf1U>\xd9\u067d\xb7\x81.O\x93\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\u251a'\xd0\xc7\x15\xd3\xf2\xaf/\xac9\xa4\x1cI\xed5\x00J;\u03cc\x01\x9d\x97\x1eO\xe8@\x1et\x00\x00\x00\xe0\x94\x9b8?\x8eL\xd5\xd3\xdd_\x90\x06\xb6\xa5\b\x96\n\x1es\x03u\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\x9bI\x1f\x041\x89\xafm1\xd3\u0531\xa7\xca\x1c\xf7/@\xc5*\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\x9eAZ\to\xf7vP\u0712]\xeaTe\x85\xb4\xad\xb3\"\xb6\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xa0vke\xa4\xf7\xb1\xday\xa1\xafy\xaciTV\uf886D\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xa2\x9b\x14JD\x9eAJG,`\u01ea\xf1\xaa\xff\xe3)\x02\x1d\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xa5S\x95Vk\vT9[2F\xf9j\v\xdcK\x8aH=\xf9\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xac\x9b\xa7/\xb6\x1a\xa7\xc3\x1a\x95\xdf\n\x8bn\xbaoA\xef\x87^\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xb0I\x8c\x15\x87\x9d\xb2\xeeTq\u0512l_\xaa%\u0260\x96\x83\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xb0J\xef*=-\x86\xb0\x10\x06\xcc\xd43\x9a.\x94=\x9cd\x80\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\u1531\x9f\xb4\xc1\xf2\x802~`\xed7\xb1\xdcn\xe7u3S\x93\x14\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\xc2\x1c\xb9\u025c1m\x18c\x14/}\xd8m\xd5Im\x81\xa8\u058a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xc4s\xd4\x12\xdcR\xe3I\x86\"\t\x92L\x89\x81\xb2\xeeB\ah\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\u010e#\xc5\xf6\xe1\xea\v\xae\xf6S\a4\xed\u00d6\x8fy\xaf.\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94\xc5d\xaf\x15F!\xee\x8d\x05\x89u\x8dSU\x11\xae\xc8\xf6{@\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe1\x94\xc6\xe2E\x99\x91\xbf\xe2|\xcam\x86r/5\xda#\xa1\xe4\u02d7\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe0\x94\xc9\xca+\xa9\xa2}\xe1\xdbX\x9d\x8c3\xab\x8e\u07e2\x11\x1b1\xfb\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xd1\xf7~L\x1cE\x18n\x86S\u0109\xf9\x0e\x00\x8asYr\x96\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\u053bU];\r\x7f\xf1|`aa\xb4N7&\x89\xc1OK\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94\xda)\xbbqf\x9fF\xf2\xa7y\xb4\xb6/\x03dJ\x84\xee4y\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe1\x94\xdb\xf6@\u07a0G\xfa~\xe7F\xd0\x1a1x#\x86\xf8'\xcf\xc1\x8b\bE\x95\x16\x14\x01HJ\x00\x00\x00\xe0\x94\u0726\u9d0e\xa8j\xeb\xfd\xf9\x92\x99I\x12@B)kn4\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xe4\x95^\x10~\xa50\xb4\xc2\u06ca2=\xf0\xa1C\u0628+\x9c\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xea(\xd0\x02\x04/\u0649\x8d\r\xb0\x16\xbe\x97X\xee\xaf\xe3\\\x1e\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xebt.C\x95n\x0e\xa8\x9a-\xb6\xbfb7\u01de\xe4dY\x81\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe0\x94\xef\xa7EO\x11\x16\x80yu\xa4u\vFi^\x96xP\xde]\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\xe1\x94\xfb\xfdo\xa9\xf7:\u01a0X\xe0\x12Y\x03L(\x00\x1b\xef\x82G\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00\xe1\x94\xfcz\xf4\x9b\x80\xac\xf0AtCf\xb0\"r\x01\x97#\xc9O\x9c\x8bR\xb7\xd2\xdc\xc8\f\xd2\xe4\x00\x00\x00" diff --git a/core/genesis_test.go b/core/genesis_test.go index 86e5617ef8d..726bda86bb0 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -104,6 +104,15 @@ func testSetupGenesis(t *testing.T, scheme string) { }, wantErr: &GenesisMismatchError{Stored: customghash, New: params.SepoliaGenesisHash}, }, + { + name: "custom block in DB, genesis == hoodi", + fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) { + tdb := triedb.NewDatabase(db, newDbConfig(scheme)) + customg.Commit(db, tdb) + return SetupGenesisBlock(db, tdb, DefaultHoodiGenesisBlock()) + }, + wantErr: &GenesisMismatchError{Stored: customghash, New: params.HoodiGenesisHash}, + }, { name: "compatible config in DB", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) { @@ -178,6 +187,8 @@ func TestGenesisHashes(t *testing.T) { }{ {DefaultGenesisBlock(), params.MainnetGenesisHash}, {DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash}, + {DefaultHoleskyGenesisBlock(), params.HoleskyGenesisHash}, + {DefaultHoodiGenesisBlock(), params.HoodiGenesisHash}, } { // Test via MustCommit db := rawdb.NewMemoryDatabase() diff --git a/core/rawdb/accessors_indexes_test.go b/core/rawdb/accessors_indexes_test.go index 1bee4555037..ea25bf1e2c0 100644 --- a/core/rawdb/accessors_indexes_test.go +++ b/core/rawdb/accessors_indexes_test.go @@ -119,6 +119,7 @@ func TestDeleteBloomBits(t *testing.T) { for s := uint64(0); s < 2; s++ { WriteBloomBits(db, i, s, params.MainnetGenesisHash, []byte{0x01, 0x02}) WriteBloomBits(db, i, s, params.SepoliaGenesisHash, []byte{0x01, 0x02}) + WriteBloomBits(db, i, s, params.HoodiGenesisHash, []byte{0x01, 0x02}) } } check := func(bit uint, section uint64, head common.Hash, exist bool) { @@ -133,24 +134,31 @@ func TestDeleteBloomBits(t *testing.T) { // Check the existence of written data. check(0, 0, params.MainnetGenesisHash, true) check(0, 0, params.SepoliaGenesisHash, true) + check(0, 0, params.HoodiGenesisHash, true) // Check the existence of deleted data. DeleteBloombits(db, 0, 0, 1) check(0, 0, params.MainnetGenesisHash, false) check(0, 0, params.SepoliaGenesisHash, false) + check(0, 0, params.HoodiGenesisHash, false) check(0, 1, params.MainnetGenesisHash, true) check(0, 1, params.SepoliaGenesisHash, true) + check(0, 1, params.HoodiGenesisHash, true) // Check the existence of deleted data. DeleteBloombits(db, 0, 0, 2) check(0, 0, params.MainnetGenesisHash, false) check(0, 0, params.SepoliaGenesisHash, false) + check(0, 0, params.HoodiGenesisHash, false) check(0, 1, params.MainnetGenesisHash, false) check(0, 1, params.SepoliaGenesisHash, false) + check(0, 1, params.HoodiGenesisHash, false) // Bit1 shouldn't be affect. check(1, 0, params.MainnetGenesisHash, true) check(1, 0, params.SepoliaGenesisHash, true) + check(1, 0, params.HoodiGenesisHash, true) check(1, 1, params.MainnetGenesisHash, true) check(1, 1, params.SepoliaGenesisHash, true) + check(1, 1, params.HoodiGenesisHash, true) } diff --git a/params/bootnodes.go b/params/bootnodes.go index 124765313a8..28e3395540d 100644 --- a/params/bootnodes.go +++ b/params/bootnodes.go @@ -28,6 +28,15 @@ var MainnetBootnodes = []string{ "enode://4aeb4ab6c14b23e2c4cfdce879c04b0748a20d8e9b59e25ded2a08143e265c6c25936e74cbc8e641e3312ca288673d91f2f93f8e277de3cfa444ecdaaf982052@157.90.35.166:30303", // bootnode-hetzner-fsn } +// HoodiBootnodes are the enode URLs of the P2P bootstrap nodes running on the +// Hoodi test network. +var HoodiBootnodes = []string{ + // EF DevOps + "enode://2112dd3839dd752813d4df7f40936f06829fc54c0e051a93967c26e5f5d27d99d886b57b4ffcc3c475e930ec9e79c56ef1dbb7d86ca5ee83a9d2ccf36e5c240c@134.209.138.84:30303", + "enode://60203fcb3524e07c5df60a14ae1c9c5b24023ea5d47463dfae051d2c9f3219f309657537576090ca0ae641f73d419f53d8e8000d7a464319d4784acd7d2abc41@209.38.124.160:30303", + "enode://8ae4a48101b2299597341263da0deb47cc38aa4d3ef4b7430b897d49bfa10eb1ccfe1655679b1ed46928ef177fbf21b86837bd724400196c508427a6f41602cd@134.199.184.23:30303", +} + // HoleskyBootnodes are the enode URLs of the P2P bootstrap nodes running on the // Holesky test network. var HoleskyBootnodes = []string{ @@ -84,6 +93,8 @@ func KnownDNSNetwork(genesis common.Hash, protocol string) string { net = "sepolia" case HoleskyGenesisHash: net = "holesky" + case HoodiGenesisHash: + net = "hoodi" default: return "" } diff --git a/params/config.go b/params/config.go index 593c70b1390..8f9e02583bf 100644 --- a/params/config.go +++ b/params/config.go @@ -31,6 +31,7 @@ var ( MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") HoleskyGenesisHash = common.HexToHash("0xb5f7f912443c940f21fd611f12828d75b534364ed9e95ca4e307729a4661bde4") SepoliaGenesisHash = common.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9") + HoodiGenesisHash = common.HexToHash("0xbbe312868b376a3001692a646dd2d7d1e4406380dfd86b98aa8a34d1557c971b") ) func newUint64(val uint64) *uint64 { return &val } @@ -125,6 +126,36 @@ var ( Prague: DefaultPragueBlobConfig, }, } + // HoodiChainConfig contains the chain parameters to run a node on the Hoodi test network. + HoodiChainConfig = &ChainConfig{ + ChainID: big.NewInt(560048), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: nil, + GrayGlacierBlock: nil, + TerminalTotalDifficulty: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + ShanghaiTime: newUint64(0), + CancunTime: newUint64(0), + PragueTime: newUint64(1742999832), + DepositContractAddress: common.HexToAddress("0x00000000219ab540356cBB839Cbe05303d7705Fa"), + Ethash: new(EthashConfig), + BlobScheduleConfig: &BlobScheduleConfig{ + Cancun: DefaultCancunBlobConfig, + Prague: DefaultPragueBlobConfig, + }, + } // AllEthashProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Ethash consensus. AllEthashProtocolChanges = &ChainConfig{ @@ -338,6 +369,7 @@ var NetworkNames = map[string]string{ MainnetChainConfig.ChainID.String(): "mainnet", SepoliaChainConfig.ChainID.String(): "sepolia", HoleskyChainConfig.ChainID.String(): "holesky", + HoodiChainConfig.ChainID.String(): "hoodi", } // ChainConfig is the core config which determines the blockchain settings. From e5f4b274b39df4e8a44b9bc14294cef8ebb91bd2 Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Tue, 18 Mar 2025 12:40:38 +0100 Subject: [PATCH 013/658] internal/ethapi: fix logs bloom in eth_simulateV1 (#31411) Geth was returning empty logsBloom for the simulated block. --- internal/ethapi/simulate.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index ac763724247..097de8b0b01 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -217,9 +217,13 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, if err := sim.sanitizeCall(&call, sim.state, header, blockContext, &gasUsed); err != nil { return nil, nil, err } - tx := call.ToTransaction(types.DynamicFeeTxType) + var ( + tx = call.ToTransaction(types.DynamicFeeTxType) + txHash = tx.Hash() + ) txes[i] = tx - tracer.reset(tx.Hash(), uint(i)) + tracer.reset(txHash, uint(i)) + sim.state.SetTxContext(txHash, i) // EoA check is always skipped, even in validation mode. msg := call.ToMessage(header.BaseFee, !sim.validate, true) result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) From fe640f8f57526e8e0790df18a7f6190adecba08d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Tue, 18 Mar 2025 13:46:23 +0100 Subject: [PATCH 014/658] core/filtermaps: fixed indexer checkpoint initialization (#31419) This PR fixes a bug in the `lastMapBoundaryBefore` logic that resulted in incorrect checkpoint initialization (started rendering from the previous epoch boundary which caused the `needTailEpoch` check to fail). Apparently the bug was present before but went unnoticed because `needTailEpoch` behaved differently. Fixes https://github.com/ethereum/go-ethereum/issues/31413 --- core/filtermaps/indexer.go | 3 +++ core/filtermaps/map_renderer.go | 29 ++++++++++++++++------------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/core/filtermaps/indexer.go b/core/filtermaps/indexer.go index addda03df42..835e84cc0ae 100644 --- a/core/filtermaps/indexer.go +++ b/core/filtermaps/indexer.go @@ -346,6 +346,9 @@ func (f *FilterMaps) needTailEpoch(epoch uint32) bool { if epoch > firstEpoch { return true } + if (epoch+1)<= f.indexedRange.maps.AfterLast() { + return true + } if epoch+1 < firstEpoch { return false } diff --git a/core/filtermaps/map_renderer.go b/core/filtermaps/map_renderer.go index 4f45a7f2b21..ba2d399e88f 100644 --- a/core/filtermaps/map_renderer.go +++ b/core/filtermaps/map_renderer.go @@ -185,27 +185,30 @@ func (f *FilterMaps) lastCanonicalMapBoundaryBefore(renderBefore 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.maps.AfterLast() == 0 { +func (f *FilterMaps) lastMapBoundaryBefore(renderBefore uint32) (uint32, bool) { + if !f.indexedRange.initialized || f.indexedRange.maps.AfterLast() == 0 || renderBefore == 0 { return 0, false } - if mapIndex > f.indexedRange.maps.AfterLast() { - mapIndex = f.indexedRange.maps.AfterLast() + afterLastFullMap := f.indexedRange.maps.AfterLast() + if afterLastFullMap > 0 && f.indexedRange.headIndexed { + afterLastFullMap-- // last map is not full } - if mapIndex > f.indexedRange.maps.First() { - return mapIndex - 1, true + firstRendered := min(renderBefore-1, afterLastFullMap) + if firstRendered == 0 { + return 0, false } - 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 - } + if firstRendered >= f.indexedRange.maps.First() { + return firstRendered - 1, true + } + if firstRendered+f.mapsPerEpoch > f.indexedRange.maps.First() { + firstRendered = min(firstRendered, f.indexedRange.maps.First()-f.mapsPerEpoch+f.indexedRange.tailPartialEpoch) } else { - mapIndex = (mapIndex >> f.logMapsPerEpoch) << f.logMapsPerEpoch + firstRendered = (firstRendered >> f.logMapsPerEpoch) << f.logMapsPerEpoch } - if mapIndex == 0 { + if firstRendered == 0 { return 0, false } - return mapIndex - 1, true + return firstRendered - 1, true } // emptyFilterMap returns an empty filter map. From dba58830e98dccc381fcc2feb9eba259789cdf5c Mon Sep 17 00:00:00 2001 From: Delweng Date: Tue, 18 Mar 2025 21:32:29 +0800 Subject: [PATCH 015/658] cmd: set name to chaindata for all the opened db (#31352) When I'm running `geth import --metrics`, the metrics is different to normal `geth --metrics`, so the grafana dashboard needs to be updated, eg: `eth_db_chaindata_disk_read` vs `disk_read`. So I think we should always set the name to `eth/db/chaindata` for more convenient. --------- Signed-off-by: jsvisa --- cmd/utils/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index d60f9c64d5e..77dac8bd1ab 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2076,7 +2076,7 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb. } chainDb = remotedb.New(client) default: - chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", cache, handles, ctx.String(AncientFlag.Name), "", readonly) + chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", cache, handles, ctx.String(AncientFlag.Name), "eth/db/chaindata/", readonly) } if err != nil { Fatalf("Could not open database: %v", err) From c4f045071031ed19534915a7ff5792354669b3db Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 18 Mar 2025 09:41:34 -0500 Subject: [PATCH 016/658] ethclient: Add EstimateGasAtBlock[Hash] to estimate against a specific block (#27508) The main use case I see of this is that it allows users to estimate gas against the same state that they query for their nonce, and the same state they base the data of their transaction against. This helps ensure that gas estimation won't fail and the transaction won't revert on-chain because of a mismatch between the state used for gas estimation and the state used to generate the inputs to gas estimation or the transaction's nonce when submitted to the mempool. This PR also updates the EstimateGas comment based on the new geth `eth_estimateGas` default of using latest state as of v1.12.0: https://github.com/ethereum/go-ethereum/pull/24363 --------- Co-authored-by: Felix Lange --- ethclient/ethclient.go | 32 +++++++++++++++++++++++++++++--- ethclient/ethclient_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 3d6a28eabfa..872b3b03dc1 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -640,9 +640,13 @@ func (ec *Client) FeeHistory(ctx context.Context, blockCount uint64, lastBlock * } // EstimateGas tries to estimate the gas needed to execute a specific transaction based on -// the current pending state of the backend blockchain. There is no guarantee that this is -// the true gas limit requirement as other transactions may be added or removed by miners, -// but it should provide a basis for setting a reasonable default. +// the current state of the backend blockchain. There is no guarantee that this is the +// true gas limit requirement as other transactions may be added or removed by miners, but +// it should provide a basis for setting a reasonable default. +// +// Note that the state used by this method is implementation-defined by the remote RPC +// server, but it's reasonable to assume that it will either be the pending or latest +// state. func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) { var hex hexutil.Uint64 err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg)) @@ -652,6 +656,28 @@ func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64 return uint64(hex), nil } +// EstimateGasAtBlock is almost the same as EstimateGas except that it selects the block height +// instead of using the remote RPC's default state for gas estimation. +func (ec *Client) EstimateGasAtBlock(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) (uint64, error) { + var hex hexutil.Uint64 + err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg), toBlockNumArg(blockNumber)) + if err != nil { + return 0, err + } + return uint64(hex), nil +} + +// EstimateGasAtBlockHash is almost the same as EstimateGas except that it selects the block +// hash instead of using the remote RPC's default state for gas estimation. +func (ec *Client) EstimateGasAtBlockHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) (uint64, error) { + var hex hexutil.Uint64 + err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg), rpc.BlockNumberOrHashWithHash(blockHash, false)) + if err != nil { + return 0, err + } + return uint64(hex), nil +} + // SendTransaction injects a signed transaction into the pending pool for execution. // // If the transaction was a contract creation use the TransactionReceipt method to get the diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 787cad3f965..29e311c1b4e 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -592,6 +592,33 @@ func testAtFunctions(t *testing.T, client *rpc.Client) { if !bytes.Equal(code, penCode) { t.Fatalf("unexpected code: %v %v", code, penCode) } + // Use HeaderByNumber to get a header for EstimateGasAtBlock and EstimateGasAtBlockHash + latestHeader, err := ec.HeaderByNumber(context.Background(), nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + // EstimateGasAtBlock + msg := ethereum.CallMsg{ + From: testAddr, + To: &common.Address{}, + Gas: 21000, + Value: big.NewInt(1), + } + gas, err := ec.EstimateGasAtBlock(context.Background(), msg, latestHeader.Number) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if gas != 21000 { + t.Fatalf("unexpected gas limit: %v", gas) + } + // EstimateGasAtBlockHash + gas, err = ec.EstimateGasAtBlockHash(context.Background(), msg, latestHeader.Hash()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if gas != 21000 { + t.Fatalf("unexpected gas limit: %v", gas) + } } func testTransactionSender(t *testing.T, client *rpc.Client) { From 930836ed66c9ff8eaffc8cd168cd9c76fad82c29 Mon Sep 17 00:00:00 2001 From: minh-bq Date: Wed, 19 Mar 2025 13:20:50 +0700 Subject: [PATCH 017/658] core/txpool, eth: add GetRLP to transaction pool (#31307) Currently, when answering GetPooledTransaction request, txpool.Get() is used. When the requested hash is blob transaction, blobpool.Get() is called. This function loads the RLP-encoded transaction from limbo then decodes and returns. Later, in answerGetPooledTransactions, we need to RLP encode again. This decode then encode is wasteful. This commit adds GetRLP to transaction pool interface so that answerGetPooledTransactions can use the RLP-encoded from limbo directly. --------- Co-authored-by: Gary Rong --- core/txpool/blobpool/blobpool.go | 24 +++++- core/txpool/legacypool/legacypool.go | 15 ++++ core/txpool/subpool.go | 3 + core/txpool/txpool.go | 11 +++ eth/handler.go | 4 + eth/handler_test.go | 15 ++++ eth/protocols/eth/handler.go | 4 + eth/protocols/eth/handler_test.go | 116 ++++++++++++++++++++++++--- eth/protocols/eth/handlers.go | 15 ++-- 9 files changed, 184 insertions(+), 23 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 7ad95612bfa..59a56450401 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1189,8 +1189,7 @@ func (p *BlobPool) Has(hash common.Hash) bool { return p.lookup.exists(hash) } -// Get returns a transaction if it is contained in the pool, or nil otherwise. -func (p *BlobPool) Get(hash common.Hash) *types.Transaction { +func (p *BlobPool) getRLP(hash common.Hash) []byte { // Track the amount of time waiting to retrieve a fully resolved blob tx from // the pool and the amount of time actually spent on pulling the data from disk. getStart := time.Now() @@ -1212,14 +1211,31 @@ func (p *BlobPool) Get(hash common.Hash) *types.Transaction { log.Error("Tracked blob transaction missing from store", "hash", hash, "id", id, "err", err) return nil } + return data +} + +// Get returns a transaction if it is contained in the pool, or nil otherwise. +func (p *BlobPool) Get(hash common.Hash) *types.Transaction { + data := p.getRLP(hash) + if len(data) == 0 { + return nil + } item := new(types.Transaction) - if err = rlp.DecodeBytes(data, item); err != nil { - log.Error("Blobs corrupted for traced transaction", "hash", hash, "id", id, "err", err) + if err := rlp.DecodeBytes(data, item); err != nil { + id, _ := p.lookup.storeidOfTx(hash) + + log.Error("Blobs corrupted for traced transaction", + "hash", hash, "id", id, "err", err) return nil } return item } +// GetRLP returns a RLP-encoded transaction if it is contained in the pool. +func (p *BlobPool) GetRLP(hash common.Hash) []byte { + return p.getRLP(hash) +} + // GetBlobs returns a number of blobs are proofs for the given versioned hashes. // This is a utility method for the engine API, enabling consensus clients to // retrieve blobs from the pools directly instead of the network. diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 8c09d48695c..78be81480fb 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -40,6 +40,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" "github.com/holiman/uint256" ) @@ -1010,6 +1011,20 @@ func (pool *LegacyPool) get(hash common.Hash) *types.Transaction { return pool.all.Get(hash) } +// GetRLP returns a RLP-encoded transaction if it is contained in the pool. +func (pool *LegacyPool) GetRLP(hash common.Hash) []byte { + tx := pool.all.Get(hash) + if tx == nil { + return nil + } + encoded, err := rlp.EncodeToBytes(tx) + if err != nil { + log.Error("Failed to encoded transaction in legacy pool", "hash", hash, "err", err) + return nil + } + return encoded +} + // GetBlobs is not supported by the legacy transaction pool, it is just here to // implement the txpool.SubPool interface. func (pool *LegacyPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof) { diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index 242af021360..1392cfb2745 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -124,6 +124,9 @@ type SubPool interface { // Get returns a transaction if it is contained in the pool, or nil otherwise. Get(hash common.Hash) *types.Transaction + // GetRLP returns a RLP-encoded transaction if it is contained in the pool. + GetRLP(hash common.Hash) []byte + // GetBlobs returns a number of blobs are proofs for the given versioned hashes. // This is a utility method for the engine API, enabling consensus clients to // retrieve blobs from the pools directly instead of the network. diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 55812415f98..042e3d36d9d 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -309,6 +309,17 @@ func (p *TxPool) Get(hash common.Hash) *types.Transaction { return nil } +// GetRLP returns a RLP-encoded transaction if it is contained in the pool. +func (p *TxPool) GetRLP(hash common.Hash) []byte { + for _, subpool := range p.subpools { + encoded := subpool.GetRLP(hash) + if len(encoded) != 0 { + return encoded + } + } + return nil +} + // GetBlobs returns a number of blobs are proofs for the given versioned hashes. // This is a utility method for the engine API, enabling consensus clients to // retrieve blobs from the pools directly instead of the network. diff --git a/eth/handler.go b/eth/handler.go index 6ac890902b6..4c83f5613cf 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -67,6 +67,10 @@ type txPool interface { // tx hash. Get(hash common.Hash) *types.Transaction + // GetRLP retrieves the RLP-encoded transaction from local txpool + // with given tx hash. + GetRLP(hash common.Hash) []byte + // Add should add the given transactions to the pool. Add(txs []*types.Transaction, sync bool) []error diff --git a/eth/handler_test.go b/eth/handler_test.go index d5d46a3c65a..0c6b9854e6f 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" "github.com/holiman/uint256" ) @@ -78,6 +79,20 @@ func (p *testTxPool) Get(hash common.Hash) *types.Transaction { return p.pool[hash] } +// Get retrieves the transaction from local txpool with given +// tx hash. +func (p *testTxPool) GetRLP(hash common.Hash) []byte { + p.lock.Lock() + defer p.lock.Unlock() + + tx := p.pool[hash] + if tx != nil { + blob, _ := rlp.EncodeToBytes(tx) + return blob + } + return nil +} + // Add appends a batch of transactions to the pool, and notifies any // listeners if the addition channel is non nil func (p *testTxPool) Add(txs []*types.Transaction, sync bool) []error { diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go index a0848137fad..eca6777bd63 100644 --- a/eth/protocols/eth/handler.go +++ b/eth/protocols/eth/handler.go @@ -86,6 +86,10 @@ type Backend interface { type TxPool interface { // Get retrieves the transaction from the local txpool with the given hash. Get(hash common.Hash) *types.Transaction + + // GetRLP retrieves the RLP-encoded transaction from the local txpool with + // the given hash. + GetRLP(hash common.Hash) []byte } // MakeProtocols constructs the P2P protocol definitions for `eth`. diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index f92599dba79..36f5e90c9ac 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -18,9 +18,11 @@ package eth import ( "bytes" + "crypto/sha256" "math" "math/big" "math/rand" + "os" "testing" "time" @@ -30,15 +32,18 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/txpool" + "github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" ) var ( @@ -62,12 +67,12 @@ type testBackend struct { // newTestBackend creates an empty chain and wraps it into a mock backend. func newTestBackend(blocks int) *testBackend { - return newTestBackendWithGenerator(blocks, false, nil) + return newTestBackendWithGenerator(blocks, false, false, nil) } // newTestBackendWithGenerator creates a chain with a number of explicitly defined blocks and // wraps it into a mock backend. -func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, *core.BlockGen)) *testBackend { +func newTestBackendWithGenerator(blocks int, shanghai bool, cancun bool, generator func(int, *core.BlockGen)) *testBackend { var ( // Create a database pre-initialize with a genesis block db = rawdb.NewMemoryDatabase() @@ -99,9 +104,21 @@ func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, } } + if cancun { + config.CancunTime = u64(0) + config.BlobScheduleConfig = ¶ms.BlobScheduleConfig{ + Cancun: ¶ms.BlobConfig{ + Target: 3, + Max: 6, + UpdateFraction: params.DefaultCancunBlobConfig.UpdateFraction, + }, + } + } + gspec := &core.Genesis{ - Config: config, - Alloc: types.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}}, + Config: config, + Alloc: types.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}}, + Difficulty: common.Big0, } chain, _ := core.NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil) @@ -115,8 +132,12 @@ func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, txconfig := legacypool.DefaultConfig txconfig.Journal = "" // Don't litter the disk with test journals - pool := legacypool.New(txconfig, chain) - txpool, _ := txpool.New(txconfig.PriceLimit, chain, []txpool.SubPool{pool}) + storage, _ := os.MkdirTemp("", "blobpool-") + defer os.RemoveAll(storage) + + blobPool := blobpool.New(blobpool.Config{Datadir: storage}, chain) + legacyPool := legacypool.New(txconfig, chain) + txpool, _ := txpool.New(txconfig.PriceLimit, chain, []txpool.SubPool{legacyPool, blobPool}) return &testBackend{ db: db, @@ -351,7 +372,7 @@ func testGetBlockBodies(t *testing.T, protocol uint) { } } - backend := newTestBackendWithGenerator(maxBodiesServe+15, true, gen) + backend := newTestBackendWithGenerator(maxBodiesServe+15, true, false, gen) defer backend.close() peer, _ := newTestPeer("peer", protocol, backend) @@ -471,7 +492,7 @@ func testGetBlockReceipts(t *testing.T, protocol uint) { } } // Assemble the test environment - backend := newTestBackendWithGenerator(4, false, generator) + backend := newTestBackendWithGenerator(4, false, false, generator) defer backend.close() peer, _ := newTestPeer("peer", protocol, backend) @@ -548,7 +569,7 @@ func setup() (*testBackend, *testPeer) { block.SetExtra([]byte("yeehaw")) } } - backend := newTestBackendWithGenerator(maxBodiesServe+15, true, gen) + backend := newTestBackendWithGenerator(maxBodiesServe+15, true, false, gen) peer, _ := newTestPeer("peer", ETH68, backend) // Discard all messages go func() { @@ -573,3 +594,80 @@ func FuzzEthProtocolHandlers(f *testing.F) { handler(backend, decoder{msg: msg}, peer.Peer) }) } + +func TestGetPooledTransaction(t *testing.T) { + t.Run("blobTx", func(t *testing.T) { + testGetPooledTransaction(t, true) + }) + t.Run("legacyTx", func(t *testing.T) { + testGetPooledTransaction(t, false) + }) +} + +func testGetPooledTransaction(t *testing.T, blobTx bool) { + var ( + emptyBlob = kzg4844.Blob{} + emptyBlobs = []kzg4844.Blob{emptyBlob} + emptyBlobCommit, _ = kzg4844.BlobToCommitment(&emptyBlob) + emptyBlobProof, _ = kzg4844.ComputeBlobProof(&emptyBlob, emptyBlobCommit) + emptyBlobHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit) + ) + backend := newTestBackendWithGenerator(0, true, true, nil) + defer backend.close() + + peer, _ := newTestPeer("peer", ETH68, backend) + defer peer.close() + + var ( + tx *types.Transaction + err error + signer = types.NewCancunSigner(params.TestChainConfig.ChainID) + ) + if blobTx { + tx, err = types.SignNewTx(testKey, signer, &types.BlobTx{ + ChainID: uint256.MustFromBig(params.TestChainConfig.ChainID), + Nonce: 0, + GasTipCap: uint256.NewInt(20_000_000_000), + GasFeeCap: uint256.NewInt(21_000_000_000), + Gas: 21000, + To: testAddr, + BlobHashes: []common.Hash{emptyBlobHash}, + BlobFeeCap: uint256.MustFromBig(common.Big1), + Sidecar: &types.BlobTxSidecar{ + Blobs: emptyBlobs, + Commitments: []kzg4844.Commitment{emptyBlobCommit}, + Proofs: []kzg4844.Proof{emptyBlobProof}, + }, + }) + if err != nil { + t.Fatal(err) + } + } else { + tx, err = types.SignTx( + types.NewTransaction(0, testAddr, big.NewInt(10_000), params.TxGas, big.NewInt(1_000_000_000), nil), + signer, + testKey, + ) + if err != nil { + t.Fatal(err) + } + } + errs := backend.txpool.Add([]*types.Transaction{tx}, true) + for _, err := range errs { + if err != nil { + t.Fatal(err) + } + } + + // Send the hash request and verify the response + p2p.Send(peer.app, GetPooledTransactionsMsg, GetPooledTransactionsPacket{ + RequestId: 123, + GetPooledTransactionsRequest: []common.Hash{tx.Hash()}, + }) + if err := p2p.ExpectMsg(peer.app, PooledTransactionsMsg, PooledTransactionsPacket{ + RequestId: 123, + PooledTransactionsResponse: []*types.Transaction{tx}, + }); err != nil { + t.Errorf("pooled transaction mismatch: %v", err) + } +} diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index b3886270f3d..ab56be7ffc6 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -397,18 +397,13 @@ func answerGetPooledTransactions(backend Backend, query GetPooledTransactionsReq break } // Retrieve the requested transaction, skipping if unknown to us - tx := backend.TxPool().Get(hash) - if tx == nil { + encoded := backend.TxPool().GetRLP(hash) + if len(encoded) == 0 { continue } - // If known, encode and queue for response packet - if encoded, err := rlp.EncodeToBytes(tx); err != nil { - log.Error("Failed to encode transaction", "err", err) - } else { - hashes = append(hashes, hash) - txs = append(txs, encoded) - bytes += len(encoded) - } + hashes = append(hashes, hash) + txs = append(txs, encoded) + bytes += len(encoded) } return hashes, txs } From b47e4d5b38b34c045cb10af6c0b5603c285310cd Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 19 Mar 2025 07:21:40 +0100 Subject: [PATCH 018/658] core/types: reduce allocs in transaction signing (#31258) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR roughly halves the number of allocations needed to compute the sigHash for a transaction. This sigHash is used whenever we recover a signature of a transaction, so quite often. During a recent benchmark full syncing on Holesky, roughly 2.8% of all allocations were happening here because the fields from the transaction would be copied multiple times. ``` 66168733 153175654 (flat, cum) 2.80% of Total . . 368:func (s londonSigner) Hash(tx *Transaction) common.Hash { . . 369: if tx.Type() != DynamicFeeTxType { . . 370: return s.eip2930Signer.Hash(tx) . . 371: } . 19169966 372: return prefixedRlpHash( . . 373: tx.Type(), 26442187 26442187 374: []interface{}{ . . 375: s.chainId, 6848616 6848616 376: tx.Nonce(), . 19694077 377: tx.GasTipCap(), . 18956774 378: tx.GasFeeCap(), 6357089 6357089 379: tx.Gas(), . 12321050 380: tx.To(), . 16865054 381: tx.Value(), 13435187 13435187 382: tx.Data(), 13085654 13085654 383: tx.AccessList(), . . 384: }) . . 385:} ``` This PR reduces the allocations and speeds up the computation of the sigHash by ~22%, which is quite significantly given that this operation involves a call to Keccak ``` // BenchmarkHash-8 440082 2639 ns/op 384 B/op 13 allocs/op // BenchmarkHash-8 493566 2033 ns/op 240 B/op 6 allocs/op ``` ``` Hash-8 2.691µ ± 8% 2.097µ ± 9% -22.07% (p=0.000 n=10) ``` It also kinda cleans up stuff in my opinion, since the transaction should itself know best how to compute the sighash ![Screenshot_2025-02-25_13-52-41](https://github.com/user-attachments/assets/e2b268aa-e137-417d-926b-f3619daef748) --------- Co-authored-by: Gary Rong --- core/types/transaction.go | 3 ++ core/types/transaction_signing.go | 70 +++---------------------------- core/types/transaction_test.go | 17 ++++++++ core/types/tx_access_list.go | 15 +++++++ core/types/tx_blob.go | 18 ++++++++ core/types/tx_dynamic_fee.go | 16 +++++++ core/types/tx_legacy.go | 13 ++++++ core/types/tx_setcode.go | 17 ++++++++ 8 files changed, 105 insertions(+), 64 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index 4e48248b4a8..a2f41046351 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -100,6 +100,9 @@ type TxData interface { encode(*bytes.Buffer) error decode([]byte) error + + // sigHash returns the hash of the transaction that is ought to be signed + sigHash(*big.Int) common.Hash } // EncodeRLP implements rlp.Encoder diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 030fc472a00..89c08aeddd5 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -233,20 +233,7 @@ func (s pragueSigner) Hash(tx *Transaction) common.Hash { if tx.Type() != SetCodeTxType { return s.cancunSigner.Hash(tx) } - return prefixedRlpHash( - tx.Type(), - []interface{}{ - s.chainId, - tx.Nonce(), - tx.GasTipCap(), - tx.GasFeeCap(), - tx.Gas(), - tx.To(), - tx.Value(), - tx.Data(), - tx.AccessList(), - tx.SetCodeAuthorizations(), - }) + return tx.inner.sigHash(s.chainId) } type cancunSigner struct{ londonSigner } @@ -301,21 +288,7 @@ func (s cancunSigner) Hash(tx *Transaction) common.Hash { if tx.Type() != BlobTxType { return s.londonSigner.Hash(tx) } - return prefixedRlpHash( - tx.Type(), - []interface{}{ - s.chainId, - tx.Nonce(), - tx.GasTipCap(), - tx.GasFeeCap(), - tx.Gas(), - tx.To(), - tx.Value(), - tx.Data(), - tx.AccessList(), - tx.BlobGasFeeCap(), - tx.BlobHashes(), - }) + return tx.inner.sigHash(s.chainId) } type londonSigner struct{ eip2930Signer } @@ -369,19 +342,7 @@ func (s londonSigner) Hash(tx *Transaction) common.Hash { if tx.Type() != DynamicFeeTxType { return s.eip2930Signer.Hash(tx) } - return prefixedRlpHash( - tx.Type(), - []interface{}{ - s.chainId, - tx.Nonce(), - tx.GasTipCap(), - tx.GasFeeCap(), - tx.Gas(), - tx.To(), - tx.Value(), - tx.Data(), - tx.AccessList(), - }) + return tx.inner.sigHash(s.chainId) } type eip2930Signer struct{ EIP155Signer } @@ -444,18 +405,7 @@ func (s eip2930Signer) Hash(tx *Transaction) common.Hash { case LegacyTxType: return s.EIP155Signer.Hash(tx) case AccessListTxType: - return prefixedRlpHash( - tx.Type(), - []interface{}{ - s.chainId, - tx.Nonce(), - tx.GasPrice(), - tx.Gas(), - tx.To(), - tx.Value(), - tx.Data(), - tx.AccessList(), - }) + return tx.inner.sigHash(s.chainId) default: // This _should_ not happen, but in case someone sends in a bad // json struct via RPC, it's probably more prudent to return an @@ -525,15 +475,7 @@ func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big // Hash returns the hash to be signed by the sender. // It does not uniquely identify the transaction. func (s EIP155Signer) Hash(tx *Transaction) common.Hash { - return rlpHash([]interface{}{ - tx.Nonce(), - tx.GasPrice(), - tx.Gas(), - tx.To(), - tx.Value(), - tx.Data(), - s.chainId, uint(0), uint(0), - }) + return tx.inner.sigHash(s.chainId) } // HomesteadSigner implements Signer interface using the @@ -597,7 +539,7 @@ func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v * // Hash returns the hash to be signed by the sender. // It does not uniquely identify the transaction. func (fs FrontierSigner) Hash(tx *Transaction) common.Hash { - return rlpHash([]interface{}{ + return rlpHash([]any{ tx.Nonce(), tx.GasPrice(), tx.Gas(), diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index 17a7dda3578..8922448d97d 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -576,3 +576,20 @@ func TestYParityJSONUnmarshalling(t *testing.T) { } } } + +func BenchmarkHash(b *testing.B) { + signer := NewLondonSigner(big.NewInt(1)) + to := common.Address{} + tx := NewTx(&DynamicFeeTx{ + ChainID: big.NewInt(123), + Nonce: 1, + Gas: 1000000, + To: &to, + Value: big.NewInt(1), + GasTipCap: big.NewInt(500), + GasFeeCap: big.NewInt(500), + }) + for i := 0; i < b.N; i++ { + signer.Hash(tx) + } +} diff --git a/core/types/tx_access_list.go b/core/types/tx_access_list.go index 730a77b7528..915de9a8ab2 100644 --- a/core/types/tx_access_list.go +++ b/core/types/tx_access_list.go @@ -127,3 +127,18 @@ func (tx *AccessListTx) encode(b *bytes.Buffer) error { func (tx *AccessListTx) decode(input []byte) error { return rlp.DecodeBytes(input, tx) } + +func (tx *AccessListTx) sigHash(chainID *big.Int) common.Hash { + return prefixedRlpHash( + AccessListTxType, + []any{ + chainID, + tx.Nonce, + tx.GasPrice, + tx.Gas, + tx.To, + tx.Value, + tx.Data, + tx.AccessList, + }) +} diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go index 32401db101b..9b1d53958fe 100644 --- a/core/types/tx_blob.go +++ b/core/types/tx_blob.go @@ -259,3 +259,21 @@ func (tx *BlobTx) decode(input []byte) error { } return nil } + +func (tx *BlobTx) sigHash(chainID *big.Int) common.Hash { + return prefixedRlpHash( + BlobTxType, + []any{ + chainID, + tx.Nonce, + tx.GasTipCap, + tx.GasFeeCap, + tx.Gas, + tx.To, + tx.Value, + tx.Data, + tx.AccessList, + tx.BlobFeeCap, + tx.BlobHashes, + }) +} diff --git a/core/types/tx_dynamic_fee.go b/core/types/tx_dynamic_fee.go index 981755cf700..bba81464f84 100644 --- a/core/types/tx_dynamic_fee.go +++ b/core/types/tx_dynamic_fee.go @@ -123,3 +123,19 @@ func (tx *DynamicFeeTx) encode(b *bytes.Buffer) error { func (tx *DynamicFeeTx) decode(input []byte) error { return rlp.DecodeBytes(input, tx) } + +func (tx *DynamicFeeTx) sigHash(chainID *big.Int) common.Hash { + return prefixedRlpHash( + DynamicFeeTxType, + []any{ + chainID, + tx.Nonce, + tx.GasTipCap, + tx.GasFeeCap, + tx.Gas, + tx.To, + tx.Value, + tx.Data, + tx.AccessList, + }) +} diff --git a/core/types/tx_legacy.go b/core/types/tx_legacy.go index 71025b78fc0..49f0a98809f 100644 --- a/core/types/tx_legacy.go +++ b/core/types/tx_legacy.go @@ -123,3 +123,16 @@ func (tx *LegacyTx) encode(*bytes.Buffer) error { func (tx *LegacyTx) decode([]byte) error { panic("decode called on LegacyTx)") } + +// OBS: This is the post-EIP155 hash, the pre-EIP155 does not contain a chainID. +func (tx *LegacyTx) sigHash(chainID *big.Int) common.Hash { + return rlpHash([]any{ + tx.Nonce, + tx.GasPrice, + tx.Gas, + tx.To, + tx.Value, + tx.Data, + chainID, uint(0), uint(0), + }) +} diff --git a/core/types/tx_setcode.go b/core/types/tx_setcode.go index 894bac10a33..b8e38ef1f70 100644 --- a/core/types/tx_setcode.go +++ b/core/types/tx_setcode.go @@ -223,3 +223,20 @@ func (tx *SetCodeTx) encode(b *bytes.Buffer) error { func (tx *SetCodeTx) decode(input []byte) error { return rlp.DecodeBytes(input, tx) } + +func (tx *SetCodeTx) sigHash(chainID *big.Int) common.Hash { + return prefixedRlpHash( + SetCodeTxType, + []any{ + chainID, + tx.Nonce, + tx.GasTipCap, + tx.GasFeeCap, + tx.Gas, + tx.To, + tx.Value, + tx.Data, + tx.AccessList, + tx.AuthList, + }) +} From 80b8d7a13c20254a9cfb9f7cbca1ab00aa6a3b50 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 19 Mar 2025 16:05:44 +0100 Subject: [PATCH 019/658] core/types: cleanup tx signer logic (#31434) This removes the signer type-train in favor of defining a single object that can handle all tx types. Supported types are enabled via a map. Notably, the new signer also supports disabling legacy transactions. --- cmd/evm/internal/t8ntool/tx_iterator.go | 2 +- core/types/transaction_signing.go | 291 ++++++++---------------- params/forks/forks.go | 4 +- 3 files changed, 98 insertions(+), 199 deletions(-) diff --git a/cmd/evm/internal/t8ntool/tx_iterator.go b/cmd/evm/internal/t8ntool/tx_iterator.go index d4ebb4b399e..047626c56b7 100644 --- a/cmd/evm/internal/t8ntool/tx_iterator.go +++ b/cmd/evm/internal/t8ntool/tx_iterator.go @@ -102,7 +102,7 @@ func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Tran if tx.protected { signed, err = types.SignTx(tx.tx, signer, tx.key) } else { - signed, err = types.SignTx(tx.tx, types.FrontierSigner{}, tx.key) + signed, err = types.SignTx(tx.tx, types.HomesteadSigner{}, tx.key) } if err != nil { return nil, NewError(ErrorJson, fmt.Errorf("tx %d: failed to sign tx: %v", i, err)) diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 89c08aeddd5..01aa67c6ba4 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -20,11 +20,13 @@ import ( "crypto/ecdsa" "errors" "fmt" + "maps" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/params/forks" ) var ErrInvalidChainId = errors.New("invalid chain id for signer") @@ -178,120 +180,122 @@ type Signer interface { Equal(Signer) bool } -type pragueSigner struct{ cancunSigner } - -// NewPragueSigner returns a signer that accepts -// - EIP-7702 set code transactions -// - EIP-4844 blob transactions -// - EIP-1559 dynamic fee transactions -// - EIP-2930 access list transactions, -// - EIP-155 replay protected transactions, and -// - legacy Homestead transactions. -func NewPragueSigner(chainId *big.Int) Signer { - signer, _ := NewCancunSigner(chainId).(cancunSigner) - return pragueSigner{signer} +// modernSigner is the signer implementation that handles non-legacy transaction types. +// For legacy transactions, it defers to one of the legacy signers (frontier, homestead, eip155). +type modernSigner struct { + txtypes map[byte]struct{} + chainID *big.Int + legacy Signer } -func (s pragueSigner) Sender(tx *Transaction) (common.Address, error) { - if tx.Type() != SetCodeTxType { - return s.cancunSigner.Sender(tx) +func newModernSigner(chainID *big.Int, fork forks.Fork) Signer { + if chainID == nil || chainID.Sign() <= 0 { + panic(fmt.Sprintf("invalid chainID %v", chainID)) } - V, R, S := tx.RawSignatureValues() - - // Set code txs are defined to use 0 and 1 as their recovery - // id, add 27 to become equivalent to unprotected Homestead signatures. - V = new(big.Int).Add(V, big.NewInt(27)) - if tx.ChainId().Cmp(s.chainId) != 0 { - return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId) + s := &modernSigner{ + chainID: chainID, + txtypes: make(map[byte]struct{}, 4), } - return recoverPlain(s.Hash(tx), R, S, V, true) + // configure legacy signer + switch { + case fork >= forks.SpuriousDragon: + s.legacy = NewEIP155Signer(chainID) + case fork >= forks.Homestead: + s.legacy = HomesteadSigner{} + default: + s.legacy = FrontierSigner{} + } + s.txtypes[LegacyTxType] = struct{}{} + // configure tx types + if fork >= forks.Berlin { + s.txtypes[AccessListTxType] = struct{}{} + } + if fork >= forks.London { + s.txtypes[DynamicFeeTxType] = struct{}{} + } + if fork >= forks.Cancun { + s.txtypes[BlobTxType] = struct{}{} + } + if fork >= forks.Prague { + s.txtypes[SetCodeTxType] = struct{}{} + } + return s } -func (s pragueSigner) Equal(s2 Signer) bool { - x, ok := s2.(pragueSigner) - return ok && x.chainId.Cmp(s.chainId) == 0 +func (s *modernSigner) ChainID() *big.Int { + return s.chainID } -func (s pragueSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { - txdata, ok := tx.inner.(*SetCodeTx) - if !ok { - return s.cancunSigner.SignatureValues(tx, sig) - } - // Check that chain ID of tx matches the signer. We also accept ID zero here, - // because it indicates that the chain ID was not specified in the tx. - if txdata.ChainID.Sign() != 0 && txdata.ChainID.CmpBig(s.chainId) != 0 { - return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId) - } - R, S, _ = decodeSignature(sig) - V = big.NewInt(int64(sig[64])) - return R, S, V, nil +func (s *modernSigner) Equal(s2 Signer) bool { + other, ok := s2.(*modernSigner) + return ok && s.chainID.Cmp(other.chainID) == 0 && maps.Equal(s.txtypes, other.txtypes) && s.legacy.Equal(other.legacy) } -// Hash returns the hash to be signed by the sender. -// It does not uniquely identify the transaction. -func (s pragueSigner) Hash(tx *Transaction) common.Hash { - if tx.Type() != SetCodeTxType { - return s.cancunSigner.Hash(tx) - } - return tx.inner.sigHash(s.chainId) +func (s *modernSigner) Hash(tx *Transaction) common.Hash { + return tx.inner.sigHash(s.chainID) } -type cancunSigner struct{ londonSigner } - -// NewCancunSigner returns a signer that accepts -// - EIP-4844 blob transactions -// - EIP-1559 dynamic fee transactions -// - EIP-2930 access list transactions, -// - EIP-155 replay protected transactions, and -// - legacy Homestead transactions. -func NewCancunSigner(chainId *big.Int) Signer { - return cancunSigner{londonSigner{eip2930Signer{NewEIP155Signer(chainId)}}} +func (s *modernSigner) supportsType(txtype byte) bool { + _, ok := s.txtypes[txtype] + return ok } -func (s cancunSigner) Sender(tx *Transaction) (common.Address, error) { - if tx.Type() != BlobTxType { - return s.londonSigner.Sender(tx) +func (s *modernSigner) Sender(tx *Transaction) (common.Address, error) { + tt := tx.Type() + if !s.supportsType(tt) { + return common.Address{}, ErrTxTypeNotSupported } - V, R, S := tx.RawSignatureValues() - // Blob txs are defined to use 0 and 1 as their recovery + if tt == LegacyTxType { + return s.legacy.Sender(tx) + } + if tx.ChainId().Cmp(s.chainID) != 0 { + return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainID) + } + // 'modern' txs are defined to use 0 and 1 as their recovery // id, add 27 to become equivalent to unprotected Homestead signatures. + V, R, S := tx.RawSignatureValues() V = new(big.Int).Add(V, big.NewInt(27)) - if tx.ChainId().Cmp(s.chainId) != 0 { - return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId) - } return recoverPlain(s.Hash(tx), R, S, V, true) } -func (s cancunSigner) Equal(s2 Signer) bool { - x, ok := s2.(cancunSigner) - return ok && x.chainId.Cmp(s.chainId) == 0 -} - -func (s cancunSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { - txdata, ok := tx.inner.(*BlobTx) - if !ok { - return s.londonSigner.SignatureValues(tx, sig) +func (s *modernSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { + tt := tx.Type() + if !s.supportsType(tt) { + return nil, nil, nil, ErrTxTypeNotSupported + } + if tt == LegacyTxType { + return s.legacy.SignatureValues(tx, sig) } // Check that chain ID of tx matches the signer. We also accept ID zero here, // because it indicates that the chain ID was not specified in the tx. - if txdata.ChainID.Sign() != 0 && txdata.ChainID.CmpBig(s.chainId) != 0 { - return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId) + if tx.inner.chainID().Sign() != 0 && tx.inner.chainID().Cmp(s.chainID) != 0 { + return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.inner.chainID(), s.chainID) } R, S, _ = decodeSignature(sig) V = big.NewInt(int64(sig[64])) return R, S, V, nil } -// Hash returns the hash to be signed by the sender. -// It does not uniquely identify the transaction. -func (s cancunSigner) Hash(tx *Transaction) common.Hash { - if tx.Type() != BlobTxType { - return s.londonSigner.Hash(tx) - } - return tx.inner.sigHash(s.chainId) +// NewPragueSigner returns a signer that accepts +// - EIP-7702 set code transactions +// - EIP-4844 blob transactions +// - EIP-1559 dynamic fee transactions +// - EIP-2930 access list transactions, +// - EIP-155 replay protected transactions, and +// - legacy Homestead transactions. +func NewPragueSigner(chainId *big.Int) Signer { + return newModernSigner(chainId, forks.Prague) } -type londonSigner struct{ eip2930Signer } +// NewCancunSigner returns a signer that accepts +// - EIP-4844 blob transactions +// - EIP-1559 dynamic fee transactions +// - EIP-2930 access list transactions, +// - EIP-155 replay protected transactions, and +// - legacy Homestead transactions. +func NewCancunSigner(chainId *big.Int) Signer { + return newModernSigner(chainId, forks.Cancun) +} // NewLondonSigner returns a signer that accepts // - EIP-1559 dynamic fee transactions @@ -299,124 +303,18 @@ type londonSigner struct{ eip2930Signer } // - EIP-155 replay protected transactions, and // - legacy Homestead transactions. func NewLondonSigner(chainId *big.Int) Signer { - return londonSigner{eip2930Signer{NewEIP155Signer(chainId)}} + return newModernSigner(chainId, forks.London) } -func (s londonSigner) Sender(tx *Transaction) (common.Address, error) { - if tx.Type() != DynamicFeeTxType { - return s.eip2930Signer.Sender(tx) - } - V, R, S := tx.RawSignatureValues() - // DynamicFee txs are defined to use 0 and 1 as their recovery - // id, add 27 to become equivalent to unprotected Homestead signatures. - V = new(big.Int).Add(V, big.NewInt(27)) - if tx.ChainId().Cmp(s.chainId) != 0 { - return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId) - } - return recoverPlain(s.Hash(tx), R, S, V, true) -} - -func (s londonSigner) Equal(s2 Signer) bool { - x, ok := s2.(londonSigner) - return ok && x.chainId.Cmp(s.chainId) == 0 -} - -func (s londonSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { - txdata, ok := tx.inner.(*DynamicFeeTx) - if !ok { - return s.eip2930Signer.SignatureValues(tx, sig) - } - // Check that chain ID of tx matches the signer. We also accept ID zero here, - // because it indicates that the chain ID was not specified in the tx. - if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 { - return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId) - } - R, S, _ = decodeSignature(sig) - V = big.NewInt(int64(sig[64])) - return R, S, V, nil -} - -// Hash returns the hash to be signed by the sender. -// It does not uniquely identify the transaction. -func (s londonSigner) Hash(tx *Transaction) common.Hash { - if tx.Type() != DynamicFeeTxType { - return s.eip2930Signer.Hash(tx) - } - return tx.inner.sigHash(s.chainId) -} - -type eip2930Signer struct{ EIP155Signer } - // NewEIP2930Signer returns a signer that accepts EIP-2930 access list transactions, // EIP-155 replay protected transactions, and legacy Homestead transactions. func NewEIP2930Signer(chainId *big.Int) Signer { - return eip2930Signer{NewEIP155Signer(chainId)} -} - -func (s eip2930Signer) ChainID() *big.Int { - return s.chainId -} - -func (s eip2930Signer) Equal(s2 Signer) bool { - x, ok := s2.(eip2930Signer) - return ok && x.chainId.Cmp(s.chainId) == 0 -} - -func (s eip2930Signer) Sender(tx *Transaction) (common.Address, error) { - V, R, S := tx.RawSignatureValues() - switch tx.Type() { - case LegacyTxType: - return s.EIP155Signer.Sender(tx) - case AccessListTxType: - // AL txs are defined to use 0 and 1 as their recovery - // id, add 27 to become equivalent to unprotected Homestead signatures. - V = new(big.Int).Add(V, big.NewInt(27)) - default: - return common.Address{}, ErrTxTypeNotSupported - } - if tx.ChainId().Cmp(s.chainId) != 0 { - return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId) - } - return recoverPlain(s.Hash(tx), R, S, V, true) -} - -func (s eip2930Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { - switch txdata := tx.inner.(type) { - case *LegacyTx: - return s.EIP155Signer.SignatureValues(tx, sig) - case *AccessListTx: - // Check that chain ID of tx matches the signer. We also accept ID zero here, - // because it indicates that the chain ID was not specified in the tx. - if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 { - return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId) - } - R, S, _ = decodeSignature(sig) - V = big.NewInt(int64(sig[64])) - default: - return nil, nil, nil, ErrTxTypeNotSupported - } - return R, S, V, nil -} - -// Hash returns the hash to be signed by the sender. -// It does not uniquely identify the transaction. -func (s eip2930Signer) Hash(tx *Transaction) common.Hash { - switch tx.Type() { - case LegacyTxType: - return s.EIP155Signer.Hash(tx) - case AccessListTxType: - return tx.inner.sigHash(s.chainId) - default: - // This _should_ not happen, but in case someone sends in a bad - // json struct via RPC, it's probably more prudent to return an - // empty hash instead of killing the node with a panic - //panic("Unsupported transaction type: %d", tx.typ) - return common.Hash{} - } + return newModernSigner(chainId, forks.Berlin) } // EIP155Signer implements Signer using the EIP-155 rules. This accepts transactions which // are replay-protected as well as unprotected homestead transactions. +// Deprecated: always use the Signer interface type type EIP155Signer struct { chainId, chainIdMul *big.Int } @@ -478,8 +376,9 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash { return tx.inner.sigHash(s.chainId) } -// HomesteadSigner implements Signer interface using the -// homestead rules. +// HomesteadSigner implements Signer using the homestead rules. The only valid reason to +// use this type is creating legacy transactions which are intentionally not +// replay-protected. type HomesteadSigner struct{ FrontierSigner } func (hs HomesteadSigner) ChainID() *big.Int { @@ -505,8 +404,8 @@ func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) { return recoverPlain(hs.Hash(tx), r, s, v, true) } -// FrontierSigner implements Signer interface using the -// frontier rules. +// FrontierSigner implements Signer using the frontier rules. +// Deprecated: always use the Signer interface type type FrontierSigner struct{} func (fs FrontierSigner) ChainID() *big.Int { diff --git a/params/forks/forks.go b/params/forks/forks.go index 2d44e13b04d..02f6e5b6125 100644 --- a/params/forks/forks.go +++ b/params/forks/forks.go @@ -24,8 +24,8 @@ const ( FrontierThawing Homestead DAO - TangerineWhistle - SpuriousDragon + TangerineWhistle // a.k.a. the EIP150 fork + SpuriousDragon // a.k.a. the EIP155 fork Byzantium Constantinople Petersburg From 8e3cd41b0490dc54022c0384c30c576b10c7f8e9 Mon Sep 17 00:00:00 2001 From: maskpp Date: Thu, 20 Mar 2025 13:14:13 +0800 Subject: [PATCH 020/658] cmd/utils: force hash scheme for archive mode (#31439) --- cmd/utils/flags.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 77dac8bd1ab..ae58c2d0539 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1659,12 +1659,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { log.Warn("The flag --txlookuplimit is deprecated and will be removed, please use --history.transactions") cfg.TransactionHistory = ctx.Uint64(TxLookupLimitFlag.Name) } - if ctx.String(GCModeFlag.Name) == "archive" && cfg.TransactionHistory != 0 { - cfg.TransactionHistory = 0 - log.Warn("Disabled transaction unindexing for archive node") + if ctx.String(GCModeFlag.Name) == "archive" { + if cfg.TransactionHistory != 0 { + cfg.TransactionHistory = 0 + log.Warn("Disabled transaction unindexing for archive node") + } - cfg.StateScheme = rawdb.HashScheme - log.Warn("Forcing hash state-scheme for archive mode") + if cfg.StateScheme != rawdb.HashScheme { + cfg.StateScheme = rawdb.HashScheme + log.Warn("Forcing hash state-scheme for archive mode") + } } if ctx.IsSet(LogHistoryFlag.Name) { cfg.LogHistory = ctx.Uint64(LogHistoryFlag.Name) From 03cc2942c2c25208dac1b7c8fc1bc19f3d72fd97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Thu, 20 Mar 2025 09:23:10 +0100 Subject: [PATCH 021/658] cmd/workload: fixed filter test request error handling (#31424) This PR fixes the broken request error handling of the workload filter tests. Until now `validateHistoryPruneErr` was invoked with `fq.Err` as an input which was always nil and a timeout or http error was reported as a result content mismatch. Also, in case of `errPrunedHistory` it is wrong to return here without setting an error because then it will look like a valid empty result and the check will later fail. So instead `errPrunedHistory` is always returned now (without printing an error message) and the callers of `run` should handle this special case (typically ignore silently). --- cmd/workload/filtertest.go | 21 ++++++++++-------- cmd/workload/filtertestgen.go | 24 ++++----------------- cmd/workload/filtertestperf.go | 39 ++++++++++++++++++++++++++++------ 3 files changed, 49 insertions(+), 35 deletions(-) diff --git a/cmd/workload/filtertest.go b/cmd/workload/filtertest.go index bc76857911a..52dd6e41ad3 100644 --- a/cmd/workload/filtertest.go +++ b/cmd/workload/filtertest.go @@ -109,6 +109,9 @@ func (s *filterTestSuite) filterFullRange(t *utesting.T) { func (s *filterTestSuite) queryAndCheck(t *utesting.T, query *filterQuery) { query.run(s.cfg.client, s.cfg.historyPruneBlock) + if query.Err == errPrunedHistory { + return + } if query.Err != nil { t.Errorf("Filter query failed (fromBlock: %d toBlock: %d addresses: %v topics: %v error: %v)", query.FromBlock, query.ToBlock, query.Address, query.Topics, query.Err) return @@ -126,6 +129,9 @@ func (s *filterTestSuite) fullRangeQueryAndCheck(t *utesting.T, query *filterQue Topics: query.Topics, } frQuery.run(s.cfg.client, s.cfg.historyPruneBlock) + if frQuery.Err == errPrunedHistory { + return + } if frQuery.Err != nil { t.Errorf("Full range filter query failed (addresses: %v topics: %v error: %v)", frQuery.Address, frQuery.Topics, frQuery.Err) return @@ -206,14 +212,11 @@ func (fq *filterQuery) run(client *client, historyPruneBlock *uint64) { Addresses: fq.Address, Topics: fq.Topics, }) - if err != nil { - if err = validateHistoryPruneErr(fq.Err, uint64(fq.FromBlock), historyPruneBlock); err == errPrunedHistory { - return - } else if err != nil { - fmt.Printf("Filter query failed: fromBlock: %d toBlock: %d addresses: %v topics: %v error: %v\n", - fq.FromBlock, fq.ToBlock, fq.Address, fq.Topics, err) - } - fq.Err = err - } fq.results = logs + fq.Err = validateHistoryPruneErr(err, uint64(fq.FromBlock), historyPruneBlock) +} + +func (fq *filterQuery) printError() { + fmt.Printf("Filter query failed: fromBlock: %d toBlock: %d addresses: %v topics: %v error: %v\n", + fq.FromBlock, fq.ToBlock, fq.Address, fq.Topics, fq.Err) } diff --git a/cmd/workload/filtertestgen.go b/cmd/workload/filtertestgen.go index e50ec57b479..6d1f6398196 100644 --- a/cmd/workload/filtertestgen.go +++ b/cmd/workload/filtertestgen.go @@ -40,7 +40,6 @@ var ( Action: filterGenCmd, Flags: []cli.Flag{ filterQueryFileFlag, - filterErrorFileFlag, }, } filterQueryFileFlag = &cli.StringFlag{ @@ -72,8 +71,8 @@ func filterGenCmd(ctx *cli.Context) error { query := f.newQuery() query.run(f.client, nil) if query.Err != nil { - f.errors = append(f.errors, query) - continue + query.printError() + exit("filter query failed") } if len(query.results) > 0 && len(query.results) <= maxFilterResultSize { for { @@ -90,8 +89,8 @@ func filterGenCmd(ctx *cli.Context) error { ) } if extQuery.Err != nil { - f.errors = append(f.errors, extQuery) - break + extQuery.printError() + exit("filter query failed") } if len(extQuery.results) > maxFilterResultSize { break @@ -101,7 +100,6 @@ func filterGenCmd(ctx *cli.Context) error { f.storeQuery(query) if time.Since(lastWrite) > time.Second*10 { f.writeQueries() - f.writeErrors() lastWrite = time.Now() } } @@ -112,18 +110,15 @@ func filterGenCmd(ctx *cli.Context) error { type filterTestGen struct { client *client queryFile string - errorFile string finalizedBlock int64 queries [filterBuckets][]*filterQuery - errors []*filterQuery } func newFilterTestGen(ctx *cli.Context) *filterTestGen { return &filterTestGen{ client: makeClient(ctx), queryFile: ctx.String(filterQueryFileFlag.Name), - errorFile: ctx.String(filterErrorFileFlag.Name), } } @@ -360,17 +355,6 @@ func (s *filterTestGen) writeQueries() { file.Close() } -// writeQueries serializes the generated errors to the error file. -func (s *filterTestGen) writeErrors() { - file, err := os.Create(s.errorFile) - if err != nil { - exit(fmt.Errorf("Error creating filter error file %s: %v", s.errorFile, err)) - return - } - defer file.Close() - json.NewEncoder(file).Encode(s.errors) -} - func mustGetFinalizedBlock(client *client) int64 { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() diff --git a/cmd/workload/filtertestperf.go b/cmd/workload/filtertestperf.go index 7f4cad4ab98..c7d2fdd02a9 100644 --- a/cmd/workload/filtertestperf.go +++ b/cmd/workload/filtertestperf.go @@ -17,8 +17,10 @@ package main import ( + "encoding/json" "fmt" "math/rand" + "os" "slices" "sort" "time" @@ -41,7 +43,7 @@ var ( } ) -const passCount = 1 +const passCount = 3 func filterPerfCmd(ctx *cli.Context) error { cfg := testConfigFromCLI(ctx) @@ -61,7 +63,10 @@ func filterPerfCmd(ctx *cli.Context) error { } // Run test queries. - var failed, mismatch int + var ( + failed, pruned, mismatch int + errors []*filterQuery + ) for i := 1; i <= passCount; i++ { fmt.Println("Performance test pass", i, "/", passCount) for len(queries) > 0 { @@ -71,27 +76,35 @@ func filterPerfCmd(ctx *cli.Context) error { queries = queries[:len(queries)-1] start := time.Now() qt.query.run(cfg.client, cfg.historyPruneBlock) + if qt.query.Err == errPrunedHistory { + pruned++ + continue + } qt.runtime = append(qt.runtime, time.Since(start)) slices.Sort(qt.runtime) qt.medianTime = qt.runtime[len(qt.runtime)/2] if qt.query.Err != nil { + qt.query.printError() + errors = append(errors, qt.query) failed++ continue } if rhash := qt.query.calculateHash(); *qt.query.ResultHash != rhash { fmt.Printf("Filter query result mismatch: fromBlock: %d toBlock: %d addresses: %v topics: %v expected hash: %064x calculated hash: %064x\n", qt.query.FromBlock, qt.query.ToBlock, qt.query.Address, qt.query.Topics, *qt.query.ResultHash, rhash) + errors = append(errors, qt.query) + mismatch++ continue } processed = append(processed, qt) if len(processed)%50 == 0 { - fmt.Println(" processed:", len(processed), "remaining", len(queries), "failed:", failed, "result mismatch:", mismatch) + fmt.Println(" processed:", len(processed), "remaining", len(queries), "failed:", failed, "pruned:", pruned, "result mismatch:", mismatch) } } queries, processed = processed, nil } // Show results and stats. - fmt.Println("Performance test finished; processed:", len(queries), "failed:", failed, "result mismatch:", mismatch) + fmt.Println("Performance test finished; processed:", len(queries), "failed:", failed, "pruned:", pruned, "result mismatch:", mismatch) stats := make([]bucketStats, len(f.queries)) var wildcardStats bucketStats for _, qt := range queries { @@ -114,11 +127,14 @@ func filterPerfCmd(ctx *cli.Context) error { sort.Slice(queries, func(i, j int) bool { return queries[i].medianTime > queries[j].medianTime }) - for i := 0; i < 10; i++ { - q := queries[i] + for i, q := range queries { + if i >= 10 { + break + } fmt.Printf("Most expensive query #%-2d median runtime: %13v max runtime: %13v result count: %4d fromBlock: %9d toBlock: %9d addresses: %v topics: %v\n", i+1, q.medianTime, q.runtime[len(q.runtime)-1], len(q.query.results), q.query.FromBlock, q.query.ToBlock, q.query.Address, q.query.Topics) } + writeErrors(ctx.String(filterErrorFileFlag.Name), errors) return nil } @@ -135,3 +151,14 @@ func (st *bucketStats) print(name string) { fmt.Printf("%-20s queries: %4d average block length: %12.2f average log count: %7.2f average runtime: %13v\n", name, st.count, float64(st.blocks)/float64(st.count), float64(st.logs)/float64(st.count), st.runtime/time.Duration(st.count)) } + +// writeQueries serializes the generated errors to the error file. +func writeErrors(errorFile string, errors []*filterQuery) { + file, err := os.Create(errorFile) + if err != nil { + exit(fmt.Errorf("Error creating filter error file %s: %v", errorFile, err)) + return + } + defer file.Close() + json.NewEncoder(file).Encode(errors) +} From 43883c64566602305c079ed6a7c83183f0443dfb Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Thu, 20 Mar 2025 10:20:51 +0100 Subject: [PATCH 022/658] eth/tracers: hex-encode returnValue (#31216) This is a **breaking change** to the opcode tracer. The top-level `returnValue` field of a trace will be now hex-encoded. If the return data is empty, this field will contain "0x". Fixes #31196 --- eth/tracers/api_test.go | 42 ++++++++++++++++++------------------ eth/tracers/logger/logger.go | 7 +++--- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 796719bd632..c28abba85f6 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -354,7 +354,7 @@ func TestTraceCall(t *testing.T) { }, config: nil, expectErr: nil, - expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, + expect: `{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}`, }, // Standard JSON trace upon the head, plain transfer. { @@ -366,7 +366,7 @@ func TestTraceCall(t *testing.T) { }, config: nil, expectErr: nil, - expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, + expect: `{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}`, }, // Upon the last state, default to the post block's state { @@ -377,7 +377,7 @@ func TestTraceCall(t *testing.T) { Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), }, config: nil, - expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, + expect: `{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}`, }, // Before the first transaction, should be failed { @@ -411,7 +411,7 @@ func TestTraceCall(t *testing.T) { }, config: &TraceCallConfig{TxIndex: uintPtr(2)}, expectErr: nil, - expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, + expect: `{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}`, }, // Standard JSON trace upon the non-existent block, error expects { @@ -435,7 +435,7 @@ func TestTraceCall(t *testing.T) { }, config: nil, expectErr: nil, - expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, + expect: `{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}`, }, // Tracing on 'pending' should fail: { @@ -458,7 +458,7 @@ func TestTraceCall(t *testing.T) { BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, }, expectErr: nil, - expect: ` {"gas":53018,"failed":false,"returnValue":"","structLogs":[ + expect: ` {"gas":53018,"failed":false,"returnValue":"0x","structLogs":[ {"pc":0,"op":"NUMBER","gas":24946984,"gasCost":2,"depth":1,"stack":[]}, {"pc":1,"op":"STOP","gas":24946982,"gasCost":0,"depth":1,"stack":["0x1337"]}]}`, }, @@ -535,7 +535,7 @@ func TestTraceTransaction(t *testing.T) { if !reflect.DeepEqual(have, &logger.ExecutionResult{ Gas: params.TxGas, Failed: false, - ReturnValue: "", + ReturnValue: []byte{}, StructLogs: []json.RawMessage{}, }) { t.Error("Transaction tracing result is different") @@ -596,7 +596,7 @@ func TestTraceBlock(t *testing.T) { // Trace head block { blockNumber: rpc.BlockNumber(genBlocks), - want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, txHash), + want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}}]`, txHash), }, // Trace non-existent block { @@ -606,12 +606,12 @@ func TestTraceBlock(t *testing.T) { // Trace latest block { blockNumber: rpc.LatestBlockNumber, - want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, txHash), + want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}}]`, txHash), }, // Trace pending block { blockNumber: rpc.PendingBlockNumber, - want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, txHash), + want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}}]`, txHash), }, } for i, tc := range testSuite { @@ -704,7 +704,7 @@ func TestTracingWithOverrides(t *testing.T) { randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, }, }, - want: `{"gas":21000,"failed":false,"returnValue":""}`, + want: `{"gas":21000,"failed":false,"returnValue":"0x"}`, }, // Invalid call without state overriding { @@ -749,7 +749,7 @@ func TestTracingWithOverrides(t *testing.T) { }, }, }, - want: `{"gas":23347,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000007b"}`, + want: `{"gas":23347,"failed":false,"returnValue":"0x000000000000000000000000000000000000000000000000000000000000007b"}`, }, { // Override blocknumber blockNumber: rpc.LatestBlockNumber, @@ -761,7 +761,7 @@ func TestTracingWithOverrides(t *testing.T) { config: &TraceCallConfig{ BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, }, - want: `{"gas":59537,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000001337"}`, + want: `{"gas":59537,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000001337"}`, }, { // Override blocknumber, and query a blockhash blockNumber: rpc.LatestBlockNumber, @@ -781,7 +781,7 @@ func TestTracingWithOverrides(t *testing.T) { config: &TraceCallConfig{ BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, }, - want: `{"gas":72666,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`, + want: `{"gas":72666,"failed":false,"returnValue":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`, }, /* pragma solidity =0.8.12; @@ -815,7 +815,7 @@ func TestTracingWithOverrides(t *testing.T) { }, }, }, - want: `{"gas":44100,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000001"}`, + want: `{"gas":44100,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000000001"}`, }, { // Same again, this time with storage override blockNumber: rpc.LatestBlockNumber, @@ -833,7 +833,7 @@ func TestTracingWithOverrides(t *testing.T) { }, }, //want: `{"gas":46900,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000539"}`, - want: `{"gas":44100,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000001"}`, + want: `{"gas":44100,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000000001"}`, }, { // No state override blockNumber: rpc.LatestBlockNumber, @@ -863,7 +863,7 @@ func TestTracingWithOverrides(t *testing.T) { }, }, }, - want: `{"gas":25288,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000077"}`, + want: `{"gas":25288,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000000077"}`, }, { // Full state override // The original storage is @@ -901,7 +901,7 @@ func TestTracingWithOverrides(t *testing.T) { }, }, }, - want: `{"gas":25288,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000011"}`, + want: `{"gas":25288,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000000011"}`, }, { // Partial state override // The original storage is @@ -939,7 +939,7 @@ func TestTracingWithOverrides(t *testing.T) { }, }, }, - want: `{"gas":25288,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000055"}`, + want: `{"gas":25288,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000000055"}`, }, { // Call to precompile ECREC (0x01), but code was modified to add 1 to input blockNumber: rpc.LatestBlockNumber, @@ -1084,7 +1084,7 @@ func TestTraceChain(t *testing.T) { backend.relHook = func() { rel.Add(1) } api := NewAPI(backend) - single := `{"txHash":"0x0000000000000000000000000000000000000000000000000000000000000000","result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}` + single := `{"txHash":"0x0000000000000000000000000000000000000000000000000000000000000000","result":{"gas":21000,"failed":false,"returnValue":"0x","structLogs":[]}}` var cases = []struct { start uint64 end uint64 @@ -1198,7 +1198,7 @@ func TestTraceBlockWithBasefee(t *testing.T) { // Trace head block { blockNumber: rpc.BlockNumber(genBlocks), - want: fmt.Sprintf(`[{"txHash":"%#x","result":{"gas":21002,"failed":false,"returnValue":"","structLogs":[{"pc":0,"op":"BASEFEE","gas":84000,"gasCost":2,"depth":1,"stack":[]},{"pc":1,"op":"STOP","gas":83998,"gasCost":0,"depth":1,"stack":["%#x"]}]}}]`, txHash, baseFee), + want: fmt.Sprintf(`[{"txHash":"%#x","result":{"gas":21002,"failed":false,"returnValue":"0x","structLogs":[{"pc":0,"op":"BASEFEE","gas":84000,"gasCost":2,"depth":1,"stack":[]},{"pc":1,"op":"STOP","gas":83998,"gasCost":0,"depth":1,"stack":["%#x"]}]}}]`, txHash, baseFee), }, } for i, tc := range testSuite { diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 02ff8146fbf..a28cecf1389 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -350,14 +350,13 @@ func (l *StructLogger) GetResult() (json.RawMessage, error) { failed := l.err != nil returnData := common.CopyBytes(l.output) // Return data when successful and revert reason when reverted, otherwise empty. - returnVal := fmt.Sprintf("%x", returnData) if failed && !errors.Is(l.err, vm.ErrExecutionReverted) { - returnVal = "" + returnData = []byte{} } return json.Marshal(&ExecutionResult{ Gas: l.usedGas, Failed: failed, - ReturnValue: returnVal, + ReturnValue: returnData, StructLogs: l.logs, }) } @@ -527,6 +526,6 @@ func (t *mdLogger) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.O type ExecutionResult struct { Gas uint64 `json:"gas"` Failed bool `json:"failed"` - ReturnValue string `json:"returnValue"` + ReturnValue hexutil.Bytes `json:"returnValue"` StructLogs []json.RawMessage `json:"structLogs"` } From 0a8f41e2cb9b28210e73b9509bf645ec56eb1f5f Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 20 Mar 2025 20:33:13 +0800 Subject: [PATCH 023/658] eth/tracers: fix test (#31445) This pull request fixes a broken unit test ``` === CONT TestTracingWithOverrides api_test.go:1012: result: {"gas":21167,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000000002","structLogs":[{"pc":0,"op":"PUSH1","gas":24978860,"gasCost":3,"depth":1,"stack":[]},{"pc":2,"op":"CALLDATALOAD","gas":24978857,"gasCost":3,"depth":1,"stack":["0x0"]},{"pc":3,"op":"PUSH1","gas":24978854,"gasCost":3,"depth":1,"stack":["0x1"]},{"pc":5,"op":"ADD","gas":24978851,"gasCost":3,"depth":1,"stack":["0x1","0x1"]},{"pc":6,"op":"PUSH1","gas":24978848,"gasCost":3,"depth":1,"stack":["0x2"]},{"pc":8,"op":"MSTORE","gas":24978845,"gasCost":6,"depth":1,"stack":["0x2","0x0"]},{"pc":9,"op":"PUSH1","gas":24978839,"gasCost":3,"depth":1,"stack":[]},{"pc":11,"op":"PUSH1","gas":24978836,"gasCost":3,"depth":1,"stack":["0x20"]},{"pc":13,"op":"RETURN","gas":24978833,"gasCost":0,"depth":1,"stack":["0x20","0x0"]}]} api_test.go:1013: test 10, result mismatch, have {21167 false 0x0000000000000000000000000000000000000000000000000000000000000002} , want {21167 false 0000000000000000000000000000000000000000000000000000000000000002} api_test.go:1012: result: {"gas":25664,"failed":false,"returnValue":"0x000000000000000000000000c6e93f4c1920eaeaa1e699f76a7a8c18e3056074","structLogs":[]} api_test.go:1013: test 11, result mismatch, have {25664 false 0x000000000000000000000000c6e93f4c1920eaeaa1e699f76a7a8c18e3056074} , want {25664 false 000000000000000000000000c6e93f4c1920eaeaa1e699f76a7a8c18e3056074} ``` --- eth/tracers/api_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index c28abba85f6..529448e3972 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -960,7 +960,7 @@ func TestTracingWithOverrides(t *testing.T) { }, }, }, - want: `{"gas":21167,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000002"}`, + want: `{"gas":21167,"failed":false,"returnValue":"0x0000000000000000000000000000000000000000000000000000000000000002"}`, }, { // Call to ECREC Precompiled on a different address, expect the original behaviour of ECREC precompile blockNumber: rpc.LatestBlockNumber, @@ -981,7 +981,7 @@ func TestTracingWithOverrides(t *testing.T) { }, }, }, - want: `{"gas":25664,"failed":false,"returnValue":"000000000000000000000000c6e93f4c1920eaeaa1e699f76a7a8c18e3056074"}`, + want: `{"gas":25664,"failed":false,"returnValue":"0x000000000000000000000000c6e93f4c1920eaeaa1e699f76a7a8c18e3056074"}`, }, } for i, tc := range testSuite { From 9fc2bbe1ceaaa3889d0a8eee8c9dcfb6ddefb95c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Thu, 20 Mar 2025 14:13:58 +0100 Subject: [PATCH 024/658] core/filtermaps: allow log search while head indexing (#31429) This PR changes the matcher syncing conditions so that it is possible to run a search while head indexing is in progress. Previously it was a requirement to have the head indexed in order to perform matcher sync before and after a search. This was unnecessarily strict as the purpose was just to avoid syncing the valid range with the temporary shortened indexed range applied while updating existing head maps. Now the sync condition explicitly checks whether the indexer has a temporary indexed range with some head maps being partially updated. It also fixes a deadlock that happened when matcher synchronization was attempted in the event handler called from the `writeFinishedMaps` periodical callback. --- core/filtermaps/filtermaps.go | 10 ++++++---- core/filtermaps/indexer.go | 14 +++++++++----- core/filtermaps/map_renderer.go | 8 ++++++-- core/filtermaps/matcher_backend.go | 2 +- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index fe75c1ea7d4..dfc20521f62 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -70,6 +70,7 @@ type FilterMaps struct { indexLock sync.RWMutex indexedRange filterMapsRange indexedView *ChainView // always consistent with the log index + hasTempRange bool // also accessed by indexer and matcher backend but no locking needed. filterMapCache *lru.Cache[uint32, filterMap] @@ -94,7 +95,7 @@ type FilterMaps struct { ptrTailUnindexMap uint32 targetView *ChainView - matcherSyncRequest *FilterMapsMatcherBackend + matcherSyncRequests []*FilterMapsMatcherBackend historyCutoff uint64 finalBlock, lastFinal uint64 lastFinalEpoch uint32 @@ -330,7 +331,7 @@ func (f *FilterMaps) init() error { fmr.blocks = common.NewRange(cp.BlockNumber+1, 0) fmr.maps = common.NewRange(uint32(bestLen)< Date: Thu, 20 Mar 2025 17:11:40 +0100 Subject: [PATCH 025/658] p2p/discover: repeat WHOAREYOU challenge when handshake in progress (#31356) This fixes the handshake in a scenario where the remote end sends two unknown packets in a row. When this happens, we would previously respond to both with a WHOAREYOU challenge, but keep only the latest sent challenge. Transmission is assumed to be unreliable, so any client that sends two request packets simultaneously has to be prepared to follow up on whichever request leads to a handshake. With this fix, we force them to do the handshake that we can actually complete. Fixes #30581 --- p2p/discover/v5_udp.go | 26 +++++++++++- p2p/discover/v5_udp_test.go | 70 +++++++++++++++++++++++++++++++++ p2p/discover/v5wire/encoding.go | 6 +++ 3 files changed, 100 insertions(+), 2 deletions(-) diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go index 9e849751c17..6f7c7971528 100644 --- a/p2p/discover/v5_udp.go +++ b/p2p/discover/v5_udp.go @@ -50,11 +50,20 @@ const ( // encoding/decoding and with the handshake; the UDPv5 object handles higher-level concerns. type codecV5 interface { // Encode encodes a packet. - Encode(enode.ID, string, v5wire.Packet, *v5wire.Whoareyou) ([]byte, v5wire.Nonce, error) + // + // If the underlying type of 'p' is *v5wire.Whoareyou, a Whoareyou challenge packet is + // encoded. If the 'challenge' parameter is non-nil, the packet is encoded as a + // handshake message packet. Otherwise, the packet will be encoded as an ordinary + // message packet. + Encode(id enode.ID, addr string, p v5wire.Packet, challenge *v5wire.Whoareyou) ([]byte, v5wire.Nonce, error) // Decode decodes a packet. It returns a *v5wire.Unknown packet if decryption fails. // The *enode.Node return value is non-nil when the input contains a handshake response. - Decode([]byte, string) (enode.ID, *enode.Node, v5wire.Packet, error) + Decode(b []byte, addr string) (enode.ID, *enode.Node, v5wire.Packet, error) + + // CurrentChallenge returns the most recent WHOAREYOU challenge that was encoded to given node. + // This will return a non-nil value if there is an active handshake attempt with the node, and nil otherwise. + CurrentChallenge(id enode.ID, addr string) *v5wire.Whoareyou } // UDPv5 is the implementation of protocol version 5. @@ -824,6 +833,19 @@ func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr netip.AddrPort // handleUnknown initiates a handshake by responding with WHOAREYOU. func (t *UDPv5) handleUnknown(p *v5wire.Unknown, fromID enode.ID, fromAddr netip.AddrPort) { + currentChallenge := t.codec.CurrentChallenge(fromID, fromAddr.String()) + if currentChallenge != nil { + // This case happens when the sender issues multiple concurrent requests. + // Since we only support one in-progress handshake at a time, we need to tell + // them which handshake attempt they need to complete. We tell them to use the + // existing handshake attempt since the response to that one might still be in + // transit. + t.log.Debug("Repeating discv5 handshake challenge", "id", fromID, "addr", fromAddr) + t.sendResponse(fromID, fromAddr, currentChallenge) + return + } + + // Send a fresh challenge. challenge := &v5wire.Whoareyou{Nonce: p.Nonce} crand.Read(challenge.IDNonce[:]) if n := t.GetNode(fromID); n != nil { diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go index 371f4147601..3026dff5380 100644 --- a/p2p/discover/v5_udp_test.go +++ b/p2p/discover/v5_udp_test.go @@ -140,6 +140,26 @@ func TestUDPv5_unknownPacket(t *testing.T) { test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) { check(p, 0) }) +} + +func TestUDPv5_unknownPacketKnownNode(t *testing.T) { + t.Parallel() + test := newUDPV5Test(t) + defer test.close() + + nonce := v5wire.Nonce{1, 2, 3} + check := func(p *v5wire.Whoareyou, wantSeq uint64) { + t.Helper() + if p.Nonce != nonce { + t.Error("wrong nonce in WHOAREYOU:", p.Nonce, nonce) + } + if p.IDNonce == ([16]byte{}) { + t.Error("all zero ID nonce") + } + if p.RecordSeq != wantSeq { + t.Errorf("wrong record seq %d in WHOAREYOU, want %d", p.RecordSeq, wantSeq) + } + } // Make node known. n := test.getNode(test.remotekey, test.remoteaddr).Node() @@ -151,6 +171,42 @@ func TestUDPv5_unknownPacket(t *testing.T) { }) } +// This test checks that, when multiple 'unknown' packets are received during a handshake, +// the node sticks to the first handshake attempt. +func TestUDPv5_handshakeRepeatChallenge(t *testing.T) { + t.Parallel() + test := newUDPV5Test(t) + defer test.close() + + nonce1 := v5wire.Nonce{1} + nonce2 := v5wire.Nonce{2} + nonce3 := v5wire.Nonce{3} + check := func(p *v5wire.Whoareyou, wantNonce v5wire.Nonce) { + t.Helper() + if p.Nonce != wantNonce { + t.Error("wrong nonce in WHOAREYOU:", p.Nonce, wantNonce) + } + } + + // Unknown packet from unknown node. + test.packetIn(&v5wire.Unknown{Nonce: nonce1}) + test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) { + check(p, nonce1) + }) + + // Second unknown packet. Here we expect the response to reference the + // first unknown packet. + test.packetIn(&v5wire.Unknown{Nonce: nonce2}) + test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) { + check(p, nonce1) + }) + // Third unknown packet. This should still return the first nonce. + test.packetIn(&v5wire.Unknown{Nonce: nonce3}) + test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) { + check(p, nonce1) + }) +} + // This test checks that incoming FINDNODE calls are handled correctly. func TestUDPv5_findnodeHandling(t *testing.T) { t.Parallel() @@ -698,6 +754,8 @@ type testCodec struct { test *udpV5Test id enode.ID ctr uint64 + + sentChallenges map[enode.ID]*v5wire.Whoareyou } type testCodecFrame struct { @@ -712,11 +770,23 @@ func (c *testCodec) Encode(toID enode.ID, addr string, p v5wire.Packet, _ *v5wir var authTag v5wire.Nonce binary.BigEndian.PutUint64(authTag[:], c.ctr) + if w, ok := p.(*v5wire.Whoareyou); ok { + // Store recently sent Whoareyou challenges. + if c.sentChallenges == nil { + c.sentChallenges = make(map[enode.ID]*v5wire.Whoareyou) + } + c.sentChallenges[toID] = w + } + penc, _ := rlp.EncodeToBytes(p) frame, err := rlp.EncodeToBytes(testCodecFrame{c.id, authTag, p.Kind(), penc}) return frame, authTag, err } +func (c *testCodec) CurrentChallenge(id enode.ID, addr string) *v5wire.Whoareyou { + return c.sentChallenges[id] +} + func (c *testCodec) Decode(input []byte, addr string) (enode.ID, *enode.Node, v5wire.Packet, error) { frame, p, err := c.decodeFrame(input) if err != nil { diff --git a/p2p/discover/v5wire/encoding.go b/p2p/discover/v5wire/encoding.go index 904a3ddec6f..e50b7cd16d1 100644 --- a/p2p/discover/v5wire/encoding.go +++ b/p2p/discover/v5wire/encoding.go @@ -245,6 +245,12 @@ func (c *Codec) EncodeRaw(id enode.ID, head Header, msgdata []byte) ([]byte, err return c.buf.Bytes(), nil } +// CurrentChallenge returns the latest challenge sent to the given node. +// This will return non-nil while a handshake is in progress. +func (c *Codec) CurrentChallenge(id enode.ID, addr string) *Whoareyou { + return c.sc.getHandshake(id, addr) +} + func (c *Codec) writeHeaders(head *Header) { c.buf.Reset() c.buf.Write(head.IV[:]) From 7fed9584b5426be5db6d7b0198acdec6515d9c81 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 21 Mar 2025 05:05:15 +0800 Subject: [PATCH 026/658] core/txpool/legacypool: reject gapped tx from delegated account (#31430) This pull request improves the protection mechanism in the txpool for senders with delegation. A sender with either delegation or pending delegation is now limited to a maximum of one in-flight executable transaction, while gapped transactions will be rejected. Reason: If nonce-gapped transaction from delegated/pending-delegated senders can be acceptable, then it's no-longer possible to send another "executable" transaction with correct nonce due to the policy of at most one inflight tx. The gapped transaction will be stuck in the txpool, with no meaningful way to unlock the sender. --------- Co-authored-by: lightclient --- core/txpool/legacypool/legacypool.go | 54 ++++++++++++++--------- core/txpool/legacypool/legacypool_test.go | 7 ++- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 78be81480fb..7a0095a5ad1 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -67,6 +67,10 @@ var ( // transactions is reached for specific accounts. ErrInflightTxLimitReached = errors.New("in-flight transaction limit reached for delegated accounts") + // ErrOutOfOrderTxFromDelegated is returned when the transaction with gapped + // nonce received from the accounts with delegation or pending delegation. + ErrOutOfOrderTxFromDelegated = errors.New("gapped-nonce tx from delegated accounts") + // ErrAuthorityReserved is returned if a transaction has an authorization // signed by an address which already has in-flight transactions known to the // pool. @@ -606,33 +610,39 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction) error { return pool.validateAuth(tx) } +// checkDelegationLimit determines if the tx sender is delegated or has a +// pending delegation, and if so, ensures they have at most one in-flight +// **executable** transaction, e.g. disallow stacked and gapped transactions +// from the account. +func (pool *LegacyPool) checkDelegationLimit(tx *types.Transaction) error { + from, _ := types.Sender(pool.signer, tx) // validated + + // Short circuit if the sender has neither delegation nor pending delegation. + if pool.currentState.GetCodeHash(from) == types.EmptyCodeHash && len(pool.all.auths[from]) == 0 { + return nil + } + pending := pool.pending[from] + if pending == nil { + // Transaction with gapped nonce is not supported for delegated accounts + if pool.pendingNonces.get(from) != tx.Nonce() { + return ErrOutOfOrderTxFromDelegated + } + return nil + } + // Transaction replacement is supported + if pending.Contains(tx.Nonce()) { + return nil + } + return ErrInflightTxLimitReached +} + // validateAuth verifies that the transaction complies with code authorization // restrictions brought by SetCode transaction type. func (pool *LegacyPool) validateAuth(tx *types.Transaction) error { - from, _ := types.Sender(pool.signer, tx) // validated - // Allow at most one in-flight tx for delegated accounts or those with a // pending authorization. - if pool.currentState.GetCodeHash(from) != types.EmptyCodeHash || len(pool.all.auths[from]) != 0 { - var ( - count int - exists bool - ) - pending := pool.pending[from] - if pending != nil { - count += pending.Len() - exists = pending.Contains(tx.Nonce()) - } - queue := pool.queue[from] - if queue != nil { - count += queue.Len() - exists = exists || queue.Contains(tx.Nonce()) - } - // Replace the existing in-flight transaction for delegated accounts - // are still supported - if count >= 1 && !exists { - return ErrInflightTxLimitReached - } + if err := pool.checkDelegationLimit(tx); err != nil { + return err } // Authorities cannot conflict with any pending or queued transactions. if auths := tx.SetCodeAuthorities(); len(auths) > 0 { diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index ef887041add..3f269bd69ec 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -2262,6 +2262,11 @@ func TestSetCodeTransactions(t *testing.T) { aa := common.Address{0xaa, 0xaa} statedb.SetCode(addrA, append(types.DelegationPrefix, aa.Bytes()...)) statedb.SetCode(aa, []byte{byte(vm.ADDRESS), byte(vm.PUSH0), byte(vm.SSTORE)}) + + // Send gapped transaction, it should be rejected. + if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrOutOfOrderTxFromDelegated) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrOutOfOrderTxFromDelegated, err) + } // Send transactions. First is accepted, second is rejected. if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyA)); err != nil { t.Fatalf("%s: failed to add remote transaction: %v", name, err) @@ -2269,7 +2274,7 @@ func TestSetCodeTransactions(t *testing.T) { if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) { t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err) } - // Also check gapped transaction. + // Check gapped transaction again. if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) { t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err) } From 07cca7ab9f9c26e435acbf81432f61102d3ed1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Fri, 21 Mar 2025 10:47:58 +0100 Subject: [PATCH 027/658] core/bloombits: remove old bloombits logic and chain indexer (#31081) This PR is #3 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 and https://github.com/ethereum/go-ethereum/pull/31080 Replaces https://github.com/ethereum/go-ethereum/pull/30370 This part removes the old bloombits package and the chain indexer that was only used by bloombits. Deletes the old bloombits database. 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 --- core/blockchain.go | 2 +- core/bloom_indexer.go | 92 ---- core/bloombits/doc.go | 18 - core/bloombits/generator.go | 98 ---- core/bloombits/generator_test.go | 100 ----- core/bloombits/matcher.go | 649 --------------------------- core/bloombits/matcher_test.go | 292 ------------ core/bloombits/scheduler.go | 181 -------- core/bloombits/scheduler_test.go | 103 ----- core/chain_indexer.go | 522 --------------------- core/chain_indexer_test.go | 246 ---------- core/filtermaps/filtermaps.go | 35 +- core/filtermaps/indexer.go | 33 +- core/rawdb/accessors_indexes.go | 56 +-- core/rawdb/accessors_indexes_test.go | 53 --- core/rawdb/database.go | 8 +- core/rawdb/schema.go | 24 +- eth/api_backend.go | 12 - eth/backend.go | 35 +- eth/bloombits.go | 74 --- params/network_params.go | 8 - 21 files changed, 80 insertions(+), 2561 deletions(-) delete mode 100644 core/bloom_indexer.go delete mode 100644 core/bloombits/doc.go delete mode 100644 core/bloombits/generator.go delete mode 100644 core/bloombits/generator_test.go delete mode 100644 core/bloombits/matcher.go delete mode 100644 core/bloombits/matcher_test.go delete mode 100644 core/bloombits/scheduler.go delete mode 100644 core/bloombits/scheduler_test.go delete mode 100644 core/chain_indexer.go delete mode 100644 core/chain_indexer_test.go delete mode 100644 eth/bloombits.go diff --git a/core/blockchain.go b/core/blockchain.go index 2024c63b80d..2bf7fba427f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -906,7 +906,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha rawdb.DeleteBody(db, hash, num) rawdb.DeleteReceipts(db, hash, num) } - // Todo(rjl493456442) txlookup, bloombits, etc + // Todo(rjl493456442) txlookup, log index, etc } // If SetHead was only called as a chain reparation method, try to skip // touching the header chain altogether, unless the freezer is broken diff --git a/core/bloom_indexer.go b/core/bloom_indexer.go deleted file mode 100644 index 68a35d811e4..00000000000 --- a/core/bloom_indexer.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2021 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 core - -import ( - "context" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/bitutil" - "github.com/ethereum/go-ethereum/core/bloombits" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" -) - -const ( - // bloomThrottling is the time to wait between processing two consecutive index - // sections. It's useful during chain upgrades to prevent disk overload. - bloomThrottling = 100 * time.Millisecond -) - -// BloomIndexer implements a core.ChainIndexer, building up a rotated bloom bits index -// for the Ethereum header bloom filters, permitting blazing fast filtering. -type BloomIndexer struct { - size uint64 // section size to generate bloombits for - db ethdb.Database // database instance to write index data and metadata into - gen *bloombits.Generator // generator to rotate the bloom bits crating the bloom index - section uint64 // Section is the section number being processed currently - head common.Hash // Head is the hash of the last header processed -} - -// NewBloomIndexer returns a chain indexer that generates bloom bits data for the -// canonical chain for fast logs filtering. -func NewBloomIndexer(db ethdb.Database, size, confirms uint64) *ChainIndexer { - backend := &BloomIndexer{ - db: db, - size: size, - } - table := rawdb.NewTable(db, string(rawdb.BloomBitsIndexPrefix)) - - return NewChainIndexer(db, table, backend, size, confirms, bloomThrottling, "bloombits") -} - -// Reset implements core.ChainIndexerBackend, starting a new bloombits index -// section. -func (b *BloomIndexer) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error { - gen, err := bloombits.NewGenerator(uint(b.size)) - b.gen, b.section, b.head = gen, section, common.Hash{} - return err -} - -// Process implements core.ChainIndexerBackend, adding a new header's bloom into -// the index. -func (b *BloomIndexer) Process(ctx context.Context, header *types.Header) error { - b.gen.AddBloom(uint(header.Number.Uint64()-b.section*b.size), header.Bloom) - b.head = header.Hash() - return nil -} - -// Commit implements core.ChainIndexerBackend, finalizing the bloom section and -// writing it out into the database. -func (b *BloomIndexer) Commit() error { - batch := b.db.NewBatchWithSize((int(b.size) / 8) * types.BloomBitLength) - for i := 0; i < types.BloomBitLength; i++ { - bits, err := b.gen.Bitset(uint(i)) - if err != nil { - return err - } - rawdb.WriteBloomBits(batch, uint(i), b.section, b.head, bitutil.CompressBytes(bits)) - } - return batch.Write() -} - -// Prune returns an empty error since we don't support pruning here. -func (b *BloomIndexer) Prune(threshold uint64) error { - return nil -} diff --git a/core/bloombits/doc.go b/core/bloombits/doc.go deleted file mode 100644 index 3d159e74f77..00000000000 --- a/core/bloombits/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2017 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 bloombits implements bloom filtering on batches of data. -package bloombits diff --git a/core/bloombits/generator.go b/core/bloombits/generator.go deleted file mode 100644 index 646151db0bf..00000000000 --- a/core/bloombits/generator.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2017 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 bloombits - -import ( - "errors" - - "github.com/ethereum/go-ethereum/core/types" -) - -var ( - // errSectionOutOfBounds is returned if the user tried to add more bloom filters - // to the batch than available space, or if tries to retrieve above the capacity. - errSectionOutOfBounds = errors.New("section out of bounds") - - // errBloomBitOutOfBounds is returned if the user tried to retrieve specified - // bit bloom above the capacity. - errBloomBitOutOfBounds = errors.New("bloom bit out of bounds") -) - -// Generator takes a number of bloom filters and generates the rotated bloom bits -// to be used for batched filtering. -type Generator struct { - blooms [types.BloomBitLength][]byte // Rotated blooms for per-bit matching - sections uint // Number of sections to batch together - nextSec uint // Next section to set when adding a bloom -} - -// NewGenerator creates a rotated bloom generator that can iteratively fill a -// batched bloom filter's bits. -func NewGenerator(sections uint) (*Generator, error) { - if sections%8 != 0 { - return nil, errors.New("section count not multiple of 8") - } - b := &Generator{sections: sections} - for i := 0; i < types.BloomBitLength; i++ { - b.blooms[i] = make([]byte, sections/8) - } - return b, nil -} - -// AddBloom takes a single bloom filter and sets the corresponding bit column -// in memory accordingly. -func (b *Generator) AddBloom(index uint, bloom types.Bloom) error { - // Make sure we're not adding more bloom filters than our capacity - if b.nextSec >= b.sections { - return errSectionOutOfBounds - } - if b.nextSec != index { - return errors.New("bloom filter with unexpected index") - } - // Rotate the bloom and insert into our collection - byteIndex := b.nextSec / 8 - bitIndex := byte(7 - b.nextSec%8) - for byt := 0; byt < types.BloomByteLength; byt++ { - bloomByte := bloom[types.BloomByteLength-1-byt] - if bloomByte == 0 { - continue - } - base := 8 * byt - b.blooms[base+7][byteIndex] |= ((bloomByte >> 7) & 1) << bitIndex - b.blooms[base+6][byteIndex] |= ((bloomByte >> 6) & 1) << bitIndex - b.blooms[base+5][byteIndex] |= ((bloomByte >> 5) & 1) << bitIndex - b.blooms[base+4][byteIndex] |= ((bloomByte >> 4) & 1) << bitIndex - b.blooms[base+3][byteIndex] |= ((bloomByte >> 3) & 1) << bitIndex - b.blooms[base+2][byteIndex] |= ((bloomByte >> 2) & 1) << bitIndex - b.blooms[base+1][byteIndex] |= ((bloomByte >> 1) & 1) << bitIndex - b.blooms[base][byteIndex] |= (bloomByte & 1) << bitIndex - } - b.nextSec++ - return nil -} - -// Bitset returns the bit vector belonging to the given bit index after all -// blooms have been added. -func (b *Generator) Bitset(idx uint) ([]byte, error) { - if b.nextSec != b.sections { - return nil, errors.New("bloom not fully generated yet") - } - if idx >= types.BloomBitLength { - return nil, errBloomBitOutOfBounds - } - return b.blooms[idx], nil -} diff --git a/core/bloombits/generator_test.go b/core/bloombits/generator_test.go deleted file mode 100644 index ac1aee0b252..00000000000 --- a/core/bloombits/generator_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2017 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 bloombits - -import ( - "bytes" - crand "crypto/rand" - "math/rand" - "testing" - - "github.com/ethereum/go-ethereum/core/types" -) - -// Tests that batched bloom bits are correctly rotated from the input bloom -// filters. -func TestGenerator(t *testing.T) { - // Generate the input and the rotated output - var input, output [types.BloomBitLength][types.BloomByteLength]byte - - for i := 0; i < types.BloomBitLength; i++ { - for j := 0; j < types.BloomBitLength; j++ { - bit := byte(rand.Int() % 2) - - input[i][j/8] |= bit << byte(7-j%8) - output[types.BloomBitLength-1-j][i/8] |= bit << byte(7-i%8) - } - } - // Crunch the input through the generator and verify the result - gen, err := NewGenerator(types.BloomBitLength) - if err != nil { - t.Fatalf("failed to create bloombit generator: %v", err) - } - for i, bloom := range input { - if err := gen.AddBloom(uint(i), bloom); err != nil { - t.Fatalf("bloom %d: failed to add: %v", i, err) - } - } - for i, want := range output { - have, err := gen.Bitset(uint(i)) - if err != nil { - t.Fatalf("output %d: failed to retrieve bits: %v", i, err) - } - if !bytes.Equal(have, want[:]) { - t.Errorf("output %d: bit vector mismatch have %x, want %x", i, have, want) - } - } -} - -func BenchmarkGenerator(b *testing.B) { - var input [types.BloomBitLength][types.BloomByteLength]byte - b.Run("empty", func(b *testing.B) { - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - // Crunch the input through the generator and verify the result - gen, err := NewGenerator(types.BloomBitLength) - if err != nil { - b.Fatalf("failed to create bloombit generator: %v", err) - } - for j, bloom := range &input { - if err := gen.AddBloom(uint(j), bloom); err != nil { - b.Fatalf("bloom %d: failed to add: %v", i, err) - } - } - } - }) - for i := 0; i < types.BloomBitLength; i++ { - crand.Read(input[i][:]) - } - b.Run("random", func(b *testing.B) { - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - // Crunch the input through the generator and verify the result - gen, err := NewGenerator(types.BloomBitLength) - if err != nil { - b.Fatalf("failed to create bloombit generator: %v", err) - } - for j, bloom := range &input { - if err := gen.AddBloom(uint(j), bloom); err != nil { - b.Fatalf("bloom %d: failed to add: %v", i, err) - } - } - } - }) -} diff --git a/core/bloombits/matcher.go b/core/bloombits/matcher.go deleted file mode 100644 index 486581fe23d..00000000000 --- a/core/bloombits/matcher.go +++ /dev/null @@ -1,649 +0,0 @@ -// Copyright 2017 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 bloombits - -import ( - "bytes" - "context" - "errors" - "math" - "sort" - "sync" - "sync/atomic" - "time" - - "github.com/ethereum/go-ethereum/common/bitutil" - "github.com/ethereum/go-ethereum/crypto" -) - -// bloomIndexes represents the bit indexes inside the bloom filter that belong -// to some key. -type bloomIndexes [3]uint - -// calcBloomIndexes returns the bloom filter bit indexes belonging to the given key. -func calcBloomIndexes(b []byte) bloomIndexes { - b = crypto.Keccak256(b) - - var idxs bloomIndexes - for i := 0; i < len(idxs); i++ { - idxs[i] = (uint(b[2*i])<<8)&2047 + uint(b[2*i+1]) - } - return idxs -} - -// partialMatches with a non-nil vector represents a section in which some sub- -// matchers have already found potential matches. Subsequent sub-matchers will -// binary AND their matches with this vector. If vector is nil, it represents a -// section to be processed by the first sub-matcher. -type partialMatches struct { - section uint64 - bitset []byte -} - -// Retrieval represents a request for retrieval task assignments for a given -// bit with the given number of fetch elements, or a response for such a request. -// It can also have the actual results set to be used as a delivery data struct. -// -// The context and error fields are used by the light client to terminate matching -// early if an error is encountered on some path of the pipeline. -type Retrieval struct { - Bit uint - Sections []uint64 - Bitsets [][]byte - - Context context.Context - Error error -} - -// Matcher is a pipelined system of schedulers and logic matchers which perform -// binary AND/OR operations on the bit-streams, creating a stream of potential -// blocks to inspect for data content. -type Matcher struct { - sectionSize uint64 // Size of the data batches to filter on - - filters [][]bloomIndexes // Filter the system is matching for - schedulers map[uint]*scheduler // Retrieval schedulers for loading bloom bits - - retrievers chan chan uint // Retriever processes waiting for bit allocations - counters chan chan uint // Retriever processes waiting for task count reports - retrievals chan chan *Retrieval // Retriever processes waiting for task allocations - deliveries chan *Retrieval // Retriever processes waiting for task response deliveries - - running atomic.Bool // Atomic flag whether a session is live or not -} - -// NewMatcher creates a new pipeline for retrieving bloom bit streams and doing -// address and topic filtering on them. Setting a filter component to `nil` is -// allowed and will result in that filter rule being skipped (OR 0x11...1). -func NewMatcher(sectionSize uint64, filters [][][]byte) *Matcher { - // Create the matcher instance - m := &Matcher{ - sectionSize: sectionSize, - schedulers: make(map[uint]*scheduler), - retrievers: make(chan chan uint), - counters: make(chan chan uint), - retrievals: make(chan chan *Retrieval), - deliveries: make(chan *Retrieval), - } - // Calculate the bloom bit indexes for the groups we're interested in - m.filters = nil - - for _, filter := range filters { - // Gather the bit indexes of the filter rule, special casing the nil filter - if len(filter) == 0 { - continue - } - bloomBits := make([]bloomIndexes, len(filter)) - for i, clause := range filter { - if clause == nil { - bloomBits = nil - break - } - bloomBits[i] = calcBloomIndexes(clause) - } - // Accumulate the filter rules if no nil rule was within - if bloomBits != nil { - m.filters = append(m.filters, bloomBits) - } - } - // For every bit, create a scheduler to load/download the bit vectors - for _, bloomIndexLists := range m.filters { - for _, bloomIndexList := range bloomIndexLists { - for _, bloomIndex := range bloomIndexList { - m.addScheduler(bloomIndex) - } - } - } - return m -} - -// addScheduler adds a bit stream retrieval scheduler for the given bit index if -// it has not existed before. If the bit is already selected for filtering, the -// existing scheduler can be used. -func (m *Matcher) addScheduler(idx uint) { - if _, ok := m.schedulers[idx]; ok { - return - } - m.schedulers[idx] = newScheduler(idx) -} - -// Start starts the matching process and returns a stream of bloom matches in -// a given range of blocks. If there are no more matches in the range, the result -// channel is closed. -func (m *Matcher) Start(ctx context.Context, begin, end uint64, results chan uint64) (*MatcherSession, error) { - // Make sure we're not creating concurrent sessions - if m.running.Swap(true) { - return nil, errors.New("matcher already running") - } - defer m.running.Store(false) - - // Initiate a new matching round - session := &MatcherSession{ - matcher: m, - quit: make(chan struct{}), - ctx: ctx, - } - for _, scheduler := range m.schedulers { - scheduler.reset() - } - sink := m.run(begin, end, cap(results), session) - - // Read the output from the result sink and deliver to the user - session.pend.Add(1) - go func() { - defer session.pend.Done() - defer close(results) - - for { - select { - case <-session.quit: - return - - case res, ok := <-sink: - // New match result found - if !ok { - return - } - // Calculate the first and last blocks of the section - sectionStart := res.section * m.sectionSize - - first := sectionStart - if begin > first { - first = begin - } - last := sectionStart + m.sectionSize - 1 - if end < last { - last = end - } - // Iterate over all the blocks in the section and return the matching ones - for i := first; i <= last; i++ { - // Skip the entire byte if no matches are found inside (and we're processing an entire byte!) - next := res.bitset[(i-sectionStart)/8] - if next == 0 { - if i%8 == 0 { - i += 7 - } - continue - } - // Some bit it set, do the actual submatching - if bit := 7 - i%8; next&(1<= req.section }) - requests[req.bit] = append(queue[:index], append([]uint64{req.section}, queue[index:]...)...) - - // If it's a new bit and we have waiting fetchers, allocate to them - if len(queue) == 0 { - assign(req.bit) - } - - case fetcher := <-retrievers: - // New retriever arrived, find the lowest section-ed bit to assign - bit, best := uint(0), uint64(math.MaxUint64) - for idx := range unallocs { - if requests[idx][0] < best { - bit, best = idx, requests[idx][0] - } - } - // Stop tracking this bit (and alloc notifications if no more work is available) - delete(unallocs, bit) - if len(unallocs) == 0 { - retrievers = nil - } - allocs++ - fetcher <- bit - - case fetcher := <-m.counters: - // New task count request arrives, return number of items - fetcher <- uint(len(requests[<-fetcher])) - - case fetcher := <-m.retrievals: - // New fetcher waiting for tasks to retrieve, assign - task := <-fetcher - if want := len(task.Sections); want >= len(requests[task.Bit]) { - task.Sections = requests[task.Bit] - delete(requests, task.Bit) - } else { - task.Sections = append(task.Sections[:0], requests[task.Bit][:want]...) - requests[task.Bit] = append(requests[task.Bit][:0], requests[task.Bit][want:]...) - } - fetcher <- task - - // If anything was left unallocated, try to assign to someone else - if len(requests[task.Bit]) > 0 { - assign(task.Bit) - } - - case result := <-m.deliveries: - // New retrieval task response from fetcher, split out missing sections and - // deliver complete ones - var ( - sections = make([]uint64, 0, len(result.Sections)) - bitsets = make([][]byte, 0, len(result.Bitsets)) - missing = make([]uint64, 0, len(result.Sections)) - ) - for i, bitset := range result.Bitsets { - if len(bitset) == 0 { - missing = append(missing, result.Sections[i]) - continue - } - sections = append(sections, result.Sections[i]) - bitsets = append(bitsets, bitset) - } - m.schedulers[result.Bit].deliver(sections, bitsets) - allocs-- - - // Reschedule missing sections and allocate bit if newly available - if len(missing) > 0 { - queue := requests[result.Bit] - for _, section := range missing { - index := sort.Search(len(queue), func(i int) bool { return queue[i] >= section }) - queue = append(queue[:index], append([]uint64{section}, queue[index:]...)...) - } - requests[result.Bit] = queue - - if len(queue) == len(missing) { - assign(result.Bit) - } - } - - // End the session when all pending deliveries have arrived. - if shutdown == nil && allocs == 0 { - return - } - } - } -} - -// MatcherSession is returned by a started matcher to be used as a terminator -// for the actively running matching operation. -type MatcherSession struct { - matcher *Matcher - - closer sync.Once // Sync object to ensure we only ever close once - quit chan struct{} // Quit channel to request pipeline termination - - ctx context.Context // Context used by the light client to abort filtering - err error // Global error to track retrieval failures deep in the chain - errLock sync.Mutex - - pend sync.WaitGroup -} - -// Close stops the matching process and waits for all subprocesses to terminate -// before returning. The timeout may be used for graceful shutdown, allowing the -// currently running retrievals to complete before this time. -func (s *MatcherSession) Close() { - s.closer.Do(func() { - // Signal termination and wait for all goroutines to tear down - close(s.quit) - s.pend.Wait() - }) -} - -// Error returns any failure encountered during the matching session. -func (s *MatcherSession) Error() error { - s.errLock.Lock() - defer s.errLock.Unlock() - - return s.err -} - -// allocateRetrieval assigns a bloom bit index to a client process that can either -// immediately request and fetch the section contents assigned to this bit or wait -// a little while for more sections to be requested. -func (s *MatcherSession) allocateRetrieval() (uint, bool) { - fetcher := make(chan uint) - - select { - case <-s.quit: - return 0, false - case s.matcher.retrievers <- fetcher: - bit, ok := <-fetcher - return bit, ok - } -} - -// pendingSections returns the number of pending section retrievals belonging to -// the given bloom bit index. -func (s *MatcherSession) pendingSections(bit uint) int { - fetcher := make(chan uint) - - select { - case <-s.quit: - return 0 - case s.matcher.counters <- fetcher: - fetcher <- bit - return int(<-fetcher) - } -} - -// allocateSections assigns all or part of an already allocated bit-task queue -// to the requesting process. -func (s *MatcherSession) allocateSections(bit uint, count int) []uint64 { - fetcher := make(chan *Retrieval) - - select { - case <-s.quit: - return nil - case s.matcher.retrievals <- fetcher: - task := &Retrieval{ - Bit: bit, - Sections: make([]uint64, count), - } - fetcher <- task - return (<-fetcher).Sections - } -} - -// deliverSections delivers a batch of section bit-vectors for a specific bloom -// bit index to be injected into the processing pipeline. -func (s *MatcherSession) deliverSections(bit uint, sections []uint64, bitsets [][]byte) { - s.matcher.deliveries <- &Retrieval{Bit: bit, Sections: sections, Bitsets: bitsets} -} - -// Multiplex polls the matcher session for retrieval tasks and multiplexes it into -// the requested retrieval queue to be serviced together with other sessions. -// -// This method will block for the lifetime of the session. Even after termination -// of the session, any request in-flight need to be responded to! Empty responses -// are fine though in that case. -func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan *Retrieval) { - waitTimer := time.NewTimer(wait) - defer waitTimer.Stop() - - for { - // Allocate a new bloom bit index to retrieve data for, stopping when done - bit, ok := s.allocateRetrieval() - if !ok { - return - } - // Bit allocated, throttle a bit if we're below our batch limit - if s.pendingSections(bit) < batch { - waitTimer.Reset(wait) - select { - case <-s.quit: - // Session terminating, we can't meaningfully service, abort - s.allocateSections(bit, 0) - s.deliverSections(bit, []uint64{}, [][]byte{}) - return - - case <-waitTimer.C: - // Throttling up, fetch whatever is available - } - } - // Allocate as much as we can handle and request servicing - sections := s.allocateSections(bit, batch) - request := make(chan *Retrieval) - - select { - case <-s.quit: - // Session terminating, we can't meaningfully service, abort - s.deliverSections(bit, sections, make([][]byte, len(sections))) - return - - case mux <- request: - // Retrieval accepted, something must arrive before we're aborting - request <- &Retrieval{Bit: bit, Sections: sections, Context: s.ctx} - - result := <-request - - // Deliver a result before s.Close() to avoid a deadlock - s.deliverSections(result.Bit, result.Sections, result.Bitsets) - - if result.Error != nil { - s.errLock.Lock() - s.err = result.Error - s.errLock.Unlock() - s.Close() - } - } - } -} diff --git a/core/bloombits/matcher_test.go b/core/bloombits/matcher_test.go deleted file mode 100644 index 7f3d5f279ca..00000000000 --- a/core/bloombits/matcher_test.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2017 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 bloombits - -import ( - "context" - "math/rand" - "sync/atomic" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" -) - -const testSectionSize = 4096 - -// Tests that wildcard filter rules (nil) can be specified and are handled well. -func TestMatcherWildcards(t *testing.T) { - t.Parallel() - matcher := NewMatcher(testSectionSize, [][][]byte{ - {common.Address{}.Bytes(), common.Address{0x01}.Bytes()}, // Default address is not a wildcard - {common.Hash{}.Bytes(), common.Hash{0x01}.Bytes()}, // Default hash is not a wildcard - {common.Hash{0x01}.Bytes()}, // Plain rule, sanity check - {common.Hash{0x01}.Bytes(), nil}, // Wildcard suffix, drop rule - {nil, common.Hash{0x01}.Bytes()}, // Wildcard prefix, drop rule - {nil, nil}, // Wildcard combo, drop rule - {}, // Inited wildcard rule, drop rule - nil, // Proper wildcard rule, drop rule - }) - if len(matcher.filters) != 3 { - t.Fatalf("filter system size mismatch: have %d, want %d", len(matcher.filters), 3) - } - if len(matcher.filters[0]) != 2 { - t.Fatalf("address clause size mismatch: have %d, want %d", len(matcher.filters[0]), 2) - } - if len(matcher.filters[1]) != 2 { - t.Fatalf("combo topic clause size mismatch: have %d, want %d", len(matcher.filters[1]), 2) - } - if len(matcher.filters[2]) != 1 { - t.Fatalf("singletone topic clause size mismatch: have %d, want %d", len(matcher.filters[2]), 1) - } -} - -// Tests the matcher pipeline on a single continuous workflow without interrupts. -func TestMatcherContinuous(t *testing.T) { - t.Parallel() - testMatcherDiffBatches(t, [][]bloomIndexes{{{10, 20, 30}}}, 0, 100000, false, 75) - testMatcherDiffBatches(t, [][]bloomIndexes{{{32, 3125, 100}}, {{40, 50, 10}}}, 0, 100000, false, 81) - testMatcherDiffBatches(t, [][]bloomIndexes{{{4, 8, 11}, {7, 8, 17}}, {{9, 9, 12}, {15, 20, 13}}, {{18, 15, 15}, {12, 10, 4}}}, 0, 10000, false, 36) -} - -// Tests the matcher pipeline on a constantly interrupted and resumed work pattern -// with the aim of ensuring data items are requested only once. -func TestMatcherIntermittent(t *testing.T) { - t.Parallel() - testMatcherDiffBatches(t, [][]bloomIndexes{{{10, 20, 30}}}, 0, 100000, true, 75) - testMatcherDiffBatches(t, [][]bloomIndexes{{{32, 3125, 100}}, {{40, 50, 10}}}, 0, 100000, true, 81) - testMatcherDiffBatches(t, [][]bloomIndexes{{{4, 8, 11}, {7, 8, 17}}, {{9, 9, 12}, {15, 20, 13}}, {{18, 15, 15}, {12, 10, 4}}}, 0, 10000, true, 36) -} - -// Tests the matcher pipeline on random input to hopefully catch anomalies. -func TestMatcherRandom(t *testing.T) { - t.Parallel() - for i := 0; i < 10; i++ { - testMatcherBothModes(t, makeRandomIndexes([]int{1}, 50), 0, 10000, 0) - testMatcherBothModes(t, makeRandomIndexes([]int{3}, 50), 0, 10000, 0) - testMatcherBothModes(t, makeRandomIndexes([]int{2, 2, 2}, 20), 0, 10000, 0) - testMatcherBothModes(t, makeRandomIndexes([]int{5, 5, 5}, 50), 0, 10000, 0) - testMatcherBothModes(t, makeRandomIndexes([]int{4, 4, 4}, 20), 0, 10000, 0) - } -} - -// Tests that the matcher can properly find matches if the starting block is -// shifted from a multiple of 8. This is needed to cover an optimisation with -// bitset matching https://github.com/ethereum/go-ethereum/issues/15309. -func TestMatcherShifted(t *testing.T) { - t.Parallel() - // Block 0 always matches in the tests, skip ahead of first 8 blocks with the - // start to get a potential zero byte in the matcher bitset. - - // To keep the second bitset byte zero, the filter must only match for the first - // time in block 16, so doing an all-16 bit filter should suffice. - - // To keep the starting block non divisible by 8, block number 9 is the first - // that would introduce a shift and not match block 0. - testMatcherBothModes(t, [][]bloomIndexes{{{16, 16, 16}}}, 9, 64, 0) -} - -// Tests that matching on everything doesn't crash (special case internally). -func TestWildcardMatcher(t *testing.T) { - t.Parallel() - testMatcherBothModes(t, nil, 0, 10000, 0) -} - -// makeRandomIndexes generates a random filter system, composed of multiple filter -// criteria, each having one bloom list component for the address and arbitrarily -// many topic bloom list components. -func makeRandomIndexes(lengths []int, max int) [][]bloomIndexes { - res := make([][]bloomIndexes, len(lengths)) - for i, topics := range lengths { - res[i] = make([]bloomIndexes, topics) - for j := 0; j < topics; j++ { - for k := 0; k < len(res[i][j]); k++ { - res[i][j][k] = uint(rand.Intn(max-1) + 2) - } - } - } - return res -} - -// testMatcherDiffBatches runs the given matches test in single-delivery and also -// in batches delivery mode, verifying that all kinds of deliveries are handled -// correctly within. -func testMatcherDiffBatches(t *testing.T, filter [][]bloomIndexes, start, blocks uint64, intermittent bool, retrievals uint32) { - singleton := testMatcher(t, filter, start, blocks, intermittent, retrievals, 1) - batched := testMatcher(t, filter, start, blocks, intermittent, retrievals, 16) - - if singleton != batched { - t.Errorf("filter = %v blocks = %v intermittent = %v: request count mismatch, %v in singleton vs. %v in batched mode", filter, blocks, intermittent, singleton, batched) - } -} - -// testMatcherBothModes runs the given matcher test in both continuous as well as -// in intermittent mode, verifying that the request counts match each other. -func testMatcherBothModes(t *testing.T, filter [][]bloomIndexes, start, blocks uint64, retrievals uint32) { - continuous := testMatcher(t, filter, start, blocks, false, retrievals, 16) - intermittent := testMatcher(t, filter, start, blocks, true, retrievals, 16) - - if continuous != intermittent { - t.Errorf("filter = %v blocks = %v: request count mismatch, %v in continuous vs. %v in intermittent mode", filter, blocks, continuous, intermittent) - } -} - -// testMatcher is a generic tester to run the given matcher test and return the -// number of requests made for cross validation between different modes. -func testMatcher(t *testing.T, filter [][]bloomIndexes, start, blocks uint64, intermittent bool, retrievals uint32, maxReqCount int) uint32 { - // Create a new matcher an simulate our explicit random bitsets - matcher := NewMatcher(testSectionSize, nil) - matcher.filters = filter - - for _, rule := range filter { - for _, topic := range rule { - for _, bit := range topic { - matcher.addScheduler(bit) - } - } - } - // Track the number of retrieval requests made - var requested atomic.Uint32 - - // Start the matching session for the filter and the retriever goroutines - quit := make(chan struct{}) - matches := make(chan uint64, 16) - - session, err := matcher.Start(context.Background(), start, blocks-1, matches) - if err != nil { - t.Fatalf("failed to stat matcher session: %v", err) - } - startRetrievers(session, quit, &requested, maxReqCount) - - // Iterate over all the blocks and verify that the pipeline produces the correct matches - for i := start; i < blocks; i++ { - if expMatch3(filter, i) { - match, ok := <-matches - if !ok { - t.Errorf("filter = %v blocks = %v intermittent = %v: expected #%v, results channel closed", filter, blocks, intermittent, i) - return 0 - } - if match != i { - t.Errorf("filter = %v blocks = %v intermittent = %v: expected #%v, got #%v", filter, blocks, intermittent, i, match) - } - // If we're testing intermittent mode, abort and restart the pipeline - if intermittent { - session.Close() - close(quit) - - quit = make(chan struct{}) - matches = make(chan uint64, 16) - - session, err = matcher.Start(context.Background(), i+1, blocks-1, matches) - if err != nil { - t.Fatalf("failed to stat matcher session: %v", err) - } - startRetrievers(session, quit, &requested, maxReqCount) - } - } - } - // Ensure the result channel is torn down after the last block - match, ok := <-matches - if ok { - t.Errorf("filter = %v blocks = %v intermittent = %v: expected closed channel, got #%v", filter, blocks, intermittent, match) - } - // Clean up the session and ensure we match the expected retrieval count - session.Close() - close(quit) - - if retrievals != 0 && requested.Load() != retrievals { - t.Errorf("filter = %v blocks = %v intermittent = %v: request count mismatch, have #%v, want #%v", filter, blocks, intermittent, requested.Load(), retrievals) - } - return requested.Load() -} - -// startRetrievers starts a batch of goroutines listening for section requests -// and serving them. -func startRetrievers(session *MatcherSession, quit chan struct{}, retrievals *atomic.Uint32, batch int) { - requests := make(chan chan *Retrieval) - - for i := 0; i < 10; i++ { - // Start a multiplexer to test multiple threaded execution - go session.Multiplex(batch, 100*time.Microsecond, requests) - - // Start a services to match the above multiplexer - go func() { - for { - // Wait for a service request or a shutdown - select { - case <-quit: - 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 - task.Bitsets[i] = generateBitset(task.Bit, section) - retrievals.Add(1) - } - } - request <- task - } - } - }() - } -} - -// generateBitset generates the rotated bitset for the given bloom bit and section -// numbers. -func generateBitset(bit uint, section uint64) []byte { - bitset := make([]byte, testSectionSize/8) - for i := 0; i < len(bitset); i++ { - for b := 0; b < 8; b++ { - blockIdx := section*testSectionSize + uint64(i*8+b) - bitset[i] += bitset[i] - if (blockIdx % uint64(bit)) == 0 { - bitset[i]++ - } - } - } - return bitset -} - -func expMatch1(filter bloomIndexes, i uint64) bool { - for _, ii := range filter { - if (i % uint64(ii)) != 0 { - return false - } - } - return true -} - -func expMatch2(filter []bloomIndexes, i uint64) bool { - for _, ii := range filter { - if expMatch1(ii, i) { - return true - } - } - return false -} - -func expMatch3(filter [][]bloomIndexes, i uint64) bool { - for _, ii := range filter { - if !expMatch2(ii, i) { - return false - } - } - return true -} diff --git a/core/bloombits/scheduler.go b/core/bloombits/scheduler.go deleted file mode 100644 index a523bc55ab4..00000000000 --- a/core/bloombits/scheduler.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2017 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 bloombits - -import ( - "sync" -) - -// request represents a bloom retrieval task to prioritize and pull from the local -// database or remotely from the network. -type request struct { - section uint64 // Section index to retrieve the bit-vector from - bit uint // Bit index within the section to retrieve the vector of -} - -// response represents the state of a requested bit-vector through a scheduler. -type response struct { - cached []byte // Cached bits to dedup multiple requests - done chan struct{} // Channel to allow waiting for completion -} - -// scheduler handles the scheduling of bloom-filter retrieval operations for -// entire section-batches belonging to a single bloom bit. Beside scheduling the -// retrieval operations, this struct also deduplicates the requests and caches -// the results to minimize network/database overhead even in complex filtering -// scenarios. -type scheduler struct { - bit uint // Index of the bit in the bloom filter this scheduler is responsible for - responses map[uint64]*response // Currently pending retrieval requests or already cached responses - lock sync.Mutex // Lock protecting the responses from concurrent access -} - -// newScheduler creates a new bloom-filter retrieval scheduler for a specific -// bit index. -func newScheduler(idx uint) *scheduler { - return &scheduler{ - bit: idx, - responses: make(map[uint64]*response), - } -} - -// run creates a retrieval pipeline, receiving section indexes from sections and -// returning the results in the same order through the done channel. Concurrent -// runs of the same scheduler are allowed, leading to retrieval task deduplication. -func (s *scheduler) run(sections chan uint64, dist chan *request, done chan []byte, quit chan struct{}, wg *sync.WaitGroup) { - // Create a forwarder channel between requests and responses of the same size as - // the distribution channel (since that will block the pipeline anyway). - pend := make(chan uint64, cap(dist)) - - // Start the pipeline schedulers to forward between user -> distributor -> user - wg.Add(2) - go s.scheduleRequests(sections, dist, pend, quit, wg) - go s.scheduleDeliveries(pend, done, quit, wg) -} - -// reset cleans up any leftovers from previous runs. This is required before a -// restart to ensure the no previously requested but never delivered state will -// cause a lockup. -func (s *scheduler) reset() { - s.lock.Lock() - defer s.lock.Unlock() - - for section, res := range s.responses { - if res.cached == nil { - delete(s.responses, section) - } - } -} - -// scheduleRequests reads section retrieval requests from the input channel, -// deduplicates the stream and pushes unique retrieval tasks into the distribution -// channel for a database or network layer to honour. -func (s *scheduler) scheduleRequests(reqs chan uint64, dist chan *request, pend chan uint64, quit chan struct{}, wg *sync.WaitGroup) { - // Clean up the goroutine and pipeline when done - defer wg.Done() - defer close(pend) - - // Keep reading and scheduling section requests - for { - select { - case <-quit: - return - - case section, ok := <-reqs: - // New section retrieval requested - if !ok { - return - } - // Deduplicate retrieval requests - unique := false - - s.lock.Lock() - if s.responses[section] == nil { - s.responses[section] = &response{ - done: make(chan struct{}), - } - unique = true - } - s.lock.Unlock() - - // Schedule the section for retrieval and notify the deliverer to expect this section - if unique { - select { - case <-quit: - return - case dist <- &request{bit: s.bit, section: section}: - } - } - select { - case <-quit: - return - case pend <- section: - } - } - } -} - -// scheduleDeliveries reads section acceptance notifications and waits for them -// to be delivered, pushing them into the output data buffer. -func (s *scheduler) scheduleDeliveries(pend chan uint64, done chan []byte, quit chan struct{}, wg *sync.WaitGroup) { - // Clean up the goroutine and pipeline when done - defer wg.Done() - defer close(done) - - // Keep reading notifications and scheduling deliveries - for { - select { - case <-quit: - return - - case idx, ok := <-pend: - // New section retrieval pending - if !ok { - return - } - // Wait until the request is honoured - s.lock.Lock() - res := s.responses[idx] - s.lock.Unlock() - - select { - case <-quit: - return - case <-res.done: - } - // Deliver the result - select { - case <-quit: - return - case done <- res.cached: - } - } - } -} - -// deliver is called by the request distributor when a reply to a request arrives. -func (s *scheduler) deliver(sections []uint64, data [][]byte) { - s.lock.Lock() - defer s.lock.Unlock() - - for i, section := range sections { - if res := s.responses[section]; res != nil && res.cached == nil { // Avoid non-requests and double deliveries - res.cached = data[i] - close(res.done) - } - } -} diff --git a/core/bloombits/scheduler_test.go b/core/bloombits/scheduler_test.go deleted file mode 100644 index dcaaa915258..00000000000 --- a/core/bloombits/scheduler_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2017 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 bloombits - -import ( - "bytes" - "math/big" - "sync" - "sync/atomic" - "testing" -) - -// Tests that the scheduler can deduplicate and forward retrieval requests to -// underlying fetchers and serve responses back, irrelevant of the concurrency -// of the requesting clients or serving data fetchers. -func TestSchedulerSingleClientSingleFetcher(t *testing.T) { testScheduler(t, 1, 1, 5000) } -func TestSchedulerSingleClientMultiFetcher(t *testing.T) { testScheduler(t, 1, 10, 5000) } -func TestSchedulerMultiClientSingleFetcher(t *testing.T) { testScheduler(t, 10, 1, 5000) } -func TestSchedulerMultiClientMultiFetcher(t *testing.T) { testScheduler(t, 10, 10, 5000) } - -func testScheduler(t *testing.T, clients int, fetchers int, requests int) { - t.Parallel() - f := newScheduler(0) - - // Create a batch of handler goroutines that respond to bloom bit requests and - // deliver them to the scheduler. - var fetchPend sync.WaitGroup - fetchPend.Add(fetchers) - defer fetchPend.Wait() - - fetch := make(chan *request, 16) - defer close(fetch) - - var delivered atomic.Uint32 - for i := 0; i < fetchers; i++ { - go func() { - defer fetchPend.Done() - - for req := range fetch { - delivered.Add(1) - - f.deliver([]uint64{ - req.section + uint64(requests), // Non-requested data (ensure it doesn't go out of bounds) - req.section, // Requested data - req.section, // Duplicated data (ensure it doesn't double close anything) - }, [][]byte{ - {}, - new(big.Int).SetUint64(req.section).Bytes(), - new(big.Int).SetUint64(req.section).Bytes(), - }) - } - }() - } - // Start a batch of goroutines to concurrently run scheduling tasks - quit := make(chan struct{}) - - var pend sync.WaitGroup - pend.Add(clients) - - for i := 0; i < clients; i++ { - go func() { - defer pend.Done() - - in := make(chan uint64, 16) - out := make(chan []byte, 16) - - f.run(in, fetch, out, quit, &pend) - - go func() { - for j := 0; j < requests; j++ { - in <- uint64(j) - } - close(in) - }() - b := new(big.Int) - for j := 0; j < requests; j++ { - bits := <-out - if want := b.SetUint64(uint64(j)).Bytes(); !bytes.Equal(bits, want) { - t.Errorf("vector %d: delivered content mismatch: have %x, want %x", j, bits, want) - } - } - }() - } - pend.Wait() - - if have := delivered.Load(); int(have) != requests { - t.Errorf("request count mismatch: have %v, want %v", have, requests) - } -} diff --git a/core/chain_indexer.go b/core/chain_indexer.go deleted file mode 100644 index 2865daa1ff4..00000000000 --- a/core/chain_indexer.go +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright 2017 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 core - -import ( - "context" - "encoding/binary" - "errors" - "fmt" - "sync" - "sync/atomic" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/log" -) - -// ChainIndexerBackend defines the methods needed to process chain segments in -// the background and write the segment results into the database. These can be -// used to create filter blooms or CHTs. -type ChainIndexerBackend interface { - // Reset initiates the processing of a new chain segment, potentially terminating - // any partially completed operations (in case of a reorg). - Reset(ctx context.Context, section uint64, prevHead common.Hash) error - - // Process crunches through the next header in the chain segment. The caller - // will ensure a sequential order of headers. - Process(ctx context.Context, header *types.Header) error - - // Commit finalizes the section metadata and stores it into the database. - Commit() error - - // Prune deletes the chain index older than the given threshold. - Prune(threshold uint64) error -} - -// ChainIndexerChain interface is used for connecting the indexer to a blockchain -type ChainIndexerChain interface { - // CurrentHeader retrieves the latest locally known header. - CurrentHeader() *types.Header - - // SubscribeChainHeadEvent subscribes to new head header notifications. - SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription -} - -// ChainIndexer does a post-processing job for equally sized sections of the -// canonical chain (like BlooomBits and CHT structures). A ChainIndexer is -// connected to the blockchain through the event system by starting a -// ChainHeadEventLoop in a goroutine. -// -// Further child ChainIndexers can be added which use the output of the parent -// section indexer. These child indexers receive new head notifications only -// after an entire section has been finished or in case of rollbacks that might -// affect already finished sections. -type ChainIndexer struct { - chainDb ethdb.Database // Chain database to index the data from - indexDb ethdb.Database // Prefixed table-view of the db to write index metadata into - backend ChainIndexerBackend // Background processor generating the index data content - children []*ChainIndexer // Child indexers to cascade chain updates to - - active atomic.Bool // Flag whether the event loop was started - update chan struct{} // Notification channel that headers should be processed - quit chan chan error // Quit channel to tear down running goroutines - ctx context.Context - ctxCancel func() - - sectionSize uint64 // Number of blocks in a single chain segment to process - confirmsReq uint64 // Number of confirmations before processing a completed segment - - storedSections uint64 // Number of sections successfully indexed into the database - knownSections uint64 // Number of sections known to be complete (block wise) - cascadedHead uint64 // Block number of the last completed section cascaded to subindexers - - checkpointSections uint64 // Number of sections covered by the checkpoint - checkpointHead common.Hash // Section head belonging to the checkpoint - - throttling time.Duration // Disk throttling to prevent a heavy upgrade from hogging resources - - log log.Logger - lock sync.Mutex -} - -// NewChainIndexer creates a new chain indexer to do background processing on -// chain segments of a given size after certain number of confirmations passed. -// The throttling parameter might be used to prevent database thrashing. -func NewChainIndexer(chainDb ethdb.Database, indexDb ethdb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string) *ChainIndexer { - c := &ChainIndexer{ - chainDb: chainDb, - indexDb: indexDb, - backend: backend, - update: make(chan struct{}, 1), - quit: make(chan chan error), - sectionSize: section, - confirmsReq: confirm, - throttling: throttling, - log: log.New("type", kind), - } - // Initialize database dependent fields and start the updater - c.loadValidSections() - c.ctx, c.ctxCancel = context.WithCancel(context.Background()) - - go c.updateLoop() - - return c -} - -// AddCheckpoint adds a checkpoint. Sections are never processed and the chain -// is not expected to be available before this point. The indexer assumes that -// the backend has sufficient information available to process subsequent sections. -// -// Note: knownSections == 0 and storedSections == checkpointSections until -// syncing reaches the checkpoint -func (c *ChainIndexer) AddCheckpoint(section uint64, shead common.Hash) { - c.lock.Lock() - defer c.lock.Unlock() - - // Short circuit if the given checkpoint is below than local's. - if c.checkpointSections >= section+1 || section < c.storedSections { - return - } - c.checkpointSections = section + 1 - c.checkpointHead = shead - - c.setSectionHead(section, shead) - c.setValidSections(section + 1) -} - -// Start creates a goroutine to feed chain head events into the indexer for -// cascading background processing. Children do not need to be started, they -// are notified about new events by their parents. -func (c *ChainIndexer) Start(chain ChainIndexerChain) { - events := make(chan ChainHeadEvent, 10) - sub := chain.SubscribeChainHeadEvent(events) - - go c.eventLoop(chain.CurrentHeader(), events, sub) -} - -// Close tears down all goroutines belonging to the indexer and returns any error -// that might have occurred internally. -func (c *ChainIndexer) Close() error { - var errs []error - - c.ctxCancel() - - // Tear down the primary update loop - errc := make(chan error) - c.quit <- errc - if err := <-errc; err != nil { - errs = append(errs, err) - } - // If needed, tear down the secondary event loop - if c.active.Load() { - c.quit <- errc - if err := <-errc; err != nil { - errs = append(errs, err) - } - } - // Close all children - for _, child := range c.children { - if err := child.Close(); err != nil { - errs = append(errs, err) - } - } - // Return any failures - switch { - case len(errs) == 0: - return nil - - case len(errs) == 1: - return errs[0] - - default: - return fmt.Errorf("%v", errs) - } -} - -// eventLoop is a secondary - optional - event loop of the indexer which is only -// started for the outermost indexer to push chain head events into a processing -// queue. -func (c *ChainIndexer) eventLoop(currentHeader *types.Header, events chan ChainHeadEvent, sub event.Subscription) { - // Mark the chain indexer as active, requiring an additional teardown - c.active.Store(true) - - defer sub.Unsubscribe() - - // Fire the initial new head event to start any outstanding processing - c.newHead(currentHeader.Number.Uint64(), false) - - var ( - prevHeader = currentHeader - prevHash = currentHeader.Hash() - ) - for { - select { - case errc := <-c.quit: - // Chain indexer terminating, report no failure and abort - errc <- nil - return - - case ev, ok := <-events: - // Received a new event, ensure it's not nil (closing) and update - if !ok { - errc := <-c.quit - errc <- nil - return - } - if ev.Header.ParentHash != prevHash { - // Reorg to the common ancestor if needed (might not exist in light sync mode, skip reorg then) - // TODO(karalabe, zsfelfoldi): This seems a bit brittle, can we detect this case explicitly? - - if rawdb.ReadCanonicalHash(c.chainDb, prevHeader.Number.Uint64()) != prevHash { - if h := rawdb.FindCommonAncestor(c.chainDb, prevHeader, ev.Header); h != nil { - c.newHead(h.Number.Uint64(), true) - } - } - } - c.newHead(ev.Header.Number.Uint64(), false) - - prevHeader, prevHash = ev.Header, ev.Header.Hash() - } - } -} - -// newHead notifies the indexer about new chain heads and/or reorgs. -func (c *ChainIndexer) newHead(head uint64, reorg bool) { - c.lock.Lock() - defer c.lock.Unlock() - - // If a reorg happened, invalidate all sections until that point - if reorg { - // Revert the known section number to the reorg point - known := (head + 1) / c.sectionSize - stored := known - if known < c.checkpointSections { - known = 0 - } - if stored < c.checkpointSections { - stored = c.checkpointSections - } - if known < c.knownSections { - c.knownSections = known - } - // Revert the stored sections from the database to the reorg point - if stored < c.storedSections { - c.setValidSections(stored) - } - // Update the new head number to the finalized section end and notify children - head = known * c.sectionSize - - if head < c.cascadedHead { - c.cascadedHead = head - for _, child := range c.children { - child.newHead(c.cascadedHead, true) - } - } - return - } - // No reorg, calculate the number of newly known sections and update if high enough - var sections uint64 - if head >= c.confirmsReq { - sections = (head + 1 - c.confirmsReq) / c.sectionSize - if sections < c.checkpointSections { - sections = 0 - } - if sections > c.knownSections { - if c.knownSections < c.checkpointSections { - // syncing reached the checkpoint, verify section head - syncedHead := rawdb.ReadCanonicalHash(c.chainDb, c.checkpointSections*c.sectionSize-1) - if syncedHead != c.checkpointHead { - c.log.Error("Synced chain does not match checkpoint", "number", c.checkpointSections*c.sectionSize-1, "expected", c.checkpointHead, "synced", syncedHead) - return - } - } - c.knownSections = sections - - select { - case c.update <- struct{}{}: - default: - } - } - } -} - -// updateLoop is the main event loop of the indexer which pushes chain segments -// down into the processing backend. -func (c *ChainIndexer) updateLoop() { - var ( - updating bool - updated time.Time - ) - - for { - select { - case errc := <-c.quit: - // Chain indexer terminating, report no failure and abort - errc <- nil - return - - case <-c.update: - // Section headers completed (or rolled back), update the index - c.lock.Lock() - if c.knownSections > c.storedSections { - // Periodically print an upgrade log message to the user - if time.Since(updated) > 8*time.Second { - if c.knownSections > c.storedSections+1 { - updating = true - c.log.Info("Upgrading chain index", "percentage", c.storedSections*100/c.knownSections) - } - updated = time.Now() - } - // Cache the current section count and head to allow unlocking the mutex - c.verifyLastHead() - section := c.storedSections - var oldHead common.Hash - if section > 0 { - oldHead = c.SectionHead(section - 1) - } - // Process the newly defined section in the background - c.lock.Unlock() - newHead, err := c.processSection(section, oldHead) - if err != nil { - select { - case <-c.ctx.Done(): - <-c.quit <- nil - return - default: - } - c.log.Error("Section processing failed", "error", err) - } - c.lock.Lock() - - // If processing succeeded and no reorgs occurred, mark the section completed - if err == nil && (section == 0 || oldHead == c.SectionHead(section-1)) { - c.setSectionHead(section, newHead) - c.setValidSections(section + 1) - if c.storedSections == c.knownSections && updating { - updating = false - c.log.Info("Finished upgrading chain index") - } - c.cascadedHead = c.storedSections*c.sectionSize - 1 - for _, child := range c.children { - c.log.Trace("Cascading chain index update", "head", c.cascadedHead) - child.newHead(c.cascadedHead, false) - } - } else { - // If processing failed, don't retry until further notification - c.log.Debug("Chain index processing failed", "section", section, "err", err) - c.verifyLastHead() - c.knownSections = c.storedSections - } - } - // If there are still further sections to process, reschedule - if c.knownSections > c.storedSections { - time.AfterFunc(c.throttling, func() { - select { - case c.update <- struct{}{}: - default: - } - }) - } - c.lock.Unlock() - } - } -} - -// processSection processes an entire section by calling backend functions while -// ensuring the continuity of the passed headers. Since the chain mutex is not -// held while processing, the continuity can be broken by a long reorg, in which -// case the function returns with an error. -func (c *ChainIndexer) processSection(section uint64, lastHead common.Hash) (common.Hash, error) { - c.log.Trace("Processing new chain section", "section", section) - - // Reset and partial processing - if err := c.backend.Reset(c.ctx, section, lastHead); err != nil { - c.setValidSections(0) - return common.Hash{}, err - } - - for number := section * c.sectionSize; number < (section+1)*c.sectionSize; number++ { - hash := rawdb.ReadCanonicalHash(c.chainDb, number) - if hash == (common.Hash{}) { - return common.Hash{}, fmt.Errorf("canonical block #%d unknown", number) - } - header := rawdb.ReadHeader(c.chainDb, hash, number) - if header == nil { - return common.Hash{}, fmt.Errorf("block #%d [%x..] not found", number, hash[:4]) - } else if header.ParentHash != lastHead { - return common.Hash{}, errors.New("chain reorged during section processing") - } - if err := c.backend.Process(c.ctx, header); err != nil { - return common.Hash{}, err - } - lastHead = header.Hash() - } - if err := c.backend.Commit(); err != nil { - return common.Hash{}, err - } - return lastHead, nil -} - -// verifyLastHead compares last stored section head with the corresponding block hash in the -// actual canonical chain and rolls back reorged sections if necessary to ensure that stored -// sections are all valid -func (c *ChainIndexer) verifyLastHead() { - for c.storedSections > 0 && c.storedSections > c.checkpointSections { - if c.SectionHead(c.storedSections-1) == rawdb.ReadCanonicalHash(c.chainDb, c.storedSections*c.sectionSize-1) { - return - } - c.setValidSections(c.storedSections - 1) - } -} - -// Sections returns the number of processed sections maintained by the indexer -// and also the information about the last header indexed for potential canonical -// verifications. -func (c *ChainIndexer) Sections() (uint64, uint64, common.Hash) { - c.lock.Lock() - defer c.lock.Unlock() - - c.verifyLastHead() - return c.storedSections, c.storedSections*c.sectionSize - 1, c.SectionHead(c.storedSections - 1) -} - -// AddChildIndexer adds a child ChainIndexer that can use the output of this one -func (c *ChainIndexer) AddChildIndexer(indexer *ChainIndexer) { - if indexer == c { - panic("can't add indexer as a child of itself") - } - c.lock.Lock() - defer c.lock.Unlock() - - c.children = append(c.children, indexer) - - // Cascade any pending updates to new children too - sections := c.storedSections - if c.knownSections < sections { - // if a section is "stored" but not "known" then it is a checkpoint without - // available chain data so we should not cascade it yet - sections = c.knownSections - } - if sections > 0 { - indexer.newHead(sections*c.sectionSize-1, false) - } -} - -// Prune deletes all chain data older than given threshold. -func (c *ChainIndexer) Prune(threshold uint64) error { - return c.backend.Prune(threshold) -} - -// loadValidSections reads the number of valid sections from the index database -// and caches is into the local state. -func (c *ChainIndexer) loadValidSections() { - data, _ := c.indexDb.Get([]byte("count")) - if len(data) == 8 { - c.storedSections = binary.BigEndian.Uint64(data) - } -} - -// setValidSections writes the number of valid sections to the index database -func (c *ChainIndexer) setValidSections(sections uint64) { - // Set the current number of valid sections in the database - var data [8]byte - binary.BigEndian.PutUint64(data[:], sections) - c.indexDb.Put([]byte("count"), data[:]) - - // Remove any reorged sections, caching the valids in the mean time - for c.storedSections > sections { - c.storedSections-- - c.removeSectionHead(c.storedSections) - } - c.storedSections = sections // needed if new > old -} - -// SectionHead retrieves the last block hash of a processed section from the -// index database. -func (c *ChainIndexer) SectionHead(section uint64) common.Hash { - var data [8]byte - binary.BigEndian.PutUint64(data[:], section) - - hash, _ := c.indexDb.Get(append([]byte("shead"), data[:]...)) - if len(hash) == len(common.Hash{}) { - return common.BytesToHash(hash) - } - return common.Hash{} -} - -// setSectionHead writes the last block hash of a processed section to the index -// database. -func (c *ChainIndexer) setSectionHead(section uint64, hash common.Hash) { - var data [8]byte - binary.BigEndian.PutUint64(data[:], section) - - c.indexDb.Put(append([]byte("shead"), data[:]...), hash.Bytes()) -} - -// removeSectionHead removes the reference to a processed section from the index -// database. -func (c *ChainIndexer) removeSectionHead(section uint64) { - var data [8]byte - binary.BigEndian.PutUint64(data[:], section) - - c.indexDb.Delete(append([]byte("shead"), data[:]...)) -} diff --git a/core/chain_indexer_test.go b/core/chain_indexer_test.go deleted file mode 100644 index bf3bde756cb..00000000000 --- a/core/chain_indexer_test.go +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2017 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 core - -import ( - "context" - "errors" - "fmt" - "math/big" - "math/rand" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" -) - -// Runs multiple tests with randomized parameters. -func TestChainIndexerSingle(t *testing.T) { - for i := 0; i < 10; i++ { - testChainIndexer(t, 1) - } -} - -// Runs multiple tests with randomized parameters and different number of -// chain backends. -func TestChainIndexerWithChildren(t *testing.T) { - for i := 2; i < 8; i++ { - testChainIndexer(t, i) - } -} - -// testChainIndexer runs a test with either a single chain indexer or a chain of -// multiple backends. The section size and required confirmation count parameters -// are randomized. -func testChainIndexer(t *testing.T, count int) { - db := rawdb.NewMemoryDatabase() - defer db.Close() - - // Create a chain of indexers and ensure they all report empty - backends := make([]*testChainIndexBackend, count) - for i := 0; i < count; i++ { - var ( - sectionSize = uint64(rand.Intn(100) + 1) - confirmsReq = uint64(rand.Intn(10)) - ) - backends[i] = &testChainIndexBackend{t: t, processCh: make(chan uint64)} - backends[i].indexer = NewChainIndexer(db, rawdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i)) - - if sections, _, _ := backends[i].indexer.Sections(); sections != 0 { - t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, 0) - } - if i > 0 { - backends[i-1].indexer.AddChildIndexer(backends[i].indexer) - } - } - defer backends[0].indexer.Close() // parent indexer shuts down children - // notify pings the root indexer about a new head or reorg, then expect - // processed blocks if a section is processable - notify := func(headNum, failNum uint64, reorg bool) { - backends[0].indexer.newHead(headNum, reorg) - if reorg { - for _, backend := range backends { - headNum = backend.reorg(headNum) - backend.assertSections() - } - return - } - var cascade bool - for _, backend := range backends { - headNum, cascade = backend.assertBlocks(headNum, failNum) - if !cascade { - break - } - backend.assertSections() - } - } - // inject inserts a new random canonical header into the database directly - inject := func(number uint64) { - header := &types.Header{Number: big.NewInt(int64(number)), Extra: big.NewInt(rand.Int63()).Bytes()} - if number > 0 { - header.ParentHash = rawdb.ReadCanonicalHash(db, number-1) - } - rawdb.WriteHeader(db, header) - rawdb.WriteCanonicalHash(db, header.Hash(), number) - } - // Start indexer with an already existing chain - for i := uint64(0); i <= 100; i++ { - inject(i) - } - notify(100, 100, false) - - // Add new blocks one by one - for i := uint64(101); i <= 1000; i++ { - inject(i) - notify(i, i, false) - } - // Do a reorg - notify(500, 500, true) - - // Create new fork - for i := uint64(501); i <= 1000; i++ { - inject(i) - notify(i, i, false) - } - for i := uint64(1001); i <= 1500; i++ { - inject(i) - } - // Failed processing scenario where less blocks are available than notified - notify(2000, 1500, false) - - // Notify about a reorg (which could have caused the missing blocks if happened during processing) - notify(1500, 1500, true) - - // Create new fork - for i := uint64(1501); i <= 2000; i++ { - inject(i) - notify(i, i, false) - } -} - -// testChainIndexBackend implements ChainIndexerBackend -type testChainIndexBackend struct { - t *testing.T - indexer *ChainIndexer - section, headerCnt, stored uint64 - processCh chan uint64 -} - -// assertSections verifies if a chain indexer has the correct number of section. -func (b *testChainIndexBackend) assertSections() { - // Keep trying for 3 seconds if it does not match - var sections uint64 - for i := 0; i < 300; i++ { - sections, _, _ = b.indexer.Sections() - if sections == b.stored { - return - } - time.Sleep(10 * time.Millisecond) - } - b.t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, b.stored) -} - -// assertBlocks expects processing calls after new blocks have arrived. If the -// failNum < headNum then we are simulating a scenario where a reorg has happened -// after the processing has started and the processing of a section fails. -func (b *testChainIndexBackend) assertBlocks(headNum, failNum uint64) (uint64, bool) { - var sections uint64 - if headNum >= b.indexer.confirmsReq { - sections = (headNum + 1 - b.indexer.confirmsReq) / b.indexer.sectionSize - if sections > b.stored { - // expect processed blocks - for expectd := b.stored * b.indexer.sectionSize; expectd < sections*b.indexer.sectionSize; expectd++ { - if expectd > failNum { - // rolled back after processing started, no more process calls expected - // wait until updating is done to make sure that processing actually fails - var updating bool - for i := 0; i < 300; i++ { - b.indexer.lock.Lock() - updating = b.indexer.knownSections > b.indexer.storedSections - b.indexer.lock.Unlock() - if !updating { - break - } - time.Sleep(10 * time.Millisecond) - } - if updating { - b.t.Fatalf("update did not finish") - } - sections = expectd / b.indexer.sectionSize - break - } - select { - case <-time.After(10 * time.Second): - b.t.Fatalf("Expected processed block #%d, got nothing", expectd) - case processed := <-b.processCh: - if processed != expectd { - b.t.Errorf("Expected processed block #%d, got #%d", expectd, processed) - } - } - } - b.stored = sections - } - } - if b.stored == 0 { - return 0, false - } - return b.stored*b.indexer.sectionSize - 1, true -} - -func (b *testChainIndexBackend) reorg(headNum uint64) uint64 { - firstChanged := (headNum + 1) / b.indexer.sectionSize - if firstChanged < b.stored { - b.stored = firstChanged - } - return b.stored * b.indexer.sectionSize -} - -func (b *testChainIndexBackend) Reset(ctx context.Context, section uint64, prevHead common.Hash) error { - b.section = section - b.headerCnt = 0 - return nil -} - -func (b *testChainIndexBackend) Process(ctx context.Context, header *types.Header) error { - b.headerCnt++ - if b.headerCnt > b.indexer.sectionSize { - b.t.Error("Processing too many headers") - } - //t.processCh <- header.Number.Uint64() - select { - case <-time.After(10 * time.Second): - b.t.Error("Unexpected call to Process") - // Can't use Fatal since this is not the test's goroutine. - // Returning error stops the chainIndexer's updateLoop - return errors.New("unexpected call to Process") - case b.processCh <- header.Number.Uint64(): - } - return nil -} - -func (b *testChainIndexBackend) Commit() error { - if b.headerCnt != b.indexer.sectionSize { - b.t.Error("Not enough headers processed") - } - return nil -} - -func (b *testChainIndexBackend) Prune(threshold uint64) error { - return nil -} diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index dfc20521f62..d74b11da044 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -17,7 +17,6 @@ package filtermaps import ( - "bytes" "errors" "fmt" "os" @@ -242,7 +241,8 @@ func (f *FilterMaps) Start() { log.Error("Could not load head filter map snapshot", "error", err) } } - f.closeWg.Add(1) + f.closeWg.Add(2) + go f.removeBloomBits() go f.indexerLoop() } @@ -292,7 +292,7 @@ func (f *FilterMaps) reset() bool { // deleting the range first ensures that resetDb will be called again at next // startup and any leftover data will be removed even if it cannot finish now. rawdb.DeleteFilterMapsRange(f.db) - return f.removeDbWithPrefix([]byte(rawdb.FilterMapsPrefix), "Resetting log index database") + return f.safeDeleteRange(rawdb.DeleteFilterMapsDb, "Resetting log index database") } // init initializes an empty log index according to the current targetView. @@ -335,24 +335,25 @@ func (f *FilterMaps) init() error { return batch.Write() } -// removeDbWithPrefix removes data with the given prefix from the database and -// returns true if everything was successfully removed. -func (f *FilterMaps) removeDbWithPrefix(prefix []byte, action string) bool { - it := f.db.NewIterator(prefix, nil) - hasData := it.Next() - it.Release() - if !hasData { - return true - } +// removeBloomBits removes old bloom bits data from the database. +func (f *FilterMaps) removeBloomBits() { + f.safeDeleteRange(rawdb.DeleteBloomBitsDb, "Removing old bloom bits database") + f.closeWg.Done() +} - end := bytes.Clone(prefix) - end[len(end)-1]++ +// safeDeleteRange calls the specified database range deleter function +// repeatedly as long as it returns leveldb.ErrTooManyKeys. +// This wrapper is necessary because of the leveldb fallback implementation +// of DeleteRange. +func (f *FilterMaps) safeDeleteRange(removeFn func(ethdb.KeyValueRangeDeleter) error, action string) bool { start := time.Now() var retry bool for { - err := f.db.DeleteRange(prefix, end) + err := removeFn(f.db) if err == nil { - log.Info(action+" finished", "elapsed", time.Since(start)) + if retry { + log.Info(action+" finished", "elapsed", time.Since(start)) + } return true } if err != leveldb.ErrTooManyKeys { @@ -365,7 +366,7 @@ func (f *FilterMaps) removeDbWithPrefix(prefix []byte, action string) bool { default: } if !retry { - log.Info(action + " in progress...") + log.Info(action+" in progress...", "elapsed", time.Since(start)) retry = true } } diff --git a/core/filtermaps/indexer.go b/core/filtermaps/indexer.go index 61a9bf03521..69f42d8b606 100644 --- a/core/filtermaps/indexer.go +++ b/core/filtermaps/indexer.go @@ -44,13 +44,18 @@ func (f *FilterMaps) indexerLoop() { if !f.indexedRange.initialized { if err := f.init(); err != nil { log.Error("Error initializing log index", "error", err) - f.waitForEvent() + // unexpected error; there is not a lot we can do here, maybe it + // recovers, maybe not. Calling event processing here ensures + // that we can still properly shutdown in case of an infinite loop. + f.processSingleEvent(true) continue } } if !f.targetHeadIndexed() { if !f.tryIndexHead() { - f.waitForEvent() + // either shutdown or unexpected error; in the latter case ensure + // that proper shutdown is still possible. + f.processSingleEvent(true) } } else { if f.finalBlock != f.lastFinal { @@ -60,7 +65,7 @@ func (f *FilterMaps) indexerLoop() { f.lastFinal = f.finalBlock } if f.tryIndexTail() && f.tryUnindexTail() { - f.waitForEvent() + f.waitForNewHead() } } } @@ -119,8 +124,9 @@ func (f *FilterMaps) WaitIdle() { } } -// waitForEvent blocks until an event happens that the indexer might react to. -func (f *FilterMaps) waitForEvent() { +// waitForNewHead blocks until there is a new target head to index and block +// processing has been finished. +func (f *FilterMaps) waitForNewHead() { for !f.stop && (f.blockProcessing || f.targetHeadIndexed()) { f.processSingleEvent(true) } @@ -129,13 +135,16 @@ func (f *FilterMaps) waitForEvent() { // processEvents processes all events, blocking only if a block processing is // happening and indexing should be suspended. func (f *FilterMaps) processEvents() { - for !f.stop && f.processSingleEvent(f.blockProcessing) { + for f.processSingleEvent(f.blockProcessing) { } } // processSingleEvent processes a single event either in a blocking or -// non-blocking manner. +// non-blocking manner. It returns true if it did process an event. func (f *FilterMaps) processSingleEvent(blocking bool) bool { + if f.stop { + return false + } if !f.hasTempRange { for _, mb := range f.matcherSyncRequests { mb.synced() @@ -356,15 +365,17 @@ func (f *FilterMaps) needTailEpoch(epoch uint32) bool { if epoch+1 < firstEpoch { return false } + var lastBlockOfPrevEpoch uint64 if epoch > 0 { - lastBlockOfPrevEpoch, _, err := f.getLastBlockOfMap(epoch<= firstEpoch } - if f.historyCutoff > lastBlockOfPrevEpoch { - return false - } + } + if f.historyCutoff > lastBlockOfPrevEpoch { + return false } lastBlockOfEpoch, _, err := f.getLastBlockOfMap((epoch+1)<= 0 { - break - } - if len(it.Key()) != len(bloomBitsPrefix)+2+8+32 { - continue - } - db.Delete(it.Key()) - } - if it.Error() != nil { - log.Crit("Failed to delete bloom bits", "err", it.Error()) - } -} - // ReadFilterMapRow retrieves a filter map row at the given mapRowIndex // (see filtermaps.mapRowIndex for the storage index encoding). // Note that zero length rows are not stored in the database and therefore all @@ -485,3 +450,24 @@ func DeleteFilterMapsRange(db ethdb.KeyValueWriter) { log.Crit("Failed to delete filter maps range", "err", err) } } + +// deletePrefixRange deletes everything with the given prefix from the database. +func deletePrefixRange(db ethdb.KeyValueRangeDeleter, prefix []byte) error { + end := bytes.Clone(prefix) + end[len(end)-1]++ + return db.DeleteRange(prefix, end) +} + +// DeleteFilterMapsDb removes the entire filter maps database +func DeleteFilterMapsDb(db ethdb.KeyValueRangeDeleter) error { + return deletePrefixRange(db, []byte(filterMapsPrefix)) +} + +// DeleteFilterMapsDb removes the old bloombits database and the associated +// chain indexer database. +func DeleteBloomBitsDb(db ethdb.KeyValueRangeDeleter) error { + if err := deletePrefixRange(db, bloomBitsPrefix); err != nil { + return err + } + return deletePrefixRange(db, bloomBitsIndexPrefix) +} diff --git a/core/rawdb/accessors_indexes_test.go b/core/rawdb/accessors_indexes_test.go index ea25bf1e2c0..29b468fb2a6 100644 --- a/core/rawdb/accessors_indexes_test.go +++ b/core/rawdb/accessors_indexes_test.go @@ -17,7 +17,6 @@ package rawdb import ( - "bytes" "math/big" "testing" @@ -25,7 +24,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/blocktest" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -111,54 +109,3 @@ func TestLookupStorage(t *testing.T) { }) } } - -func TestDeleteBloomBits(t *testing.T) { - // Prepare testing data - db := NewMemoryDatabase() - for i := uint(0); i < 2; i++ { - for s := uint64(0); s < 2; s++ { - WriteBloomBits(db, i, s, params.MainnetGenesisHash, []byte{0x01, 0x02}) - WriteBloomBits(db, i, s, params.SepoliaGenesisHash, []byte{0x01, 0x02}) - WriteBloomBits(db, i, s, params.HoodiGenesisHash, []byte{0x01, 0x02}) - } - } - check := func(bit uint, section uint64, head common.Hash, exist bool) { - bits, _ := ReadBloomBits(db, bit, section, head) - if exist && !bytes.Equal(bits, []byte{0x01, 0x02}) { - t.Fatalf("Bloombits mismatch") - } - if !exist && len(bits) > 0 { - t.Fatalf("Bloombits should be removed") - } - } - // Check the existence of written data. - check(0, 0, params.MainnetGenesisHash, true) - check(0, 0, params.SepoliaGenesisHash, true) - check(0, 0, params.HoodiGenesisHash, true) - - // Check the existence of deleted data. - DeleteBloombits(db, 0, 0, 1) - check(0, 0, params.MainnetGenesisHash, false) - check(0, 0, params.SepoliaGenesisHash, false) - check(0, 0, params.HoodiGenesisHash, false) - check(0, 1, params.MainnetGenesisHash, true) - check(0, 1, params.SepoliaGenesisHash, true) - check(0, 1, params.HoodiGenesisHash, true) - - // Check the existence of deleted data. - DeleteBloombits(db, 0, 0, 2) - check(0, 0, params.MainnetGenesisHash, false) - check(0, 0, params.SepoliaGenesisHash, false) - check(0, 0, params.HoodiGenesisHash, false) - check(0, 1, params.MainnetGenesisHash, false) - check(0, 1, params.SepoliaGenesisHash, false) - check(0, 1, params.HoodiGenesisHash, false) - - // Bit1 shouldn't be affect. - check(1, 0, params.MainnetGenesisHash, true) - check(1, 0, params.SepoliaGenesisHash, true) - check(1, 0, params.HoodiGenesisHash, true) - check(1, 1, params.MainnetGenesisHash, true) - check(1, 1, params.SepoliaGenesisHash, true) - check(1, 1, params.HoodiGenesisHash, true) -} diff --git a/core/rawdb/database.go b/core/rawdb/database.go index bc293473248..4c87e66cfd6 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -375,7 +375,6 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { accountSnaps stat storageSnaps stat preimages stat - bloomBits stat filterMaps stat beaconHeaders stat cliqueSnaps stat @@ -437,11 +436,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { metadata.Add(size) case bytes.HasPrefix(key, genesisPrefix) && len(key) == (len(genesisPrefix)+common.HashLength): metadata.Add(size) - case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength): - bloomBits.Add(size) - case bytes.HasPrefix(key, BloomBitsIndexPrefix): - bloomBits.Add(size) - case bytes.HasPrefix(key, []byte(FilterMapsPrefix)): + case bytes.HasPrefix(key, []byte(filterMapsPrefix)): filterMaps.Add(size) case bytes.HasPrefix(key, skeletonHeaderPrefix) && len(key) == (len(skeletonHeaderPrefix)+8): beaconHeaders.Add(size) @@ -507,7 +502,6 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { {"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()}, {"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()}, {"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()}, - {"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()}, {"Key-Value store", "Log search index", filterMaps.Size(), filterMaps.Count()}, {"Key-Value store", "Contract codes", codes.Size(), codes.Count()}, {"Key-Value store", "Hash trie nodes", legacyTries.Size(), legacyTries.Count()}, diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index b41951113ce..c21a96bd24f 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -128,8 +128,8 @@ var ( configPrefix = []byte("ethereum-config-") // config prefix for the db genesisPrefix = []byte("ethereum-genesis-") // genesis state prefix for the db - // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress - BloomBitsIndexPrefix = []byte("iB") + // bloomBitsIndexPrefix is the data table of a chain indexer to track its progress + bloomBitsIndexPrefix = []byte("iB") ChtPrefix = []byte("chtRootV2-") // ChtPrefix + chtNum (uint64 big endian) -> trie root hash ChtTablePrefix = []byte("cht-") @@ -145,11 +145,11 @@ var ( FixedCommitteeRootKey = []byte("fixedRoot-") // bigEndian64(syncPeriod) -> committee root hash SyncCommitteeKey = []byte("committee-") // bigEndian64(syncPeriod) -> serialized committee - FilterMapsPrefix = "fm-" - filterMapsRangeKey = []byte(FilterMapsPrefix + "R") - filterMapRowPrefix = []byte(FilterMapsPrefix + "r") // filterMapRowPrefix + mapRowIndex (uint64 big endian) -> filter row - filterMapLastBlockPrefix = []byte(FilterMapsPrefix + "b") // filterMapLastBlockPrefix + mapIndex (uint32 big endian) -> block number (uint64 big endian) - filterMapBlockLVPrefix = []byte(FilterMapsPrefix + "p") // filterMapBlockLVPrefix + num (uint64 big endian) -> log value pointer (uint64 big endian) + filterMapsPrefix = "fm-" + filterMapsRangeKey = []byte(filterMapsPrefix + "R") + filterMapRowPrefix = []byte(filterMapsPrefix + "r") // filterMapRowPrefix + mapRowIndex (uint64 big endian) -> filter row + filterMapLastBlockPrefix = []byte(filterMapsPrefix + "b") // filterMapLastBlockPrefix + mapIndex (uint32 big endian) -> block number (uint64 big endian) + filterMapBlockLVPrefix = []byte(filterMapsPrefix + "p") // filterMapBlockLVPrefix + num (uint64 big endian) -> log value pointer (uint64 big endian) preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil) preimageHitsCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil) @@ -225,16 +225,6 @@ func storageSnapshotsKey(accountHash common.Hash) []byte { return append(SnapshotStoragePrefix, accountHash.Bytes()...) } -// bloomBitsKey = bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -func bloomBitsKey(bit uint, section uint64, hash common.Hash) []byte { - key := append(append(bloomBitsPrefix, make([]byte, 10)...), hash.Bytes()...) - - binary.BigEndian.PutUint16(key[1:], uint16(bit)) - binary.BigEndian.PutUint64(key[3:], section) - - return key -} - // skeletonHeaderKey = skeletonHeaderPrefix + num (uint64 big endian) func skeletonHeaderKey(number uint64) []byte { return append(skeletonHeaderPrefix, encodeBlockNumber(number)...) diff --git a/eth/api_backend.go b/eth/api_backend.go index b4b63556a9c..b08e38afe5d 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/consensus" "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" @@ -403,17 +402,6 @@ func (b *EthAPIBackend) RPCTxFeeCap() float64 { return b.eth.config.RPCTxFeeCap } -func (b *EthAPIBackend) BloomStatus() (uint64, uint64) { - sections, _, _ := b.eth.bloomIndexer.Sections() - return params.BloomBitsBlocks, sections -} - -func (b *EthAPIBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) { - for i := 0; i < bloomFilterThreads; i++ { - go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests) - } -} - func (b *EthAPIBackend) NewMatcherBackend() filtermaps.MatcherBackend { return b.eth.filterMaps.NewMatcherBackend() } diff --git a/eth/backend.go b/eth/backend.go index 99cb3841c37..6deedab8726 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -30,7 +30,6 @@ 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/rawdb" "github.com/ethereum/go-ethereum/core/state/pruner" @@ -85,10 +84,6 @@ type Ethereum struct { engine consensus.Engine accountManager *accounts.Manager - bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests - bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports - closeBloomHandler chan struct{} - filterMaps *filtermaps.FilterMaps closeFilterMaps chan chan struct{} @@ -176,19 +171,16 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { // Assemble the Ethereum object. eth := &Ethereum{ - config: config, - chainDb: chainDb, - eventMux: stack.EventMux(), - accountManager: stack.AccountManager(), - engine: engine, - closeBloomHandler: make(chan struct{}), - networkID: networkID, - gasPrice: config.Miner.GasPrice, - bloomRequests: make(chan chan *bloombits.Retrieval), - bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms), - p2pServer: stack.Server(), - discmix: enode.NewFairMix(0), - shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb), + config: config, + chainDb: chainDb, + eventMux: stack.EventMux(), + accountManager: stack.AccountManager(), + engine: engine, + networkID: networkID, + gasPrice: config.Miner.GasPrice, + p2pServer: stack.Server(), + discmix: enode.NewFairMix(0), + shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb), } bcVersion := rawdb.ReadDatabaseVersion(chainDb) var dbVer = "" @@ -247,7 +239,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err != nil { 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) @@ -379,7 +370,6 @@ func (s *Ethereum) Downloader() *downloader.Downloader { return s.handler.downlo func (s *Ethereum) Synced() bool { return s.handler.synced.Load() } func (s *Ethereum) SetSynced() { s.handler.enableSyncedFeatures() } func (s *Ethereum) ArchiveMode() bool { return s.config.NoPruning } -func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer } // Protocols returns all the currently configured // network protocols to start. @@ -398,9 +388,6 @@ func (s *Ethereum) Start() error { return err } - // Start the bloom bits servicing goroutines - s.startBloomHandlers(params.BloomBitsBlocks) - // Regularly update shutdown marker s.shutdownTracker.Start() @@ -511,8 +498,6 @@ func (s *Ethereum) Stop() error { s.handler.Stop() // Then stop everything else. - s.bloomIndexer.Close() - close(s.closeBloomHandler) ch := make(chan struct{}) s.closeFilterMaps <- ch <-ch diff --git a/eth/bloombits.go b/eth/bloombits.go deleted file mode 100644 index 0cb7050d232..00000000000 --- a/eth/bloombits.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2017 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 eth - -import ( - "time" - - "github.com/ethereum/go-ethereum/common/bitutil" - "github.com/ethereum/go-ethereum/core/rawdb" -) - -const ( - // bloomServiceThreads is the number of goroutines used globally by an Ethereum - // instance to service bloombits lookups for all running filters. - bloomServiceThreads = 16 - - // bloomFilterThreads is the number of goroutines used locally per filter to - // multiplex requests onto the global servicing goroutines. - bloomFilterThreads = 3 - - // bloomRetrievalBatch is the maximum number of bloom bit retrievals to service - // in a single batch. - bloomRetrievalBatch = 16 - - // bloomRetrievalWait is the maximum time to wait for enough bloom bit requests - // to accumulate request an entire batch (avoiding hysteresis). - bloomRetrievalWait = time.Duration(0) -) - -// startBloomHandlers starts a batch of goroutines to accept bloom bit database -// retrievals from possibly a range of filters and serving the data to satisfy. -func (eth *Ethereum) startBloomHandlers(sectionSize uint64) { - for i := 0; i < bloomServiceThreads; i++ { - go func() { - for { - select { - case <-eth.closeBloomHandler: - return - - case request := <-eth.bloomRequests: - task := <-request - task.Bitsets = make([][]byte, len(task.Sections)) - for i, section := range task.Sections { - head := rawdb.ReadCanonicalHash(eth.chainDb, (section+1)*sectionSize-1) - if compVector, err := rawdb.ReadBloomBits(eth.chainDb, task.Bit, section, head); err == nil { - if blob, err := bitutil.DecompressBytes(compVector, int(sectionSize/8)); err == nil { - task.Bitsets[i] = blob - } else { - task.Error = err - } - } else { - task.Error = err - } - } - request <- task - } - } - }() - } -} diff --git a/params/network_params.go b/params/network_params.go index 61bd6b2f422..c016e7fcf32 100644 --- a/params/network_params.go +++ b/params/network_params.go @@ -20,14 +20,6 @@ package params // aren't necessarily consensus related. const ( - // BloomBitsBlocks is the number of blocks a single bloom bit section vector - // contains on the server side. - BloomBitsBlocks uint64 = 4096 - - // BloomConfirms is the number of confirmation blocks before a bloom section is - // considered probably final and its rotated bits are calculated. - BloomConfirms = 256 - // FullImmutabilityThreshold is the number of blocks after which a chain segment is // considered immutable (i.e. soft finality). It is used by the downloader as a // hard limit against deep ancestors, by the blockchain against deep reorgs, by From 18869222646d1706168929515df83c442ebba2b0 Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Fri, 21 Mar 2025 11:29:51 +0100 Subject: [PATCH 028/658] core: respect history cutoff in txindexer (#31393) In #31384 we unindex TXes prior to the merge block. However when the node starts up it will try to re-index those back if the config is to index the whole chain. This change makes the indexer aware of the history cutoff block, avoiding reindexing in that segment. --------- Co-authored-by: Gary Rong Co-authored-by: Felix Lange --- core/rawdb/accessors_chain.go | 8 + core/rawdb/accessors_indexes.go | 48 ++- core/txindexer.go | 151 +++++++-- core/txindexer_test.go | 534 ++++++++++++++++++++++---------- 4 files changed, 541 insertions(+), 200 deletions(-) diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 1a2b31b3606..020d35619e7 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -277,6 +277,14 @@ func WriteTxIndexTail(db ethdb.KeyValueWriter, number uint64) { } } +// DeleteTxIndexTail deletes the number of oldest indexed block +// from database. +func DeleteTxIndexTail(db ethdb.KeyValueWriter) { + if err := db.Delete(txIndexTailKey); err != nil { + log.Crit("Failed to delete the transaction index tail", "err", err) + } +} + // ReadHeaderRange returns the rlp-encoded headers, starting at 'number', and going // backwards towards genesis. This method assumes that the caller already has // placed a cap on count, to prevent DoS issues. diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go index 1d5897cef1a..342aedd8dc3 100644 --- a/core/rawdb/accessors_indexes.go +++ b/core/rawdb/accessors_indexes.go @@ -30,13 +30,8 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -// ReadTxLookupEntry retrieves the positional metadata associated with a transaction -// hash to allow retrieving the transaction or receipt by hash. -func ReadTxLookupEntry(db ethdb.Reader, hash common.Hash) *uint64 { - data, _ := db.Get(txLookupKey(hash)) - if len(data) == 0 { - return nil - } +// DecodeTxLookupEntry decodes the supplied tx lookup data. +func DecodeTxLookupEntry(data []byte, db ethdb.Reader) *uint64 { // Database v6 tx lookup just stores the block number if len(data) < common.HashLength { number := new(big.Int).SetBytes(data).Uint64() @@ -49,12 +44,22 @@ func ReadTxLookupEntry(db ethdb.Reader, hash common.Hash) *uint64 { // Finally try database v3 tx lookup format var entry LegacyTxLookupEntry if err := rlp.DecodeBytes(data, &entry); err != nil { - log.Error("Invalid transaction lookup entry RLP", "hash", hash, "blob", data, "err", err) + log.Error("Invalid transaction lookup entry RLP", "blob", data, "err", err) return nil } return &entry.BlockIndex } +// ReadTxLookupEntry retrieves the positional metadata associated with a transaction +// hash to allow retrieving the transaction or receipt by hash. +func ReadTxLookupEntry(db ethdb.Reader, hash common.Hash) *uint64 { + data, _ := db.Get(txLookupKey(hash)) + if len(data) == 0 { + return nil + } + return DecodeTxLookupEntry(data, db) +} + // writeTxLookupEntry stores a positional metadata for a transaction, // enabling hash based transaction and receipt lookups. func writeTxLookupEntry(db ethdb.KeyValueWriter, hash common.Hash, numberBytes []byte) { @@ -95,6 +100,33 @@ func DeleteTxLookupEntries(db ethdb.KeyValueWriter, hashes []common.Hash) { } } +// DeleteAllTxLookupEntries purges all the transaction indexes in the database. +// If condition is specified, only the entry with condition as True will be +// removed; If condition is not specified, the entry is deleted. +func DeleteAllTxLookupEntries(db ethdb.KeyValueStore, condition func([]byte) bool) { + iter := NewKeyLengthIterator(db.NewIterator(txLookupPrefix, nil), common.HashLength+len(txLookupPrefix)) + defer iter.Release() + + batch := db.NewBatch() + for iter.Next() { + if condition == nil || condition(iter.Value()) { + batch.Delete(iter.Key()) + } + if batch.ValueSize() >= ethdb.IdealBatchSize { + if err := batch.Write(); err != nil { + log.Crit("Failed to delete transaction lookup entries", "err", err) + } + batch.Reset() + } + } + if batch.ValueSize() > 0 { + if err := batch.Write(); err != nil { + log.Crit("Failed to delete transaction lookup entries", "err", err) + } + batch.Reset() + } +} + // ReadTransaction retrieves a specific transaction from the database, along with // its added positional metadata. func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) { diff --git a/core/txindexer.go b/core/txindexer.go index 293124f681a..29e87905d50 100644 --- a/core/txindexer.go +++ b/core/txindexer.go @@ -44,7 +44,11 @@ type txIndexer struct { // * 0: means the entire chain should be indexed // * N: means the latest N blocks [HEAD-N+1, HEAD] should be indexed // and all others shouldn't. - limit uint64 + limit uint64 + + // cutoff denotes the block number before which the chain segment should + // be pruned and not available locally. + cutoff uint64 db ethdb.Database progress chan chan TxIndexProgress term chan chan struct{} @@ -55,6 +59,7 @@ type txIndexer struct { func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer { indexer := &txIndexer{ limit: limit, + cutoff: chain.HistoryPruningCutoff(), db: chain.db, progress: make(chan chan TxIndexProgress), term: make(chan chan struct{}), @@ -64,7 +69,11 @@ func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer { var msg string if limit == 0 { - msg = "entire chain" + if indexer.cutoff == 0 { + msg = "entire chain" + } else { + msg = fmt.Sprintf("blocks since #%d", indexer.cutoff) + } } else { msg = fmt.Sprintf("last %d blocks", limit) } @@ -74,23 +83,31 @@ func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer { } // run executes the scheduled indexing/unindexing task in a separate thread. -// If the stop channel is closed, the task should be terminated as soon as -// possible, the done channel will be closed once the task is finished. -func (indexer *txIndexer) run(tail *uint64, head uint64, stop chan struct{}, done chan struct{}) { +// If the stop channel is closed, the task should terminate as soon as possible. +// The done channel will be closed once the task is complete. +// +// Existing transaction indexes are assumed to be valid, with both the head +// and tail above the configured cutoff. +func (indexer *txIndexer) run(head uint64, stop chan struct{}, done chan struct{}) { defer func() { close(done) }() - // Short circuit if chain is empty and nothing to index. - if head == 0 { + // Short circuit if the chain is either empty, or entirely below the + // cutoff point. + if head == 0 || head < indexer.cutoff { return } // The tail flag is not existent, it means the node is just initialized // and all blocks in the chain (part of them may from ancient store) are // not indexed yet, index the chain according to the configured limit. + tail := rawdb.ReadTxIndexTail(indexer.db) if tail == nil { + // Determine the first block for transaction indexing, taking the + // configured cutoff point into account. from := uint64(0) if indexer.limit != 0 && head >= indexer.limit { from = head - indexer.limit + 1 } + from = max(from, indexer.cutoff) rawdb.IndexTransactions(indexer.db, from, head+1, stop, true) return } @@ -98,25 +115,82 @@ func (indexer *txIndexer) run(tail *uint64, head uint64, stop chan struct{}, don // present), while the whole chain are requested for indexing. if indexer.limit == 0 || head < indexer.limit { if *tail > 0 { - // It can happen when chain is rewound to a historical point which - // is even lower than the indexes tail, recap the indexing target - // to new head to avoid reading non-existent block bodies. - end := *tail - if end > head+1 { - end = head + 1 - } - rawdb.IndexTransactions(indexer.db, 0, end, stop, true) + from := max(uint64(0), indexer.cutoff) + rawdb.IndexTransactions(indexer.db, from, *tail, stop, true) } return } // The tail flag is existent, adjust the index range according to configured // limit and the latest chain head. - if head-indexer.limit+1 < *tail { + from := head - indexer.limit + 1 + from = max(from, indexer.cutoff) + if from < *tail { // Reindex a part of missing indices and rewind index tail to HEAD-limit - rawdb.IndexTransactions(indexer.db, head-indexer.limit+1, *tail, stop, true) + rawdb.IndexTransactions(indexer.db, from, *tail, stop, true) } else { // Unindex a part of stale indices and forward index tail to HEAD-limit - rawdb.UnindexTransactions(indexer.db, *tail, head-indexer.limit+1, stop, false) + rawdb.UnindexTransactions(indexer.db, *tail, from, stop, false) + } +} + +// repair ensures that transaction indexes are in a valid state and invalidates +// them if they are not. The following cases are considered invalid: +// * The index tail is higher than the chain head. +// * The chain head is below the configured cutoff, but the index tail is not empty. +// * The index tail is below the configured cutoff, but it is not empty. +func (indexer *txIndexer) repair(head uint64) { + // If the transactions haven't been indexed yet, nothing to repair + tail := rawdb.ReadTxIndexTail(indexer.db) + if tail == nil { + return + } + // The transaction index tail is higher than the chain head, which may occur + // when the chain is rewound to a historical height below the index tail. + // Purge the transaction indexes from the database. **It's not a common case + // to rewind the chain head below the index tail**. + if *tail > head { + // A crash may occur between the two delete operations, + // potentially leaving dangling indexes in the database. + // However, this is considered acceptable. + rawdb.DeleteTxIndexTail(indexer.db) + rawdb.DeleteAllTxLookupEntries(indexer.db, nil) + log.Warn("Purge transaction indexes", "head", head, "tail", *tail) + return + } + + // If the entire chain is below the configured cutoff point, + // removing the tail of transaction indexing and purges the + // transaction indexes. **It's not a common case, as the cutoff + // is usually defined below the chain head**. + if head < indexer.cutoff { + // A crash may occur between the two delete operations, + // potentially leaving dangling indexes in the database. + // However, this is considered acceptable. + // + // The leftover indexes can't be unindexed by scanning + // the blocks as they are not guaranteed to be available. + // Traversing the database directly within the transaction + // index namespace might be slow and expensive, but we + // have no choice. + rawdb.DeleteTxIndexTail(indexer.db) + rawdb.DeleteAllTxLookupEntries(indexer.db, nil) + log.Warn("Purge transaction indexes", "head", head, "cutoff", indexer.cutoff) + return + } + + // The chain head is above the cutoff while the tail is below the + // cutoff. Shift the tail to the cutoff point and remove the indexes + // below. + if *tail < indexer.cutoff { + // A crash may occur between the two delete operations, + // potentially leaving dangling indexes in the database. + // However, this is considered acceptable. + rawdb.WriteTxIndexTail(indexer.db, indexer.cutoff) + rawdb.DeleteAllTxLookupEntries(indexer.db, func(blob []byte) bool { + n := rawdb.DecodeTxLookupEntry(blob, indexer.db) + return n != nil && *n < indexer.cutoff + }) + log.Warn("Purge transaction indexes below cutoff", "tail", *tail, "cutoff", indexer.cutoff) } } @@ -127,39 +201,39 @@ func (indexer *txIndexer) loop(chain *BlockChain) { // Listening to chain events and manipulate the transaction indexes. var ( - stop chan struct{} // Non-nil if background routine is active. - done chan struct{} // Non-nil if background routine is active. - lastHead uint64 // The latest announced chain head (whose tx indexes are assumed created) - lastTail = rawdb.ReadTxIndexTail(indexer.db) // The oldest indexed block, nil means nothing indexed + stop chan struct{} // Non-nil if background routine is active + done chan struct{} // Non-nil if background routine is active + head = rawdb.ReadHeadBlock(indexer.db).NumberU64() // The latest announced chain head headCh = make(chan ChainHeadEvent) sub = chain.SubscribeChainHeadEvent(headCh) ) defer sub.Unsubscribe() + // Validate the transaction indexes and repair if necessary + indexer.repair(head) + // Launch the initial processing if chain is not empty (head != genesis). // This step is useful in these scenarios that chain has no progress. - if head := rawdb.ReadHeadBlock(indexer.db); head != nil && head.Number().Uint64() != 0 { + if head != 0 { stop = make(chan struct{}) done = make(chan struct{}) - lastHead = head.Number().Uint64() - go indexer.run(rawdb.ReadTxIndexTail(indexer.db), head.NumberU64(), stop, done) + go indexer.run(head, stop, done) } for { select { - case head := <-headCh: + case h := <-headCh: if done == nil { stop = make(chan struct{}) done = make(chan struct{}) - go indexer.run(rawdb.ReadTxIndexTail(indexer.db), head.Header.Number.Uint64(), stop, done) + go indexer.run(h.Header.Number.Uint64(), stop, done) } - lastHead = head.Header.Number.Uint64() + head = h.Header.Number.Uint64() case <-done: stop = nil done = nil - lastTail = rawdb.ReadTxIndexTail(indexer.db) case ch := <-indexer.progress: - ch <- indexer.report(lastHead, lastTail) + ch <- indexer.report(head) case ch := <-indexer.term: if stop != nil { close(stop) @@ -175,12 +249,27 @@ func (indexer *txIndexer) loop(chain *BlockChain) { } // report returns the tx indexing progress. -func (indexer *txIndexer) report(head uint64, tail *uint64) TxIndexProgress { +func (indexer *txIndexer) report(head uint64) TxIndexProgress { + // Special case if the head is even below the cutoff, + // nothing to index. + if head < indexer.cutoff { + return TxIndexProgress{ + Indexed: 0, + Remaining: 0, + } + } + // Compute how many blocks are supposed to be indexed total := indexer.limit if indexer.limit == 0 || total > head { total = head + 1 // genesis included } + length := head - indexer.cutoff + 1 // all available chain for indexing + if total > length { + total = length + } + // Compute how many blocks have been indexed var indexed uint64 + tail := rawdb.ReadTxIndexTail(indexer.db) if tail != nil { indexed = head - *tail + 1 } diff --git a/core/txindexer_test.go b/core/txindexer_test.go index 4425f0d9a5c..7a5688241f0 100644 --- a/core/txindexer_test.go +++ b/core/txindexer_test.go @@ -29,6 +29,46 @@ import ( "github.com/ethereum/go-ethereum/params" ) +func verifyIndexes(t *testing.T, db ethdb.Database, block *types.Block, exist bool) { + for _, tx := range block.Transactions() { + lookup := rawdb.ReadTxLookupEntry(db, tx.Hash()) + if exist && lookup == nil { + t.Fatalf("missing %d %x", block.NumberU64(), tx.Hash().Hex()) + } + if !exist && lookup != nil { + t.Fatalf("unexpected %d %x", block.NumberU64(), tx.Hash().Hex()) + } + } +} + +func verify(t *testing.T, db ethdb.Database, blocks []*types.Block, expTail uint64) { + tail := rawdb.ReadTxIndexTail(db) + if tail == nil { + t.Fatal("Failed to write tx index tail") + return + } + if *tail != expTail { + t.Fatalf("Unexpected tx index tail, want %v, got %d", expTail, *tail) + } + for _, b := range blocks { + if b.Number().Uint64() < *tail { + verifyIndexes(t, db, b, false) + } else { + verifyIndexes(t, db, b, true) + } + } +} + +func verifyNoIndex(t *testing.T, db ethdb.Database, blocks []*types.Block) { + tail := rawdb.ReadTxIndexTail(db) + if tail != nil { + t.Fatalf("Unexpected tx index tail %d", *tail) + } + for _, b := range blocks { + verifyIndexes(t, db, b, false) + } +} + // TestTxIndexer tests the functionalities for managing transaction indexes. func TestTxIndexer(t *testing.T) { var ( @@ -50,163 +90,149 @@ func TestTxIndexer(t *testing.T) { gen.AddTx(tx) nonce += 1 }) + var cases = []struct { + limits []uint64 + tails []uint64 + }{ + { + limits: []uint64{0, 1, 64, 129, 0}, + tails: []uint64{0, 128, 65, 0, 0}, + }, + { + limits: []uint64{64, 1, 64, 0}, + tails: []uint64{65, 128, 65, 0}, + }, + { + limits: []uint64{127, 1, 64, 0}, + tails: []uint64{2, 128, 65, 0}, + }, + { + limits: []uint64{128, 1, 64, 0}, + tails: []uint64{1, 128, 65, 0}, + }, + { + limits: []uint64{129, 1, 64, 0}, + tails: []uint64{0, 128, 65, 0}, + }, + } + for _, c := range cases { + db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...)) - // verifyIndexes checks if the transaction indexes are present or not - // of the specified block. - verifyIndexes := func(db ethdb.Database, number uint64, exist bool) { - if number == 0 { - return + // Index the initial blocks from ancient store + indexer := &txIndexer{ + limit: 0, + db: db, + progress: make(chan chan TxIndexProgress), } - block := blocks[number-1] - for _, tx := range block.Transactions() { - lookup := rawdb.ReadTxLookupEntry(db, tx.Hash()) - if exist && lookup == nil { - t.Fatalf("missing %d %x", number, tx.Hash().Hex()) - } - if !exist && lookup != nil { - t.Fatalf("unexpected %d %x", number, tx.Hash().Hex()) - } + for i, limit := range c.limits { + indexer.limit = limit + indexer.run(chainHead, make(chan struct{}), make(chan struct{})) + verify(t, db, blocks, c.tails[i]) } + db.Close() } - verify := func(db ethdb.Database, expTail uint64, indexer *txIndexer) { - tail := rawdb.ReadTxIndexTail(db) - if tail == nil { - t.Fatal("Failed to write tx index tail") - } - if *tail != expTail { - t.Fatalf("Unexpected tx index tail, want %v, got %d", expTail, *tail) - } - if *tail != 0 { - for number := uint64(0); number < *tail; number += 1 { - verifyIndexes(db, number, false) - } - } - for number := *tail; number <= chainHead; number += 1 { - verifyIndexes(db, number, true) - } - progress := indexer.report(chainHead, tail) - if !progress.Done() { - t.Fatalf("Expect fully indexed") +} + +func TestTxIndexerRepair(t *testing.T) { + var ( + testBankKey, _ = crypto.GenerateKey() + testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) + testBankFunds = big.NewInt(1000000000000000000) + + gspec = &Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, + BaseFee: big.NewInt(params.InitialBaseFee), } + engine = ethash.NewFaker() + nonce = uint64(0) + chainHead = uint64(128) + ) + _, blocks, receipts := GenerateChainWithGenesis(gspec, engine, int(chainHead), func(i int, gen *BlockGen) { + tx, _ := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("0xdeadbeef"), big.NewInt(1000), params.TxGas, big.NewInt(10*params.InitialBaseFee), nil), types.HomesteadSigner{}, testBankKey) + gen.AddTx(tx) + nonce += 1 + }) + tailPointer := func(n uint64) *uint64 { + return &n } - var cases = []struct { - limitA uint64 - tailA uint64 - limitB uint64 - tailB uint64 - limitC uint64 - tailC uint64 + limit uint64 + head uint64 + cutoff uint64 + expTail *uint64 }{ + // if *tail > head => purge indexes + { + limit: 0, + head: chainHead / 2, + cutoff: 0, + expTail: tailPointer(0), + }, + { + limit: 1, // tail = 128 + head: chainHead / 2, // newhead = 64 + cutoff: 0, + expTail: nil, + }, + { + limit: 64, // tail = 65 + head: chainHead / 2, // newhead = 64 + cutoff: 0, + expTail: nil, + }, + { + limit: 65, // tail = 64 + head: chainHead / 2, // newhead = 64 + cutoff: 0, + expTail: tailPointer(64), + }, + { + limit: 66, // tail = 63 + head: chainHead / 2, // newhead = 64 + cutoff: 0, + expTail: tailPointer(63), + }, + + // if tail < cutoff => remove indexes below cutoff + { + limit: 0, // tail = 0 + head: chainHead, // head = 128 + cutoff: chainHead, // cutoff = 128 + expTail: tailPointer(chainHead), + }, + { + limit: 1, // tail = 128 + head: chainHead, // head = 128 + cutoff: chainHead, // cutoff = 128 + expTail: tailPointer(128), + }, + { + limit: 2, // tail = 127 + head: chainHead, // head = 128 + cutoff: chainHead, // cutoff = 128 + expTail: tailPointer(chainHead), + }, + { + limit: 2, // tail = 127 + head: chainHead, // head = 128 + cutoff: chainHead / 2, // cutoff = 64 + expTail: tailPointer(127), + }, + + // if head < cutoff => purge indexes { - // LimitA: 0 - // TailA: 0 - // - // all blocks are indexed - limitA: 0, - tailA: 0, - - // LimitB: 1 - // TailB: 128 - // - // block-128 is indexed - limitB: 1, - tailB: 128, - - // LimitB: 64 - // TailB: 65 - // - // block [65, 128] are indexed - limitC: 64, - tailC: 65, - }, - { - // LimitA: 64 - // TailA: 65 - // - // block [65, 128] are indexed - limitA: 64, - tailA: 65, - - // LimitB: 1 - // TailB: 128 - // - // block-128 is indexed - limitB: 1, - tailB: 128, - - // LimitB: 64 - // TailB: 65 - // - // block [65, 128] are indexed - limitC: 64, - tailC: 65, - }, - { - // LimitA: 127 - // TailA: 2 - // - // block [2, 128] are indexed - limitA: 127, - tailA: 2, - - // LimitB: 1 - // TailB: 128 - // - // block-128 is indexed - limitB: 1, - tailB: 128, - - // LimitB: 64 - // TailB: 65 - // - // block [65, 128] are indexed - limitC: 64, - tailC: 65, - }, - { - // LimitA: 128 - // TailA: 1 - // - // block [2, 128] are indexed - limitA: 128, - tailA: 1, - - // LimitB: 1 - // TailB: 128 - // - // block-128 is indexed - limitB: 1, - tailB: 128, - - // LimitB: 64 - // TailB: 65 - // - // block [65, 128] are indexed - limitC: 64, - tailC: 65, - }, - { - // LimitA: 129 - // TailA: 0 - // - // block [0, 128] are indexed - limitA: 129, - tailA: 0, - - // LimitB: 1 - // TailB: 128 - // - // block-128 is indexed - limitB: 1, - tailB: 128, - - // LimitB: 64 - // TailB: 65 - // - // block [65, 128] are indexed - limitC: 64, - tailC: 65, + limit: 0, // tail = 0 + head: chainHead, // head = 128 + cutoff: 2 * chainHead, // cutoff = 256 + expTail: nil, + }, + { + limit: 64, // tail = 65 + head: chainHead, // head = 128 + cutoff: chainHead / 2, // cutoff = 64 + expTail: tailPointer(65), }, } for _, c := range cases { @@ -215,26 +241,212 @@ func TestTxIndexer(t *testing.T) { // Index the initial blocks from ancient store indexer := &txIndexer{ - limit: c.limitA, + limit: c.limit, db: db, progress: make(chan chan TxIndexProgress), } - indexer.run(nil, 128, make(chan struct{}), make(chan struct{})) - verify(db, c.tailA, indexer) + indexer.run(chainHead, make(chan struct{}), make(chan struct{})) + + indexer.cutoff = c.cutoff + indexer.repair(c.head) + + if c.expTail == nil { + verifyNoIndex(t, db, blocks) + } else { + verify(t, db, blocks, *c.expTail) + } + db.Close() + } +} + +func TestTxIndexerReport(t *testing.T) { + var ( + testBankKey, _ = crypto.GenerateKey() + testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) + testBankFunds = big.NewInt(1000000000000000000) + + gspec = &Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, + BaseFee: big.NewInt(params.InitialBaseFee), + } + engine = ethash.NewFaker() + nonce = uint64(0) + chainHead = uint64(128) + ) + _, blocks, receipts := GenerateChainWithGenesis(gspec, engine, int(chainHead), func(i int, gen *BlockGen) { + tx, _ := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("0xdeadbeef"), big.NewInt(1000), params.TxGas, big.NewInt(10*params.InitialBaseFee), nil), types.HomesteadSigner{}, testBankKey) + gen.AddTx(tx) + nonce += 1 + }) + tailPointer := func(n uint64) *uint64 { + return &n + } + var cases = []struct { + head uint64 + limit uint64 + cutoff uint64 + tail *uint64 + expIndexed uint64 + expRemaining uint64 + }{ + // The entire chain is supposed to be indexed + { + // head = 128, limit = 0, cutoff = 0 => all: 129 + head: chainHead, + limit: 0, + cutoff: 0, - indexer.limit = c.limitB - indexer.run(rawdb.ReadTxIndexTail(db), 128, make(chan struct{}), make(chan struct{})) - verify(db, c.tailB, indexer) + // tail = 0 + tail: tailPointer(0), + expIndexed: 129, + expRemaining: 0, + }, + { + // head = 128, limit = 0, cutoff = 0 => all: 129 + head: chainHead, + limit: 0, + cutoff: 0, - indexer.limit = c.limitC - indexer.run(rawdb.ReadTxIndexTail(db), 128, make(chan struct{}), make(chan struct{})) - verify(db, c.tailC, indexer) + // tail = 1 + tail: tailPointer(1), + expIndexed: 128, + expRemaining: 1, + }, + { + // head = 128, limit = 0, cutoff = 0 => all: 129 + head: chainHead, + limit: 0, + cutoff: 0, - // Recover all indexes - indexer.limit = 0 - indexer.run(rawdb.ReadTxIndexTail(db), 128, make(chan struct{}), make(chan struct{})) - verify(db, 0, indexer) + // tail = 128 + tail: tailPointer(chainHead), + expIndexed: 1, + expRemaining: 128, + }, + { + // head = 128, limit = 256, cutoff = 0 => all: 129 + head: chainHead, + limit: 256, + cutoff: 0, + // tail = 0 + tail: tailPointer(0), + expIndexed: 129, + expRemaining: 0, + }, + + // The chain with specific range is supposed to be indexed + { + // head = 128, limit = 64, cutoff = 0 => index: [65, 128] + head: chainHead, + limit: 64, + cutoff: 0, + + // tail = 0, part of them need to be unindexed + tail: tailPointer(0), + expIndexed: 129, + expRemaining: 0, + }, + { + // head = 128, limit = 64, cutoff = 0 => index: [65, 128] + head: chainHead, + limit: 64, + cutoff: 0, + + // tail = 64, one of them needs to be unindexed + tail: tailPointer(64), + expIndexed: 65, + expRemaining: 0, + }, + { + // head = 128, limit = 64, cutoff = 0 => index: [65, 128] + head: chainHead, + limit: 64, + cutoff: 0, + + // tail = 65, all of them have been indexed + tail: tailPointer(65), + expIndexed: 64, + expRemaining: 0, + }, + { + // head = 128, limit = 64, cutoff = 0 => index: [65, 128] + head: chainHead, + limit: 64, + cutoff: 0, + + // tail = 66, one of them has to be indexed + tail: tailPointer(66), + expIndexed: 63, + expRemaining: 1, + }, + + // The chain with configured cutoff, the chain range could be capped + { + // head = 128, limit = 64, cutoff = 66 => index: [66, 128] + head: chainHead, + limit: 64, + cutoff: 66, + + // tail = 0, part of them need to be unindexed + tail: tailPointer(0), + expIndexed: 129, + expRemaining: 0, + }, + { + // head = 128, limit = 64, cutoff = 66 => index: [66, 128] + head: chainHead, + limit: 64, + cutoff: 66, + + // tail = 66, all of them have been indexed + tail: tailPointer(66), + expIndexed: 63, + expRemaining: 0, + }, + { + // head = 128, limit = 64, cutoff = 66 => index: [66, 128] + head: chainHead, + limit: 64, + cutoff: 66, + + // tail = 67, one of them has to be indexed + tail: tailPointer(67), + expIndexed: 62, + expRemaining: 1, + }, + { + // head = 128, limit = 64, cutoff = 256 => index: [66, 128] + head: chainHead, + limit: 0, + cutoff: 256, + tail: nil, + expIndexed: 0, + expRemaining: 0, + }, + } + for _, c := range cases { + db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...)) + + // Index the initial blocks from ancient store + indexer := &txIndexer{ + limit: c.limit, + cutoff: c.cutoff, + db: db, + progress: make(chan chan TxIndexProgress), + } + if c.tail != nil { + rawdb.WriteTxIndexTail(db, *c.tail) + } + p := indexer.report(c.head) + if p.Indexed != c.expIndexed { + t.Fatalf("Unexpected indexed: %d, expected: %d", p.Indexed, c.expIndexed) + } + if p.Remaining != c.expRemaining { + t.Fatalf("Unexpected remaining: %d, expected: %d", p.Remaining, c.expRemaining) + } db.Close() } } From 8fe09df54f55c2803f492a4be3bd16a43fa7b659 Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Fri, 21 Mar 2025 13:12:56 +0100 Subject: [PATCH 029/658] cmd/geth: add prune history command (#31384) This adds a new subcommand 'geth prune-history' that removes the pre-merge history on supported networks. Geth is not fully ready to work in this mode, please do not run this command on your production node. --------- Co-authored-by: Felix Lange --- cmd/geth/chaincmd.go | 61 ++++++++++++++++++++++ cmd/geth/main.go | 1 + core/blockchain.go | 3 +- core/rawdb/accessors_indexes.go | 5 +- core/rawdb/chain_iterator.go | 36 +++++++++++++ core/rawdb/chain_iterator_test.go | 85 +++++++++++++++++++++++-------- core/txindexer.go | 3 +- tests/testdata | 2 +- 8 files changed, 171 insertions(+), 25 deletions(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 95239bd6405..05c8bc4c7c3 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/era" "github.com/ethereum/go-ethereum/log" @@ -189,6 +190,18 @@ It's deprecated, please use "geth db import" instead. This command dumps out the state for a given block (or latest, if none provided). `, } + + pruneCommand = &cli.Command{ + Action: pruneHistory, + Name: "prune-history", + Usage: "Prune blockchain history (block bodies and receipts) up to the merge block", + ArgsUsage: "", + Flags: utils.DatabaseFlags, + Description: ` +The prune-history command removes historical block bodies and receipts from the +blockchain database up to the merge block, while preserving block headers. This +helps reduce storage requirements for nodes that don't need full historical data.`, + } ) // initGenesis will initialise the given JSON format genesis file and writes it as @@ -598,3 +611,51 @@ func hashish(x string) bool { _, err := strconv.Atoi(x) return err != nil } + +func pruneHistory(ctx *cli.Context) error { + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + // Open the chain database + chain, chaindb := utils.MakeChain(ctx, stack, false) + defer chaindb.Close() + defer chain.Stop() + + // Determine the prune point. This will be the first PoS block. + prunePoint, ok := ethconfig.HistoryPrunePoints[chain.Genesis().Hash()] + if !ok || prunePoint == nil { + return errors.New("prune point not found") + } + var ( + mergeBlock = prunePoint.BlockNumber + mergeBlockHash = prunePoint.BlockHash.Hex() + ) + + // Check we're far enough past merge to ensure all data is in freezer + currentHeader := chain.CurrentHeader() + if currentHeader == nil { + return errors.New("current header not found") + } + if currentHeader.Number.Uint64() < mergeBlock+params.FullImmutabilityThreshold { + return fmt.Errorf("chain not far enough past merge block, need %d more blocks", + mergeBlock+params.FullImmutabilityThreshold-currentHeader.Number.Uint64()) + } + + // Double-check the prune block in db has the expected hash. + hash := rawdb.ReadCanonicalHash(chaindb, mergeBlock) + if hash != common.HexToHash(mergeBlockHash) { + return fmt.Errorf("merge block hash mismatch: got %s, want %s", hash.Hex(), mergeBlockHash) + } + + log.Info("Starting history pruning", "head", currentHeader.Number, "tail", mergeBlock, "tailHash", mergeBlockHash) + start := time.Now() + rawdb.PruneTransactionIndex(chaindb, mergeBlock) + if _, err := chaindb.TruncateTail(mergeBlock); err != nil { + return fmt.Errorf("failed to truncate ancient data: %v", err) + } + log.Info("History pruning completed", "tail", mergeBlock, "elapsed", common.PrettyDuration(time.Since(start))) + + // TODO(s1na): what if there is a crash between the two prune operations? + + return nil +} diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 07fbeaca5c1..9c0c0d9dfcc 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -226,6 +226,7 @@ func init() { removedbCommand, dumpCommand, dumpGenesisCommand, + pruneCommand, // See accountcmd.go: accountCommand, walletCommand, diff --git a/core/blockchain.go b/core/blockchain.go index 2bf7fba427f..d80236c9022 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -332,7 +332,8 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc) bc.processor = NewStateProcessor(chainConfig, bc.hc) - bc.genesisBlock = bc.GetBlockByNumber(0) + genesisHeader := bc.GetHeaderByNumber(0) + bc.genesisBlock = types.NewBlockWithHeader(genesisHeader) if bc.genesisBlock == nil { return nil, ErrNoGenesis } diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go index 342aedd8dc3..7bb96b1fa18 100644 --- a/core/rawdb/accessors_indexes.go +++ b/core/rawdb/accessors_indexes.go @@ -103,13 +103,14 @@ func DeleteTxLookupEntries(db ethdb.KeyValueWriter, hashes []common.Hash) { // DeleteAllTxLookupEntries purges all the transaction indexes in the database. // If condition is specified, only the entry with condition as True will be // removed; If condition is not specified, the entry is deleted. -func DeleteAllTxLookupEntries(db ethdb.KeyValueStore, condition func([]byte) bool) { +func DeleteAllTxLookupEntries(db ethdb.KeyValueStore, condition func(common.Hash, []byte) bool) { iter := NewKeyLengthIterator(db.NewIterator(txLookupPrefix, nil), common.HashLength+len(txLookupPrefix)) defer iter.Release() batch := db.NewBatch() for iter.Next() { - if condition == nil || condition(iter.Value()) { + txhash := common.Hash(iter.Key()[1:]) + if condition == nil || condition(txhash, iter.Value()) { batch.Delete(iter.Key()) } if batch.ValueSize() >= ethdb.IdealBatchSize { diff --git a/core/rawdb/chain_iterator.go b/core/rawdb/chain_iterator.go index 759e5913d13..ecbc44e1f1e 100644 --- a/core/rawdb/chain_iterator.go +++ b/core/rawdb/chain_iterator.go @@ -17,6 +17,7 @@ package rawdb import ( + "encoding/binary" "runtime" "sync/atomic" "time" @@ -361,3 +362,38 @@ func UnindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch func unindexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) { unindexTransactions(db, from, to, interrupt, hook, false) } + +// PruneTransactionIndex removes all tx index entries below a certain block number. +func PruneTransactionIndex(db ethdb.Database, pruneBlock uint64) { + tail := ReadTxIndexTail(db) + if tail == nil || *tail > pruneBlock { + return // no index, or index ends above pruneBlock + } + // There are blocks below pruneBlock in the index. Iterate the entire index to remove + // their entries. Note if this fails, the index is messed up, but tail still points to + // the old tail. + var count, removed int + DeleteAllTxLookupEntries(db, func(txhash common.Hash, v []byte) bool { + count++ + if count%10000000 == 0 { + log.Info("Pruning tx index", "count", count, "removed", removed) + } + if len(v) > 8 { + log.Error("Skipping legacy tx index entry", "hash", txhash) + return false + } + bn := decodeNumber(v) + if bn < pruneBlock { + removed++ + return true + } + return false + }) + WriteTxIndexTail(db, pruneBlock) +} + +func decodeNumber(b []byte) uint64 { + var numBuffer [8]byte + copy(numBuffer[8-len(b):], b) + return binary.BigEndian.Uint64(numBuffer[:]) +} diff --git a/core/rawdb/chain_iterator_test.go b/core/rawdb/chain_iterator_test.go index 390424f673f..75bd5a9a942 100644 --- a/core/rawdb/chain_iterator_test.go +++ b/core/rawdb/chain_iterator_test.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" ) func TestChainIterator(t *testing.T) { @@ -102,19 +103,18 @@ func TestChainIterator(t *testing.T) { } } -func TestIndexTransactions(t *testing.T) { - // Construct test chain db - chainDb := NewMemoryDatabase() - - var block *types.Block +func initDatabaseWithTransactions(db ethdb.Database) ([]*types.Block, []*types.Transaction) { + var blocks []*types.Block var txs []*types.Transaction to := common.BytesToAddress([]byte{0x11}) // Write empty genesis block - block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, newTestHasher()) - WriteBlock(chainDb, block) - WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) + block := types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, newTestHasher()) + WriteBlock(db, block) + WriteCanonicalHash(db, block.Hash(), block.NumberU64()) + blocks = append(blocks, block) + // Create transactions. for i := uint64(1); i <= 10; i++ { var tx *types.Transaction if i%2 == 0 { @@ -138,10 +138,21 @@ func TestIndexTransactions(t *testing.T) { }) } txs = append(txs, tx) - block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, &types.Body{Transactions: types.Transactions{tx}}, nil, newTestHasher()) - WriteBlock(chainDb, block) - WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) + block := types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, &types.Body{Transactions: types.Transactions{tx}}, nil, newTestHasher()) + WriteBlock(db, block) + WriteCanonicalHash(db, block.Hash(), block.NumberU64()) + blocks = append(blocks, block) } + + return blocks, txs +} + +func TestIndexTransactions(t *testing.T) { + // Construct test chain db + chainDB := NewMemoryDatabase() + + _, txs := initDatabaseWithTransactions(chainDB) + // verify checks whether the tx indices in the range [from, to) // is expected. verify := func(from, to int, exist bool, tail uint64) { @@ -149,7 +160,7 @@ func TestIndexTransactions(t *testing.T) { if i == 0 { continue } - number := ReadTxLookupEntry(chainDb, txs[i-1].Hash()) + number := ReadTxLookupEntry(chainDB, txs[i-1].Hash()) if exist && number == nil { t.Fatalf("Transaction index %d missing", i) } @@ -157,29 +168,29 @@ func TestIndexTransactions(t *testing.T) { t.Fatalf("Transaction index %d is not deleted", i) } } - number := ReadTxIndexTail(chainDb) + number := ReadTxIndexTail(chainDB) if number == nil || *number != tail { t.Fatalf("Transaction tail mismatch") } } - IndexTransactions(chainDb, 5, 11, nil, false) + IndexTransactions(chainDB, 5, 11, nil, false) verify(5, 11, true, 5) verify(0, 5, false, 5) - IndexTransactions(chainDb, 0, 5, nil, false) + IndexTransactions(chainDB, 0, 5, nil, false) verify(0, 11, true, 0) - UnindexTransactions(chainDb, 0, 5, nil, false) + UnindexTransactions(chainDB, 0, 5, nil, false) verify(5, 11, true, 5) verify(0, 5, false, 5) - UnindexTransactions(chainDb, 5, 11, nil, false) + UnindexTransactions(chainDB, 5, 11, nil, false) verify(0, 11, false, 11) // Testing corner cases signal := make(chan struct{}) var once sync.Once - indexTransactionsForTesting(chainDb, 5, 11, signal, func(n uint64) bool { + indexTransactionsForTesting(chainDB, 5, 11, signal, func(n uint64) bool { if n <= 8 { once.Do(func() { close(signal) @@ -190,11 +201,11 @@ func TestIndexTransactions(t *testing.T) { }) verify(9, 11, true, 9) verify(0, 9, false, 9) - IndexTransactions(chainDb, 0, 9, nil, false) + IndexTransactions(chainDB, 0, 9, nil, false) signal = make(chan struct{}) var once2 sync.Once - unindexTransactionsForTesting(chainDb, 0, 11, signal, func(n uint64) bool { + unindexTransactionsForTesting(chainDB, 0, 11, signal, func(n uint64) bool { if n >= 8 { once2.Do(func() { close(signal) @@ -206,3 +217,37 @@ func TestIndexTransactions(t *testing.T) { verify(8, 11, true, 8) verify(0, 8, false, 8) } + +func TestPruneTransactionIndex(t *testing.T) { + chainDB := NewMemoryDatabase() + blocks, _ := initDatabaseWithTransactions(chainDB) + lastBlock := blocks[len(blocks)-1].NumberU64() + pruneBlock := lastBlock - 3 + + IndexTransactions(chainDB, 0, lastBlock+1, nil, false) + + // Check all transactions are in index. + for _, block := range blocks { + for _, tx := range block.Transactions() { + num := ReadTxLookupEntry(chainDB, tx.Hash()) + if num == nil || *num != block.NumberU64() { + t.Fatalf("wrong TxLookup entry: %x -> %v", tx.Hash(), num) + } + } + } + + PruneTransactionIndex(chainDB, pruneBlock) + + // Check transactions from old blocks not included. + for _, block := range blocks { + for _, tx := range block.Transactions() { + num := ReadTxLookupEntry(chainDB, tx.Hash()) + if block.NumberU64() < pruneBlock && num != nil { + t.Fatalf("TxLookup entry not removed: %x -> %v", tx.Hash(), num) + } + if block.NumberU64() >= pruneBlock && (num == nil || *num != block.NumberU64()) { + t.Fatalf("wrong TxLookup entry after pruning: %x -> %v", tx.Hash(), num) + } + } + } +} diff --git a/core/txindexer.go b/core/txindexer.go index 29e87905d50..d0fce302f3e 100644 --- a/core/txindexer.go +++ b/core/txindexer.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -186,7 +187,7 @@ func (indexer *txIndexer) repair(head uint64) { // potentially leaving dangling indexes in the database. // However, this is considered acceptable. rawdb.WriteTxIndexTail(indexer.db, indexer.cutoff) - rawdb.DeleteAllTxLookupEntries(indexer.db, func(blob []byte) bool { + rawdb.DeleteAllTxLookupEntries(indexer.db, func(txhash common.Hash, blob []byte) bool { n := rawdb.DecodeTxLookupEntry(blob, indexer.db) return n != nil && *n < indexer.cutoff }) diff --git a/tests/testdata b/tests/testdata index 81862e48485..faf33b47146 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit 81862e4848585a438d64f911a19b3825f0f4cd95 +Subproject commit faf33b471465d3c6cdc3d04fbd690895f78d33f2 From 624a5d8b235961a53f3091f86570d71c1cb872c4 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 21 Mar 2025 21:08:51 +0800 Subject: [PATCH 030/658] eth/filter: downgrade log level (#31450) --- eth/filters/filter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/filters/filter.go b/eth/filters/filter.go index cedb5b35190..b743c25994a 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -372,7 +372,7 @@ func (f *Filter) indexedLogs(ctx context.Context, mb filtermaps.MatcherBackend, // iteration and bloom matching. 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) + log.Debug("Performing unindexed log search", "begin", begin, "end", end) var matches []*types.Log for blockNumber := begin; blockNumber <= end; blockNumber++ { select { @@ -390,7 +390,7 @@ func (f *Filter) unindexedLogs(ctx context.Context, begin, end uint64) ([]*types } matches = append(matches, found...) } - log.Trace("Performed unindexed log search", "begin", begin, "end", end, "matches", len(matches), "elapsed", common.PrettyDuration(time.Since(start))) + log.Debug("Performed unindexed log search", "begin", begin, "end", end, "matches", len(matches), "elapsed", common.PrettyDuration(time.Since(start))) return matches, nil } From b0b2b765094ed4abc490ba26bc3be431c84c5b96 Mon Sep 17 00:00:00 2001 From: jwasinger Date: Sun, 23 Mar 2025 17:38:26 +0100 Subject: [PATCH 031/658] internal/ethapi: return code 3 from call/estimateGas even if a revert reason was not returned (#31456) --- internal/ethapi/api.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f3975d35a00..979a7e822ac 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -761,8 +761,7 @@ func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockN if err != nil { return nil, err } - // If the result contains a revert reason, try to unpack and return it. - if len(result.Revert()) > 0 { + if errors.Is(result.Err, vm.ErrExecutionReverted) { return nil, newRevertError(result.Revert()) } return result.Return(), result.Err @@ -842,7 +841,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr // Run the gas estimation and wrap any revertals into a custom return estimate, revert, err := gasestimator.Estimate(ctx, call, opts, gasCap) if err != nil { - if len(revert) > 0 { + if errors.Is(err, vm.ErrExecutionReverted) { return 0, newRevertError(revert) } return 0, err From fd4049dc1e9d8e80368c9e1ae1da5c2fd81644c5 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 24 Mar 2025 10:07:38 +0100 Subject: [PATCH 032/658] core/rawdb: improve database stats output (#31463) Instead of reporting all filtermaps stuff in one line, I'm breaking it down into the three separate kinds of entries here. ``` +-----------------------+-----------------------------+------------+------------+ | DATABASE | CATEGORY | SIZE | ITEMS | +-----------------------+-----------------------------+------------+------------+ | Key-Value store | Log index filter-map rows | 59.21 GiB | 616077345 | | Key-Value store | Log index last-block-of-map | 12.35 MiB | 269755 | | Key-Value store | Log index block-lv | 421.70 MiB | 22109169 | ``` Also added some other changes to make it easier to debug: - restored bloombits into the inspect output, so we notice if it doesn't get deleted for some reason - tracking of unaccounted key examples --- core/rawdb/accessors_indexes.go | 2 +- core/rawdb/database.go | 123 +++++++++++++++++++++----------- core/rawdb/schema.go | 23 +++--- 3 files changed, 94 insertions(+), 54 deletions(-) diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go index 7bb96b1fa18..297e339c83e 100644 --- a/core/rawdb/accessors_indexes.go +++ b/core/rawdb/accessors_indexes.go @@ -502,5 +502,5 @@ func DeleteBloomBitsDb(db ethdb.KeyValueRangeDeleter) error { if err := deletePrefixRange(db, bloomBitsPrefix); err != nil { return err } - return deletePrefixRange(db, bloomBitsIndexPrefix) + return deletePrefixRange(db, bloomBitsMetaPrefix) } diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 4c87e66cfd6..a394f46e0ac 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -20,8 +20,10 @@ import ( "bytes" "errors" "fmt" + "maps" "os" "path/filepath" + "slices" "strings" "time" @@ -360,24 +362,27 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { logged = time.Now() // Key-value store statistics - headers stat - bodies stat - receipts stat - tds stat - numHashPairings stat - hashNumPairings stat - legacyTries stat - stateLookups stat - accountTries stat - storageTries stat - codes stat - txLookups stat - accountSnaps stat - storageSnaps stat - preimages stat - filterMaps stat - beaconHeaders stat - cliqueSnaps stat + headers stat + bodies stat + receipts stat + tds stat + numHashPairings stat + hashNumPairings stat + legacyTries stat + stateLookups stat + accountTries stat + storageTries stat + codes stat + txLookups stat + accountSnaps stat + storageSnaps stat + preimages stat + beaconHeaders stat + cliqueSnaps stat + bloomBits stat + filterMapRows stat + filterMapLastBlock stat + filterMapBlockLV stat // Verkle statistics verkleTries stat @@ -393,6 +398,11 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { // Totals total common.StorageSize + + // This map tracks example keys for unaccounted data. + // For each unique two-byte prefix, the first unaccounted key encountered + // by the iterator will be stored. + unaccountedKeys = make(map[[2]byte][]byte) ) // Inspect key-value database first. for it.Next() { @@ -436,19 +446,33 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { metadata.Add(size) case bytes.HasPrefix(key, genesisPrefix) && len(key) == (len(genesisPrefix)+common.HashLength): metadata.Add(size) - case bytes.HasPrefix(key, []byte(filterMapsPrefix)): - filterMaps.Add(size) case bytes.HasPrefix(key, skeletonHeaderPrefix) && len(key) == (len(skeletonHeaderPrefix)+8): beaconHeaders.Add(size) case bytes.HasPrefix(key, CliqueSnapshotPrefix) && len(key) == 7+common.HashLength: cliqueSnaps.Add(size) - case bytes.HasPrefix(key, ChtTablePrefix) || - bytes.HasPrefix(key, ChtIndexTablePrefix) || - bytes.HasPrefix(key, ChtPrefix): // Canonical hash trie + + // new log index + case bytes.HasPrefix(key, filterMapRowPrefix) && len(key) <= len(filterMapRowPrefix)+9: + filterMapRows.Add(size) + case bytes.HasPrefix(key, filterMapLastBlockPrefix) && len(key) == len(filterMapLastBlockPrefix)+4: + filterMapLastBlock.Add(size) + case bytes.HasPrefix(key, filterMapBlockLVPrefix) && len(key) == len(filterMapBlockLVPrefix)+8: + filterMapBlockLV.Add(size) + + // old log index (deprecated) + case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength): + bloomBits.Add(size) + case bytes.HasPrefix(key, bloomBitsMetaPrefix) && len(key) < len(bloomBitsMetaPrefix)+8: + bloomBits.Add(size) + + // LES indexes (deprecated) + case bytes.HasPrefix(key, chtTablePrefix) || + bytes.HasPrefix(key, chtIndexTablePrefix) || + bytes.HasPrefix(key, chtPrefix): // Canonical hash trie chtTrieNodes.Add(size) - case bytes.HasPrefix(key, BloomTrieTablePrefix) || - bytes.HasPrefix(key, BloomTrieIndexPrefix) || - bytes.HasPrefix(key, BloomTriePrefix): // Bloomtrie sub + case bytes.HasPrefix(key, bloomTrieTablePrefix) || + bytes.HasPrefix(key, bloomTrieIndexPrefix) || + bytes.HasPrefix(key, bloomTriePrefix): // Bloomtrie sub bloomTrieNodes.Add(size) // Verkle trie data is detected, determine the sub-category @@ -468,24 +492,19 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { default: unaccounted.Add(size) } + + // Metadata keys + case slices.ContainsFunc(knownMetadataKeys, func(x []byte) bool { return bytes.Equal(x, key) }): + metadata.Add(size) + default: - var accounted bool - for _, meta := range [][]byte{ - databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, headFinalizedBlockKey, - lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey, - snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey, - uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey, - persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey, snapSyncStatusFlagKey, - } { - if bytes.Equal(key, meta) { - metadata.Add(size) - accounted = true - break + unaccounted.Add(size) + if len(key) >= 2 { + prefix := [2]byte(key[:2]) + if _, ok := unaccountedKeys[prefix]; !ok { + unaccountedKeys[prefix] = bytes.Clone(key) } } - if !accounted { - unaccounted.Add(size) - } } count++ if count%1000 == 0 && time.Since(logged) > 8*time.Second { @@ -502,7 +521,10 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { {"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()}, {"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()}, {"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()}, - {"Key-Value store", "Log search index", filterMaps.Size(), filterMaps.Count()}, + {"Key-Value store", "Log index filter-map rows", filterMapRows.Size(), filterMapRows.Count()}, + {"Key-Value store", "Log index last-block-of-map", filterMapLastBlock.Size(), filterMapLastBlock.Count()}, + {"Key-Value store", "Log index block-lv", filterMapBlockLV.Size(), filterMapBlockLV.Count()}, + {"Key-Value store", "Log bloombits (deprecated)", bloomBits.Size(), bloomBits.Count()}, {"Key-Value store", "Contract codes", codes.Size(), codes.Count()}, {"Key-Value store", "Hash trie nodes", legacyTries.Size(), legacyTries.Count()}, {"Key-Value store", "Path trie state lookups", stateLookups.Size(), stateLookups.Count()}, @@ -543,10 +565,23 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { if unaccounted.size > 0 { log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count) + for _, e := range slices.SortedFunc(maps.Values(unaccountedKeys), bytes.Compare) { + log.Error(fmt.Sprintf(" example key: %x", e)) + } } return nil } +// This is the list of known 'metadata' keys stored in the databasse. +var knownMetadataKeys = [][]byte{ + databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, headFinalizedBlockKey, + lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey, + snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey, + uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey, + persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey, snapSyncStatusFlagKey, + filterMapsRangeKey, +} + // printChainMetadata prints out chain metadata to stderr. func printChainMetadata(db ethdb.KeyValueStore) { fmt.Fprintf(os.Stderr, "Chain metadata\n") @@ -566,6 +601,7 @@ func ReadChainMetadata(db ethdb.KeyValueStore) [][]string { } return fmt.Sprintf("%d (%#x)", *val, *val) } + data := [][]string{ {"databaseVersion", pp(ReadDatabaseVersion(db))}, {"headBlockHash", fmt.Sprintf("%v", ReadHeadBlockHash(db))}, @@ -582,5 +618,8 @@ func ReadChainMetadata(db ethdb.KeyValueStore) [][]string { if b := ReadSkeletonSyncStatus(db); b != nil { data = append(data, []string{"SkeletonSyncStatus", string(b)}) } + if fmr, ok, _ := ReadFilterMapsRange(db); ok { + data = append(data, []string{"filterMapsRange", fmt.Sprintf("%+v", fmr)}) + } return data } diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index c21a96bd24f..31e80da0793 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -128,29 +128,30 @@ var ( configPrefix = []byte("ethereum-config-") // config prefix for the db genesisPrefix = []byte("ethereum-genesis-") // genesis state prefix for the db - // bloomBitsIndexPrefix is the data table of a chain indexer to track its progress - bloomBitsIndexPrefix = []byte("iB") - - ChtPrefix = []byte("chtRootV2-") // ChtPrefix + chtNum (uint64 big endian) -> trie root hash - ChtTablePrefix = []byte("cht-") - ChtIndexTablePrefix = []byte("chtIndexV2-") - - BloomTriePrefix = []byte("bltRoot-") // BloomTriePrefix + bloomTrieNum (uint64 big endian) -> trie root hash - BloomTrieTablePrefix = []byte("blt-") - BloomTrieIndexPrefix = []byte("bltIndex-") - CliqueSnapshotPrefix = []byte("clique-") BestUpdateKey = []byte("update-") // bigEndian64(syncPeriod) -> RLP(types.LightClientUpdate) (nextCommittee only referenced by root hash) FixedCommitteeRootKey = []byte("fixedRoot-") // bigEndian64(syncPeriod) -> committee root hash SyncCommitteeKey = []byte("committee-") // bigEndian64(syncPeriod) -> serialized committee + // new log index filterMapsPrefix = "fm-" filterMapsRangeKey = []byte(filterMapsPrefix + "R") filterMapRowPrefix = []byte(filterMapsPrefix + "r") // filterMapRowPrefix + mapRowIndex (uint64 big endian) -> filter row filterMapLastBlockPrefix = []byte(filterMapsPrefix + "b") // filterMapLastBlockPrefix + mapIndex (uint32 big endian) -> block number (uint64 big endian) filterMapBlockLVPrefix = []byte(filterMapsPrefix + "p") // filterMapBlockLVPrefix + num (uint64 big endian) -> log value pointer (uint64 big endian) + // old log index + bloomBitsMetaPrefix = []byte("iB") + + // LES indexes + chtPrefix = []byte("chtRootV2-") // ChtPrefix + chtNum (uint64 big endian) -> trie root hash + chtTablePrefix = []byte("cht-") + chtIndexTablePrefix = []byte("chtIndexV2-") + bloomTriePrefix = []byte("bltRoot-") // BloomTriePrefix + bloomTrieNum (uint64 big endian) -> trie root hash + bloomTrieTablePrefix = []byte("blt-") + bloomTrieIndexPrefix = []byte("bltIndex-") + preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil) preimageHitsCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil) preimageMissCounter = metrics.NewRegisteredCounter("db/preimage/miss", nil) From cbe902d5dadd5fb356f54f85dee7bdf556fa608e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Mon, 24 Mar 2025 12:27:11 +0100 Subject: [PATCH 033/658] core/filtermaps: fix log indexer init conditions (#31455) This PR adds an extra condition to the log indexer initialization in order to avoid initializing with block 0 as target head. Previously this caused the indexer to initialize without a checkpoint. Later, when the real chain head was set, it indexed the entire history, then unindexed most of it if only the recent history was supposed to be indexed. Now the init only happens when there is an actual synced chain head and therefore the index is initialized at the most recent checkpoint and only the last year is indexed according to the default parameters. During checkpoint initialization the best available checkpoint is also checked against the history cutoff point and fails if the indexing would have to start from a block older than the cutoff. If initialization fails then the indexer reverts to unindexed mode instead of retrying because the the failure conditions cannot be expected to recover later. --- core/filtermaps/filtermaps.go | 29 +++++++++++++++++++++++++---- core/filtermaps/indexer.go | 15 ++++++++++----- core/filtermaps/matcher_backend.go | 8 ++++---- eth/backend.go | 7 ++++++- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index d74b11da044..db7ab0a4260 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -53,7 +53,8 @@ type FilterMaps struct { // This is configured by the --history.logs.disable Geth flag. // We chose to implement disabling this way because it requires less special // case logic in eth/filters. - disabled bool + disabled bool + disabledCh chan struct{} // closed by indexer if disabled closeCh chan struct{} closeWg sync.WaitGroup @@ -196,6 +197,7 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f blockProcessingCh: make(chan bool, 1), history: config.History, disabled: config.Disabled, + disabledCh: make(chan struct{}), exportFileName: config.ExportFileName, Params: params, indexedRange: filterMapsRange{ @@ -206,6 +208,8 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f maps: common.NewRange(rs.MapsFirst, rs.MapsAfterLast-rs.MapsFirst), tailPartialEpoch: rs.TailPartialEpoch, }, + historyCutoff: historyCutoff, + finalBlock: finalBlock, matcherSyncCh: make(chan *FilterMapsMatcherBackend), matchers: make(map[*FilterMapsMatcherBackend]struct{}), filterMapCache: lru.NewCache[uint32, filterMap](cachedFilterMaps), @@ -278,8 +282,13 @@ func (f *FilterMaps) initChainView(chainView *ChainView) *ChainView { } // reset un-initializes the FilterMaps structure and removes all related data from -// the database. The function returns true if everything was successfully removed. -func (f *FilterMaps) reset() bool { +// the database. +// Note that in case of leveldb database the fallback implementation of DeleteRange +// might take a long time to finish and deleting the entire database may be +// interrupted by a shutdown. Deleting the filterMapsRange entry first does +// guarantee though that the next init() will not return successfully until the +// entire database has been cleaned. +func (f *FilterMaps) reset() { f.indexLock.Lock() f.indexedRange = filterMapsRange{} f.indexedView = nil @@ -292,11 +301,16 @@ func (f *FilterMaps) reset() bool { // deleting the range first ensures that resetDb will be called again at next // startup and any leftover data will be removed even if it cannot finish now. rawdb.DeleteFilterMapsRange(f.db) - return f.safeDeleteRange(rawdb.DeleteFilterMapsDb, "Resetting log index database") + f.safeDeleteRange(rawdb.DeleteFilterMapsDb, "Resetting log index database") } // init initializes an empty log index according to the current targetView. func (f *FilterMaps) init() error { + // ensure that there is no remaining data in the filter maps key range + if !f.safeDeleteRange(rawdb.DeleteFilterMapsDb, "Resetting log index database") { + return errors.New("could not reset log index database") + } + f.indexLock.Lock() defer f.indexLock.Unlock() @@ -317,6 +331,13 @@ func (f *FilterMaps) init() error { bestIdx, bestLen = idx, max } } + var initBlockNumber uint64 + if bestLen > 0 { + initBlockNumber = checkpoints[bestIdx][bestLen-1].BlockNumber + } + if initBlockNumber < f.historyCutoff { + return errors.New("cannot start indexing before history cutoff point") + } batch := f.db.NewBatch() for epoch := range bestLen { cp := checkpoints[bestIdx][epoch] diff --git a/core/filtermaps/indexer.go b/core/filtermaps/indexer.go index 69f42d8b606..026b3b4f38a 100644 --- a/core/filtermaps/indexer.go +++ b/core/filtermaps/indexer.go @@ -36,20 +36,25 @@ func (f *FilterMaps) indexerLoop() { if f.disabled { f.reset() + close(f.disabledCh) return } log.Info("Started log indexer") for !f.stop { if !f.indexedRange.initialized { - if err := f.init(); err != nil { - log.Error("Error initializing log index", "error", err) - // unexpected error; there is not a lot we can do here, maybe it - // recovers, maybe not. Calling event processing here ensures - // that we can still properly shutdown in case of an infinite loop. + if f.targetView.headNumber == 0 { + // initialize when chain head is available f.processSingleEvent(true) continue } + if err := f.init(); err != nil { + log.Error("Error initializing log index; reverting to unindexed mode", "error", err) + f.reset() + f.disabled = true + close(f.disabledCh) + return + } } if !f.targetHeadIndexed() { if !f.tryIndexHead() { diff --git a/core/filtermaps/matcher_backend.go b/core/filtermaps/matcher_backend.go index f24e9706cbf..01bae7bb22d 100644 --- a/core/filtermaps/matcher_backend.go +++ b/core/filtermaps/matcher_backend.go @@ -141,10 +141,6 @@ func (fm *FilterMapsMatcherBackend) synced() { // range that has not been changed and has been consistent with all states of the // chain since the previous SyncLogIndex or the creation of the matcher backend. func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange, error) { - if fm.f.disabled { - return SyncRange{HeadNumber: fm.f.targetView.headNumber}, nil - } - syncCh := make(chan SyncRange, 1) fm.f.matchersLock.Lock() fm.syncCh = syncCh @@ -154,12 +150,16 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange case fm.f.matcherSyncCh <- fm: case <-ctx.Done(): return SyncRange{}, ctx.Err() + case <-fm.f.disabledCh: + return SyncRange{HeadNumber: fm.f.targetView.headNumber}, nil } select { case vr := <-syncCh: return vr, nil case <-ctx.Done(): return SyncRange{}, ctx.Err() + case <-fm.f.disabledCh: + return SyncRange{HeadNumber: fm.f.targetView.headNumber}, nil } } diff --git a/eth/backend.go b/eth/backend.go index 6deedab8726..909d153a2b5 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -241,7 +241,12 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { } 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) + historyCutoff := eth.blockchain.HistoryPruningCutoff() + var finalBlock uint64 + if fb := eth.blockchain.CurrentFinalBlock(); fb != nil { + finalBlock = fb.Number.Uint64() + } + eth.filterMaps = filtermaps.NewFilterMaps(chainDb, chainView, historyCutoff, finalBlock, filtermaps.DefaultParams, fmConfig) eth.closeFilterMaps = make(chan chan struct{}) if config.BlobPool.Datadir != "" { From 28238b6b7e004d838762bf96aa4c5be1271e5954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Mon, 24 Mar 2025 12:27:40 +0100 Subject: [PATCH 034/658] beacon/params: new checkpoints (#31470) This PR updates beacon checkpoints. The checkpoints are now stored as embedded hex files, in the same format that https://github.com/ethereum/go-ethereum/pull/31469 uses. --- beacon/params/checkpoint_holesky.hex | 1 + beacon/params/checkpoint_mainnet.hex | 1 + beacon/params/checkpoint_sepolia.hex | 1 + beacon/params/networks.go | 17 ++++++++++++++--- 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 beacon/params/checkpoint_holesky.hex create mode 100644 beacon/params/checkpoint_mainnet.hex create mode 100644 beacon/params/checkpoint_sepolia.hex diff --git a/beacon/params/checkpoint_holesky.hex b/beacon/params/checkpoint_holesky.hex new file mode 100644 index 00000000000..f1e27b48f65 --- /dev/null +++ b/beacon/params/checkpoint_holesky.hex @@ -0,0 +1 @@ +0xf5606a019f0f1006e9ec2070695045f4334450362a48da4c5965314510e0b4c3 \ No newline at end of file diff --git a/beacon/params/checkpoint_mainnet.hex b/beacon/params/checkpoint_mainnet.hex new file mode 100644 index 00000000000..a11c3394bb5 --- /dev/null +++ b/beacon/params/checkpoint_mainnet.hex @@ -0,0 +1 @@ +0x3c0cb4aa83beded1803d262664ba4392b1023f334d9cca02dc3a6925987ebe91 \ No newline at end of file diff --git a/beacon/params/checkpoint_sepolia.hex b/beacon/params/checkpoint_sepolia.hex new file mode 100644 index 00000000000..e4f2f96733f --- /dev/null +++ b/beacon/params/checkpoint_sepolia.hex @@ -0,0 +1 @@ +0xa8d56457aa414523d93659aef1f7409bbfb72ad75e94d917c8c0b1baa38153ef \ No newline at end of file diff --git a/beacon/params/networks.go b/beacon/params/networks.go index c5479869776..7e73a2c69c1 100644 --- a/beacon/params/networks.go +++ b/beacon/params/networks.go @@ -17,14 +17,25 @@ package params import ( + _ "embed" + "github.com/ethereum/go-ethereum/common" ) +//go:embed checkpoint_mainnet.hex +var checkpointMainnet string + +//go:embed checkpoint_sepolia.hex +var checkpointSepolia string + +//go:embed checkpoint_holesky.hex +var checkpointHolesky string + var ( MainnetLightConfig = (&ChainConfig{ GenesisValidatorsRoot: common.HexToHash("0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"), GenesisTime: 1606824023, - Checkpoint: common.HexToHash("0x6509b691f4de4f7b083f2784938fd52f0e131675432b3fd85ea549af9aebd3d0"), + Checkpoint: common.HexToHash(checkpointMainnet), }). AddFork("GENESIS", 0, []byte{0, 0, 0, 0}). AddFork("ALTAIR", 74240, []byte{1, 0, 0, 0}). @@ -35,7 +46,7 @@ var ( SepoliaLightConfig = (&ChainConfig{ GenesisValidatorsRoot: common.HexToHash("0xd8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078"), GenesisTime: 1655733600, - Checkpoint: common.HexToHash("0x456e85f5608afab3465a0580bff8572255f6d97af0c5f939e3f7536b5edb2d3f"), + Checkpoint: common.HexToHash(checkpointSepolia), }). AddFork("GENESIS", 0, []byte{144, 0, 0, 105}). AddFork("ALTAIR", 50, []byte{144, 0, 0, 112}). @@ -47,7 +58,7 @@ var ( HoleskyLightConfig = (&ChainConfig{ GenesisValidatorsRoot: common.HexToHash("0x9143aa7c615a7f7115e2b6aac319c03529df8242ae705fba9df39b79c59fa8b1"), GenesisTime: 1695902400, - Checkpoint: common.HexToHash("0x6456a1317f54d4b4f2cb5bc9d153b5af0988fe767ef0609f0236cf29030bcff7"), + Checkpoint: common.HexToHash(checkpointHolesky), }). AddFork("GENESIS", 0, []byte{1, 1, 112, 0}). AddFork("ALTAIR", 0, []byte{2, 1, 112, 0}). From 8e3b94da1e9437dc48ef945011f6ad21c03f3c51 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 24 Mar 2025 22:19:14 +0800 Subject: [PATCH 035/658] tests: update test submodule (#31479) This commit upgrades the test submodule to latest version: Latest: https://github.com/ethereum/tests/commit/81862e4848585a438d64f911a19b3825f0f4cd95 Old: https://github.com/ethereum/tests/commit/faf33b471465d3c6cdc3d04fbd690895f78d33f2 --- tests/testdata | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testdata b/tests/testdata index faf33b47146..81862e48485 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit faf33b471465d3c6cdc3d04fbd690895f78d33f2 +Subproject commit 81862e4848585a438d64f911a19b3825f0f4cd95 From 71e9c9b8a732b33f50b4565627463fb252b4aeff Mon Sep 17 00:00:00 2001 From: Rez Date: Tue, 25 Mar 2025 05:08:53 +1100 Subject: [PATCH 036/658] internal/ethapi: support for beacon root and withdrawals in simulate api (#31304) Adds block override fields for beacon block root and withdrawals to the eth_simulateV1. Addresses https://github.com/ethereum/go-ethereum/issues/31264 --- eth/gasestimator/gasestimator.go | 4 +- eth/tracers/api.go | 4 +- internal/ethapi/api.go | 4 +- internal/ethapi/api_test.go | 18 +++++++ internal/ethapi/override/override.go | 14 ++++- internal/ethapi/simulate.go | 79 +++++++++++++++++++++++----- 6 files changed, 106 insertions(+), 17 deletions(-) diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index a6c4718cf4a..fc8e3a2e424 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -223,7 +223,9 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio dirtyState = opts.State.Copy() ) if opts.BlockOverrides != nil { - opts.BlockOverrides.Apply(&evmContext) + if err := opts.BlockOverrides.Apply(&evmContext); err != nil { + return nil, err + } } // Lower the basefee to 0 to avoid breaking EVM // invariants (basefee < feecap). diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 627dd487fc3..3cb86c80e26 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -950,7 +950,9 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) // Apply the customization rules if required. if config != nil { - config.BlockOverrides.Apply(&vmctx) + if overrideErr := config.BlockOverrides.Apply(&vmctx); overrideErr != nil { + return nil, overrideErr + } rules := api.backend.ChainConfig().Rules(vmctx.BlockNumber, vmctx.Random != nil, vmctx.Time) precompiles = vm.ActivePrecompiledContracts(rules) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 979a7e822ac..e0d5b32622e 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -660,7 +660,9 @@ func (context *ChainContext) Config() *params.ChainConfig { func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil) if blockOverrides != nil { - blockOverrides.Apply(&blockCtx) + if err := blockOverrides.Apply(&blockCtx); err != nil { + return nil, err + } } rules := b.ChainConfig().Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time) precompiles := vm.ActivePrecompiledContracts(rules) diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 88ff7b8af35..b086d1a6d51 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -1134,6 +1134,24 @@ func TestCall(t *testing.T) { }, want: "0x0000000000000000000000000000000000000000000000000000000000000000", }, + { + name: "unsupported block override beaconRoot", + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{}, + blockOverrides: override.BlockOverrides{ + BeaconRoot: &common.Hash{0, 1, 2}, + }, + expectErr: errors.New(`block override "beaconRoot" is not supported for this RPC method`), + }, + { + name: "unsupported block override withdrawals", + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{}, + blockOverrides: override.BlockOverrides{ + Withdrawals: &types.Withdrawals{}, + }, + expectErr: errors.New(`block override "withdrawals" is not supported for this RPC method`), + }, } for _, tc := range testSuite { result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides) diff --git a/internal/ethapi/override/override.go b/internal/ethapi/override/override.go index f6a8a94ffda..0bcf3c444d9 100644 --- a/internal/ethapi/override/override.go +++ b/internal/ethapi/override/override.go @@ -17,6 +17,7 @@ package override import ( + "errors" "fmt" "math/big" @@ -128,12 +129,20 @@ type BlockOverrides struct { PrevRandao *common.Hash BaseFeePerGas *hexutil.Big BlobBaseFee *hexutil.Big + BeaconRoot *common.Hash + Withdrawals *types.Withdrawals } // Apply overrides the given header fields into the given block context. -func (o *BlockOverrides) Apply(blockCtx *vm.BlockContext) { +func (o *BlockOverrides) Apply(blockCtx *vm.BlockContext) error { if o == nil { - return + return nil + } + if o.BeaconRoot != nil { + return errors.New(`block override "beaconRoot" is not supported for this RPC method`) + } + if o.Withdrawals != nil { + return errors.New(`block override "withdrawals" is not supported for this RPC method`) } if o.Number != nil { blockCtx.BlockNumber = o.Number.ToInt() @@ -159,6 +168,7 @@ func (o *BlockOverrides) Apply(blockCtx *vm.BlockContext) { if o.BlobBaseFee != nil { blockCtx.BlobBaseFee = o.BlobBaseFee.ToInt() } + return nil } // MakeHeader returns a new header object with the overridden diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 097de8b0b01..ba346b132f9 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -36,7 +36,6 @@ import ( "github.com/ethereum/go-ethereum/internal/ethapi/override" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/trie" ) const ( @@ -95,6 +94,47 @@ type simOpts struct { ReturnFullTransactions bool } +// simChainHeadReader implements ChainHeaderReader which is needed as input for FinalizeAndAssemble. +type simChainHeadReader struct { + context.Context + Backend +} + +func (m *simChainHeadReader) Config() *params.ChainConfig { + return m.Backend.ChainConfig() +} + +func (m *simChainHeadReader) CurrentHeader() *types.Header { + return m.Backend.CurrentHeader() +} + +func (m *simChainHeadReader) GetHeader(hash common.Hash, number uint64) *types.Header { + header, err := m.Backend.HeaderByNumber(m.Context, rpc.BlockNumber(number)) + if err != nil || header == nil { + return nil + } + if header.Hash() != hash { + return nil + } + return header +} + +func (m *simChainHeadReader) GetHeaderByNumber(number uint64) *types.Header { + header, err := m.Backend.HeaderByNumber(m.Context, rpc.BlockNumber(number)) + if err != nil { + return nil + } + return header +} + +func (m *simChainHeadReader) GetHeaderByHash(hash common.Hash) *types.Header { + header, err := m.Backend.HeaderByHash(m.Context, hash) + if err != nil { + return nil + } + return header +} + // simulator is a stateful object that simulates a series of blocks. // it is not safe for concurrent use. type simulator struct { @@ -209,6 +249,9 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, if sim.chainConfig.IsPrague(header.Number, header.Time) || sim.chainConfig.IsVerkle(header.Number, header.Time) { core.ProcessParentBlockHash(header.ParentHash, evm) } + if header.ParentBeaconRoot != nil { + core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, evm) + } var allLogs []*types.Log for i, call := range block.Calls { if err := ctx.Err(); err != nil { @@ -258,6 +301,10 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, } callResults[i] = callRes } + header.GasUsed = gasUsed + if sim.chainConfig.IsCancun(header.Number, header.Time) { + header.BlobGasUsed = &blobGasUsed + } var requests [][]byte // Process EIP-7685 requests if sim.chainConfig.IsPrague(header.Number, header.Time) { @@ -271,20 +318,16 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, // EIP-7251 core.ProcessConsolidationQueue(&requests, evm) } - header.Root = sim.state.IntermediateRoot(true) - header.GasUsed = gasUsed - if sim.chainConfig.IsCancun(header.Number, header.Time) { - header.BlobGasUsed = &blobGasUsed - } - var withdrawals types.Withdrawals - if sim.chainConfig.IsShanghai(header.Number, header.Time) { - withdrawals = make([]*types.Withdrawal, 0) - } if requests != nil { reqHash := types.CalcRequestsHash(requests) header.RequestsHash = &reqHash } - b := types.NewBlock(header, &types.Body{Transactions: txes, Withdrawals: withdrawals}, receipts, trie.NewStackTrie(nil)) + blockBody := &types.Body{Transactions: txes, Withdrawals: *block.BlockOverrides.Withdrawals} + chainHeadReader := &simChainHeadReader{ctx, sim.b} + b, err := sim.b.Engine().FinalizeAndAssemble(chainHeadReader, header, sim.state, blockBody, receipts) + if err != nil { + return nil, nil, err + } repairLogs(callResults, b.Hash()) return b, callResults, nil } @@ -346,6 +389,9 @@ func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) { n := new(big.Int).Add(prevNumber, big.NewInt(1)) block.BlockOverrides.Number = (*hexutil.Big)(n) } + if block.BlockOverrides.Withdrawals == nil { + block.BlockOverrides.Withdrawals = &types.Withdrawals{} + } diff := new(big.Int).Sub(block.BlockOverrides.Number.ToInt(), prevNumber) if diff.Cmp(common.Big0) <= 0 { return nil, &invalidBlockNumberError{fmt.Sprintf("block numbers must be in order: %d <= %d", block.BlockOverrides.Number.ToInt().Uint64(), prevNumber)} @@ -360,7 +406,13 @@ func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) { for i := uint64(0); i < gap.Uint64(); i++ { n := new(big.Int).Add(prevNumber, big.NewInt(int64(i+1))) t := prevTimestamp + timestampIncrement - b := simBlock{BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(n), Time: (*hexutil.Uint64)(&t)}} + b := simBlock{ + BlockOverrides: &override.BlockOverrides{ + Number: (*hexutil.Big)(n), + Time: (*hexutil.Uint64)(&t), + Withdrawals: &types.Withdrawals{}, + }, + } prevTimestamp = t res = append(res, b) } @@ -405,6 +457,9 @@ func (sim *simulator) makeHeaders(blocks []simBlock) ([]*types.Header, error) { var parentBeaconRoot *common.Hash if sim.chainConfig.IsCancun(overrides.Number.ToInt(), (uint64)(*overrides.Time)) { parentBeaconRoot = &common.Hash{} + if overrides.BeaconRoot != nil { + parentBeaconRoot = overrides.BeaconRoot + } } header = overrides.MakeHeader(&types.Header{ UncleHash: types.EmptyUncleHash, From a14b8eca04e9b7476f4a03c225c02e7bc2c32b71 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Tue, 25 Mar 2025 18:16:26 +0800 Subject: [PATCH 037/658] core/txpool: reject stale transaction for local tracking (#31473) Fixes https://github.com/ethereum/go-ethereum/issues/31451 --- core/txpool/locals/tx_tracker_test.go | 179 ++++++++++++++++++++++++++ core/txpool/txpool.go | 54 +++++++- 2 files changed, 230 insertions(+), 3 deletions(-) create mode 100644 core/txpool/locals/tx_tracker_test.go diff --git a/core/txpool/locals/tx_tracker_test.go b/core/txpool/locals/tx_tracker_test.go new file mode 100644 index 00000000000..cb6b9b34535 --- /dev/null +++ b/core/txpool/locals/tx_tracker_test.go @@ -0,0 +1,179 @@ +// 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 locals + +import ( + "errors" + "math/big" + "testing" + "time" + + "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/rawdb" + "github.com/ethereum/go-ethereum/core/txpool" + "github.com/ethereum/go-ethereum/core/txpool/legacypool" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" +) + +var ( + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000000000) + gspec = &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{ + address: {Balance: funds}, + }, + BaseFee: big.NewInt(params.InitialBaseFee), + } + signer = types.LatestSigner(gspec.Config) +) + +type testEnv struct { + chain *core.BlockChain + pool *txpool.TxPool + tracker *TxTracker + genDb ethdb.Database +} + +func newTestEnv(t *testing.T, n int, gasTip uint64, journal string) *testEnv { + genDb, blocks, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), n, func(i int, gen *core.BlockGen) { + tx, err := types.SignTx(types.NewTransaction(gen.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, gen.BaseFee(), nil), signer, key) + if err != nil { + panic(err) + } + gen.AddTx(tx) + }) + + db := rawdb.NewMemoryDatabase() + chain, _ := core.NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil) + + legacyPool := legacypool.New(legacypool.DefaultConfig, chain) + pool, err := txpool.New(gasTip, chain, []txpool.SubPool{legacyPool}) + if err != nil { + t.Fatalf("Failed to create tx pool: %v", err) + } + if n, err := chain.InsertChain(blocks); err != nil { + t.Fatalf("Failed to process block %d: %v", n, err) + } + if err := pool.Sync(); err != nil { + t.Fatalf("Failed to sync the txpool, %v", err) + } + return &testEnv{ + chain: chain, + pool: pool, + tracker: New(journal, time.Minute, gspec.Config, pool), + genDb: genDb, + } +} + +func (env *testEnv) close() { + env.chain.Stop() +} + +func (env *testEnv) setGasTip(gasTip uint64) { + env.pool.SetGasTip(new(big.Int).SetUint64(gasTip)) +} + +func (env *testEnv) makeTx(nonce uint64, gasPrice *big.Int) *types.Transaction { + if nonce == 0 { + head := env.chain.CurrentHeader() + state, _ := env.chain.StateAt(head.Root) + nonce = state.GetNonce(address) + } + if gasPrice == nil { + gasPrice = big.NewInt(params.GWei) + } + tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{0x00}, big.NewInt(1000), params.TxGas, gasPrice, nil), signer, key) + return tx +} + +func (env *testEnv) commit() { + head := env.chain.CurrentBlock() + block := env.chain.GetBlock(head.Hash(), head.Number.Uint64()) + blocks, _ := core.GenerateChain(env.chain.Config(), block, ethash.NewFaker(), env.genDb, 1, func(i int, gen *core.BlockGen) { + tx, err := types.SignTx(types.NewTransaction(gen.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, gen.BaseFee(), nil), signer, key) + if err != nil { + panic(err) + } + gen.AddTx(tx) + }) + env.chain.InsertChain(blocks) + if err := env.pool.Sync(); err != nil { + panic(err) + } +} + +func TestRejectInvalids(t *testing.T) { + env := newTestEnv(t, 10, 0, "") + defer env.close() + + var cases = []struct { + gasTip uint64 + tx *types.Transaction + expErr error + commit bool + }{ + { + tx: env.makeTx(5, nil), // stale + expErr: core.ErrNonceTooLow, + }, + { + tx: env.makeTx(11, nil), // future transaction + expErr: nil, + }, + { + gasTip: params.GWei, + tx: env.makeTx(0, new(big.Int).SetUint64(params.GWei/2)), // low price + expErr: txpool.ErrUnderpriced, + }, + { + tx: types.NewTransaction(10, common.Address{0x00}, big.NewInt(1000), params.TxGas, big.NewInt(params.GWei), nil), // invalid signature + expErr: types.ErrInvalidSig, + }, + { + commit: true, + tx: env.makeTx(10, nil), // stale + expErr: core.ErrNonceTooLow, + }, + { + tx: env.makeTx(11, nil), + expErr: nil, + }, + } + for i, c := range cases { + if c.gasTip != 0 { + env.setGasTip(c.gasTip) + } + if c.commit { + env.commit() + } + gotErr := env.tracker.Track(c.tx) + if c.expErr == nil && gotErr != nil { + t.Fatalf("%d, unexpected error: %v", i, gotErr) + } + if c.expErr != nil && !errors.Is(gotErr, c.expErr) { + t.Fatalf("%d, unexpected error, want: %v, got: %v", i, c.expErr, gotErr) + } + } +} diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 042e3d36d9d..3c00699dc70 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -24,11 +24,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/params" ) // TxStatus is the current status of a transaction as seen by the pool. @@ -53,11 +55,17 @@ var ( // BlockChain defines the minimal set of methods needed to back a tx pool with // a chain. Exists to allow mocking the live chain out of tests. type BlockChain interface { + // Config retrieves the chain's fork configuration. + Config() *params.ChainConfig + // CurrentBlock returns the current head of the chain. CurrentBlock() *types.Header // SubscribeChainHeadEvent subscribes to new blocks being added to the chain. SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription + + // StateAt returns a state database for a given root hash (generally the head). + StateAt(root common.Hash) (*state.StateDB, error) } // TxPool is an aggregator for various transaction specific pools, collectively @@ -67,6 +75,11 @@ type BlockChain interface { // resource constraints. type TxPool struct { subpools []SubPool // List of subpools for specialized transaction handling + chain BlockChain + signer types.Signer + + stateLock sync.RWMutex // The lock for protecting state instance + state *state.StateDB // Current state at the blockchain head reservations map[common.Address]SubPool // Map with the account to pool reservations reserveLock sync.Mutex // Lock protecting the account reservations @@ -86,8 +99,21 @@ func New(gasTip uint64, chain BlockChain, subpools []SubPool) (*TxPool, error) { // during initialization. head := chain.CurrentBlock() + // Initialize the state with head block, or fallback to empty one in + // case the head state is not available (might occur when node is not + // fully synced). + statedb, err := chain.StateAt(head.Root) + if err != nil { + statedb, err = chain.StateAt(types.EmptyRootHash) + } + if err != nil { + return nil, err + } pool := &TxPool{ subpools: subpools, + chain: chain, + signer: types.LatestSigner(chain.Config()), + state: statedb, reservations: make(map[common.Address]SubPool), quit: make(chan chan error), term: make(chan struct{}), @@ -101,7 +127,7 @@ func New(gasTip uint64, chain BlockChain, subpools []SubPool) (*TxPool, error) { return nil, err } } - go pool.loop(head, chain) + go pool.loop(head) return pool, nil } @@ -179,14 +205,14 @@ func (p *TxPool) Close() error { // loop is the transaction pool's main event loop, waiting for and reacting to // outside blockchain events as well as for various reporting and transaction // eviction events. -func (p *TxPool) loop(head *types.Header, chain BlockChain) { +func (p *TxPool) loop(head *types.Header) { // Close the termination marker when the pool stops defer close(p.term) // Subscribe to chain head events to trigger subpool resets var ( newHeadCh = make(chan core.ChainHeadEvent) - newHeadSub = chain.SubscribeChainHeadEvent(newHeadCh) + newHeadSub = p.chain.SubscribeChainHeadEvent(newHeadCh) ) defer newHeadSub.Unsubscribe() @@ -219,6 +245,14 @@ func (p *TxPool) loop(head *types.Header, chain BlockChain) { // Try to inject a busy marker and start a reset if successful select { case resetBusy <- struct{}{}: + statedb, err := p.chain.StateAt(newHead.Root) + if err != nil { + log.Crit("Failed to reset txpool state", "err", err) + } + p.stateLock.Lock() + p.state = statedb + p.stateLock.Unlock() + // Busy marker injected, start a new subpool reset go func(oldHead, newHead *types.Header) { for _, subpool := range p.subpools { @@ -339,6 +373,20 @@ func (p *TxPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Pr // ValidateTxBasics checks whether a transaction is valid according to the consensus // rules, but does not check state-dependent validation such as sufficient balance. func (p *TxPool) ValidateTxBasics(tx *types.Transaction) error { + addr, err := types.Sender(p.signer, tx) + if err != nil { + return err + } + // Reject transactions with stale nonce. Gapped-nonce future transactions + // are considered valid and will be handled by the subpool according to its + // internal policy. + p.stateLock.RLock() + nonce := p.state.GetNonce(addr) + p.stateLock.RUnlock() + + if nonce > tx.Nonce() { + return core.ErrNonceTooLow + } for _, subpool := range p.subpools { if subpool.Filter(tx) { return subpool.ValidateTxBasics(tx) From 19d2b4c88083463f4ff38ebe51962970b7b37fc3 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 25 Mar 2025 11:30:13 +0100 Subject: [PATCH 038/658] version: release v1.15.6 stable --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 586bb870d14..44740849bb2 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 6 // Patch version component of the current release - Meta = "unstable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 6 // Patch version component of the current release + Meta = "stable" // Version metadata to append to the version string ) From aa6d0ef5f27508ba0ba3d7a146e9a033759a8edb Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 25 Mar 2025 12:26:15 +0100 Subject: [PATCH 039/658] version: begin v1.15.7 release cycle --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 44740849bb2..8cd428fd102 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 6 // Patch version component of the current release - Meta = "stable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 7 // Patch version component of the current release + Meta = "unstable" // Version metadata to append to the version string ) From 21d36f7c3797c6c5b8b737d586a79fa5bb47115f Mon Sep 17 00:00:00 2001 From: nethoxa <135072738+nethoxa@users.noreply.github.com> Date: Tue, 25 Mar 2025 14:13:05 +0100 Subject: [PATCH 040/658] core: process EL requests in GenerateVerkleChain (#31175) --- core/chain_makers.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/chain_makers.go b/core/chain_makers.go index 8d09390b72d..7a258dc05f8 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -504,6 +504,13 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine if gen != nil { gen(i, b) } + + requests := b.collectRequests(false) + if requests != nil { + reqHash := types.CalcRequestsHash(requests) + b.header.RequestsHash = &reqHash + } + body := &types.Body{ Transactions: b.txs, Uncles: b.uncles, From 4ff5093df1dba2d5578f8eb4bfd44ca805d7f67f Mon Sep 17 00:00:00 2001 From: Shude Li Date: Tue, 25 Mar 2025 21:53:02 +0800 Subject: [PATCH 041/658] all: use fmt.Appendf instead of fmt.Sprintf where possible (#31301) --- cmd/utils/export_test.go | 14 ++++---- common/math/big.go | 2 +- common/math/integer.go | 2 +- core/state/snapshot/generate_test.go | 2 +- core/state/snapshot/iterator_test.go | 32 +++++++++---------- p2p/nat/nat.go | 2 +- p2p/nat/natpmp.go | 2 +- .../apitypes/signed_data_internal_test.go | 2 +- signer/core/signed_data_test.go | 2 +- triedb/pathdb/iterator_test.go | 32 +++++++++---------- 10 files changed, 46 insertions(+), 46 deletions(-) diff --git a/cmd/utils/export_test.go b/cmd/utils/export_test.go index b70d2451c68..d1a004d9b7f 100644 --- a/cmd/utils/export_test.go +++ b/cmd/utils/export_test.go @@ -54,8 +54,8 @@ func (iter *testIterator) Next() (byte, []byte, []byte, bool) { if iter.index == 42 { iter.index += 1 } - return OpBatchAdd, []byte(fmt.Sprintf("key-%04d", iter.index)), - []byte(fmt.Sprintf("value %d", iter.index)), true + return OpBatchAdd, fmt.Appendf(nil, "key-%04d", iter.index), + fmt.Appendf(nil, "value %d", iter.index), true } func (iter *testIterator) Release() {} @@ -72,7 +72,7 @@ func testExport(t *testing.T, f string) { } // verify for i := 0; i < 1000; i++ { - v, err := db.Get([]byte(fmt.Sprintf("key-%04d", i))) + v, err := db.Get(fmt.Appendf(nil, "key-%04d", i)) if (i < 5 || i == 42) && err == nil { t.Fatalf("expected no element at idx %d, got '%v'", i, string(v)) } @@ -85,7 +85,7 @@ func testExport(t *testing.T, f string) { } } } - v, err := db.Get([]byte(fmt.Sprintf("key-%04d", 1000))) + v, err := db.Get(fmt.Appendf(nil, "key-%04d", 1000)) if err == nil { t.Fatalf("expected no element at idx %d, got '%v'", 1000, string(v)) } @@ -120,7 +120,7 @@ func (iter *deletionIterator) Next() (byte, []byte, []byte, bool) { if iter.index == 42 { iter.index += 1 } - return OpBatchDel, []byte(fmt.Sprintf("key-%04d", iter.index)), nil, true + return OpBatchDel, fmt.Appendf(nil, "key-%04d", iter.index), nil, true } func (iter *deletionIterator) Release() {} @@ -132,14 +132,14 @@ func testDeletion(t *testing.T, f string) { } db := rawdb.NewMemoryDatabase() for i := 0; i < 1000; i++ { - db.Put([]byte(fmt.Sprintf("key-%04d", i)), []byte(fmt.Sprintf("value %d", i))) + db.Put(fmt.Appendf(nil, "key-%04d", i), fmt.Appendf(nil, "value %d", i)) } err = ImportLDBData(db, f, 5, make(chan struct{})) if err != nil { t.Fatal(err) } for i := 0; i < 1000; i++ { - v, err := db.Get([]byte(fmt.Sprintf("key-%04d", i))) + v, err := db.Get(fmt.Appendf(nil, "key-%04d", i)) if i < 5 || i == 42 { if err != nil { t.Fatalf("expected element at idx %d, got '%v'", i, err) diff --git a/common/math/big.go b/common/math/big.go index 825f4baec9e..493c2b7861c 100644 --- a/common/math/big.go +++ b/common/math/big.go @@ -72,7 +72,7 @@ func (i *HexOrDecimal256) MarshalText() ([]byte, error) { if i == nil { return []byte("0x0"), nil } - return []byte(fmt.Sprintf("%#x", (*big.Int)(i))), nil + return fmt.Appendf(nil, "%#x", (*big.Int)(i)), nil } // Decimal256 unmarshals big.Int as a decimal string. When unmarshalling, diff --git a/common/math/integer.go b/common/math/integer.go index 25ced870530..dfcb0aecc4e 100644 --- a/common/math/integer.go +++ b/common/math/integer.go @@ -48,7 +48,7 @@ func (i *HexOrDecimal64) UnmarshalText(input []byte) error { // MarshalText implements encoding.TextMarshaler. func (i HexOrDecimal64) MarshalText() ([]byte, error) { - return []byte(fmt.Sprintf("%#x", uint64(i))), nil + return fmt.Appendf(nil, "%#x", uint64(i)), nil } // ParseUint64 parses s as an integer in decimal or hexadecimal syntax. diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go index 661610840a0..3de7735ef86 100644 --- a/core/state/snapshot/generate_test.go +++ b/core/state/snapshot/generate_test.go @@ -655,7 +655,7 @@ func testGenerateWithManyExtraAccounts(t *testing.T, scheme string) { for i := 0; i < 1000; i++ { acc := &types.StateAccount{Balance: uint256.NewInt(uint64(i)), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()} val, _ := rlp.EncodeToBytes(acc) - key := hashData([]byte(fmt.Sprintf("acc-%d", i))) + key := hashData(fmt.Appendf(nil, "acc-%d", i)) rawdb.WriteAccountSnapshot(helper.diskdb, key, val) } } diff --git a/core/state/snapshot/iterator_test.go b/core/state/snapshot/iterator_test.go index b9fe370b861..2e882f484ec 100644 --- a/core/state/snapshot/iterator_test.go +++ b/core/state/snapshot/iterator_test.go @@ -329,27 +329,27 @@ func TestAccountIteratorTraversalValues(t *testing.T) { h = make(map[common.Hash][]byte) ) for i := byte(2); i < 0xff; i++ { - a[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 0, i)) + a[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 0, i) if i > 20 && i%2 == 0 { - b[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 1, i)) + b[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 1, i) } if i%4 == 0 { - c[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 2, i)) + c[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 2, i) } if i%7 == 0 { - d[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 3, i)) + d[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 3, i) } if i%8 == 0 { - e[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 4, i)) + e[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 4, i) } if i > 50 || i < 85 { - f[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 5, i)) + f[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 5, i) } if i%64 == 0 { - g[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 6, i)) + g[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 6, i) } if i%128 == 0 { - h[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 7, i)) + h[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 7, i) } } // Assemble a stack of snapshots from the account layers @@ -428,27 +428,27 @@ func TestStorageIteratorTraversalValues(t *testing.T) { h = make(map[common.Hash][]byte) ) for i := byte(2); i < 0xff; i++ { - a[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 0, i)) + a[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 0, i) if i > 20 && i%2 == 0 { - b[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 1, i)) + b[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 1, i) } if i%4 == 0 { - c[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 2, i)) + c[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 2, i) } if i%7 == 0 { - d[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 3, i)) + d[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 3, i) } if i%8 == 0 { - e[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 4, i)) + e[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 4, i) } if i > 50 || i < 85 { - f[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 5, i)) + f[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 5, i) } if i%64 == 0 { - g[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 6, i)) + g[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 6, i) } if i%128 == 0 { - h[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 7, i)) + h[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 7, i) } } // Assemble a stack of snapshots from the account layers diff --git a/p2p/nat/nat.go b/p2p/nat/nat.go index 5f7af7424d7..a0ddb3b29b5 100644 --- a/p2p/nat/nat.go +++ b/p2p/nat/nat.go @@ -140,7 +140,7 @@ type ExtIP net.IP func (n ExtIP) ExternalIP() (net.IP, error) { return net.IP(n), nil } func (n ExtIP) String() string { return fmt.Sprintf("ExtIP(%v)", net.IP(n)) } -func (n ExtIP) MarshalText() ([]byte, error) { return []byte(fmt.Sprintf("extip:%v", net.IP(n))), nil } +func (n ExtIP) MarshalText() ([]byte, error) { return fmt.Appendf(nil, "extip:%v", net.IP(n)), nil } // These do nothing. diff --git a/p2p/nat/natpmp.go b/p2p/nat/natpmp.go index b8f59ee890a..4a9644ac1a2 100644 --- a/p2p/nat/natpmp.go +++ b/p2p/nat/natpmp.go @@ -71,7 +71,7 @@ func (n *pmp) DeleteMapping(protocol string, extport, intport int) (err error) { } func (n *pmp) MarshalText() ([]byte, error) { - return []byte(fmt.Sprintf("natpmp:%v", n.gw)), nil + return fmt.Appendf(nil, "natpmp:%v", n.gw), nil } func discoverPMP() Interface { diff --git a/signer/core/apitypes/signed_data_internal_test.go b/signer/core/apitypes/signed_data_internal_test.go index 1a14b35ef24..bee9f1c3515 100644 --- a/signer/core/apitypes/signed_data_internal_test.go +++ b/signer/core/apitypes/signed_data_internal_test.go @@ -282,7 +282,7 @@ func TestTypedDataArrayValidate(t *testing.T) { messageHash, tErr := td.HashStruct(td.PrimaryType, td.Message) assert.NoError(t, tErr, "failed to hash message: %v", tErr) - digest := crypto.Keccak256Hash([]byte(fmt.Sprintf("%s%s%s", "\x19\x01", string(domainSeparator), string(messageHash)))) + digest := crypto.Keccak256Hash(fmt.Appendf(nil, "%s%s%s", "\x19\x01", string(domainSeparator), string(messageHash))) assert.Equal(t, tc.Digest, digest.String(), "digest doesn't not match") assert.NoError(t, td.validate(), "validation failed", tErr) diff --git a/signer/core/signed_data_test.go b/signer/core/signed_data_test.go index b6c080736c3..001f6b6838c 100644 --- a/signer/core/signed_data_test.go +++ b/signer/core/signed_data_test.go @@ -369,7 +369,7 @@ func sign(typedData apitypes.TypedData) ([]byte, []byte, error) { if err != nil { return nil, nil, err } - rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) + rawData := fmt.Appendf(nil, "\x19\x01%s%s", string(domainSeparator), string(typedDataHash)) sighash := crypto.Keccak256(rawData) return typedDataHash, sighash, nil } diff --git a/triedb/pathdb/iterator_test.go b/triedb/pathdb/iterator_test.go index 05a166d1b68..38941180340 100644 --- a/triedb/pathdb/iterator_test.go +++ b/triedb/pathdb/iterator_test.go @@ -371,27 +371,27 @@ func TestAccountIteratorTraversalValues(t *testing.T) { h = make(map[common.Hash][]byte) ) for i := byte(2); i < 0xff; i++ { - a[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 0, i)) + a[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 0, i) if i > 20 && i%2 == 0 { - b[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 1, i)) + b[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 1, i) } if i%4 == 0 { - c[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 2, i)) + c[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 2, i) } if i%7 == 0 { - d[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 3, i)) + d[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 3, i) } if i%8 == 0 { - e[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 4, i)) + e[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 4, i) } if i > 50 || i < 85 { - f[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 5, i)) + f[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 5, i) } if i%64 == 0 { - g[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 6, i)) + g[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 6, i) } if i%128 == 0 { - h[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 7, i)) + h[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 7, i) } } // Assemble a stack of snapshots from the account layers @@ -480,27 +480,27 @@ func TestStorageIteratorTraversalValues(t *testing.T) { h = make(map[common.Hash][]byte) ) for i := byte(2); i < 0xff; i++ { - a[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 0, i)) + a[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 0, i) if i > 20 && i%2 == 0 { - b[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 1, i)) + b[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 1, i) } if i%4 == 0 { - c[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 2, i)) + c[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 2, i) } if i%7 == 0 { - d[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 3, i)) + d[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 3, i) } if i%8 == 0 { - e[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 4, i)) + e[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 4, i) } if i > 50 || i < 85 { - f[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 5, i)) + f[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 5, i) } if i%64 == 0 { - g[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 6, i)) + g[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 6, i) } if i%128 == 0 { - h[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 7, i)) + h[common.Hash{i}] = fmt.Appendf(nil, "layer-%d, key %d", 7, i) } } // Assemble a stack of snapshots from the account layers From 4dfec7e83e4634cc8ab254b4bdbe9e692142316b Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Tue, 25 Mar 2025 21:59:44 +0800 Subject: [PATCH 042/658] trie: optimize memory allocation (#30932) This pull request removes the node copy operation to reduce memory allocation. Key Changes as below: **(a) Use `decodeNodeUnsafe` for decoding nodes retrieved from the trie node reader** In the current implementation of the MPT, once a trie node blob is retrieved, it is passed to `decodeNode` for decoding. However, `decodeNode` assumes the supplied byte slice might be mutated later, so it performs a deep copy internally before parsing the node. Given that the node reader is implemented by the path database and the hash database, both of which guarantee the immutability of the returned byte slice. By restricting the node reader interface to explicitly guarantee that the returned byte slice will not be modified, we can safely replace `decodeNode` with `decodeNodeUnsafe`. This eliminates the need for a redundant byte copy during each node resolution. **(b) Modify the trie in place** In the current implementation of the MPT, a copy of a trie node is created before any modifications are made. These modifications include: - Node resolution: Converting the value from a hash to the actual node. - Node hashing: Tagging the hash into its cache. - Node commit: Replacing the children with its hash. - Structural changes: For example, adding a new child to a fullNode or replacing a child of a shortNode. This mechanism ensures that modifications only affect the live tree, leaving all previously created copies unaffected. Unfortunately, this property leads to a huge memory allocation requirement. For example, if we want to modify the fullNode for n times, the node will be copied for n times. In this pull request, all the trie modifications are made in place. In order to make sure all previously created copies are unaffected, the `Copy` function now will deep-copy all the live nodes rather than the root node itself. With this change, while the `Copy` function becomes more expensive, it's totally acceptable as it's not a frequently used one. For the normal trie operations (Get, GetNode, Hash, Commit, Insert, Delete), the node copy is not required anymore. --- trie/committer.go | 39 +++------ trie/hasher.go | 84 +++++++++--------- trie/node.go | 14 ++- trie/trie.go | 68 ++++++++++----- trie/trie_test.go | 168 ++++++++++++++++++++++++++++++++++++ triedb/database/database.go | 2 + 6 files changed, 279 insertions(+), 96 deletions(-) diff --git a/trie/committer.go b/trie/committer.go index 6c4374ccfdd..0939a07abb6 100644 --- a/trie/committer.go +++ b/trie/committer.go @@ -57,32 +57,26 @@ func (c *committer) commit(path []byte, n node, parallel bool) node { // Commit children, then parent, and remove the dirty flag. switch cn := n.(type) { case *shortNode: - // Commit child - collapsed := cn.copy() - // If the child is fullNode, recursively commit, // otherwise it can only be hashNode or valueNode. if _, ok := cn.Val.(*fullNode); ok { - collapsed.Val = c.commit(append(path, cn.Key...), cn.Val, false) + cn.Val = c.commit(append(path, cn.Key...), cn.Val, false) } // The key needs to be copied, since we're adding it to the // modified nodeset. - collapsed.Key = hexToCompact(cn.Key) - hashedNode := c.store(path, collapsed) + cn.Key = hexToCompact(cn.Key) + hashedNode := c.store(path, cn) if hn, ok := hashedNode.(hashNode); ok { return hn } - return collapsed + return cn case *fullNode: - hashedKids := c.commitChildren(path, cn, parallel) - collapsed := cn.copy() - collapsed.Children = hashedKids - - hashedNode := c.store(path, collapsed) + c.commitChildren(path, cn, parallel) + hashedNode := c.store(path, cn) if hn, ok := hashedNode.(hashNode); ok { return hn } - return collapsed + return cn case hashNode: return cn default: @@ -92,11 +86,10 @@ func (c *committer) commit(path []byte, n node, parallel bool) node { } // commitChildren commits the children of the given fullnode -func (c *committer) commitChildren(path []byte, n *fullNode, parallel bool) [17]node { +func (c *committer) commitChildren(path []byte, n *fullNode, parallel bool) { var ( - wg sync.WaitGroup - nodesMu sync.Mutex - children [17]node + wg sync.WaitGroup + nodesMu sync.Mutex ) for i := 0; i < 16; i++ { child := n.Children[i] @@ -106,22 +99,21 @@ func (c *committer) commitChildren(path []byte, n *fullNode, parallel bool) [17] // If it's the hashed child, save the hash value directly. // Note: it's impossible that the child in range [0, 15] // is a valueNode. - if hn, ok := child.(hashNode); ok { - children[i] = hn + if _, ok := child.(hashNode); ok { continue } // Commit the child recursively and store the "hashed" value. // Note the returned node can be some embedded nodes, so it's // possible the type is not hashNode. if !parallel { - children[i] = c.commit(append(path, byte(i)), child, false) + n.Children[i] = c.commit(append(path, byte(i)), child, false) } else { wg.Add(1) go func(index int) { p := append(path, byte(index)) childSet := trienode.NewNodeSet(c.nodes.Owner) childCommitter := newCommitter(childSet, c.tracer, c.collectLeaf) - children[index] = childCommitter.commit(p, child, false) + n.Children[index] = childCommitter.commit(p, child, false) nodesMu.Lock() c.nodes.MergeSet(childSet) nodesMu.Unlock() @@ -132,11 +124,6 @@ func (c *committer) commitChildren(path []byte, n *fullNode, parallel bool) [17] if parallel { wg.Wait() } - // For the 17th child, it's possible the type is valuenode. - if n.Children[16] != nil { - children[16] = n.Children[16] - } - return children } // store hashes the node n and adds it to the modified nodeset. If leaf collection diff --git a/trie/hasher.go b/trie/hasher.go index 28f7f3d0c38..614640ae3a9 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -53,62 +53,56 @@ func returnHasherToPool(h *hasher) { hasherPool.Put(h) } -// hash collapses a node down into a hash node, also returning a copy of the -// original node initialized with the computed hash to replace the original one. -func (h *hasher) hash(n node, force bool) (hashed node, cached node) { +// hash collapses a node down into a hash node. +func (h *hasher) hash(n node, force bool) node { // Return the cached hash if it's available if hash, _ := n.cache(); hash != nil { - return hash, n + return hash } // Trie not processed yet, walk the children switch n := n.(type) { case *shortNode: - collapsed, cached := h.hashShortNodeChildren(n) + collapsed := h.hashShortNodeChildren(n) hashed := h.shortnodeToHash(collapsed, force) - // We need to retain the possibly _not_ hashed node, in case it was too - // small to be hashed if hn, ok := hashed.(hashNode); ok { - cached.flags.hash = hn + n.flags.hash = hn } else { - cached.flags.hash = nil + n.flags.hash = nil } - return hashed, cached + return hashed case *fullNode: - collapsed, cached := h.hashFullNodeChildren(n) - hashed = h.fullnodeToHash(collapsed, force) + collapsed := h.hashFullNodeChildren(n) + hashed := h.fullnodeToHash(collapsed, force) if hn, ok := hashed.(hashNode); ok { - cached.flags.hash = hn + n.flags.hash = hn } else { - cached.flags.hash = nil + n.flags.hash = nil } - return hashed, cached + return hashed default: // Value and hash nodes don't have children, so they're left as were - return n, n + return n } } -// hashShortNodeChildren collapses the short node. The returned collapsed node -// holds a live reference to the Key, and must not be modified. -func (h *hasher) hashShortNodeChildren(n *shortNode) (collapsed, cached *shortNode) { - // Hash the short node's child, caching the newly hashed subtree - collapsed, cached = n.copy(), n.copy() - // Previously, we did copy this one. We don't seem to need to actually - // do that, since we don't overwrite/reuse keys - // cached.Key = common.CopyBytes(n.Key) +// hashShortNodeChildren returns a copy of the supplied shortNode, with its child +// being replaced by either the hash or an embedded node if the child is small. +func (h *hasher) hashShortNodeChildren(n *shortNode) *shortNode { + var collapsed shortNode collapsed.Key = hexToCompact(n.Key) - // Unless the child is a valuenode or hashnode, hash it switch n.Val.(type) { case *fullNode, *shortNode: - collapsed.Val, cached.Val = h.hash(n.Val, false) + collapsed.Val = h.hash(n.Val, false) + default: + collapsed.Val = n.Val } - return collapsed, cached + return &collapsed } -func (h *hasher) hashFullNodeChildren(n *fullNode) (collapsed *fullNode, cached *fullNode) { - // Hash the full node's children, caching the newly hashed subtrees - cached = n.copy() - collapsed = n.copy() +// hashFullNodeChildren returns a copy of the supplied fullNode, with its child +// being replaced by either the hash or an embedded node if the child is small. +func (h *hasher) hashFullNodeChildren(n *fullNode) *fullNode { + var children [17]node if h.parallel { var wg sync.WaitGroup wg.Add(16) @@ -116,9 +110,9 @@ func (h *hasher) hashFullNodeChildren(n *fullNode) (collapsed *fullNode, cached go func(i int) { hasher := newHasher(false) if child := n.Children[i]; child != nil { - collapsed.Children[i], cached.Children[i] = hasher.hash(child, false) + children[i] = hasher.hash(child, false) } else { - collapsed.Children[i] = nilValueNode + children[i] = nilValueNode } returnHasherToPool(hasher) wg.Done() @@ -128,19 +122,21 @@ func (h *hasher) hashFullNodeChildren(n *fullNode) (collapsed *fullNode, cached } else { for i := 0; i < 16; i++ { if child := n.Children[i]; child != nil { - collapsed.Children[i], cached.Children[i] = h.hash(child, false) + children[i] = h.hash(child, false) } else { - collapsed.Children[i] = nilValueNode + children[i] = nilValueNode } } } - return collapsed, cached + if n.Children[16] != nil { + children[16] = n.Children[16] + } + return &fullNode{flags: nodeFlag{}, Children: children} } -// shortnodeToHash creates a hashNode from a shortNode. The supplied shortnode -// should have hex-type Key, which will be converted (without modification) -// into compact form for RLP encoding. -// If the rlp data is smaller than 32 bytes, `nil` is returned. +// shortNodeToHash computes the hash of the given shortNode. The shortNode must +// first be collapsed, with its key converted to compact form. If the RLP-encoded +// node data is smaller than 32 bytes, the node itself is returned. func (h *hasher) shortnodeToHash(n *shortNode, force bool) node { n.encode(h.encbuf) enc := h.encodedBytes() @@ -151,8 +147,8 @@ func (h *hasher) shortnodeToHash(n *shortNode, force bool) node { return h.hashData(enc) } -// fullnodeToHash is used to create a hashNode from a fullNode, (which -// may contain nil values) +// fullnodeToHash computes the hash of the given fullNode. If the RLP-encoded +// node data is smaller than 32 bytes, the node itself is returned. func (h *hasher) fullnodeToHash(n *fullNode, force bool) node { n.encode(h.encbuf) enc := h.encodedBytes() @@ -203,10 +199,10 @@ func (h *hasher) hashDataTo(dst, data []byte) { func (h *hasher) proofHash(original node) (collapsed, hashed node) { switch n := original.(type) { case *shortNode: - sn, _ := h.hashShortNodeChildren(n) + sn := h.hashShortNodeChildren(n) return sn, h.shortnodeToHash(sn, false) case *fullNode: - fn, _ := h.hashFullNodeChildren(n) + fn := h.hashFullNodeChildren(n) return fn, h.fullnodeToHash(fn, false) default: // Value and hash nodes don't have children, so they're left as were diff --git a/trie/node.go b/trie/node.go index ecc2de192d3..96f077ebbb7 100644 --- a/trie/node.go +++ b/trie/node.go @@ -79,15 +79,19 @@ func (n *fullNode) EncodeRLP(w io.Writer) error { return eb.Flush() } -func (n *fullNode) copy() *fullNode { copy := *n; return © } -func (n *shortNode) copy() *shortNode { copy := *n; return © } - // nodeFlag contains caching-related metadata about a node. type nodeFlag struct { hash hashNode // cached hash of the node (may be nil) dirty bool // whether the node has changes that must be written to the database } +func (n nodeFlag) copy() nodeFlag { + return nodeFlag{ + hash: common.CopyBytes(n.hash), + dirty: n.dirty, + } +} + func (n *fullNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty } func (n *shortNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty } func (n hashNode) cache() (hashNode, bool) { return nil, true } @@ -228,7 +232,9 @@ func decodeRef(buf []byte) (node, []byte, error) { err := fmt.Errorf("oversized embedded node (size is %d bytes, want size < %d)", size, hashLen) return nil, buf, err } - n, err := decodeNode(nil, buf) + // The buffer content has already been copied or is safe to use; + // no additional copy is required. + n, err := decodeNodeUnsafe(nil, buf) return n, rest, err case kind == rlp.String && len(val) == 0: // empty node diff --git a/trie/trie.go b/trie/trie.go index ae2a7b21a23..fdb4da9be47 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -29,11 +29,11 @@ import ( "github.com/ethereum/go-ethereum/triedb/database" ) -// Trie is a Merkle Patricia Trie. Use New to create a trie that sits on -// top of a database. Whenever trie performs a commit operation, the generated -// nodes will be gathered and returned in a set. Once the trie is committed, -// it's not usable anymore. Callers have to re-create the trie with new root -// based on the updated trie database. +// Trie represents a Merkle Patricia Trie. Use New to create a trie that operates +// on top of a node database. During a commit operation, the trie collects all +// modified nodes into a set for return. After committing, the trie becomes +// unusable, and callers must recreate it with the new root based on the updated +// trie database. // // Trie is not safe for concurrent use. type Trie struct { @@ -67,13 +67,13 @@ func (t *Trie) newFlag() nodeFlag { // Copy returns a copy of Trie. func (t *Trie) Copy() *Trie { return &Trie{ - root: t.root, + root: copyNode(t.root), owner: t.owner, committed: t.committed, + unhashed: t.unhashed, + uncommitted: t.uncommitted, reader: t.reader, tracer: t.tracer.copy(), - uncommitted: t.uncommitted, - unhashed: t.unhashed, } } @@ -169,14 +169,12 @@ func (t *Trie) get(origNode node, key []byte, pos int) (value []byte, newnode no } value, newnode, didResolve, err = t.get(n.Val, key, pos+len(n.Key)) if err == nil && didResolve { - n = n.copy() n.Val = newnode } return value, n, didResolve, err case *fullNode: value, newnode, didResolve, err = t.get(n.Children[key[pos]], key, pos+1) if err == nil && didResolve { - n = n.copy() n.Children[key[pos]] = newnode } return value, n, didResolve, err @@ -257,7 +255,6 @@ func (t *Trie) getNode(origNode node, path []byte, pos int) (item []byte, newnod } item, newnode, resolved, err = t.getNode(n.Val, path, pos+len(n.Key)) if err == nil && resolved > 0 { - n = n.copy() n.Val = newnode } return item, n, resolved, err @@ -265,7 +262,6 @@ func (t *Trie) getNode(origNode node, path []byte, pos int) (item []byte, newnod case *fullNode: item, newnode, resolved, err = t.getNode(n.Children[path[pos]], path, pos+1) if err == nil && resolved > 0 { - n = n.copy() n.Children[path[pos]] = newnode } return item, n, resolved, err @@ -375,7 +371,6 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error if !dirty || err != nil { return false, n, err } - n = n.copy() n.flags = t.newFlag() n.Children[key[0]] = nn return true, n, nil @@ -483,7 +478,6 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { if !dirty || err != nil { return false, n, err } - n = n.copy() n.flags = t.newFlag() n.Children[key[0]] = nn @@ -576,6 +570,36 @@ func concat(s1 []byte, s2 ...byte) []byte { return r } +// copyNode deep-copies the supplied node along with its children recursively. +func copyNode(n node) node { + switch n := (n).(type) { + case nil: + return nil + case valueNode: + return valueNode(common.CopyBytes(n)) + + case *shortNode: + return &shortNode{ + flags: n.flags.copy(), + Key: common.CopyBytes(n.Key), + Val: copyNode(n.Val), + } + case *fullNode: + var children [17]node + for i, cn := range n.Children { + children[i] = copyNode(cn) + } + return &fullNode{ + flags: n.flags.copy(), + Children: children, + } + case hashNode: + return n + default: + panic(fmt.Sprintf("%T: unknown node type", n)) + } +} + func (t *Trie) resolve(n node, prefix []byte) (node, error) { if n, ok := n.(hashNode); ok { return t.resolveAndTrack(n, prefix) @@ -593,15 +617,16 @@ func (t *Trie) resolveAndTrack(n hashNode, prefix []byte) (node, error) { return nil, err } t.tracer.onRead(prefix, blob) - return mustDecodeNode(n, blob), nil + + // The returned node blob won't be changed afterward. No need to + // deep-copy the slice. + return decodeNodeUnsafe(n, blob) } // Hash returns the root hash of the trie. It does not write to the // database and can be used even if the trie doesn't have one. func (t *Trie) Hash() common.Hash { - hash, cached := t.hashRoot() - t.root = cached - return common.BytesToHash(hash.(hashNode)) + return common.BytesToHash(t.hashRoot().(hashNode)) } // Commit collects all dirty nodes in the trie and replaces them with the @@ -652,9 +677,9 @@ func (t *Trie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet) { } // hashRoot calculates the root hash of the given trie -func (t *Trie) hashRoot() (node, node) { +func (t *Trie) hashRoot() node { if t.root == nil { - return hashNode(types.EmptyRootHash.Bytes()), nil + return hashNode(types.EmptyRootHash.Bytes()) } // If the number of changes is below 100, we let one thread handle it h := newHasher(t.unhashed >= 100) @@ -662,8 +687,7 @@ func (t *Trie) hashRoot() (node, node) { returnHasherToPool(h) t.unhashed = 0 }() - hashed, cached := h.hash(t.root, true) - return hashed, cached + return h.hash(t.root, true) } // Witness returns a set containing all trie nodes that have been accessed. diff --git a/trie/trie_test.go b/trie/trie_test.go index 77234d9d9b2..54d1b083d8e 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -1330,3 +1330,171 @@ func printSet(set *trienode.NodeSet) string { } return out.String() } + +func TestTrieCopy(t *testing.T) { + testTrieCopy(t, []kv{ + {k: []byte("do"), v: []byte("verb")}, + {k: []byte("ether"), v: []byte("wookiedoo")}, + {k: []byte("horse"), v: []byte("stallion")}, + {k: []byte("shaman"), v: []byte("horse")}, + {k: []byte("doge"), v: []byte("coin")}, + {k: []byte("dog"), v: []byte("puppy")}, + }) + + var entries []kv + for i := 0; i < 256; i++ { + entries = append(entries, kv{k: testrand.Bytes(32), v: testrand.Bytes(32)}) + } + testTrieCopy(t, entries) +} + +func testTrieCopy(t *testing.T, entries []kv) { + tr := NewEmpty(nil) + for _, entry := range entries { + tr.Update(entry.k, entry.v) + } + trCpy := tr.Copy() + + if tr.Hash() != trCpy.Hash() { + t.Errorf("Hash mismatch: old %v, copy %v", tr.Hash(), trCpy.Hash()) + } + + // Check iterator + it, _ := tr.NodeIterator(nil) + itCpy, _ := trCpy.NodeIterator(nil) + + for it.Next(false) { + hasNext := itCpy.Next(false) + if !hasNext { + t.Fatal("Iterator is not matched") + } + if !bytes.Equal(it.Path(), itCpy.Path()) { + t.Fatal("Iterator is not matched") + } + if it.Leaf() != itCpy.Leaf() { + t.Fatal("Iterator is not matched") + } + if it.Leaf() && !bytes.Equal(it.LeafBlob(), itCpy.LeafBlob()) { + t.Fatal("Iterator is not matched") + } + } + + // Check commit + root, nodes := tr.Commit(false) + rootCpy, nodesCpy := trCpy.Commit(false) + if root != rootCpy { + t.Fatal("root mismatch") + } + if len(nodes.Nodes) != len(nodesCpy.Nodes) { + t.Fatal("commit node mismatch") + } + for p, n := range nodes.Nodes { + nn, exists := nodesCpy.Nodes[p] + if !exists { + t.Fatalf("node not exists: %v", p) + } + if !reflect.DeepEqual(n, nn) { + t.Fatalf("node mismatch: %v", p) + } + } +} + +func TestTrieCopyOldTrie(t *testing.T) { + testTrieCopyOldTrie(t, []kv{ + {k: []byte("do"), v: []byte("verb")}, + {k: []byte("ether"), v: []byte("wookiedoo")}, + {k: []byte("horse"), v: []byte("stallion")}, + {k: []byte("shaman"), v: []byte("horse")}, + {k: []byte("doge"), v: []byte("coin")}, + {k: []byte("dog"), v: []byte("puppy")}, + }) + + var entries []kv + for i := 0; i < 256; i++ { + entries = append(entries, kv{k: testrand.Bytes(32), v: testrand.Bytes(32)}) + } + testTrieCopyOldTrie(t, entries) +} + +func testTrieCopyOldTrie(t *testing.T, entries []kv) { + tr := NewEmpty(nil) + for _, entry := range entries { + tr.Update(entry.k, entry.v) + } + hash := tr.Hash() + + trCpy := tr.Copy() + for _, val := range entries { + if rand.Intn(2) == 0 { + trCpy.Delete(val.k) + } else { + trCpy.Update(val.k, testrand.Bytes(32)) + } + } + for i := 0; i < 10; i++ { + trCpy.Update(testrand.Bytes(32), testrand.Bytes(32)) + } + trCpy.Hash() + trCpy.Commit(false) + + // Traverse the original tree, the changes made on the copy one shouldn't + // affect the old one + for _, entry := range entries { + d, _ := tr.Get(entry.k) + if !bytes.Equal(d, entry.v) { + t.Errorf("Unexpected data, key: %v, want: %v, got: %v", entry.k, entry.v, d) + } + } + if tr.Hash() != hash { + t.Errorf("Hash mismatch: old %v, new %v", hash, tr.Hash()) + } +} + +func TestTrieCopyNewTrie(t *testing.T) { + testTrieCopyNewTrie(t, []kv{ + {k: []byte("do"), v: []byte("verb")}, + {k: []byte("ether"), v: []byte("wookiedoo")}, + {k: []byte("horse"), v: []byte("stallion")}, + {k: []byte("shaman"), v: []byte("horse")}, + {k: []byte("doge"), v: []byte("coin")}, + {k: []byte("dog"), v: []byte("puppy")}, + }) + + var entries []kv + for i := 0; i < 256; i++ { + entries = append(entries, kv{k: testrand.Bytes(32), v: testrand.Bytes(32)}) + } + testTrieCopyNewTrie(t, entries) +} + +func testTrieCopyNewTrie(t *testing.T, entries []kv) { + tr := NewEmpty(nil) + for _, entry := range entries { + tr.Update(entry.k, entry.v) + } + trCpy := tr.Copy() + hash := trCpy.Hash() + + for _, val := range entries { + if rand.Intn(2) == 0 { + tr.Delete(val.k) + } else { + tr.Update(val.k, testrand.Bytes(32)) + } + } + for i := 0; i < 10; i++ { + tr.Update(testrand.Bytes(32), testrand.Bytes(32)) + } + + // Traverse the original tree, the changes made on the copy one shouldn't + // affect the old one + for _, entry := range entries { + d, _ := trCpy.Get(entry.k) + if !bytes.Equal(d, entry.v) { + t.Errorf("Unexpected data, key: %v, want: %v, got: %v", entry.k, entry.v, d) + } + } + if trCpy.Hash() != hash { + t.Errorf("Hash mismatch: old %v, new %v", hash, tr.Hash()) + } +} diff --git a/triedb/database/database.go b/triedb/database/database.go index cd7ec1d9314..8c61ea02939 100644 --- a/triedb/database/database.go +++ b/triedb/database/database.go @@ -27,6 +27,8 @@ type NodeReader interface { // node path and the corresponding node hash. No error will be returned // if the node is not found. // + // The returned node content won't be changed after the call. + // // Don't modify the returned byte slice since it's not deep-copied and // still be referenced by database. Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) From c49aadc4b628c55da9948b0a1f37f487129ce28a Mon Sep 17 00:00:00 2001 From: sashabeton <23243361+sashabeton@users.noreply.github.com> Date: Tue, 25 Mar 2025 15:01:21 +0100 Subject: [PATCH 043/658] internal/ethapi: exclude 7702 authorities from result in eth_createAccessList (#31336) closes https://github.com/ethereum/go-ethereum/issues/31335 --------- Co-authored-by: sashabeton --- eth/tracers/logger/access_list_tracer.go | 12 +++------- internal/ethapi/api.go | 29 +++++++++++++++++++++--- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index e8231461b0c..0d51f405225 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -103,16 +103,10 @@ type AccessListTracer struct { // NewAccessListTracer creates a new tracer that can generate AccessLists. // An optional AccessList can be specified to occupy slots and addresses in // the resulting accesslist. -func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompiles []common.Address) *AccessListTracer { - excl := map[common.Address]struct{}{ - from: {}, to: {}, - } - for _, addr := range precompiles { - excl[addr] = struct{}{} - } +func NewAccessListTracer(acl types.AccessList, addressesToExclude map[common.Address]struct{}) *AccessListTracer { list := newAccessList() for _, al := range acl { - if _, ok := excl[al.Address]; !ok { + if _, ok := addressesToExclude[al.Address]; !ok { list.addAddress(al.Address) } for _, slot := range al.StorageKeys { @@ -120,7 +114,7 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi } } return &AccessListTracer{ - excl: excl, + excl: addressesToExclude, list: list, } } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index e0d5b32622e..203834fe388 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1166,10 +1166,33 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH // Retrieve the precompiles since they don't need to be added to the access list precompiles := vm.ActivePrecompiles(b.ChainConfig().Rules(header.Number, isPostMerge, header.Time)) + // addressesToExclude contains sender, receiver, precompiles and valid authorizations + addressesToExclude := map[common.Address]struct{}{args.from(): {}, to: {}} + for _, addr := range precompiles { + addressesToExclude[addr] = struct{}{} + } + + // Prevent redundant operations if args contain more authorizations than EVM may handle + maxAuthorizations := uint64(*args.Gas) / params.CallNewAccountGas + if uint64(len(args.AuthorizationList)) > maxAuthorizations { + return nil, 0, nil, errors.New("insufficient gas to process all authorizations") + } + + for _, auth := range args.AuthorizationList { + // Duplicating stateTransition.validateAuthorization() logic + if (!auth.ChainID.IsZero() && auth.ChainID.CmpBig(b.ChainConfig().ChainID) != 0) || auth.Nonce+1 < auth.Nonce { + continue + } + + if authority, err := auth.Authority(); err == nil { + addressesToExclude[authority] = struct{}{} + } + } + // Create an initial tracer - prevTracer := logger.NewAccessListTracer(nil, args.from(), to, precompiles) + prevTracer := logger.NewAccessListTracer(nil, addressesToExclude) if args.AccessList != nil { - prevTracer = logger.NewAccessListTracer(*args.AccessList, args.from(), to, precompiles) + prevTracer = logger.NewAccessListTracer(*args.AccessList, addressesToExclude) } for { if err := ctx.Err(); err != nil { @@ -1186,7 +1209,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH msg := args.ToMessage(header.BaseFee, true, true) // Apply the transaction with the access list tracer - tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles) + tracer := logger.NewAccessListTracer(accessList, addressesToExclude) config := vm.Config{Tracer: tracer.Hooks(), NoBaseFee: true} evm := b.GetEVM(ctx, statedb, header, &config, nil) From c1ff2d8ba973f9f7ebfbf45e3c36f8d3299846ba Mon Sep 17 00:00:00 2001 From: Delweng Date: Wed, 26 Mar 2025 12:59:40 +0800 Subject: [PATCH 044/658] core/state: fix double-increment of accountLoaded counter (#31493) --- core/state/statedb.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index efafdc1aa22..e3f5b9e1a0a 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -600,7 +600,6 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject { // Insert into the live set obj := newObject(s, addr, acct) s.setStateObject(obj) - s.AccountLoaded++ return obj } From 5b4a74349372402fac545db7a7f80812a40b1b2b Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Wed, 26 Mar 2025 19:48:04 +0800 Subject: [PATCH 045/658] core/rawdb: remove LES database stats (#31495) This removes DB schema for LES related db entries. LES has been non-functional since the merge. --- core/rawdb/database.go | 16 ---------------- core/rawdb/schema.go | 8 -------- 2 files changed, 24 deletions(-) diff --git a/core/rawdb/database.go b/core/rawdb/database.go index a394f46e0ac..7fca8221552 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -388,10 +388,6 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { verkleTries stat verkleStateLookups stat - // Les statistic - chtTrieNodes stat - bloomTrieNodes stat - // Meta- and unaccounted data metadata stat unaccounted stat @@ -465,16 +461,6 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { case bytes.HasPrefix(key, bloomBitsMetaPrefix) && len(key) < len(bloomBitsMetaPrefix)+8: bloomBits.Add(size) - // LES indexes (deprecated) - case bytes.HasPrefix(key, chtTablePrefix) || - bytes.HasPrefix(key, chtIndexTablePrefix) || - bytes.HasPrefix(key, chtPrefix): // Canonical hash trie - chtTrieNodes.Add(size) - case bytes.HasPrefix(key, bloomTrieTablePrefix) || - bytes.HasPrefix(key, bloomTrieIndexPrefix) || - bytes.HasPrefix(key, bloomTriePrefix): // Bloomtrie sub - bloomTrieNodes.Add(size) - // Verkle trie data is detected, determine the sub-category case bytes.HasPrefix(key, VerklePrefix): remain := key[len(VerklePrefix):] @@ -538,8 +524,6 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { {"Key-Value store", "Beacon sync headers", beaconHeaders.Size(), beaconHeaders.Count()}, {"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()}, {"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()}, - {"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()}, - {"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()}, } // Inspect all registered append-only file store then. ancients, err := inspectFreezers(db) diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 31e80da0793..fa125cecc05 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -144,14 +144,6 @@ var ( // old log index bloomBitsMetaPrefix = []byte("iB") - // LES indexes - chtPrefix = []byte("chtRootV2-") // ChtPrefix + chtNum (uint64 big endian) -> trie root hash - chtTablePrefix = []byte("cht-") - chtIndexTablePrefix = []byte("chtIndexV2-") - bloomTriePrefix = []byte("bltRoot-") // BloomTriePrefix + bloomTrieNum (uint64 big endian) -> trie root hash - bloomTrieTablePrefix = []byte("blt-") - bloomTrieIndexPrefix = []byte("bltIndex-") - preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil) preimageHitsCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil) preimageMissCounter = metrics.NewRegisteredCounter("db/preimage/miss", nil) From a775e68421595d9c3807e68cce7ff2037991a781 Mon Sep 17 00:00:00 2001 From: VolodymyrBg Date: Wed, 26 Mar 2025 13:57:08 +0200 Subject: [PATCH 046/658] eth: downgrade peer removal error to warning level (#31492) --- eth/handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/handler.go b/eth/handler.go index 4c83f5613cf..7179c9980b0 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -403,7 +403,7 @@ func (h *handler) unregisterPeer(id string) { // Abort if the peer does not exist peer := h.peers.peer(id) if peer == nil { - logger.Error("Ethereum peer removal failed", "err", errPeerNotRegistered) + logger.Warn("Ethereum peer removal failed", "err", errPeerNotRegistered) return } // Remove the `eth` peer if it exists From a82303f4e3cedcebe31540a53dde4f24fc93da80 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 26 Mar 2025 16:14:17 +0100 Subject: [PATCH 047/658] accounts/abi: include access-list in gas estimation (#31394) Simple bugfix to include the access-list in the gas-estimation step of the ABI bindings code. --- accounts/abi/bind/v2/base.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/accounts/abi/bind/v2/base.go b/accounts/abi/bind/v2/base.go index a9b667e6977..535c0ed4fd7 100644 --- a/accounts/abi/bind/v2/base.go +++ b/accounts/abi/bind/v2/base.go @@ -383,13 +383,14 @@ func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Ad } } msg := ethereum.CallMsg{ - From: opts.From, - To: contract, - GasPrice: gasPrice, - GasTipCap: gasTipCap, - GasFeeCap: gasFeeCap, - Value: value, - Data: input, + From: opts.From, + To: contract, + GasPrice: gasPrice, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Value: value, + Data: input, + AccessList: opts.AccessList, } return c.transactor.EstimateGas(ensureContext(opts.Context), msg) } From 6143c350ae1ecf3330678be02b4c2745bb6b8134 Mon Sep 17 00:00:00 2001 From: georgehao Date: Thu, 27 Mar 2025 19:22:17 +0800 Subject: [PATCH 048/658] internal/ethapi: CreateAccessList with stateOverrides (#31497) Add support for state overrides in eth_createAccessList. This will make the method consistent with other execution methods. --------- Co-authored-by: Sina Mahmoodi --- internal/ethapi/api.go | 16 ++++++-- internal/ethapi/api_test.go | 73 +++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 203834fe388..36a5df8087b 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1117,12 +1117,13 @@ type accessListResult struct { // CreateAccessList creates an EIP-2930 type AccessList for the given transaction. // Reexec and BlockNrOrHash can be specified to create the accessList on top of a certain state. -func (api *BlockChainAPI) CreateAccessList(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*accessListResult, error) { +// StateOverrides can be used to create the accessList while taking into account state changes from previous transactions. +func (api *BlockChainAPI) CreateAccessList(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, stateOverrides *override.StateOverride) (*accessListResult, error) { bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) if blockNrOrHash != nil { bNrOrHash = *blockNrOrHash } - acl, gasUsed, vmerr, err := AccessList(ctx, api.b, bNrOrHash, args) + acl, gasUsed, vmerr, err := AccessList(ctx, api.b, bNrOrHash, args, stateOverrides) if err != nil { return nil, err } @@ -1136,13 +1137,22 @@ func (api *BlockChainAPI) CreateAccessList(ctx context.Context, args Transaction // AccessList creates an access list for the given transaction. // If the accesslist creation fails an error is returned. // If the transaction itself fails, an vmErr is returned. -func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrHash, args TransactionArgs) (acl types.AccessList, gasUsed uint64, vmErr error, err error) { +func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrHash, args TransactionArgs, stateOverrides *override.StateOverride) (acl types.AccessList, gasUsed uint64, vmErr error, err error) { // Retrieve the execution context db, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if db == nil || err != nil { return nil, 0, nil, err } + // Apply state overrides immediately after StateAndHeaderByNumberOrHash. + // If not applied here, there could be cases where user-specified overrides (e.g., nonce) + // may conflict with default values from the database, leading to inconsistencies. + if stateOverrides != nil { + if err := stateOverrides.Apply(db, nil); err != nil { + return nil, 0, nil, err + } + } + // Ensure any missing fields are filled, extract the recipient and input data if err = args.setFeeDefaults(ctx, b, header); err != nil { return nil, 0, nil, err diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index b086d1a6d51..1ed1a8c8d8e 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -3529,3 +3529,76 @@ func testRPCResponseWithFile(t *testing.T, testid int, result interface{}, rpc s func addressToHash(a common.Address) common.Hash { return common.BytesToHash(a.Bytes()) } + +func TestCreateAccessListWithStateOverrides(t *testing.T) { + // Initialize test backend + genesis := &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{ + common.HexToAddress("0x71562b71999873db5b286df957af199ec94617f7"): {Balance: big.NewInt(1000000000000000000)}, + }, + } + backend := newTestBackend(t, 1, genesis, ethash.NewFaker(), nil) + + // Create a new BlockChainAPI instance + api := NewBlockChainAPI(backend) + + // Create test contract code - a simple storage contract + // + // SPDX-License-Identifier: MIT + // pragma solidity ^0.8.0; + // + // contract SimpleStorage { + // uint256 private value; + // + // function retrieve() public view returns (uint256) { + // return value; + // } + // } + var ( + contractCode = hexutil.Bytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80632e64cec114602d575b600080fd5b60336047565b604051603e91906067565b60405180910390f35b60008054905090565b6000819050919050565b6061816050565b82525050565b6000602082019050607a6000830184605a565b9291505056")) + // Create state overrides with more complete state + contractAddr = common.HexToAddress("0x1234567890123456789012345678901234567890") + nonce = hexutil.Uint64(1) + overrides = &override.StateOverride{ + contractAddr: override.OverrideAccount{ + Code: &contractCode, + Balance: (*hexutil.Big)(big.NewInt(1000000000000000000)), + Nonce: &nonce, + State: map[common.Hash]common.Hash{ + common.Hash{}: common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000002a"), + }, + }, + } + ) + + // Create transaction arguments with gas and value + var ( + from = common.HexToAddress("0x71562b71999873db5b286df957af199ec94617f7") + data = hexutil.Bytes(common.Hex2Bytes("2e64cec1")) // retrieve() + gas = hexutil.Uint64(100000) + args = TransactionArgs{ + From: &from, + To: &contractAddr, + Data: &data, + Gas: &gas, + Value: new(hexutil.Big), + } + ) + // Call CreateAccessList + result, err := api.CreateAccessList(context.Background(), args, nil, overrides) + if err != nil { + t.Fatalf("Failed to create access list: %v", err) + } + if err != nil || result == nil { + t.Fatalf("Failed to create access list: %v", err) + } + require.NotNil(t, result.Accesslist) + + // Verify access list contains the contract address and storage slot + expected := &types.AccessList{{ + Address: contractAddr, + StorageKeys: []common.Hash{{}}, + }} + require.Equal(t, expected, result.Accesslist) +} From 714fa4f2e69a53ad714d6a0a259ab1c8b2032845 Mon Sep 17 00:00:00 2001 From: Delweng Date: Fri, 28 Mar 2025 15:15:13 +0800 Subject: [PATCH 049/658] cmd/geth: update geth subcommand arguments (#31293) --- cmd/geth/chaincmd.go | 35 ++++++++--------- cmd/geth/dbcmd.go | 94 +++++++++++++++++--------------------------- cmd/geth/main.go | 1 - 3 files changed, 53 insertions(+), 77 deletions(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 05c8bc4c7c3..708dd8fb7a3 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -37,7 +37,9 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/era" + "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/urfave/cli/v2" @@ -66,7 +68,7 @@ It expects the genesis file as argument.`, Name: "dumpgenesis", Usage: "Dumps genesis block JSON configuration to stdout", ArgsUsage: "", - Flags: append([]cli.Flag{utils.DataDirFlag}, utils.NetworkFlags...), + Flags: slices.Concat([]cli.Flag{utils.DataDirFlag}, utils.NetworkFlags), Description: ` The dumpgenesis command prints the genesis configuration of the network preset if one is set. Otherwise it prints the genesis from the datadir.`, @@ -78,11 +80,11 @@ if one is set. Otherwise it prints the genesis from the datadir.`, ArgsUsage: " ( ... ) ", Flags: slices.Concat([]cli.Flag{ utils.CacheFlag, - utils.SyncModeFlag, utils.GCModeFlag, utils.SnapshotFlag, utils.CacheDatabaseFlag, utils.CacheGCFlag, + utils.NoCompactionFlag, utils.MetricsEnabledFlag, utils.MetricsEnabledExpensiveFlag, utils.MetricsHTTPFlag, @@ -105,7 +107,11 @@ if one is set. Otherwise it prints the genesis from the datadir.`, utils.LogNoHistoryFlag, utils.LogExportCheckpointsFlag, utils.StateHistoryFlag, - }, utils.DatabaseFlags), + }, utils.DatabaseFlags, debug.Flags), + Before: func(ctx *cli.Context) error { + flags.MigrateGlobalFlags(ctx) + return debug.Setup(ctx) + }, Description: ` The import command allows the import of blocks from an RLP-encoded format. This format can be a single file containing multiple RLP-encoded blocks, or multiple files can be given. @@ -119,10 +125,7 @@ to import successfully.`, Name: "export", Usage: "Export blockchain into file", ArgsUsage: " [ ]", - Flags: slices.Concat([]cli.Flag{ - utils.CacheFlag, - utils.SyncModeFlag, - }, utils.DatabaseFlags), + Flags: slices.Concat([]cli.Flag{utils.CacheFlag}, utils.DatabaseFlags), Description: ` Requires a first argument of the file to write to. Optional second and third arguments control the first and @@ -135,12 +138,7 @@ be gzipped.`, Name: "import-history", Usage: "Import an Era archive", ArgsUsage: "", - Flags: slices.Concat([]cli.Flag{ - utils.TxLookupLimitFlag, - }, - utils.DatabaseFlags, - utils.NetworkFlags, - ), + Flags: slices.Concat([]cli.Flag{utils.TxLookupLimitFlag, utils.TransactionHistoryFlag}, utils.DatabaseFlags, utils.NetworkFlags), Description: ` The import-history command will import blocks and their corresponding receipts from Era archives. @@ -151,7 +149,7 @@ from Era archives. Name: "export-history", Usage: "Export blockchain history to Era archives", ArgsUsage: " ", - Flags: slices.Concat(utils.DatabaseFlags), + Flags: utils.DatabaseFlags, Description: ` The export-history command will export blocks and their corresponding receipts into Era archives. Eras are typically packaged in steps of 8192 blocks. @@ -162,10 +160,7 @@ into Era archives. Eras are typically packaged in steps of 8192 blocks. Name: "import-preimages", Usage: "Import the preimage database from an RLP stream", ArgsUsage: "", - Flags: slices.Concat([]cli.Flag{ - utils.CacheFlag, - utils.SyncModeFlag, - }, utils.DatabaseFlags), + Flags: slices.Concat([]cli.Flag{utils.CacheFlag}, utils.DatabaseFlags), Description: ` The import-preimages command imports hash preimages from an RLP encoded stream. It's deprecated, please use "geth db import" instead. @@ -435,6 +430,10 @@ func importHistory(ctx *cli.Context) error { network = "mainnet" case ctx.Bool(utils.SepoliaFlag.Name): network = "sepolia" + case ctx.Bool(utils.HoleskyFlag.Name): + network = "holesky" + case ctx.Bool(utils.HoodiFlag.Name): + network = "hoodi" } } else { // No network flag set, try to determine network based on files diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index cd41c57c75e..44a52521f04 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -86,12 +86,10 @@ Remove blockchain and state databases`, }, } dbInspectCmd = &cli.Command{ - Action: inspect, - Name: "inspect", - ArgsUsage: " ", - Flags: slices.Concat([]cli.Flag{ - utils.SyncModeFlag, - }, utils.NetworkFlags, utils.DatabaseFlags), + Action: inspect, + Name: "inspect", + ArgsUsage: " ", + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Usage: "Inspect the storage size for each type of data in the database", Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`, } @@ -109,16 +107,13 @@ a data corruption.`, Action: dbStats, Name: "stats", Usage: "Print leveldb statistics", - Flags: slices.Concat([]cli.Flag{ - utils.SyncModeFlag, - }, utils.NetworkFlags, utils.DatabaseFlags), + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), } dbCompactCmd = &cli.Command{ Action: dbCompact, Name: "compact", Usage: "Compact leveldb database. WARNING: May take a very long time", Flags: slices.Concat([]cli.Flag{ - utils.SyncModeFlag, utils.CacheFlag, utils.CacheDatabaseFlag, }, utils.NetworkFlags, utils.DatabaseFlags), @@ -127,13 +122,11 @@ WARNING: This operation may take a very long time to finish, and may cause datab corruption if it is aborted during execution'!`, } dbGetCmd = &cli.Command{ - Action: dbGet, - Name: "get", - Usage: "Show the value of a database key", - ArgsUsage: "", - Flags: slices.Concat([]cli.Flag{ - utils.SyncModeFlag, - }, utils.NetworkFlags, utils.DatabaseFlags), + Action: dbGet, + Name: "get", + Usage: "Show the value of a database key", + ArgsUsage: "", + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: "This command looks up the specified database key from the database.", } dbDeleteCmd = &cli.Command{ @@ -141,9 +134,7 @@ corruption if it is aborted during execution'!`, Name: "delete", Usage: "Delete a database key (WARNING: may corrupt your database)", ArgsUsage: "", - Flags: slices.Concat([]cli.Flag{ - utils.SyncModeFlag, - }, utils.NetworkFlags, utils.DatabaseFlags), + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: `This command deletes the specified database key from the database. WARNING: This is a low-level operation which may cause database corruption!`, } @@ -152,59 +143,47 @@ WARNING: This is a low-level operation which may cause database corruption!`, Name: "put", Usage: "Set the value of a database key (WARNING: may corrupt your database)", ArgsUsage: " ", - Flags: slices.Concat([]cli.Flag{ - utils.SyncModeFlag, - }, utils.NetworkFlags, utils.DatabaseFlags), + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: `This command sets a given database key to the given value. WARNING: This is a low-level operation which may cause database corruption!`, } dbGetSlotsCmd = &cli.Command{ - Action: dbDumpTrie, - Name: "dumptrie", - Usage: "Show the storage key/values of a given storage trie", - ArgsUsage: " ", - Flags: slices.Concat([]cli.Flag{ - utils.SyncModeFlag, - }, utils.NetworkFlags, utils.DatabaseFlags), + Action: dbDumpTrie, + Name: "dumptrie", + Usage: "Show the storage key/values of a given storage trie", + ArgsUsage: " ", + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: "This command looks up the specified database key from the database.", } dbDumpFreezerIndex = &cli.Command{ - Action: freezerInspect, - Name: "freezer-index", - Usage: "Dump out the index of a specific freezer table", - ArgsUsage: " ", - Flags: slices.Concat([]cli.Flag{ - utils.SyncModeFlag, - }, utils.NetworkFlags, utils.DatabaseFlags), + Action: freezerInspect, + Name: "freezer-index", + Usage: "Dump out the index of a specific freezer table", + ArgsUsage: " ", + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: "This command displays information about the freezer index.", } dbImportCmd = &cli.Command{ - Action: importLDBdata, - Name: "import", - Usage: "Imports leveldb-data from an exported RLP dump.", - ArgsUsage: " has .gz suffix, gzip compression will be used.", - ArgsUsage: " ", - Flags: slices.Concat([]cli.Flag{ - utils.SyncModeFlag, - }, utils.NetworkFlags, utils.DatabaseFlags), + Action: exportChaindata, + Name: "export", + Usage: "Exports the chain data into an RLP dump. If the has .gz suffix, gzip compression will be used.", + ArgsUsage: " ", + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.", } dbMetadataCmd = &cli.Command{ - Action: showMetaData, - Name: "metadata", - Usage: "Shows metadata about the chain status.", - Flags: slices.Concat([]cli.Flag{ - utils.SyncModeFlag, - }, utils.NetworkFlags, utils.DatabaseFlags), + Action: showMetaData, + Name: "metadata", + Usage: "Shows metadata about the chain status.", + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: "Shows metadata about the chain status.", } dbInspectHistoryCmd = &cli.Command{ @@ -213,7 +192,6 @@ WARNING: This is a low-level operation which may cause database corruption!`, Usage: "Inspect the state history within block range", ArgsUsage: "
[OPTIONAL ]", Flags: slices.Concat([]cli.Flag{ - utils.SyncModeFlag, &cli.Uint64Flag{ Name: "start", Usage: "block number of the range start, zero means earliest history", diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 9c0c0d9dfcc..cd74fb7b6a5 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -142,7 +142,6 @@ var ( utils.VMTraceJsonConfigFlag, utils.NetworkIdFlag, utils.EthStatsURLFlag, - utils.NoCompactionFlag, utils.GpoBlocksFlag, utils.GpoPercentileFlag, utils.GpoMaxGasPriceFlag, From 141968a48b45bf343c5921057eca158df5d3c96e Mon Sep 17 00:00:00 2001 From: rekyyang <511965710@qq.com> Date: Fri, 28 Mar 2025 15:16:37 +0800 Subject: [PATCH 050/658] core/txpool/legacypool: fix data race in checkDelegationLimit (#31475) --- core/txpool/legacypool/legacypool.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 7a0095a5ad1..dafd1858367 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -618,7 +618,7 @@ func (pool *LegacyPool) checkDelegationLimit(tx *types.Transaction) error { from, _ := types.Sender(pool.signer, tx) // validated // Short circuit if the sender has neither delegation nor pending delegation. - if pool.currentState.GetCodeHash(from) == types.EmptyCodeHash && len(pool.all.auths[from]) == 0 { + if pool.currentState.GetCodeHash(from) == types.EmptyCodeHash && pool.all.delegationTxsCount(from) == 0 { return nil } pending := pool.pending[from] @@ -1849,6 +1849,13 @@ func (t *lookup) removeAuthorities(tx *types.Transaction) { } } +// delegationTxsCount returns the number of pending authorizations for the specified address. +func (t *lookup) delegationTxsCount(addr common.Address) int { + t.lock.RLock() + defer t.lock.RUnlock() + return len(t.auths[addr]) +} + // numSlots calculates the number of slots needed for a single transaction. func numSlots(tx *types.Transaction) int { return int((tx.Size() + txSlotSize - 1) / txSlotSize) From 32f36a674990b5e2316321415d595c67b2f00b4f Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 28 Mar 2025 19:32:24 +0800 Subject: [PATCH 051/658] core/txpool: fix nonce assignment in local tracker (#31496) Fixes #31494 --- core/txpool/locals/tx_tracker_test.go | 39 +++++++++++++++++++++++++++ core/txpool/txpool.go | 13 +++++++-- eth/api_backend.go | 2 +- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/core/txpool/locals/tx_tracker_test.go b/core/txpool/locals/tx_tracker_test.go index cb6b9b34535..5585589b6cd 100644 --- a/core/txpool/locals/tx_tracker_test.go +++ b/core/txpool/locals/tx_tracker_test.go @@ -108,6 +108,19 @@ func (env *testEnv) makeTx(nonce uint64, gasPrice *big.Int) *types.Transaction { return tx } +func (env *testEnv) makeTxs(n int) []*types.Transaction { + head := env.chain.CurrentHeader() + state, _ := env.chain.StateAt(head.Root) + nonce := state.GetNonce(address) + + var txs []*types.Transaction + for i := 0; i < n; i++ { + tx, _ := types.SignTx(types.NewTransaction(nonce+uint64(i), common.Address{0x00}, big.NewInt(1000), params.TxGas, big.NewInt(params.GWei), nil), signer, key) + txs = append(txs, tx) + } + return txs +} + func (env *testEnv) commit() { head := env.chain.CurrentBlock() block := env.chain.GetBlock(head.Hash(), head.Number.Uint64()) @@ -177,3 +190,29 @@ func TestRejectInvalids(t *testing.T) { } } } + +func TestResubmit(t *testing.T) { + env := newTestEnv(t, 10, 0, "") + defer env.close() + + txs := env.makeTxs(10) + txsA := txs[:len(txs)/2] + txsB := txs[len(txs)/2:] + env.pool.Add(txsA, true) + pending, queued := env.pool.ContentFrom(address) + if len(pending) != len(txsA) || len(queued) != 0 { + t.Fatalf("Unexpected txpool content: %d, %d", len(pending), len(queued)) + } + env.tracker.TrackAll(txs) + + resubmit, all := env.tracker.recheck(true) + if len(resubmit) != len(txsB) { + t.Fatalf("Unexpected transactions to resubmit, got: %d, want: %d", len(resubmit), len(txsB)) + } + if len(all) == 0 || len(all[address]) == 0 { + t.Fatalf("Unexpected transactions being tracked, got: %d, want: %d", 0, len(txs)) + } + if len(all[address]) != len(txs) { + t.Fatalf("Unexpected transactions being tracked, got: %d, want: %d", len(all[address]), len(txs)) + } +} diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index c5f7718057b..649c5d1a785 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -466,9 +466,9 @@ func (p *TxPool) SubscribeTransactions(ch chan<- core.NewTxsEvent, reorgs bool) return p.subs.Track(event.JoinSubscriptions(subs...)) } -// Nonce returns the next nonce of an account, with all transactions executable +// PoolNonce returns the next nonce of an account, with all transactions executable // by the pool already applied on top. -func (p *TxPool) Nonce(addr common.Address) uint64 { +func (p *TxPool) PoolNonce(addr common.Address) uint64 { // Since (for now) accounts are unique to subpools, only one pool will have // (at max) a non-state nonce. To avoid stateful lookups, just return the // highest nonce for now. @@ -481,6 +481,15 @@ func (p *TxPool) Nonce(addr common.Address) uint64 { return nonce } +// Nonce returns the next nonce of an account at the current chain head. Unlike +// PoolNonce, this function does not account for pending executable transactions. +func (p *TxPool) Nonce(addr common.Address) uint64 { + p.stateLock.RLock() + defer p.stateLock.RUnlock() + + return p.state.GetNonce(addr) +} + // Stats retrieves the current pool stats, namely the number of pending and the // number of queued (non-executable) transactions. func (p *TxPool) Stats() (int, int) { diff --git a/eth/api_backend.go b/eth/api_backend.go index b08e38afe5d..c8c5ca707f9 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -327,7 +327,7 @@ func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) } func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { - return b.eth.txPool.Nonce(addr), nil + return b.eth.txPool.PoolNonce(addr), nil } func (b *EthAPIBackend) Stats() (runnable int, blocked int) { From c8a9a9c0917dd57d077a79044e65dbbdd421458b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Fri, 28 Mar 2025 16:17:28 +0100 Subject: [PATCH 052/658] core/filtermaps: revert to unindexed mode in case of indexing error (#31500) This PR changes log indexer error handling so that if an indexing error happens then it disables the indexer and reverts to unindexed more without resetting the database (except in case of a failed database init). Resetting the database on the first error would probably be overkill as a client update might fix this without having to reindex the entire history. It would also make debugging very hard. On the other hand, these errors do not resolve themselves automatically so constantly retrying makes no sense either. With these changes a new attempt to resume indexing is made every time the client is restarted. The PR also fixes https://github.com/ethereum/go-ethereum/issues/31491 which originated from the tail indexer trying to resume processing a failed map renderer. --------- Co-authored-by: Felix Lange --- core/filtermaps/chain_view.go | 6 ++- core/filtermaps/indexer.go | 97 ++++++++++++++++++++++------------- 2 files changed, 65 insertions(+), 38 deletions(-) diff --git a/core/filtermaps/chain_view.go b/core/filtermaps/chain_view.go index bc0de47ded4..a8cf53b1c0f 100644 --- a/core/filtermaps/chain_view.go +++ b/core/filtermaps/chain_view.go @@ -83,7 +83,11 @@ func (cv *ChainView) getReceipts(number uint64) types.Receipts { if number > cv.headNumber { panic("invalid block number") } - return cv.chain.GetReceiptsByHash(cv.blockHash(number)) + blockHash := cv.blockHash(number) + if blockHash == (common.Hash{}) { + log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber) + } + return cv.chain.GetReceiptsByHash(blockHash) } // limitedView returns a new chain view that is a truncated version of the parent view. diff --git a/core/filtermaps/indexer.go b/core/filtermaps/indexer.go index 026b3b4f38a..6732dc85ea2 100644 --- a/core/filtermaps/indexer.go +++ b/core/filtermaps/indexer.go @@ -17,6 +17,7 @@ package filtermaps import ( + "errors" "math" "time" @@ -49,18 +50,15 @@ func (f *FilterMaps) indexerLoop() { continue } if err := f.init(); err != nil { - log.Error("Error initializing log index; reverting to unindexed mode", "error", err) - f.reset() - f.disabled = true - close(f.disabledCh) + f.disableForError("initialization", err) + f.reset() // remove broken index from DB return } } if !f.targetHeadIndexed() { - if !f.tryIndexHead() { - // either shutdown or unexpected error; in the latter case ensure - // that proper shutdown is still possible. - f.processSingleEvent(true) + if err := f.tryIndexHead(); err != nil && err != errChainUpdate { + f.disableForError("head rendering", err) + return } } else { if f.finalBlock != f.lastFinal { @@ -69,13 +67,36 @@ func (f *FilterMaps) indexerLoop() { } f.lastFinal = f.finalBlock } - if f.tryIndexTail() && f.tryUnindexTail() { - f.waitForNewHead() + if done, err := f.tryIndexTail(); err != nil { + f.disableForError("tail rendering", err) + return + } else if !done { + continue + } + if done, err := f.tryUnindexTail(); err != nil { + f.disableForError("tail unindexing", err) + return + } else if !done { + continue } + // tail indexing/unindexing is done; if head is also indexed then + // wait here until there is a new head + f.waitForNewHead() } } } +// disableForError is called when the indexer encounters a database error, for example a +// missing receipt. We can't continue operating when the database is broken, so the +// indexer goes into disabled state. +// Note that the partial index is left in disk; maybe a client update can fix the +// issue without reindexing. +func (f *FilterMaps) disableForError(op string, err error) { + log.Error("Log index "+op+" failed, reverting to unindexed mode", "error", err) + f.disabled = true + close(f.disabledCh) +} + type targetUpdate struct { targetView *ChainView historyCutoff, finalBlock uint64 @@ -116,14 +137,15 @@ func (f *FilterMaps) SetBlockProcessing(blockProcessing bool) { // WaitIdle blocks until the indexer is in an idle state while synced up to the // latest targetView. func (f *FilterMaps) WaitIdle() { - if f.disabled { - f.closeWg.Wait() - return - } for { ch := make(chan bool) - f.waitIdleCh <- ch - if <-ch { + select { + case f.waitIdleCh <- ch: + if <-ch { + return + } + case <-f.disabledCh: + f.closeWg.Wait() return } } @@ -196,16 +218,16 @@ func (f *FilterMaps) setTarget(target targetUpdate) { f.finalBlock = target.finalBlock } -// tryIndexHead tries to render head maps according to the current targetView -// and returns true if successful. -func (f *FilterMaps) tryIndexHead() bool { +// tryIndexHead tries to render head maps according to the current targetView. +// Should be called when targetHeadIndexed returns false. If this function +// returns no error then either stop is true or head indexing is finished. +func (f *FilterMaps) tryIndexHead() error { headRenderer, err := f.renderMapsBefore(math.MaxUint32) if err != nil { - log.Error("Error creating log index head renderer", "error", err) - return false + return err } if headRenderer == nil { - return true + return errors.New("head indexer has nothing to do") // tryIndexHead should be called when head is not indexed } if !f.startedHeadIndex { f.lastLogHeadIndex = time.Now() @@ -230,8 +252,7 @@ func (f *FilterMaps) tryIndexHead() bool { f.lastLogHeadIndex = time.Now() } }); err != nil { - log.Error("Log index head rendering failed", "error", err) - return false + return err } if f.loggedHeadIndex && f.indexedRange.hasIndexedBlocks() { log.Info("Log index head rendering finished", @@ -240,7 +261,7 @@ func (f *FilterMaps) tryIndexHead() bool { "elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt))) } f.loggedHeadIndex, f.startedHeadIndex = false, false - return true + return nil } // tryIndexTail tries to render tail epochs until the tail target block is @@ -248,7 +269,7 @@ func (f *FilterMaps) tryIndexHead() bool { // Note that tail indexing is only started if the log index head is fully // rendered according to targetView and is suspended as soon as the targetView // is changed. -func (f *FilterMaps) tryIndexTail() bool { +func (f *FilterMaps) tryIndexTail() (bool, error) { for { firstEpoch := f.indexedRange.maps.First() >> f.logMapsPerEpoch if firstEpoch == 0 || !f.needTailEpoch(firstEpoch-1) { @@ -256,7 +277,7 @@ func (f *FilterMaps) tryIndexTail() bool { } f.processEvents() if f.stop || !f.targetHeadIndexed() { - return false + return false, nil } // resume process if tail rendering was interrupted because of head rendering tailRenderer := f.tailRenderer @@ -268,8 +289,7 @@ func (f *FilterMaps) tryIndexTail() bool { var err error tailRenderer, err = f.renderMapsBefore(f.indexedRange.maps.First()) if err != nil { - log.Error("Error creating log index tail renderer", "error", err) - return false + return false, err } } if tailRenderer == nil { @@ -302,13 +322,16 @@ func (f *FilterMaps) tryIndexTail() bool { f.lastLogTailIndex = time.Now() } }) - if err != nil && f.needTailEpoch(firstEpoch-1) { + 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) + return true, nil + } + if err != nil { + return false, err } if !done { f.tailRenderer = tailRenderer // only keep tail renderer if interrupted by stopCb - return false + return false, nil } } if f.loggedTailIndex && f.indexedRange.hasIndexedBlocks() { @@ -318,14 +341,14 @@ func (f *FilterMaps) tryIndexTail() bool { "elapsed", common.PrettyDuration(time.Since(f.startedTailIndexAt))) f.loggedTailIndex = false } - return true + return true, nil } // tryUnindexTail removes entire epochs of log index data as long as the first // fully indexed block is at least as old as the tail target. // Note that unindexing is very quick as it only removes continuous ranges of // data from the database and is also called while running head indexing. -func (f *FilterMaps) tryUnindexTail() bool { +func (f *FilterMaps) tryUnindexTail() (bool, error) { for { firstEpoch := (f.indexedRange.maps.First() - f.indexedRange.tailPartialEpoch) >> f.logMapsPerEpoch if f.needTailEpoch(firstEpoch) { @@ -333,7 +356,7 @@ func (f *FilterMaps) tryUnindexTail() bool { } f.processEvents() if f.stop { - return false + return false, nil } if !f.startedTailUnindex { f.startedTailUnindexAt = time.Now() @@ -343,7 +366,7 @@ func (f *FilterMaps) tryUnindexTail() bool { } if err := f.deleteTailEpoch(firstEpoch); err != nil { log.Error("Log index tail epoch unindexing failed", "error", err) - return false + return false, err } } if f.startedTailUnindex && f.indexedRange.hasIndexedBlocks() { @@ -354,7 +377,7 @@ func (f *FilterMaps) tryUnindexTail() bool { "elapsed", common.PrettyDuration(time.Since(f.startedTailUnindexAt))) f.startedTailUnindex = false } - return true + return true, nil } // needTailEpoch returns true if the given tail epoch needs to be kept From ffa315f7460b6f050e89b63ca63876d974fb19bf Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 31 Mar 2025 09:49:19 +0200 Subject: [PATCH 053/658] .gitignore: ignore binaries (#31531) Ignores all hand-built binaries (built with go build, everything built with make is already ignored) --- .gitignore | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.gitignore b/.gitignore index 7000fedd25c..269455db7a7 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,16 @@ profile.cov .vscode tests/spec-tests/ + +# binaries +cmd/abidump/abidump +cmd/abigen/abigen +cmd/blsync/blsync +cmd/clef/clef +cmd/devp2p/devp2p +cmd/era/era +cmd/ethkey/ethkey +cmd/evm/evm +cmd/geth/geth +cmd/rlpdump/rlpdump +cmd/workload/workload \ No newline at end of file From 14d576c002309e38864f9afd95e7305e35a68035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Mon, 31 Mar 2025 14:47:56 +0200 Subject: [PATCH 054/658] core/filtermaps: hashdb safe delete range (#31525) This PR adds `rawdb.SafeDeleteRange` and uses it for range deletion in `core/filtermaps`. This includes deleting the old bloombits database, resetting the log index database and removing index data for unindexed tail epochs (which previously weren't properly implemented for the fallback case). `SafeDeleteRange` either calls `ethdb.DeleteRange` if the node uses the new path based state scheme or uses an iterator based fallback method that safely skips trie nodes in the range if the old hash based state scheme is used. Note that `ethdb.DeleteRange` also has its own iterator based fallback implementation in `ethdb/leveldb`. If a path based state scheme is used and the backing db is pebble (as it is on the majority of new nodes) then `rawdb.SafeDeleteRange` uses the fast native range delete. Also note that `rawdb.SafeDeleteRange` has different semantics from `ethdb.DeleteRange`, it does not automatically return if the operation takes a long time. Instead it receives a `stopCallback` that can interrupt the process if necessary. This is because in the safe mode potentially a lot of entries are iterated without being deleted (this is definitely the case when deleting the old bloombits database which has a single byte prefix) and therefore restarting the process every time a fixed number of entries have been iterated would result in a quadratic run time in the number of skipped entries. When running in safe mode, unindexing an epoch takes about a second, removing bloombits takes around 10s while resetting a full log index might take a few minutes. If a range delete operation takes a significant amount of time then log messages are printed. Also, any range delete operation can be interrupted by shutdown (tail uinindexing can also be interrupted by head indexing, similarly to how tail indexing works). If the last unindexed epoch might have "dirty" index data left then the indexed map range points to the first valid epoch and `cleanedEpochsBefore` points to the previous, potentially dirty one. At startup it is always assumed that the epoch before the first fully indexed one might be dirty. New tail maps are never rendered and also no further maps are unindexed before the previous unindexing is properly cleaned up. --------- Co-authored-by: Gary Rong Co-authored-by: Felix Lange --- core/filtermaps/filtermaps.go | 210 ++++++++++++++++++++------------ core/filtermaps/indexer.go | 32 ++--- core/rawdb/accessors_indexes.go | 38 +++--- core/rawdb/database.go | 73 +++++++++++ eth/backend.go | 7 +- ethdb/database.go | 9 +- ethdb/leveldb/leveldb.go | 4 +- 7 files changed, 256 insertions(+), 117 deletions(-) diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index db7ab0a4260..5722f17daa3 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -29,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/ethdb/leveldb" "github.com/ethereum/go-ethereum/log" ) @@ -59,6 +58,7 @@ type FilterMaps struct { closeCh chan struct{} closeWg sync.WaitGroup history uint64 + hashScheme bool // use hashdb-safe delete range method exportFileName string Params @@ -67,10 +67,11 @@ type FilterMaps struct { // fields written by the indexer and read by matcher backend. Indexer can // read them without a lock and write them under indexLock write lock. // Matcher backend can read them under indexLock read lock. - indexLock sync.RWMutex - indexedRange filterMapsRange - indexedView *ChainView // always consistent with the log index - hasTempRange bool + indexLock sync.RWMutex + indexedRange filterMapsRange + cleanedEpochsBefore uint32 // all unindexed data cleaned before this point + indexedView *ChainView // always consistent with the log index + hasTempRange bool // also accessed by indexer and matcher backend but no locking needed. filterMapCache *lru.Cache[uint32, filterMap] @@ -180,6 +181,10 @@ type Config struct { // This option enables the checkpoint JSON file generator. // If set, the given file will be updated with checkpoint information. ExportFileName string + + // expect trie nodes of hash based state scheme in the filtermaps key range; + // use safe iterator based implementation of DeleteRange that skips them + HashScheme bool } // NewFilterMaps creates a new FilterMaps and starts the indexer. @@ -197,6 +202,7 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f blockProcessingCh: make(chan bool, 1), history: config.History, disabled: config.Disabled, + hashScheme: config.HashScheme, disabledCh: make(chan struct{}), exportFileName: config.ExportFileName, Params: params, @@ -208,15 +214,17 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f maps: common.NewRange(rs.MapsFirst, rs.MapsAfterLast-rs.MapsFirst), tailPartialEpoch: rs.TailPartialEpoch, }, - historyCutoff: historyCutoff, - finalBlock: finalBlock, - matcherSyncCh: make(chan *FilterMapsMatcherBackend), - matchers: make(map[*FilterMapsMatcherBackend]struct{}), - filterMapCache: lru.NewCache[uint32, filterMap](cachedFilterMaps), - lastBlockCache: lru.NewCache[uint32, lastBlockOfMap](cachedLastBlocks), - lvPointerCache: lru.NewCache[uint64, uint64](cachedLvPointers), - baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows), - renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots), + // deleting last unindexed epoch might have been interrupted by shutdown + cleanedEpochsBefore: max(rs.MapsFirst>>params.logMapsPerEpoch, 1) - 1, + historyCutoff: historyCutoff, + finalBlock: finalBlock, + matcherSyncCh: make(chan *FilterMapsMatcherBackend), + matchers: make(map[*FilterMapsMatcherBackend]struct{}), + filterMapCache: lru.NewCache[uint32, filterMap](cachedFilterMaps), + lastBlockCache: lru.NewCache[uint32, lastBlockOfMap](cachedLastBlocks), + lvPointerCache: lru.NewCache[uint64, uint64](cachedLvPointers), + baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows), + renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots), } // Set initial indexer target. @@ -301,14 +309,24 @@ func (f *FilterMaps) reset() { // deleting the range first ensures that resetDb will be called again at next // startup and any leftover data will be removed even if it cannot finish now. rawdb.DeleteFilterMapsRange(f.db) - f.safeDeleteRange(rawdb.DeleteFilterMapsDb, "Resetting log index database") + f.safeDeleteWithLogs(rawdb.DeleteFilterMapsDb, "Resetting log index database", f.isShuttingDown) +} + +// isShuttingDown returns true if FilterMaps is shutting down. +func (f *FilterMaps) isShuttingDown() bool { + select { + case <-f.closeCh: + return true + default: + return false + } } // init initializes an empty log index according to the current targetView. func (f *FilterMaps) init() error { // ensure that there is no remaining data in the filter maps key range - if !f.safeDeleteRange(rawdb.DeleteFilterMapsDb, "Resetting log index database") { - return errors.New("could not reset log index database") + if err := f.safeDeleteWithLogs(rawdb.DeleteFilterMapsDb, "Resetting log index database", f.isShuttingDown); err != nil { + return err } f.indexLock.Lock() @@ -358,38 +376,37 @@ func (f *FilterMaps) init() error { // removeBloomBits removes old bloom bits data from the database. func (f *FilterMaps) removeBloomBits() { - f.safeDeleteRange(rawdb.DeleteBloomBitsDb, "Removing old bloom bits database") + f.safeDeleteWithLogs(rawdb.DeleteBloomBitsDb, "Removing old bloom bits database", f.isShuttingDown) f.closeWg.Done() } -// safeDeleteRange calls the specified database range deleter function -// repeatedly as long as it returns leveldb.ErrTooManyKeys. -// This wrapper is necessary because of the leveldb fallback implementation -// of DeleteRange. -func (f *FilterMaps) safeDeleteRange(removeFn func(ethdb.KeyValueRangeDeleter) error, action string) bool { - start := time.Now() - var retry bool - for { - err := removeFn(f.db) - if err == nil { - if retry { - log.Info(action+" finished", "elapsed", time.Since(start)) - } - return true - } - if err != leveldb.ErrTooManyKeys { - log.Error(action+" failed", "error", err) - return false +// safeDeleteWithLogs is a wrapper for a function that performs a safe range +// delete operation using rawdb.SafeDeleteRange. It emits log messages if the +// process takes long enough to call the stop callback. +func (f *FilterMaps) safeDeleteWithLogs(deleteFn func(db ethdb.KeyValueStore, hashScheme bool, stopCb func(bool) bool) error, action string, stopCb func() bool) error { + var ( + start = time.Now() + logPrinted bool + lastLogPrinted = start + ) + switch err := deleteFn(f.db, f.hashScheme, func(deleted bool) bool { + if deleted && !logPrinted || time.Since(lastLogPrinted) > time.Second*10 { + log.Info(action+" in progress...", "elapsed", common.PrettyDuration(time.Since(start))) + logPrinted, lastLogPrinted = true, time.Now() } - select { - case <-f.closeCh: - return false - default: - } - if !retry { - log.Info(action+" in progress...", "elapsed", time.Since(start)) - retry = true + return stopCb() + }); { + case err == nil: + if logPrinted { + log.Info(action+" finished", "elapsed", common.PrettyDuration(time.Since(start))) } + return nil + case errors.Is(err, rawdb.ErrDeleteRangeInterrupted): + log.Warn(action+" interrupted", "elapsed", common.PrettyDuration(time.Since(start))) + return err + default: + log.Error(action+" failed", "error", err) + return err } } @@ -658,54 +675,97 @@ func (f *FilterMaps) deleteLastBlockOfMap(batch ethdb.Batch, mapIndex uint32) { rawdb.DeleteFilterMapLastBlock(batch, mapIndex) } -// deleteTailEpoch deletes index data from the earliest, either fully or partially -// indexed epoch. The last block pointer for the last map of the epoch and the -// corresponding block log value pointer are retained as these are always assumed -// to be available for each epoch. -func (f *FilterMaps) deleteTailEpoch(epoch uint32) error { +// deleteTailEpoch deletes index data from the specified epoch. The last block +// pointer for the last map of the epoch and the corresponding block log value +// pointer are retained as these are always assumed to be available for each +// epoch as boundary markers. +// The function returns true if all index data related to the epoch (except for +// the boundary markers) has been fully removed. +func (f *FilterMaps) deleteTailEpoch(epoch uint32) (bool, error) { f.indexLock.Lock() defer f.indexLock.Unlock() + // determine epoch boundaries firstMap := epoch << f.logMapsPerEpoch lastBlock, _, err := f.getLastBlockOfMap(firstMap + f.mapsPerEpoch - 1) if err != nil { - return fmt.Errorf("failed to retrieve last block of deleted epoch %d: %v", epoch, err) + return false, fmt.Errorf("failed to retrieve last block of deleted epoch %d: %v", epoch, err) } var firstBlock uint64 if epoch > 0 { firstBlock, _, err = f.getLastBlockOfMap(firstMap - 1) if err != nil { - return fmt.Errorf("failed to retrieve last block before deleted epoch %d: %v", epoch, err) + return false, fmt.Errorf("failed to retrieve last block before deleted epoch %d: %v", epoch, err) } firstBlock++ } - fmr := f.indexedRange - if f.indexedRange.maps.First() == firstMap && - f.indexedRange.maps.AfterLast() > firstMap+f.mapsPerEpoch && - f.indexedRange.tailPartialEpoch == 0 { - fmr.maps.SetFirst(firstMap + f.mapsPerEpoch) - fmr.blocks.SetFirst(lastBlock + 1) - } else if f.indexedRange.maps.First() == firstMap+f.mapsPerEpoch { + // update rendered range if necessary + var ( + fmr = f.indexedRange + firstEpoch = f.indexedRange.maps.First() >> f.logMapsPerEpoch + afterLastEpoch = (f.indexedRange.maps.AfterLast() + f.mapsPerEpoch - 1) >> f.logMapsPerEpoch + ) + if f.indexedRange.tailPartialEpoch != 0 && firstEpoch > 0 { + firstEpoch-- + } + switch { + case epoch < firstEpoch: + // cleanup of already unindexed epoch; range not affected + case epoch == firstEpoch && epoch+1 < afterLastEpoch: + // first fully or partially rendered epoch and there is at least one + // rendered map in the next epoch; remove from indexed range fmr.tailPartialEpoch = 0 + fmr.maps.SetFirst((epoch + 1) << f.logMapsPerEpoch) + fmr.blocks.SetFirst(lastBlock + 1) + f.setRange(f.db, f.indexedView, fmr, false) + default: + // cannot be cleaned or unindexed; return with error + return false, errors.New("invalid tail epoch number") + } + // remove index data + if err := f.safeDeleteWithLogs(func(db ethdb.KeyValueStore, hashScheme bool, stopCb func(bool) bool) error { + first := f.mapRowIndex(firstMap, 0) + count := f.mapRowIndex(firstMap+f.mapsPerEpoch, 0) - first + if err := rawdb.DeleteFilterMapRows(f.db, common.NewRange(first, count), hashScheme, stopCb); err != nil { + return err + } + for mapIndex := firstMap; mapIndex < firstMap+f.mapsPerEpoch; mapIndex++ { + f.filterMapCache.Remove(mapIndex) + } + delMapRange := common.NewRange(firstMap, f.mapsPerEpoch-1) // keep last entry + if err := rawdb.DeleteFilterMapLastBlocks(f.db, delMapRange, hashScheme, stopCb); err != nil { + return err + } + for mapIndex := firstMap; mapIndex < firstMap+f.mapsPerEpoch-1; mapIndex++ { + f.lastBlockCache.Remove(mapIndex) + } + delBlockRange := common.NewRange(firstBlock, lastBlock-firstBlock) // keep last entry + if err := rawdb.DeleteBlockLvPointers(f.db, delBlockRange, hashScheme, stopCb); err != nil { + return err + } + for blockNumber := firstBlock; blockNumber < lastBlock; blockNumber++ { + f.lvPointerCache.Remove(blockNumber) + } + return nil + }, fmt.Sprintf("Deleting tail epoch #%d", epoch), func() bool { + f.processEvents() + return f.stop || !f.targetHeadIndexed() + }); err == nil { + // everything removed; mark as cleaned and report success + if f.cleanedEpochsBefore == epoch { + f.cleanedEpochsBefore = epoch + 1 + } + return true, nil } else { - return errors.New("invalid tail epoch number") - } - f.setRange(f.db, f.indexedView, fmr, false) - 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, 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, common.NewRange(firstBlock, lastBlock-firstBlock)) // keep last enrty - for blockNumber := firstBlock; blockNumber < lastBlock; blockNumber++ { - f.lvPointerCache.Remove(blockNumber) + // more data left in epoch range; mark as dirty and report unfinished + if f.cleanedEpochsBefore > epoch { + f.cleanedEpochsBefore = epoch + } + if errors.Is(err, rawdb.ErrDeleteRangeInterrupted) { + return false, nil + } + return false, err } - return nil } // exportCheckpoints exports epoch checkpoints in the format used by checkpoints.go. diff --git a/core/filtermaps/indexer.go b/core/filtermaps/indexer.go index 6732dc85ea2..9a5424da4aa 100644 --- a/core/filtermaps/indexer.go +++ b/core/filtermaps/indexer.go @@ -67,14 +67,17 @@ func (f *FilterMaps) indexerLoop() { } f.lastFinal = f.finalBlock } - if done, err := f.tryIndexTail(); err != nil { - f.disableForError("tail rendering", err) + // always attempt unindexing before indexing the tail in order to + // ensure that a potentially dirty previously unindexed epoch is + // always cleaned up before any new maps are rendered. + if done, err := f.tryUnindexTail(); err != nil { + f.disableForError("tail unindexing", err) return } else if !done { continue } - if done, err := f.tryUnindexTail(); err != nil { - f.disableForError("tail unindexing", err) + if done, err := f.tryIndexTail(); err != nil { + f.disableForError("tail rendering", err) return } else if !done { continue @@ -349,25 +352,24 @@ func (f *FilterMaps) tryIndexTail() (bool, error) { // Note that unindexing is very quick as it only removes continuous ranges of // data from the database and is also called while running head indexing. func (f *FilterMaps) tryUnindexTail() (bool, error) { - for { - firstEpoch := (f.indexedRange.maps.First() - f.indexedRange.tailPartialEpoch) >> f.logMapsPerEpoch - if f.needTailEpoch(firstEpoch) { - break - } - f.processEvents() - if f.stop { - return false, nil - } + firstEpoch := f.indexedRange.maps.First() >> f.logMapsPerEpoch + if f.indexedRange.tailPartialEpoch > 0 && firstEpoch > 0 { + firstEpoch-- + } + for epoch := min(firstEpoch, f.cleanedEpochsBefore); !f.needTailEpoch(epoch); epoch++ { if !f.startedTailUnindex { f.startedTailUnindexAt = time.Now() f.startedTailUnindex = true 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) + if done, err := f.deleteTailEpoch(epoch); !done { return false, err } + f.processEvents() + if f.stop || !f.targetHeadIndexed() { + return false, nil + } } if f.startedTailUnindex && f.indexedRange.hasIndexedBlocks() { log.Info("Log index tail unindexing finished", diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go index 297e339c83e..c413839b7bf 100644 --- a/core/rawdb/accessors_indexes.go +++ b/core/rawdb/accessors_indexes.go @@ -354,10 +354,8 @@ func WriteFilterMapBaseRows(db ethdb.KeyValueWriter, mapRowIndex uint64, rows [] } } -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) - } +func DeleteFilterMapRows(db ethdb.KeyValueStore, mapRows common.Range[uint64], hashScheme bool, stopCallback func(bool) bool) error { + return SafeDeleteRange(db, filterMapRowKey(mapRows.First(), false), filterMapRowKey(mapRows.AfterLast(), false), hashScheme, stopCallback) } // ReadFilterMapLastBlock retrieves the number of the block that generated the @@ -368,7 +366,7 @@ func ReadFilterMapLastBlock(db ethdb.KeyValueReader, mapIndex uint32) (uint64, c return 0, common.Hash{}, err } if len(enc) != 40 { - return 0, common.Hash{}, errors.New("Invalid block number and id encoding") + return 0, common.Hash{}, errors.New("invalid block number and id encoding") } var id common.Hash copy(id[:], enc[8:]) @@ -394,10 +392,8 @@ func DeleteFilterMapLastBlock(db ethdb.KeyValueWriter, mapIndex uint32) { } } -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) - } +func DeleteFilterMapLastBlocks(db ethdb.KeyValueStore, maps common.Range[uint32], hashScheme bool, stopCallback func(bool) bool) error { + return SafeDeleteRange(db, filterMapLastBlockKey(maps.First()), filterMapLastBlockKey(maps.AfterLast()), hashScheme, stopCallback) } // ReadBlockLvPointer retrieves the starting log value index where the log values @@ -408,7 +404,7 @@ func ReadBlockLvPointer(db ethdb.KeyValueReader, blockNumber uint64) (uint64, er return 0, err } if len(encPtr) != 8 { - return 0, errors.New("Invalid log value pointer encoding") + return 0, errors.New("invalid log value pointer encoding") } return binary.BigEndian.Uint64(encPtr), nil } @@ -431,10 +427,8 @@ func DeleteBlockLvPointer(db ethdb.KeyValueWriter, blockNumber uint64) { } } -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) - } +func DeleteBlockLvPointers(db ethdb.KeyValueStore, blocks common.Range[uint64], hashScheme bool, stopCallback func(bool) bool) error { + return SafeDeleteRange(db, filterMapBlockLVKey(blocks.First()), filterMapBlockLVKey(blocks.AfterLast()), hashScheme, stopCallback) } // FilterMapsRange is a storage representation of the block range covered by the @@ -485,22 +479,22 @@ func DeleteFilterMapsRange(db ethdb.KeyValueWriter) { } // deletePrefixRange deletes everything with the given prefix from the database. -func deletePrefixRange(db ethdb.KeyValueRangeDeleter, prefix []byte) error { +func deletePrefixRange(db ethdb.KeyValueStore, prefix []byte, hashScheme bool, stopCallback func(bool) bool) error { end := bytes.Clone(prefix) end[len(end)-1]++ - return db.DeleteRange(prefix, end) + return SafeDeleteRange(db, prefix, end, hashScheme, stopCallback) } // DeleteFilterMapsDb removes the entire filter maps database -func DeleteFilterMapsDb(db ethdb.KeyValueRangeDeleter) error { - return deletePrefixRange(db, []byte(filterMapsPrefix)) +func DeleteFilterMapsDb(db ethdb.KeyValueStore, hashScheme bool, stopCallback func(bool) bool) error { + return deletePrefixRange(db, []byte(filterMapsPrefix), hashScheme, stopCallback) } -// DeleteFilterMapsDb removes the old bloombits database and the associated +// DeleteBloomBitsDb removes the old bloombits database and the associated // chain indexer database. -func DeleteBloomBitsDb(db ethdb.KeyValueRangeDeleter) error { - if err := deletePrefixRange(db, bloomBitsPrefix); err != nil { +func DeleteBloomBitsDb(db ethdb.KeyValueStore, hashScheme bool, stopCallback func(bool) bool) error { + if err := deletePrefixRange(db, bloomBitsPrefix, hashScheme, stopCallback); err != nil { return err } - return deletePrefixRange(db, bloomBitsMetaPrefix) + return deletePrefixRange(db, bloomBitsMetaPrefix, hashScheme, stopCallback) } diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 7fca8221552..2a50e3f9eef 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -28,12 +28,15 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/log" "github.com/olekukonko/tablewriter" ) +var ErrDeleteRangeInterrupted = errors.New("safe delete range operation interrupted") + // freezerdb is a database wrapper that enables ancient chain segment freezing. type freezerdb struct { ethdb.KeyValueStore @@ -607,3 +610,73 @@ func ReadChainMetadata(db ethdb.KeyValueStore) [][]string { } return data } + +// SafeDeleteRange deletes all of the keys (and values) in the range +// [start,end) (inclusive on start, exclusive on end). +// If hashScheme is true then it always uses an iterator and skips hashdb trie +// node entries. If it is false and the backing db is pebble db then it uses +// the fast native range delete. +// In case of fallback mode (hashdb or leveldb) the range deletion might be +// very slow depending on the number of entries. In this case stopCallback +// is periodically called and if it returns an error then SafeDeleteRange +// stops and also returns that error. The callback is not called if native +// range delete is used or there are a small number of keys only. The bool +// argument passed to the callback is true if enrties have actually been +// deleted already. +func SafeDeleteRange(db ethdb.KeyValueStore, start, end []byte, hashScheme bool, stopCallback func(bool) bool) error { + if !hashScheme { + // delete entire range; use fast native range delete on pebble db + for { + switch err := db.DeleteRange(start, end); { + case err == nil: + return nil + case errors.Is(err, ethdb.ErrTooManyKeys): + if stopCallback(true) { + return ErrDeleteRangeInterrupted + } + default: + return err + } + } + } + + var ( + count, deleted, skipped int + buff = crypto.NewKeccakState() + startTime = time.Now() + ) + + batch := db.NewBatch() + it := db.NewIterator(nil, start) + defer func() { + it.Release() // it might be replaced during the process + log.Debug("SafeDeleteRange finished", "deleted", deleted, "skipped", skipped, "elapsed", common.PrettyDuration(time.Since(startTime))) + }() + + for it.Next() && bytes.Compare(end, it.Key()) > 0 { + // Prevent deletion for trie nodes in hash mode + if len(it.Key()) != 32 || crypto.HashData(buff, it.Value()) != common.BytesToHash(it.Key()) { + if err := batch.Delete(it.Key()); err != nil { + return err + } + deleted++ + } else { + skipped++ + } + count++ + if count > 10000 { // should not block for more than a second + if err := batch.Write(); err != nil { + return err + } + if stopCallback(deleted != 0) { + return ErrDeleteRangeInterrupted + } + start = append(bytes.Clone(it.Key()), 0) // appending a zero gives us the next possible key + it.Release() + batch = db.NewBatch() + it = db.NewIterator(nil, start) + count = 0 + } + } + return batch.Write() +} diff --git a/eth/backend.go b/eth/backend.go index 909d153a2b5..ab612b1de75 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -239,7 +239,12 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err != nil { return nil, err } - fmConfig := filtermaps.Config{History: config.LogHistory, Disabled: config.LogNoHistory, ExportFileName: config.LogExportCheckpoints} + fmConfig := filtermaps.Config{ + History: config.LogHistory, + Disabled: config.LogNoHistory, + ExportFileName: config.LogExportCheckpoints, + HashScheme: scheme == rawdb.HashScheme, + } chainView := eth.newChainView(eth.blockchain.CurrentBlock()) historyCutoff := eth.blockchain.HistoryPruningCutoff() var finalBlock uint64 diff --git a/ethdb/database.go b/ethdb/database.go index b1577512f3a..f2d458b85f3 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -17,7 +17,10 @@ // Package ethdb defines the interfaces for an Ethereum data store. package ethdb -import "io" +import ( + "errors" + "io" +) // KeyValueReader wraps the Has and Get method of a backing data store. type KeyValueReader interface { @@ -37,10 +40,14 @@ type KeyValueWriter interface { Delete(key []byte) error } +var ErrTooManyKeys = errors.New("too many keys in deleted range") + // KeyValueRangeDeleter wraps the DeleteRange method of a backing data store. type KeyValueRangeDeleter interface { // DeleteRange deletes all of the keys (and values) in the range [start,end) // (inclusive on start, exclusive on end). + // Some implementations of DeleteRange may return ErrTooManyKeys after + // partially deleting entries in the given range. DeleteRange(start, end []byte) error } diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go index 7f47523b820..ef02e91822e 100644 --- a/ethdb/leveldb/leveldb.go +++ b/ethdb/leveldb/leveldb.go @@ -207,8 +207,6 @@ func (db *Database) Delete(key []byte) error { return db.db.Delete(key, nil) } -var ErrTooManyKeys = errors.New("too many keys in deleted range") - // DeleteRange deletes all of the keys (and values) in the range [start,end) // (inclusive on start, exclusive on end). // Note that this is a fallback implementation as leveldb does not natively @@ -228,7 +226,7 @@ func (db *Database) DeleteRange(start, end []byte) error { if err := batch.Write(); err != nil { return err } - return ErrTooManyKeys + return ethdb.ErrTooManyKeys } if err := batch.Delete(it.Key()); err != nil { return err From 82fc77a8658dc7adf53ba8c55e8329bf89d47544 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 31 Mar 2025 15:29:03 +0200 Subject: [PATCH 055/658] version: release go-ethereum v1.15.7 stable --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 8cd428fd102..2098af17b50 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 7 // Patch version component of the current release - Meta = "unstable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 7 // Patch version component of the current release + Meta = "stable" // Version metadata to append to the version string ) From 9c970d80a28d1eb98ea101cdb6209cc3be9962bc Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 31 Mar 2025 15:30:31 +0200 Subject: [PATCH 056/658] version: begin v1.15.8 release cycle --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 2098af17b50..72fd903a1f7 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 7 // Patch version component of the current release - Meta = "stable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 8 // Patch version component of the current release + Meta = "unstable" // Version metadata to append to the version string ) From 9af88d1100e17da0764081acb76b0d2154fc1893 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 31 Mar 2025 18:26:56 +0200 Subject: [PATCH 057/658] version: back to v1.15.7, to fix the build --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 72fd903a1f7..2098af17b50 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 8 // Patch version component of the current release - Meta = "unstable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 7 // Patch version component of the current release + Meta = "stable" // Version metadata to append to the version string ) From 827d3fccf72b69324ba00f7050afd59cf956348d Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 31 Mar 2025 18:27:43 +0200 Subject: [PATCH 058/658] .travis.yml: remove macos build --- .travis.yml | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5e8a493d03c..43f8ced19c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,6 @@ language: go go_import_path: github.com/ethereum/go-ethereum sudo: false jobs: - allow_failures: - - stage: build - os: osx - env: - - azure-osx - include: # This builder create and push the Docker images for all architectures - stage: build @@ -62,23 +56,6 @@ jobs: - go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - # This builder does the OSX Azure uploads - - stage: build - if: type = push - os: osx - osx_image: xcode14.2 - go: 1.23.1 # See https://github.com/ethereum/go-ethereum/pull/30478 - env: - - azure-osx - git: - submodules: false # avoid cloning ethereum/tests - script: - - ln -sf /Users/travis/gopath/bin/go1.23.1 /usr/local/bin/go # Work around travis go-setup bug - - go run build/ci.go install -dlgo - - go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - - go run build/ci.go install -dlgo -arch arm64 - - go run build/ci.go archive -arch arm64 -type tar -signer OSX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - # These builders run the tests - stage: build if: type = push From f0cdc40cebd3fcb26d21ced0f1093efd7a2c949f Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 31 Mar 2025 18:29:33 +0200 Subject: [PATCH 059/658] version: begin v1.15.8 release cycle reloaded --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 2098af17b50..72fd903a1f7 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 7 // Patch version component of the current release - Meta = "stable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 8 // Patch version component of the current release + Meta = "unstable" // Version metadata to append to the version string ) From bc36f2de83a5ba8e805af6ce4ea9da3ab7cb1df9 Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Tue, 1 Apr 2025 13:42:01 +0200 Subject: [PATCH 060/658] eth, eth/filters: implement API error code for pruned blocks (#31361) Implements #31275 --------- Co-authored-by: Jared Wasinger Co-authored-by: Felix Lange --- core/blockchain_reader.go | 5 ++ eth/api_backend.go | 46 ++++++++++++++++--- eth/ethconfig/historymode.go | 6 +++ eth/filters/api.go | 5 ++ eth/filters/filter.go | 20 ++++++-- eth/filters/filter_system.go | 10 ++++ eth/filters/filter_system_test.go | 4 ++ internal/ethapi/api.go | 58 +++++++++++++----------- internal/ethapi/api_test.go | 7 +++ internal/ethapi/backend.go | 1 + internal/ethapi/transaction_args_test.go | 2 + rpc/types.go | 2 +- 12 files changed, 128 insertions(+), 38 deletions(-) diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 025b912ceba..415a0f54426 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -86,6 +86,11 @@ func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header { return bc.hc.GetHeaderByNumber(number) } +// GetBlockNumber retrieves the block number associated with a block hash. +func (bc *BlockChain) GetBlockNumber(hash common.Hash) *uint64 { + return bc.hc.GetBlockNumber(hash) +} + // GetHeadersFrom returns a contiguous segment of headers, in rlp-form, going // backwards from the given number. func (bc *BlockChain) GetHeadersFrom(number, count uint64) []rlp.RawValue { diff --git a/eth/api_backend.go b/eth/api_backend.go index c8c5ca707f9..944c357e78f 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -34,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/ethdb" @@ -91,7 +92,13 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb } return block, nil } - return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil + var bn uint64 + if number == rpc.EarliestBlockNumber { + bn = b.eth.blockchain.HistoryPruningCutoff() + } else { + bn = uint64(number) + } + return b.eth.blockchain.GetHeaderByNumber(bn), nil } func (b *EthAPIBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) { @@ -143,11 +150,27 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe } return b.eth.blockchain.GetBlock(header.Hash(), header.Number.Uint64()), nil } - return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil + bn := uint64(number) // the resolved number + if number == rpc.EarliestBlockNumber { + bn = b.eth.blockchain.HistoryPruningCutoff() + } + block := b.eth.blockchain.GetBlockByNumber(bn) + if block == nil && bn < b.eth.blockchain.HistoryPruningCutoff() { + return nil, ðconfig.PrunedHistoryError{} + } + return block, nil } func (b *EthAPIBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { - return b.eth.blockchain.GetBlockByHash(hash), nil + number := b.eth.blockchain.GetBlockNumber(hash) + if number == nil { + return nil, nil + } + block := b.eth.blockchain.GetBlock(hash, *number) + if block == nil && *number < b.eth.blockchain.HistoryPruningCutoff() { + return nil, ðconfig.PrunedHistoryError{} + } + return block, nil } // GetBody returns body of a block. It does not resolve special block numbers. @@ -155,10 +178,14 @@ func (b *EthAPIBackend) GetBody(ctx context.Context, hash common.Hash, number rp if number < 0 || hash == (common.Hash{}) { return nil, errors.New("invalid arguments; expect hash and no special block numbers") } - if body := b.eth.blockchain.GetBody(hash); body != nil { - return body, nil + body := b.eth.blockchain.GetBody(hash) + if body == nil { + if uint64(number) < b.eth.blockchain.HistoryPruningCutoff() { + return nil, ðconfig.PrunedHistoryError{} + } + return nil, errors.New("block body not found") } - return nil, errors.New("block body not found") + return body, nil } func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) { @@ -175,6 +202,9 @@ func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash r } block := b.eth.blockchain.GetBlock(hash, header.Number.Uint64()) if block == nil { + if header.Number.Uint64() < b.eth.blockchain.HistoryPruningCutoff() { + return nil, ðconfig.PrunedHistoryError{} + } return nil, errors.New("header found, but block body is missing") } return block, nil @@ -234,6 +264,10 @@ func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockN return nil, nil, errors.New("invalid arguments; neither block nor hash specified") } +func (b *EthAPIBackend) HistoryPruningCutoff() uint64 { + return b.eth.blockchain.HistoryPruningCutoff() +} + func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { return b.eth.blockchain.GetReceiptsByHash(hash), nil } diff --git a/eth/ethconfig/historymode.go b/eth/ethconfig/historymode.go index c3661004e81..a595d72feb3 100644 --- a/eth/ethconfig/historymode.go +++ b/eth/ethconfig/historymode.go @@ -90,3 +90,9 @@ var HistoryPrunePoints = map[common.Hash]*HistoryPrunePoint{ BlockHash: common.HexToHash("0x229f6b18ca1552f1d5146deceb5387333f40dc6275aebee3f2c5c4ece07d02db"), }, } + +// PrunedHistoryError is returned when the requested history is pruned. +type PrunedHistoryError struct{} + +func (e *PrunedHistoryError) Error() string { return "pruned history unavailable" } +func (e *PrunedHistoryError) ErrorCode() int { return 4444 } diff --git a/eth/filters/api.go b/eth/filters/api.go index e3057a2af26..6593cbef275 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/rpc" ) @@ -354,9 +355,13 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type if crit.ToBlock != nil { end = crit.ToBlock.Int64() } + // Block numbers below 0 are special cases. if begin > 0 && end > 0 && begin > end { return nil, errInvalidBlockRange } + if begin > 0 && begin < int64(api.events.backend.HistoryPruningCutoff()) { + return nil, ðconfig.PrunedHistoryError{} + } // Construct the range filter filter = api.sys.NewRangeFilter(begin, end, crit.Addresses, crit.Topics) } diff --git a/eth/filters/filter.go b/eth/filters/filter.go index b743c25994a..e44b37d0473 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" ) @@ -86,6 +87,9 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { if header == nil { return nil, errors.New("unknown block") } + if header.Number.Uint64() < f.sys.backend.HistoryPruningCutoff() { + return nil, ðconfig.PrunedHistoryError{} + } return f.blockLogs(ctx, header) } @@ -114,11 +118,19 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { return 0, errors.New("safe header not found") } return hdr.Number.Uint64(), nil + case rpc.EarliestBlockNumber.Int64(): + earliest := f.sys.backend.HistoryPruningCutoff() + hdr, _ := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(earliest)) + if hdr == nil { + return 0, errors.New("earliest header not found") + } + return hdr.Number.Uint64(), nil + default: + if number < 0 { + return 0, errors.New("negative block number") + } + return uint64(number), nil } - if number < 0 { - return 0, errors.New("negative block number") - } - return uint64(number), nil } // range query need to resolve the special begin/end block number diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index 7531a1ecfcd..aca3c03ad64 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" @@ -64,6 +65,7 @@ type Backend interface { CurrentHeader() *types.Header ChainConfig() *params.ChainConfig + HistoryPruningCutoff() uint64 SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription @@ -304,6 +306,14 @@ func (es *EventSystem) SubscribeLogs(crit ethereum.FilterQuery, logs chan []*typ return nil, errPendingLogsUnsupported } + if from == rpc.EarliestBlockNumber { + from = rpc.BlockNumber(es.backend.HistoryPruningCutoff()) + } + // Queries beyond the pruning cutoff are not supported. + if uint64(from) < es.backend.HistoryPruningCutoff() { + return nil, ðconfig.PrunedHistoryError{} + } + // only interested in new mined logs if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber { return es.subscribeLogs(crit, logs), nil diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index c35d823f5ae..3bb019d105f 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -181,6 +181,10 @@ func (b *testBackend) setPending(block *types.Block, receipts types.Receipts) { b.pendingReceipts = receipts } +func (b *testBackend) HistoryPruningCutoff() uint64 { + return 0 +} + func newTestFilterSystem(db ethdb.Database, cfg Config) (*testBackend, *FilterSystem) { backend := &testBackend{db: db} sys := NewFilterSystem(backend, cfg) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 36a5df8087b..3b699748b89 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -549,21 +549,23 @@ func (api *BlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, block } // GetUncleCountByBlockNumber returns number of uncles in the block for the given block number -func (api *BlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint { - if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil { +func (api *BlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) (*hexutil.Uint, error) { + block, err := api.b.BlockByNumber(ctx, blockNr) + if block != nil { n := hexutil.Uint(len(block.Uncles())) - return &n + return &n, nil } - return nil + return nil, err } // GetUncleCountByBlockHash returns number of uncles in the block for the given block hash -func (api *BlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint { - if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil { +func (api *BlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) (*hexutil.Uint, error) { + block, err := api.b.BlockByHash(ctx, blockHash) + if block != nil { n := hexutil.Uint(len(block.Uncles())) - return &n + return &n, nil } - return nil + return nil, err } // GetCode returns the code stored at the given address in the state for the given block number. @@ -596,9 +598,7 @@ func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Addre func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { block, err := api.b.BlockByNumberOrHash(ctx, blockNrOrHash) if block == nil || err != nil { - // When the block doesn't exist, the RPC method should return JSON null - // as per specification. - return nil, nil + return nil, err } receipts, err := api.b.GetReceipts(ctx, block.Hash()) if err != nil { @@ -1258,37 +1258,41 @@ func NewTransactionAPI(b Backend, nonceLock *AddrLocker) *TransactionAPI { } // GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. -func (api *TransactionAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint { - if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil { +func (api *TransactionAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*hexutil.Uint, error) { + block, err := api.b.BlockByNumber(ctx, blockNr) + if block != nil { n := hexutil.Uint(len(block.Transactions())) - return &n + return &n, nil } - return nil + return nil, err } // GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash. -func (api *TransactionAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint { - if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil { +func (api *TransactionAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) (*hexutil.Uint, error) { + block, err := api.b.BlockByHash(ctx, blockHash) + if block != nil { n := hexutil.Uint(len(block.Transactions())) - return &n + return &n, nil } - return nil + return nil, err } // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. -func (api *TransactionAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction { - if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil { - return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()) +func (api *TransactionAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (*RPCTransaction, error) { + block, err := api.b.BlockByNumber(ctx, blockNr) + if block != nil { + return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()), nil } - return nil + return nil, err } // GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. -func (api *TransactionAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction { - if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil { - return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()) +func (api *TransactionAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (*RPCTransaction, error) { + block, err := api.b.BlockByHash(ctx, blockHash) + if block != nil { + return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()), nil } - return nil + return nil, err } // GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index. diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 1ed1a8c8d8e..37210aa78bd 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -520,8 +520,12 @@ func (b testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) if number == rpc.PendingBlockNumber { return b.pending, nil } + if number == rpc.EarliestBlockNumber { + number = 0 + } return b.chain.GetBlockByNumber(uint64(number)), nil } + func (b testBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { return b.chain.GetBlockByHash(hash), nil } @@ -618,6 +622,9 @@ func (b testBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscripti func (b testBackend) NewMatcherBackend() filtermaps.MatcherBackend { panic("implement me") } + +func (b testBackend) HistoryPruningCutoff() uint64 { return b.chain.HistoryPruningCutoff() } + func TestEstimateGas(t *testing.T) { t.Parallel() // Initialize test accounts diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 9e2ea2c876e..c4bf2e05910 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -85,6 +85,7 @@ type Backend interface { ChainConfig() *params.ChainConfig Engine() consensus.Engine + HistoryPruningCutoff() uint64 // This is copied from filters.Backend // eth/filters needs to be initialized from this backend type, so methods needed by diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index a5fd9bb0d4d..b4d11a3033f 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -402,3 +402,5 @@ func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) func (b *backendMock) Engine() consensus.Engine { return nil } func (b *backendMock) NewMatcherBackend() filtermaps.MatcherBackend { return nil } + +func (b *backendMock) HistoryPruningCutoff() uint64 { return 0 } diff --git a/rpc/types.go b/rpc/types.go index 2e53174b872..85f15344e86 100644 --- a/rpc/types.go +++ b/rpc/types.go @@ -63,11 +63,11 @@ type jsonWriter interface { type BlockNumber int64 const ( + EarliestBlockNumber = BlockNumber(-5) SafeBlockNumber = BlockNumber(-4) FinalizedBlockNumber = BlockNumber(-3) LatestBlockNumber = BlockNumber(-2) PendingBlockNumber = BlockNumber(-1) - EarliestBlockNumber = BlockNumber(0) ) // UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports: From 1bd70ba57aa943340e7a685ee3d89db072ac6ef6 Mon Sep 17 00:00:00 2001 From: John <33443230+gazzua@users.noreply.github.com> Date: Tue, 1 Apr 2025 21:07:47 +0900 Subject: [PATCH 061/658] p2p/nat: improve AddMapping code (#31486) It introduces a new variable to store the external port returned by the addAnyPortMapping function and ensures that the correct external port is returned even in case of an error. --------- Co-authored-by: Felix Lange --- p2p/nat/natupnp.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/p2p/nat/natupnp.go b/p2p/nat/natupnp.go index f1bb955892e..1014dc69d23 100644 --- a/p2p/nat/natupnp.go +++ b/p2p/nat/natupnp.go @@ -82,7 +82,7 @@ func (n *upnp) ExternalIP() (addr net.IP, err error) { func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, lifetime time.Duration) (uint16, error) { ip, err := n.internalAddress() if err != nil { - return 0, nil // TODO: Shouldn't we return the error? + return 0, err } protocol = strings.ToUpper(protocol) lifetimeS := uint32(lifetime / time.Second) @@ -94,14 +94,15 @@ func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, li if err == nil { return uint16(extport), nil } - - return uint16(extport), n.withRateLimit(func() error { + // Try addAnyPortMapping if mapping specified port didn't work. + err = n.withRateLimit(func() error { p, err := n.addAnyPortMapping(protocol, extport, intport, ip, desc, lifetimeS) if err == nil { extport = int(p) } return err }) + return uint16(extport), err } func (n *upnp) addAnyPortMapping(protocol string, extport, intport int, ip net.IP, desc string, lifetimeS uint32) (uint16, error) { From 4add312c8a8332b76e5263066a475e962637c9ac Mon Sep 17 00:00:00 2001 From: Delweng Date: Tue, 1 Apr 2025 20:10:22 +0800 Subject: [PATCH 062/658] cmd: apply snapshot cache flag in the MakeChain (#31534) --- cmd/utils/flags.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index ae58c2d0539..fb2892d2c1d 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2189,6 +2189,8 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh } if !ctx.Bool(SnapshotFlag.Name) { cache.SnapshotLimit = 0 // Disabled + } else if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheSnapshotFlag.Name) { + cache.SnapshotLimit = ctx.Int(CacheFlag.Name) * ctx.Int(CacheSnapshotFlag.Name) / 100 } // If we're in readonly, do not bother generating snapshot data. if readonly { From 7e3170fb5ce4e03348d29d1a153c2591680058a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Tue, 1 Apr 2025 14:29:20 +0200 Subject: [PATCH 063/658] core/filtermaps: add metrics (#31511) This PR adds metrics related to map rendering and pattern matching to the `core/filtermaps` package. --- core/filtermaps/filtermaps.go | 21 +++++++++++++++++++++ core/filtermaps/map_renderer.go | 22 +++++++++++++++++++++- core/filtermaps/matcher.go | 12 ++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index 5722f17daa3..18b1c7dc790 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -30,6 +30,23 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" +) + +var ( + mapCountGauge = metrics.NewRegisteredGauge("filtermaps/maps/count", nil) // actual number of rendered maps + mapLogValueMeter = metrics.NewRegisteredMeter("filtermaps/maps/logvalues", nil) // number of log values processed + mapBlockMeter = metrics.NewRegisteredMeter("filtermaps/maps/blocks", nil) // number of block delimiters processed + mapRenderTimer = metrics.NewRegisteredTimer("filtermaps/maps/rendertime", nil) // time elapsed while rendering a single map + mapWriteTimer = metrics.NewRegisteredTimer("filtermaps/maps/writetime", nil) // time elapsed while writing a batch of finished maps to db + matchRequestTimer = metrics.NewRegisteredTimer("filtermaps/match/requesttime", nil) // processing time a matching request in a single epoch + matchEpochTimer = metrics.NewRegisteredTimer("filtermaps/match/epochtime", nil) // total processing time a matching request + matchBaseRowAccessMeter = metrics.NewRegisteredMeter("filtermaps/match/baserowaccess", nil) // number of accessed rows on layer 0 + matchBaseRowSizeMeter = metrics.NewRegisteredMeter("filtermaps/match/baserowsize", nil) // size of accessed rows on layer 0 + matchExtRowAccessMeter = metrics.NewRegisteredMeter("filtermaps/match/extrowaccess", nil) // number of accessed rows on higher layers + matchExtRowSizeMeter = metrics.NewRegisteredMeter("filtermaps/match/extrowsize", nil) // size of accessed rows on higher layers + matchLogLookup = metrics.NewRegisteredMeter("filtermaps/match/loglookup", nil) // number of log lookups based on potential matches + matchAllMeter = metrics.NewRegisteredMeter("filtermaps/match/matchall", nil) // number of requests returned with ErrMatchAll ) const ( @@ -429,8 +446,12 @@ func (f *FilterMaps) setRange(batch ethdb.KeyValueWriter, newView *ChainView, ne TailPartialEpoch: newRange.tailPartialEpoch, } rawdb.WriteFilterMapsRange(batch, rs) + if !isTempRange { + mapCountGauge.Update(int64(newRange.maps.Count() + newRange.tailPartialEpoch)) + } } else { rawdb.DeleteFilterMapsRange(batch) + mapCountGauge.Update(0) } } diff --git a/core/filtermaps/map_renderer.go b/core/filtermaps/map_renderer.go index 1eaaa9bb1a7..28f943abb31 100644 --- a/core/filtermaps/map_renderer.go +++ b/core/filtermaps/map_renderer.go @@ -21,6 +21,7 @@ import ( "fmt" "math" "sort" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/lru" @@ -301,6 +302,11 @@ func (r *mapRenderer) run(stopCb func() bool, writeCb func()) (bool, error) { // renderCurrentMap renders a single map. func (r *mapRenderer) renderCurrentMap(stopCb func() bool) (bool, error) { + var ( + totalTime time.Duration + logValuesProcessed, blocksProcessed int64 + ) + start := time.Now() if !r.iterator.updateChainView(r.f.targetView) { return false, errChainUpdate } @@ -316,9 +322,11 @@ func (r *mapRenderer) renderCurrentMap(stopCb func() bool) (bool, error) { for r.iterator.lvIndex < uint64(r.currentMap.mapIndex+1)<= valuesPerCallback { + totalTime += time.Since(start) if stopCb() { return false, nil } + start = time.Now() if !r.iterator.updateChainView(r.f.targetView) { return false, errChainUpdate } @@ -343,8 +351,10 @@ func (r *mapRenderer) renderCurrentMap(stopCb func() bool) (bool, error) { 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.iterator.skipToBoundary { + logValuesProcessed++ r.currentMap.lastBlock = r.iterator.blockNumber if r.iterator.blockStart { + blocksProcessed++ r.currentMap.blockLvPtrs = append(r.currentMap.blockLvPtrs, r.iterator.lvIndex) } if !r.f.testDisableSnapshots && r.renderBefore >= r.f.indexedRange.maps.AfterLast() && @@ -358,12 +368,18 @@ func (r *mapRenderer) renderCurrentMap(stopCb func() bool) (bool, error) { r.currentMap.headDelimiter = r.iterator.lvIndex } r.currentMap.lastBlockId = r.f.targetView.getBlockId(r.currentMap.lastBlock) + totalTime += time.Since(start) + mapRenderTimer.Update(totalTime) + mapLogValueMeter.Mark(logValuesProcessed) + mapBlockMeter.Mark(blocksProcessed) return true, nil } // writeFinishedMaps writes rendered maps to the database and updates // filterMapsRange and indexedView accordingly. func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error { + var totalTime time.Duration + start := time.Now() if len(r.finishedMaps) == 0 { return nil } @@ -379,7 +395,7 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error { if err != nil { return fmt.Errorf("failed to get updated rendered range: %v", err) } - renderedView := r.f.targetView // stopCb callback might still change targetView while writing finished maps + renderedView := r.f.targetView // pauseCb callback might still change targetView while writing finished maps batch := r.f.db.NewBatch() var writeCnt int @@ -393,7 +409,9 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error { // do not exit while in partially written state but do allow processing // events and pausing while block processing is in progress r.f.indexLock.Unlock() + totalTime += time.Since(start) pauseCb() + start = time.Now() r.f.indexLock.Lock() batch = r.f.db.NewBatch() } @@ -477,6 +495,8 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error { if err := batch.Write(); err != nil { log.Crit("Error writing log index update batch", "error", err) } + totalTime += time.Since(start) + mapWriteTimer.Update(totalTime) return nil } diff --git a/core/filtermaps/matcher.go b/core/filtermaps/matcher.go index 6c05672cbcf..5738bf166a4 100644 --- a/core/filtermaps/matcher.go +++ b/core/filtermaps/matcher.go @@ -125,6 +125,7 @@ func GetPotentialMatches(ctx context.Context, backend MatcherBackend, firstBlock start := time.Now() res, err := m.process() + matchRequestTimer.Update(time.Since(start)) if doRuntimeStats { log.Info("Log search finished", "elapsed", time.Since(start)) @@ -202,6 +203,7 @@ func (m *matcherEnv) process() ([]*types.Log, error) { logs = append(logs, tasks[waitEpoch].logs...) if err := tasks[waitEpoch].err; err != nil { if err == ErrMatchAll { + matchAllMeter.Mark(1) return logs, err } return logs, fmt.Errorf("failed to process log index epoch %d: %v", waitEpoch, err) @@ -220,6 +222,7 @@ func (m *matcherEnv) process() ([]*types.Log, error) { // processEpoch returns the potentially matching logs from the given epoch. func (m *matcherEnv) processEpoch(epochIndex uint32) ([]*types.Log, error) { + start := time.Now() var logs []*types.Log // create a list of map indices to process fm, lm := epochIndex< Date: Tue, 1 Apr 2025 22:13:37 +0800 Subject: [PATCH 064/658] accounts/abi/abigen: fix a flaky bind test case `NewSingleStructArgument` (#31501) found the failed testcase here https://ci.appveyor.com/project/ethereum/go-ethereum/builds/51767091/job/rbjke432c05pufja add a timeout to wait the tx to be mined. --------- Signed-off-by: jsvisa Co-authored-by: Jared Wasinger --- accounts/abi/abigen/bind_test.go | 55 ++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/accounts/abi/abigen/bind_test.go b/accounts/abi/abigen/bind_test.go index 3871560912d..195064fb7a9 100644 --- a/accounts/abi/abigen/bind_test.go +++ b/accounts/abi/abigen/bind_test.go @@ -541,7 +541,7 @@ var bindTests = []struct { struct A { bytes32 B; } - + function F() public view returns (A[] memory a, uint256[] memory c, bool[] memory d) { A[] memory a = new A[](2); a[0].B = bytes32(uint256(1234) << 96); @@ -549,7 +549,7 @@ var bindTests = []struct { bool[] memory d; return (a, c, d); } - + function G() public view returns (A[] memory a) { A[] memory a = new A[](2); a[0].B = bytes32(uint256(1234) << 96); @@ -571,10 +571,10 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) - + sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000) defer sim.Close() - + // Deploy a structs method invoker contract and execute its default method _, _, structs, err := DeployStructs(auth, sim) if err != nil { @@ -1701,13 +1701,13 @@ var bindTests = []struct { `NewFallbacks`, ` pragma solidity >=0.6.0 <0.7.0; - + contract NewFallbacks { event Fallback(bytes data); fallback() external { emit Fallback(msg.data); } - + event Received(address addr, uint value); receive() external payable { emit Received(msg.sender, msg.value); @@ -1719,7 +1719,7 @@ var bindTests = []struct { ` "bytes" "math/big" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/core/types" @@ -1728,22 +1728,22 @@ var bindTests = []struct { ` key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey) - + sim := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 1000000) defer sim.Close() - + opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) _, _, c, err := DeployNewFallbacks(opts, sim) if err != nil { t.Fatalf("Failed to deploy contract: %v", err) } sim.Commit() - + // Test receive function opts.Value = big.NewInt(100) c.Receive(opts) sim.Commit() - + var gotEvent bool iter, _ := c.FilterReceived(nil) defer iter.Close() @@ -1760,14 +1760,14 @@ var bindTests = []struct { if !gotEvent { t.Fatal("Expect to receive event emitted by receive") } - + // Test fallback function gotEvent = false opts.Value = nil calldata := []byte{0x01, 0x02, 0x03} c.Fallback(opts, calldata) sim.Commit() - + iter2, _ := c.FilterFallback(nil) defer iter2.Close() for iter2.Next() { @@ -1806,7 +1806,9 @@ var bindTests = []struct { []string{"608060405234801561001057600080fd5b50610113806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806324ec1d3f14602d575b600080fd5b60336035565b005b7fb4b2ff75e30cb4317eaae16dd8a187dd89978df17565104caa6c2797caae27d460405180604001604052806001815260200160028152506040516078919060ba565b60405180910390a1565b6040820160008201516096600085018260ad565b50602082015160a7602085018260ad565b50505050565b60b48160d3565b82525050565b600060408201905060cd60008301846082565b92915050565b600081905091905056fea26469706673582212208823628796125bf9941ce4eda18da1be3cf2931b231708ab848e1bd7151c0c9a64736f6c63430008070033"}, []string{`[{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"b","type":"uint256"}],"indexed":false,"internalType":"struct Test.MyStruct","name":"s","type":"tuple"}],"name":"StructEvent","type":"event"},{"inputs":[],"name":"TestEvent","outputs":[],"stateMutability":"nonpayable","type":"function"}]`}, ` + "context" "math/big" + "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" @@ -1828,12 +1830,23 @@ var bindTests = []struct { } sim.Commit() - _, err = d.TestEvent(user) + tx, err := d.TestEvent(user) if err != nil { t.Fatalf("Failed to call contract %v", err) } sim.Commit() + // Wait for the transaction to be mined + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + receipt, err := bind.WaitMined(ctx, sim, tx) + if err != nil { + t.Fatalf("Failed to wait for tx to be mined: %v", err) + } + if receipt.Status != types.ReceiptStatusSuccessful { + t.Fatal("Transaction failed") + } + it, err := d.FilterStructEvent(nil) if err != nil { t.Fatalf("Failed to filter contract event %v", err) @@ -1862,7 +1875,7 @@ var bindTests = []struct { `NewErrors`, ` pragma solidity >0.8.4; - + contract NewErrors { error MyError(uint256); error MyError1(uint256); @@ -1878,7 +1891,7 @@ var bindTests = []struct { ` "context" "math/big" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/core/types" @@ -1892,7 +1905,7 @@ var bindTests = []struct { sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil) ) defer sim.Close() - + _, tx, contract, err := DeployNewErrors(user, sim) if err != nil { t.Fatal(err) @@ -1917,12 +1930,12 @@ var bindTests = []struct { name: `ConstructorWithStructParam`, contract: ` pragma solidity >=0.8.0 <0.9.0; - + contract ConstructorWithStructParam { struct StructType { uint256 field; } - + constructor(StructType memory st) {} } `, @@ -1951,7 +1964,7 @@ var bindTests = []struct { t.Fatalf("DeployConstructorWithStructParam() got err %v; want nil err", err) } sim.Commit() - + if _, err = bind.WaitDeployed(context.Background(), sim, tx); err != nil { t.Logf("Deployment tx: %+v", tx) t.Errorf("bind.WaitDeployed(nil, %T, ) got err %v; want nil err", sim, err) @@ -2000,7 +2013,7 @@ var bindTests = []struct { t.Fatalf("DeployNameConflict() got err %v; want nil err", err) } sim.Commit() - + if _, err = bind.WaitDeployed(context.Background(), sim, tx); err != nil { t.Logf("Deployment tx: %+v", tx) t.Errorf("bind.WaitDeployed(nil, %T, ) got err %v; want nil err", sim, err) From a9e6c8daae7aa2691e227d7a79323f306f529ffd Mon Sep 17 00:00:00 2001 From: Ng Wei Han <47109095+weiihann@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:06:54 +0800 Subject: [PATCH 065/658] triedb/pathdb: improve perf by separating nodes map (#31306) This PR refactors the `nodeSet` structure in the path database to use separate maps for account and storage trie nodes, resulting in performance improvements. The change maintains the same API while optimizing the internal data structure. --- triedb/pathdb/nodes.go | 193 ++++++++++++++++++++++++++--------------- 1 file changed, 125 insertions(+), 68 deletions(-) diff --git a/triedb/pathdb/nodes.go b/triedb/pathdb/nodes.go index c56e38066b1..f90bd0f01cf 100644 --- a/triedb/pathdb/nodes.go +++ b/triedb/pathdb/nodes.go @@ -36,8 +36,9 @@ import ( // transition, typically corresponding to a block execution. It can also represent // the combined trie node set from several aggregated state transitions. type nodeSet struct { - size uint64 // aggregated size of the trie node - nodes map[common.Hash]map[string]*trienode.Node // node set, mapped by owner and path + size uint64 // aggregated size of the trie node + accountNodes map[string]*trienode.Node // account trie nodes, mapped by path + storageNodes map[common.Hash]map[string]*trienode.Node // storage trie nodes, mapped by owner and path } // newNodeSet constructs the set with the provided dirty trie nodes. @@ -46,7 +47,17 @@ func newNodeSet(nodes map[common.Hash]map[string]*trienode.Node) *nodeSet { if nodes == nil { nodes = make(map[common.Hash]map[string]*trienode.Node) } - s := &nodeSet{nodes: nodes} + s := &nodeSet{ + accountNodes: make(map[string]*trienode.Node), + storageNodes: make(map[common.Hash]map[string]*trienode.Node), + } + for owner, subset := range nodes { + if owner == (common.Hash{}) { + s.accountNodes = subset + } else { + s.storageNodes[owner] = subset + } + } s.computeSize() return s } @@ -54,13 +65,12 @@ func newNodeSet(nodes map[common.Hash]map[string]*trienode.Node) *nodeSet { // computeSize calculates the database size of the held trie nodes. func (s *nodeSet) computeSize() { var size uint64 - for owner, subset := range s.nodes { - var prefix int - if owner != (common.Hash{}) { - prefix = common.HashLength // owner (32 bytes) for storage trie nodes - } + for path, n := range s.accountNodes { + size += uint64(len(n.Blob) + len(path)) + } + for _, subset := range s.storageNodes { for path, n := range subset { - size += uint64(prefix + len(n.Blob) + len(path)) + size += uint64(common.HashLength + len(n.Blob) + len(path)) } } s.size = size @@ -79,15 +89,18 @@ func (s *nodeSet) updateSize(delta int64) { // node retrieves the trie node with node path and its trie identifier. func (s *nodeSet) node(owner common.Hash, path []byte) (*trienode.Node, bool) { - subset, ok := s.nodes[owner] - if !ok { - return nil, false + // Account trie node + if owner == (common.Hash{}) { + n, ok := s.accountNodes[string(path)] + return n, ok } - n, ok := subset[string(path)] + // Storage trie node + subset, ok := s.storageNodes[owner] if !ok { return nil, false } - return n, true + n, ok := subset[string(path)] + return n, ok } // merge integrates the provided dirty nodes into the set. The provided nodeset @@ -97,15 +110,24 @@ func (s *nodeSet) merge(set *nodeSet) { delta int64 // size difference resulting from node merging overwrite counter // counter of nodes being overwritten ) - for owner, subset := range set.nodes { - var prefix int - if owner != (common.Hash{}) { - prefix = common.HashLength + + // Merge account nodes + for path, n := range set.accountNodes { + if orig, exist := s.accountNodes[path]; !exist { + delta += int64(len(n.Blob) + len(path)) + } else { + delta += int64(len(n.Blob) - len(orig.Blob)) + overwrite.add(len(orig.Blob) + len(path)) } - current, exist := s.nodes[owner] + s.accountNodes[path] = n + } + + // Merge storage nodes + for owner, subset := range set.storageNodes { + current, exist := s.storageNodes[owner] if !exist { for path, n := range subset { - delta += int64(prefix + len(n.Blob) + len(path)) + delta += int64(common.HashLength + len(n.Blob) + len(path)) } // Perform a shallow copy of the map for the subset instead of claiming it // directly from the provided nodeset to avoid potential concurrent map @@ -113,19 +135,19 @@ func (s *nodeSet) merge(set *nodeSet) { // accessible even after merging. Therefore, ownership of the nodes map // should still belong to the original layer, and any modifications to it // should be prevented. - s.nodes[owner] = maps.Clone(subset) + s.storageNodes[owner] = maps.Clone(subset) continue } for path, n := range subset { if orig, exist := current[path]; !exist { - delta += int64(prefix + len(n.Blob) + len(path)) + delta += int64(common.HashLength + len(n.Blob) + len(path)) } else { delta += int64(len(n.Blob) - len(orig.Blob)) - overwrite.add(prefix + len(orig.Blob) + len(path)) + overwrite.add(common.HashLength + len(orig.Blob) + len(path)) } current[path] = n } - s.nodes[owner] = current + s.storageNodes[owner] = current } overwrite.report(gcTrieNodeMeter, gcTrieNodeBytesMeter) s.updateSize(delta) @@ -136,34 +158,38 @@ func (s *nodeSet) merge(set *nodeSet) { func (s *nodeSet) revertTo(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node) { var delta int64 for owner, subset := range nodes { - current, ok := s.nodes[owner] - if !ok { - panic(fmt.Sprintf("non-existent subset (%x)", owner)) - } - for path, n := range subset { - orig, ok := current[path] - if !ok { - // There is a special case in merkle tree that one child is removed - // from a fullNode which only has two children, and then a new child - // with different position is immediately inserted into the fullNode. - // In this case, the clean child of the fullNode will also be marked - // as dirty because of node collapse and expansion. In case of database - // rollback, don't panic if this "clean" node occurs which is not - // present in buffer. - var blob []byte - if owner == (common.Hash{}) { - blob = rawdb.ReadAccountTrieNode(db, []byte(path)) - } else { - blob = rawdb.ReadStorageTrieNode(db, owner, []byte(path)) + if owner == (common.Hash{}) { + // Account trie nodes + for path, n := range subset { + orig, ok := s.accountNodes[path] + if !ok { + blob := rawdb.ReadAccountTrieNode(db, []byte(path)) + if bytes.Equal(blob, n.Blob) { + continue + } + panic(fmt.Sprintf("non-existent account node (%v) blob: %v", path, crypto.Keccak256Hash(n.Blob).Hex())) } - // Ignore the clean node in the case described above. - if bytes.Equal(blob, n.Blob) { - continue + s.accountNodes[path] = n + delta += int64(len(n.Blob)) - int64(len(orig.Blob)) + } + } else { + // Storage trie nodes + current, ok := s.storageNodes[owner] + if !ok { + panic(fmt.Sprintf("non-existent subset (%x)", owner)) + } + for path, n := range subset { + orig, ok := current[path] + if !ok { + blob := rawdb.ReadStorageTrieNode(db, owner, []byte(path)) + if bytes.Equal(blob, n.Blob) { + continue + } + panic(fmt.Sprintf("non-existent storage node (%x %v) blob: %v", owner, path, crypto.Keccak256Hash(n.Blob).Hex())) } - panic(fmt.Sprintf("non-existent node (%x %v) blob: %v", owner, path, crypto.Keccak256Hash(n.Blob).Hex())) + current[path] = n + delta += int64(len(n.Blob)) - int64(len(orig.Blob)) } - current[path] = n - delta += int64(len(n.Blob)) - int64(len(orig.Blob)) } } s.updateSize(delta) @@ -184,8 +210,21 @@ type journalNodes struct { // encode serializes the content of trie nodes into the provided writer. func (s *nodeSet) encode(w io.Writer) error { - nodes := make([]journalNodes, 0, len(s.nodes)) - for owner, subset := range s.nodes { + nodes := make([]journalNodes, 0, len(s.storageNodes)+1) + + // Encode account nodes + if len(s.accountNodes) > 0 { + entry := journalNodes{Owner: common.Hash{}} + for path, node := range s.accountNodes { + entry.Nodes = append(entry.Nodes, journalNode{ + Path: []byte(path), + Blob: node.Blob, + }) + } + nodes = append(nodes, entry) + } + // Encode storage nodes + for owner, subset := range s.storageNodes { entry := journalNodes{Owner: owner} for path, node := range subset { entry.Nodes = append(entry.Nodes, journalNode{ @@ -204,43 +243,61 @@ func (s *nodeSet) decode(r *rlp.Stream) error { if err := r.Decode(&encoded); err != nil { return fmt.Errorf("load nodes: %v", err) } - nodes := make(map[common.Hash]map[string]*trienode.Node) + s.accountNodes = make(map[string]*trienode.Node) + s.storageNodes = make(map[common.Hash]map[string]*trienode.Node) + for _, entry := range encoded { - subset := make(map[string]*trienode.Node) - for _, n := range entry.Nodes { - if len(n.Blob) > 0 { - subset[string(n.Path)] = trienode.New(crypto.Keccak256Hash(n.Blob), n.Blob) - } else { - subset[string(n.Path)] = trienode.NewDeleted() + if entry.Owner == (common.Hash{}) { + // Account nodes + for _, n := range entry.Nodes { + if len(n.Blob) > 0 { + s.accountNodes[string(n.Path)] = trienode.New(crypto.Keccak256Hash(n.Blob), n.Blob) + } else { + s.accountNodes[string(n.Path)] = trienode.NewDeleted() + } + } + } else { + // Storage nodes + subset := make(map[string]*trienode.Node) + for _, n := range entry.Nodes { + if len(n.Blob) > 0 { + subset[string(n.Path)] = trienode.New(crypto.Keccak256Hash(n.Blob), n.Blob) + } else { + subset[string(n.Path)] = trienode.NewDeleted() + } } + s.storageNodes[entry.Owner] = subset } - nodes[entry.Owner] = subset } - s.nodes = nodes s.computeSize() return nil } // write flushes nodes into the provided database batch as a whole. func (s *nodeSet) write(batch ethdb.Batch, clean *fastcache.Cache) int { - return writeNodes(batch, s.nodes, clean) + nodes := make(map[common.Hash]map[string]*trienode.Node) + if len(s.accountNodes) > 0 { + nodes[common.Hash{}] = s.accountNodes + } + for owner, subset := range s.storageNodes { + nodes[owner] = subset + } + return writeNodes(batch, nodes, clean) } // reset clears all cached trie node data. func (s *nodeSet) reset() { - s.nodes = make(map[common.Hash]map[string]*trienode.Node) + s.accountNodes = make(map[string]*trienode.Node) + s.storageNodes = make(map[common.Hash]map[string]*trienode.Node) s.size = 0 } // dbsize returns the approximate size of db write. func (s *nodeSet) dbsize() int { var m int - for owner, nodes := range s.nodes { - if owner == (common.Hash{}) { - m += len(nodes) * len(rawdb.TrieNodeAccountPrefix) // database key prefix - } else { - m += len(nodes) * (len(rawdb.TrieNodeStoragePrefix)) // database key prefix - } + m += len(s.accountNodes) * len(rawdb.TrieNodeAccountPrefix) // database key prefix + for _, nodes := range s.storageNodes { + m += len(nodes) * (len(rawdb.TrieNodeStoragePrefix)) // database key prefix } return m + int(s.size) } From ee30681a8d4d176a3561db20e9c8867dafe97441 Mon Sep 17 00:00:00 2001 From: minh-bq Date: Wed, 2 Apr 2025 14:47:56 +0700 Subject: [PATCH 066/658] core/txpool: add GetMetadata to transaction pool (#31433) This is an alternative to #31309 With eth/68, transaction announcement must have transaction type and size. So in announceTransactions, we need to query the transaction from transaction pool with its hash. This creates overhead in case of blob transaction which needs to load data from billy and RLP decode. This commit creates a lightweight lookup from transaction hash to transaction size and a function GetMetadata to query transaction type and transaction size given the transaction hash. --------- Co-authored-by: Gary Rong --- core/txpool/blobpool/blobpool.go | 81 ++++++++++++++++---------- core/txpool/blobpool/blobpool_test.go | 12 +++- core/txpool/blobpool/evictheap_test.go | 8 +-- core/txpool/blobpool/lookup.go | 35 ++++++++--- core/txpool/legacypool/legacypool.go | 13 +++++ core/txpool/subpool.go | 10 ++++ core/txpool/txpool.go | 11 ++++ eth/handler.go | 4 ++ eth/handler_test.go | 16 +++++ eth/protocols/eth/broadcast.go | 6 +- eth/protocols/eth/handler.go | 5 ++ 11 files changed, 156 insertions(+), 45 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 59a56450401..5a20c3ce5a6 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -87,8 +87,9 @@ type blobTxMeta struct { hash common.Hash // Transaction hash to maintain the lookup table vhashes []common.Hash // Blob versioned hashes to maintain the lookup table - id uint64 // Storage ID in the pool's persistent store - size uint32 // Byte size in the pool's persistent store + id uint64 // Storage ID in the pool's persistent store + storageSize uint32 // Byte size in the pool's persistent store + size uint64 // RLP-encoded size of transaction including the attached blob nonce uint64 // Needed to prioritize inclusion order within an account costCap *uint256.Int // Needed to validate cumulative balance sufficiency @@ -108,19 +109,20 @@ type blobTxMeta struct { // newBlobTxMeta retrieves the indexed metadata fields from a blob transaction // and assembles a helper struct to track in memory. -func newBlobTxMeta(id uint64, size uint32, tx *types.Transaction) *blobTxMeta { +func newBlobTxMeta(id uint64, size uint64, storageSize uint32, tx *types.Transaction) *blobTxMeta { meta := &blobTxMeta{ - hash: tx.Hash(), - vhashes: tx.BlobHashes(), - id: id, - size: size, - nonce: tx.Nonce(), - costCap: uint256.MustFromBig(tx.Cost()), - execTipCap: uint256.MustFromBig(tx.GasTipCap()), - execFeeCap: uint256.MustFromBig(tx.GasFeeCap()), - blobFeeCap: uint256.MustFromBig(tx.BlobGasFeeCap()), - execGas: tx.Gas(), - blobGas: tx.BlobGas(), + hash: tx.Hash(), + vhashes: tx.BlobHashes(), + id: id, + storageSize: storageSize, + size: size, + nonce: tx.Nonce(), + costCap: uint256.MustFromBig(tx.Cost()), + execTipCap: uint256.MustFromBig(tx.GasTipCap()), + execFeeCap: uint256.MustFromBig(tx.GasFeeCap()), + blobFeeCap: uint256.MustFromBig(tx.BlobGasFeeCap()), + execGas: tx.Gas(), + blobGas: tx.BlobGas(), } meta.basefeeJumps = dynamicFeeJumps(meta.execFeeCap) meta.blobfeeJumps = dynamicFeeJumps(meta.blobFeeCap) @@ -480,7 +482,7 @@ func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error { return errors.New("missing blob sidecar") } - meta := newBlobTxMeta(id, size, tx) + meta := newBlobTxMeta(id, tx.Size(), size, tx) if p.lookup.exists(meta.hash) { // This path is only possible after a crash, where deleted items are not // removed via the normal shutdown-startup procedure and thus may get @@ -507,7 +509,7 @@ func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error { p.spent[sender] = new(uint256.Int).Add(p.spent[sender], meta.costCap) p.lookup.track(meta) - p.stored += uint64(meta.size) + p.stored += uint64(meta.storageSize) return nil } @@ -539,7 +541,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 ids = append(ids, txs[i].id) nonces = append(nonces, txs[i].nonce) - p.stored -= uint64(txs[i].size) + p.stored -= uint64(txs[i].storageSize) p.lookup.untrack(txs[i]) // Included transactions blobs need to be moved to the limbo @@ -580,7 +582,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 nonces = append(nonces, txs[0].nonce) p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], txs[0].costCap) - p.stored -= uint64(txs[0].size) + p.stored -= uint64(txs[0].storageSize) p.lookup.untrack(txs[0]) // Included transactions blobs need to be moved to the limbo @@ -636,7 +638,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 dropRepeatedMeter.Mark(1) p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], txs[i].costCap) - p.stored -= uint64(txs[i].size) + p.stored -= uint64(txs[i].storageSize) p.lookup.untrack(txs[i]) if err := p.store.Delete(id); err != nil { @@ -658,7 +660,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 nonces = append(nonces, txs[j].nonce) p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], txs[j].costCap) - p.stored -= uint64(txs[j].size) + p.stored -= uint64(txs[j].storageSize) p.lookup.untrack(txs[j]) } txs = txs[:i] @@ -696,7 +698,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 nonces = append(nonces, last.nonce) p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], last.costCap) - p.stored -= uint64(last.size) + p.stored -= uint64(last.storageSize) p.lookup.untrack(last) } if len(txs) == 0 { @@ -736,7 +738,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 nonces = append(nonces, last.nonce) p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], last.costCap) - p.stored -= uint64(last.size) + p.stored -= uint64(last.storageSize) p.lookup.untrack(last) } p.index[addr] = txs @@ -1002,7 +1004,7 @@ func (p *BlobPool) reinject(addr common.Address, txhash common.Hash) error { } // Update the indices and metrics - meta := newBlobTxMeta(id, p.store.Size(id), tx) + meta := newBlobTxMeta(id, tx.Size(), p.store.Size(id), tx) if _, ok := p.index[addr]; !ok { if err := p.reserve(addr, true); err != nil { log.Warn("Failed to reserve account for blob pool", "tx", tx.Hash(), "from", addr, "err", err) @@ -1016,7 +1018,7 @@ func (p *BlobPool) reinject(addr common.Address, txhash common.Hash) error { p.spent[addr] = new(uint256.Int).Add(p.spent[addr], meta.costCap) } p.lookup.track(meta) - p.stored += uint64(meta.size) + p.stored += uint64(meta.storageSize) return nil } @@ -1041,7 +1043,7 @@ func (p *BlobPool) SetGasTip(tip *big.Int) { nonces = []uint64{tx.nonce} ) p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], txs[i].costCap) - p.stored -= uint64(tx.size) + p.stored -= uint64(tx.storageSize) p.lookup.untrack(tx) txs[i] = nil @@ -1051,7 +1053,7 @@ func (p *BlobPool) SetGasTip(tip *big.Int) { nonces = append(nonces, tx.nonce) p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], tx.costCap) - p.stored -= uint64(tx.size) + p.stored -= uint64(tx.storageSize) p.lookup.untrack(tx) txs[i+1+j] = nil } @@ -1236,6 +1238,25 @@ func (p *BlobPool) GetRLP(hash common.Hash) []byte { return p.getRLP(hash) } +// GetMetadata returns the transaction type and transaction size with the +// given transaction hash. +// +// The size refers the length of the 'rlp encoding' of a blob transaction +// including the attached blobs. +func (p *BlobPool) GetMetadata(hash common.Hash) *txpool.TxMetadata { + p.lock.RLock() + defer p.lock.RUnlock() + + size, ok := p.lookup.sizeOfTx(hash) + if !ok { + return nil + } + return &txpool.TxMetadata{ + Type: types.BlobTxType, + Size: size, + } +} + // GetBlobs returns a number of blobs are proofs for the given versioned hashes. // This is a utility method for the engine API, enabling consensus clients to // retrieve blobs from the pools directly instead of the network. @@ -1375,7 +1396,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) { if err != nil { return err } - meta := newBlobTxMeta(id, p.store.Size(id), tx) + meta := newBlobTxMeta(id, tx.Size(), p.store.Size(id), tx) var ( next = p.state.GetNonce(from) @@ -1403,7 +1424,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) { p.lookup.untrack(prev) p.lookup.track(meta) - p.stored += uint64(meta.size) - uint64(prev.size) + p.stored += uint64(meta.storageSize) - uint64(prev.storageSize) } else { // Transaction extends previously scheduled ones p.index[from] = append(p.index[from], meta) @@ -1413,7 +1434,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) { } p.spent[from] = new(uint256.Int).Add(p.spent[from], meta.costCap) p.lookup.track(meta) - p.stored += uint64(meta.size) + p.stored += uint64(meta.storageSize) } // Recompute the rolling eviction fields. In case of a replacement, this will // recompute all subsequent fields. In case of an append, this will only do @@ -1500,7 +1521,7 @@ func (p *BlobPool) drop() { p.index[from] = txs p.spent[from] = new(uint256.Int).Sub(p.spent[from], drop.costCap) } - p.stored -= uint64(drop.size) + p.stored -= uint64(drop.storageSize) p.lookup.untrack(drop) // Remove the transaction from the pool's eviction heap: diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index d9137cb6794..b7c6cfa51e8 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -376,7 +376,7 @@ func verifyPoolInternals(t *testing.T, pool *BlobPool) { var stored uint64 for _, txs := range pool.index { for _, tx := range txs { - stored += uint64(tx.size) + stored += uint64(tx.storageSize) } } if pool.stored != stored { @@ -1553,6 +1553,16 @@ func TestAdd(t *testing.T) { if err := pool.add(signed); !errors.Is(err, add.err) { t.Errorf("test %d, tx %d: adding transaction error mismatch: have %v, want %v", i, j, err, add.err) } + if add.err == nil { + size, exist := pool.lookup.sizeOfTx(signed.Hash()) + if !exist { + t.Errorf("test %d, tx %d: failed to lookup transaction's size", i, j) + } + if size != signed.Size() { + t.Errorf("test %d, tx %d: transaction's size mismatches: have %v, want %v", + i, j, size, signed.Size()) + } + } verifyPoolInternals(t, pool) } verifyPoolInternals(t, pool) diff --git a/core/txpool/blobpool/evictheap_test.go b/core/txpool/blobpool/evictheap_test.go index e3929324010..de4076e2985 100644 --- a/core/txpool/blobpool/evictheap_test.go +++ b/core/txpool/blobpool/evictheap_test.go @@ -146,7 +146,7 @@ func TestPriceHeapSorting(t *testing.T) { ) index[addr] = []*blobTxMeta{{ id: uint64(j), - size: 128 * 1024, + storageSize: 128 * 1024, nonce: 0, execTipCap: execTip, execFeeCap: execFee, @@ -205,7 +205,7 @@ func benchmarkPriceHeapReinit(b *testing.B, datacap uint64) { ) index[addr] = []*blobTxMeta{{ id: uint64(i), - size: 128 * 1024, + storageSize: 128 * 1024, nonce: 0, execTipCap: execTip, execFeeCap: execFee, @@ -281,7 +281,7 @@ func benchmarkPriceHeapOverflow(b *testing.B, datacap uint64) { ) index[addr] = []*blobTxMeta{{ id: uint64(i), - size: 128 * 1024, + storageSize: 128 * 1024, nonce: 0, execTipCap: execTip, execFeeCap: execFee, @@ -312,7 +312,7 @@ func benchmarkPriceHeapOverflow(b *testing.B, datacap uint64) { ) metas[i] = &blobTxMeta{ id: uint64(int(blobs) + i), - size: 128 * 1024, + storageSize: 128 * 1024, nonce: 0, execTipCap: execTip, execFeeCap: execFee, diff --git a/core/txpool/blobpool/lookup.go b/core/txpool/blobpool/lookup.go index b5cf4d37992..7607cd487a2 100644 --- a/core/txpool/blobpool/lookup.go +++ b/core/txpool/blobpool/lookup.go @@ -20,18 +20,24 @@ import ( "github.com/ethereum/go-ethereum/common" ) +type txMetadata struct { + id uint64 // the billy id of transction + size uint64 // the RLP encoded size of transaction (blobs are included) +} + // lookup maps blob versioned hashes to transaction hashes that include them, -// and transaction hashes to billy entries that include them. +// transaction hashes to billy entries that include them, transaction hashes +// to the transaction size type lookup struct { blobIndex map[common.Hash]map[common.Hash]struct{} - txIndex map[common.Hash]uint64 + txIndex map[common.Hash]*txMetadata } // newLookup creates a new index for tracking blob to tx; and tx to billy mappings. func newLookup() *lookup { return &lookup{ blobIndex: make(map[common.Hash]map[common.Hash]struct{}), - txIndex: make(map[common.Hash]uint64), + txIndex: make(map[common.Hash]*txMetadata), } } @@ -43,8 +49,11 @@ func (l *lookup) exists(txhash common.Hash) bool { // storeidOfTx returns the datastore storage item id of a transaction. func (l *lookup) storeidOfTx(txhash common.Hash) (uint64, bool) { - id, ok := l.txIndex[txhash] - return id, ok + meta, ok := l.txIndex[txhash] + if !ok { + return 0, false + } + return meta.id, true } // storeidOfBlob returns the datastore storage item id of a blob. @@ -61,6 +70,15 @@ func (l *lookup) storeidOfBlob(vhash common.Hash) (uint64, bool) { return 0, false // Weird, don't choke } +// sizeOfTx returns the RLP-encoded size of transaction +func (l *lookup) sizeOfTx(txhash common.Hash) (uint64, bool) { + meta, ok := l.txIndex[txhash] + if !ok { + return 0, false + } + return meta.size, true +} + // track inserts a new set of mappings from blob versioned hashes to transaction // hashes; and from transaction hashes to datastore storage item ids. func (l *lookup) track(tx *blobTxMeta) { @@ -71,8 +89,11 @@ func (l *lookup) track(tx *blobTxMeta) { } l.blobIndex[vhash][tx.hash] = struct{}{} // may be double mapped if a tx contains the same blob twice } - // Map the transaction hash to the datastore id - l.txIndex[tx.hash] = tx.id + // Map the transaction hash to the datastore id and RLP-encoded transaction size + l.txIndex[tx.hash] = &txMetadata{ + id: tx.id, + size: tx.size, + } } // untrack removes a set of mappings from blob versioned hashes to transaction diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index dafd1858367..9066f3e16b2 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -1035,6 +1035,19 @@ func (pool *LegacyPool) GetRLP(hash common.Hash) []byte { return encoded } +// GetMetadata returns the transaction type and transaction size with the +// given transaction hash. +func (pool *LegacyPool) GetMetadata(hash common.Hash) *txpool.TxMetadata { + tx := pool.all.Get(hash) + if tx == nil { + return nil + } + return &txpool.TxMetadata{ + Type: tx.Type(), + Size: tx.Size(), + } +} + // GetBlobs is not supported by the legacy transaction pool, it is just here to // implement the txpool.SubPool interface. func (pool *LegacyPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof) { diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index 1392cfb2745..f5cb852d8ff 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -86,6 +86,12 @@ type PendingFilter struct { OnlyBlobTxs bool // Return only blob transactions (block blob-space filling) } +// TxMetadata denotes the metadata of a transaction. +type TxMetadata struct { + Type uint8 // The type of the transaction + Size uint64 // The length of the 'rlp encoding' of a transaction +} + // SubPool represents a specialized transaction pool that lives on its own (e.g. // blob pool). Since independent of how many specialized pools we have, they do // need to be updated in lockstep and assemble into one coherent view for block @@ -127,6 +133,10 @@ type SubPool interface { // GetRLP returns a RLP-encoded transaction if it is contained in the pool. GetRLP(hash common.Hash) []byte + // GetMetadata returns the transaction type and transaction size with the + // given transaction hash. + GetMetadata(hash common.Hash) *TxMetadata + // GetBlobs returns a number of blobs are proofs for the given versioned hashes. // This is a utility method for the engine API, enabling consensus clients to // retrieve blobs from the pools directly instead of the network. diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 649c5d1a785..083aac92c66 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -354,6 +354,17 @@ func (p *TxPool) GetRLP(hash common.Hash) []byte { return nil } +// GetMetadata returns the transaction type and transaction size with the given +// hash. +func (p *TxPool) GetMetadata(hash common.Hash) *TxMetadata { + for _, subpool := range p.subpools { + if meta := subpool.GetMetadata(hash); meta != nil { + return meta + } + } + return nil +} + // GetBlobs returns a number of blobs are proofs for the given versioned hashes. // This is a utility method for the engine API, enabling consensus clients to // retrieve blobs from the pools directly instead of the network. diff --git a/eth/handler.go b/eth/handler.go index 7179c9980b0..b2ad6effdb4 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -71,6 +71,10 @@ type txPool interface { // with given tx hash. GetRLP(hash common.Hash) []byte + // GetMetadata returns the transaction type and transaction size with the + // given transaction hash. + GetMetadata(hash common.Hash) *txpool.TxMetadata + // Add should add the given transactions to the pool. Add(txs []*types.Transaction, sync bool) []error diff --git a/eth/handler_test.go b/eth/handler_test.go index 0c6b9854e6f..fb3103f241e 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -93,6 +93,22 @@ func (p *testTxPool) GetRLP(hash common.Hash) []byte { return nil } +// GetMetadata returns the transaction type and transaction size with the given +// hash. +func (p *testTxPool) GetMetadata(hash common.Hash) *txpool.TxMetadata { + p.lock.Lock() + defer p.lock.Unlock() + + tx := p.pool[hash] + if tx != nil { + return &txpool.TxMetadata{ + Type: tx.Type(), + Size: tx.Size(), + } + } + return nil +} + // Add appends a batch of transactions to the pool, and notifies any // listeners if the addition channel is non nil func (p *testTxPool) Add(txs []*types.Transaction, sync bool) []error { diff --git a/eth/protocols/eth/broadcast.go b/eth/protocols/eth/broadcast.go index f0ed1d6bc9a..21cea0d4efa 100644 --- a/eth/protocols/eth/broadcast.go +++ b/eth/protocols/eth/broadcast.go @@ -116,10 +116,10 @@ func (p *Peer) announceTransactions() { size common.StorageSize ) for count = 0; count < len(queue) && size < maxTxPacketSize; count++ { - if tx := p.txpool.Get(queue[count]); tx != nil { + if meta := p.txpool.GetMetadata(queue[count]); meta != nil { pending = append(pending, queue[count]) - pendingTypes = append(pendingTypes, tx.Type()) - pendingSizes = append(pendingSizes, uint32(tx.Size())) + pendingTypes = append(pendingTypes, meta.Type) + pendingSizes = append(pendingSizes, uint32(meta.Size)) size += common.HashLength } } diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go index eca6777bd63..f2a3cb02926 100644 --- a/eth/protocols/eth/handler.go +++ b/eth/protocols/eth/handler.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/p2p" @@ -90,6 +91,10 @@ type TxPool interface { // GetRLP retrieves the RLP-encoded transaction from the local txpool with // the given hash. GetRLP(hash common.Hash) []byte + + // GetMetadata returns the transaction type and transaction size with the + // given transaction hash. + GetMetadata(hash common.Hash) *txpool.TxMetadata } // MakeProtocols constructs the P2P protocol definitions for `eth`. From 3e4fbce034b384c99afeead6cf0f72be0a2b8f13 Mon Sep 17 00:00:00 2001 From: thinkAfCod Date: Wed, 2 Apr 2025 19:47:44 +0800 Subject: [PATCH 067/658] p2p/discover: repeat exact encoding when resending WHOAREYOU packet (#31543) When resending the WHOAREYOU packet, a new nonce and random IV should not be generated. The sent packet needs to match the previously-sent one exactly in order to make the handshake retry work. --------- Co-authored-by: Felix Lange --- p2p/discover/v5_udp_test.go | 40 +++++++++++++++++++++++---------- p2p/discover/v5wire/encoding.go | 16 +++++++++++-- p2p/discover/v5wire/msg.go | 3 +++ 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go index 3026dff5380..606b35c4f29 100644 --- a/p2p/discover/v5_udp_test.go +++ b/p2p/discover/v5_udp_test.go @@ -181,29 +181,35 @@ func TestUDPv5_handshakeRepeatChallenge(t *testing.T) { nonce1 := v5wire.Nonce{1} nonce2 := v5wire.Nonce{2} nonce3 := v5wire.Nonce{3} - check := func(p *v5wire.Whoareyou, wantNonce v5wire.Nonce) { + var firstAuthTag *v5wire.Nonce + check := func(p *v5wire.Whoareyou, authTag, wantNonce v5wire.Nonce) { t.Helper() if p.Nonce != wantNonce { - t.Error("wrong nonce in WHOAREYOU:", p.Nonce, wantNonce) + t.Error("wrong nonce in WHOAREYOU:", p.Nonce, "want:", wantNonce) + } + if firstAuthTag == nil { + firstAuthTag = &authTag + } else if authTag != *firstAuthTag { + t.Error("wrong auth tag in WHOAREYOU header:", authTag, "want:", *firstAuthTag) } } // Unknown packet from unknown node. test.packetIn(&v5wire.Unknown{Nonce: nonce1}) - test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) { - check(p, nonce1) + test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, authTag v5wire.Nonce) { + check(p, authTag, nonce1) }) // Second unknown packet. Here we expect the response to reference the // first unknown packet. test.packetIn(&v5wire.Unknown{Nonce: nonce2}) - test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) { - check(p, nonce1) + test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, authTag v5wire.Nonce) { + check(p, authTag, nonce1) }) // Third unknown packet. This should still return the first nonce. test.packetIn(&v5wire.Unknown{Nonce: nonce3}) - test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) { - check(p, nonce1) + test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, authTag v5wire.Nonce) { + check(p, authTag, nonce1) }) } @@ -766,20 +772,30 @@ type testCodecFrame struct { } func (c *testCodec) Encode(toID enode.ID, addr string, p v5wire.Packet, _ *v5wire.Whoareyou) ([]byte, v5wire.Nonce, error) { + // To match the behavior of v5wire.Codec, we return the cached encoding of + // WHOAREYOU challenges. + if wp, ok := p.(*v5wire.Whoareyou); ok && len(wp.Encoded) > 0 { + return wp.Encoded, wp.Nonce, nil + } + c.ctr++ var authTag v5wire.Nonce binary.BigEndian.PutUint64(authTag[:], c.ctr) + penc, _ := rlp.EncodeToBytes(p) + frame, err := rlp.EncodeToBytes(testCodecFrame{c.id, authTag, p.Kind(), penc}) + if err != nil { + return frame, authTag, err + } + // Store recently sent challenges. if w, ok := p.(*v5wire.Whoareyou); ok { - // Store recently sent Whoareyou challenges. + w.Nonce = authTag + w.Encoded = frame if c.sentChallenges == nil { c.sentChallenges = make(map[enode.ID]*v5wire.Whoareyou) } c.sentChallenges[toID] = w } - - penc, _ := rlp.EncodeToBytes(p) - frame, err := rlp.EncodeToBytes(testCodecFrame{c.id, authTag, p.Kind(), penc}) return frame, authTag, err } diff --git a/p2p/discover/v5wire/encoding.go b/p2p/discover/v5wire/encoding.go index e50b7cd16d1..b16d14eda55 100644 --- a/p2p/discover/v5wire/encoding.go +++ b/p2p/discover/v5wire/encoding.go @@ -189,6 +189,11 @@ func (c *Codec) Encode(id enode.ID, addr string, packet Packet, challenge *Whoar ) switch { case packet.Kind() == WhoareyouPacket: + // just send the WHOAREYOU packet raw again, rather than the re-encoded challenge data + w := packet.(*Whoareyou) + if len(w.Encoded) > 0 { + return w.Encoded, w.Nonce, nil + } head, err = c.encodeWhoareyou(id, packet.(*Whoareyou)) case challenge != nil: // We have an unanswered challenge, send handshake. @@ -218,15 +223,22 @@ func (c *Codec) Encode(id enode.ID, addr string, packet Packet, challenge *Whoar // Store sent WHOAREYOU challenges. if challenge, ok := packet.(*Whoareyou); ok { challenge.ChallengeData = bytesCopy(&c.buf) + enc, err := c.EncodeRaw(id, head, msgData) + if err != nil { + return nil, Nonce{}, err + } + challenge.Encoded = bytes.Clone(enc) c.sc.storeSentHandshake(id, addr, challenge) - } else if msgData == nil { + return enc, head.Nonce, err + } + + if msgData == nil { headerData := c.buf.Bytes() msgData, err = c.encryptMessage(session, packet, &head, headerData) if err != nil { return nil, Nonce{}, err } } - enc, err := c.EncodeRaw(id, head, msgData) return enc, head.Nonce, err } diff --git a/p2p/discover/v5wire/msg.go b/p2p/discover/v5wire/msg.go index 401db2f6c58..089fd4ebdc8 100644 --- a/p2p/discover/v5wire/msg.go +++ b/p2p/discover/v5wire/msg.go @@ -73,6 +73,9 @@ type ( Node *enode.Node sent mclock.AbsTime // for handshake GC. + + // Encoded is packet raw data for sending out, but should not be include in the RLP encoding. + Encoded []byte `rlp:"-"` } // PING is sent during liveness checks. From d2176f463b873567ff8982f362b843a898c429d1 Mon Sep 17 00:00:00 2001 From: thinkAfCod Date: Wed, 2 Apr 2025 20:56:21 +0800 Subject: [PATCH 068/658] p2p/discover: pass node instead of node ID to TALKREQ handler (#31075) This is for the implementation of Portal Network in the Shisui client. Their handler needs access to the node object in order to send further calls to the requesting node. This is a breaking API change but it should be fine, since there are basically no known users of TALKREQ outside of Portal network. --------- Signed-off-by: thinkAfCod Co-authored-by: Felix Lange --- p2p/discover/v5_talk.go | 14 ++++++++++---- p2p/discover/v5_udp.go | 3 +++ p2p/discover/v5_udp_test.go | 6 +++++- p2p/discover/v5wire/encoding.go | 8 ++++++-- p2p/discover/v5wire/encoding_test.go | 14 +++++++------- p2p/discover/v5wire/session.go | 16 ++++++++++++++-- 6 files changed, 45 insertions(+), 16 deletions(-) diff --git a/p2p/discover/v5_talk.go b/p2p/discover/v5_talk.go index 2246b47141c..dca09870d89 100644 --- a/p2p/discover/v5_talk.go +++ b/p2p/discover/v5_talk.go @@ -39,7 +39,7 @@ const talkHandlerLaunchTimeout = 400 * time.Millisecond // Note that talk handlers are expected to come up with a response very quickly, within at // most 200ms or so. If the handler takes longer than that, the remote end may time out // and wont receive the response. -type TalkRequestHandler func(enode.ID, *net.UDPAddr, []byte) []byte +type TalkRequestHandler func(*enode.Node, *net.UDPAddr, []byte) []byte type talkSystem struct { transport *UDPv5 @@ -72,13 +72,19 @@ func (t *talkSystem) register(protocol string, handler TalkRequestHandler) { // handleRequest handles a talk request. func (t *talkSystem) handleRequest(id enode.ID, addr netip.AddrPort, req *v5wire.TalkRequest) { + n := t.transport.codec.SessionNode(id, addr.String()) + if n == nil { + // The node must be contained in the session here, since we wouldn't have + // received the request otherwise. + panic("missing node in session") + } t.mutex.Lock() handler, ok := t.handlers[req.Protocol] t.mutex.Unlock() if !ok { resp := &v5wire.TalkResponse{ReqID: req.ReqID} - t.transport.sendResponse(id, addr, resp) + t.transport.sendResponse(n.ID(), addr, resp) return } @@ -90,9 +96,9 @@ func (t *talkSystem) handleRequest(id enode.ID, addr netip.AddrPort, req *v5wire go func() { defer func() { t.slots <- struct{}{} }() udpAddr := &net.UDPAddr{IP: addr.Addr().AsSlice(), Port: int(addr.Port())} - respMessage := handler(id, udpAddr, req.Message) + respMessage := handler(n, udpAddr, req.Message) resp := &v5wire.TalkResponse{ReqID: req.ReqID, Message: respMessage} - t.transport.sendFromAnotherThread(id, addr, resp) + t.transport.sendFromAnotherThread(n.ID(), addr, resp) }() case <-timeout.C: // Couldn't get it in time, drop the request. diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go index 6f7c7971528..9679f5c61a9 100644 --- a/p2p/discover/v5_udp.go +++ b/p2p/discover/v5_udp.go @@ -64,6 +64,9 @@ type codecV5 interface { // CurrentChallenge returns the most recent WHOAREYOU challenge that was encoded to given node. // This will return a non-nil value if there is an active handshake attempt with the node, and nil otherwise. CurrentChallenge(id enode.ID, addr string) *v5wire.Whoareyou + + // SessionNode returns a node that has completed the handshake. + SessionNode(id enode.ID, addr string) *enode.Node } // UDPv5 is the implementation of protocol version 5. diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go index 606b35c4f29..3a384aab129 100644 --- a/p2p/discover/v5_udp_test.go +++ b/p2p/discover/v5_udp_test.go @@ -492,7 +492,7 @@ func TestUDPv5_talkHandling(t *testing.T) { defer test.close() var recvMessage []byte - test.udp.RegisterTalkHandler("test", func(id enode.ID, addr *net.UDPAddr, message []byte) []byte { + test.udp.RegisterTalkHandler("test", func(n *enode.Node, addr *net.UDPAddr, message []byte) []byte { recvMessage = message return []byte("test response") }) @@ -811,6 +811,10 @@ func (c *testCodec) Decode(input []byte, addr string) (enode.ID, *enode.Node, v5 return frame.NodeID, nil, p, nil } +func (c *testCodec) SessionNode(id enode.ID, addr string) *enode.Node { + return c.test.nodesByID[id].Node() +} + func (c *testCodec) decodeFrame(input []byte) (frame testCodecFrame, p v5wire.Packet, err error) { if err = rlp.DecodeBytes(input, &frame); err != nil { return frame, nil, fmt.Errorf("invalid frame: %v", err) diff --git a/p2p/discover/v5wire/encoding.go b/p2p/discover/v5wire/encoding.go index b16d14eda55..ec5ef8a261a 100644 --- a/p2p/discover/v5wire/encoding.go +++ b/p2p/discover/v5wire/encoding.go @@ -359,7 +359,7 @@ func (c *Codec) encodeHandshakeHeader(toID enode.ID, addr string, challenge *Who } // TODO: this should happen when the first authenticated message is received - c.sc.storeNewSession(toID, addr, session) + c.sc.storeNewSession(toID, addr, session, challenge.Node) // Encode the auth header. var ( @@ -534,7 +534,7 @@ func (c *Codec) decodeHandshakeMessage(fromAddr string, head *Header, headerData } // Handshake OK, drop the challenge and store the new session keys. - c.sc.storeNewSession(auth.h.SrcID, fromAddr, session) + c.sc.storeNewSession(auth.h.SrcID, fromAddr, session, node) c.sc.deleteHandshake(auth.h.SrcID, fromAddr) return node, msg, nil } @@ -656,6 +656,10 @@ func (c *Codec) decryptMessage(input, nonce, headerData, readKey []byte) (Packet return DecodeMessage(msgdata[0], msgdata[1:]) } +func (c *Codec) SessionNode(id enode.ID, addr string) *enode.Node { + return c.sc.readNode(id, addr) +} + // checkValid performs some basic validity checks on the header. // The packetLen here is the length remaining after the static header. func (h *StaticHeader) checkValid(packetLen int, protocolID [6]byte) error { diff --git a/p2p/discover/v5wire/encoding_test.go b/p2p/discover/v5wire/encoding_test.go index df97e40e892..2304d0f1327 100644 --- a/p2p/discover/v5wire/encoding_test.go +++ b/p2p/discover/v5wire/encoding_test.go @@ -166,7 +166,7 @@ func TestHandshake_rekey(t *testing.T) { readKey: []byte("BBBBBBBBBBBBBBBB"), writeKey: []byte("AAAAAAAAAAAAAAAA"), } - net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), session) + net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), session, net.nodeB.n()) // A -> B FINDNODE (encrypted with zero keys) findnode, authTag := net.nodeA.encode(t, net.nodeB, &Findnode{}) @@ -209,8 +209,8 @@ func TestHandshake_rekey2(t *testing.T) { readKey: []byte("CCCCCCCCCCCCCCCC"), writeKey: []byte("DDDDDDDDDDDDDDDD"), } - net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), initKeysA) - net.nodeB.c.sc.storeNewSession(net.nodeA.id(), net.nodeA.addr(), initKeysB) + net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), initKeysA, net.nodeB.n()) + net.nodeB.c.sc.storeNewSession(net.nodeA.id(), net.nodeA.addr(), initKeysB, net.nodeA.n()) // A -> B FINDNODE encrypted with initKeysA findnode, authTag := net.nodeA.encode(t, net.nodeB, &Findnode{Distances: []uint{3}}) @@ -362,8 +362,8 @@ func TestTestVectorsV5(t *testing.T) { ENRSeq: 2, }, prep: func(net *handshakeTest) { - net.nodeA.c.sc.storeNewSession(idB, addr, session) - net.nodeB.c.sc.storeNewSession(idA, addr, session.keysFlipped()) + net.nodeA.c.sc.storeNewSession(idB, addr, session, net.nodeB.n()) + net.nodeB.c.sc.storeNewSession(idA, addr, session.keysFlipped(), net.nodeA.n()) }, }, { @@ -499,8 +499,8 @@ func BenchmarkV5_DecodePing(b *testing.B) { readKey: []byte{233, 203, 93, 195, 86, 47, 177, 186, 227, 43, 2, 141, 244, 230, 120, 17}, writeKey: []byte{79, 145, 252, 171, 167, 216, 252, 161, 208, 190, 176, 106, 214, 39, 178, 134}, } - net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), session) - net.nodeB.c.sc.storeNewSession(net.nodeA.id(), net.nodeA.addr(), session.keysFlipped()) + net.nodeA.c.sc.storeNewSession(net.nodeB.id(), net.nodeB.addr(), session, net.nodeB.n()) + net.nodeB.c.sc.storeNewSession(net.nodeA.id(), net.nodeA.addr(), session.keysFlipped(), net.nodeA.n()) addrB := net.nodeA.addr() ping := &Ping{ReqID: []byte("reqid"), ENRSeq: 5} enc, _, err := net.nodeA.c.Encode(net.nodeB.id(), addrB, ping, nil) diff --git a/p2p/discover/v5wire/session.go b/p2p/discover/v5wire/session.go index 862c21fcee9..5a2166b1438 100644 --- a/p2p/discover/v5wire/session.go +++ b/p2p/discover/v5wire/session.go @@ -54,11 +54,12 @@ type session struct { writeKey []byte readKey []byte nonceCounter uint32 + node *enode.Node } // keysFlipped returns a copy of s with the read and write keys flipped. func (s *session) keysFlipped() *session { - return &session{s.readKey, s.writeKey, s.nonceCounter} + return &session{s.readKey, s.writeKey, s.nonceCounter, s.node} } func NewSessionCache(maxItems int, clock mclock.Clock) *SessionCache { @@ -103,8 +104,19 @@ func (sc *SessionCache) readKey(id enode.ID, addr string) []byte { return nil } +func (sc *SessionCache) readNode(id enode.ID, addr string) *enode.Node { + if s := sc.session(id, addr); s != nil { + return s.node + } + return nil +} + // storeNewSession stores new encryption keys in the cache. -func (sc *SessionCache) storeNewSession(id enode.ID, addr string, s *session) { +func (sc *SessionCache) storeNewSession(id enode.ID, addr string, s *session, n *enode.Node) { + if n == nil { + panic("nil node in storeNewSession") + } + s.node = n sc.sessions.Add(sessionID{id, addr}, s) } From 82f01f9f24192f85de0019c1778ea239af701a41 Mon Sep 17 00:00:00 2001 From: owen <158327443+owenzimmew06@users.noreply.github.com> Date: Wed, 2 Apr 2025 20:19:25 +0300 Subject: [PATCH 069/658] README: fixup typos (#31540) Fixes a few typos in readme. --- cmd/clef/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/clef/README.md b/cmd/clef/README.md index a73ffd5b455..d23e70a3d47 100644 --- a/cmd/clef/README.md +++ b/cmd/clef/README.md @@ -1,6 +1,6 @@ # Clef -Clef can be used to sign transactions and data and is meant as a(n eventual) replacement for Geth's account management. This allows DApps to not depend on Geth's account management. When a DApp wants to sign data (or a transaction), it can send the content to Clef, which will then provide the user with context and asks for permission to sign the content. If the users grants the signing request, Clef will send the signature back to the DApp. +Clef can be used to sign transactions and data and is meant as a(n eventual) replacement for Geth's account management. This allows DApps to not depend on Geth's account management. When a DApp wants to sign data (or a transaction), it can send the content to Clef, which will then provide the user with context and ask for permission to sign the content. If the user grants the signing request, Clef will send the signature back to the DApp. This setup allows a DApp to connect to a remote Ethereum node and send transactions that are locally signed. This can help in situations when a DApp is connected to an untrusted remote Ethereum node, because a local one is not available, not synchronized with the chain, or is a node that has no built-in (or limited) account management. @@ -123,7 +123,7 @@ The External API is **untrusted**: it does not accept credentials, nor does it e Clef has one native console-based UI, for operation without any standalone tools. However, there is also an API to communicate with an external UI. To enable that UI, the signer needs to be executed with the `--stdio-ui` option, which allocates `stdin` / `stdout` for the UI API. -An example (insecure) proof-of-concept of has been implemented in `pythonsigner.py`. +An example (insecure) proof-of-concept has been implemented in `pythonsigner.py`. The model is as follows: @@ -335,7 +335,7 @@ Bash example: #### Arguments - content type [string]: type of signed data - - `text/validator`: hex data with custom validator defined in a contract + - `text/validator`: hex data with a custom validator defined in a contract - `application/clique`: [clique](https://github.com/ethereum/EIPs/issues/225) headers - `text/plain`: simple hex data validated by `account_ecRecover` - account [address]: account to sign with From e6098437a6b15c74ef5eb80ca0b99d38d2dd23d6 Mon Sep 17 00:00:00 2001 From: "fuder.eth" <139509124+vtjl10@users.noreply.github.com> Date: Wed, 2 Apr 2025 22:52:40 +0300 Subject: [PATCH 070/658] all: fix typos in docs and comments (#31548) Co-authored-by: lightclient --- cmd/clef/rules.md | 2 +- cmd/evm/README.md | 2 +- cmd/evm/eest.go | 2 +- eth/downloader/skeleton.go | 2 +- internal/guide/guide_test.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/clef/rules.md b/cmd/clef/rules.md index 3cd1bb52c6f..ef89ec00c89 100644 --- a/cmd/clef/rules.md +++ b/cmd/clef/rules.md @@ -2,7 +2,7 @@ The `signer` binary contains a ruleset engine, implemented with [OttoVM](https://github.com/robertkrimen/otto) -It enables usecases like the following: +It enables use cases like the following: * I want to auto-approve transactions with contract `CasinoDapp`, with up to `0.05 ether` in value to maximum `1 ether` per 24h period * I want to auto-approve transaction to contract `EthAlarmClock` with `data`=`0xdeadbeef`, if `value=0`, `gas < 44k` and `gasPrice < 40Gwei` diff --git a/cmd/evm/README.md b/cmd/evm/README.md index f95b6b4d7b3..17d663a50cf 100644 --- a/cmd/evm/README.md +++ b/cmd/evm/README.md @@ -16,7 +16,7 @@ which can 1. Take a prestate, including - Accounts, - Block context information, - - Previous blockshashes (*optional) + - Previous block hashes (*optional) 2. Apply a set of transactions, 3. Apply a mining-reward (*optional), 4. And generate a post-state, including diff --git a/cmd/evm/eest.go b/cmd/evm/eest.go index 43071a3e5d3..4cda2fc5174 100644 --- a/cmd/evm/eest.go +++ b/cmd/evm/eest.go @@ -22,7 +22,7 @@ import "regexp" // within its filename by the execution spec test (EEST). type testMetadata struct { fork string - module string // which python module gnerated the test, e.g. eip7702 + module string // which python module generated the test, e.g. eip7702 file string // exact file the test came from, e.g. test_gas.py function string // func that created the test, e.g. test_valid_mcopy_operations parameters string // the name of the parameters which were used to fill the test, e.g. zero_inputs diff --git a/eth/downloader/skeleton.go b/eth/downloader/skeleton.go index 04421a2bf5c..ad74dab8446 100644 --- a/eth/downloader/skeleton.go +++ b/eth/downloader/skeleton.go @@ -275,7 +275,7 @@ func (s *skeleton) startup() { for { // If the sync cycle terminated or was terminated, propagate up when // higher layers request termination. There's no fancy explicit error - // signalling as the sync loop should never terminate (TM). + // signaling as the sync loop should never terminate (TM). newhead, err := s.sync(head) switch { case err == errSyncLinked: diff --git a/internal/guide/guide_test.go b/internal/guide/guide_test.go index 573898d7d07..71614c125d2 100644 --- a/internal/guide/guide_test.go +++ b/internal/guide/guide_test.go @@ -15,7 +15,7 @@ // along with the go-ethereum library. If not, see . // This file contains the code snippets from the developer's guide embedded into -// Go tests. This ensures that any code published in out guides will not break +// Go tests. This ensures that any code published in our guides will not break // accidentally via some code update. If some API changes nonetheless that needs // modifying this file, please port any modification over into the developer's // guide wiki pages too! From 22c0605b68f544cbc6095c50e734eba9758ec34e Mon Sep 17 00:00:00 2001 From: antonis19 Date: Thu, 3 Apr 2025 06:35:52 +0200 Subject: [PATCH 071/658] eth/protocols/eth: improve over/underflow handling in `GetBlockHeaders` (#31522) --- eth/protocols/eth/handler_test.go | 28 ++++++++++++++++++++++++++++ eth/protocols/eth/handlers.go | 17 ++++++++++++----- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index 36f5e90c9ac..bbbd888701d 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -297,6 +297,34 @@ func testGetBlockHeaders(t *testing.T, protocol uint) { backend.chain.GetBlockByNumber(0).Hash(), }, }, + // Check a corner case where skipping causes overflow with reverse=false + { + &GetBlockHeadersRequest{Origin: HashOrNumber{Number: 1}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1}, + []common.Hash{ + backend.chain.GetBlockByNumber(1).Hash(), + }, + }, + // Check a corner case where skipping causes overflow with reverse=true + { + &GetBlockHeadersRequest{Origin: HashOrNumber{Number: 1}, Amount: 2, Reverse: true, Skip: math.MaxUint64 - 1}, + []common.Hash{ + backend.chain.GetBlockByNumber(1).Hash(), + }, + }, + // Check another corner case where skipping causes overflow with reverse=false + { + &GetBlockHeadersRequest{Origin: HashOrNumber{Number: 1}, Amount: 2, Reverse: false, Skip: math.MaxUint64}, + []common.Hash{ + backend.chain.GetBlockByNumber(1).Hash(), + }, + }, + // Check another corner case where skipping causes overflow with reverse=true + { + &GetBlockHeadersRequest{Origin: HashOrNumber{Number: 1}, Amount: 2, Reverse: true, Skip: math.MaxUint64}, + []common.Hash{ + backend.chain.GetBlockByNumber(1).Hash(), + }, + }, // Check a corner case where skipping overflow loops back into the chain start { &GetBlockHeadersRequest{Origin: HashOrNumber{Hash: backend.chain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1}, diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index ab56be7ffc6..fda650da1cf 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -128,15 +128,22 @@ func serviceNonContiguousBlockHeaderQuery(chain *core.BlockChain, query *GetBloc } case query.Reverse: // Number based traversal towards the genesis block - if query.Origin.Number >= query.Skip+1 { - query.Origin.Number -= query.Skip + 1 - } else { + current := query.Origin.Number + ancestor := current - (query.Skip + 1) + if ancestor >= current { // check for underflow unknown = true + } else { + query.Origin.Number = ancestor } case !query.Reverse: - // Number based traversal towards the leaf block - query.Origin.Number += query.Skip + 1 + current := query.Origin.Number + next := current + query.Skip + 1 + if next <= current { // check for overflow + unknown = true + } else { + query.Origin.Number = next + } } } return headers From 90d44e715d4f44fdad582f458c5973b0e6463082 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 3 Apr 2025 21:16:35 +0800 Subject: [PATCH 072/658] core, eth/downloader: implement pruning mode sync (#31414) This pull request introduces new sync logic for pruning mode. The downloader will now skip insertion of block bodies and receipts before the configured history cutoff point. Originally, in snap sync, the header chain and other components (bodies and receipts) were inserted separately. However, in Proof-of-Stake, this separation is unnecessary since the sync target is already verified by the CL. To simplify the process, this pull request modifies `InsertReceiptChain` to insert headers along with block bodies and receipts together. Besides, `InsertReceiptChain` doesn't have the notion of reorg, as the common ancestor is always be found before the sync and extra side chain is truncated at the beginning if they fall in the ancient store. The stale canonical chain flags will always be rewritten by the new chain. Explicit reorg logic is no longer required in `InsertReceiptChain`. --- cmd/geth/chaincmd.go | 2 +- cmd/utils/cmd.go | 10 +- cmd/utils/history_test.go | 2 +- core/blockchain.go | 251 +++++++++++---------- core/blockchain_reader.go | 6 + core/blockchain_test.go | 341 +++++++++++++++++++---------- core/error.go | 2 - core/headerchain.go | 10 +- core/rawdb/accessors_chain.go | 24 ++ core/rawdb/accessors_chain_test.go | 42 ++++ core/txindexer.go | 3 +- eth/backend.go | 34 +-- eth/downloader/beaconsync.go | 24 +- eth/downloader/downloader.go | 144 ++++++++---- eth/downloader/metrics.go | 1 - eth/downloader/queue.go | 242 +------------------- eth/downloader/resultstore.go | 4 +- 17 files changed, 599 insertions(+), 543 deletions(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 708dd8fb7a3..0c36b82af58 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -457,7 +457,7 @@ func importHistory(ctx *cli.Context) error { network = networks[0] } - if err := utils.ImportHistory(chain, db, dir, network); err != nil { + if err := utils.ImportHistory(chain, dir, network); err != nil { return err } fmt.Printf("Import done in %v\n", time.Since(start)) diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index d91d6ca5a80..d34e15ebc08 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -246,8 +246,9 @@ func readList(filename string) ([]string, error) { } // ImportHistory imports Era1 files containing historical block information, -// starting from genesis. -func ImportHistory(chain *core.BlockChain, db ethdb.Database, dir string, network string) error { +// starting from genesis. The assumption is held that the provided chain +// segment in Era1 file should all be canonical and verified. +func ImportHistory(chain *core.BlockChain, dir string, network string) error { if chain.CurrentSnapBlock().Number.BitLen() != 0 { return errors.New("history import only supported when starting from genesis") } @@ -308,11 +309,6 @@ func ImportHistory(chain *core.BlockChain, db ethdb.Database, dir string, networ if err != nil { return fmt.Errorf("error reading receipts %d: %w", it.Number(), err) } - if status, err := chain.HeaderChain().InsertHeaderChain([]*types.Header{block.Header()}, start); err != nil { - return fmt.Errorf("error inserting header %d: %w", it.Number(), err) - } else if status != core.CanonStatTy { - return fmt.Errorf("error inserting header %d, not canon: %v", it.Number(), status) - } if _, err := chain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{receipts}, 2^64-1); err != nil { return fmt.Errorf("error inserting body %d: %w", it.Number(), err) } diff --git a/cmd/utils/history_test.go b/cmd/utils/history_test.go index 8654c454f95..d3c6bda1c50 100644 --- a/cmd/utils/history_test.go +++ b/cmd/utils/history_test.go @@ -171,7 +171,7 @@ func TestHistoryImportAndExport(t *testing.T) { if err != nil { t.Fatalf("unable to initialize chain: %v", err) } - if err := ImportHistory(imported, db2, dir, "mainnet"); err != nil { + if err := ImportHistory(imported, dir, "mainnet"); err != nil { t.Fatalf("failed to import chain: %v", err) } if have, want := imported.CurrentHeader(), chain.CurrentHeader(); have.Hash() != want.Hash() { diff --git a/core/blockchain.go b/core/blockchain.go index d80236c9022..d56996dadbe 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -24,6 +24,7 @@ import ( "math/big" "runtime" "slices" + "sort" "strings" "sync" "sync/atomic" @@ -157,7 +158,8 @@ type CacheConfig struct { // This defines the cutoff block for history expiry. // Blocks before this number may be unavailable in the chain database. - HistoryPruningCutoff uint64 + HistoryPruningCutoffNumber uint64 + HistoryPruningCutoffHash common.Hash } // triedbConfig derives the configures for trie database. @@ -262,7 +264,6 @@ type BlockChain struct { txLookupLock sync.RWMutex txLookupCache *lru.Cache[common.Hash, txLookup] - wg sync.WaitGroup quit chan struct{} // shutdown signal, closed in Stop. stopping atomic.Bool // false if chain is running, true when stopped procInterrupt atomic.Bool // interrupt signaler for block processing @@ -333,10 +334,10 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis bc.processor = NewStateProcessor(chainConfig, bc.hc) genesisHeader := bc.GetHeaderByNumber(0) - bc.genesisBlock = types.NewBlockWithHeader(genesisHeader) - if bc.genesisBlock == nil { + if genesisHeader == nil { return nil, ErrNoGenesis } + bc.genesisBlock = types.NewBlockWithHeader(genesisHeader) bc.currentBlock.Store(nil) bc.currentSnapBlock.Store(nil) @@ -1110,7 +1111,6 @@ func (bc *BlockChain) stopWithoutSaving() { // the mutex should become available quickly. It cannot be taken again after Close has // returned. bc.chainmu.Close() - bc.wg.Wait() } // Stop stops the blockchain service. If any imports are currently in progress @@ -1197,79 +1197,64 @@ const ( SideStatTy ) -// InsertReceiptChain attempts to complete an already existing header chain with -// transaction and receipt data. +// InsertReceiptChain inserts a batch of blocks along with their receipts into +// the database. Unlike InsertChain, this function does not verify the state root +// in the blocks. It is used exclusively for snap sync. All the inserted blocks +// will be regarded as canonical, chain reorg is not supported. +// +// The optional ancientLimit can also be specified and chain segment before that +// will be directly stored in the ancient, getting rid of the chain migration. func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts, ancientLimit uint64) (int, error) { - // We don't require the chainMu here since we want to maximize the - // concurrency of header insertion and receipt insertion. - bc.wg.Add(1) - defer bc.wg.Done() - - var ( - ancientBlocks, liveBlocks types.Blocks - ancientReceipts, liveReceipts []types.Receipts - ) - // Do a sanity check that the provided chain is actually ordered and linked - for i, block := range blockChain { - if i != 0 { - prev := blockChain[i-1] - if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() { - log.Error("Non contiguous receipt insert", - "number", block.Number(), "hash", block.Hash(), "parent", block.ParentHash(), - "prevnumber", prev.Number(), "prevhash", prev.Hash()) - return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", - i-1, prev.NumberU64(), prev.Hash().Bytes()[:4], - i, block.NumberU64(), block.Hash().Bytes()[:4], block.ParentHash().Bytes()[:4]) - } - } - if block.NumberU64() <= ancientLimit { - ancientBlocks, ancientReceipts = append(ancientBlocks, block), append(ancientReceipts, receiptChain[i]) - } else { - liveBlocks, liveReceipts = append(liveBlocks, block), append(liveReceipts, receiptChain[i]) - } - - // Here we also validate that blob transactions in the block do not contain a sidecar. - // While the sidecar does not affect the block hash / tx hash, sending blobs within a block is not allowed. + // Verify the supplied headers before insertion without lock + var headers []*types.Header + for _, block := range blockChain { + headers = append(headers, block.Header()) + + // Here we also validate that blob transactions in the block do not + // contain a sidecar. While the sidecar does not affect the block hash + // or tx hash, sending blobs within a block is not allowed. for txIndex, tx := range block.Transactions() { if tx.Type() == types.BlobTxType && tx.BlobTxSidecar() != nil { return 0, fmt.Errorf("block #%d contains unexpected blob sidecar in tx at index %d", block.NumberU64(), txIndex) } } } + if n, err := bc.hc.ValidateHeaderChain(headers); err != nil { + return n, err + } + // Hold the mutation lock + if !bc.chainmu.TryLock() { + return 0, errChainStopped + } + defer bc.chainmu.Unlock() var ( stats = struct{ processed, ignored int32 }{} start = time.Now() size = int64(0) ) - - // updateHead updates the head snap sync block if the inserted blocks are better - // and returns an indicator whether the inserted blocks are canonical. - updateHead := func(head *types.Block) bool { - if !bc.chainmu.TryLock() { - return false - } - defer bc.chainmu.Unlock() - - // Rewind may have occurred, skip in that case. - if bc.CurrentHeader().Number.Cmp(head.Number()) >= 0 { - rawdb.WriteHeadFastBlockHash(bc.db, head.Hash()) - bc.currentSnapBlock.Store(head.Header()) - headFastBlockGauge.Update(int64(head.NumberU64())) - return true + // updateHead updates the head header and head snap block flags. + updateHead := func(header *types.Header) error { + batch := bc.db.NewBatch() + hash := header.Hash() + rawdb.WriteHeadHeaderHash(batch, hash) + rawdb.WriteHeadFastBlockHash(batch, hash) + if err := batch.Write(); err != nil { + return err } - return false + bc.hc.currentHeader.Store(header) + bc.currentSnapBlock.Store(header) + headHeaderGauge.Update(header.Number.Int64()) + headFastBlockGauge.Update(header.Number.Int64()) + return nil } // writeAncient writes blockchain and corresponding receipt chain into ancient store. // // this function only accepts canonical chain data. All side chain will be reverted // eventually. writeAncient := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) { - first := blockChain[0] - last := blockChain[len(blockChain)-1] - - // Ensure genesis is in ancients. - if first.NumberU64() == 1 { + // Ensure genesis is in the ancient store + if blockChain[0].NumberU64() == 1 { if frozen, _ := bc.db.Ancients(); frozen == 0 { writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil}) if err != nil { @@ -1280,12 +1265,6 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ log.Info("Wrote genesis to ancients") } } - // Before writing the blocks to the ancients, we need to ensure that - // they correspond to the what the headerchain 'expects'. - // We only check the last block/header, since it's a contiguous chain. - if !bc.HasHeader(last.Hash(), last.NumberU64()) { - return 0, fmt.Errorf("containing header #%d [%x..] unknown", last.Number(), last.Hash().Bytes()[:4]) - } // Write all chain data to ancients. writeSize, err := rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain) if err != nil { @@ -1298,44 +1277,28 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ if err := bc.db.Sync(); err != nil { return 0, err } - // Update the current snap block because all block data is now present in DB. - previousSnapBlock := bc.CurrentSnapBlock().Number.Uint64() - if !updateHead(blockChain[len(blockChain)-1]) { - // We end up here if the header chain has reorg'ed, and the blocks/receipts - // don't match the canonical chain. - if _, err := bc.db.TruncateHead(previousSnapBlock + 1); err != nil { - log.Error("Can't truncate ancient store after failed insert", "err", err) - } - return 0, errSideChainReceipts - } - - // Delete block data from the main database. - var ( - batch = bc.db.NewBatch() - canonHashes = make(map[common.Hash]struct{}, len(blockChain)) - ) + // Write hash to number mappings + batch := bc.db.NewBatch() for _, block := range blockChain { - canonHashes[block.Hash()] = struct{}{} - if block.NumberU64() == 0 { - continue - } - rawdb.DeleteCanonicalHash(batch, block.NumberU64()) - rawdb.DeleteBlockWithoutNumber(batch, block.Hash(), block.NumberU64()) - } - // Delete side chain hash-to-number mappings. - for _, nh := range rawdb.ReadAllHashesInRange(bc.db, first.NumberU64(), last.NumberU64()) { - if _, canon := canonHashes[nh.Hash]; !canon { - rawdb.DeleteHeader(batch, nh.Hash, nh.Number) - } + rawdb.WriteHeaderNumber(batch, block.Hash(), block.NumberU64()) } if err := batch.Write(); err != nil { return 0, err } + // Update the current snap block because all block data is now present in DB. + if err := updateHead(blockChain[len(blockChain)-1].Header()); err != nil { + return 0, err + } stats.processed += int32(len(blockChain)) return 0, nil } - // writeLive writes blockchain and corresponding receipt chain into active store. + // writeLive writes the blockchain and corresponding receipt chain to the active store. + // + // Notably, in different snap sync cycles, the supplied chain may partially reorganize + // existing local chain segments (reorg around the chain tip). The reorganized part + // will be included in the provided chain segment, and stale canonical markers will be + // silently rewritten. Therefore, no explicit reorg logic is needed. writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) { var ( skipPresenceCheck = false @@ -1346,10 +1309,6 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ if bc.insertStopped() { return 0, errInsertionInterrupted } - // Short circuit if the owner header is unknown - if !bc.HasHeader(block.Hash(), block.NumberU64()) { - return i, fmt.Errorf("containing header #%d [%x..] unknown", block.Number(), block.Hash().Bytes()[:4]) - } if !skipPresenceCheck { // Ignore if the entire data is already known if bc.HasBlock(block.Hash(), block.NumberU64()) { @@ -1363,7 +1322,8 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ } } // Write all the data out into the database - rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()) + rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64()) + rawdb.WriteBlock(batch, block) rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i]) // Write everything belongs to the blocks into the database. So that @@ -1387,21 +1347,27 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ return 0, err } } - updateHead(blockChain[len(blockChain)-1]) + if err := updateHead(blockChain[len(blockChain)-1].Header()); err != nil { + return 0, err + } return 0, nil } - // Write downloaded chain data and corresponding receipt chain data - if len(ancientBlocks) > 0 { - if n, err := writeAncient(ancientBlocks, ancientReceipts); err != nil { + // Split the supplied blocks into two groups, according to the + // given ancient limit. + index := sort.Search(len(blockChain), func(i int) bool { + return blockChain[i].NumberU64() >= ancientLimit + }) + if index > 0 { + if n, err := writeAncient(blockChain[:index], receiptChain[:index]); err != nil { if err == errInsertionInterrupted { return 0, nil } return n, err } } - if len(liveBlocks) > 0 { - if n, err := writeLive(liveBlocks, liveReceipts); err != nil { + if index != len(blockChain) { + if n, err := writeLive(blockChain[index:], receiptChain[index:]); err != nil { if err == errInsertionInterrupted { return 0, nil } @@ -1420,7 +1386,6 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ context = append(context, []interface{}{"ignored", stats.ignored}...) } log.Debug("Imported new block receipts", context...) - return 0, nil } @@ -2505,15 +2470,83 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header) (int, error) { if i, err := bc.hc.ValidateHeaderChain(chain); err != nil { return i, err } - if !bc.chainmu.TryLock() { return 0, errChainStopped } defer bc.chainmu.Unlock() + _, err := bc.hc.InsertHeaderChain(chain, start) return 0, err } +// InsertHeadersBeforeCutoff inserts the given headers into the ancient store +// as they are claimed older than the configured chain cutoff point. All the +// inserted headers are regarded as canonical and chain reorg is not supported. +func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, error) { + if len(headers) == 0 { + return 0, nil + } + // TODO(rjl493456442): Headers before the configured cutoff have already + // been verified by the hash of cutoff header. Theoretically, header validation + // could be skipped here. + if n, err := bc.hc.ValidateHeaderChain(headers); err != nil { + return n, err + } + if !bc.chainmu.TryLock() { + return 0, errChainStopped + } + defer bc.chainmu.Unlock() + + // Initialize the ancient store with genesis block if it's empty. + var ( + frozen, _ = bc.db.Ancients() + first = headers[0].Number.Uint64() + ) + if first == 1 && frozen == 0 { + _, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil}) + if err != nil { + log.Error("Error writing genesis to ancients", "err", err) + return 0, err + } + log.Info("Wrote genesis to ancient store") + } else if frozen != first { + return 0, fmt.Errorf("headers are gapped with the ancient store, first: %d, ancient: %d", first, frozen) + } + + // Write headers to the ancient store, with block bodies and receipts set to nil + // to ensure consistency across tables in the freezer. + _, err := rawdb.WriteAncientHeaderChain(bc.db, headers) + if err != nil { + return 0, err + } + if err := bc.db.Sync(); err != nil { + return 0, err + } + // Write hash to number mappings + batch := bc.db.NewBatch() + for _, header := range headers { + rawdb.WriteHeaderNumber(batch, header.Hash(), header.Number.Uint64()) + } + // Write head header and head snap block flags + last := headers[len(headers)-1] + rawdb.WriteHeadHeaderHash(batch, last.Hash()) + rawdb.WriteHeadFastBlockHash(batch, last.Hash()) + if err := batch.Write(); err != nil { + return 0, err + } + // Truncate the useless chain segment (zero bodies and receipts) in the + // ancient store. + if _, err := bc.db.TruncateTail(last.Number.Uint64() + 1); err != nil { + return 0, err + } + // Last step update all in-memory markers + bc.hc.currentHeader.Store(last) + bc.currentSnapBlock.Store(last) + headHeaderGauge.Update(last.Number.Int64()) + headFastBlockGauge.Update(last.Number.Int64()) + return 0, nil +} + // SetBlockValidatorAndProcessorForTesting sets the current validator and processor. // This method can be used to force an invalid blockchain to be verified for tests. // This method is unsafe and should only be used before block import starts. @@ -2533,9 +2566,3 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) { func (bc *BlockChain) GetTrieFlushInterval() time.Duration { return time.Duration(bc.flushInterval.Load()) } - -// HistoryPruningCutoff returns the configured history pruning point. -// Blocks before this might not be available in the database. -func (bc *BlockChain) HistoryPruningCutoff() uint64 { - return bc.cacheConfig.HistoryPruningCutoff -} diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 415a0f54426..41147234699 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -407,6 +407,12 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) { return bc.txIndexer.txIndexProgress() } +// HistoryPruningCutoff returns the configured history pruning point. +// Blocks before this might not be available in the database. +func (bc *BlockChain) HistoryPruningCutoff() (uint64, common.Hash) { + return bc.cacheConfig.HistoryPruningCutoffNumber, bc.cacheConfig.HistoryPruningCutoffHash +} + // TrieDB retrieves the low level trie database used for data storage. func (bc *BlockChain) TrieDB() *triedb.Database { return bc.triedb diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 04dfa87b8b8..8f5a64e2066 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -733,13 +733,6 @@ func testFastVsFullChains(t *testing.T, scheme string) { fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) defer fast.Stop() - headers := make([]*types.Header, len(blocks)) - for i, block := range blocks { - headers[i] = block.Header() - } - if n, err := fast.InsertHeaderChain(headers); err != nil { - t.Fatalf("failed to insert header %d: %v", n, err) - } if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } @@ -753,9 +746,6 @@ func testFastVsFullChains(t *testing.T, scheme string) { ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) defer ancient.Stop() - if n, err := ancient.InsertHeaderChain(headers); err != nil { - t.Fatalf("failed to insert header %d: %v", n, err) - } if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(len(blocks)/2)); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } @@ -880,13 +870,6 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) { fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) defer fast.Stop() - headers := make([]*types.Header, len(blocks)) - for i, block := range blocks { - headers[i] = block.Header() - } - if n, err := fast.InsertHeaderChain(headers); err != nil { - t.Fatalf("failed to insert header %d: %v", n, err) - } if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } @@ -900,9 +883,6 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) { ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) defer ancient.Stop() - if n, err := ancient.InsertHeaderChain(headers); err != nil { - t.Fatalf("failed to insert header %d: %v", n, err) - } if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } @@ -916,6 +896,11 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) { // Import the chain as a light node and ensure all pointers are updated lightDb := makeDb() defer lightDb.Close() + + headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { + headers[i] = block.Header() + } light, _ := NewBlockChain(lightDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) if n, err := light.InsertHeaderChain(headers); err != nil { t.Fatalf("failed to insert header %d: %v", n, err) @@ -1710,13 +1695,6 @@ func testBlockchainRecovery(t *testing.T, scheme string) { defer ancientDb.Close() ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) - headers := make([]*types.Header, len(blocks)) - for i, block := range blocks { - headers[i] = block.Header() - } - if n, err := ancient.InsertHeaderChain(headers); err != nil { - t.Fatalf("failed to insert header %d: %v", n, err) - } if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } @@ -1741,82 +1719,6 @@ func testBlockchainRecovery(t *testing.T, scheme string) { } } -// This test checks that InsertReceiptChain will roll back correctly when attempting to insert a side chain. -func TestInsertReceiptChainRollback(t *testing.T) { - testInsertReceiptChainRollback(t, rawdb.HashScheme) - testInsertReceiptChainRollback(t, rawdb.PathScheme) -} - -func testInsertReceiptChainRollback(t *testing.T, scheme string) { - // Generate forked chain. The returned BlockChain object is used to process the side chain blocks. - tmpChain, sideblocks, canonblocks, gspec, err := getLongAndShortChains(scheme) - if err != nil { - t.Fatal(err) - } - defer tmpChain.Stop() - // Get the side chain receipts. - if _, err := tmpChain.InsertChain(sideblocks); err != nil { - t.Fatal("processing side chain failed:", err) - } - t.Log("sidechain head:", tmpChain.CurrentBlock().Number, tmpChain.CurrentBlock().Hash()) - sidechainReceipts := make([]types.Receipts, len(sideblocks)) - for i, block := range sideblocks { - sidechainReceipts[i] = tmpChain.GetReceiptsByHash(block.Hash()) - } - // Get the canon chain receipts. - if _, err := tmpChain.InsertChain(canonblocks); err != nil { - t.Fatal("processing canon chain failed:", err) - } - t.Log("canon head:", tmpChain.CurrentBlock().Number, tmpChain.CurrentBlock().Hash()) - canonReceipts := make([]types.Receipts, len(canonblocks)) - for i, block := range canonblocks { - canonReceipts[i] = tmpChain.GetReceiptsByHash(block.Hash()) - } - - // Set up a BlockChain that uses the ancient store. - ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) - if err != nil { - t.Fatalf("failed to create temp freezer db: %v", err) - } - defer ancientDb.Close() - - ancientChain, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) - defer ancientChain.Stop() - - // Import the canonical header chain. - canonHeaders := make([]*types.Header, len(canonblocks)) - for i, block := range canonblocks { - canonHeaders[i] = block.Header() - } - if _, err = ancientChain.InsertHeaderChain(canonHeaders); err != nil { - t.Fatal("can't import canon headers:", err) - } - - // Try to insert blocks/receipts of the side chain. - _, err = ancientChain.InsertReceiptChain(sideblocks, sidechainReceipts, uint64(len(sideblocks))) - if err == nil { - t.Fatal("expected error from InsertReceiptChain.") - } - if ancientChain.CurrentSnapBlock().Number.Uint64() != 0 { - t.Fatalf("failed to rollback ancient data, want %d, have %d", 0, ancientChain.CurrentSnapBlock().Number) - } - if frozen, err := ancientChain.db.Ancients(); err != nil || frozen != 1 { - t.Fatalf("failed to truncate ancient data, frozen index is %d", frozen) - } - - // Insert blocks/receipts of the canonical chain. - _, err = ancientChain.InsertReceiptChain(canonblocks, canonReceipts, uint64(len(canonblocks))) - if err != nil { - t.Fatalf("can't import canon chain receipts: %v", err) - } - if ancientChain.CurrentSnapBlock().Number.Uint64() != canonblocks[len(canonblocks)-1].NumberU64() { - t.Fatalf("failed to insert ancient recept chain after rollback") - } - if frozen, _ := ancientChain.db.Ancients(); frozen != uint64(len(canonblocks))+1 { - t.Fatalf("wrong ancients count %d", frozen) - } -} - // Tests that importing a very large side fork, which is larger than the canon chain, // but where the difficulty per block is kept low: this means that it will not // overtake the 'canon' chain until after it's passed canon by about 200 blocks. @@ -2088,14 +1990,6 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) { } } else if typ == "receipts" { inserter = func(blocks []*types.Block, receipts []types.Receipts) error { - headers := make([]*types.Header, 0, len(blocks)) - for _, block := range blocks { - headers = append(headers, block.Header()) - } - _, err := chain.InsertHeaderChain(headers) - if err != nil { - return err - } _, err = chain.InsertReceiptChain(blocks, receipts, 0) return err } @@ -2262,14 +2156,6 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i } } else if typ == "receipts" { inserter = func(blocks []*types.Block, receipts []types.Receipts) error { - headers := make([]*types.Header, 0, len(blocks)) - for _, block := range blocks { - headers = append(headers, block.Header()) - } - i, err := chain.InsertHeaderChain(headers) - if err != nil { - return fmt.Errorf("index %d: %w", i, err) - } _, err = chain.InsertReceiptChain(blocks, receipts, 0) return err } @@ -4265,3 +4151,220 @@ func TestEIP7702(t *testing.T) { t.Fatalf("addr2 storage wrong: expected %d, got %d", fortyTwo, actual) } } + +// Tests the scenario that the synchronization target in snap sync has been changed +// with a chain reorg at the tip. In this case the reorg'd segment should be unmarked +// with canonical flags. +func TestChainReorgSnapSync(t *testing.T) { + testChainReorgSnapSync(t, 0) + testChainReorgSnapSync(t, 32) + testChainReorgSnapSync(t, gomath.MaxUint64) +} + +func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) { + // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true))) + + // Configure and generate a sample block chain + var ( + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000000000) + gspec = &Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{address: {Balance: funds}}, + BaseFee: big.NewInt(params.InitialBaseFee), + } + signer = types.LatestSigner(gspec.Config) + engine = beacon.New(ethash.NewFaker()) + ) + genDb, blocks, receipts := GenerateChainWithGenesis(gspec, engine, 32, func(i int, block *BlockGen) { + block.SetCoinbase(common.Address{0x00}) + + // If the block number is multiple of 3, send a few bonus transactions to the miner + if i%3 == 2 { + for j := 0; j < i%4+1; j++ { + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key) + if err != nil { + panic(err) + } + block.AddTx(tx) + } + } + }) + chainA, receiptsA := GenerateChain(gspec.Config, blocks[len(blocks)-1], engine, genDb, 16, func(i int, gen *BlockGen) { + gen.SetCoinbase(common.Address{0: byte(0xa), 19: byte(i)}) + }) + chainB, receiptsB := GenerateChain(gspec.Config, blocks[len(blocks)-1], engine, genDb, 20, func(i int, gen *BlockGen) { + gen.SetCoinbase(common.Address{0: byte(0xb), 19: byte(i)}) + }) + + db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + defer db.Close() + + chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) + defer chain.Stop() + + if n, err := chain.InsertReceiptChain(blocks, receipts, ancientLimit); err != nil { + t.Fatalf("failed to insert receipt %d: %v", n, err) + } + if n, err := chain.InsertReceiptChain(chainA, receiptsA, ancientLimit); err != nil { + t.Fatalf("failed to insert receipt %d: %v", n, err) + } + // If the common ancestor is below the ancient limit, rewind the chain head. + // It's aligned with the behavior in the snap sync + ancestor := blocks[len(blocks)-1].NumberU64() + if ancestor < ancientLimit { + rawdb.WriteLastPivotNumber(db, ancestor) + chain.SetHead(ancestor) + } + if n, err := chain.InsertReceiptChain(chainB, receiptsB, ancientLimit); err != nil { + t.Fatalf("failed to insert receipt %d: %v", n, err) + } + head := chain.CurrentSnapBlock() + if head.Hash() != chainB[len(chainB)-1].Hash() { + t.Errorf("head snap block #%d: header mismatch: want: %v, got: %v", head.Number, chainB[len(chainB)-1].Hash(), head.Hash()) + } + + // Iterate over all chain data components, and cross reference + for i := 0; i < len(blocks); i++ { + num, hash := blocks[i].NumberU64(), blocks[i].Hash() + header := chain.GetHeaderByNumber(num) + if header.Hash() != hash { + t.Errorf("block #%d: header mismatch: want: %v, got: %v", num, hash, header.Hash()) + } + } + for i := 0; i < len(chainA); i++ { + num, hash := chainA[i].NumberU64(), chainA[i].Hash() + header := chain.GetHeaderByNumber(num) + if header == nil { + continue + } + if header.Hash() == hash { + t.Errorf("block #%d: unexpected canonical header: %v", num, hash) + } + } + for i := 0; i < len(chainB); i++ { + num, hash := chainB[i].NumberU64(), chainB[i].Hash() + header := chain.GetHeaderByNumber(num) + if header.Hash() != hash { + t.Errorf("block #%d: header mismatch: want: %v, got: %v", num, hash, header.Hash()) + } + } +} + +// Tests the scenario that all the inserted chain segment are with the configured +// chain cutoff point. In this case the chain segment before the cutoff should +// be persisted without the receipts and bodies; chain after should be persisted +// normally. +func TestInsertChainWithCutoff(t *testing.T) { + testInsertChainWithCutoff(t, 32, 32) // cutoff = 32, ancientLimit = 32 + testInsertChainWithCutoff(t, 32, 64) // cutoff = 32, ancientLimit = 64 (entire chain in ancient) + testInsertChainWithCutoff(t, 32, 65) // cutoff = 32, ancientLimit = 65 (64 blocks in ancient, 1 block in live) +} + +func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64) { + // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true))) + + // Configure and generate a sample block chain + var ( + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000000000) + gspec = &Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{address: {Balance: funds}}, + BaseFee: big.NewInt(params.InitialBaseFee), + } + signer = types.LatestSigner(gspec.Config) + engine = beacon.New(ethash.NewFaker()) + ) + _, blocks, receipts := GenerateChainWithGenesis(gspec, engine, int(2*cutoff), func(i int, block *BlockGen) { + block.SetCoinbase(common.Address{0x00}) + + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key) + if err != nil { + panic(err) + } + block.AddTx(tx) + }) + db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + defer db.Close() + + cutoffBlock := blocks[cutoff-1] + config := DefaultCacheConfigWithScheme(rawdb.PathScheme) + config.HistoryPruningCutoffNumber = cutoffBlock.NumberU64() + config.HistoryPruningCutoffHash = cutoffBlock.Hash() + + chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) + defer chain.Stop() + + var ( + headersBefore []*types.Header + blocksAfter []*types.Block + receiptsAfter []types.Receipts + ) + for i, b := range blocks { + if b.NumberU64() < cutoffBlock.NumberU64() { + headersBefore = append(headersBefore, b.Header()) + } else { + blocksAfter = append(blocksAfter, b) + receiptsAfter = append(receiptsAfter, receipts[i]) + } + } + if n, err := chain.InsertHeadersBeforeCutoff(headersBefore); err != nil { + t.Fatalf("failed to insert headers before cutoff %d: %v", n, err) + } + if n, err := chain.InsertReceiptChain(blocksAfter, receiptsAfter, ancientLimit); err != nil { + t.Fatalf("failed to insert receipt %d: %v", n, err) + } + headSnap := chain.CurrentSnapBlock() + if headSnap.Hash() != blocks[len(blocks)-1].Hash() { + t.Errorf("head snap block #%d: header mismatch: want: %v, got: %v", headSnap.Number, blocks[len(blocks)-1].Hash(), headSnap.Hash()) + } + headHeader := chain.CurrentHeader() + if headHeader.Hash() != blocks[len(blocks)-1].Hash() { + t.Errorf("head header #%d: header mismatch: want: %v, got: %v", headHeader.Number, blocks[len(blocks)-1].Hash(), headHeader.Hash()) + } + headBlock := chain.CurrentBlock() + if headBlock.Hash() != gspec.ToBlock().Hash() { + t.Errorf("head block #%d: header mismatch: want: %v, got: %v", headBlock.Number, gspec.ToBlock().Hash(), headBlock.Hash()) + } + + // Iterate over all chain data components, and cross reference + for i := 0; i < len(blocks); i++ { + num, hash := blocks[i].NumberU64(), blocks[i].Hash() + + // Canonical headers should be visible regardless of cutoff + header := chain.GetHeaderByNumber(num) + if header.Hash() != hash { + t.Errorf("block #%d: header mismatch: want: %v, got: %v", num, hash, header.Hash()) + } + tail, err := db.Tail() + if err != nil { + t.Fatalf("Failed to get chain tail, %v", err) + } + if tail != cutoffBlock.NumberU64() { + t.Fatalf("Unexpected chain tail, want: %d, got: %d", cutoffBlock.NumberU64(), tail) + } + // Block bodies and receipts before the cutoff should be non-existent + if num < cutoffBlock.NumberU64() { + body := chain.GetBody(hash) + if body != nil { + t.Fatalf("Unexpected block body: %d, cutoff: %d", num, cutoffBlock.NumberU64()) + } + receipts := chain.GetReceiptsByHash(hash) + if receipts != nil { + t.Fatalf("Unexpected block receipts: %d, cutoff: %d", num, cutoffBlock.NumberU64()) + } + } else { + body := chain.GetBody(hash) + if body == nil || len(body.Transactions) != 1 { + t.Fatalf("Missed block body: %d, cutoff: %d", num, cutoffBlock.NumberU64()) + } + receipts := chain.GetReceiptsByHash(hash) + if receipts == nil || len(receipts) != 1 { + t.Fatalf("Missed block receipts: %d, cutoff: %d", num, cutoffBlock.NumberU64()) + } + } + } +} diff --git a/core/error.go b/core/error.go index ce3bbb78888..de95e646362 100644 --- a/core/error.go +++ b/core/error.go @@ -28,8 +28,6 @@ var ( // ErrNoGenesis is returned when there is no Genesis Block. ErrNoGenesis = errors.New("genesis not found in chain") - - errSideChainReceipts = errors.New("side blocks can't be accepted as ancient chain data") ) // List of evm-call-message pre-checking errors. All state transition messages will diff --git a/core/headerchain.go b/core/headerchain.go index cb707a152f5..f7acc49bef4 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -237,7 +237,8 @@ func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) { } // writeHeadersAndSetHead writes a batch of block headers and applies the last -// header as the chain head if the fork choicer says it's ok to update the chain. +// header as the chain head. +// // Note: This method is not concurrent-safe with inserting blocks simultaneously // into the chain, as side effects caused by reorganisations cannot be emulated // without the real blocks. Hence, writing headers directly should only be done @@ -272,12 +273,14 @@ func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header) (*headerW return result, nil } +// ValidateHeaderChain verifies that the supplied header chain is contiguous +// and conforms to consensus rules. func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header) (int, error) { // Do a sanity check that the provided chain is actually ordered and linked for i := 1; i < len(chain); i++ { if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 { - hash := chain[i].Hash() - parentHash := chain[i-1].Hash() + hash, parentHash := chain[i].Hash(), chain[i-1].Hash() + // Chain broke ancestry, log a message (programming error) and skip insertion log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", hash, "parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", parentHash) @@ -302,7 +305,6 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header) (int, error) { return i, err } } - return 0, nil } diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 020d35619e7..2f62d86e4b7 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -737,6 +737,30 @@ func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *type return nil } +// WriteAncientHeaderChain writes the supplied headers along with nil block +// bodies and receipts into the ancient store. It's supposed to be used for +// storing chain segment before the chain cutoff. +func WriteAncientHeaderChain(db ethdb.AncientWriter, headers []*types.Header) (int64, error) { + return db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + for _, header := range headers { + num := header.Number.Uint64() + if err := op.AppendRaw(ChainFreezerHashTable, num, header.Hash().Bytes()); err != nil { + return fmt.Errorf("can't add block %d hash: %v", num, err) + } + if err := op.Append(ChainFreezerHeaderTable, num, header); err != nil { + return fmt.Errorf("can't append block header %d: %v", num, err) + } + if err := op.AppendRaw(ChainFreezerBodiesTable, num, nil); err != nil { + return fmt.Errorf("can't append block body %d: %v", num, err) + } + if err := op.AppendRaw(ChainFreezerReceiptTable, num, nil); err != nil { + return fmt.Errorf("can't append block %d receipts: %v", num, err) + } + } + return nil + }) +} + // DeleteBlock removes all block data associated with a hash. func DeleteBlock(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { DeleteReceipts(db, hash, number) diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index efd16d5fa7d..247e2775829 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -464,6 +464,48 @@ func TestAncientStorage(t *testing.T) { } } +func TestWriteAncientHeaderChain(t *testing.T) { + db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), t.TempDir(), "", false) + if err != nil { + t.Fatalf("failed to create database with ancient backend") + } + defer db.Close() + + // Create a test block + var headers []*types.Header + headers = append(headers, &types.Header{ + Number: big.NewInt(0), + Extra: []byte("test block"), + UncleHash: types.EmptyUncleHash, + TxHash: types.EmptyTxsHash, + ReceiptHash: types.EmptyReceiptsHash, + }) + headers = append(headers, &types.Header{ + Number: big.NewInt(1), + Extra: []byte("test block"), + UncleHash: types.EmptyUncleHash, + TxHash: types.EmptyTxsHash, + ReceiptHash: types.EmptyReceiptsHash, + }) + // Write and verify the header in the database + WriteAncientHeaderChain(db, headers) + + for _, header := range headers { + if blob := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); len(blob) == 0 { + t.Fatalf("no header returned") + } + if h := ReadCanonicalHash(db, header.Number.Uint64()); h != header.Hash() { + t.Fatalf("no canonical hash returned") + } + if blob := ReadBodyRLP(db, header.Hash(), header.Number.Uint64()); len(blob) != 0 { + t.Fatalf("unexpected body returned") + } + if blob := ReadReceiptsRLP(db, header.Hash(), header.Number.Uint64()); len(blob) != 0 { + t.Fatalf("unexpected body returned") + } + } +} + func TestCanonicalHashIteration(t *testing.T) { var cases = []struct { from, to uint64 diff --git a/core/txindexer.go b/core/txindexer.go index d0fce302f3e..31f069995bc 100644 --- a/core/txindexer.go +++ b/core/txindexer.go @@ -58,9 +58,10 @@ type txIndexer struct { // newTxIndexer initializes the transaction indexer. func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer { + cutoff, _ := chain.HistoryPruningCutoff() indexer := &txIndexer{ limit: limit, - cutoff: chain.HistoryPruningCutoff(), + cutoff: cutoff, db: chain.db, progress: make(chan chan TxIndexProgress), term: make(chan chan struct{}), diff --git a/eth/backend.go b/eth/backend.go index ab612b1de75..79759710b61 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -154,13 +154,18 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { } // Validate history pruning configuration. - var historyPruningCutoff uint64 + var ( + cutoffNumber uint64 + cutoffHash common.Hash + ) if config.HistoryMode == ethconfig.PostMergeHistory { prunecfg, ok := ethconfig.HistoryPrunePoints[genesisHash] if !ok { return nil, fmt.Errorf("no history pruning point is defined for genesis %x", genesisHash) } - historyPruningCutoff = prunecfg.BlockNumber + cutoffNumber = prunecfg.BlockNumber + cutoffHash = prunecfg.BlockHash + log.Info("Chain cutoff configured", "number", cutoffNumber, "hash", cutoffHash) } // Set networkID to chainID by default. @@ -204,16 +209,17 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { EnablePreimageRecording: config.EnablePreimageRecording, } cacheConfig = &core.CacheConfig{ - TrieCleanLimit: config.TrieCleanCache, - TrieCleanNoPrefetch: config.NoPrefetch, - TrieDirtyLimit: config.TrieDirtyCache, - TrieDirtyDisabled: config.NoPruning, - TrieTimeLimit: config.TrieTimeout, - SnapshotLimit: config.SnapshotCache, - Preimages: config.Preimages, - StateHistory: config.StateHistory, - StateScheme: scheme, - HistoryPruningCutoff: historyPruningCutoff, + TrieCleanLimit: config.TrieCleanCache, + TrieCleanNoPrefetch: config.NoPrefetch, + TrieDirtyLimit: config.TrieDirtyCache, + TrieDirtyDisabled: config.NoPruning, + TrieTimeLimit: config.TrieTimeout, + SnapshotLimit: config.SnapshotCache, + Preimages: config.Preimages, + StateHistory: config.StateHistory, + StateScheme: scheme, + HistoryPruningCutoffNumber: cutoffNumber, + HistoryPruningCutoffHash: cutoffHash, } ) if config.VMTrace != "" { @@ -246,7 +252,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { HashScheme: scheme == rawdb.HashScheme, } chainView := eth.newChainView(eth.blockchain.CurrentBlock()) - historyCutoff := eth.blockchain.HistoryPruningCutoff() + historyCutoff, _ := eth.blockchain.HistoryPruningCutoff() var finalBlock uint64 if fb := eth.blockchain.CurrentFinalBlock(); fb != nil { finalBlock = fb.Number.Uint64() @@ -443,7 +449,7 @@ func (s *Ethereum) updateFilterMapsHeads() { if head == nil || newHead.Hash() != head.Hash() { head = newHead chainView := s.newChainView(head) - historyCutoff := s.blockchain.HistoryPruningCutoff() + historyCutoff, _ := s.blockchain.HistoryPruningCutoff() var finalBlock uint64 if fb := s.blockchain.CurrentFinalBlock(); fb != nil { finalBlock = fb.Number.Uint64() diff --git a/eth/downloader/beaconsync.go b/eth/downloader/beaconsync.go index c142ea7435e..33ad0f89714 100644 --- a/eth/downloader/beaconsync.go +++ b/eth/downloader/beaconsync.go @@ -273,8 +273,7 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) { // fetchHeaders feeds skeleton headers to the downloader queue for scheduling // until sync errors or is finished. func (d *Downloader) fetchHeaders(from uint64) error { - var head *types.Header - _, tail, _, err := d.skeleton.Bounds() + head, tail, _, err := d.skeleton.Bounds() if err != nil { return err } @@ -294,6 +293,27 @@ func (d *Downloader) fetchHeaders(from uint64) error { fsHeaderContCheckTimer := time.NewTimer(fsHeaderContCheck) defer fsHeaderContCheckTimer.Stop() + // Verify the header at configured chain cutoff, ensuring it's matched with + // the configured hash. Skip the check if the configured cutoff is even higher + // than the sync target, which is definitely not a common case. + if d.chainCutoffNumber != 0 && d.chainCutoffNumber >= from && d.chainCutoffNumber <= head.Number.Uint64() { + h := d.skeleton.Header(d.chainCutoffNumber) + if h == nil { + if d.chainCutoffNumber < tail.Number.Uint64() { + dist := tail.Number.Uint64() - d.chainCutoffNumber + if len(localHeaders) >= int(dist) { + h = localHeaders[dist-1] + } + } + } + if h == nil { + return fmt.Errorf("header at chain cutoff is not available, cutoff: %d", d.chainCutoffNumber) + } + if h.Hash() != d.chainCutoffHash { + return fmt.Errorf("header at chain cutoff mismatched, want: %v, got: %v", d.chainCutoffHash, h.Hash()) + } + } + for { // Some beacon headers might have appeared since the last cycle, make // sure we're always syncing to all available ones diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 3f3f9b7f0ca..4d13ae304c2 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -20,6 +20,7 @@ package downloader import ( "errors" "fmt" + "sort" "sync" "sync/atomic" "time" @@ -120,6 +121,12 @@ type Downloader struct { committed atomic.Bool ancientLimit uint64 // The maximum block number which can be regarded as ancient data. + // The cutoff block number and hash before which chain segments (bodies + // and receipts) are skipped during synchronization. 0 means the entire + // chain segment is aimed for synchronization. + chainCutoffNumber uint64 + chainCutoffHash common.Hash + // Channels headerProcCh chan *headerTask // Channel to feed the header processor new tasks @@ -163,9 +170,6 @@ type BlockChain interface { // CurrentHeader retrieves the head header from the local chain. CurrentHeader() *types.Header - // InsertHeaderChain inserts a batch of headers into the local chain. - InsertHeaderChain([]*types.Header) (int, error) - // SetHead rewinds the local chain to a new head. SetHead(uint64) error @@ -187,10 +191,17 @@ type BlockChain interface { // SnapSyncCommitHead directly commits the head block to a certain entity. SnapSyncCommitHead(common.Hash) error + // InsertHeadersBeforeCutoff inserts a batch of headers before the configured + // chain cutoff into the ancient store. + InsertHeadersBeforeCutoff([]*types.Header) (int, error) + // InsertChain inserts a batch of blocks into the local chain. InsertChain(types.Blocks) (int, error) - // InsertReceiptChain inserts a batch of receipts into the local chain. + // InsertReceiptChain inserts a batch of blocks along with their receipts + // into the local chain. Blocks older than the specified `ancientLimit` + // are stored directly in the ancient store, while newer blocks are stored + // in the live key-value store. InsertReceiptChain(types.Blocks, []types.Receipts, uint64) (int, error) // Snapshots returns the blockchain snapshot tree to paused it during sync. @@ -199,22 +210,29 @@ type BlockChain interface { // TrieDB retrieves the low level trie database used for interacting // with trie nodes. TrieDB() *triedb.Database + + // HistoryPruningCutoff returns the configured history pruning point. + // Block bodies along with the receipts will be skipped for synchronization. + HistoryPruningCutoff() (uint64, common.Hash) } // New creates a new downloader to fetch hashes and blocks from remote peers. func New(stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, dropPeer peerDropFn, success func()) *Downloader { + cutoffNumber, cutoffHash := chain.HistoryPruningCutoff() dl := &Downloader{ - stateDB: stateDb, - mux: mux, - queue: newQueue(blockCacheMaxItems, blockCacheInitialItems), - peers: newPeerSet(), - blockchain: chain, - dropPeer: dropPeer, - headerProcCh: make(chan *headerTask, 1), - quitCh: make(chan struct{}), - SnapSyncer: snap.NewSyncer(stateDb, chain.TrieDB().Scheme()), - stateSyncStart: make(chan *stateSync), - syncStartBlock: chain.CurrentSnapBlock().Number.Uint64(), + stateDB: stateDb, + mux: mux, + queue: newQueue(blockCacheMaxItems, blockCacheInitialItems), + peers: newPeerSet(), + blockchain: chain, + chainCutoffNumber: cutoffNumber, + chainCutoffHash: cutoffHash, + dropPeer: dropPeer, + headerProcCh: make(chan *headerTask, 1), + quitCh: make(chan struct{}), + SnapSyncer: snap.NewSyncer(stateDb, chain.TrieDB().Scheme()), + stateSyncStart: make(chan *stateSync), + syncStartBlock: chain.CurrentSnapBlock().Number.Uint64(), } // Create the post-merge skeleton syncer and start the process dl.skeleton = newSkeleton(stateDb, dl.peers, dropPeer, newBeaconBackfiller(dl, success)) @@ -503,6 +521,12 @@ func (d *Downloader) syncToHead() (err error) { } else { d.ancientLimit = 0 } + // Extend the ancient chain segment range if the ancient limit is even + // below the pre-configured chain cutoff. + if d.chainCutoffNumber != 0 && d.chainCutoffNumber > d.ancientLimit { + d.ancientLimit = d.chainCutoffNumber + log.Info("Extend the ancient range with configured cutoff", "cutoff", d.chainCutoffNumber) + } frozen, _ := d.stateDB.Ancients() // Ignore the error here since light client can also hit here. // If a part of blockchain data has already been written into active store, @@ -521,14 +545,23 @@ func (d *Downloader) syncToHead() (err error) { log.Info("Truncated excess ancient chain segment", "oldhead", frozen-1, "newhead", origin) } } + // Skip ancient chain segments if Geth is running with a configured chain cutoff. + // These segments are not guaranteed to be available in the network. + chainOffset := origin + 1 + if mode == ethconfig.SnapSync && d.chainCutoffNumber != 0 { + if chainOffset < d.chainCutoffNumber { + chainOffset = d.chainCutoffNumber + log.Info("Skip chain segment before cutoff", "origin", origin, "cutoff", d.chainCutoffNumber) + } + } // Initiate the sync using a concurrent header and content retrieval algorithm - d.queue.Prepare(origin+1, mode) + d.queue.Prepare(chainOffset, mode) // In beacon mode, headers are served by the skeleton syncer fetchers := []func() error{ - func() error { return d.fetchHeaders(origin + 1) }, // Headers are always retrieved - func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and snap sync - func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during snap sync + func() error { return d.fetchHeaders(origin + 1) }, // Headers are always retrieved + func() error { return d.fetchBodies(chainOffset) }, // Bodies are retrieved during normal and snap sync + func() error { return d.fetchReceipts(chainOffset) }, // Receipts are retrieved during snap sync func() error { return d.processHeaders(origin + 1) }, } if mode == ethconfig.SnapSync { @@ -666,7 +699,7 @@ func (d *Downloader) processHeaders(origin uint64) error { return nil } // Otherwise split the chunk of headers into batches and process them - headers, hashes := task.headers, task.hashes + headers, hashes, scheduled := task.headers, task.hashes, false for len(headers) > 0 { // Terminate if something failed in between processing chunks @@ -683,17 +716,21 @@ func (d *Downloader) processHeaders(origin uint64) error { chunkHeaders := headers[:limit] chunkHashes := hashes[:limit] - // In case of header only syncing, validate the chunk immediately - if mode == ethconfig.SnapSync { - // Although the received headers might be all valid, a legacy - // PoW/PoA sync must not accept post-merge headers. Make sure - // that any transition is rejected at this point. - if len(chunkHeaders) > 0 { - if n, err := d.blockchain.InsertHeaderChain(chunkHeaders); err != nil { - log.Warn("Invalid header encountered", "number", chunkHeaders[n].Number, "hash", chunkHashes[n], "parent", chunkHeaders[n].ParentHash, "err", err) - return fmt.Errorf("%w: %v", errInvalidChain, err) - } + // Split the headers around the chain cutoff + var cutoff int + if mode == ethconfig.SnapSync && d.chainCutoffNumber != 0 { + cutoff = sort.Search(len(chunkHeaders), func(i int) bool { + return chunkHeaders[i].Number.Uint64() >= d.chainCutoffNumber + }) + } + // Insert the header chain into the ancient store (with block bodies and + // receipts set to nil) if they fall before the cutoff. + if mode == ethconfig.SnapSync && cutoff != 0 { + if n, err := d.blockchain.InsertHeadersBeforeCutoff(chunkHeaders[:cutoff]); err != nil { + log.Warn("Failed to insert ancient header chain", "number", chunkHeaders[n].Number, "hash", chunkHashes[n], "parent", chunkHeaders[n].ParentHash, "err", err) + return fmt.Errorf("%w: %v", errInvalidChain, err) } + log.Debug("Inserted headers before cutoff", "number", chunkHeaders[cutoff-1].Number, "hash", chunkHashes[cutoff-1]) } // If we've reached the allowed number of pending headers, stall a bit for d.queue.PendingBodies() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders { @@ -704,12 +741,21 @@ func (d *Downloader) processHeaders(origin uint64) error { case <-timer.C: } } - // Otherwise insert the headers for content retrieval - inserts := d.queue.Schedule(chunkHeaders, chunkHashes, origin) - if len(inserts) != len(chunkHeaders) { - return fmt.Errorf("%w: stale headers", errBadPeer) + // Otherwise, schedule the headers for content retrieval (block bodies and + // potentially receipts in snap sync). + // + // Skip the bodies/receipts retrieval scheduling before the cutoff in snap + // sync if chain pruning is configured. + if mode == ethconfig.SnapSync && cutoff != 0 { + chunkHeaders = chunkHeaders[cutoff:] + chunkHashes = chunkHashes[cutoff:] + } + if len(chunkHeaders) > 0 { + scheduled = true + if d.queue.Schedule(chunkHeaders, chunkHashes, origin+uint64(cutoff)) != len(chunkHeaders) { + return fmt.Errorf("%w: stale headers", errBadPeer) + } } - headers = headers[limit:] hashes = hashes[limit:] origin += uint64(limit) @@ -721,11 +767,13 @@ func (d *Downloader) processHeaders(origin uint64) error { } d.syncStatsLock.Unlock() - // Signal the content downloaders of the availability of new tasks - for _, ch := range []chan bool{d.queue.blockWakeCh, d.queue.receiptWakeCh} { - select { - case ch <- true: - default: + // Signal the downloader of the availability of new tasks + if scheduled { + for _, ch := range []chan bool{d.queue.blockWakeCh, d.queue.receiptWakeCh} { + select { + case ch <- true: + default: + } } } } @@ -1085,10 +1133,20 @@ func (d *Downloader) reportSnapSyncProgress(force bool) { header = d.blockchain.CurrentHeader() block = d.blockchain.CurrentSnapBlock() ) - syncedBlocks := block.Number.Uint64() - d.syncStartBlock - if syncedBlocks == 0 { + // Prevent reporting if nothing has been synchronized yet + if block.Number.Uint64() <= d.syncStartBlock { + return + } + // Prevent reporting noise if the actual chain synchronization (headers + // and bodies) hasn't started yet. Inserting the ancient header chain is + // fast enough and would introduce significant bias if included in the count. + if d.chainCutoffNumber != 0 && block.Number.Uint64() <= d.chainCutoffNumber { return } + fetchedBlocks := block.Number.Uint64() - d.syncStartBlock + if d.chainCutoffNumber != 0 && d.chainCutoffNumber > d.syncStartBlock { + fetchedBlocks = block.Number.Uint64() - d.chainCutoffNumber + } // Retrieve the current chain head and calculate the ETA latest, _, _, err := d.skeleton.Bounds() if err != nil { @@ -1103,7 +1161,7 @@ func (d *Downloader) reportSnapSyncProgress(force bool) { } var ( left = latest.Number.Uint64() - block.Number.Uint64() - eta = time.Since(d.syncStartTime) / time.Duration(syncedBlocks) * time.Duration(left) + eta = time.Since(d.syncStartTime) / time.Duration(fetchedBlocks) * time.Duration(left) progress = fmt.Sprintf("%.2f%%", float64(block.Number.Uint64())*100/float64(latest.Number.Uint64())) headers = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(header.Number.Uint64()), common.StorageSize(headerBytes).TerminalString()) diff --git a/eth/downloader/metrics.go b/eth/downloader/metrics.go index 23c033a8ad1..bfe80ddbf1c 100644 --- a/eth/downloader/metrics.go +++ b/eth/downloader/metrics.go @@ -25,7 +25,6 @@ import ( var ( headerInMeter = metrics.NewRegisteredMeter("eth/downloader/headers/in", nil) headerReqTimer = metrics.NewRegisteredTimer("eth/downloader/headers/req", nil) - headerDropMeter = metrics.NewRegisteredMeter("eth/downloader/headers/drop", nil) headerTimeoutMeter = metrics.NewRegisteredMeter("eth/downloader/headers/timeout", nil) bodyInMeter = metrics.NewRegisteredMeter("eth/downloader/bodies/in", nil) diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index a1c114f0578..000ad97ca9c 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -73,7 +73,7 @@ type fetchResult struct { Withdrawals types.Withdrawals } -func newFetchResult(header *types.Header, fastSync bool) *fetchResult { +func newFetchResult(header *types.Header, snapSync bool) *fetchResult { item := &fetchResult{ Header: header, } @@ -82,7 +82,7 @@ func newFetchResult(header *types.Header, fastSync bool) *fetchResult { } else if header.WithdrawalsHash != nil { item.Withdrawals = make(types.Withdrawals, 0) } - if fastSync && !header.EmptyReceipts() { + if snapSync && !header.EmptyReceipts() { item.pending.Store(item.pending.Load() | (1 << receiptType)) } return item @@ -124,19 +124,8 @@ func (f *fetchResult) Done(kind uint) bool { // queue represents hashes that are either need fetching or are being fetched type queue struct { - mode SyncMode // Synchronisation mode to decide on the block parts to schedule for fetching - - // Headers are "special", they download in batches, supported by a skeleton chain - headerHead common.Hash // Hash of the last queued header to verify order - headerTaskPool map[uint64]*types.Header // Pending header retrieval tasks, mapping starting indexes to skeleton headers - headerTaskQueue *prque.Prque[int64, uint64] // Priority queue of the skeleton indexes to fetch the filling headers for - headerPeerMiss map[string]map[uint64]struct{} // Set of per-peer header batches known to be unavailable - headerPendPool map[string]*fetchRequest // Currently pending header retrieval operations - headerResults []*types.Header // Result cache accumulating the completed headers - headerHashes []common.Hash // Result cache accumulating the completed header hashes - headerProced int // Number of headers already processed from the results - headerOffset uint64 // Number of the first header in the result cache - headerContCh chan bool // Channel to notify when header download finishes + mode SyncMode // Synchronisation mode to decide on the block parts to schedule for fetching + headerHead common.Hash // Hash of the last queued header to verify order // All data retrievals below are based on an already assembles header chain blockTaskPool map[common.Hash]*types.Header // Pending block (body) retrieval tasks, mapping hashes to headers @@ -163,7 +152,6 @@ type queue struct { func newQueue(blockCacheLimit int, thresholdInitialSize int) *queue { lock := new(sync.RWMutex) q := &queue{ - headerContCh: make(chan bool, 1), blockTaskQueue: prque.New[int64, *types.Header](nil), blockWakeCh: make(chan bool, 1), receiptTaskQueue: prque.New[int64, *types.Header](nil), @@ -182,9 +170,7 @@ func (q *queue) Reset(blockCacheLimit int, thresholdInitialSize int) { q.closed = false q.mode = ethconfig.FullSync - q.headerHead = common.Hash{} - q.headerPendPool = make(map[string]*fetchRequest) q.blockTaskPool = make(map[common.Hash]*types.Header) q.blockTaskQueue.Reset() @@ -207,14 +193,6 @@ func (q *queue) Close() { q.lock.Unlock() } -// PendingHeaders retrieves the number of header requests pending for retrieval. -func (q *queue) PendingHeaders() int { - q.lock.Lock() - defer q.lock.Unlock() - - return q.headerTaskQueue.Size() -} - // PendingBodies retrieves the number of block body requests pending for retrieval. func (q *queue) PendingBodies() int { q.lock.Lock() @@ -260,54 +238,14 @@ func (q *queue) Idle() bool { return (queued + pending) == 0 } -// ScheduleSkeleton adds a batch of header retrieval tasks to the queue to fill -// up an already retrieved header skeleton. -func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) { - q.lock.Lock() - defer q.lock.Unlock() - - // No skeleton retrieval can be in progress, fail hard if so (huge implementation bug) - if q.headerResults != nil { - panic("skeleton assembly already in progress") - } - // Schedule all the header retrieval tasks for the skeleton assembly - q.headerTaskPool = make(map[uint64]*types.Header) - q.headerTaskQueue = prque.New[int64, uint64](nil) - q.headerPeerMiss = make(map[string]map[uint64]struct{}) // Reset availability to correct invalid chains - q.headerResults = make([]*types.Header, len(skeleton)*MaxHeaderFetch) - q.headerHashes = make([]common.Hash, len(skeleton)*MaxHeaderFetch) - q.headerProced = 0 - q.headerOffset = from - q.headerContCh = make(chan bool, 1) - - for i, header := range skeleton { - index := from + uint64(i*MaxHeaderFetch) - - q.headerTaskPool[index] = header - q.headerTaskQueue.Push(index, -int64(index)) - } -} - -// RetrieveHeaders retrieves the header chain assemble based on the scheduled -// skeleton. -func (q *queue) RetrieveHeaders() ([]*types.Header, []common.Hash, int) { - q.lock.Lock() - defer q.lock.Unlock() - - headers, hashes, proced := q.headerResults, q.headerHashes, q.headerProced - q.headerResults, q.headerHashes, q.headerProced = nil, nil, 0 - - return headers, hashes, proced -} - // Schedule adds a set of headers for the download queue for scheduling, returning // the new headers encountered. -func (q *queue) Schedule(headers []*types.Header, hashes []common.Hash, from uint64) []*types.Header { +func (q *queue) Schedule(headers []*types.Header, hashes []common.Hash, from uint64) int { q.lock.Lock() defer q.lock.Unlock() // Insert all the headers prioritised by the contained block number - inserts := make([]*types.Header, 0, len(headers)) + var inserts int for i, header := range headers { // Make sure chain order is honoured and preserved throughout hash := hashes[i] @@ -337,7 +275,7 @@ func (q *queue) Schedule(headers []*types.Header, hashes []common.Hash, from uin q.receiptTaskQueue.Push(header, -int64(header.Number.Uint64())) } } - inserts = append(inserts, header) + inserts++ q.headerHead = hash from++ } @@ -390,7 +328,7 @@ func (q *queue) Results(block bool) []*fetchResult { q.resultSize = common.StorageSize(blockCacheSizeWeight)*size + (1-common.StorageSize(blockCacheSizeWeight))*q.resultSize } - // Using the newly calibrated resultsize, figure out the new throttle limit + // Using the newly calibrated result size, figure out the new throttle limit // on the result cache throttleThreshold := uint64((common.StorageSize(blockCacheMemory) + q.resultSize - 1) / q.resultSize) throttleThreshold = q.resultCache.SetThrottleThreshold(throttleThreshold) @@ -428,46 +366,6 @@ func (q *queue) stats() []interface{} { } } -// ReserveHeaders reserves a set of headers for the given peer, skipping any -// previously failed batches. -func (q *queue) ReserveHeaders(p *peerConnection, count int) *fetchRequest { - q.lock.Lock() - defer q.lock.Unlock() - - // Short circuit if the peer's already downloading something (sanity check to - // not corrupt state) - if _, ok := q.headerPendPool[p.id]; ok { - return nil - } - // Retrieve a batch of hashes, skipping previously failed ones - send, skip := uint64(0), []uint64{} - for send == 0 && !q.headerTaskQueue.Empty() { - from, _ := q.headerTaskQueue.Pop() - if q.headerPeerMiss[p.id] != nil { - if _, ok := q.headerPeerMiss[p.id][from]; ok { - skip = append(skip, from) - continue - } - } - send = from - } - // Merge all the skipped batches back - for _, from := range skip { - q.headerTaskQueue.Push(from, -int64(from)) - } - // Assemble and return the block download request - if send == 0 { - return nil - } - request := &fetchRequest{ - Peer: p, - From: send, - Time: time.Now(), - } - q.headerPendPool[p.id] = request - return request -} - // ReserveBodies reserves a set of body fetches for the given peer, skipping any // previously failed downloads. Beside the next batch of needed fetches, it also // returns a flag whether empty blocks were queued requiring processing. @@ -594,10 +492,6 @@ func (q *queue) Revoke(peerID string) { q.lock.Lock() defer q.lock.Unlock() - if request, ok := q.headerPendPool[peerID]; ok { - q.headerTaskQueue.Push(request.From, -int64(request.From)) - delete(q.headerPendPool, peerID) - } if request, ok := q.blockPendPool[peerID]; ok { for _, header := range request.Headers { q.blockTaskQueue.Push(header, -int64(header.Number.Uint64())) @@ -612,16 +506,6 @@ func (q *queue) Revoke(peerID string) { } } -// ExpireHeaders cancels a request that timed out and moves the pending fetch -// task back into the queue for rescheduling. -func (q *queue) ExpireHeaders(peer string) int { - q.lock.Lock() - defer q.lock.Unlock() - - headerTimeoutMeter.Mark(1) - return q.expire(peer, q.headerPendPool, q.headerTaskQueue) -} - // ExpireBodies checks for in flight block body requests that exceeded a timeout // allowance, canceling them and returning the responsible peers for penalisation. func (q *queue) ExpireBodies(peer string) int { @@ -670,116 +554,6 @@ func (q *queue) expire(peer string, pendPool map[string]*fetchRequest, taskQueue return len(req.Headers) } -// DeliverHeaders injects a header retrieval response into the header results -// cache. This method either accepts all headers it received, or none of them -// if they do not map correctly to the skeleton. -// -// If the headers are accepted, the method makes an attempt to deliver the set -// of ready headers to the processor to keep the pipeline full. However, it will -// not block to prevent stalling other pending deliveries. -func (q *queue) DeliverHeaders(id string, headers []*types.Header, hashes []common.Hash, headerProcCh chan *headerTask) (int, error) { - q.lock.Lock() - defer q.lock.Unlock() - - var logger log.Logger - if len(id) < 16 { - // Tests use short IDs, don't choke on them - logger = log.New("peer", id) - } else { - logger = log.New("peer", id[:16]) - } - // Short circuit if the data was never requested - request := q.headerPendPool[id] - if request == nil { - headerDropMeter.Mark(int64(len(headers))) - return 0, errNoFetchesPending - } - delete(q.headerPendPool, id) - - headerReqTimer.UpdateSince(request.Time) - headerInMeter.Mark(int64(len(headers))) - - // Ensure headers can be mapped onto the skeleton chain - target := q.headerTaskPool[request.From].Hash() - - accepted := len(headers) == MaxHeaderFetch - if accepted { - if headers[0].Number.Uint64() != request.From { - logger.Trace("First header broke chain ordering", "number", headers[0].Number, "hash", hashes[0], "expected", request.From) - accepted = false - } else if hashes[len(headers)-1] != target { - logger.Trace("Last header broke skeleton structure ", "number", headers[len(headers)-1].Number, "hash", hashes[len(headers)-1], "expected", target) - accepted = false - } - } - if accepted { - parentHash := hashes[0] - for i, header := range headers[1:] { - hash := hashes[i+1] - if want := request.From + 1 + uint64(i); header.Number.Uint64() != want { - logger.Warn("Header broke chain ordering", "number", header.Number, "hash", hash, "expected", want) - accepted = false - break - } - if parentHash != header.ParentHash { - logger.Warn("Header broke chain ancestry", "number", header.Number, "hash", hash) - accepted = false - break - } - // Set-up parent hash for next round - parentHash = hash - } - } - // If the batch of headers wasn't accepted, mark as unavailable - if !accepted { - logger.Trace("Skeleton filling not accepted", "from", request.From) - headerDropMeter.Mark(int64(len(headers))) - - miss := q.headerPeerMiss[id] - if miss == nil { - q.headerPeerMiss[id] = make(map[uint64]struct{}) - miss = q.headerPeerMiss[id] - } - miss[request.From] = struct{}{} - - q.headerTaskQueue.Push(request.From, -int64(request.From)) - return 0, errors.New("delivery not accepted") - } - // Clean up a successful fetch and try to deliver any sub-results - copy(q.headerResults[request.From-q.headerOffset:], headers) - copy(q.headerHashes[request.From-q.headerOffset:], hashes) - - delete(q.headerTaskPool, request.From) - - ready := 0 - for q.headerProced+ready < len(q.headerResults) && q.headerResults[q.headerProced+ready] != nil { - ready += MaxHeaderFetch - } - if ready > 0 { - // Headers are ready for delivery, gather them and push forward (non blocking) - processHeaders := make([]*types.Header, ready) - copy(processHeaders, q.headerResults[q.headerProced:q.headerProced+ready]) - - processHashes := make([]common.Hash, ready) - copy(processHashes, q.headerHashes[q.headerProced:q.headerProced+ready]) - - select { - case headerProcCh <- &headerTask{ - headers: processHeaders, - hashes: processHashes, - }: - logger.Trace("Pre-scheduled new headers", "count", len(processHeaders), "from", processHeaders[0].Number) - q.headerProced += len(processHeaders) - default: - } - } - // Check for termination and return - if len(q.headerTaskPool) == 0 { - q.headerContCh <- false - } - return len(headers), nil -} - // DeliverBodies injects a block body retrieval response into the results queue. // The method returns the number of blocks bodies accepted from the delivery and // also wakes any threads waiting for data delivery. diff --git a/eth/downloader/resultstore.go b/eth/downloader/resultstore.go index e4323c04ebc..36c382fefcc 100644 --- a/eth/downloader/resultstore.go +++ b/eth/downloader/resultstore.go @@ -76,7 +76,7 @@ func (r *resultStore) SetThrottleThreshold(threshold uint64) uint64 { // throttled - if true, the store is at capacity, this particular header is not prio now // item - the result to store data into // err - any error that occurred -func (r *resultStore) AddFetch(header *types.Header, fastSync bool) (stale, throttled bool, item *fetchResult, err error) { +func (r *resultStore) AddFetch(header *types.Header, snapSync bool) (stale, throttled bool, item *fetchResult, err error) { r.lock.Lock() defer r.lock.Unlock() @@ -86,7 +86,7 @@ func (r *resultStore) AddFetch(header *types.Header, fastSync bool) (stale, thro return stale, throttled, item, err } if item == nil { - item = newFetchResult(header, fastSync) + item = newFetchResult(header, snapSync) r.items[index] = item } return stale, throttled, item, err From db9be56bab751e3d75fcc29e282ff7e51bd95b8b Mon Sep 17 00:00:00 2001 From: Mobin Mohanan <47410557+tr1sm0s1n@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:48:35 +0530 Subject: [PATCH 073/658] build: upgrade to golangci-lint v2 (#31530) --- .golangci.yml | 131 +++++++++++++++++++++++++------------------- build/checksums.txt | 106 +++++++++++++++++------------------ 2 files changed, 127 insertions(+), 110 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index a702da524bc..5ed06537bd2 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,31 +1,25 @@ # This file configures github.com/golangci/golangci-lint. - +version: '2' run: - timeout: 20m tests: true - linters: - disable-all: true + default: none enable: - - goimports - - gosimple - - govet - - ineffassign - - misspell - - unconvert - - typecheck - - unused - - staticcheck - bidichk - - durationcheck - copyloopvar - - whitespace - - revive # only certain checks enabled - durationcheck - gocheckcompilerdirectives - - reassign + - govet + - ineffassign - mirror + - misspell + - reassign + - revive # only certain checks enabled + - staticcheck + - unconvert + - unused - usetesting + - whitespace ### linters we tried and will not be using: ### # - structcheck # lots of false positives @@ -36,44 +30,67 @@ linters: # - exhaustive # silly check # - makezero # false positives # - nilerr # several intentional - -linters-settings: - gofmt: - simplify: true - revive: - enable-all-rules: false - # here we enable specific useful rules - # see https://golangci-lint.run/usage/linters/#revive for supported rules + settings: + staticcheck: + checks: + # disable Quickfixes + - -QF1* + revive: + enable-all-rules: false + # here we enable specific useful rules + # see https://golangci-lint.run/usage/linters/#revive for supported rules + rules: + - name: receiver-naming + severity: warning + disabled: false + exclude: + - '' + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling rules: - - name: receiver-naming - severity: warning - disabled: false - exclude: [""] - -issues: - # default is true. Enables skipping of directories: - # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ - exclude-dirs-use-default: true - exclude-files: - - core/genesis_alloc.go - exclude-rules: - - path: crypto/bn256/cloudflare/optate.go - linters: - - deadcode - - staticcheck - - path: crypto/bn256/ - linters: - - revive - - path: cmd/utils/flags.go - text: "SA1019: cfg.TxLookupLimit is deprecated: use 'TransactionHistory' instead." - - path: cmd/utils/flags.go - text: "SA1019: ethconfig.Defaults.TxLookupLimit is deprecated: use 'TransactionHistory' instead." - - path: internal/build/pgp.go - text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.' - - path: core/vm/contracts.go - text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.' - exclude: - - 'SA1019: event.TypeMux is deprecated: use Feed' - - 'SA1019: strings.Title is deprecated' - - 'SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.' - - 'SA1029: should not use built-in type string as key for value' + - linters: + - deadcode + - staticcheck + path: crypto/bn256/cloudflare/optate.go + - linters: + - revive + path: crypto/bn256/ + - path: cmd/utils/flags.go + text: "SA1019: cfg.TxLookupLimit is deprecated: use 'TransactionHistory' instead." + - path: cmd/utils/flags.go + text: "SA1019: ethconfig.Defaults.TxLookupLimit is deprecated: use 'TransactionHistory' instead." + - path: internal/build/pgp.go + text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.' + - path: core/vm/contracts.go + text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.' + - path: (.+)\.go$ + text: 'SA1019: event.TypeMux is deprecated: use Feed' + - path: (.+)\.go$ + text: 'SA1019: strings.Title is deprecated' + - path: (.+)\.go$ + text: 'SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.' + - path: (.+)\.go$ + text: 'SA1029: should not use built-in type string as key for value' + paths: + - core/genesis_alloc.go + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - goimports + settings: + gofmt: + simplify: true + exclusions: + generated: lax + paths: + - core/genesis_alloc.go + - third_party$ + - builtin$ + - examples$ diff --git a/build/checksums.txt b/build/checksums.txt index 5ccb85bba6b..6d3b718c3cb 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -54,60 +54,60 @@ db128981033ac82a64688a123f631e61297b6b8f52ca913145e57caa8ce94cc3 go1.24.1.windo e28c4e6d0b913955765b46157ab88ae59bb636acaa12d7bec959aa6900f1cebd go1.24.1.windows-arm64.zip 6d352c1f154a102a5b90c480cc64bab205ccf2681e34e78a3a4d3f1ddfbc81e4 go1.24.1.windows-arm64.msi -# version:golangci 1.64.6 +# version:golangci 2.0.2 # https://github.com/golangci/golangci-lint/releases/ -# https://github.com/golangci/golangci-lint/releases/download/v1.64.6/ -08f9459e7125fed2612abd71596e04d172695921aff82120d1c1e5e9b6fff2a3 golangci-lint-1.64.6-darwin-amd64.tar.gz -8c10d0c7c3935b8c2269d628b6a06a8f48d8fb4cc31af02fe4ce0cd18b0704c1 golangci-lint-1.64.6-darwin-arm64.tar.gz -c07fcabb55deb8d2c4d390025568e76162d7f91b1d14bd2311be45d8d440a624 golangci-lint-1.64.6-freebsd-386.tar.gz -8ed1ef1102e1a42983ffcaae8e06de6a540334c3a69e205c610b8a7c92d469cd golangci-lint-1.64.6-freebsd-amd64.tar.gz -8f8dda66d1b4a85cc8a1daf1b69af6559c3eb0a41dd8033148a9ad85cfc0e1d9 golangci-lint-1.64.6-freebsd-armv6.tar.gz -59e8ca1fa254661b2a55e96515b14a10cd02221d443054deac5b28c3c3e12d6b golangci-lint-1.64.6-freebsd-armv7.tar.gz -e3d323d5f132e9b7593141bfe1d19c8460e65a55cea1008ec96945fed563f981 golangci-lint-1.64.6-illumos-amd64.tar.gz -5ce910f2a864c5fbeb32a30cbd506e1b2ef215f7a0f422cd5be6370b13db6f03 golangci-lint-1.64.6-linux-386.deb -2caab682a26b9a5fb94ba24e3a7e1404fa9eab2c12e36ae1c5548d80a1be190c golangci-lint-1.64.6-linux-386.rpm -2d82d0a4716e6d9b70c95103054855cb4b5f20f7bbdee42297f0189955bd14b6 golangci-lint-1.64.6-linux-386.tar.gz -9cd82503e9841abcaa57663fc899587fe90363c26d86a793a98d3080fd25e907 golangci-lint-1.64.6-linux-amd64.deb -981aaca5e5202d4fbb162ec7080490eb67ef5d32add5fb62fb02210597acc9da golangci-lint-1.64.6-linux-amd64.rpm -71e290acbacb7b3ba4f68f0540fb74dc180c4336ac8a6f3bdcd7fcc48b15148d golangci-lint-1.64.6-linux-amd64.tar.gz -718016bb06c1f598a8d23c7964e2643de621dbe5808688cb38857eb0bb773c84 golangci-lint-1.64.6-linux-arm64.deb -ddc0e7b4b10b47cf894aea157e89e3674bbb60f8f5c480110c21c49cc2c1634d golangci-lint-1.64.6-linux-arm64.rpm -99a7ff13dec7a8781a68408b6ecb8a1c5e82786cba3189eaa91d5cdcc24362ce golangci-lint-1.64.6-linux-arm64.tar.gz -90e360f89c244394912b8709fb83a900b6d56cf19141df2afaf9e902ef3057b1 golangci-lint-1.64.6-linux-armv6.deb -46546ff7c98d873ffcdbee06b39dc1024fc08db4fbf1f6309360a44cf976b5c2 golangci-lint-1.64.6-linux-armv6.rpm -e45c1a42868aca0b0ee54d14fb89da216f3b4409367cd7bce3b5f36788b4fc92 golangci-lint-1.64.6-linux-armv6.tar.gz -3cf6ddbbbf358db3de4b64a24f9683bbe2da3f003cfdee86cb610124b57abafb golangci-lint-1.64.6-linux-armv7.deb -508b6712710da10f11aab9ea5e63af415c932dfe7fff718e649d3100b838f797 golangci-lint-1.64.6-linux-armv7.rpm -da9a8bbee86b4dfee73fbc17e0856ec84c5b04919eb09bf3dd5904c39ce41753 golangci-lint-1.64.6-linux-armv7.tar.gz -ad496a58284e1e9c8ac6f993fec429dcd96c85a8c4715dbb6530a5af0dae7fbd golangci-lint-1.64.6-linux-loong64.deb -3bd70fa737b224750254dce616d9a188570e49b894b0cdb2fd04155e2c061350 golangci-lint-1.64.6-linux-loong64.rpm -a535af973499729f2215a84303eb0de61399057aad6901ddea1b4f73f68d5f2c golangci-lint-1.64.6-linux-loong64.tar.gz -6ad2a1cd37ca30303a488abfca2de52aff57519901c6d8d2608656fe9fb05292 golangci-lint-1.64.6-linux-mips64.deb -5f18369f0ca010a02c267352ebe6e3e0514c6debff49899c9e5520906c0da287 golangci-lint-1.64.6-linux-mips64.rpm -3449d6c13307b91b0e8817f8911c07c3412cdb6931b8d101e42db3e9762e91ad golangci-lint-1.64.6-linux-mips64.tar.gz -d4712a348f65dcaf9f6c58f1cfd6d0984d54a902873dca5e76f0d686f5c59499 golangci-lint-1.64.6-linux-mips64le.deb -57cea4538894558cb8c956d6b69c5509e4304546abe4a467478fc9ada0c736d6 golangci-lint-1.64.6-linux-mips64le.rpm -bc030977d44535b2152fddb2732f056d193c043992fe638ddecea21a40ef09fe golangci-lint-1.64.6-linux-mips64le.tar.gz -1ceb4e492efa527d246c61798c581f49113756fab8c39bb3eefdb1fa97041f92 golangci-lint-1.64.6-linux-ppc64le.deb -454e1c2b3583a8644e0c33a970fb4ce35b8f11848e1a770d9095d99d159ad0ab golangci-lint-1.64.6-linux-ppc64le.rpm -fddf30d35923eb6c7bb57520d8645768f802bf86c4cbf5a3a13049efb9e71b82 golangci-lint-1.64.6-linux-ppc64le.tar.gz -bd75f0dd6f65bee5821c433803b28f3c427ef3582764c3d0e7f7fae1c9d468b6 golangci-lint-1.64.6-linux-riscv64.deb -58457207c225cbd5340c8dcb75d9a44aa890d604e28464115a3a9762febaed04 golangci-lint-1.64.6-linux-riscv64.rpm -82639518a613a6705b7e2de5b28c278e875d782a5c97e9c1b2ac10b4ecd7852f golangci-lint-1.64.6-linux-riscv64.tar.gz -131d69204f8ced200b1437731e987cc886edac30dc87e6e1dcbd4f833d351713 golangci-lint-1.64.6-linux-s390x.deb -ca6caf28ca7a1df7cff720f8fd6ac4b8f2eac10c8cbfe7d2eade70876aded570 golangci-lint-1.64.6-linux-s390x.rpm -ed966d28a6512bc2b1120029a9f78ed77f2015e330b589a731d67d59be30b0b1 golangci-lint-1.64.6-linux-s390x.tar.gz -b181132b41943fc77ace7f9f5523085d12d3613f27774a0e35e17dd5e65ba589 golangci-lint-1.64.6-netbsd-386.tar.gz -f7b81c426006e3c699dc8665797a462aecba8c2cd23ac4971472e55184d95bc9 golangci-lint-1.64.6-netbsd-amd64.tar.gz -663d6fb4c3bef57b8aacdb1b1a4634075e55d294ed170724b443374860897ca6 golangci-lint-1.64.6-netbsd-arm64.tar.gz -8c7a76ee822568cc19352bbb9b2b0863dc5e185eb07782adbbaf648afd02ebcd golangci-lint-1.64.6-netbsd-armv6.tar.gz -0ce26d56ce78e516529e087118ba3f1bcd71d311b4c5b2bde6ec24a6e8966d85 golangci-lint-1.64.6-netbsd-armv7.tar.gz -135d5d998168683f46e4fab308cef5aa3c282025b7de6b258f976aadfb820db7 golangci-lint-1.64.6-source.tar.gz -ccdb0cc249531a205304a76308cfa7cda830083d083d557884416a2461148263 golangci-lint-1.64.6-windows-386.zip -2d88f282e08e1853c70bc7c914b5f58beaa6509903d56098aeb9bc3df30ea9d5 golangci-lint-1.64.6-windows-amd64.zip -6a3c6e7afc6916392679d7d6b95ac239827644e3e50ec4e8ca6ab1e4e6db65fe golangci-lint-1.64.6-windows-arm64.zip -9c9c1ef9687651566987f93e76252f7526c386901d7d8aeee044ca88115da4b1 golangci-lint-1.64.6-windows-armv6.zip -4f0df114155791799dfde8cd8cb6307fecfb17fed70b44205486ec925e2f39cf golangci-lint-1.64.6-windows-armv7.zip +# https://github.com/golangci/golangci-lint/releases/download/v2.0.2/ +a88cbdc86b483fe44e90bf2dcc3fec2af8c754116e6edf0aa6592cac5baa7a0e golangci-lint-2.0.2-darwin-amd64.tar.gz +664550e7954f5f4451aae99b4f7382c1a47039c66f39ca605f5d9af1a0d32b49 golangci-lint-2.0.2-darwin-arm64.tar.gz +bda0f0f27d300502faceda8428834a76ca25986f6d9fc2bd41d201c3ed73f08e golangci-lint-2.0.2-freebsd-386.tar.gz +1cbd0c7ade3fb027d61d38a646ec1b51be5846952b4b04a5330e7f4687f2270c golangci-lint-2.0.2-freebsd-amd64.tar.gz +1e828a597726198b2e35acdbcc5f3aad85244d79846d2d2bdb05241c5a535f9e golangci-lint-2.0.2-freebsd-armv6.tar.gz +848b49315dc5cddd0c9ce35e96ab33d584db0ea8fb57bcbf9784f1622bec0269 golangci-lint-2.0.2-freebsd-armv7.tar.gz +cabf9a6beab574c7f98581eb237919e580024759e3cdc05c4d516b044dce6770 golangci-lint-2.0.2-illumos-amd64.tar.gz +2fde80d15ed6527791f106d606120620e913c3a663c90a8596861d0a4461169e golangci-lint-2.0.2-linux-386.deb +804bc6e350a8c613aaa0a33d8d45414a80157b0ba1b2c2335ac859f85ad98ebd golangci-lint-2.0.2-linux-386.rpm +e64beb72fecf581e57d88ae3adb1c9d4bf022694b6bd92e3c8e460910bbdc37d golangci-lint-2.0.2-linux-386.tar.gz +9c55aed174d7a52bb1d4006b36e7edee9023631f6b814a80cb39c9860d6f75c3 golangci-lint-2.0.2-linux-amd64.deb +c55a2ef741a687b4c679696931f7fd4a467babd64c9457cf17bb9632fd1cecd1 golangci-lint-2.0.2-linux-amd64.rpm +89cc8a7810dc63b9a37900da03e37c3601caf46d42265d774e0f1a5d883d53e2 golangci-lint-2.0.2-linux-amd64.tar.gz +a3e78583c4e7ea1b63e82559f126bb3a5b12788676f158526752d53e67824b99 golangci-lint-2.0.2-linux-arm64.deb +bd5dd52b5c9f18aa7a2904eda9a9f91c628e98623fe70b7afcbb847e2de84422 golangci-lint-2.0.2-linux-arm64.rpm +789d5b91219ac68c2336f77d41cd7e33a910420594780f455893f8453d09595b golangci-lint-2.0.2-linux-arm64.tar.gz +534cd4c464a66178714ed68152c1ed7aa73e5700bf409e4ed1a8363adf96afca golangci-lint-2.0.2-linux-armv6.deb +cf7d02905a5fc80b96c9a64621693b4cc7337b1ce29986c19fd72608dafe66c5 golangci-lint-2.0.2-linux-armv6.rpm +a0d81cb527d8fe878377f2356b5773e219b0b91832a6b59e7b9bcf9a90fe0b0e golangci-lint-2.0.2-linux-armv6.tar.gz +dedd5be7fff8cba8fe15b658a59347ea90d7d02a9fff87f09c7687e6da05a8b6 golangci-lint-2.0.2-linux-armv7.deb +85521b6f3ad2f5a2bc9bfe14b9b08623f764964048f75ed6dfcfaf8eb7d57cc1 golangci-lint-2.0.2-linux-armv7.rpm +96471046c7780dda4ea680f65e92c2ef56ff58d40bcffaf6cfe9d6d48e3c27aa golangci-lint-2.0.2-linux-armv7.tar.gz +815d914a7738e4362466b2d11004e8618b696b49e8ace13df2c2b25f28fb1e17 golangci-lint-2.0.2-linux-loong64.deb +f16381e3d8a0f011b95e086d83d620248432b915d01f4beab4d29cfe4dc388b0 golangci-lint-2.0.2-linux-loong64.rpm +1bd8d7714f9c92db6a0f23bae89f39c85ba047bec8eeb42b8748d30ae3228d18 golangci-lint-2.0.2-linux-loong64.tar.gz +ea6e9d4aabb526aa298e47e8b026d8893d918c5eb919ba0ab403e315def74cc5 golangci-lint-2.0.2-linux-mips64.deb +519d8d53af83fdc9c25cc3fba8b663331ac22ef68131d4b0084cb6f425b6f79a golangci-lint-2.0.2-linux-mips64.rpm +80d655a0a1ac1b19dcef4b58fa2a7dadb646cc50ad08d460b5c53cdb421165e4 golangci-lint-2.0.2-linux-mips64.tar.gz +aa0e75384bb482c865d4dfc95d23ceb25666bf20461b67a832f0eea6670312ec golangci-lint-2.0.2-linux-mips64le.deb +f2a8b500fb69bdea1b01df6267aaa5218fa4a58aeb781c1a20d0d802fe465a52 golangci-lint-2.0.2-linux-mips64le.rpm +e66a0c0c9a275f02d27a7caa9576112622306f001d73dfc082cf1ae446fc1242 golangci-lint-2.0.2-linux-mips64le.tar.gz +e85ad51aac6428be2d8a37000d053697371a538a5bcbc1644caa7c5e77f6d0af golangci-lint-2.0.2-linux-ppc64le.deb +906798365eac1944af2a9b9a303e6fd49ec9043307bc681b7a96277f7f8beea5 golangci-lint-2.0.2-linux-ppc64le.rpm +f7f1a271b0af274d6c9ce000f5dc6e1fb194350c67bcc62494f96f791882ba92 golangci-lint-2.0.2-linux-ppc64le.tar.gz +eea8bf643a42bf05de9780530db22923e5ab0d588f0e173594dc6518f2a25d2a golangci-lint-2.0.2-linux-riscv64.deb +4ff40f9fe2954400836e2a011ba4744d00ffab5068a51368552dfce6aba3b81b golangci-lint-2.0.2-linux-riscv64.rpm +531d8f225866674977d630afbf0533eb02f9bec607fb13895f7a2cd7b2e0a648 golangci-lint-2.0.2-linux-riscv64.tar.gz +6f827647046c603f40d97ea5aadc6f48cd0bb5d19f7a3d56500c3b833d2a0342 golangci-lint-2.0.2-linux-s390x.deb +387a090e9576d19ca86aac738172e58e07c19f2784a13bb387f4f0d75fb9c8d3 golangci-lint-2.0.2-linux-s390x.rpm +57de1fb7722a9feb2d11ed0a007a93959d05b9db5929a392abc222e30012467e golangci-lint-2.0.2-linux-s390x.tar.gz +ed95e0492ea86bf79eb661f0334474b2a4255093685ff587eccd797c5a54db7e golangci-lint-2.0.2-netbsd-386.tar.gz +eab81d729778166415d349a80e568b2f2b3a781745a9be3212a92abb1e732daf golangci-lint-2.0.2-netbsd-amd64.tar.gz +d20add73f7c2de2c3b01ed4fd7b63ffcf0a6597d5ea228d1699e92339a3cd047 golangci-lint-2.0.2-netbsd-arm64.tar.gz +4e4f44e6057879cd62424ff1800a767d25a595c0e91d6d48809eea9186b4c739 golangci-lint-2.0.2-netbsd-armv6.tar.gz +51ec17b16d8743ae4098a0171f04f0ed4d64561e3051b982778b0e6c306a1b03 golangci-lint-2.0.2-netbsd-armv7.tar.gz +5482cf27b93fae1765c70ee2a95d4074d038e9dee61bdd61d017ce8893d3a4a8 golangci-lint-2.0.2-source.tar.gz +a35d8fdf3e14079a10880dbbb7586b46faec89be96f086b244b3e565aac80313 golangci-lint-2.0.2-windows-386.zip +fe4b946cc01366b989001215687003a9c4a7098589921f75e6228d6d8cffc15c golangci-lint-2.0.2-windows-amd64.zip +646bd9250ef8c771d85cd22fe8e6f2397ae39599179755e3bbfa9ef97ad44090 golangci-lint-2.0.2-windows-arm64.zip +ce1dc0bad6f8a61d64e6b3779eeb773479c175125d6f686b0e67ef9c8432d16e golangci-lint-2.0.2-windows-armv6.zip +92684a48faabe792b11ac27ca8b25551eff940b0a1e84ad7244e98b4994962db golangci-lint-2.0.2-windows-armv7.zip # This is the builder on PPA that will build Go itself (inception-y), don't modify! # From a0620f114c469503985376e7e4bdc28547d4ea5a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 3 Apr 2025 15:44:07 +0200 Subject: [PATCH 074/658] eth: fix calls to HistoryPruningCutoff (#31552) These were caused by crossed merges of recent PRs #31414 and #31361 --- eth/api_backend.go | 15 ++++++++------- internal/ethapi/api_test.go | 5 ++++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/eth/api_backend.go b/eth/api_backend.go index 944c357e78f..b39dd4cbdb2 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -94,7 +94,7 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb } var bn uint64 if number == rpc.EarliestBlockNumber { - bn = b.eth.blockchain.HistoryPruningCutoff() + bn = b.HistoryPruningCutoff() } else { bn = uint64(number) } @@ -152,10 +152,10 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe } bn := uint64(number) // the resolved number if number == rpc.EarliestBlockNumber { - bn = b.eth.blockchain.HistoryPruningCutoff() + bn = b.HistoryPruningCutoff() } block := b.eth.blockchain.GetBlockByNumber(bn) - if block == nil && bn < b.eth.blockchain.HistoryPruningCutoff() { + if block == nil && bn < b.HistoryPruningCutoff() { return nil, ðconfig.PrunedHistoryError{} } return block, nil @@ -167,7 +167,7 @@ func (b *EthAPIBackend) BlockByHash(ctx context.Context, hash common.Hash) (*typ return nil, nil } block := b.eth.blockchain.GetBlock(hash, *number) - if block == nil && *number < b.eth.blockchain.HistoryPruningCutoff() { + if block == nil && *number < b.HistoryPruningCutoff() { return nil, ðconfig.PrunedHistoryError{} } return block, nil @@ -180,7 +180,7 @@ func (b *EthAPIBackend) GetBody(ctx context.Context, hash common.Hash, number rp } body := b.eth.blockchain.GetBody(hash) if body == nil { - if uint64(number) < b.eth.blockchain.HistoryPruningCutoff() { + if uint64(number) < b.HistoryPruningCutoff() { return nil, ðconfig.PrunedHistoryError{} } return nil, errors.New("block body not found") @@ -202,7 +202,7 @@ func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash r } block := b.eth.blockchain.GetBlock(hash, header.Number.Uint64()) if block == nil { - if header.Number.Uint64() < b.eth.blockchain.HistoryPruningCutoff() { + if header.Number.Uint64() < b.HistoryPruningCutoff() { return nil, ðconfig.PrunedHistoryError{} } return nil, errors.New("header found, but block body is missing") @@ -265,7 +265,8 @@ func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockN } func (b *EthAPIBackend) HistoryPruningCutoff() uint64 { - return b.eth.blockchain.HistoryPruningCutoff() + bn, _ := b.eth.blockchain.HistoryPruningCutoff() + return bn } func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 37210aa78bd..3fbf32e22e3 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -623,7 +623,10 @@ func (b testBackend) NewMatcherBackend() filtermaps.MatcherBackend { panic("implement me") } -func (b testBackend) HistoryPruningCutoff() uint64 { return b.chain.HistoryPruningCutoff() } +func (b testBackend) HistoryPruningCutoff() uint64 { + bn, _ := b.chain.HistoryPruningCutoff() + return bn +} func TestEstimateGas(t *testing.T) { t.Parallel() From 49f0d49e89af34eef7ea4d712b6936c4e7440311 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 3 Apr 2025 15:58:37 +0200 Subject: [PATCH 075/658] cmd/devp2p/internal/v5test: log test descriptions (#31551) This adds the test description text to the output, instead of keeping it as a Go comment. Logs are visible in the hive UI where these tests run, while Go comments are not. --- cmd/devp2p/internal/v5test/discv5tests.go | 35 ++++++++++++++++------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/cmd/devp2p/internal/v5test/discv5tests.go b/cmd/devp2p/internal/v5test/discv5tests.go index 7dbd3c3be5e..2139cd8ca6f 100644 --- a/cmd/devp2p/internal/v5test/discv5tests.go +++ b/cmd/devp2p/internal/v5test/discv5tests.go @@ -59,8 +59,9 @@ func (s *Suite) AllTests() []utesting.Test { } } -// TestPing sends PING and expects a PONG response. func (s *Suite) TestPing(t *utesting.T) { + t.Log(`This test is just a sanity check. It sends PING and expects a PONG response.`) + conn, l1 := s.listen1(t) defer conn.close() @@ -85,9 +86,10 @@ func checkPong(t *utesting.T, pong *v5wire.Pong, ping *v5wire.Ping, c net.Packet } } -// TestPingLargeRequestID sends PING with a 9-byte request ID, which isn't allowed by the spec. -// The remote node should not respond. func (s *Suite) TestPingLargeRequestID(t *utesting.T) { + t.Log(`This test sends PING with a 9-byte request ID, which isn't allowed by the spec. +The remote node should not respond.`) + conn, l1 := s.listen1(t) defer conn.close() @@ -104,10 +106,11 @@ func (s *Suite) TestPingLargeRequestID(t *utesting.T) { } } -// TestPingMultiIP establishes a session from one IP as usual. The session is then reused -// on another IP, which shouldn't work. The remote node should respond with WHOAREYOU for -// the attempt from a different IP. func (s *Suite) TestPingMultiIP(t *utesting.T) { + t.Log(`This test establishes a session from one IP as usual. The session is then reused +on another IP, which shouldn't work. The remote node should respond with WHOAREYOU for +the attempt from a different IP.`) + conn, l1, l2 := s.listen2(t) defer conn.close() @@ -120,6 +123,7 @@ func (s *Suite) TestPingMultiIP(t *utesting.T) { checkPong(t, resp.(*v5wire.Pong), ping, l1) // Send on l2. This reuses the session because there is only one codec. + t.Log("sending ping from alternate IP", l2.LocalAddr()) ping2 := &v5wire.Ping{ReqID: conn.nextReqID()} conn.write(l2, ping2, nil) switch resp := conn.read(l2).(type) { @@ -158,6 +162,10 @@ func (s *Suite) TestPingMultiIP(t *utesting.T) { // packet instead of a handshake message packet. The remote node should respond with // another WHOAREYOU challenge for the second packet. func (s *Suite) TestPingHandshakeInterrupted(t *utesting.T) { + t.Log(`TestPingHandshakeInterrupted starts a handshake, but doesn't finish it and sends a second ordinary message +packet instead of a handshake message packet. The remote node should respond with +another WHOAREYOU challenge for the second packet.`) + conn, l1 := s.listen1(t) defer conn.close() @@ -181,8 +189,10 @@ func (s *Suite) TestPingHandshakeInterrupted(t *utesting.T) { } } -// TestTalkRequest sends TALKREQ and expects an empty TALKRESP response. func (s *Suite) TestTalkRequest(t *utesting.T) { + t.Log(`This test sends some examples of TALKREQ with a protocol-id of "test-protocol" +and expects an empty TALKRESP response.`) + conn, l1 := s.listen1(t) defer conn.close() @@ -202,6 +212,7 @@ func (s *Suite) TestTalkRequest(t *utesting.T) { } // Empty request ID. + t.Log("sending TALKREQ with empty request-id") resp = conn.reqresp(l1, &v5wire.TalkRequest{Protocol: "test-protocol"}) switch resp := resp.(type) { case *v5wire.TalkResponse: @@ -216,8 +227,9 @@ func (s *Suite) TestTalkRequest(t *utesting.T) { } } -// TestFindnodeZeroDistance checks that the remote node returns itself for FINDNODE with distance zero. func (s *Suite) TestFindnodeZeroDistance(t *utesting.T) { + t.Log(`This test checks that the remote node returns itself for FINDNODE with distance zero.`) + conn, l1 := s.listen1(t) defer conn.close() @@ -233,9 +245,11 @@ func (s *Suite) TestFindnodeZeroDistance(t *utesting.T) { } } -// TestFindnodeResults pings the node under test from multiple nodes. After waiting for them to be -// accepted into the remote table, the test checks that they are returned by FINDNODE. func (s *Suite) TestFindnodeResults(t *utesting.T) { + t.Log(`This test pings the node under test from multiple other endpoints and node identities +(the 'bystanders'). After waiting for them to be accepted into the remote table, the test checks +that they are returned by FINDNODE.`) + // Create bystanders. nodes := make([]*bystander, 5) added := make(chan enode.ID, len(nodes)) @@ -273,6 +287,7 @@ func (s *Suite) TestFindnodeResults(t *utesting.T) { } // Send FINDNODE for all distances. + t.Log("requesting nodes") conn, l1 := s.listen1(t) defer conn.close() foundNodes, err := conn.findnode(l1, dists) From 553183e5de5dd6aa73038f3a24f90fb65e141402 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 3 Apr 2025 22:03:03 +0800 Subject: [PATCH 076/658] core, eth, node: use sync write option in pebble (#31519) Fixes #31499 --- core/bench_test.go | 8 ++++---- core/blockchain_repair_test.go | 8 ++++---- core/blockchain_sethead_test.go | 2 +- core/blockchain_snapshot_test.go | 4 ++-- core/blockchain_test.go | 2 +- ethdb/pebble/pebble.go | 4 ++-- node/database.go | 13 +++++++++---- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/core/bench_test.go b/core/bench_test.go index 155fa6c3b54..00f924076ad 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -183,7 +183,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { if !disk { db = rawdb.NewMemoryDatabase() } else { - pdb, err := pebble.New(b.TempDir(), 128, 128, "", false) + pdb, err := pebble.New(b.TempDir(), 128, 128, "", false, true) if err != nil { b.Fatalf("cannot create temporary database: %v", err) } @@ -303,7 +303,7 @@ func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uin func benchWriteChain(b *testing.B, full bool, count uint64) { genesis := &Genesis{Config: params.AllEthashProtocolChanges} for i := 0; i < b.N; i++ { - pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false) + pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false, true) if err != nil { b.Fatalf("error opening database: %v", err) } @@ -316,7 +316,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) { func benchReadChain(b *testing.B, full bool, count uint64) { dir := b.TempDir() - pdb, err := pebble.New(dir, 1024, 128, "", false) + pdb, err := pebble.New(dir, 1024, 128, "", false, true) if err != nil { b.Fatalf("error opening database: %v", err) } @@ -332,7 +332,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) { b.ResetTimer() for i := 0; i < b.N; i++ { - pdb, err = pebble.New(dir, 1024, 128, "", false) + pdb, err = pebble.New(dir, 1024, 128, "", false, true) if err != nil { b.Fatalf("error opening database: %v", err) } diff --git a/core/blockchain_repair_test.go b/core/blockchain_repair_test.go index 6c52d057adf..3ff1d77fc83 100644 --- a/core/blockchain_repair_test.go +++ b/core/blockchain_repair_test.go @@ -1765,7 +1765,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s datadir := t.TempDir() ancient := filepath.Join(datadir, "ancient") - pdb, err := pebble.New(datadir, 0, 0, "", false) + pdb, err := pebble.New(datadir, 0, 0, "", false, true) if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } @@ -1850,7 +1850,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s chain.stopWithoutSaving() // Start a new blockchain back up and see where the repair leads us - pdb, err = pebble.New(datadir, 0, 0, "", false) + pdb, err = pebble.New(datadir, 0, 0, "", false, true) if err != nil { t.Fatalf("Failed to reopen persistent key-value database: %v", err) } @@ -1915,7 +1915,7 @@ func testIssue23496(t *testing.T, scheme string) { datadir := t.TempDir() ancient := filepath.Join(datadir, "ancient") - pdb, err := pebble.New(datadir, 0, 0, "", false) + pdb, err := pebble.New(datadir, 0, 0, "", false, true) if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } @@ -1973,7 +1973,7 @@ func testIssue23496(t *testing.T, scheme string) { chain.stopWithoutSaving() // Start a new blockchain back up and see where the repair leads us - pdb, err = pebble.New(datadir, 0, 0, "", false) + pdb, err = pebble.New(datadir, 0, 0, "", false, true) if err != nil { t.Fatalf("Failed to reopen persistent key-value database: %v", err) } diff --git a/core/blockchain_sethead_test.go b/core/blockchain_sethead_test.go index 424854b2bf8..e998b510df9 100644 --- a/core/blockchain_sethead_test.go +++ b/core/blockchain_sethead_test.go @@ -1969,7 +1969,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme datadir := t.TempDir() ancient := filepath.Join(datadir, "ancient") - pdb, err := pebble.New(datadir, 0, 0, "", false) + pdb, err := pebble.New(datadir, 0, 0, "", false, true) if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } diff --git a/core/blockchain_snapshot_test.go b/core/blockchain_snapshot_test.go index 1a6fe38af6d..23effea15e2 100644 --- a/core/blockchain_snapshot_test.go +++ b/core/blockchain_snapshot_test.go @@ -66,7 +66,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo datadir := t.TempDir() ancient := filepath.Join(datadir, "ancient") - pdb, err := pebble.New(datadir, 0, 0, "", false) + pdb, err := pebble.New(datadir, 0, 0, "", false, true) if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } @@ -257,7 +257,7 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) { chain.triedb.Close() // Start a new blockchain back up and see where the repair leads us - pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false) + pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false, true) if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 8f5a64e2066..3f7c03b93c4 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -2491,7 +2491,7 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) { datadir := t.TempDir() ancient := path.Join(datadir, "ancient") - pdb, err := pebble.New(datadir, 0, 0, "", false) + pdb, err := pebble.New(datadir, 0, 0, "", false, true) if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go index b87ecb25950..969e67af5a3 100644 --- a/ethdb/pebble/pebble.go +++ b/ethdb/pebble/pebble.go @@ -144,7 +144,7 @@ func (l panicLogger) Fatalf(format string, args ...interface{}) { // New returns a wrapped pebble DB object. The namespace is the prefix that the // metrics reporting should use for surfacing internal stats. -func New(file string, cache int, handles int, namespace string, readonly bool) (*Database, error) { +func New(file string, cache int, handles int, namespace string, readonly bool, ephemeral bool) (*Database, error) { // Ensure we have some minimal caching and file guarantees if cache < minCache { cache = minCache @@ -185,7 +185,7 @@ func New(file string, cache int, handles int, namespace string, readonly bool) ( fn: file, log: logger, quitChan: make(chan chan error), - writeOptions: &pebble.WriteOptions{Sync: false}, + writeOptions: &pebble.WriteOptions{Sync: !ephemeral}, } opt := &pebble.Options{ // Pebble has a single combined cache area and the write diff --git a/node/database.go b/node/database.go index e3ccb910667..b7d0d856cbd 100644 --- a/node/database.go +++ b/node/database.go @@ -36,6 +36,11 @@ type openOptions struct { Cache int // the capacity(in megabytes) of the data caching Handles int // number of files to be open simultaneously ReadOnly bool + + // Ephemeral means that filesystem sync operations should be avoided: + // data integrity in the face of a crash is not important. This option + // should typically be used in tests. + Ephemeral bool } // openDatabase opens both a disk-based key-value database such as leveldb or pebble, but also @@ -78,7 +83,7 @@ func openKeyValueDatabase(o openOptions) (ethdb.Database, error) { } if o.Type == rawdb.DBPebble || existingDb == rawdb.DBPebble { log.Info("Using pebble as the backing database") - return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly) + return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral) } if o.Type == rawdb.DBLeveldb || existingDb == rawdb.DBLeveldb { log.Info("Using leveldb as the backing database") @@ -86,7 +91,7 @@ func openKeyValueDatabase(o openOptions) (ethdb.Database, error) { } // No pre-existing database, no user-requested one either. Default to Pebble. log.Info("Defaulting to pebble as the backing database") - return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly) + return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral) } // newLevelDBDatabase creates a persistent key-value database without a freezer @@ -102,8 +107,8 @@ func newLevelDBDatabase(file string, cache int, handles int, namespace string, r // newPebbleDBDatabase creates a persistent key-value database without a freezer // moving immutable chain segments into cold storage. -func newPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) { - db, err := pebble.New(file, cache, handles, namespace, readonly) +func newPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly bool, ephemeral bool) (ethdb.Database, error) { + db, err := pebble.New(file, cache, handles, namespace, readonly, ephemeral) if err != nil { return nil, err } From 9f83e9e6734f82facbc66fd4cd591dd648aaec92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Thu, 3 Apr 2025 16:04:11 +0200 Subject: [PATCH 077/658] beacon/blsync: add checkpoint import/export file feature (#31469) This PR adds a new `--beacon.checkpoint.file` config flag to geth and blsync which specifies a checkpoint import/export file. If a file with an existing checkpoint is specified, it is used for initialization instead of the hardcoded one (except when `--beacon.checkpoint` is also specified simultaneously). Whenever the client encounters a new valid finality update with a suitable finalized beacon block root at an epoch boundary, it saves the block root in hex format to the checkpoint file. --- beacon/blsync/client.go | 10 +++++++++- beacon/light/head_tracker.go | 9 ++++++++- beacon/params/config.go | 34 ++++++++++++++++++++++++++++++++++ cmd/blsync/main.go | 1 + cmd/geth/main.go | 1 + cmd/utils/flags.go | 30 +++++++++++++++++++++++++----- 6 files changed, 78 insertions(+), 7 deletions(-) diff --git a/beacon/blsync/client.go b/beacon/blsync/client.go index 3c93754d3df..744f4691240 100644 --- a/beacon/blsync/client.go +++ b/beacon/blsync/client.go @@ -23,9 +23,11 @@ import ( "github.com/ethereum/go-ethereum/beacon/light/sync" "github.com/ethereum/go-ethereum/beacon/params" "github.com/ethereum/go-ethereum/beacon/types" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" ) @@ -46,7 +48,13 @@ func NewClient(config params.ClientConfig) *Client { var ( db = memorydb.New() committeeChain = light.NewCommitteeChain(db, &config.ChainConfig, config.Threshold, !config.NoFilter) - headTracker = light.NewHeadTracker(committeeChain, config.Threshold) + headTracker = light.NewHeadTracker(committeeChain, config.Threshold, func(checkpoint common.Hash) { + if saved, err := config.SaveCheckpointToFile(checkpoint); saved { + log.Debug("Saved beacon checkpoint", "file", config.CheckpointFile, "checkpoint", checkpoint) + } else if err != nil { + log.Error("Failed to save beacon checkpoint", "file", config.CheckpointFile, "checkpoint", checkpoint, "error", err) + } + }) ) headSync := sync.NewHeadSync(headTracker, committeeChain) diff --git a/beacon/light/head_tracker.go b/beacon/light/head_tracker.go index 010e548ddbd..62faf1dbc1e 100644 --- a/beacon/light/head_tracker.go +++ b/beacon/light/head_tracker.go @@ -21,7 +21,9 @@ import ( "sync" "time" + "github.com/ethereum/go-ethereum/beacon/params" "github.com/ethereum/go-ethereum/beacon/types" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" ) @@ -38,13 +40,15 @@ type HeadTracker struct { hasFinalityUpdate bool prefetchHead types.HeadInfo changeCounter uint64 + saveCheckpoint func(common.Hash) } // NewHeadTracker creates a new HeadTracker. -func NewHeadTracker(committeeChain *CommitteeChain, minSignerCount int) *HeadTracker { +func NewHeadTracker(committeeChain *CommitteeChain, minSignerCount int, saveCheckpoint func(common.Hash)) *HeadTracker { return &HeadTracker{ committeeChain: committeeChain, minSignerCount: minSignerCount, + saveCheckpoint: saveCheckpoint, } } @@ -100,6 +104,9 @@ func (h *HeadTracker) ValidateFinality(update types.FinalityUpdate) (bool, error if replace { h.finalityUpdate, h.hasFinalityUpdate = update, true h.changeCounter++ + if h.saveCheckpoint != nil && update.Finalized.Slot%params.EpochLength == 0 { + h.saveCheckpoint(update.Finalized.Hash()) + } } return replace, err } diff --git a/beacon/params/config.go b/beacon/params/config.go index be2a40f1718..2f6ba082c51 100644 --- a/beacon/params/config.go +++ b/beacon/params/config.go @@ -54,6 +54,7 @@ type ChainConfig struct { GenesisValidatorsRoot common.Hash // Root hash of the genesis validator set, used for signature domain calculation Forks Forks Checkpoint common.Hash + CheckpointFile string } // ForkAtEpoch returns the latest active fork at the given epoch. @@ -211,3 +212,36 @@ func (f Forks) Less(i, j int) bool { } return f[i].knownIndex < f[j].knownIndex } + +// SetCheckpointFile sets the checkpoint import/export file name and attempts to +// read the checkpoint from the file if it already exists. It returns true if +// a checkpoint has been loaded. +func (c *ChainConfig) SetCheckpointFile(checkpointFile string) (bool, error) { + c.CheckpointFile = checkpointFile + file, err := os.ReadFile(checkpointFile) + if os.IsNotExist(err) { + return false, nil // did not load checkpoint + } + if err != nil { + return false, fmt.Errorf("failed to read beacon checkpoint file: %v", err) + } + cp, err := hexutil.Decode(string(file)) + if err != nil { + return false, fmt.Errorf("failed to decode hex string in beacon checkpoint file: %v", err) + } + if len(cp) != 32 { + return false, fmt.Errorf("invalid hex string length in beacon checkpoint file: %d", len(cp)) + } + copy(c.Checkpoint[:len(cp)], cp) + return true, nil +} + +// SaveCheckpointToFile saves the given checkpoint to file if a checkpoint +// import/export file has been specified. +func (c *ChainConfig) SaveCheckpointToFile(checkpoint common.Hash) (bool, error) { + if c.CheckpointFile == "" { + return false, nil + } + err := os.WriteFile(c.CheckpointFile, []byte(checkpoint.Hex()), 0600) + return err == nil, err +} diff --git a/cmd/blsync/main.go b/cmd/blsync/main.go index 60caa4aa2a0..39a94073045 100644 --- a/cmd/blsync/main.go +++ b/cmd/blsync/main.go @@ -43,6 +43,7 @@ func main() { utils.BeaconGenesisRootFlag, utils.BeaconGenesisTimeFlag, utils.BeaconCheckpointFlag, + utils.BeaconCheckpointFileFlag, //TODO datadir for optional permanent database utils.MainnetFlag, utils.SepoliaFlag, diff --git a/cmd/geth/main.go b/cmd/geth/main.go index cd74fb7b6a5..ab46e059f39 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -157,6 +157,7 @@ var ( utils.BeaconGenesisRootFlag, utils.BeaconGenesisTimeFlag, utils.BeaconCheckpointFlag, + utils.BeaconCheckpointFileFlag, }, utils.NetworkFlags, utils.DatabaseFlags) rpcFlags = []cli.Flag{ diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index fb2892d2c1d..f5fc94cebc5 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -342,6 +342,11 @@ var ( Usage: "Beacon chain weak subjectivity checkpoint block hash", Category: flags.BeaconCategory, } + BeaconCheckpointFileFlag = &cli.StringFlag{ + Name: "beacon.checkpoint.file", + Usage: "Beacon chain weak subjectivity checkpoint import/export file", + Category: flags.BeaconCategory, + } BlsyncApiFlag = &cli.StringFlag{ Name: "blsync.engine.api", Usage: "Target EL engine API URL", @@ -1890,7 +1895,7 @@ func MakeBeaconLightConfig(ctx *cli.Context) bparams.ClientConfig { if !ctx.IsSet(BeaconGenesisTimeFlag.Name) { Fatalf("Custom beacon chain config is specified but genesis time is missing") } - if !ctx.IsSet(BeaconCheckpointFlag.Name) { + if !ctx.IsSet(BeaconCheckpointFlag.Name) && !ctx.IsSet(BeaconCheckpointFileFlag.Name) { Fatalf("Custom beacon chain config is specified but checkpoint is missing") } config.ChainConfig = bparams.ChainConfig{ @@ -1915,12 +1920,27 @@ func MakeBeaconLightConfig(ctx *cli.Context) bparams.ClientConfig { } } // Checkpoint is required with custom chain config and is optional with pre-defined config + // If both checkpoint block hash and checkpoint file are specified then the + // client is initialized with the specified block hash and new checkpoints + // are saved to the specified file. + if ctx.IsSet(BeaconCheckpointFileFlag.Name) { + if _, err := config.SetCheckpointFile(ctx.String(BeaconCheckpointFileFlag.Name)); err != nil { + Fatalf("Could not load beacon checkpoint file", "beacon.checkpoint.file", ctx.String(BeaconCheckpointFileFlag.Name), "error", err) + } + } if ctx.IsSet(BeaconCheckpointFlag.Name) { - if c, err := hexutil.Decode(ctx.String(BeaconCheckpointFlag.Name)); err == nil && len(c) <= 32 { - copy(config.Checkpoint[:len(c)], c) - } else { - Fatalf("Invalid hex string", "beacon.checkpoint", ctx.String(BeaconCheckpointFlag.Name), "error", err) + hex := ctx.String(BeaconCheckpointFlag.Name) + c, err := hexutil.Decode(hex) + if err != nil { + Fatalf("Invalid hex string", "beacon.checkpoint", hex, "error", err) } + if len(c) != 32 { + Fatalf("Invalid hex string length", "beacon.checkpoint", hex, "length", len(c)) + } + copy(config.Checkpoint[:len(c)], c) + } + if config.Checkpoint == (common.Hash{}) { + Fatalf("Beacon checkpoint not specified") } config.Apis = ctx.StringSlice(BeaconApiFlag.Name) if config.Apis == nil { From ff365afc63f047c09ff04f41e12678c6b6698b72 Mon Sep 17 00:00:00 2001 From: Nathan Jo <162083209+qqqeck@users.noreply.github.com> Date: Fri, 4 Apr 2025 17:56:55 +0900 Subject: [PATCH 078/658] p2p/nat: remove forceful port mapping in upnp (#30265) Here we are modifying the port mapping logic so that existing port mappings will only be removed when they were previously created by geth. The AddAnyPortMapping functionality has been adapted to work consistently between the IGDv1 and IGDv2 backends. --- p2p/nat/natpmp.go | 3 +++ p2p/nat/natupnp.go | 36 +++++++++++++++++++++++------------- p2p/server_nat.go | 13 ++++--------- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/p2p/nat/natpmp.go b/p2p/nat/natpmp.go index 4a9644ac1a2..ee07eb4ff68 100644 --- a/p2p/nat/natpmp.go +++ b/p2p/nat/natpmp.go @@ -49,6 +49,9 @@ func (n *pmp) AddMapping(protocol string, extport, intport int, name string, lif if lifetime <= 0 { return 0, errors.New("lifetime must not be <= 0") } + if extport == 0 { + extport = intport + } // Note order of port arguments is switched between our // AddMapping and the client's AddPortMapping. res, err := n.c.AddPortMapping(strings.ToLower(protocol), intport, extport, int(lifetime/time.Second)) diff --git a/p2p/nat/natupnp.go b/p2p/nat/natupnp.go index 1014dc69d23..ed00b8eeb67 100644 --- a/p2p/nat/natupnp.go +++ b/p2p/nat/natupnp.go @@ -86,15 +86,15 @@ func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, li } protocol = strings.ToUpper(protocol) lifetimeS := uint32(lifetime / time.Second) - n.DeleteMapping(protocol, extport, intport) - err = n.withRateLimit(func() error { - return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) - }) - if err == nil { - return uint16(extport), nil + if extport == 0 { + extport = intport + } else { + // Only delete port mapping if the external port was already used by geth. + n.DeleteMapping(protocol, extport, intport) } - // Try addAnyPortMapping if mapping specified port didn't work. + + // Try to add port mapping, preferring the specified external port. err = n.withRateLimit(func() error { p, err := n.addAnyPortMapping(protocol, extport, intport, ip, desc, lifetimeS) if err == nil { @@ -105,18 +105,28 @@ func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, li return uint16(extport), err } +// addAnyPortMapping tries to add a port mapping with the specified external port. +// If the external port is already in use, it will try to assign another port. func (n *upnp) addAnyPortMapping(protocol string, extport, intport int, ip net.IP, desc string, lifetimeS uint32) (uint16, error) { if client, ok := n.client.(*internetgateway2.WANIPConnection2); ok { return client.AddAnyPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) } - // It will retry with a random port number if the client does - // not support AddAnyPortMapping. - extport = n.randomPort() + // For IGDv1 and v1 services we should first try to add with extport. err := n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) - if err != nil { - return 0, err + if err == nil { + return uint16(extport), nil + } + + // If above fails, we retry with a random port. + // We retry several times because of possible port conflicts. + for i := 0; i < 3; i++ { + extport = n.randomPort() + err := n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) + if err == nil { + return uint16(extport), nil + } } - return uint16(extport), nil + return 0, err } func (n *upnp) randomPort() int { diff --git a/p2p/server_nat.go b/p2p/server_nat.go index 933993bc1f4..5830f950e1b 100644 --- a/p2p/server_nat.go +++ b/p2p/server_nat.go @@ -150,14 +150,9 @@ func (srv *Server) portMappingLoop() { continue } - external := m.port - if m.extPort != 0 { - external = m.extPort - } - log := newLogger(m.protocol, external, m.port) - + log := newLogger(m.protocol, m.extPort, m.port) log.Trace("Attempting port mapping") - p, err := srv.NAT.AddMapping(m.protocol, external, m.port, m.name, portMapDuration) + p, err := srv.NAT.AddMapping(m.protocol, m.extPort, m.port, m.name, portMapDuration) if err != nil { log.Debug("Couldn't add port mapping", "err", err) m.extPort = 0 @@ -167,8 +162,8 @@ func (srv *Server) portMappingLoop() { // It was mapped! m.extPort = int(p) m.nextTime = srv.clock.Now().Add(portMapRefreshInterval) - if external != m.extPort { - log = newLogger(m.protocol, m.extPort, m.port) + log = newLogger(m.protocol, m.extPort, m.port) + if m.port != m.extPort { log.Info("NAT mapped alternative port") } else { log.Info("NAT mapped port") From 77dc1acafaad69e6adc98293541ee49644ed9218 Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Fri, 4 Apr 2025 14:07:46 +0200 Subject: [PATCH 079/658] internal/era: random access to header and receipts (#31544) Co-authored-by: lightclient Add GetHeaderByNumber and GetReceiptsByNumber to allow more efficient API request filling from Era files. --- internal/era/e2store/e2store.go | 12 +++++++ internal/era/era.go | 64 ++++++++++++++++++++++++++++----- internal/era/era_test.go | 53 ++++++++++++++++++++++----- 3 files changed, 112 insertions(+), 17 deletions(-) diff --git a/internal/era/e2store/e2store.go b/internal/era/e2store/e2store.go index 9832b72d48c..b0d43bf55aa 100644 --- a/internal/era/e2store/e2store.go +++ b/internal/era/e2store/e2store.go @@ -219,3 +219,15 @@ func (r *Reader) FindAll(want uint16) ([]*Entry, error) { off += int64(headerSize + length) } } + +// SkipN skips `n` entries starting from `offset` and returns the new offset. +func (r *Reader) SkipN(offset int64, n uint64) (int64, error) { + for i := uint64(0); i < n; i++ { + length, err := r.LengthAt(offset) + if err != nil { + return 0, err + } + offset += length + } + return offset, nil +} diff --git a/internal/era/era.go b/internal/era/era.go index daf337963d0..5129186fe78 100644 --- a/internal/era/era.go +++ b/internal/era/era.go @@ -70,7 +70,7 @@ func ReadDir(dir, network string) ([]string, error) { } parts := strings.Split(entry.Name(), "-") if len(parts) != 3 || parts[0] != network { - // invalid era1 filename, skip + // Invalid era1 filename, skip. continue } if epoch, err := strconv.ParseUint(parts[1], 10, 64); err != nil { @@ -126,6 +126,29 @@ func (e *Era) Close() error { return e.f.Close() } +// GetHeaderByNumber returns the header for the given block number. +func (e *Era) GetHeaderByNumber(num uint64) (*types.Header, error) { + if e.m.start > num || e.m.start+e.m.count <= num { + return nil, errors.New("out-of-bounds") + } + off, err := e.readOffset(num) + if err != nil { + return nil, err + } + + // Read and decompress header. + r, _, err := newSnappyReader(e.s, TypeCompressedHeader, off) + if err != nil { + return nil, err + } + var header types.Header + if err := rlp.Decode(r, &header); err != nil { + return nil, err + } + return &header, nil +} + +// GetBlockByNumber returns the block for the given block number. func (e *Era) GetBlockByNumber(num uint64) (*types.Block, error) { if e.m.start > num || e.m.start+e.m.count <= num { return nil, errors.New("out-of-bounds") @@ -154,6 +177,34 @@ func (e *Era) GetBlockByNumber(num uint64) (*types.Block, error) { return types.NewBlockWithHeader(&header).WithBody(body), nil } +// GetReceiptsByNumber returns the receipts for the given block number. +func (e *Era) GetReceiptsByNumber(num uint64) (types.Receipts, error) { + if e.m.start > num || e.m.start+e.m.count <= num { + return nil, errors.New("out-of-bounds") + } + off, err := e.readOffset(num) + if err != nil { + return nil, err + } + + // Skip over header and body. + off, err = e.s.SkipN(off, 2) + if err != nil { + return nil, err + } + + // Read and decompress receipts. + r, _, err := newSnappyReader(e.s, TypeCompressedReceipts, off) + if err != nil { + return nil, err + } + var receipts types.Receipts + if err := rlp.Decode(r, &receipts); err != nil { + return nil, err + } + return receipts, nil +} + // Accumulator reads the accumulator entry in the Era1 file. func (e *Era) Accumulator() (common.Hash, error) { entry, err := e.s.Find(TypeAccumulator) @@ -187,13 +238,10 @@ func (e *Era) InitialTD() (*big.Int, error) { } off += n - // Skip over next two records. - for i := 0; i < 2; i++ { - length, err := e.s.LengthAt(off) - if err != nil { - return nil, err - } - off += length + // Skip over header and body. + off, err = e.s.SkipN(off, 2) + if err != nil { + return nil, err } // Read total difficulty after first block. diff --git a/internal/era/era_test.go b/internal/era/era_test.go index 72c3b385dd3..46fc2e91f38 100644 --- a/internal/era/era_test.go +++ b/internal/era/era_test.go @@ -18,12 +18,15 @@ package era import ( "bytes" + "fmt" "io" "math/big" "os" "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" ) type testchain struct { @@ -48,9 +51,9 @@ func TestEra1Builder(t *testing.T) { chain = testchain{} ) for i := 0; i < 128; i++ { - chain.headers = append(chain.headers, []byte{byte('h'), byte(i)}) - chain.bodies = append(chain.bodies, []byte{byte('b'), byte(i)}) - chain.receipts = append(chain.receipts, []byte{byte('r'), byte(i)}) + chain.headers = append(chain.headers, mustEncode(&types.Header{Number: big.NewInt(int64(i))})) + chain.bodies = append(chain.bodies, mustEncode(&types.Body{Transactions: []*types.Transaction{types.NewTransaction(0, common.Address{byte(i)}, nil, 0, nil, nil)}})) + chain.receipts = append(chain.receipts, mustEncode(&types.Receipts{{CumulativeGasUsed: uint64(i)}})) chain.tds = append(chain.tds, big.NewInt(int64(i))) } @@ -91,13 +94,25 @@ func TestEra1Builder(t *testing.T) { t.Fatalf("unexpected error %v", it.Error()) } // Check headers. - header, err := io.ReadAll(it.Header) + rawHeader, err := io.ReadAll(it.Header) + if err != nil { + t.Fatalf("error reading header from iterator: %v", err) + } + if !bytes.Equal(rawHeader, chain.headers[i]) { + t.Fatalf("mismatched header: want %s, got %s", chain.headers[i], rawHeader) + } + header, err := e.GetHeaderByNumber(i) if err != nil { t.Fatalf("error reading header: %v", err) } - if !bytes.Equal(header, chain.headers[i]) { - t.Fatalf("mismatched header: want %s, got %s", chain.headers[i], header) + encHeader, err := rlp.EncodeToBytes(header) + if err != nil { + t.Fatalf("error encoding header: %v", err) + } + if !bytes.Equal(encHeader, chain.headers[i]) { + t.Fatalf("mismatched header: want %s, got %s", chain.headers[i], encHeader) } + // Check bodies. body, err := io.ReadAll(it.Body) if err != nil { @@ -106,13 +121,25 @@ func TestEra1Builder(t *testing.T) { if !bytes.Equal(body, chain.bodies[i]) { t.Fatalf("mismatched body: want %s, got %s", chain.bodies[i], body) } + // Check receipts. - receipts, err := io.ReadAll(it.Receipts) + rawReceipts, err := io.ReadAll(it.Receipts) + if err != nil { + t.Fatalf("error reading receipts from iterator: %v", err) + } + if !bytes.Equal(rawReceipts, chain.receipts[i]) { + t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], rawReceipts) + } + receipts, err := e.GetReceiptsByNumber(i) if err != nil { t.Fatalf("error reading receipts: %v", err) } - if !bytes.Equal(receipts, chain.receipts[i]) { - t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], receipts) + encReceipts, err := rlp.EncodeToBytes(receipts) + if err != nil { + t.Fatalf("error encoding receipts: %v", err) + } + if !bytes.Equal(encReceipts, chain.receipts[i]) { + t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], encReceipts) } // Check total difficulty. @@ -144,3 +171,11 @@ func TestEraFilename(t *testing.T) { } } } + +func mustEncode(obj any) []byte { + b, err := rlp.EncodeToBytes(obj) + if err != nil { + panic(fmt.Sprintf("failed in encode obj: %v", err)) + } + return b +} From 21b035eb29f6489ffee66d8ee2451873bc96dd2d Mon Sep 17 00:00:00 2001 From: Delweng Date: Mon, 7 Apr 2025 13:16:26 +0800 Subject: [PATCH 080/658] cmd/geth: set trie,gc and other cache flags for import chain (#31577) Signed-off-by: jsvisa --- cmd/geth/chaincmd.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 0c36b82af58..22795095424 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -79,11 +79,15 @@ if one is set. Otherwise it prints the genesis from the datadir.`, Usage: "Import a blockchain file", ArgsUsage: " ( ... ) ", Flags: slices.Concat([]cli.Flag{ - utils.CacheFlag, utils.GCModeFlag, utils.SnapshotFlag, + utils.CacheFlag, utils.CacheDatabaseFlag, + utils.CacheTrieFlag, utils.CacheGCFlag, + utils.CacheSnapshotFlag, + utils.CacheNoPrefetchFlag, + utils.CachePreimagesFlag, utils.NoCompactionFlag, utils.MetricsEnabledFlag, utils.MetricsEnabledExpensiveFlag, From ec6d1044045bcd6f8fe96892d840fe35c16ca7c8 Mon Sep 17 00:00:00 2001 From: Ocenka Date: Tue, 8 Apr 2025 15:44:13 +0300 Subject: [PATCH 081/658] eth/remotedb: improve error handling (#31331) This PR improves error handling in the remotedb package by fixing two issues: 1. In the `Has` method, we now properly propagate errors instead of silently returning false. This makes the behavior more predictable and helps clients better understand when there are connection issues. 2. In the `New` constructor, we add a nil check for the client parameter to prevent potential panics. This follows Go best practices for constructor functions. These changes make the code more robust and follow Go's error handling idioms without requiring any changes to other parts of the codebase. Changes: - Modified `Has` method to return errors instead of silently returning false - Added nil check in `New` constructor - Fixed field name in constructor to match struct definition --- ethdb/remotedb/remotedb.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ethdb/remotedb/remotedb.go b/ethdb/remotedb/remotedb.go index 247a4392db8..8a91fdbcf26 100644 --- a/ethdb/remotedb/remotedb.go +++ b/ethdb/remotedb/remotedb.go @@ -34,7 +34,7 @@ type Database struct { func (db *Database) Has(key []byte) (bool, error) { if _, err := db.Get(key); err != nil { - return false, nil + return false, err } return true, nil } @@ -50,7 +50,7 @@ func (db *Database) Get(key []byte) ([]byte, error) { func (db *Database) HasAncient(kind string, number uint64) (bool, error) { if _, err := db.Ancient(kind, number); err != nil { - return false, nil + return false, err } return true, nil } @@ -144,7 +144,8 @@ func (db *Database) Close() error { } func New(client *rpc.Client) ethdb.Database { - return &Database{ - remote: client, + if client == nil { + return nil } + return &Database{remote: client} } From 2e739fce584a3cc8d84bf4c4604cd81c07294e2b Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Tue, 8 Apr 2025 21:46:27 +0800 Subject: [PATCH 082/658] core/txpool: add 7702 protection to blobpool (#31526) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This pull request introduces two constraints in the blobPool: (a) If the sender has a pending authorization or delegation, only one in-flight executable transaction can be cached. (b) If the authority address in a SetCode transaction is already reserved by the blobPool, the transaction will be rejected. These constraints mitigate an attack where an attacker spams the pool with numerous blob transactions, evicts other transactions, and then cancels all pending blob transactions by draining the sender’s funds if they have a delegation. Note, because there is no exclusive lock held between different subpools when processing transactions, it's totally possible the SetCode transaction and blob transactions with conflict sender and authorities are accepted simultaneously. I think it's acceptable as it's very hard to be exploited. --------- Co-authored-by: lightclient --- core/txpool/blobpool/blobpool.go | 66 ++++++++--- core/txpool/blobpool/blobpool_test.go | 60 +++------- core/txpool/errors.go | 4 + core/txpool/legacypool/legacypool.go | 60 ++++++---- core/txpool/legacypool/legacypool2_test.go | 8 +- core/txpool/legacypool/legacypool_test.go | 82 +++++--------- core/txpool/reserver.go | 124 +++++++++++++++++++++ core/txpool/subpool.go | 6 +- core/txpool/txpool.go | 77 ++----------- eth/backend.go | 10 +- eth/protocols/eth/handler_test.go | 2 +- 11 files changed, 286 insertions(+), 213 deletions(-) create mode 100644 core/txpool/reserver.go diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 5a20c3ce5a6..b1966905a8f 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -298,8 +298,9 @@ func newBlobTxMeta(id uint64, size uint64, storageSize uint32, tx *types.Transac // minimums will need to be done only starting at the swapped in/out nonce // and leading up to the first no-change. type BlobPool struct { - config Config // Pool configuration - reserve txpool.AddressReserver // Address reserver to ensure exclusivity across subpools + config Config // Pool configuration + reserver *txpool.Reserver // Address reserver to ensure exclusivity across subpools + hasPendingAuth func(common.Address) bool // Determine whether the specified address has a pending 7702-auth store billy.Database // Persistent data store for the tx metadata and blobs stored uint64 // Useful data size of all transactions on disk @@ -329,13 +330,14 @@ type BlobPool struct { // New creates a new blob transaction pool to gather, sort and filter inbound // blob transactions from the network. -func New(config Config, chain BlockChain) *BlobPool { +func New(config Config, chain BlockChain, hasPendingAuth func(common.Address) bool) *BlobPool { // Sanitize the input to ensure no vulnerable gas prices are set config = (&config).sanitize() // Create the transaction pool with its initial settings return &BlobPool{ config: config, + hasPendingAuth: hasPendingAuth, signer: types.LatestSigner(chain.Config()), chain: chain, lookup: newLookup(), @@ -353,8 +355,8 @@ func (p *BlobPool) Filter(tx *types.Transaction) bool { // Init sets the gas price needed to keep a transaction in the pool and the chain // head to allow balance / nonce checks. The transaction journal will be loaded // from disk and filtered based on the provided starting settings. -func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserve txpool.AddressReserver) error { - p.reserve = reserve +func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver *txpool.Reserver) error { + p.reserver = reserver var ( queuedir string @@ -499,7 +501,7 @@ func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error { return err } if _, ok := p.index[sender]; !ok { - if err := p.reserve(sender, true); err != nil { + if err := p.reserver.Hold(sender); err != nil { return err } p.index[sender] = []*blobTxMeta{} @@ -554,7 +556,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 if inclusions != nil { // only during reorgs will the heap be initialized heap.Remove(p.evict, p.evict.index[addr]) } - p.reserve(addr, false) + p.reserver.Release(addr) if gapped { log.Warn("Dropping dangling blob transactions", "from", addr, "missing", next, "drop", nonces, "ids", ids) @@ -707,7 +709,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 if inclusions != nil { // only during reorgs will the heap be initialized heap.Remove(p.evict, p.evict.index[addr]) } - p.reserve(addr, false) + p.reserver.Release(addr) } else { p.index[addr] = txs } @@ -1006,7 +1008,7 @@ func (p *BlobPool) reinject(addr common.Address, txhash common.Hash) error { // Update the indices and metrics meta := newBlobTxMeta(id, tx.Size(), p.store.Size(id), tx) if _, ok := p.index[addr]; !ok { - if err := p.reserve(addr, true); err != nil { + if err := p.reserver.Hold(addr); err != nil { log.Warn("Failed to reserve account for blob pool", "tx", tx.Hash(), "from", addr, "err", err) return err } @@ -1066,7 +1068,7 @@ func (p *BlobPool) SetGasTip(tip *big.Int) { delete(p.spent, addr) heap.Remove(p.evict, p.evict.index[addr]) - p.reserve(addr, false) + p.reserver.Release(addr) } // Clear out the transactions from the data store log.Warn("Dropping underpriced blob transaction", "from", addr, "rejected", tx.nonce, "tip", tx.execTipCap, "want", tip, "drop", nonces, "ids", ids) @@ -1101,6 +1103,39 @@ func (p *BlobPool) ValidateTxBasics(tx *types.Transaction) error { return txpool.ValidateTransaction(tx, p.head, p.signer, opts) } +// checkDelegationLimit determines if the tx sender is delegated or has a +// pending delegation, and if so, ensures they have at most one in-flight +// **executable** transaction, e.g. disallow stacked and gapped transactions +// from the account. +func (p *BlobPool) checkDelegationLimit(tx *types.Transaction) error { + from, _ := types.Sender(p.signer, tx) // validated + + // Short circuit if the sender has neither delegation nor pending delegation. + if p.state.GetCodeHash(from) == types.EmptyCodeHash { + // Because there is no exclusive lock held between different subpools + // when processing transactions, a blob transaction may be accepted + // while other SetCode transactions with pending authorities from the + // same address are also accepted simultaneously. + // + // This scenario is considered acceptable, as the rule primarily ensures + // that attackers cannot easily and endlessly stack blob transactions + // with a delegated or pending delegated sender. + if p.hasPendingAuth == nil || !p.hasPendingAuth(from) { + return nil + } + } + // Allow a single in-flight pending transaction. + pending := p.index[from] + if len(pending) == 0 { + return nil + } + // If account already has a pending transaction, allow replacement only. + if len(pending) == 1 && pending[0].nonce == tx.Nonce() { + return nil + } + return txpool.ErrInflightTxLimitReached +} + // validateTx checks whether a transaction is valid according to the consensus // rules and adheres to some heuristic limits of the local node (price and size). func (p *BlobPool) validateTx(tx *types.Transaction) error { @@ -1141,6 +1176,9 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error { if err := txpool.ValidateTransactionWithState(tx, p.signer, stateOpts); err != nil { return err } + if err := p.checkDelegationLimit(tx); err != nil { + return err + } // If the transaction replaces an existing one, ensure that price bumps are // adhered to. var ( @@ -1369,7 +1407,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) { // only by this subpool until all transactions are evicted from, _ := types.Sender(p.signer, tx) // already validated above if _, ok := p.index[from]; !ok { - if err := p.reserve(from, true); err != nil { + if err := p.reserver.Hold(from); err != nil { addNonExclusiveMeter.Mark(1) return err } @@ -1381,7 +1419,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) { // by a return statement before running deferred methods. Take care with // removing or subscoping err as it will break this clause. if err != nil { - p.reserve(from, false) + p.reserver.Release(from) } }() } @@ -1513,7 +1551,7 @@ func (p *BlobPool) drop() { if last { delete(p.index, from) delete(p.spent, from) - p.reserve(from, false) + p.reserver.Release(from) } else { txs[len(txs)-1] = nil txs = txs[:len(txs)-1] @@ -1789,7 +1827,7 @@ func (p *BlobPool) Clear() { // can't happen until Clear releases the reservation lock. Clear cannot // acquire the subpool lock until the transaction addition is completed. for acct := range p.index { - p.reserve(acct, false) + p.reserver.Release(acct) } p.lookup = newLookup() p.index = make(map[common.Address][]*blobTxMeta) diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index b7c6cfa51e8..4dfba3b52b1 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -26,7 +26,6 @@ import ( "math/big" "os" "path/filepath" - "sync" "testing" "github.com/ethereum/go-ethereum/common" @@ -168,33 +167,6 @@ func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { return bc.statedb, nil } -// makeAddressReserver is a utility method to sanity check that accounts are -// properly reserved by the blobpool (no duplicate reserves or unreserves). -func makeAddressReserver() txpool.AddressReserver { - var ( - reserved = make(map[common.Address]struct{}) - lock sync.Mutex - ) - return func(addr common.Address, reserve bool) error { - lock.Lock() - defer lock.Unlock() - - _, exists := reserved[addr] - if reserve { - if exists { - panic("already reserved") - } - reserved[addr] = struct{}{} - return nil - } - if !exists { - panic("not reserved") - } - delete(reserved, addr) - return nil - } -} - // makeTx is a utility method to construct a random blob transaction and sign it // with a valid key, only setting the interesting fields from the perspective of // the blob pool. @@ -433,6 +405,10 @@ func verifyBlobRetrievals(t *testing.T, pool *BlobPool) { } } +func newReserver() *txpool.Reserver { + return txpool.NewReservationTracker().NewHandle(42) +} + // Tests that transactions can be loaded from disk on startup and that they are // correctly discarded if invalid. // @@ -699,8 +675,8 @@ func TestOpenDrops(t *testing.T) { blobfee: uint256.NewInt(params.BlobTxMinBlobGasprice), statedb: statedb, } - pool := New(Config{Datadir: storage}, chain) - if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { + pool := New(Config{Datadir: storage}, chain, nil) + if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil { t.Fatalf("failed to create blob pool: %v", err) } defer pool.Close() @@ -817,8 +793,8 @@ func TestOpenIndex(t *testing.T) { blobfee: uint256.NewInt(params.BlobTxMinBlobGasprice), statedb: statedb, } - pool := New(Config{Datadir: storage}, chain) - if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { + pool := New(Config{Datadir: storage}, chain, nil) + if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil { t.Fatalf("failed to create blob pool: %v", err) } defer pool.Close() @@ -918,8 +894,8 @@ func TestOpenHeap(t *testing.T) { blobfee: uint256.NewInt(105), statedb: statedb, } - pool := New(Config{Datadir: storage}, chain) - if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { + pool := New(Config{Datadir: storage}, chain, nil) + if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil { t.Fatalf("failed to create blob pool: %v", err) } defer pool.Close() @@ -997,8 +973,8 @@ func TestOpenCap(t *testing.T) { blobfee: uint256.NewInt(105), statedb: statedb, } - pool := New(Config{Datadir: storage, Datacap: datacap}, chain) - if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { + pool := New(Config{Datadir: storage, Datacap: datacap}, chain, nil) + if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil { t.Fatalf("failed to create blob pool: %v", err) } // Verify that enough transactions have been dropped to get the pool's size @@ -1098,8 +1074,8 @@ func TestChangingSlotterSize(t *testing.T) { blobfee: uint256.NewInt(105), statedb: statedb, } - pool := New(Config{Datadir: storage}, chain) - if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { + pool := New(Config{Datadir: storage}, chain, nil) + if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil { t.Fatalf("failed to create blob pool: %v", err) } @@ -1541,8 +1517,8 @@ func TestAdd(t *testing.T) { blobfee: uint256.NewInt(105), statedb: statedb, } - pool := New(Config{Datadir: storage}, chain) - if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { + pool := New(Config{Datadir: storage}, chain, nil) + if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil { t.Fatalf("test %d: failed to create blob pool: %v", i, err) } verifyPoolInternals(t, pool) @@ -1638,10 +1614,10 @@ func benchmarkPoolPending(b *testing.B, datacap uint64) { blobfee: uint256.NewInt(blobfee), statedb: statedb, } - pool = New(Config{Datadir: ""}, chain) + pool = New(Config{Datadir: ""}, chain, nil) ) - if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { + if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil { b.Fatalf("failed to create blob pool: %v", err) } // Make the pool not use disk (just drop everything). This test never reads diff --git a/core/txpool/errors.go b/core/txpool/errors.go index c38644857e3..02f5703b6ca 100644 --- a/core/txpool/errors.go +++ b/core/txpool/errors.go @@ -56,4 +56,8 @@ var ( // input transaction of non-blob type when a blob transaction from this sender // remains pending (and vice-versa). ErrAlreadyReserved = errors.New("address already reserved") + + // ErrInflightTxLimitReached is returned when the maximum number of in-flight + // transactions is reached for specific accounts. + ErrInflightTxLimitReached = errors.New("in-flight transaction limit reached for delegated accounts") ) diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 9066f3e16b2..278ad0791f0 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -63,10 +63,6 @@ var ( // another remote transaction. ErrTxPoolOverflow = errors.New("txpool is full") - // ErrInflightTxLimitReached is returned when the maximum number of in-flight - // transactions is reached for specific accounts. - ErrInflightTxLimitReached = errors.New("in-flight transaction limit reached for delegated accounts") - // ErrOutOfOrderTxFromDelegated is returned when the transaction with gapped // nonce received from the accounts with delegation or pending delegation. ErrOutOfOrderTxFromDelegated = errors.New("gapped-nonce tx from delegated accounts") @@ -241,8 +237,8 @@ type LegacyPool struct { currentHead atomic.Pointer[types.Header] // Current head of the blockchain currentState *state.StateDB // Current state in the blockchain head pendingNonces *noncer // Pending state tracking virtual nonces + reserver *txpool.Reserver // Address reserver to ensure exclusivity across subpools - reserve txpool.AddressReserver // Address reserver to ensure exclusivity across subpools pending map[common.Address]*list // All currently processable transactions queue map[common.Address]*list // Queued but non-processable transactions beats map[common.Address]time.Time // Last heartbeat from each known account @@ -306,9 +302,9 @@ func (pool *LegacyPool) Filter(tx *types.Transaction) bool { // Init sets the gas price needed to keep a transaction in the pool and the chain // head to allow balance / nonce checks. The internal // goroutines will be spun up and the pool deemed operational afterwards. -func (pool *LegacyPool) Init(gasTip uint64, head *types.Header, reserve txpool.AddressReserver) error { +func (pool *LegacyPool) Init(gasTip uint64, head *types.Header, reserver *txpool.Reserver) error { // Set the address reserver to request exclusive access to pooled accounts - pool.reserve = reserve + pool.reserver = reserver // Set the basic pool parameters pool.gasTip.Store(uint256.NewInt(gasTip)) @@ -618,7 +614,7 @@ func (pool *LegacyPool) checkDelegationLimit(tx *types.Transaction) error { from, _ := types.Sender(pool.signer, tx) // validated // Short circuit if the sender has neither delegation nor pending delegation. - if pool.currentState.GetCodeHash(from) == types.EmptyCodeHash && pool.all.delegationTxsCount(from) == 0 { + if pool.currentState.GetCodeHash(from) == types.EmptyCodeHash && !pool.all.hasAuth(from) { return nil } pending := pool.pending[from] @@ -633,7 +629,7 @@ func (pool *LegacyPool) checkDelegationLimit(tx *types.Transaction) error { if pending.Contains(tx.Nonce()) { return nil } - return ErrInflightTxLimitReached + return txpool.ErrInflightTxLimitReached } // validateAuth verifies that the transaction complies with code authorization @@ -644,12 +640,24 @@ func (pool *LegacyPool) validateAuth(tx *types.Transaction) error { if err := pool.checkDelegationLimit(tx); err != nil { return err } - // Authorities cannot conflict with any pending or queued transactions. + // Authorities must not conflict with any pending or queued transactions, + // nor with addresses that have already been reserved. if auths := tx.SetCodeAuthorities(); len(auths) > 0 { for _, auth := range auths { if pool.pending[auth] != nil || pool.queue[auth] != nil { return ErrAuthorityReserved } + // Because there is no exclusive lock held between different subpools + // when processing transactions, the SetCode transaction may be accepted + // while other transactions with the same sender address are also + // accepted simultaneously in the other pools. + // + // This scenario is considered acceptable, as the rule primarily ensures + // that attackers cannot easily stack a SetCode transaction when the sender + // is reserved by other pools. + if pool.reserver.Has(auth) { + return ErrAuthorityReserved + } } } return nil @@ -683,7 +691,7 @@ func (pool *LegacyPool) add(tx *types.Transaction) (replaced bool, err error) { _, hasQueued = pool.queue[from] ) if !hasPending && !hasQueued { - if err := pool.reserve(from, true); err != nil { + if err := pool.reserver.Hold(from); err != nil { return false, err } defer func() { @@ -694,7 +702,7 @@ func (pool *LegacyPool) add(tx *types.Transaction) (replaced bool, err error) { // by a return statement before running deferred methods. Take care with // removing or subscoping err as it will break this clause. if err != nil { - pool.reserve(from, false) + pool.reserver.Release(from) } }() } @@ -1087,7 +1095,7 @@ func (pool *LegacyPool) removeTx(hash common.Hash, outofbound bool, unreserve bo _, hasQueued = pool.queue[addr] ) if !hasPending && !hasQueued { - pool.reserve(addr, false) + pool.reserver.Release(addr) } }() } @@ -1467,7 +1475,7 @@ func (pool *LegacyPool) promoteExecutables(accounts []common.Address) []*types.T delete(pool.queue, addr) delete(pool.beats, addr) if _, ok := pool.pending[addr]; !ok { - pool.reserve(addr, false) + pool.reserver.Release(addr) } } } @@ -1653,7 +1661,7 @@ func (pool *LegacyPool) demoteUnexecutables() { if list.Empty() { delete(pool.pending, addr) if _, ok := pool.queue[addr]; !ok { - pool.reserve(addr, false) + pool.reserver.Release(addr) } } } @@ -1862,11 +1870,13 @@ func (t *lookup) removeAuthorities(tx *types.Transaction) { } } -// delegationTxsCount returns the number of pending authorizations for the specified address. -func (t *lookup) delegationTxsCount(addr common.Address) int { +// hasAuth returns a flag indicating whether there are pending authorizations +// from the specified address. +func (t *lookup) hasAuth(addr common.Address) bool { t.lock.RLock() defer t.lock.RUnlock() - return len(t.auths[addr]) + + return len(t.auths[addr]) > 0 } // numSlots calculates the number of slots needed for a single transaction. @@ -1880,8 +1890,8 @@ func (pool *LegacyPool) Clear() { pool.mu.Lock() defer pool.mu.Unlock() - // unreserve each tracked account. Ideally, we could just clear the - // reservation map in the parent txpool context. However, if we clear in + // unreserve each tracked account. Ideally, we could just clear the + // reservation map in the parent txpool context. However, if we clear in // parent context, to avoid exposing the subpool lock, we have to lock the // reservations and then lock each subpool. // @@ -1892,11 +1902,11 @@ func (pool *LegacyPool) Clear() { // * TxPool.Clear attempts to lock subpool mutex // // The transaction addition may attempt to reserve the sender addr which - // can't happen until Clear releases the reservation lock. Clear cannot + // can't happen until Clear releases the reservation lock. Clear cannot // acquire the subpool lock until the transaction addition is completed. for _, tx := range pool.all.txs { senderAddr, _ := types.Sender(pool.signer, tx) - pool.reserve(senderAddr, false) + pool.reserver.Release(senderAddr) } pool.all = newLookup() pool.priced = newPricedList(pool.all) @@ -1904,3 +1914,9 @@ func (pool *LegacyPool) Clear() { pool.queue = make(map[common.Address]*list) pool.pendingNonces = newNoncer(pool.currentState) } + +// HasPendingAuth returns a flag indicating whether there are pending +// authorizations from the specific address cached in the pool. +func (pool *LegacyPool) HasPendingAuth(addr common.Address) bool { + return pool.all.hasAuth(addr) +} diff --git a/core/txpool/legacypool/legacypool2_test.go b/core/txpool/legacypool/legacypool2_test.go index d55e85d74f7..3f210e3d1b9 100644 --- a/core/txpool/legacypool/legacypool2_test.go +++ b/core/txpool/legacypool/legacypool2_test.go @@ -86,7 +86,7 @@ func TestTransactionFutureAttack(t *testing.T) { config.GlobalQueue = 100 config.GlobalSlots = 100 pool := New(config, blockchain) - pool.Init(config.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(config.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() fillPool(t, pool) pending, _ := pool.Stats() @@ -120,7 +120,7 @@ func TestTransactionFuture1559(t *testing.T) { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) - pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Create a number of test accounts, fund them and make transactions @@ -153,7 +153,7 @@ func TestTransactionZAttack(t *testing.T) { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) - pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Create a number of test accounts, fund them and make transactions fillPool(t, pool) @@ -224,7 +224,7 @@ func BenchmarkFutureAttack(b *testing.B) { config.GlobalQueue = 100 config.GlobalSlots = 100 pool := New(config, blockchain) - pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() fillPool(b, pool) diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index 3f269bd69ec..c47a6552049 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -24,7 +24,6 @@ import ( "math/big" "math/rand" "slices" - "sync" "sync/atomic" "testing" "time" @@ -168,42 +167,21 @@ func pricedSetCodeTxWithAuth(nonce uint64, gaslimit uint64, gasFee, tip *uint256 }) } -func makeAddressReserver() txpool.AddressReserver { - var ( - reserved = make(map[common.Address]struct{}) - lock sync.Mutex - ) - return func(addr common.Address, reserve bool) error { - lock.Lock() - defer lock.Unlock() - - _, exists := reserved[addr] - if reserve { - if exists { - panic("already reserved") - } - reserved[addr] = struct{}{} - return nil - } - if !exists { - panic("not reserved") - } - delete(reserved, addr) - return nil - } -} - func setupPool() (*LegacyPool, *ecdsa.PrivateKey) { return setupPoolWithConfig(params.TestChainConfig) } +func newReserver() *txpool.Reserver { + return txpool.NewReservationTracker().NewHandle(42) +} + func setupPoolWithConfig(config *params.ChainConfig) (*LegacyPool, *ecdsa.PrivateKey) { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) blockchain := newTestBlockChain(config, 10000000, statedb, new(event.Feed)) key, _ := crypto.GenerateKey() pool := New(testTxPoolConfig, blockchain) - if err := pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()); err != nil { + if err := pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()); err != nil { panic(err) } // wait for the pool to initialize @@ -336,7 +314,7 @@ func TestStateChangeDuringReset(t *testing.T) { tx1 := transaction(1, 100000, key) pool := New(testTxPoolConfig, blockchain) - pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() nonce := pool.Nonce(address) @@ -753,7 +731,7 @@ func TestPostponing(t *testing.T) { blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) - pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Create two test accounts to produce different gap profiles with @@ -963,7 +941,7 @@ func TestQueueGlobalLimiting(t *testing.T) { config.GlobalQueue = config.AccountQueue*3 - 1 // reduce the queue limits to shorten test time (-1 to make it non divisible) pool := New(config, blockchain) - pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Create a number of test accounts and fund them (last one will be the local) @@ -1015,7 +993,7 @@ func TestQueueTimeLimiting(t *testing.T) { config.Lifetime = time.Second pool := New(config, blockchain) - pool.Init(config.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(config.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Create a test account to ensure remotes expire @@ -1176,7 +1154,7 @@ func TestPendingGlobalLimiting(t *testing.T) { config.GlobalSlots = config.AccountSlots * 10 pool := New(config, blockchain) - pool.Init(config.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(config.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Create a number of test accounts and fund them @@ -1275,7 +1253,7 @@ func TestCapClearsFromAll(t *testing.T) { config.GlobalSlots = 8 pool := New(config, blockchain) - pool.Init(config.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(config.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Create a number of test accounts and fund them @@ -1308,7 +1286,7 @@ func TestPendingMinimumAllowance(t *testing.T) { config.GlobalSlots = 1 pool := New(config, blockchain) - pool.Init(config.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(config.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Create a number of test accounts and fund them @@ -1352,7 +1330,7 @@ func TestRepricing(t *testing.T) { blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) - pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Keep track of transaction events to ensure all executables get announced @@ -1457,7 +1435,7 @@ func TestMinGasPriceEnforced(t *testing.T) { txPoolConfig := DefaultConfig txPoolConfig.NoLocals = true pool := New(txPoolConfig, blockchain) - pool.Init(txPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(txPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() key, _ := crypto.GenerateKey() @@ -1606,7 +1584,7 @@ func TestUnderpricing(t *testing.T) { config.GlobalQueue = 2 pool := New(config, blockchain) - pool.Init(config.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(config.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Keep track of transaction events to ensure all executables get announced @@ -1696,7 +1674,7 @@ func TestStableUnderpricing(t *testing.T) { config.GlobalQueue = 0 pool := New(config, blockchain) - pool.Init(config.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(config.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Keep track of transaction events to ensure all executables get announced @@ -1899,7 +1877,7 @@ func TestDeduplication(t *testing.T) { blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) - pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Create a test account to add transactions with @@ -1966,7 +1944,7 @@ func TestReplacement(t *testing.T) { blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) - pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Keep track of transaction events to ensure all executables get announced @@ -2157,7 +2135,7 @@ func TestStatusCheck(t *testing.T) { blockchain := newTestBlockChain(params.TestChainConfig, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) - pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Create the test accounts to check various transaction statuses with @@ -2230,7 +2208,7 @@ func TestSetCodeTransactions(t *testing.T) { blockchain := newTestBlockChain(params.MergedTestChainConfig, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) - pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Create the test accounts @@ -2271,12 +2249,12 @@ func TestSetCodeTransactions(t *testing.T) { if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyA)); err != nil { t.Fatalf("%s: failed to add remote transaction: %v", name, err) } - if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) { - t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err) + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, txpool.ErrInflightTxLimitReached) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrInflightTxLimitReached, err) } // Check gapped transaction again. - if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) { - t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err) + if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, txpool.ErrInflightTxLimitReached) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrInflightTxLimitReached, err) } // Replace by fee. if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(10), keyA)); err != nil { @@ -2310,8 +2288,8 @@ func TestSetCodeTransactions(t *testing.T) { t.Fatalf("%s: failed to add with pending delegatio: %v", name, err) } // Also check gapped transaction is rejected. - if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyC)); !errors.Is(err, ErrInflightTxLimitReached) { - t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err) + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyC)); !errors.Is(err, txpool.ErrInflightTxLimitReached) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrInflightTxLimitReached, err) } }, }, @@ -2386,7 +2364,7 @@ func TestSetCodeTransactions(t *testing.T) { if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil { t.Fatalf("%s: failed to added single pooled for account with pending delegation: %v", name, err) } - if err, want := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1000), keyC)), ErrInflightTxLimitReached; !errors.Is(err, want) { + if err, want := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1000), keyC)), txpool.ErrInflightTxLimitReached; !errors.Is(err, want) { t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err) } }, @@ -2454,7 +2432,7 @@ func TestSetCodeTransactionsReorg(t *testing.T) { blockchain := newTestBlockChain(params.MergedTestChainConfig, 1000000, statedb, new(event.Feed)) pool := New(testTxPoolConfig, blockchain) - pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() // Create the test accounts @@ -2489,8 +2467,8 @@ func TestSetCodeTransactionsReorg(t *testing.T) { t.Fatalf("failed to add with remote setcode transaction: %v", err) } // Try to add a transactions in - if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1000), keyA)); !errors.Is(err, ErrInflightTxLimitReached) { - t.Fatalf("unexpected error %v, expecting %v", err, ErrInflightTxLimitReached) + if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1000), keyA)); !errors.Is(err, txpool.ErrInflightTxLimitReached) { + t.Fatalf("unexpected error %v, expecting %v", err, txpool.ErrInflightTxLimitReached) } // Simulate the chain moving blockchain.statedb.SetNonce(addrA, 2, tracing.NonceChangeAuthorization) diff --git a/core/txpool/reserver.go b/core/txpool/reserver.go new file mode 100644 index 00000000000..76ead0f3bb0 --- /dev/null +++ b/core/txpool/reserver.go @@ -0,0 +1,124 @@ +// 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 txpool + +import ( + "errors" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" +) + +var ( + // reservationsGaugeName is the prefix of a per-subpool address reservation + // metric. + // + // This is mostly a sanity metric to ensure there's no bug that would make + // some subpool hog all the reservations due to mis-accounting. + reservationsGaugeName = "txpool/reservations" +) + +// ReservationTracker is a struct shared between different subpools. It is used to reserve +// the account and ensure that one address cannot initiate transactions, authorizations, +// and other state-changing behaviors in different pools at the same time. +type ReservationTracker struct { + accounts map[common.Address]int + lock sync.RWMutex +} + +// NewReservationTracker initializes the account reservation tracker. +func NewReservationTracker() *ReservationTracker { + return &ReservationTracker{ + accounts: make(map[common.Address]int), + } +} + +// NewHandle creates a named handle on the ReservationTracker. The handle +// identifies the subpool so ownership of reservations can be determined. +func (r *ReservationTracker) NewHandle(id int) *Reserver { + return &Reserver{r, id} +} + +// Reserver is a named handle on ReservationTracker. It is held by subpools to +// make reservations for accounts it is tracking. The id is used to determine +// which pool owns an address and disallows non-owners to hold or release +// addresses it doesn't own. +type Reserver struct { + tracker *ReservationTracker + id int +} + +// Hold attempts to reserve the specified account address for the given pool. +// Returns an error if the account is already reserved. +func (h *Reserver) Hold(addr common.Address) error { + h.tracker.lock.Lock() + defer h.tracker.lock.Unlock() + + // Double reservations are forbidden even from the same pool to + // avoid subtle bugs in the long term. + owner, exists := h.tracker.accounts[addr] + if exists { + if owner == h.id { + log.Error("pool attempted to reserve already-owned address", "address", addr) + return nil // Ignore fault to give the pool a chance to recover while the bug gets fixed + } + return ErrAlreadyReserved + } + h.tracker.accounts[addr] = h.id + if metrics.Enabled() { + m := fmt.Sprintf("%s/%d", reservationsGaugeName, h.id) + metrics.GetOrRegisterGauge(m, nil).Inc(1) + } + return nil +} + +// Release attempts to release the reservation for the specified account. +// Returns an error if the address is not reserved or is reserved by another pool. +func (h *Reserver) Release(addr common.Address) error { + h.tracker.lock.Lock() + defer h.tracker.lock.Unlock() + + // Ensure subpools only attempt to unreserve their own owned addresses, + // otherwise flag as a programming error. + owner, exists := h.tracker.accounts[addr] + if !exists { + log.Error("pool attempted to unreserve non-reserved address", "address", addr) + return errors.New("address not reserved") + } + if owner != h.id { + log.Error("pool attempted to unreserve non-owned address", "address", addr) + return errors.New("address not owned") + } + delete(h.tracker.accounts, addr) + if metrics.Enabled() { + m := fmt.Sprintf("%s/%d", reservationsGaugeName, h.id) + metrics.GetOrRegisterGauge(m, nil).Dec(1) + } + return nil +} + +// Has returns a flag indicating if the address has been reserved or not. +func (h *Reserver) Has(address common.Address) bool { + h.tracker.lock.RLock() + defer h.tracker.lock.RUnlock() + + _, exists := h.tracker.accounts[address] + return exists +} diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index f5cb852d8ff..2cb51038754 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -67,10 +67,6 @@ type LazyResolver interface { Get(hash common.Hash) *types.Transaction } -// AddressReserver is passed by the main transaction pool to subpools, so they -// may request (and relinquish) exclusive access to certain addresses. -type AddressReserver func(addr common.Address, reserve bool) error - // PendingFilter is a collection of filter rules to allow retrieving a subset // of transactions for announcement or mining. // @@ -109,7 +105,7 @@ type SubPool interface { // These should not be passed as a constructor argument - nor should the pools // start by themselves - in order to keep multiple subpools in lockstep with // one another. - Init(gasTip uint64, head *types.Header, reserve AddressReserver) error + Init(gasTip uint64, head *types.Header, reserver *Reserver) error // Close terminates any background processing threads and releases any held // resources. diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 083aac92c66..47d83e03d4b 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -29,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" ) @@ -43,15 +42,6 @@ const ( TxStatusIncluded ) -var ( - // reservationsGaugeName is the prefix of a per-subpool address reservation - // metric. - // - // This is mostly a sanity metric to ensure there's no bug that would make - // some subpool hog all the reservations due to mis-accounting. - reservationsGaugeName = "txpool/reservations" -) - // BlockChain defines the minimal set of methods needed to back a tx pool with // a chain. Exists to allow mocking the live chain out of tests. type BlockChain interface { @@ -81,9 +71,6 @@ type TxPool struct { stateLock sync.RWMutex // The lock for protecting state instance state *state.StateDB // Current state at the blockchain head - reservations map[common.Address]SubPool // Map with the account to pool reservations - reserveLock sync.Mutex // Lock protecting the account reservations - subs event.SubscriptionScope // Subscription scope to unsubscribe all on shutdown quit chan chan error // Quit channel to tear down the head updater term chan struct{} // Termination channel to detect a closed pool @@ -110,17 +97,17 @@ func New(gasTip uint64, chain BlockChain, subpools []SubPool) (*TxPool, error) { return nil, err } pool := &TxPool{ - subpools: subpools, - chain: chain, - signer: types.LatestSigner(chain.Config()), - state: statedb, - reservations: make(map[common.Address]SubPool), - quit: make(chan chan error), - term: make(chan struct{}), - sync: make(chan chan error), + subpools: subpools, + chain: chain, + signer: types.LatestSigner(chain.Config()), + state: statedb, + quit: make(chan chan error), + term: make(chan struct{}), + sync: make(chan chan error), } + reserver := NewReservationTracker() for i, subpool := range subpools { - if err := subpool.Init(gasTip, head, pool.reserver(i, subpool)); err != nil { + if err := subpool.Init(gasTip, head, reserver.NewHandle(i)); err != nil { for j := i - 1; j >= 0; j-- { subpools[j].Close() } @@ -131,52 +118,6 @@ func New(gasTip uint64, chain BlockChain, subpools []SubPool) (*TxPool, error) { return pool, nil } -// reserver is a method to create an address reservation callback to exclusively -// assign/deassign addresses to/from subpools. This can ensure that at any point -// in time, only a single subpool is able to manage an account, avoiding cross -// subpool eviction issues and nonce conflicts. -func (p *TxPool) reserver(id int, subpool SubPool) AddressReserver { - return func(addr common.Address, reserve bool) error { - p.reserveLock.Lock() - defer p.reserveLock.Unlock() - - owner, exists := p.reservations[addr] - if reserve { - // Double reservations are forbidden even from the same pool to - // avoid subtle bugs in the long term. - if exists { - if owner == subpool { - log.Error("pool attempted to reserve already-owned address", "address", addr) - return nil // Ignore fault to give the pool a chance to recover while the bug gets fixed - } - return ErrAlreadyReserved - } - p.reservations[addr] = subpool - if metrics.Enabled() { - m := fmt.Sprintf("%s/%d", reservationsGaugeName, id) - metrics.GetOrRegisterGauge(m, nil).Inc(1) - } - return nil - } - // Ensure subpools only attempt to unreserve their own owned addresses, - // otherwise flag as a programming error. - if !exists { - log.Error("pool attempted to unreserve non-reserved address", "address", addr) - return errors.New("address not reserved") - } - if subpool != owner { - log.Error("pool attempted to unreserve non-owned address", "address", addr) - return errors.New("address not owned") - } - delete(p.reservations, addr) - if metrics.Enabled() { - m := fmt.Sprintf("%s/%d", reservationsGaugeName, id) - metrics.GetOrRegisterGauge(m, nil).Dec(1) - } - return nil - } -} - // Close terminates the transaction pool and all its subpools. func (p *TxPool) Close() error { var errs []error diff --git a/eth/backend.go b/eth/backend.go index 79759710b61..6716a77562b 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -260,16 +260,16 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { eth.filterMaps = filtermaps.NewFilterMaps(chainDb, chainView, historyCutoff, finalBlock, filtermaps.DefaultParams, fmConfig) eth.closeFilterMaps = make(chan chan struct{}) - if config.BlobPool.Datadir != "" { - config.BlobPool.Datadir = stack.ResolvePath(config.BlobPool.Datadir) - } - blobPool := blobpool.New(config.BlobPool, eth.blockchain) - if config.TxPool.Journal != "" { config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal) } legacyPool := legacypool.New(config.TxPool, eth.blockchain) + if config.BlobPool.Datadir != "" { + config.BlobPool.Datadir = stack.ResolvePath(config.BlobPool.Datadir) + } + blobPool := blobpool.New(config.BlobPool, eth.blockchain, legacyPool.HasPendingAuth) + eth.txPool, err = txpool.New(config.TxPool.PriceLimit, eth.blockchain, []txpool.SubPool{legacyPool, blobPool}) if err != nil { return nil, err diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index bbbd888701d..fa031d98997 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -135,7 +135,7 @@ func newTestBackendWithGenerator(blocks int, shanghai bool, cancun bool, generat storage, _ := os.MkdirTemp("", "blobpool-") defer os.RemoveAll(storage) - blobPool := blobpool.New(blobpool.Config{Datadir: storage}, chain) + blobPool := blobpool.New(blobpool.Config{Datadir: storage}, chain, nil) legacyPool := legacypool.New(txconfig, chain) txpool, _ := txpool.New(txconfig.PriceLimit, chain, []txpool.SubPool{legacyPool, blobPool}) From 5cc9137c9cec8a977b845a025d3deced9457fd48 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 8 Apr 2025 19:57:45 +0200 Subject: [PATCH 083/658] core/vm: optimize push2 opcode (#31267) During my benchmarks on Holesky, around 10% of all CPU time was spent in PUSH2 ``` ROUTINE ======================== github.com/ethereum/go-ethereum/core/vm.newFrontierInstructionSet.makePush.func1 in github.com/ethereum/go-ethereum/core/vm/instructions.go 16.38s 20.35s (flat, cum) 10.31% of Total 740ms 740ms 976: return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { . . 977: var ( 40ms 40ms 978: codeLen = len(scope.Contract.Code) 970ms 970ms 979: start = min(codeLen, int(*pc+1)) 200ms 200ms 980: end = min(codeLen, start+pushByteSize) . . 981: ) 670ms 2.39s 982: a := new(uint256.Int).SetBytes(scope.Contract.Code[start:end]) . . 983: . . 984: // Missing bytes: pushByteSize - len(pushData) 410ms 410ms 985: if missing := pushByteSize - (end - start); missing > 0 { . . 986: a.Lsh(a, uint(8*missing)) . . 987: } 12.69s 14.94s 988: scope.Stack.push2(*a) 10ms 10ms 989: *pc += size 650ms 650ms 990: return nil, nil . . 991: } . . 992:} ``` Which is quite crazy. We have a handwritten encoder for PUSH1 already, this PR adds one for PUSH2. PUSH2 is the second most used opcode as shown here: https://gist.github.com/shemnon/fb9b292a103abb02d98d64df6fbd35c8 since it is used by solidity quite significantly. Its used ~20 times as much as PUSH20 and PUSH32. # Benchmarks ``` BenchmarkPush/makePush-14 94196547 12.27 ns/op 0 B/op 0 allocs/op BenchmarkPush/push-14 429976924 2.829 ns/op 0 B/op 0 allocs/op ``` --------- Co-authored-by: jwasinger --- core/vm/instructions.go | 17 +++++++++++++++++ core/vm/jump_table.go | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 1785ffc1397..0b3b1d15690 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -971,6 +971,23 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by return nil, nil } +// opPush2 is a specialized version of pushN +func opPush2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + codeLen = uint64(len(scope.Contract.Code)) + integer = new(uint256.Int) + ) + if *pc+2 < codeLen { + scope.Stack.push(integer.SetBytes2(scope.Contract.Code[*pc+1 : *pc+3])) + } else if *pc+1 < codeLen { + scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc+1]) << 8)) + } else { + scope.Stack.push(integer.Clear()) + } + *pc += 2 + return nil, nil +} + // make push instruction function func makePush(size uint64, pushByteSize int) executionFunc { return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 6610fa7f9ab..ee811b447e8 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -631,7 +631,7 @@ func newFrontierInstructionSet() JumpTable { maxStack: maxStack(0, 1), }, PUSH2: { - execute: makePush(2, 2), + execute: opPush2, constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), From a7f24c26c0a6a73357d76adf64d98f343bafd0e5 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 9 Apr 2025 11:28:29 +0200 Subject: [PATCH 084/658] p2p/nat: fix UPnP port reset (#31566) Make UPnP more robust - Once a random port was mapped, we try to stick to it even if a UPnP refresh fails. Previously we were immediately moving back to try the default port, leading to frequent ENR changes. - We were deleting port mappings before refresh as a possible workaround. This created issues in some UPnP servers. The UPnP (and PMP) specification is explicit about the refresh requirements, and delete is clearly not needed (see https://github.com/ethereum/go-ethereum/pull/30265#issuecomment-2766987859). From now on we only delete when closing. - We were trying to add port mappings only once, and then moved on to random ports. Now we insist a bit more, so that a simple failed request won't lead to ENR changes. Fixes https://github.com/ethereum/go-ethereum/issues/31418 --------- Signed-off-by: Csaba Kiraly Co-authored-by: Felix Lange --- p2p/nat/natupnp.go | 49 +++++++++++++++++++++++++-------------- p2p/server_nat.go | 57 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 72 insertions(+), 34 deletions(-) diff --git a/p2p/nat/natupnp.go b/p2p/nat/natupnp.go index ed00b8eeb67..d79677db551 100644 --- a/p2p/nat/natupnp.go +++ b/p2p/nat/natupnp.go @@ -26,6 +26,7 @@ import ( "sync" "time" + "github.com/ethereum/go-ethereum/log" "github.com/huin/goupnp" "github.com/huin/goupnp/dcps/internetgateway1" "github.com/huin/goupnp/dcps/internetgateway2" @@ -34,6 +35,8 @@ import ( const ( soapRequestTimeout = 3 * time.Second rateLimit = 200 * time.Millisecond + retryCount = 3 // number of retries after a failed AddPortMapping + randomCount = 3 // number of random ports to try ) type upnp struct { @@ -89,42 +92,43 @@ func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, li if extport == 0 { extport = intport - } else { - // Only delete port mapping if the external port was already used by geth. - n.DeleteMapping(protocol, extport, intport) } // Try to add port mapping, preferring the specified external port. - err = n.withRateLimit(func() error { - p, err := n.addAnyPortMapping(protocol, extport, intport, ip, desc, lifetimeS) - if err == nil { - extport = int(p) - } - return err - }) - return uint16(extport), err + return n.addAnyPortMapping(protocol, extport, intport, ip, desc, lifetimeS) } // addAnyPortMapping tries to add a port mapping with the specified external port. // If the external port is already in use, it will try to assign another port. func (n *upnp) addAnyPortMapping(protocol string, extport, intport int, ip net.IP, desc string, lifetimeS uint32) (uint16, error) { if client, ok := n.client.(*internetgateway2.WANIPConnection2); ok { - return client.AddAnyPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) + return n.portWithRateLimit(func() (uint16, error) { + return client.AddAnyPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) + }) } // For IGDv1 and v1 services we should first try to add with extport. - err := n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) - if err == nil { - return uint16(extport), nil + for i := 0; i < retryCount+1; i++ { + err := n.withRateLimit(func() error { + return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) + }) + if err == nil { + return uint16(extport), nil + } + log.Debug("Failed to add port mapping", "protocol", protocol, "extport", extport, "intport", intport, "err", err) } // If above fails, we retry with a random port. // We retry several times because of possible port conflicts. - for i := 0; i < 3; i++ { + var err error + for i := 0; i < randomCount; i++ { extport = n.randomPort() - err := n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) + err := n.withRateLimit(func() error { + return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) + }) if err == nil { return uint16(extport), nil } + log.Debug("Failed to add random port mapping", "protocol", protocol, "extport", extport, "intport", intport, "err", err) } return 0, err } @@ -169,6 +173,17 @@ func (n *upnp) String() string { return "UPNP " + n.service } +func (n *upnp) portWithRateLimit(pfn func() (uint16, error)) (uint16, error) { + var port uint16 + var err error + fn := func() error { + port, err = pfn() + return err + } + n.withRateLimit(fn) + return port, err +} + func (n *upnp) withRateLimit(fn func() error) error { n.mu.Lock() defer n.mu.Unlock() diff --git a/p2p/server_nat.go b/p2p/server_nat.go index 5830f950e1b..298c454a4a0 100644 --- a/p2p/server_nat.go +++ b/p2p/server_nat.go @@ -31,12 +31,14 @@ const ( portMapRefreshInterval = 8 * time.Minute portMapRetryInterval = 5 * time.Minute extipRetryInterval = 2 * time.Minute + maxRetries = 5 // max number of failed attempts to refresh the mapping ) type portMapping struct { protocol string name string port int + retries int // number of failed attempts to refresh the mapping // for use by the portMappingLoop goroutine: extPort int // the mapped port returned by the NAT interface @@ -154,28 +156,49 @@ func (srv *Server) portMappingLoop() { log.Trace("Attempting port mapping") p, err := srv.NAT.AddMapping(m.protocol, m.extPort, m.port, m.name, portMapDuration) if err != nil { - log.Debug("Couldn't add port mapping", "err", err) - m.extPort = 0 + // Failed to add or refresh port mapping. + if m.extPort == 0 { + log.Debug("Couldn't add port mapping", "err", err) + } else { + // Failed refresh. Since UPnP implementation are often buggy, + // and lifetime is larger than the retry interval, this does not + // mean we lost our existing mapping. We do not reset the external + // port, as it is still our best chance, but we do retry soon. + // We could check the error code, but UPnP implementations are buggy. + log.Debug("Couldn't refresh port mapping", "err", err) + m.retries++ + if m.retries > maxRetries { + m.retries = 0 + err := srv.NAT.DeleteMapping(m.protocol, m.extPort, m.port) + log.Debug("Couldn't refresh port mapping, trying to delete it:", "err", err) + m.extPort = 0 + } + } m.nextTime = srv.clock.Now().Add(portMapRetryInterval) + // Note ENR is not updated here, i.e. we keep the last port. continue } - // It was mapped! - m.extPort = int(p) - m.nextTime = srv.clock.Now().Add(portMapRefreshInterval) - log = newLogger(m.protocol, m.extPort, m.port) - if m.port != m.extPort { - log.Info("NAT mapped alternative port") - } else { - log.Info("NAT mapped port") - } - // Update port in local ENR. - switch m.protocol { - case "TCP": - srv.localnode.Set(enr.TCP(m.extPort)) - case "UDP": - srv.localnode.SetFallbackUDP(m.extPort) + // It was mapped! + m.retries = 0 + log = newLogger(m.protocol, int(p), m.port) + if int(p) != m.extPort { + m.extPort = int(p) + if m.port != m.extPort { + log.Info("NAT mapped alternative port") + } else { + log.Info("NAT mapped port") + } + + // Update port in local ENR. + switch m.protocol { + case "TCP": + srv.localnode.Set(enr.TCP(m.extPort)) + case "UDP": + srv.localnode.SetFallbackUDP(m.extPort) + } } + m.nextTime = srv.clock.Now().Add(portMapRefreshInterval) } } } From 60b922fd529237ac5070a1750c93a4c132ed32fb Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:19:28 -0600 Subject: [PATCH 085/658] core/txpool: add notice to` Clear` that is not meant for production code (#31567) The `Sync(..)` function explicitly says not to rely on in production code, but it is used in `Clear(..)` so should add a similar mention. --- core/txpool/blobpool/blobpool.go | 6 ++++++ core/txpool/legacypool/legacypool.go | 7 +++++-- core/txpool/txpool.go | 11 +++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index b1966905a8f..1770066a8d8 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1349,6 +1349,9 @@ func (p *BlobPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844. // Add inserts a set of blob transactions into the pool if they pass validation (both // consensus validity and pool restrictions). +// +// Note, if sync is set the method will block until all internal maintenance +// related to the add is finished. Only use this during tests for determinism. func (p *BlobPool) Add(txs []*types.Transaction, sync bool) []error { var ( adds = make([]*types.Transaction, 0, len(txs)) @@ -1792,6 +1795,9 @@ func (p *BlobPool) Status(hash common.Hash) txpool.TxStatus { // Clear implements txpool.SubPool, removing all tracked transactions // from the blob pool and persistent store. +// +// Note, do not use this in production / live code. In live code, the pool is +// meant to reset on a separate thread to avoid DoS vectors. func (p *BlobPool) Clear() { p.lock.Lock() defer p.lock.Unlock() diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 278ad0791f0..75dc4a8461f 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -927,8 +927,8 @@ func (pool *LegacyPool) addRemoteSync(tx *types.Transaction) error { // Add enqueues a batch of transactions into the pool if they are valid. // -// If sync is set, the method will block until all internal maintenance related -// to the add is finished. Only use this during tests for determinism! +// Note, if sync is set the method will block until all internal maintenance +// related to the add is finished. Only use this during tests for determinism. func (pool *LegacyPool) Add(txs []*types.Transaction, sync bool) []error { // Filter out known ones without obtaining the pool lock or recovering signatures var ( @@ -1886,6 +1886,9 @@ func numSlots(tx *types.Transaction) int { // Clear implements txpool.SubPool, removing all tracked txs from the pool // and rotating the journal. +// +// Note, do not use this in production / live code. In live code, the pool is +// meant to reset on a separate thread to avoid DoS vectors. func (pool *LegacyPool) Clear() { pool.mu.Lock() defer pool.mu.Unlock() diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 47d83e03d4b..2ed38772ce8 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -350,6 +350,9 @@ func (p *TxPool) ValidateTxBasics(tx *types.Transaction) error { // Add enqueues a batch of transactions into the pool if they are valid. Due // to the large transaction churn, add may postpone fully integrating the tx // to a later point to batch multiple ones together. +// +// Note, if sync is set the method will block until all internal maintenance +// related to the add is finished. Only use this during tests for determinism. func (p *TxPool) Add(txs []*types.Transaction, sync bool) []error { // Split the input transactions between the subpools. It shouldn't really // happen that we receive merged batches, but better graceful than strange @@ -503,8 +506,8 @@ func (p *TxPool) Status(hash common.Hash) TxStatus { // internal background reset operations. This method will run an explicit reset // operation to ensure the pool stabilises, thus avoiding flakey behavior. // -// Note, do not use this in production / live code. In live code, the pool is -// meant to reset on a separate thread to avoid DoS vectors. +// Note, this method is only used for testing and is susceptible to DoS vectors. +// In production code, the pool is meant to reset on a separate thread. func (p *TxPool) Sync() error { sync := make(chan error) select { @@ -516,6 +519,10 @@ func (p *TxPool) Sync() error { } // Clear removes all tracked txs from the subpools. +// +// Note, this method invokes Sync() and is only used for testing, because it is +// susceptible to DoS vectors. In production code, the pool is meant to reset on +// a separate thread. func (p *TxPool) Clear() { // Invoke Sync to ensure that txs pending addition don't get added to the pool after // the subpools are subsequently cleared From 0c2ad076734e87fcc008e4718ba7b1f4f881b1fa Mon Sep 17 00:00:00 2001 From: Luis Ayuso Date: Thu, 10 Apr 2025 03:11:24 +0200 Subject: [PATCH 086/658] core/txpool: allow tx and authority regardless of admission order (#31373) This PR proposes a change to the authorizations' validation introduced in commit cdb66c8. These changes make the expected behavior independent of the order of admission of authorizations, improving the predictability of the resulting state and the usability of the system with it. The current implementation behavior is dependent on the transaction submission order: This issue is related to authorities and the sender of a transaction, and can be reproduced respecting the normal nonce rules. The issue can be reproduced by the two following cases: **First case** - Given an empty pool. - Submit transaction `{ from: B, auths [ A ] }`: is accepted. - Submit transaction `{ from: A }`: Is accepted: it becomes the one in-flight transaction allowed. **Second case** - Given an empty pool. - Submit transaction `{ from: A }`: is accepted - Submit transaction `{ from: B, auths [ A ] }`: is rejected since there is already a queued/pending transaction from A. The expected behavior is that both sequences of events would lead to the same sets of accepted and rejected transactions. **Proposed changes** The queued/pending transactions issued from any authority of the transaction being validated have to be counted, allowing one transaction from accounts submitting an authorization. - Notice that the expected behavior was explicitly forbidden in the case "reject-delegation-from-pending-account", I believe that this behavior conflicts to the definition of the limitation, and it is removed in this PR. The expected behavior is tested in "accept-authorization-from-sender-of-one-inflight-tx". - Replacement tests have been separated to improve readability of the acceptance test. - The test "allow-more-than-one-tx-from-replaced-authority" has been extended with one extra transaction, since the system would always have accepted one transaction (but not two). - The test "accept-one-inflight-tx-of-delegated-account" is extended to clean-up state, avoiding leaking the delegation used into the other tests. Additionally, replacement check is removed to be tested in its own test case. **Expected behavior** The expected behavior of the authorizations' validation shall be as follows: ![image](https://github.com/user-attachments/assets/dbde7a1f-9679-4691-94eb-c197a0cbb823) Notice that replacement shall be allowed, and behavior shall remain coherent with the table, according to the replaced transaction. --------- Co-authored-by: lightclient --- core/txpool/blobpool/blobpool.go | 4 +- core/txpool/blobpool/blobpool_test.go | 43 ++++- core/txpool/legacypool/legacypool.go | 28 +++- core/txpool/legacypool/legacypool_test.go | 190 +++++++++++++++++----- core/txpool/reserver.go | 42 +++-- core/txpool/subpool.go | 2 +- 6 files changed, 238 insertions(+), 71 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 1770066a8d8..12a4133b40c 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -299,7 +299,7 @@ func newBlobTxMeta(id uint64, size uint64, storageSize uint32, tx *types.Transac // and leading up to the first no-change. type BlobPool struct { config Config // Pool configuration - reserver *txpool.Reserver // Address reserver to ensure exclusivity across subpools + reserver txpool.Reserver // Address reserver to ensure exclusivity across subpools hasPendingAuth func(common.Address) bool // Determine whether the specified address has a pending 7702-auth store billy.Database // Persistent data store for the tx metadata and blobs @@ -355,7 +355,7 @@ func (p *BlobPool) Filter(tx *types.Transaction) bool { // Init sets the gas price needed to keep a transaction in the pool and the chain // head to allow balance / nonce checks. The transaction journal will be loaded // from disk and filtered based on the provided starting settings. -func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver *txpool.Reserver) error { +func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reserver) error { p.reserver = reserver var ( diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 4dfba3b52b1..76d21a0c9e0 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -26,6 +26,7 @@ import ( "math/big" "os" "path/filepath" + "sync" "testing" "github.com/ethereum/go-ethereum/common" @@ -167,6 +168,44 @@ func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { return bc.statedb, nil } +// reserver is a utility struct to sanity check that accounts are +// properly reserved by the blobpool (no duplicate reserves or unreserves). +type reserver struct { + accounts map[common.Address]struct{} + lock sync.RWMutex +} + +func newReserver() txpool.Reserver { + return &reserver{accounts: make(map[common.Address]struct{})} +} + +func (r *reserver) Hold(addr common.Address) error { + r.lock.Lock() + defer r.lock.Unlock() + if _, exists := r.accounts[addr]; exists { + panic("already reserved") + } + r.accounts[addr] = struct{}{} + return nil +} + +func (r *reserver) Release(addr common.Address) error { + r.lock.Lock() + defer r.lock.Unlock() + if _, exists := r.accounts[addr]; !exists { + panic("not reserved") + } + delete(r.accounts, addr) + return nil +} + +func (r *reserver) Has(address common.Address) bool { + r.lock.RLock() + defer r.lock.RUnlock() + _, exists := r.accounts[address] + return exists +} + // makeTx is a utility method to construct a random blob transaction and sign it // with a valid key, only setting the interesting fields from the perspective of // the blob pool. @@ -405,10 +444,6 @@ func verifyBlobRetrievals(t *testing.T, pool *BlobPool) { } } -func newReserver() *txpool.Reserver { - return txpool.NewReservationTracker().NewHandle(42) -} - // Tests that transactions can be loaded from disk on startup and that they are // correctly discarded if invalid. // diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 75dc4a8461f..04f1a2234c4 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -237,7 +237,7 @@ type LegacyPool struct { currentHead atomic.Pointer[types.Header] // Current head of the blockchain currentState *state.StateDB // Current state in the blockchain head pendingNonces *noncer // Pending state tracking virtual nonces - reserver *txpool.Reserver // Address reserver to ensure exclusivity across subpools + reserver txpool.Reserver // Address reserver to ensure exclusivity across subpools pending map[common.Address]*list // All currently processable transactions queue map[common.Address]*list // Queued but non-processable transactions @@ -302,7 +302,7 @@ func (pool *LegacyPool) Filter(tx *types.Transaction) bool { // Init sets the gas price needed to keep a transaction in the pool and the chain // head to allow balance / nonce checks. The internal // goroutines will be spun up and the pool deemed operational afterwards. -func (pool *LegacyPool) Init(gasTip uint64, head *types.Header, reserver *txpool.Reserver) error { +func (pool *LegacyPool) Init(gasTip uint64, head *types.Header, reserver txpool.Reserver) error { // Set the address reserver to request exclusive access to pooled accounts pool.reserver = reserver @@ -640,11 +640,18 @@ func (pool *LegacyPool) validateAuth(tx *types.Transaction) error { if err := pool.checkDelegationLimit(tx); err != nil { return err } - // Authorities must not conflict with any pending or queued transactions, - // nor with addresses that have already been reserved. + // For symmetry, allow at most one in-flight tx for any authority with a + // pending transaction. if auths := tx.SetCodeAuthorities(); len(auths) > 0 { for _, auth := range auths { - if pool.pending[auth] != nil || pool.queue[auth] != nil { + var count int + if pending := pool.pending[auth]; pending != nil { + count += pending.Len() + } + if queue := pool.queue[auth]; queue != nil { + count += queue.Len() + } + if count > 1 { return ErrAuthorityReserved } // Because there is no exclusive lock held between different subpools @@ -1907,9 +1914,14 @@ func (pool *LegacyPool) Clear() { // The transaction addition may attempt to reserve the sender addr which // can't happen until Clear releases the reservation lock. Clear cannot // acquire the subpool lock until the transaction addition is completed. - for _, tx := range pool.all.txs { - senderAddr, _ := types.Sender(pool.signer, tx) - pool.reserver.Release(senderAddr) + + for addr := range pool.pending { + if _, ok := pool.queue[addr]; !ok { + pool.reserver.Release(addr) + } + } + for addr := range pool.queue { + pool.reserver.Release(addr) } pool.all = newLookup() pool.priced = newPricedList(pool.all) diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index c47a6552049..bb1323a7d1f 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -24,6 +24,7 @@ import ( "math/big" "math/rand" "slices" + "sync" "sync/atomic" "testing" "time" @@ -171,8 +172,39 @@ func setupPool() (*LegacyPool, *ecdsa.PrivateKey) { return setupPoolWithConfig(params.TestChainConfig) } -func newReserver() *txpool.Reserver { - return txpool.NewReservationTracker().NewHandle(42) +// reserver is a utility struct to sanity check that accounts are +// properly reserved by the blobpool (no duplicate reserves or unreserves). +type reserver struct { + accounts map[common.Address]struct{} + lock sync.RWMutex +} + +func newReserver() txpool.Reserver { + return &reserver{accounts: make(map[common.Address]struct{})} +} + +func (r *reserver) Hold(addr common.Address) error { + r.lock.Lock() + defer r.lock.Unlock() + if _, exists := r.accounts[addr]; exists { + panic("already reserved") + } + r.accounts[addr] = struct{}{} + return nil +} + +func (r *reserver) Release(addr common.Address) error { + r.lock.Lock() + defer r.lock.Unlock() + if _, exists := r.accounts[addr]; !exists { + panic("not reserved") + } + delete(r.accounts, addr) + return nil +} + +func (r *reserver) Has(address common.Address) bool { + return false // reserver only supports a single pool } func setupPoolWithConfig(config *params.ChainConfig) (*LegacyPool, *ecdsa.PrivateKey) { @@ -2232,9 +2264,8 @@ func TestSetCodeTransactions(t *testing.T) { }{ { // Check that only one in-flight transaction is allowed for accounts - // with delegation set. Also verify the accepted transaction can be - // replaced by fee. - name: "only-one-in-flight", + // with delegation set. + name: "accept-one-inflight-tx-of-delegated-account", pending: 1, run: func(name string) { aa := common.Address{0xaa, 0xaa} @@ -2249,6 +2280,7 @@ func TestSetCodeTransactions(t *testing.T) { if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyA)); err != nil { t.Fatalf("%s: failed to add remote transaction: %v", name, err) } + // Second and further transactions shall be rejected if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, txpool.ErrInflightTxLimitReached) { t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrInflightTxLimitReached, err) } @@ -2260,44 +2292,90 @@ func TestSetCodeTransactions(t *testing.T) { if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(10), keyA)); err != nil { t.Fatalf("%s: failed to replace with remote transaction: %v", name, err) } + + // Reset the delegation, avoid leaking state into the other tests + statedb.SetCode(addrA, nil) }, }, { - name: "allow-setcode-tx-with-pending-authority-tx", + // This test is analogous to the previous one, but the delegation is pending + // instead of set. + name: "allow-one-tx-from-pooled-delegation", pending: 2, run: func(name string) { - // Send two transactions where the first has no conflicting delegations and - // the second should be allowed despite conflicting with the authorities in 1). - if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})); err != nil { + // Create a pending delegation request from B. + if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{0, keyB}})); err != nil { t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) } - if err := pool.addRemoteSync(setCodeTx(0, keyB, []unsignedAuth{{1, keyC}})); err != nil { - t.Fatalf("%s: failed to add conflicting delegation: %v", name, err) + // First transaction from B is accepted. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyB)); err != nil { + t.Fatalf("%s: failed to add remote transaction: %v", name, err) + } + // Second transaction fails due to limit. + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyB)); !errors.Is(err, txpool.ErrInflightTxLimitReached) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrInflightTxLimitReached, err) + } + // Replace by fee for first transaction from B works. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(2), keyB)); err != nil { + t.Fatalf("%s: failed to add remote transaction: %v", name, err) } }, }, { - name: "allow-one-tx-from-pooled-delegation", + // This is the symmetric case of the previous one, where the delegation request + // is received after the transaction. The resulting state shall be the same. + name: "accept-authorization-from-sender-of-one-inflight-tx", pending: 2, run: func(name string) { - // Verify C cannot originate another transaction when it has a pooled delegation. - if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{0, keyC}})); err != nil { - t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + // The first in-flight transaction is accepted. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyB)); err != nil { + t.Fatalf("%s: failed to add with pending delegation: %v", name, err) } - if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyC)); err != nil { - t.Fatalf("%s: failed to add with pending delegatio: %v", name, err) + // Delegation is accepted. + if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{0, keyB}})); err != nil { + t.Fatalf("%s: failed to add remote transaction: %v", name, err) } - // Also check gapped transaction is rejected. - if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyC)); !errors.Is(err, txpool.ErrInflightTxLimitReached) { + // The second in-flight transaction is rejected. + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyB)); !errors.Is(err, txpool.ErrInflightTxLimitReached) { t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrInflightTxLimitReached, err) } }, }, + { + name: "reject-authorization-from-sender-with-more-than-one-inflight-tx", + pending: 2, + run: func(name string) { + // Submit two transactions. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyB)); err != nil { + t.Fatalf("%s: failed to add with pending delegation: %v", name, err) + } + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyB)); err != nil { + t.Fatalf("%s: failed to add with pending delegation: %v", name, err) + } + // Delegation rejected since two txs are already in-flight. + if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{0, keyB}})); !errors.Is(err, ErrAuthorityReserved) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrAuthorityReserved, err) + } + }, + }, + { + name: "allow-setcode-tx-with-pending-authority-tx", + pending: 2, + run: func(name string) { + // Send two transactions where the first has no conflicting delegations and + // the second should be allowed despite conflicting with the authorities in the first. + if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + if err := pool.addRemoteSync(setCodeTx(0, keyB, []unsignedAuth{{1, keyC}})); err != nil { + t.Fatalf("%s: failed to add conflicting delegation: %v", name, err) + } + }, + }, { name: "replace-by-fee-setcode-tx", pending: 1, run: func(name string) { - // 4. Fee bump the setcode tx send. if err := pool.addRemoteSync(setCodeTx(0, keyB, []unsignedAuth{{1, keyC}})); err != nil { t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) } @@ -2307,44 +2385,85 @@ func TestSetCodeTransactions(t *testing.T) { }, }, { - name: "allow-tx-from-replaced-authority", - pending: 2, + name: "allow-more-than-one-tx-from-replaced-authority", + pending: 3, run: func(name string) { - // Fee bump with a different auth list. Make sure that unlocks the authorities. + // Send transaction from A with B as an authority. if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keyA, []unsignedAuth{{0, keyB}})); err != nil { t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) } + // Replace transaction with another having C as an authority. if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(3000), uint256.NewInt(300), keyA, []unsignedAuth{{0, keyC}})); err != nil { t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) } - // Now send a regular tx from B. + // B should not be considred as having an in-flight delegation, so + // should allow more than one pooled transaction. if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(10), keyB)); err != nil { t.Fatalf("%s: failed to replace with remote transaction: %v", name, err) } + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(10), keyB)); err != nil { + t.Fatalf("%s: failed to replace with remote transaction: %v", name, err) + } }, }, { + // This test is analogous to the previous one, but the the replaced + // transaction is self-sponsored. name: "allow-tx-from-replaced-self-sponsor-authority", - pending: 2, + pending: 3, run: func(name string) { - // + // Send transaction from A with A as an authority. if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keyA, []unsignedAuth{{0, keyA}})); err != nil { t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) } + // Replace transaction with a transaction with B as an authority. if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(30), uint256.NewInt(30), keyA, []unsignedAuth{{0, keyB}})); err != nil { t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) } - // Now send a regular tx from keyA. - if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyA)); err != nil { + // The one in-flight transaction limit from A no longer applies, so we + // can stack a second transaction for the account. + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1000), keyA)); err != nil { t.Fatalf("%s: failed to replace with remote transaction: %v", name, err) } - // Make sure we can still send from keyB. + // B should still be able to send transactions. if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyB)); err != nil { t.Fatalf("%s: failed to replace with remote transaction: %v", name, err) } + // However B still has the limitation to one in-flight transaction. + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyB)); !errors.Is(err, txpool.ErrInflightTxLimitReached) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrInflightTxLimitReached, err) + } }, }, { + name: "replacements-respect-inflight-tx-count", + pending: 2, + run: func(name string) { + // Send transaction from A with B as an authority. + if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keyA, []unsignedAuth{{0, keyB}})); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + // Send two transactions from B. Only the first should be accepted due + // to in-flight limit. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyB)); err != nil { + t.Fatalf("%s: failed to add remote transaction: %v", name, err) + } + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyB)); !errors.Is(err, txpool.ErrInflightTxLimitReached) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrInflightTxLimitReached, err) + } + // Replace the in-flight transaction from B. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(30), keyB)); err != nil { + t.Fatalf("%s: failed to replace with remote transaction: %v", name, err) + } + // Ensure the in-flight limit for B is still in place. + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyB)); !errors.Is(err, txpool.ErrInflightTxLimitReached) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrInflightTxLimitReached, err) + } + }, + }, + { + // Since multiple authorizations can be pending simultaneously, replacing + // one of them should not break the one in-flight-transaction limit. name: "track-multiple-conflicting-delegations", pending: 3, run: func(name string) { @@ -2369,19 +2488,6 @@ func TestSetCodeTransactions(t *testing.T) { } }, }, - { - name: "reject-delegation-from-pending-account", - pending: 1, - run: func(name string) { - // Attempt to submit a delegation from an account with a pending tx. - if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil { - t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) - } - if err, want := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})), ErrAuthorityReserved; !errors.Is(err, want) { - t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err) - } - }, - }, { name: "remove-hash-from-authority-tracker", pending: 10, diff --git a/core/txpool/reserver.go b/core/txpool/reserver.go index 76ead0f3bb0..b6ecef9f1a8 100644 --- a/core/txpool/reserver.go +++ b/core/txpool/reserver.go @@ -52,22 +52,37 @@ func NewReservationTracker() *ReservationTracker { // NewHandle creates a named handle on the ReservationTracker. The handle // identifies the subpool so ownership of reservations can be determined. -func (r *ReservationTracker) NewHandle(id int) *Reserver { - return &Reserver{r, id} +func (r *ReservationTracker) NewHandle(id int) *ReservationHandle { + return &ReservationHandle{r, id} } -// Reserver is a named handle on ReservationTracker. It is held by subpools to +// Reserver is an interface for creating and releasing owned reservations in the +// ReservationTracker struct, which is shared between subpools. +type Reserver interface { + // Hold attempts to reserve the specified account address for the given pool. + // Returns an error if the account is already reserved. + Hold(addr common.Address) error + + // Release attempts to release the reservation for the specified account. + // Returns an error if the address is not reserved or is reserved by another pool. + Release(addr common.Address) error + + // Has returns a flag indicating if the address has been reserved by a pool + // other than one with the current Reserver handle. + Has(address common.Address) bool +} + +// ReservationHandle is a named handle on ReservationTracker. It is held by subpools to // make reservations for accounts it is tracking. The id is used to determine // which pool owns an address and disallows non-owners to hold or release // addresses it doesn't own. -type Reserver struct { +type ReservationHandle struct { tracker *ReservationTracker id int } -// Hold attempts to reserve the specified account address for the given pool. -// Returns an error if the account is already reserved. -func (h *Reserver) Hold(addr common.Address) error { +// Hold implements the Reserver interface. +func (h *ReservationHandle) Hold(addr common.Address) error { h.tracker.lock.Lock() defer h.tracker.lock.Unlock() @@ -89,9 +104,8 @@ func (h *Reserver) Hold(addr common.Address) error { return nil } -// Release attempts to release the reservation for the specified account. -// Returns an error if the address is not reserved or is reserved by another pool. -func (h *Reserver) Release(addr common.Address) error { +// Release implements the Reserver interface. +func (h *ReservationHandle) Release(addr common.Address) error { h.tracker.lock.Lock() defer h.tracker.lock.Unlock() @@ -114,11 +128,11 @@ func (h *Reserver) Release(addr common.Address) error { return nil } -// Has returns a flag indicating if the address has been reserved or not. -func (h *Reserver) Has(address common.Address) bool { +// Has implements the Reserver interface. +func (h *ReservationHandle) Has(address common.Address) bool { h.tracker.lock.RLock() defer h.tracker.lock.RUnlock() - _, exists := h.tracker.accounts[address] - return exists + id, exists := h.tracker.accounts[address] + return exists && id != h.id } diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index 2cb51038754..8cfc14f1640 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -105,7 +105,7 @@ type SubPool interface { // These should not be passed as a constructor argument - nor should the pools // start by themselves - in order to keep multiple subpools in lockstep with // one another. - Init(gasTip uint64, head *types.Header, reserver *Reserver) error + Init(gasTip uint64, head *types.Header, reserver Reserver) error // Close terminates any background processing threads and releases any held // resources. From 2547bb28a4a25969dd677f048155b64394fe59fd Mon Sep 17 00:00:00 2001 From: crStiv Date: Thu, 10 Apr 2025 12:26:35 +0300 Subject: [PATCH 087/658] eth/fetcher: Fix flaky TestTransactionForgotten test using mock clock (#31468) Fixes #31169 The TestTransactionForgotten test was flaky due to real time dependencies. This PR: - Replaces real time with mock clock for deterministic timing control - Adds precise state checks at timeout boundaries - Verifies underpriced cache states and cleanup - Improves test reliability by controlling transaction timestamps - Adds checks for transaction re-enqueueing behavior The changes ensure consistent test behavior without timing-related flakiness. --------- Co-authored-by: Zsolt Felfoldi --- eth/fetcher/tx_fetcher.go | 14 +-- eth/fetcher/tx_fetcher_test.go | 98 ++++++++++++++++++--- tests/fuzzers/txfetcher/txfetcher_fuzzer.go | 7 +- 3 files changed, 99 insertions(+), 20 deletions(-) diff --git a/eth/fetcher/tx_fetcher.go b/eth/fetcher/tx_fetcher.go index 97d1e298621..1c192d41122 100644 --- a/eth/fetcher/tx_fetcher.go +++ b/eth/fetcher/tx_fetcher.go @@ -202,22 +202,23 @@ type TxFetcher struct { fetchTxs func(string, []common.Hash) error // Retrieves a set of txs from a remote peer dropPeer func(string) // Drops a peer in case of announcement violation - step chan struct{} // Notification channel when the fetcher loop iterates - clock mclock.Clock // Time wrapper to simulate in tests - rand *mrand.Rand // Randomizer to use in tests instead of map range loops (soft-random) + step chan struct{} // Notification channel when the fetcher loop iterates + clock mclock.Clock // Monotonic clock or simulated clock for tests + realTime func() time.Time // Real system time or simulated time for tests + rand *mrand.Rand // Randomizer to use in tests instead of map range loops (soft-random) } // NewTxFetcher creates a transaction fetcher to retrieve transaction // based on hash announcements. func NewTxFetcher(hasTx func(common.Hash) bool, addTxs func([]*types.Transaction) []error, fetchTxs func(string, []common.Hash) error, dropPeer func(string)) *TxFetcher { - return NewTxFetcherForTests(hasTx, addTxs, fetchTxs, dropPeer, mclock.System{}, nil) + return NewTxFetcherForTests(hasTx, addTxs, fetchTxs, dropPeer, mclock.System{}, time.Now, nil) } // NewTxFetcherForTests is a testing method to mock out the realtime clock with // a simulated version and the internal randomness with a deterministic one. func NewTxFetcherForTests( hasTx func(common.Hash) bool, addTxs func([]*types.Transaction) []error, fetchTxs func(string, []common.Hash) error, dropPeer func(string), - clock mclock.Clock, rand *mrand.Rand) *TxFetcher { + clock mclock.Clock, realTime func() time.Time, rand *mrand.Rand) *TxFetcher { return &TxFetcher{ notify: make(chan *txAnnounce), cleanup: make(chan *txDelivery), @@ -237,6 +238,7 @@ func NewTxFetcherForTests( fetchTxs: fetchTxs, dropPeer: dropPeer, clock: clock, + realTime: realTime, rand: rand, } } @@ -293,7 +295,7 @@ func (f *TxFetcher) Notify(peer string, types []byte, sizes []uint32, hashes []c // isKnownUnderpriced reports whether a transaction hash was recently found to be underpriced. func (f *TxFetcher) isKnownUnderpriced(hash common.Hash) bool { prevTime, ok := f.underpriced.Peek(hash) - if ok && prevTime.Before(time.Now().Add(-maxTxUnderpricedTimeout)) { + if ok && prevTime.Before(f.realTime().Add(-maxTxUnderpricedTimeout)) { f.underpriced.Remove(hash) return false } diff --git a/eth/fetcher/tx_fetcher_test.go b/eth/fetcher/tx_fetcher_test.go index 52b35910862..7f3080f5f6f 100644 --- a/eth/fetcher/tx_fetcher_test.go +++ b/eth/fetcher/tx_fetcher_test.go @@ -2150,9 +2150,22 @@ func containsHashInAnnounces(slice []announce, hash common.Hash) bool { return false } -// Tests that a transaction is forgotten after the timeout. +// TestTransactionForgotten verifies that underpriced transactions are properly +// forgotten after the timeout period, testing both the exact timeout boundary +// and the cleanup of the underpriced cache. func TestTransactionForgotten(t *testing.T) { - fetcher := NewTxFetcher( + // Test ensures that underpriced transactions are properly forgotten after a timeout period, + // including checks for timeout boundary and cache cleanup. + t.Parallel() + + // Create a mock clock for deterministic time control + mockClock := new(mclock.Simulated) + mockTime := func() time.Time { + nanoTime := int64(mockClock.Now()) + return time.Unix(nanoTime/1000000000, nanoTime%1000000000) + } + + fetcher := NewTxFetcherForTests( func(common.Hash) bool { return false }, func(txs []*types.Transaction) []error { errs := make([]error, len(txs)) @@ -2163,24 +2176,83 @@ func TestTransactionForgotten(t *testing.T) { }, func(string, []common.Hash) error { return nil }, func(string) {}, + mockClock, + mockTime, + rand.New(rand.NewSource(0)), // Use fixed seed for deterministic behavior ) fetcher.Start() defer fetcher.Stop() - // Create one TX which is 5 minutes old, and one which is recent - tx1 := types.NewTx(&types.LegacyTx{Nonce: 0}) - tx1.SetTime(time.Now().Add(-maxTxUnderpricedTimeout - 1*time.Second)) - tx2 := types.NewTx(&types.LegacyTx{Nonce: 1}) - // Enqueue both in the fetcher. They will be immediately tagged as underpriced - if err := fetcher.Enqueue("asdf", []*types.Transaction{tx1, tx2}, false); err != nil { + // Create two test transactions with the same timestamp + tx1 := types.NewTransaction(0, common.Address{}, big.NewInt(100), 21000, big.NewInt(1), nil) + tx2 := types.NewTransaction(1, common.Address{}, big.NewInt(100), 21000, big.NewInt(1), nil) + + now := mockTime() + tx1.SetTime(now) + tx2.SetTime(now) + + // Initial state: both transactions should be marked as underpriced + if err := fetcher.Enqueue("peer", []*types.Transaction{tx1, tx2}, false); err != nil { t.Fatal(err) } - // isKnownUnderpriced should trigger removal of the first tx (no longer be known underpriced) - if fetcher.isKnownUnderpriced(tx1.Hash()) { - t.Fatal("transaction should be forgotten by now") + if !fetcher.isKnownUnderpriced(tx1.Hash()) { + t.Error("tx1 should be underpriced") + } + if !fetcher.isKnownUnderpriced(tx2.Hash()) { + t.Error("tx2 should be underpriced") + } + + // Verify cache size + if size := fetcher.underpriced.Len(); size != 2 { + t.Errorf("wrong underpriced cache size: got %d, want %d", size, 2) + } + + // Just before timeout: transactions should still be underpriced + mockClock.Run(maxTxUnderpricedTimeout - time.Second) + if !fetcher.isKnownUnderpriced(tx1.Hash()) { + t.Error("tx1 should still be underpriced before timeout") + } + if !fetcher.isKnownUnderpriced(tx2.Hash()) { + t.Error("tx2 should still be underpriced before timeout") + } + + // Exactly at timeout boundary: transactions should still be present + mockClock.Run(time.Second) + if !fetcher.isKnownUnderpriced(tx1.Hash()) { + t.Error("tx1 should be present exactly at timeout") } - // isKnownUnderpriced should not trigger removal of the second if !fetcher.isKnownUnderpriced(tx2.Hash()) { - t.Fatal("transaction should be known underpriced") + t.Error("tx2 should be present exactly at timeout") + } + + // After timeout: transactions should be forgotten + mockClock.Run(time.Second) + if fetcher.isKnownUnderpriced(tx1.Hash()) { + t.Error("tx1 should be forgotten after timeout") + } + if fetcher.isKnownUnderpriced(tx2.Hash()) { + t.Error("tx2 should be forgotten after timeout") + } + + // Verify cache is empty + if size := fetcher.underpriced.Len(); size != 0 { + t.Errorf("wrong underpriced cache size after timeout: got %d, want 0", size) + } + + // Re-enqueue tx1 with updated timestamp + tx1.SetTime(mockTime()) + if err := fetcher.Enqueue("peer", []*types.Transaction{tx1}, false); err != nil { + t.Fatal(err) + } + if !fetcher.isKnownUnderpriced(tx1.Hash()) { + t.Error("tx1 should be underpriced after re-enqueueing with new timestamp") + } + if fetcher.isKnownUnderpriced(tx2.Hash()) { + t.Error("tx2 should remain forgotten") + } + + // Verify final cache state + if size := fetcher.underpriced.Len(); size != 1 { + t.Errorf("wrong final underpriced cache size: got %d, want 1", size) } } diff --git a/tests/fuzzers/txfetcher/txfetcher_fuzzer.go b/tests/fuzzers/txfetcher/txfetcher_fuzzer.go index 51f2fc3b4d9..c136253a62b 100644 --- a/tests/fuzzers/txfetcher/txfetcher_fuzzer.go +++ b/tests/fuzzers/txfetcher/txfetcher_fuzzer.go @@ -84,7 +84,12 @@ func fuzz(input []byte) int { }, func(string, []common.Hash) error { return nil }, nil, - clock, rand, + clock, + func() time.Time { + nanoTime := int64(clock.Now()) + return time.Unix(nanoTime/1000000000, nanoTime%1000000000) + }, + rand, ) f.Start() defer f.Stop() From 4906c9911316c52ccd20e122d1e092abe3c7d6ca Mon Sep 17 00:00:00 2001 From: HackyMiner Date: Thu, 10 Apr 2025 19:46:54 +0900 Subject: [PATCH 088/658] accounts/usbwallet: full 32bit chainId support for Trezor (#17439) This fix allows Trezor to support full 32bit chainId in geth, with the next version of firmware. For `chainId > 2147483630` case, Trezor returns signature bit only. - Trezor returns only signature parity for `chainId > 2147483630` case. - for `chainId == 2147483630` case, Trezor returns `MAX_UINT32` or `0`, but it doesn't matter. (`2147483630 * 2 + 35` = `4294967295`(`MAX_UINT32`)) chainId | returned signature_v | compatible issue ---------|------------------------|-------------------- 0 < chainId <= 255 | chainId * 2 + 35 + v | no issue (firmware `1.6.2` for Trezor one) 255 < chainId <= 2147483630 | chainId * 2 + 35 + v | ***fixed.*** *firmware `1.6.3`* chainId > 2147483630 | v | *firmware `1.6.3`* Please see also: full 32bit chainId support for Trezor - Trezor one: https://github.com/trezor/trezor-mcu/pull/399 ***merged*** - Trezor model T: https://github.com/trezor/trezor-core/pull/311 ***merged*** --------- Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Co-authored-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --- accounts/usbwallet/trezor.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/accounts/usbwallet/trezor.go b/accounts/usbwallet/trezor.go index 1c4270d255c..d4862d161b7 100644 --- a/accounts/usbwallet/trezor.go +++ b/accounts/usbwallet/trezor.go @@ -25,6 +25,7 @@ import ( "errors" "fmt" "io" + "math" "math/big" "github.com/ethereum/go-ethereum/accounts" @@ -249,7 +250,11 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction } } // Extract the Ethereum signature and do a sanity validation - if len(response.GetSignatureR()) == 0 || len(response.GetSignatureS()) == 0 || response.GetSignatureV() == 0 { + if len(response.GetSignatureR()) == 0 || len(response.GetSignatureS()) == 0 { + return common.Address{}, nil, errors.New("reply lacks signature") + } else if response.GetSignatureV() == 0 && int(chainID.Int64()) <= (math.MaxUint32-36)/2 { + // for chainId >= (MaxUint32-36)/2, Trezor returns signature bit only + // https://github.com/trezor/trezor-mcu/pull/399 return common.Address{}, nil, errors.New("reply lacks signature") } signature := append(append(response.GetSignatureR(), response.GetSignatureS()...), byte(response.GetSignatureV())) @@ -261,7 +266,11 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction } else { // Trezor backend does not support typed transactions yet. signer = types.NewEIP155Signer(chainID) - signature[64] -= byte(chainID.Uint64()*2 + 35) + // if chainId is above (MaxUint32 - 36) / 2 then the final v values is returned + // directly. Otherwise, the returned value is 35 + chainid * 2. + if signature[64] > 1 && int(chainID.Int64()) <= (math.MaxUint32-36)/2 { + signature[64] -= byte(chainID.Uint64()*2 + 35) + } } // Inject the final signature into the transaction and sanity check the sender From 9b4eab6a29704f55fa7b4b92e296094f0dbcee22 Mon Sep 17 00:00:00 2001 From: jwasinger Date: Thu, 10 Apr 2025 19:49:54 +0800 Subject: [PATCH 089/658] eth/catalyst: in tests, manually sync txpool after initial chain insertion to prevent race between txpool head reset and promotion of txs that will be subsequently added (#31595) before this changes, this will result in numerous test failures: ``` > go test -run=Eth2AssembleBlock -c > stress ./catalyst.test ``` The reason is that after creating/inserting the test chain, there is a race between the txpool head reset and the promotion of txs added from tests. Ensuring that the txpool state is up to date with the head of the chain before proceeding fixes these flaky tests. --- eth/catalyst/api_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index e91e07d05d4..cb6ae053b62 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -447,6 +447,9 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) n.Close() t.Fatal("can't import test blocks:", err) } + if err := ethservice.TxPool().Sync(); err != nil { + t.Fatal("failed to sync txpool after initial blockchain import:", err) + } ethservice.SetSynced() return n, ethservice From f64aa6eaf7a021226648aeae5ceef800fb9b66d0 Mon Sep 17 00:00:00 2001 From: jwasinger Date: Fri, 11 Apr 2025 00:21:32 +0800 Subject: [PATCH 090/658] internal/testlog: fix log output from sub-loggers (#31539) When we instantiate a sub-logger via `go-ethereum/internal/testlog/logger.With`, we copy the reference to the `bufHandler` from the parent logger. However, internally, `go-ethereum/internal/testlog/logger.With` calls `log/slog/Logger.With` which creates a new handler instance (via `internal/bufHandler.WithAttrs`). This PR modifies sub-logger instantiation to use the newly-instantiated handler, instead of copying the reference from the parent instance. The type cast from `slog.Handler` to `*bufHandler` in `internal/testlog/Logger.With` is safe here because a `internal/testlog/Logger` can only be instantiated with a `*bufHandler` as the underlying handler type. Note, that I've also removed a pre-existing method that broke the above assumption. However, this method is not used in our codebase. I'm not sure if the assumption holds for forks of geth (e.g. optimism has modified the testlogger somewhat allowing test loggers to accept arbitrary handler types), but it seems okay to break API compatibility given that this is in the `internal` package. closes https://github.com/ethereum/go-ethereum/issues/31533 --- internal/testlog/testlog.go | 28 +++++++------ internal/testlog/testlog_test.go | 67 ++++++++++++++++++++++++++++++++ tests/testdata | 2 +- 3 files changed, 81 insertions(+), 16 deletions(-) create mode 100644 internal/testlog/testlog_test.go diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go index ad61af9eac2..8a3ea854384 100644 --- a/internal/testlog/testlog.go +++ b/internal/testlog/testlog.go @@ -23,7 +23,6 @@ import ( "fmt" "log/slog" "sync" - "testing" "github.com/ethereum/go-ethereum/log" ) @@ -32,12 +31,21 @@ const ( termTimeFormat = "01-02|15:04:05.000" ) +// T wraps methods from testing.T used by the test logger into an interface. +// It is specified so that unit tests can instantiate the logger with an +// implementation of T which can capture the output of logging statements +// from T.Logf, as this cannot be using testing.T. +type T interface { + Logf(format string, args ...any) + Helper() +} + // logger implements log.Logger such that all output goes to the unit test log via // t.Logf(). All methods in between logger.Trace, logger.Debug, etc. are marked as test // helpers, so the file and line number in unit test output correspond to the call site // which emitted the log message. type logger struct { - t *testing.T + t T l log.Logger mu *sync.Mutex h *bufHandler @@ -78,7 +86,7 @@ func (h *bufHandler) WithGroup(_ string) slog.Handler { } // Logger returns a logger which logs to the unit test log of t. -func Logger(t *testing.T, level slog.Level) log.Logger { +func Logger(t T, level slog.Level) log.Logger { handler := bufHandler{ buf: []slog.Record{}, attrs: []slog.Attr{}, @@ -92,17 +100,6 @@ func Logger(t *testing.T, level slog.Level) log.Logger { } } -// LoggerWithHandler returns -func LoggerWithHandler(t *testing.T, handler slog.Handler) log.Logger { - var bh bufHandler - return &logger{ - t: t, - l: log.NewLogger(handler), - mu: new(sync.Mutex), - h: &bh, - } -} - func (l *logger) Handler() slog.Handler { return l.l.Handler() } @@ -170,7 +167,8 @@ func (l *logger) Crit(msg string, ctx ...interface{}) { } func (l *logger) With(ctx ...interface{}) log.Logger { - return &logger{l.t, l.l.With(ctx...), l.mu, l.h} + newLogger := l.l.With(ctx...) + return &logger{l.t, newLogger, l.mu, newLogger.Handler().(*bufHandler)} } func (l *logger) New(ctx ...interface{}) log.Logger { diff --git a/internal/testlog/testlog_test.go b/internal/testlog/testlog_test.go new file mode 100644 index 00000000000..7789a47270d --- /dev/null +++ b/internal/testlog/testlog_test.go @@ -0,0 +1,67 @@ +package testlog + +import ( + "bytes" + "fmt" + "io" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/log" +) + +type mockT struct { + out io.Writer +} + +func (t *mockT) Helper() { + // noop for the purposes of unit tests +} + +func (t *mockT) Logf(format string, args ...any) { + // we could gate this operation in a mutex, but because testlogger + // only calls Logf with its internal mutex held, we just write output here + var lineBuf bytes.Buffer + if _, err := fmt.Fprintf(&lineBuf, format, args...); err != nil { + panic(err) + } + // The timestamp is locale-dependent, so we want to trim that off + // "INFO [01-01|00:00:00.000] a message ..." -> "a message..." + sanitized := strings.Split(lineBuf.String(), "]")[1] + if _, err := t.out.Write([]byte(sanitized)); err != nil { + panic(err) + } +} + +func TestLogging(t *testing.T) { + tests := []struct { + name string + expected string + run func(t *mockT) + }{ + { + "SubLogger", + ` Visible + Hide and seek foobar=123 + Also visible +`, + func(t *mockT) { + l := Logger(t, log.LevelInfo) + subLogger := l.New("foobar", 123) + + l.Info("Visible") + subLogger.Info("Hide and seek") + l.Info("Also visible") + }, + }, + } + + for _, tc := range tests { + outp := bytes.Buffer{} + mock := mockT{&outp} + tc.run(&mock) + if outp.String() != tc.expected { + fmt.Printf("output mismatch.\nwant: '%s'\ngot: '%s'\n", tc.expected, outp.String()) + } + } +} diff --git a/tests/testdata b/tests/testdata index 81862e48485..faf33b47146 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit 81862e4848585a438d64f911a19b3825f0f4cd95 +Subproject commit faf33b471465d3c6cdc3d04fbd690895f78d33f2 From a9444ea312eb4077a470cdb9b394ec0544cf116b Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 10 Apr 2025 23:54:44 +0200 Subject: [PATCH 091/658] tests/testdata: revert to v17.0 The submodule was accidentally updated to another commit by f64aa6eaf7. --- tests/testdata | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testdata b/tests/testdata index faf33b47146..81862e48485 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit faf33b471465d3c6cdc3d04fbd690895f78d33f2 +Subproject commit 81862e4848585a438d64f911a19b3825f0f4cd95 From 0059b0ab6676232dd3809baa664d1e5b0d5e85ab Mon Sep 17 00:00:00 2001 From: levisyin Date: Fri, 11 Apr 2025 17:28:14 +0800 Subject: [PATCH 092/658] build: upgrade -dlgo version to Go 1.24.2 (#31538) --- build/checksums.txt | 94 ++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/build/checksums.txt b/build/checksums.txt index 6d3b718c3cb..a914d798600 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -5,54 +5,54 @@ # https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-6%40v1.0.0/ b69211752a3029083c020dc635fe12156ca1a6725a08559da540a0337586a77e fixtures_pectra-devnet-6.tar.gz -# version:golang 1.24.1 +# version:golang 1.24.2 # https://go.dev/dl/ -8244ebf46c65607db10222b5806aeb31c1fcf8979c1b6b12f60c677e9a3c0656 go1.24.1.src.tar.gz -8d627dc163a4bffa2b1887112ad6194af175dce108d606ed1714a089fb806033 go1.24.1.aix-ppc64.tar.gz -addbfce2056744962e2d7436313ab93486660cf7a2e066d171b9d6f2da7c7abe go1.24.1.darwin-amd64.tar.gz -58d529334561cff11087cd4ab18fe0b46d8d5aad88f45c02b9645f847e014512 go1.24.1.darwin-amd64.pkg -295581b5619acc92f5106e5bcb05c51869337eb19742fdfa6c8346c18e78ff88 go1.24.1.darwin-arm64.tar.gz -78b0fc8ddc344eb499f1a952c687cb84cbd28ba2b739cfa0d4eb042f07e44e82 go1.24.1.darwin-arm64.pkg -e70053f56f7eb93806d80cbd5726f78509a0a467602f7bea0e2c4ee8ed7c3968 go1.24.1.dragonfly-amd64.tar.gz -3595e2674ed8fe72e604ca59c964d3e5277aafb08475c2b1aaca2d2fd69c24fc go1.24.1.freebsd-386.tar.gz -47d7de8bb64d5c3ee7b6723aa62d5ecb11e3568ef2249bbe1d4bbd432d37c00c go1.24.1.freebsd-amd64.tar.gz -04eec3bcfaa14c1370cdf98e8307fac7e4853496c3045afb9c3124a29cbca205 go1.24.1.freebsd-arm.tar.gz -51aa70146e40cfdc20927424083dc86e6223f85dc08089913a1651973b55665b go1.24.1.freebsd-arm64.tar.gz -3c131d8e3fc285a1340f87813153e24226d3ddbd6e54f3facbd6e4c46a84655e go1.24.1.freebsd-riscv64.tar.gz -201d09da737ba39d5367f87d4e8b31edaeeb3dc9b9c407cb8cfb40f90c5a727a go1.24.1.illumos-amd64.tar.gz -8c530ecedbc17e42ce10177bea07ccc96a3e77c792ea1ea72173a9675d16ffa5 go1.24.1.linux-386.tar.gz -cb2396bae64183cdccf81a9a6df0aea3bce9511fc21469fb89a0c00470088073 go1.24.1.linux-amd64.tar.gz -8df5750ffc0281017fb6070fba450f5d22b600a02081dceef47966ffaf36a3af go1.24.1.linux-arm64.tar.gz -6d95f8d7884bfe2364644c837f080f2b585903d0b771eb5b06044e226a4f120a go1.24.1.linux-armv6l.tar.gz -19304a4a56e46d04604547d2d83235dc4f9b192c79832560ce337d26cc7b835a go1.24.1.linux-loong64.tar.gz -6347be77fa5359c12a5308c8ab87147c1fc4717b0c216493d1706c3b9fcde22d go1.24.1.linux-mips.tar.gz -1647df415f7030b82d4105670192aa7e8910e18563bb0d505192d72800cc2d21 go1.24.1.linux-mips64.tar.gz -762da594e4ec0f9cf6defae6ef971f5f7901203ee6a2d979e317adec96657317 go1.24.1.linux-mips64le.tar.gz -9d8133c7b23a557399fab870b5cf464079c2b623a43b214a7567cf11c254a444 go1.24.1.linux-mipsle.tar.gz -132f10999abbaccbada47fa85462db30c423955913b14d6c692de25f4636c766 go1.24.1.linux-ppc64.tar.gz -0fb522efcefabae6e37e69bdc444094e75bfe824ea6d4cc3cbc70c7ae1b16858 go1.24.1.linux-ppc64le.tar.gz -eaef4323d5467ff97fb1979c8491764060dade19f02f3275a9313f9a0da3b9c0 go1.24.1.linux-riscv64.tar.gz -6c05e14d8f11094cb56a1c50f390b6b658bed8a7cbd8d1a57e926581b7eabfce go1.24.1.linux-s390x.tar.gz -5dbb287d343ea00d58a70b11629f32ee716dc50a6875c459ea2018df0f294cd8 go1.24.1.netbsd-386.tar.gz -617aa3faee50ce84c343db0888e9a210c310a7203666b4ed620f31030c9fb32f go1.24.1.netbsd-amd64.tar.gz -59a928b7080c4a6ac985946274b7c65ce1cecc0b468ecd992d17b7c12fab9296 go1.24.1.netbsd-arm.tar.gz -28daa8d0feb4aef2af60cefa3305bb9314de7e8a05cbca41ac548964cdfe89b7 go1.24.1.netbsd-arm64.tar.gz -b7382b2f5d99813aeac14db482faa3bfbd47a68880b607fa2a7e669e26bab9cd go1.24.1.openbsd-386.tar.gz -2513b6537c45deead5e641c7ce7502913e7d5e6f0b21c52542fb11f81578690f go1.24.1.openbsd-amd64.tar.gz -853c1917d4fc7b144c55a02842aa48542d5cc798dde8db96dc0fdbc263200e04 go1.24.1.openbsd-arm.tar.gz -6bc207b91e6f6ae3347fb54616a8fb2f5c11983713846a4cef111ff3f4f94d14 go1.24.1.openbsd-arm64.tar.gz -4279260e2f2b94ee94e81470d13db7367f4393b061fee60985528fa0fa430df4 go1.24.1.openbsd-ppc64.tar.gz -6fc4023a0a339ee0778522364a127d94c78e62122288d47d820dba703f81dc07 go1.24.1.openbsd-riscv64.tar.gz -b5eb9fafd77146e7e1f748acfd95559580ecc8d2f15abf432a20f58c929c7cd2 go1.24.1.plan9-386.tar.gz -24dcad6361b141fc8cced15b092351e12a99d2e58d7013204a3013c50daf9fdd go1.24.1.plan9-amd64.tar.gz -a026ac3b55aa1e6fdc2aaab30207a117eafbe965ed81d3aa0676409f280ddc37 go1.24.1.plan9-arm.tar.gz -8e4f6a77388dc6e5aa481efd5abdb3b9f5c9463bb82f4db074494e04e5c84992 go1.24.1.solaris-amd64.tar.gz -b799f4ab264eef12a014c759383ed934056608c483e0f73e34ea6caf9f1df5f9 go1.24.1.windows-386.zip -db128981033ac82a64688a123f631e61297b6b8f52ca913145e57caa8ce94cc3 go1.24.1.windows-386.msi -95666b551453209a2b8869d29d177285ff9573af10f085d961d7ae5440f645ce go1.24.1.windows-amd64.zip -5968e7adcf26e68a54f1cd41ad561275a670a8e2ca5263bc375b524638557dfb go1.24.1.windows-amd64.msi -e28c4e6d0b913955765b46157ab88ae59bb636acaa12d7bec959aa6900f1cebd go1.24.1.windows-arm64.zip -6d352c1f154a102a5b90c480cc64bab205ccf2681e34e78a3a4d3f1ddfbc81e4 go1.24.1.windows-arm64.msi +9dc77ffadc16d837a1bf32d99c624cb4df0647cee7b119edd9e7b1bcc05f2e00 go1.24.2.src.tar.gz +427b373540d8fd51dbcc46bdecd340af109cd41514443c000d3dcde72b2c65a3 go1.24.2.aix-ppc64.tar.gz +238d9c065d09ff6af229d2e3b8b5e85e688318d69f4006fb85a96e41c216ea83 go1.24.2.darwin-amd64.tar.gz +535ed9ff283fee39575a7fb9b6d8b1901b6dc640d06dc71fd7d3faeefdaf8030 go1.24.2.darwin-amd64.pkg +b70f8b3c5b4ccb0ad4ffa5ee91cd38075df20fdbd953a1daedd47f50fbcff47a go1.24.2.darwin-arm64.tar.gz +4732f607a47ce4d898c0af01ff68f07e0820a6b50603aef5d5c777d1102505e2 go1.24.2.darwin-arm64.pkg +c17686b5fd61a663fbfafccfa177961be59386cf294e935ce35866b9dcb8e78a go1.24.2.dragonfly-amd64.tar.gz +026f1dd906189acff714c7625686bbc4ed91042618ba010d45b671461acc9e63 go1.24.2.freebsd-386.tar.gz +49399ba759b570a8f87d12179133403da6c2dd296d63a8830dee309161b9c40c go1.24.2.freebsd-amd64.tar.gz +1f48f47183794d97c29736004247ab541177cf984ac6322c78bc43828daa1172 go1.24.2.freebsd-arm.tar.gz +ef856428b60a8c0bd9a2cba596e83024be6f1c2d5574e89cb1ff2262b08df8b9 go1.24.2.freebsd-arm64.tar.gz +ec2088823e16df00600a6d0f72e9a7dc6d2f80c9c140c2043c0cf20e1404d1a9 go1.24.2.freebsd-riscv64.tar.gz +e030e7cedbb8688f1d75cb80f3de6ee2e6617a67d34051e794e5992b53462147 go1.24.2.illumos-amd64.tar.gz +4c382776d52313266f3026236297a224a6688751256a2dffa3f524d8d6f6c0ba go1.24.2.linux-386.tar.gz +68097bd680839cbc9d464a0edce4f7c333975e27a90246890e9f1078c7e702ad go1.24.2.linux-amd64.tar.gz +756274ea4b68fa5535eb9fe2559889287d725a8da63c6aae4d5f23778c229f4b go1.24.2.linux-arm64.tar.gz +438d5d3d7dcb239b58d893a715672eabe670b9730b1fd1c8fc858a46722a598a go1.24.2.linux-armv6l.tar.gz +6aefd3bf59c3c5592eda4fb287322207f119c2210f3795afa9be48d3ccb73e1b go1.24.2.linux-loong64.tar.gz +93e49bb4692783b0e9a2deab9558c6e8d2867f35592aeff285adda60924167f3 go1.24.2.linux-mips.tar.gz +6e86e703675016f3faf6604b8f68f20dc1bba75849136e6dd4f43f69c8a4a9d9 go1.24.2.linux-mips64.tar.gz +f233d237538ca1559a7d7cf519a29f0147923a951377bc4e467af4c059e68851 go1.24.2.linux-mips64le.tar.gz +545e1b9a7939f923fd53bde98334b987ef42eb353ee3e0bfede8aa06079d6b24 go1.24.2.linux-mipsle.tar.gz +6eab31481f2f46187bc1b6c887662eef06fc9d7271a8390854072cdb387c8d74 go1.24.2.linux-ppc64.tar.gz +5fff857791d541c71d8ea0171c73f6f99770d15ff7e2ad979104856d01f36563 go1.24.2.linux-ppc64le.tar.gz +91bda1558fcbd1c92769ad86c8f5cf796f8c67b0d9d9c19f76eecfc75ce71527 go1.24.2.linux-riscv64.tar.gz +1cb3448166d6abb515a85a3ee5afbdf932081fb58ad7143a8fb666fbc06146d9 go1.24.2.linux-s390x.tar.gz +a9a2c0db2e826f20f00b02bee01dfdaeb49591c2f6ffacb78dc64a950894f7ff go1.24.2.netbsd-386.tar.gz +cd1a35b76ed9c7b6c0c1616741bd319699a77867ade0be9924f32496c0a87a3f go1.24.2.netbsd-amd64.tar.gz +8c666388d066e479155cc5116950eeb435df28087ef277c18f1dc7479f836e60 go1.24.2.netbsd-arm.tar.gz +5d42f0be04f58da5be788a1e260f8747c316b8ce182bf0b273c2e4c691feaa1a go1.24.2.netbsd-arm64.tar.gz +688effa23ea3973cc8b0fdf4246712cbeef55ff20c45f3a9e28b0c2db04246cf go1.24.2.openbsd-386.tar.gz +e5daf95f1048d8026b1366450a3f8044d668b0639db6422ad9a83755c6745cf7 go1.24.2.openbsd-amd64.tar.gz +aeadaf74bd544d1a12ba9b14c0e7cdb1964de3ba9a52acb4619e91dbae7def7b go1.24.2.openbsd-arm.tar.gz +9e222d9adb0ce836a5b3c8d5aadbd167c8869c030b113f4a81aa88e9a200f279 go1.24.2.openbsd-arm64.tar.gz +192fffa34536adc3cd1bb7c1ee785b8bc156ae7afd10bbf5db99ec8f2e93066e go1.24.2.openbsd-ppc64.tar.gz +a23e90b451a390549042c2a7efbec6f29ed98b2d5618c8d2a35704e21be96e09 go1.24.2.openbsd-riscv64.tar.gz +5cdcafe455d859b02779611a5a1e1d63e498b922e05818fb3debe410a5959e9e go1.24.2.plan9-386.tar.gz +81351659804fa505c1b3ec6fdf9599f7f88df08614307eeb96071bf5e2e74beb go1.24.2.plan9-amd64.tar.gz +6e337d5def14ed0123423c1c32e2e6d8b19161e5d5ffaa7356dad48ee0fd80b4 go1.24.2.plan9-arm.tar.gz +07e6926ebc476c044d7d5b17706abfc52be52bccc2073d1734174efe63c6b35e go1.24.2.solaris-amd64.tar.gz +13d86cb818bba331da75fcd18246ab31a1067b44fb4a243b6dfd93097eda7f37 go1.24.2.windows-386.zip +8a702d9f7104a15bd935f4191c58c24c0b6389e066b9d5661b93915114a2bef0 go1.24.2.windows-386.msi +29c553aabee0743e2ffa3e9fa0cda00ef3b3cc4ff0bc92007f31f80fd69892e1 go1.24.2.windows-amd64.zip +acefb191e72fea0bdb1a3f5f8f6f5ab18b42b3bbce0c7183f189f25953aff275 go1.24.2.windows-amd64.msi +ab267f7f9a3366d48d7664be9e627ce3e63273231430cce5f7783fb910f14148 go1.24.2.windows-arm64.zip +d187bfe539356c39573d2f46766d1d08122b4f33da00fd14d12485fa9e241ff5 go1.24.2.windows-arm64.msi # version:golangci 2.0.2 # https://github.com/golangci/golangci-lint/releases/ From f8ff24e8cfa95c7a1b55857dd5306a9d3b4e50ca Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 11 Apr 2025 11:31:16 +0200 Subject: [PATCH 093/658] version: release go-ethereum v1.15.8 stable --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 72fd903a1f7..945b3b58a21 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 8 // Patch version component of the current release - Meta = "unstable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 8 // Patch version component of the current release + Meta = "stable" // Version metadata to append to the version string ) From 80753ba14787ba82954106d7b9025e3a623b96b0 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 11 Apr 2025 11:31:56 +0200 Subject: [PATCH 094/658] version: begin v1.15.9 release cycle --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 945b3b58a21..c969ab479eb 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 8 // Patch version component of the current release - Meta = "stable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 9 // Patch version component of the current release + Meta = "unstable" // Version metadata to append to the version string ) From ecd5c18610c5276e7c6c34d2f317cf774441a81c Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Mon, 14 Apr 2025 10:13:45 +0200 Subject: [PATCH 095/658] p2p: better dial/serve success metrics (#31629) Our previous success metrics gave success even if a peer disconnected right after connection. These metrics only count peers that stayed connected for at least 1 min. The 1 min limit is an arbitrary choice. We do not use this for decision logic, only statistics. --- p2p/metrics.go | 4 ++++ p2p/peer.go | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/p2p/metrics.go b/p2p/metrics.go index 1fd0f26db30..8c9804206b1 100644 --- a/p2p/metrics.go +++ b/p2p/metrics.go @@ -51,6 +51,10 @@ var ( dialSuccessMeter = metrics.NewRegisteredMeter("p2p/dials/success", nil) dialConnectionError = metrics.NewRegisteredMeter("p2p/dials/error/connection", nil) + // count peers that stayed connected for at least 1 min + serve1MinSuccessMeter = metrics.NewRegisteredMeter("p2p/serves/success/1min", nil) + dial1MinSuccessMeter = metrics.NewRegisteredMeter("p2p/dials/success/1min", nil) + // handshake error meters dialTooManyPeers = metrics.NewRegisteredMeter("p2p/dials/error/saturated", nil) dialAlreadyConnected = metrics.NewRegisteredMeter("p2p/dials/error/known", nil) diff --git a/p2p/peer.go b/p2p/peer.go index a01df63d0c8..9ffb94e5a8a 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -254,6 +254,8 @@ func (p *Peer) run() (remoteRequested bool, err error) { p.wg.Add(2) go p.readLoop(readErr) go p.pingLoop() + live1min := time.NewTimer(1 * time.Minute) + defer live1min.Stop() // Start all protocol handlers. writeStart <- struct{}{} @@ -285,6 +287,12 @@ loop: case err = <-p.disc: reason = discReasonForError(err) break loop + case <-live1min.C: + if p.Inbound() { + serve1MinSuccessMeter.Mark(1) + } else { + dial1MinSuccessMeter.Mark(1) + } } } From c5c75977ab55e4d7ea6147cc0e221b588e5e3754 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Mon, 14 Apr 2025 12:45:27 +0200 Subject: [PATCH 096/658] eth: add logic to drop peers randomly when saturated (#31476) As of now, Geth disconnects peers only on protocol error or timeout, meaning once connection slots are filled, the peerset is largely fixed. As mentioned in https://github.com/ethereum/go-ethereum/issues/31321, Geth should occasionally disconnect peers to ensure some churn. What/when to disconnect could depend on: - the state of geth (e.g. sync or not) - current number of peers - peer level metrics This PR adds a very slow churn using a random drop. --------- Signed-off-by: Csaba Kiraly Co-authored-by: Felix Lange --- eth/backend.go | 7 +++ eth/dropper.go | 167 +++++++++++++++++++++++++++++++++++++++++++++++++ p2p/peer.go | 26 +++++++- p2p/server.go | 10 +-- 4 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 eth/dropper.go diff --git a/eth/backend.go b/eth/backend.go index 6716a77562b..c5dec77962f 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -76,6 +76,7 @@ type Ethereum struct { handler *handler discmix *enode.FairMix + dropper *dropper // DB interfaces chainDb ethdb.Database // Block chain database @@ -300,6 +301,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { return nil, err } + eth.dropper = newDropper(eth.p2pServer.MaxDialedConns(), eth.p2pServer.MaxInboundConns()) + eth.miner = miner.New(eth, config.Miner, eth.engine) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) eth.miner.SetPrioAddresses(config.TxPool.Locals) @@ -410,6 +413,9 @@ func (s *Ethereum) Start() error { // Start the networking layer s.handler.Start(s.p2pServer.MaxPeers) + // Start the connection manager + s.dropper.Start(s.p2pServer, func() bool { return !s.Synced() }) + // start log indexer s.filterMaps.Start() go s.updateFilterMapsHeads() @@ -511,6 +517,7 @@ func (s *Ethereum) setupDiscovery() error { func (s *Ethereum) Stop() error { // Stop all the peer-related stuff first. s.discmix.Close() + s.dropper.Stop() s.handler.Stop() // Then stop everything else. diff --git a/eth/dropper.go b/eth/dropper.go new file mode 100644 index 00000000000..51f2a7a95a3 --- /dev/null +++ b/eth/dropper.go @@ -0,0 +1,167 @@ +// 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 eth + +import ( + mrand "math/rand" + "slices" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/p2p" +) + +const ( + // Interval between peer drop events (uniform between min and max) + peerDropIntervalMin = 3 * time.Minute + // Interval between peer drop events (uniform between min and max) + peerDropIntervalMax = 7 * time.Minute + // Avoid dropping peers for some time after connection + doNotDropBefore = 10 * time.Minute + // How close to max should we initiate the drop timer. O should be fine, + // dropping when no more peers can be added. Larger numbers result in more + // aggressive drop behavior. + peerDropThreshold = 0 +) + +var ( + // droppedInbound is the number of inbound peers dropped + droppedInbound = metrics.NewRegisteredMeter("eth/dropper/inbound", nil) + // droppedOutbound is the number of outbound peers dropped + droppedOutbound = metrics.NewRegisteredMeter("eth/dropper/outbound", nil) +) + +// dropper monitors the state of the peer pool and makes changes as follows: +// - during sync the Downloader handles peer connections, so dropper is disabled +// - if not syncing and the peer count is close to the limit, it drops peers +// randomly every peerDropInterval to make space for new peers +// - peers are dropped separately from the inboud pool and from the dialed pool +type dropper struct { + maxDialPeers int // maximum number of dialed peers + maxInboundPeers int // maximum number of inbound peers + peersFunc getPeersFunc + syncingFunc getSyncingFunc + + // peerDropTimer introduces churn if we are close to limit capacity. + // We handle Dialed and Inbound connections separately + peerDropTimer *time.Timer + + wg sync.WaitGroup // wg for graceful shutdown + shutdownCh chan struct{} +} + +// Callback type to get the list of connected peers. +type getPeersFunc func() []*p2p.Peer + +// Callback type to get syncing status. +// Returns true while syncing, false when synced. +type getSyncingFunc func() bool + +func newDropper(maxDialPeers, maxInboundPeers int) *dropper { + cm := &dropper{ + maxDialPeers: maxDialPeers, + maxInboundPeers: maxInboundPeers, + peerDropTimer: time.NewTimer(randomDuration(peerDropIntervalMin, peerDropIntervalMax)), + shutdownCh: make(chan struct{}), + } + if peerDropIntervalMin > peerDropIntervalMax { + panic("peerDropIntervalMin duration must be less than or equal to peerDropIntervalMax duration") + } + return cm +} + +// Start the dropper. +func (cm *dropper) Start(srv *p2p.Server, syncingFunc getSyncingFunc) { + cm.peersFunc = srv.Peers + cm.syncingFunc = syncingFunc + cm.wg.Add(1) + go cm.loop() +} + +// Stop the dropper. +func (cm *dropper) Stop() { + cm.peerDropTimer.Stop() + close(cm.shutdownCh) + cm.wg.Wait() +} + +// dropRandomPeer selects one of the peers randomly and drops it from the peer pool. +func (cm *dropper) dropRandomPeer() bool { + peers := cm.peersFunc() + var numInbound int + for _, p := range peers { + if p.Inbound() { + numInbound++ + } + } + numDialed := len(peers) - numInbound + + selectDoNotDrop := func(p *p2p.Peer) bool { + // Avoid dropping trusted and static peers, or recent peers. + // Only drop peers if their respective category (dialed/inbound) + // is close to limit capacity. + return p.Trusted() || p.StaticDialed() || + p.Lifetime() < mclock.AbsTime(doNotDropBefore) || + (p.DynDialed() && cm.maxDialPeers-numDialed > peerDropThreshold) || + (p.Inbound() && cm.maxInboundPeers-numInbound > peerDropThreshold) + } + + droppable := slices.DeleteFunc(peers, selectDoNotDrop) + if len(droppable) > 0 { + p := droppable[mrand.Intn(len(droppable))] + log.Debug("Dropping random peer", "inbound", p.Inbound(), + "id", p.ID(), "duration", common.PrettyDuration(p.Lifetime()), "peercountbefore", len(peers)) + p.Disconnect(p2p.DiscUselessPeer) + if p.Inbound() { + droppedInbound.Mark(1) + } else { + droppedOutbound.Mark(1) + } + return true + } + return false +} + +// randomDuration generates a random duration between min and max. +func randomDuration(min, max time.Duration) time.Duration { + if min > max { + panic("min duration must be less than or equal to max duration") + } + return time.Duration(mrand.Int63n(int64(max-min)) + int64(min)) +} + +// loop is the main loop of the connection dropper. +func (cm *dropper) loop() { + defer cm.wg.Done() + + for { + select { + case <-cm.peerDropTimer.C: + // Drop a random peer if we are not syncing and the peer count is close to the limit. + if !cm.syncingFunc() { + cm.dropRandomPeer() + } + cm.peerDropTimer.Reset(randomDuration(peerDropIntervalMin, peerDropIntervalMax)) + case <-cm.shutdownCh: + return + } + } +} diff --git a/p2p/peer.go b/p2p/peer.go index 9ffb94e5a8a..9a0a750ac8b 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -220,11 +220,35 @@ func (p *Peer) String() string { return fmt.Sprintf("Peer %x %v", id[:8], p.RemoteAddr()) } -// Inbound returns true if the peer is an inbound connection +// Inbound returns true if the peer is an inbound (not dialed) connection. func (p *Peer) Inbound() bool { return p.rw.is(inboundConn) } +// Trusted returns true if the peer is configured as trusted. +// Trusted peers are accepted in above the MaxInboundConns limit. +// The peer can be either inbound or dialed. +func (p *Peer) Trusted() bool { + return p.rw.is(trustedConn) +} + +// DynDialed returns true if the peer was dialed successfully (passed handshake) and +// it is not configured as static. +func (p *Peer) DynDialed() bool { + return p.rw.is(dynDialedConn) +} + +// StaticDialed returns true if the peer was dialed successfully (passed handshake) and +// it is configured as static. +func (p *Peer) StaticDialed() bool { + return p.rw.is(staticDialedConn) +} + +// Lifetime returns the time since peer creation. +func (p *Peer) Lifetime() mclock.AbsTime { + return mclock.Now() - p.created +} + func newPeer(log log.Logger, conn *conn, protocols []Protocol) *Peer { protomap := matchProtocols(protocols, conn.caps, conn) p := &Peer{ diff --git a/p2p/server.go b/p2p/server.go index c1564352e5d..4e72e29fa06 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -508,7 +508,7 @@ func (srv *Server) setupDiscovery() error { func (srv *Server) setupDialScheduler() { config := dialConfig{ self: srv.localnode.ID(), - maxDialPeers: srv.maxDialedConns(), + maxDialPeers: srv.MaxDialedConns(), maxActiveDials: srv.MaxPendingPeers, log: srv.Logger, netRestrict: srv.NetRestrict, @@ -527,11 +527,11 @@ func (srv *Server) setupDialScheduler() { } } -func (srv *Server) maxInboundConns() int { - return srv.MaxPeers - srv.maxDialedConns() +func (srv *Server) MaxInboundConns() int { + return srv.MaxPeers - srv.MaxDialedConns() } -func (srv *Server) maxDialedConns() (limit int) { +func (srv *Server) MaxDialedConns() (limit int) { if srv.NoDial || srv.MaxPeers == 0 { return 0 } @@ -736,7 +736,7 @@ func (srv *Server) postHandshakeChecks(peers map[enode.ID]*Peer, inboundCount in switch { case !c.is(trustedConn) && len(peers) >= srv.MaxPeers: return DiscTooManyPeers - case !c.is(trustedConn) && c.is(inboundConn) && inboundCount >= srv.maxInboundConns(): + case !c.is(trustedConn) && c.is(inboundConn) && inboundCount >= srv.MaxInboundConns(): return DiscTooManyPeers case peers[c.node.ID()] != nil: return DiscAlreadyConnected From 48ec86abbbcbe42d66302e9d4839d82cd9ced36f Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 15 Apr 2025 14:32:46 +0200 Subject: [PATCH 097/658] core: initialize history pruning in BlockChain (#31636) I added the history mode configuration in eth/ethconfig initially, since it seemed like the logical place. But it turns out we need access to the intended pruning setting at a deeper level, and it actually needs to be integrated with the blockchain startup procedure. With this change applied, if a node previously had its history pruned, and is subsequently restarted **without** the `--history.chain postmerge` flag, the `BlockChain` initialization code will now verify the freezer tail against the known pruning point of the predefined network and will restore pruning status. Note that this logic is quite restrictive, we allow non-zero tail only for known networks, and only for the specific pruning point that is defined. --- cmd/geth/chaincmd.go | 4 +- cmd/workload/testsuite.go | 6 +- core/block_validator_test.go | 10 ++- core/blockchain.go | 64 ++++++++++++++++++- core/blockchain_reader.go | 6 +- core/blockchain_test.go | 54 +++++++++++----- .../ethconfig => core/history}/historymode.go | 28 ++++---- eth/api_backend.go | 10 +-- eth/backend.go | 44 +++++-------- eth/ethconfig/config.go | 5 +- eth/ethconfig/gen_config.go | 31 +++++++-- eth/filters/api.go | 4 +- eth/filters/filter.go | 4 +- eth/filters/filter_system.go | 4 +- 14 files changed, 186 insertions(+), 88 deletions(-) rename {eth/ethconfig => core/history}/historymode.go (79%) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 22795095424..c57a9a947dd 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -31,11 +31,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/history" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/era" @@ -625,7 +625,7 @@ func pruneHistory(ctx *cli.Context) error { defer chain.Stop() // Determine the prune point. This will be the first PoS block. - prunePoint, ok := ethconfig.HistoryPrunePoints[chain.Genesis().Hash()] + prunePoint, ok := history.PrunePoints[chain.Genesis().Hash()] if !ok || prunePoint == nil { return errors.New("prune point not found") } diff --git a/cmd/workload/testsuite.go b/cmd/workload/testsuite.go index e7019e20558..e8e25e77314 100644 --- a/cmd/workload/testsuite.go +++ b/cmd/workload/testsuite.go @@ -23,7 +23,7 @@ import ( "os" "slices" - "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/core/history" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/internal/utesting" "github.com/ethereum/go-ethereum/log" @@ -124,13 +124,13 @@ func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) { cfg.filterQueryFile = "queries/filter_queries_mainnet.json" cfg.historyTestFile = "queries/history_mainnet.json" cfg.historyPruneBlock = new(uint64) - *cfg.historyPruneBlock = ethconfig.HistoryPrunePoints[params.MainnetGenesisHash].BlockNumber + *cfg.historyPruneBlock = history.PrunePoints[params.MainnetGenesisHash].BlockNumber case ctx.Bool(testSepoliaFlag.Name): cfg.fsys = builtinTestFiles cfg.filterQueryFile = "queries/filter_queries_sepolia.json" cfg.historyTestFile = "queries/history_sepolia.json" cfg.historyPruneBlock = new(uint64) - *cfg.historyPruneBlock = ethconfig.HistoryPrunePoints[params.SepoliaGenesisHash].BlockNumber + *cfg.historyPruneBlock = history.PrunePoints[params.SepoliaGenesisHash].BlockNumber default: cfg.fsys = os.DirFS(".") cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name) diff --git a/core/block_validator_test.go b/core/block_validator_test.go index 8af4057693e..5217979236c 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -50,8 +50,11 @@ func testHeaderVerification(t *testing.T, scheme string) { headers[i] = block.Header() } // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces - chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) + chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) defer chain.Stop() + if err != nil { + t.Fatal(err) + } for i := 0; i < len(blocks); i++ { for j, valid := range []bool{true, false} { @@ -163,8 +166,11 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) { postHeaders[i] = block.Header() } // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces - chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil) + chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil) defer chain.Stop() + if err != nil { + t.Fatal(err) + } // Verify the blocks before the merging for i := 0; i < len(preBlocks); i++ { diff --git a/core/blockchain.go b/core/blockchain.go index d56996dadbe..901a93315af 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" + "github.com/ethereum/go-ethereum/core/history" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state/snapshot" @@ -158,8 +159,7 @@ type CacheConfig struct { // This defines the cutoff block for history expiry. // Blocks before this number may be unavailable in the chain database. - HistoryPruningCutoffNumber uint64 - HistoryPruningCutoffHash common.Hash + ChainHistoryMode history.HistoryMode } // triedbConfig derives the configures for trie database. @@ -255,6 +255,7 @@ type BlockChain struct { currentSnapBlock atomic.Pointer[types.Header] // Current head of snap-sync currentFinalBlock atomic.Pointer[types.Header] // Latest (consensus) finalized block currentSafeBlock atomic.Pointer[types.Header] // Latest (consensus) safe block + historyPrunePoint atomic.Pointer[history.PrunePoint] bodyCache *lru.Cache[common.Hash, *types.Body] bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue] @@ -533,6 +534,12 @@ func (bc *BlockChain) loadLastState() error { } bc.hc.SetCurrentHeader(headHeader) + // Initialize history pruning. + latest := max(headBlock.NumberU64(), headHeader.Number.Uint64()) + if err := bc.initializeHistoryPruning(latest); err != nil { + return err + } + // Restore the last known head snap block bc.currentSnapBlock.Store(headBlock.Header()) headFastBlockGauge.Update(int64(headBlock.NumberU64())) @@ -555,6 +562,7 @@ func (bc *BlockChain) loadLastState() error { headSafeBlockGauge.Update(int64(block.NumberU64())) } } + // Issue a status log for the user var ( currentSnapBlock = bc.CurrentSnapBlock() @@ -573,9 +581,57 @@ func (bc *BlockChain) loadLastState() error { if pivot := rawdb.ReadLastPivotNumber(bc.db); pivot != nil { log.Info("Loaded last snap-sync pivot marker", "number", *pivot) } + if pruning := bc.historyPrunePoint.Load(); pruning != nil { + log.Info("Chain history is pruned", "earliest", pruning.BlockNumber, "hash", pruning.BlockHash) + } return nil } +// initializeHistoryPruning sets bc.historyPrunePoint. +func (bc *BlockChain) initializeHistoryPruning(latest uint64) error { + freezerTail, _ := bc.db.Tail() + + switch bc.cacheConfig.ChainHistoryMode { + case history.KeepAll: + if freezerTail == 0 { + return nil + } + // The database was pruned somehow, so we need to figure out if it's a known + // configuration or an error. + predefinedPoint := history.PrunePoints[bc.genesisBlock.Hash()] + if predefinedPoint == nil || freezerTail != predefinedPoint.BlockNumber { + log.Error("Chain history database is pruned with unknown configuration", "tail", freezerTail) + return fmt.Errorf("unexpected database tail") + } + bc.historyPrunePoint.Store(predefinedPoint) + return nil + + case history.KeepPostMerge: + if freezerTail == 0 && latest != 0 { + // This is the case where a user is trying to run with --history.chain + // postmerge directly on an existing DB. We could just trigger the pruning + // here, but it'd be a bit dangerous since they may not have intended this + // action to happen. So just tell them how to do it. + log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is not pruned.", bc.cacheConfig.ChainHistoryMode.String())) + log.Error(fmt.Sprintf("Run 'geth prune-history' to prune pre-merge history.")) + return fmt.Errorf("history pruning requested via configuration") + } + predefinedPoint := history.PrunePoints[bc.genesisBlock.Hash()] + if predefinedPoint == nil { + log.Error("Chain history pruning is not supported for this network", "genesis", bc.genesisBlock.Hash()) + return fmt.Errorf("history pruning requested for unknown network") + } else if freezerTail != predefinedPoint.BlockNumber { + log.Error("Chain history database is pruned to unknown block", "tail", freezerTail) + return fmt.Errorf("unexpected database tail") + } + bc.historyPrunePoint.Store(predefinedPoint) + return nil + + default: + return fmt.Errorf("invalid history mode: %d", bc.cacheConfig.ChainHistoryMode) + } +} + // SetHead rewinds the local chain to a new head. Depending on whether the node // was snap synced or full synced and in which state, the method will try to // delete minimal data from disk whilst retaining chain consistency. @@ -1014,7 +1070,9 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error { bc.hc.SetCurrentHeader(bc.genesisBlock.Header()) bc.currentSnapBlock.Store(bc.genesisBlock.Header()) headFastBlockGauge.Update(int64(bc.genesisBlock.NumberU64())) - return nil + + // Reset history pruning status. + return bc.initializeHistoryPruning(0) } // Export writes the active chain to the given writer. diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 41147234699..a8c2e26d18b 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -410,7 +410,11 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) { // HistoryPruningCutoff returns the configured history pruning point. // Blocks before this might not be available in the database. func (bc *BlockChain) HistoryPruningCutoff() (uint64, common.Hash) { - return bc.cacheConfig.HistoryPruningCutoffNumber, bc.cacheConfig.HistoryPruningCutoffHash + pt := bc.historyPrunePoint.Load() + if pt == nil { + return 0, bc.genesisBlock.Hash() + } + return pt.BlockNumber, pt.BlockHash } // TrieDB retrieves the low level trie database used for data storage. diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 3f7c03b93c4..289eff0a8f5 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/history" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -4257,13 +4258,7 @@ func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) { // be persisted without the receipts and bodies; chain after should be persisted // normally. func TestInsertChainWithCutoff(t *testing.T) { - testInsertChainWithCutoff(t, 32, 32) // cutoff = 32, ancientLimit = 32 - testInsertChainWithCutoff(t, 32, 64) // cutoff = 32, ancientLimit = 64 (entire chain in ancient) - testInsertChainWithCutoff(t, 32, 65) // cutoff = 32, ancientLimit = 65 (64 blocks in ancient, 1 block in live) -} - -func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64) { - // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true))) + const chainLength = 64 // Configure and generate a sample block chain var ( @@ -4278,24 +4273,51 @@ func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64) signer = types.LatestSigner(gspec.Config) engine = beacon.New(ethash.NewFaker()) ) - _, blocks, receipts := GenerateChainWithGenesis(gspec, engine, int(2*cutoff), func(i int, block *BlockGen) { + _, blocks, receipts := GenerateChainWithGenesis(gspec, engine, chainLength, func(i int, block *BlockGen) { block.SetCoinbase(common.Address{0x00}) - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key) if err != nil { panic(err) } block.AddTx(tx) }) - db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) - defer db.Close() + // Run the actual tests. + t.Run("cutoff-32/ancientLimit-32", func(t *testing.T) { + // cutoff = 32, ancientLimit = 32 + testInsertChainWithCutoff(t, 32, 32, gspec, blocks, receipts) + }) + t.Run("cutoff-32/ancientLimit-64", func(t *testing.T) { + // cutoff = 32, ancientLimit = 64 (entire chain in ancient) + testInsertChainWithCutoff(t, 32, 64, gspec, blocks, receipts) + }) + t.Run("cutoff-32/ancientLimit-64", func(t *testing.T) { + // cutoff = 32, ancientLimit = 65 (64 blocks in ancient, 1 block in live) + testInsertChainWithCutoff(t, 32, 65, gspec, blocks, receipts) + }) +} + +func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64, genesis *Genesis, blocks []*types.Block, receipts []types.Receipts) { + // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true))) + + // Add a known pruning point for the duration of the test. + ghash := genesis.ToBlock().Hash() cutoffBlock := blocks[cutoff-1] + history.PrunePoints[ghash] = &history.PrunePoint{ + BlockNumber: cutoffBlock.NumberU64(), + BlockHash: cutoffBlock.Hash(), + } + defer func() { + delete(history.PrunePoints, ghash) + }() + + // Enable pruning in cache config. config := DefaultCacheConfigWithScheme(rawdb.PathScheme) - config.HistoryPruningCutoffNumber = cutoffBlock.NumberU64() - config.HistoryPruningCutoffHash = cutoffBlock.Hash() + config.ChainHistoryMode = history.KeepPostMerge - chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) + db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + defer db.Close() + chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), genesis, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) defer chain.Stop() var ( @@ -4326,8 +4348,8 @@ func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64) t.Errorf("head header #%d: header mismatch: want: %v, got: %v", headHeader.Number, blocks[len(blocks)-1].Hash(), headHeader.Hash()) } headBlock := chain.CurrentBlock() - if headBlock.Hash() != gspec.ToBlock().Hash() { - t.Errorf("head block #%d: header mismatch: want: %v, got: %v", headBlock.Number, gspec.ToBlock().Hash(), headBlock.Hash()) + if headBlock.Hash() != ghash { + t.Errorf("head block #%d: header mismatch: want: %v, got: %v", headBlock.Number, ghash, headBlock.Hash()) } // Iterate over all chain data components, and cross reference diff --git a/eth/ethconfig/historymode.go b/core/history/historymode.go similarity index 79% rename from eth/ethconfig/historymode.go rename to core/history/historymode.go index a595d72feb3..e735222d372 100644 --- a/eth/ethconfig/historymode.go +++ b/core/history/historymode.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package ethconfig +package history import ( "fmt" @@ -27,22 +27,22 @@ import ( type HistoryMode uint32 const ( - // AllHistory (default) means that all chain history down to genesis block will be kept. - AllHistory HistoryMode = iota + // KeepAll (default) means that all chain history down to genesis block will be kept. + KeepAll HistoryMode = iota - // PostMergeHistory sets the history pruning point to the merge activation block. - PostMergeHistory + // KeepPostMerge sets the history pruning point to the merge activation block. + KeepPostMerge ) func (m HistoryMode) IsValid() bool { - return m <= PostMergeHistory + return m <= KeepPostMerge } func (m HistoryMode) String() string { switch m { - case AllHistory: + case KeepAll: return "all" - case PostMergeHistory: + case KeepPostMerge: return "postmerge" default: return fmt.Sprintf("invalid HistoryMode(%d)", m) @@ -61,24 +61,24 @@ func (m HistoryMode) MarshalText() ([]byte, error) { func (m *HistoryMode) UnmarshalText(text []byte) error { switch string(text) { case "all": - *m = AllHistory + *m = KeepAll case "postmerge": - *m = PostMergeHistory + *m = KeepPostMerge default: return fmt.Errorf(`unknown sync mode %q, want "all" or "postmerge"`, text) } return nil } -type HistoryPrunePoint struct { +type PrunePoint struct { BlockNumber uint64 BlockHash common.Hash } -// HistoryPrunePoints contains the pre-defined history pruning cutoff blocks for known networks. +// PrunePoints the pre-defined history pruning cutoff blocks for known networks. // They point to the first post-merge block. Any pruning should truncate *up to* but excluding // given block. -var HistoryPrunePoints = map[common.Hash]*HistoryPrunePoint{ +var PrunePoints = map[common.Hash]*PrunePoint{ // mainnet params.MainnetGenesisHash: { BlockNumber: 15537393, @@ -91,7 +91,7 @@ var HistoryPrunePoints = map[common.Hash]*HistoryPrunePoint{ }, } -// PrunedHistoryError is returned when the requested history is pruned. +// PrunedHistoryError is returned by APIs when the requested history is pruned. type PrunedHistoryError struct{} func (e *PrunedHistoryError) Error() string { return "pruned history unavailable" } diff --git a/eth/api_backend.go b/eth/api_backend.go index b39dd4cbdb2..182c081d2b3 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -29,12 +29,12 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/filtermaps" + "github.com/ethereum/go-ethereum/core/history" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/ethdb" @@ -156,7 +156,7 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe } block := b.eth.blockchain.GetBlockByNumber(bn) if block == nil && bn < b.HistoryPruningCutoff() { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } return block, nil } @@ -168,7 +168,7 @@ func (b *EthAPIBackend) BlockByHash(ctx context.Context, hash common.Hash) (*typ } block := b.eth.blockchain.GetBlock(hash, *number) if block == nil && *number < b.HistoryPruningCutoff() { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } return block, nil } @@ -181,7 +181,7 @@ func (b *EthAPIBackend) GetBody(ctx context.Context, hash common.Hash, number rp body := b.eth.blockchain.GetBody(hash) if body == nil { if uint64(number) < b.HistoryPruningCutoff() { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } return nil, errors.New("block body not found") } @@ -203,7 +203,7 @@ func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash r block := b.eth.blockchain.GetBlock(hash, header.Number.Uint64()) if block == nil { if header.Number.Uint64() < b.HistoryPruningCutoff() { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } return nil, errors.New("header found, but block body is missing") } diff --git a/eth/backend.go b/eth/backend.go index c5dec77962f..6d1b6bae991 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -145,7 +145,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { // Here we determine genesis hash and active ChainConfig. // We need these to figure out the consensus parameters and to set up history pruning. - chainConfig, genesisHash, err := core.LoadChainConfig(chainDb, config.Genesis) + chainConfig, _, err := core.LoadChainConfig(chainDb, config.Genesis) if err != nil { return nil, err } @@ -153,22 +153,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err != nil { return nil, err } - - // Validate history pruning configuration. - var ( - cutoffNumber uint64 - cutoffHash common.Hash - ) - if config.HistoryMode == ethconfig.PostMergeHistory { - prunecfg, ok := ethconfig.HistoryPrunePoints[genesisHash] - if !ok { - return nil, fmt.Errorf("no history pruning point is defined for genesis %x", genesisHash) - } - cutoffNumber = prunecfg.BlockNumber - cutoffHash = prunecfg.BlockHash - log.Info("Chain cutoff configured", "number", cutoffNumber, "hash", cutoffHash) - } - // Set networkID to chainID by default. networkID := config.NetworkId if networkID == 0 { @@ -195,6 +179,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { } log.Info("Initialising Ethereum protocol", "network", networkID, "dbversion", dbVer) + // Create BlockChain object. if !config.SkipBcVersionCheck { if bcVersion != nil && *bcVersion > core.BlockChainVersion { return nil, fmt.Errorf("database version is v%d, Geth %s only supports v%d", *bcVersion, version.WithMeta, core.BlockChainVersion) @@ -210,17 +195,16 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { EnablePreimageRecording: config.EnablePreimageRecording, } cacheConfig = &core.CacheConfig{ - TrieCleanLimit: config.TrieCleanCache, - TrieCleanNoPrefetch: config.NoPrefetch, - TrieDirtyLimit: config.TrieDirtyCache, - TrieDirtyDisabled: config.NoPruning, - TrieTimeLimit: config.TrieTimeout, - SnapshotLimit: config.SnapshotCache, - Preimages: config.Preimages, - StateHistory: config.StateHistory, - StateScheme: scheme, - HistoryPruningCutoffNumber: cutoffNumber, - HistoryPruningCutoffHash: cutoffHash, + TrieCleanLimit: config.TrieCleanCache, + TrieCleanNoPrefetch: config.NoPrefetch, + TrieDirtyLimit: config.TrieDirtyCache, + TrieDirtyDisabled: config.NoPruning, + TrieTimeLimit: config.TrieTimeout, + SnapshotLimit: config.SnapshotCache, + Preimages: config.Preimages, + StateHistory: config.StateHistory, + StateScheme: scheme, + ChainHistoryMode: config.HistoryMode, } ) if config.VMTrace != "" { @@ -246,6 +230,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err != nil { return nil, err } + + // Initialize filtermaps log index. fmConfig := filtermaps.Config{ History: config.LogHistory, Disabled: config.LogNoHistory, @@ -261,6 +247,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { eth.filterMaps = filtermaps.NewFilterMaps(chainDb, chainView, historyCutoff, finalBlock, filtermaps.DefaultParams, fmConfig) eth.closeFilterMaps = make(chan chan struct{}) + // TxPool if config.TxPool.Journal != "" { config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal) } @@ -285,6 +272,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { eth.localTxTracker = locals.New(config.TxPool.Journal, rejournal, eth.blockchain.Config(), eth.txPool) stack.RegisterLifecycle(eth.localTxTracker) } + // Permit the downloader to use the trie cache allowance during fast sync cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit if eth.handler, err = newHandler(&handlerConfig{ diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 365857347c7..5e198241352 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/history" "github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/eth/gasprice" @@ -48,7 +49,7 @@ var FullNodeGPO = gasprice.Config{ // Defaults contains default settings for use on the Ethereum main net. var Defaults = Config{ - HistoryMode: AllHistory, + HistoryMode: history.KeepAll, SyncMode: SnapSync, NetworkId: 0, // enable auto configuration of networkID == chainID TxLookupLimit: 2350000, @@ -84,7 +85,7 @@ type Config struct { SyncMode SyncMode // HistoryMode configures chain history retention. - HistoryMode HistoryMode + HistoryMode history.HistoryMode // This can be set to list of enrtree:// URLs which will be queried for // nodes to connect to. diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index bcfe8a31d62..cdcddcc7720 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/history" "github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/eth/gasprice" @@ -19,13 +20,16 @@ func (c Config) MarshalTOML() (interface{}, error) { Genesis *core.Genesis `toml:",omitempty"` NetworkId uint64 SyncMode SyncMode - HistoryMode HistoryMode + HistoryMode history.HistoryMode EthDiscoveryURLs []string SnapDiscoveryURLs []string NoPruning bool NoPrefetch bool - TxLookupLimit uint64 `toml:",omitempty"` - TransactionHistory uint64 `toml:",omitempty"` + TxLookupLimit uint64 `toml:",omitempty"` + TransactionHistory uint64 `toml:",omitempty"` + LogHistory uint64 `toml:",omitempty"` + LogNoHistory bool `toml:",omitempty"` + LogExportCheckpoints string StateHistory uint64 `toml:",omitempty"` StateScheme string `toml:",omitempty"` RequiredBlocks map[uint64]common.Hash `toml:"-"` @@ -63,6 +67,9 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.NoPrefetch = c.NoPrefetch enc.TxLookupLimit = c.TxLookupLimit enc.TransactionHistory = c.TransactionHistory + enc.LogHistory = c.LogHistory + enc.LogNoHistory = c.LogNoHistory + enc.LogExportCheckpoints = c.LogExportCheckpoints enc.StateHistory = c.StateHistory enc.StateScheme = c.StateScheme enc.RequiredBlocks = c.RequiredBlocks @@ -97,13 +104,16 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { Genesis *core.Genesis `toml:",omitempty"` NetworkId *uint64 SyncMode *SyncMode - HistoryMode *HistoryMode + HistoryMode *history.HistoryMode EthDiscoveryURLs []string SnapDiscoveryURLs []string NoPruning *bool NoPrefetch *bool - TxLookupLimit *uint64 `toml:",omitempty"` - TransactionHistory *uint64 `toml:",omitempty"` + TxLookupLimit *uint64 `toml:",omitempty"` + TransactionHistory *uint64 `toml:",omitempty"` + LogHistory *uint64 `toml:",omitempty"` + LogNoHistory *bool `toml:",omitempty"` + LogExportCheckpoints *string StateHistory *uint64 `toml:",omitempty"` StateScheme *string `toml:",omitempty"` RequiredBlocks map[uint64]common.Hash `toml:"-"` @@ -164,6 +174,15 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.TransactionHistory != nil { c.TransactionHistory = *dec.TransactionHistory } + if dec.LogHistory != nil { + c.LogHistory = *dec.LogHistory + } + if dec.LogNoHistory != nil { + c.LogNoHistory = *dec.LogNoHistory + } + if dec.LogExportCheckpoints != nil { + c.LogExportCheckpoints = *dec.LogExportCheckpoints + } if dec.StateHistory != nil { c.StateHistory = *dec.StateHistory } diff --git a/eth/filters/api.go b/eth/filters/api.go index 6593cbef275..864dfd3746d 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -28,8 +28,8 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/history" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/rpc" ) @@ -360,7 +360,7 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type return nil, errInvalidBlockRange } if begin > 0 && begin < int64(api.events.backend.HistoryPruningCutoff()) { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } // Construct the range filter filter = api.sys.NewRangeFilter(begin, end, crit.Addresses, crit.Topics) diff --git a/eth/filters/filter.go b/eth/filters/filter.go index e44b37d0473..78c80d8f264 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -26,8 +26,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/filtermaps" + "github.com/ethereum/go-ethereum/core/history" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" ) @@ -88,7 +88,7 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { return nil, errors.New("unknown block") } if header.Number.Uint64() < f.sys.backend.HistoryPruningCutoff() { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } return f.blockLogs(ctx, header) } diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index aca3c03ad64..b787f1067b8 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -30,8 +30,8 @@ import ( "github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/filtermaps" + "github.com/ethereum/go-ethereum/core/history" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" @@ -311,7 +311,7 @@ func (es *EventSystem) SubscribeLogs(crit ethereum.FilterQuery, logs chan []*typ } // Queries beyond the pruning cutoff are not supported. if uint64(from) < es.backend.HistoryPruningCutoff() { - return nil, ðconfig.PrunedHistoryError{} + return nil, &history.PrunedHistoryError{} } // only interested in new mined logs From 476f117211d0618ac3fa762a347089005fa90d2e Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Tue, 15 Apr 2025 20:34:34 +0800 Subject: [PATCH 098/658] all: remove martin from CODEOWNERS (#31637) Thank you, @holiman, for being an integral part of the Go-Ethereum and for your invaluable contributions over the years. This will always be your home and you're welcome back anytime! --- .github/CODEOWNERS | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b5691808529..681b5e7c668 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -9,12 +9,11 @@ beacon/light/ @zsfelfoldi beacon/merkle/ @zsfelfoldi beacon/types/ @zsfelfoldi @fjl beacon/params/ @zsfelfoldi @fjl -cmd/clef/ @holiman -cmd/evm/ @holiman @MariusVanDerWijden @lightclient -core/state/ @rjl493456442 @holiman -crypto/ @gballet @jwasinger @holiman @fjl -core/ @holiman @rjl493456442 -eth/ @holiman @rjl493456442 +cmd/evm/ @MariusVanDerWijden @lightclient +core/state/ @rjl493456442 +crypto/ @gballet @jwasinger @fjl +core/ @rjl493456442 +eth/ @rjl493456442 eth/catalyst/ @MariusVanDerWijden @lightclient @fjl @jwasinger eth/tracers/ @s1na ethclient/ @fjl @@ -26,11 +25,9 @@ core/tracing/ @s1na graphql/ @s1na internal/ethapi/ @fjl @s1na @lightclient internal/era/ @lightclient -metrics/ @holiman -miner/ @MariusVanDerWijden @holiman @fjl @rjl493456442 +miner/ @MariusVanDerWijden @fjl @rjl493456442 node/ @fjl p2p/ @fjl @zsfelfoldi rlp/ @fjl -params/ @fjl @holiman @karalabe @gballet @rjl493456442 @zsfelfoldi -rpc/ @fjl @holiman -signer/ @holiman +params/ @fjl @karalabe @gballet @rjl493456442 @zsfelfoldi +rpc/ @fjl From 6928ec5d924604cc403a74ef3bbca29505443ab6 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 15 Apr 2025 20:40:30 +0200 Subject: [PATCH 099/658] p2p: fix dial metrics not picking up the right error (#31621) Our metrics related to dial errors were off. The original error was not wrapped, so the caller function had no chance of picking it up. Therefore the most common error, which is "TooManyPeers", was not correctly counted. The metrics were originally introduced in https://github.com/ethereum/go-ethereum/pull/27621 I was thinking of various possible solutions. - the one proposed here wraps both the new error and the origial error. It is not a pattern we use in other parts of the code, but works. This is maybe the smallest possible change. - as an alternate, I could write a proper `errProtoHandshakeError` with it's own wrapped error - finally, I'm not even sure we need `errProtoHandshakeError`, maybe we could just pass up the original error. --------- Signed-off-by: Csaba Kiraly Co-authored-by: Felix Lange --- p2p/metrics.go | 41 ++++++++++++++++++++++++----------------- p2p/server.go | 12 ++++++++---- p2p/server_test.go | 4 ++-- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/p2p/metrics.go b/p2p/metrics.go index 8c9804206b1..29c2acb0cba 100644 --- a/p2p/metrics.go +++ b/p2p/metrics.go @@ -49,7 +49,7 @@ var ( serveSuccessMeter = metrics.NewRegisteredMeter("p2p/serves/success", nil) dialMeter = metrics.NewRegisteredMeter("p2p/dials", nil) dialSuccessMeter = metrics.NewRegisteredMeter("p2p/dials/success", nil) - dialConnectionError = metrics.NewRegisteredMeter("p2p/dials/error/connection", nil) + dialConnectionError = metrics.NewRegisteredMeter("p2p/dials/error/connection", nil) // dial timeout; no route to host; connection refused; network is unreachable // count peers that stayed connected for at least 1 min serve1MinSuccessMeter = metrics.NewRegisteredMeter("p2p/serves/success/1min", nil) @@ -61,34 +61,41 @@ var ( dialSelf = metrics.NewRegisteredMeter("p2p/dials/error/self", nil) dialUselessPeer = metrics.NewRegisteredMeter("p2p/dials/error/useless", nil) dialUnexpectedIdentity = metrics.NewRegisteredMeter("p2p/dials/error/id/unexpected", nil) - dialEncHandshakeError = metrics.NewRegisteredMeter("p2p/dials/error/rlpx/enc", nil) - dialProtoHandshakeError = metrics.NewRegisteredMeter("p2p/dials/error/rlpx/proto", nil) + dialEncHandshakeError = metrics.NewRegisteredMeter("p2p/dials/error/rlpx/enc", nil) // EOF; connection reset during handshake; message too big; i/o timeout + dialProtoHandshakeError = metrics.NewRegisteredMeter("p2p/dials/error/rlpx/proto", nil) // EOF + + // capture the rest of errors that are not handled by the above meters + dialOtherError = metrics.NewRegisteredMeter("p2p/dials/error/other", nil) ) -// markDialError matches errors that occur while setting up a dial connection -// to the corresponding meter. +// markDialError matches errors that occur while setting up a dial connection to the +// corresponding meter. We don't maintain meters for evert possible error, just for +// the most interesting ones. func markDialError(err error) { if !metrics.Enabled() { return } - if err2 := errors.Unwrap(err); err2 != nil { - err = err2 - } - switch err { - case DiscTooManyPeers: + + var reason DiscReason + var handshakeErr *protoHandshakeError + d := errors.As(err, &reason) + switch { + case d && reason == DiscTooManyPeers: dialTooManyPeers.Mark(1) - case DiscAlreadyConnected: + case d && reason == DiscAlreadyConnected: dialAlreadyConnected.Mark(1) - case DiscSelf: + case d && reason == DiscSelf: dialSelf.Mark(1) - case DiscUselessPeer: + case d && reason == DiscUselessPeer: dialUselessPeer.Mark(1) - case DiscUnexpectedIdentity: + case d && reason == DiscUnexpectedIdentity: dialUnexpectedIdentity.Mark(1) - case errEncHandshakeError: - dialEncHandshakeError.Mark(1) - case errProtoHandshakeError: + case errors.As(err, &handshakeErr): dialProtoHandshakeError.Mark(1) + case errors.Is(err, errEncHandshakeError): + dialEncHandshakeError.Mark(1) + default: + dialOtherError.Mark(1) } } diff --git a/p2p/server.go b/p2p/server.go index 4e72e29fa06..d9105976dd2 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -66,11 +66,15 @@ const ( ) var ( - errServerStopped = errors.New("server stopped") - errEncHandshakeError = errors.New("rlpx enc error") - errProtoHandshakeError = errors.New("rlpx proto error") + errServerStopped = errors.New("server stopped") + errEncHandshakeError = errors.New("rlpx enc error") ) +type protoHandshakeError struct{ err error } + +func (e *protoHandshakeError) Error() string { return fmt.Sprintf("rlpx proto error: %v", e.err) } +func (e *protoHandshakeError) Unwrap() error { return e.err } + // Server manages all peer connections. type Server struct { // Config fields may not be modified while the server is running. @@ -907,7 +911,7 @@ func (srv *Server) setupConn(c *conn, dialDest *enode.Node) error { phs, err := c.doProtoHandshake(srv.ourHandshake) if err != nil { clog.Trace("Failed p2p handshake", "err", err) - return fmt.Errorf("%w: %v", errProtoHandshakeError, err) + return &protoHandshakeError{err: err} } if id := c.node.ID(); !bytes.Equal(crypto.Keccak256(phs.ID), id[:]) { clog.Trace("Wrong devp2p handshake identity", "phsid", hex.EncodeToString(phs.ID)) diff --git a/p2p/server_test.go b/p2p/server_test.go index a0491e984a7..d42926cf4c6 100644 --- a/p2p/server_test.go +++ b/p2p/server_test.go @@ -410,11 +410,11 @@ func TestServerSetupConn(t *testing.T) { wantCloseErr: DiscUnexpectedIdentity, }, { - tt: &setupTransport{pubkey: clientpub, protoHandshakeErr: errProtoHandshakeError}, + tt: &setupTransport{pubkey: clientpub, protoHandshakeErr: DiscTooManyPeers}, dialDest: enode.NewV4(clientpub, nil, 0, 0), flags: dynDialedConn, wantCalls: "doEncHandshake,doProtoHandshake,close,", - wantCloseErr: errProtoHandshakeError, + wantCloseErr: DiscTooManyPeers, }, { tt: &setupTransport{pubkey: srvpub, phs: protoHandshake{ID: crypto.FromECDSAPub(srvpub)[1:]}}, From e3e9d7ccb677fe687f11d63c96be4b1b09211695 Mon Sep 17 00:00:00 2001 From: Delweng Date: Wed, 16 Apr 2025 15:50:05 +0800 Subject: [PATCH 100/658] cmd/geth: remove the unused bloomfilter.size flag (#31646) --- cmd/geth/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index ab46e059f39..289030ae659 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -100,7 +100,6 @@ var ( utils.LightNoSyncServeFlag, // deprecated utils.EthRequiredBlocksFlag, utils.LegacyWhitelistFlag, // deprecated - utils.BloomFilterSizeFlag, utils.CacheFlag, utils.CacheDatabaseFlag, utils.CacheTrieFlag, From ebb3eb29d33770543fc0864a82e47a24d1c3e460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Wed, 16 Apr 2025 23:30:13 +0200 Subject: [PATCH 101/658] core/filtermaps: fix map renderer reorg issue (#31642) This PR fixes a bug in the map renderer that sometimes used an obsolete block log value pointer to initialize the iterator for rendering from a snapshot. This bug was triggered by chain reorgs and sometimes caused indexing errors and invalid search results. A few other conditions are also made safer that were not reported to cause issues yet but could potentially be unsafe in some corner cases. A new unit test is also added that reproduced the bug but passes with the new fixes. Fixes https://github.com/ethereum/go-ethereum/issues/31593 Might also fix https://github.com/ethereum/go-ethereum/issues/31589 though this issue has not been reproduced yet, but it appears to be related to a log index database corruption around a specific block, similarly to the other issue. Note that running this branch resets and regenerates the log index database. For this purpose a `Version` field has been added to `rawdb.FilterMapsRange` which will also make this easier in the future if a breaking database change is needed or the existing one is considered potentially broken due to a bug, like in this case. --- core/filtermaps/filtermaps.go | 27 +++++-- core/filtermaps/indexer_test.go | 110 +++++++++++++++++++++++++++++ core/filtermaps/map_renderer.go | 45 +++++------- core/filtermaps/matcher_backend.go | 3 + core/rawdb/accessors_indexes.go | 1 + 5 files changed, 153 insertions(+), 33 deletions(-) diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index 18b1c7dc790..fa2d6e3ffb9 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -50,6 +50,7 @@ var ( ) const ( + databaseVersion = 1 // reindexed if database version does not match cachedLastBlocks = 1000 // last block of map pointers cachedLvPointers = 1000 // first log value pointer of block pointers cachedBaseRows = 100 // groups of base layer filter row data @@ -138,13 +139,25 @@ type FilterMaps struct { // as transparent (uncached/unchanged). type filterMap []FilterRow -// copy returns a copy of the given filter map. Note that the row slices are -// copied but their contents are not. This permits extending the rows further +// fastCopy returns a copy of the given filter map. Note that the row slices are +// copied but their contents are not. This permits appending to the rows further // (which happens during map rendering) without affecting the validity of // copies made for snapshots during rendering. -func (fm filterMap) copy() filterMap { +// Appending to the rows of both the original map and the fast copy, or two fast +// copies of the same map would result in data corruption, therefore a fast copy +// should always be used in a read only way. +func (fm filterMap) fastCopy() filterMap { + return slices.Clone(fm) +} + +// fullCopy returns a copy of the given filter map, also making a copy of each +// individual filter row, ensuring that a modification to either one will never +// affect the other. +func (fm filterMap) fullCopy() filterMap { c := make(filterMap, len(fm)) - copy(c, fm) + for i, row := range fm { + c[i] = slices.Clone(row) + } return c } @@ -207,8 +220,9 @@ type Config struct { // NewFilterMaps creates a new FilterMaps and starts the indexer. 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) + if err != nil || rs.Version != databaseVersion { + rs, initialized = rawdb.FilterMapsRange{}, false + log.Warn("Invalid log index database version; resetting log index") } params.deriveFields() f := &FilterMaps{ @@ -437,6 +451,7 @@ func (f *FilterMaps) setRange(batch ethdb.KeyValueWriter, newView *ChainView, ne f.updateMatchersValidRange() if newRange.initialized { rs := rawdb.FilterMapsRange{ + Version: databaseVersion, HeadIndexed: newRange.headIndexed, HeadDelimiter: newRange.headDelimiter, BlocksFirst: newRange.blocks.First(), diff --git a/core/filtermaps/indexer_test.go b/core/filtermaps/indexer_test.go index a02f8d24599..4dddd27087e 100644 --- a/core/filtermaps/indexer_test.go +++ b/core/filtermaps/indexer_test.go @@ -17,8 +17,10 @@ package filtermaps import ( + "context" crand "crypto/rand" "crypto/sha256" + "encoding/binary" "math/big" "math/rand" "sync" @@ -31,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" ) var testParams = Params{ @@ -104,6 +107,7 @@ func TestIndexerRandomRange(t *testing.T) { fork, head = rand.Intn(len(forks)), rand.Intn(1001) ts.chain.setCanonicalChain(forks[fork][:head+1]) case 2: + checkSnapshot = false if head < 1000 { checkSnapshot = !noHistory && head != 0 // no snapshot generated for block 0 // add blocks after the current head @@ -158,6 +162,63 @@ func TestIndexerRandomRange(t *testing.T) { } } +func TestIndexerMatcherView(t *testing.T) { + testIndexerMatcherView(t, false) +} + +func TestIndexerMatcherViewWithConcurrentRead(t *testing.T) { + testIndexerMatcherView(t, true) +} + +func testIndexerMatcherView(t *testing.T, concurrentRead bool) { + ts := newTestSetup(t) + defer ts.close() + + forks := make([][]common.Hash, 20) + hashes := make([]common.Hash, 20) + ts.chain.addBlocks(100, 5, 2, 4, true) + ts.setHistory(0, false) + for i := range forks { + if i != 0 { + ts.chain.setHead(100 - i) + ts.chain.addBlocks(i, 5, 2, 4, true) + } + ts.fm.WaitIdle() + forks[i] = ts.chain.getCanonicalChain() + hashes[i] = ts.matcherViewHash() + } + fork := len(forks) - 1 + for i := 0; i < 5000; i++ { + oldFork := fork + fork = rand.Intn(len(forks)) + stopCh := make(chan chan struct{}) + if concurrentRead { + go func() { + for { + ts.matcherViewHash() + select { + case ch := <-stopCh: + close(ch) + return + default: + } + } + }() + } + ts.chain.setCanonicalChain(forks[fork]) + ts.fm.WaitIdle() + if concurrentRead { + ch := make(chan struct{}) + stopCh <- ch + <-ch + } + hash := ts.matcherViewHash() + if hash != hashes[fork] { + t.Fatalf("Matcher view hash mismatch when switching from for %d to %d", oldFork, fork) + } + } +} + func TestIndexerCompareDb(t *testing.T) { ts := newTestSetup(t) defer ts.close() @@ -291,6 +352,55 @@ func (ts *testSetup) fmDbHash() common.Hash { return result } +func (ts *testSetup) matcherViewHash() common.Hash { + mb := ts.fm.NewMatcherBackend() + defer mb.Close() + + ctx := context.Background() + params := mb.GetParams() + hasher := sha256.New() + var headPtr uint64 + for b := uint64(0); ; b++ { + lvptr, err := mb.GetBlockLvPointer(ctx, b) + if err != nil || (b > 0 && lvptr == headPtr) { + break + } + var enc [8]byte + binary.LittleEndian.PutUint64(enc[:], lvptr) + hasher.Write(enc[:]) + headPtr = lvptr + } + headMap := uint32(headPtr >> params.logValuesPerMap) + var enc [12]byte + for r := uint32(0); r < params.mapHeight; r++ { + binary.LittleEndian.PutUint32(enc[:4], r) + for m := uint32(0); m <= headMap; m++ { + binary.LittleEndian.PutUint32(enc[4:8], m) + row, _ := mb.GetFilterMapRow(ctx, m, r, false) + for _, v := range row { + binary.LittleEndian.PutUint32(enc[8:], v) + hasher.Write(enc[:]) + } + } + } + var hash common.Hash + hasher.Sum(hash[:0]) + for i := 0; i < 50; i++ { + hasher.Reset() + hasher.Write(hash[:]) + lvptr := binary.LittleEndian.Uint64(hash[:8]) % headPtr + if log, _ := mb.GetLogByLvIndex(ctx, lvptr); log != nil { + enc, err := rlp.EncodeToBytes(log) + if err != nil { + panic(err) + } + hasher.Write(enc) + } + hasher.Sum(hash[:0]) + } + return hash +} + func (ts *testSetup) close() { if ts.fm != nil { ts.fm.Stop() diff --git a/core/filtermaps/map_renderer.go b/core/filtermaps/map_renderer.go index 28f943abb31..cd960ad31cf 100644 --- a/core/filtermaps/map_renderer.go +++ b/core/filtermaps/map_renderer.go @@ -84,7 +84,7 @@ func (f *FilterMaps) renderMapsBefore(renderBefore uint32) (*mapRenderer, error) if err != nil { return nil, err } - if snapshot := f.lastCanonicalSnapshotBefore(renderBefore); snapshot != nil && snapshot.mapIndex >= nextMap { + if snapshot := f.lastCanonicalSnapshotOfMap(nextMap); snapshot != nil { return f.renderMapsFromSnapshot(snapshot) } if nextMap >= renderBefore { @@ -97,14 +97,14 @@ func (f *FilterMaps) renderMapsBefore(renderBefore uint32) (*mapRenderer, error) // snapshot made at a block boundary. func (f *FilterMaps) renderMapsFromSnapshot(cp *renderedMap) (*mapRenderer, error) { f.testSnapshotUsed = true - iter, err := f.newLogIteratorFromBlockDelimiter(cp.lastBlock) + iter, err := f.newLogIteratorFromBlockDelimiter(cp.lastBlock, cp.headDelimiter) if err != nil { return nil, fmt.Errorf("failed to create log iterator from block delimiter %d: %v", cp.lastBlock, err) } return &mapRenderer{ f: f, currentMap: &renderedMap{ - filterMap: cp.filterMap.copy(), + filterMap: cp.filterMap.fullCopy(), mapIndex: cp.mapIndex, lastBlock: cp.lastBlock, blockLvPtrs: cp.blockLvPtrs, @@ -137,14 +137,14 @@ func (f *FilterMaps) renderMapsFromMapBoundary(firstMap, renderBefore uint32, st }, nil } -// lastCanonicalSnapshotBefore returns the latest cached snapshot that matches -// the current targetView. -func (f *FilterMaps) lastCanonicalSnapshotBefore(renderBefore uint32) *renderedMap { +// lastCanonicalSnapshotOfMap returns the latest cached snapshot of the given map +// that is also consistent with the current targetView. +func (f *FilterMaps) lastCanonicalSnapshotOfMap(mapIndex uint32) *renderedMap { var best *renderedMap for _, blockNumber := range f.renderSnapshots.Keys() { 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 < renderBefore && (best == nil || blockNumber > best.lastBlock) { + cp.mapIndex == mapIndex && (best == nil || blockNumber > best.lastBlock) { best = cp } } @@ -171,10 +171,9 @@ func (f *FilterMaps) lastCanonicalMapBoundaryBefore(renderBefore uint32) (nextMa if err != nil { return 0, 0, 0, fmt.Errorf("failed to retrieve last block of reverse iterated map %d: %v", mapIndex, err) } - if lastBlock >= f.indexedView.headNumber || lastBlock >= f.targetView.headNumber || - lastBlockId != f.targetView.getBlockId(lastBlock) { - // map is not full or inconsistent with targetView; roll back - continue + if (f.indexedRange.headIndexed && mapIndex >= f.indexedRange.maps.Last()) || + lastBlock >= f.targetView.headNumber || lastBlockId != f.targetView.getBlockId(lastBlock) { + continue // map is not full or inconsistent with targetView; roll back } lvPtr, err := f.getBlockLvPointer(lastBlock) if err != nil { @@ -257,11 +256,14 @@ func (f *FilterMaps) loadHeadSnapshot() error { // makeSnapshot creates a snapshot of the current state of the rendered map. func (r *mapRenderer) makeSnapshot() { - r.f.renderSnapshots.Add(r.iterator.blockNumber, &renderedMap{ - filterMap: r.currentMap.filterMap.copy(), + if r.iterator.blockNumber != r.currentMap.lastBlock || r.iterator.chainView != r.f.targetView { + panic("iterator state inconsistent with current rendered map") + } + r.f.renderSnapshots.Add(r.currentMap.lastBlock, &renderedMap{ + filterMap: r.currentMap.filterMap.fastCopy(), mapIndex: r.currentMap.mapIndex, - lastBlock: r.iterator.blockNumber, - lastBlockId: r.f.targetView.getBlockId(r.currentMap.lastBlock), + lastBlock: r.currentMap.lastBlock, + lastBlockId: r.iterator.chainView.getBlockId(r.currentMap.lastBlock), blockLvPtrs: r.currentMap.blockLvPtrs, finished: true, headDelimiter: r.iterator.lvIndex, @@ -661,24 +663,13 @@ var errUnindexedRange = errors.New("unindexed range") // newLogIteratorFromBlockDelimiter creates a logIterator starting at the // given block's first log value entry (the block delimiter), according to the // current targetView. -func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber uint64) (*logIterator, error) { +func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber, lvIndex uint64) (*logIterator, error) { if blockNumber > f.targetView.headNumber { return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", blockNumber, f.targetView.headNumber) } if !f.indexedRange.blocks.Includes(blockNumber) { return nil, errUnindexedRange } - var lvIndex uint64 - if f.indexedRange.headIndexed && blockNumber+1 == f.indexedRange.blocks.AfterLast() { - lvIndex = f.indexedRange.headDelimiter - } else { - var err error - lvIndex, err = f.getBlockLvPointer(blockNumber + 1) - if err != nil { - return nil, fmt.Errorf("failed to retrieve log value pointer of block %d after delimiter: %v", blockNumber+1, err) - } - lvIndex-- - } finished := blockNumber == f.targetView.headNumber l := &logIterator{ chainView: f.targetView, diff --git a/core/filtermaps/matcher_backend.go b/core/filtermaps/matcher_backend.go index 01bae7bb22d..335ac845510 100644 --- a/core/filtermaps/matcher_backend.go +++ b/core/filtermaps/matcher_backend.go @@ -75,6 +75,9 @@ func (fm *FilterMapsMatcherBackend) Close() { // on write. // GetFilterMapRow implements MatcherBackend. func (fm *FilterMapsMatcherBackend) GetFilterMapRow(ctx context.Context, mapIndex, rowIndex uint32, baseLayerOnly bool) (FilterRow, error) { + fm.f.indexLock.RLock() + defer fm.f.indexLock.RUnlock() + return fm.f.getFilterMapRow(mapIndex, rowIndex, baseLayerOnly) } diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go index c413839b7bf..1a5c414c8e9 100644 --- a/core/rawdb/accessors_indexes.go +++ b/core/rawdb/accessors_indexes.go @@ -434,6 +434,7 @@ func DeleteBlockLvPointers(db ethdb.KeyValueStore, blocks common.Range[uint64], // 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 { + Version uint32 HeadIndexed bool HeadDelimiter uint64 BlocksFirst, BlocksAfterLast uint64 From 846d578cc3c9629f226f70e39c25c461e4cbf143 Mon Sep 17 00:00:00 2001 From: maskpp Date: Thu, 17 Apr 2025 05:46:38 +0800 Subject: [PATCH 102/658] core/state: fix log format (#31610) Log `key` in hexadecimal string format. --- core/state/dump.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/state/dump.go b/core/state/dump.go index c9aad4f8e23..11b5c327827 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -188,7 +188,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] c.OnAccount(address, account) accounts++ if time.Since(logged) > 8*time.Second { - log.Info("Trie dumping in progress", "at", it.Key, "accounts", accounts, + log.Info("Trie dumping in progress", "at", common.Bytes2Hex(it.Key), "accounts", accounts, "elapsed", common.PrettyDuration(time.Since(start))) logged = time.Now() } From 87974974a7b3fcce873851205b889f7d839d7fb7 Mon Sep 17 00:00:00 2001 From: Miro Date: Wed, 16 Apr 2025 22:36:53 -0400 Subject: [PATCH 103/658] core/txpool/legacypool: fix data race of txlookup access (#31641) --- core/txpool/legacypool/legacypool.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 04f1a2234c4..7bf360ff65f 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -1827,6 +1827,16 @@ func (t *lookup) Remove(hash common.Hash) { delete(t.txs, hash) } +// Clear resets the lookup structure, removing all stored entries. +func (t *lookup) Clear() { + t.lock.Lock() + defer t.lock.Unlock() + + t.slots = 0 + t.txs = make(map[common.Hash]*types.Transaction) + t.auths = make(map[common.Address][]common.Hash) +} + // TxsBelowTip finds all remote transactions below the given tip threshold. func (t *lookup) TxsBelowTip(threshold *big.Int) types.Transactions { found := make(types.Transactions, 0, 128) @@ -1923,7 +1933,7 @@ func (pool *LegacyPool) Clear() { for addr := range pool.queue { pool.reserver.Release(addr) } - pool.all = newLookup() + pool.all.Clear() pool.priced = newPricedList(pool.all) pool.pending = make(map[common.Address]*list) pool.queue = make(map[common.Address]*list) From cb21177aa8b15861067c430f12439e40fc1ad124 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 17 Apr 2025 04:39:21 +0200 Subject: [PATCH 104/658] core: fix history pruning initialization for empty DB (#31656) This fixes an issue where running geth with `--history.chain postmerge` would not work on an empty database. ``` ERROR[04-16|23:11:12.913] Chain history database is pruned to unknown block tail=0 Fatal: Failed to register the Ethereum service: unexpected database tail ``` --- core/blockchain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/blockchain.go b/core/blockchain.go index 901a93315af..203dcd26937 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -620,7 +620,7 @@ func (bc *BlockChain) initializeHistoryPruning(latest uint64) error { if predefinedPoint == nil { log.Error("Chain history pruning is not supported for this network", "genesis", bc.genesisBlock.Hash()) return fmt.Errorf("history pruning requested for unknown network") - } else if freezerTail != predefinedPoint.BlockNumber { + } else if freezerTail > 0 && freezerTail != predefinedPoint.BlockNumber { log.Error("Chain history database is pruned to unknown block", "tail", freezerTail) return fmt.Errorf("unexpected database tail") } From e4448233940904e9c36f227b71f274c9df80a250 Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Thu, 17 Apr 2025 10:32:40 +0200 Subject: [PATCH 105/658] core: fix sync reset in pruned nodes (#31638) This is an attempt at fixing #31601. I think what happens is the startup logic will try to get the full block body (it's `bc.loadLastState`) and fail because genesis block has been pruned from the freezer. This will cause it to keep repeating the reset logic, causing a deadlock. This can happen when due to an unsuccessful sync we don't have the state for the head (or any other state) fully, and try to redo the snap sync. --------- Co-authored-by: Gary Rong --- core/blockchain.go | 50 +++++++++++++++++++++++++++++++------------ core/txindexer.go | 19 +++++++++++++--- core/txpool/txpool.go | 14 ++++++------ 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 203dcd26937..c4caec66ed6 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -514,19 +514,33 @@ func (bc *BlockChain) loadLastState() error { log.Warn("Empty database, resetting chain") return bc.Reset() } - // Make sure the entire head block is available - headBlock := bc.GetBlockByHash(head) + headHeader := bc.GetHeaderByHash(head) + if headHeader == nil { + // Corrupt or empty database, init from scratch + log.Warn("Head header missing, resetting chain", "hash", head) + return bc.Reset() + } + + var headBlock *types.Block + if cmp := headHeader.Number.Cmp(new(big.Int)); cmp == 1 { + // Make sure the entire head block is available. + headBlock = bc.GetBlockByHash(head) + } else if cmp == 0 { + // On a pruned node the block body might not be available. But a pruned + // block should never be the head block. The only exception is when, as + // a last resort, chain is reset to genesis. + headBlock = bc.genesisBlock + } if headBlock == nil { // Corrupt or empty database, init from scratch log.Warn("Head block missing, resetting chain", "hash", head) return bc.Reset() } // Everything seems to be fine, set as the head block - bc.currentBlock.Store(headBlock.Header()) + bc.currentBlock.Store(headHeader) headBlockGauge.Update(int64(headBlock.NumberU64())) // Restore the last known head header - headHeader := headBlock.Header() if head := rawdb.ReadHeadHeaderHash(bc.db); head != (common.Hash{}) { if header := bc.GetHeaderByHash(head); header != nil { headHeader = header @@ -642,11 +656,15 @@ func (bc *BlockChain) SetHead(head uint64) error { // Send chain head event to update the transaction pool header := bc.CurrentBlock() if block := bc.GetBlock(header.Hash(), header.Number.Uint64()); block == nil { - // This should never happen. In practice, previously currentBlock - // contained the entire block whereas now only a "marker", so there - // is an ever so slight chance for a race we should handle. - log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash()) - return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4]) + // In a pruned node the genesis block will not exist in the freezer. + // It should not happen that we set head to any other pruned block. + if header.Number.Uint64() > 0 { + // This should never happen. In practice, previously currentBlock + // contained the entire block whereas now only a "marker", so there + // is an ever so slight chance for a race we should handle. + log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash()) + return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4]) + } } bc.chainHeadFeed.Send(ChainHeadEvent{Header: header}) return nil @@ -663,11 +681,15 @@ func (bc *BlockChain) SetHeadWithTimestamp(timestamp uint64) error { // Send chain head event to update the transaction pool header := bc.CurrentBlock() if block := bc.GetBlock(header.Hash(), header.Number.Uint64()); block == nil { - // This should never happen. In practice, previously currentBlock - // contained the entire block whereas now only a "marker", so there - // is an ever so slight chance for a race we should handle. - log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash()) - return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4]) + // In a pruned node the genesis block will not exist in the freezer. + // It should not happen that we set head to any other pruned block. + if header.Number.Uint64() > 0 { + // This should never happen. In practice, previously currentBlock + // contained the entire block whereas now only a "marker", so there + // is an ever so slight chance for a race we should handle. + log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash()) + return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4]) + } } bc.chainHeadFeed.Send(ChainHeadEvent{Header: header}) return nil diff --git a/core/txindexer.go b/core/txindexer.go index 31f069995bc..64a2e8c49f5 100644 --- a/core/txindexer.go +++ b/core/txindexer.go @@ -196,6 +196,19 @@ func (indexer *txIndexer) repair(head uint64) { } } +// resolveHead resolves the block number of the current chain head. +func (indexer *txIndexer) resolveHead() uint64 { + headBlockHash := rawdb.ReadHeadBlockHash(indexer.db) + if headBlockHash == (common.Hash{}) { + return 0 + } + headBlockNumber := rawdb.ReadHeaderNumber(indexer.db, headBlockHash) + if headBlockNumber == nil { + return 0 + } + return *headBlockNumber +} + // loop is the scheduler of the indexer, assigning indexing/unindexing tasks depending // on the received chain event. func (indexer *txIndexer) loop(chain *BlockChain) { @@ -203,9 +216,9 @@ func (indexer *txIndexer) loop(chain *BlockChain) { // Listening to chain events and manipulate the transaction indexes. var ( - stop chan struct{} // Non-nil if background routine is active - done chan struct{} // Non-nil if background routine is active - head = rawdb.ReadHeadBlock(indexer.db).NumberU64() // The latest announced chain head + stop chan struct{} // Non-nil if background routine is active + done chan struct{} // Non-nil if background routine is active + head = indexer.resolveHead() // The latest announced chain head headCh = make(chan ChainHeadEvent) sub = chain.SubscribeChainHeadEvent(headCh) diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 2ed38772ce8..fc4a7be6d29 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -186,13 +186,15 @@ func (p *TxPool) loop(head *types.Header) { // Try to inject a busy marker and start a reset if successful select { case resetBusy <- struct{}{}: - statedb, err := p.chain.StateAt(newHead.Root) - if err != nil { - log.Crit("Failed to reset txpool state", "err", err) + // Updates the statedb with the new chain head. The head state may be + // unavailable if the initial state sync has not yet completed. + if statedb, err := p.chain.StateAt(newHead.Root); err != nil { + log.Error("Failed to reset txpool state", "err", err) + } else { + p.stateLock.Lock() + p.state = statedb + p.stateLock.Unlock() } - p.stateLock.Lock() - p.state = statedb - p.stateLock.Unlock() // Busy marker injected, start a new subpool reset go func(oldHead, newHead *types.Header) { From 01786f329f2fea24ad0d8308dce3ec72880ff5d7 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Thu, 17 Apr 2025 10:33:59 +0200 Subject: [PATCH 106/658] eth: fix transaction sender cache miss before broadcast (#31657) BroadcastTransactions needs the Sender address to route message flows from the same Sender address consistently to the same random subset of peers. It however spent considerable time calculating the Sender addresses, even if the Sender address was already calculated and cached in other parts of the code. Since we only need the mapping, we can use any signer, and the one that had already been used is a better choice because of cache reuse. --- eth/handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/handler.go b/eth/handler.go index b2ad6effdb4..8283d7d02fe 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -484,7 +484,7 @@ func (h *handler) BroadcastTransactions(txs types.Transactions) { total := new(big.Int).Exp(direct, big.NewInt(2), nil) // Stabilise total peer count a bit based on sqrt peers var ( - signer = types.LatestSignerForChainID(h.chain.Config().ChainID) // Don't care about chain status, we just need *a* sender + signer = types.LatestSigner(h.chain.Config()) // Don't care about chain status, we just need *a* sender hasher = crypto.NewKeccakState() hash = make([]byte, 32) ) From 50b5f3125b0f13293eda17a8724103eafd249384 Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Thu, 17 Apr 2025 02:46:00 -0600 Subject: [PATCH 107/658] params: add prague timestamp for mainnet (#31535) https://eips.ethereum.org/EIPS/eip-7600#activation Timestamp: `1746612311` Fork id: `0xc376cf8b` --- core/forkid/forkid_test.go | 19 ++++++++++--------- params/config.go | 2 ++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index 31e2b534bef..413e4d77a8f 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -76,8 +76,10 @@ func TestCreation(t *testing.T) { {20000000, 1681338454, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 1681338455}}, // Last Gray Glacier block {20000000, 1681338455, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}}, // First Shanghai block {30000000, 1710338134, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}}, // Last Shanghai block - {40000000, 1710338135, ID{Hash: checksumToBytes(0x9f3d2254), Next: 0}}, // First Cancun block - {50000000, 2000000000, ID{Hash: checksumToBytes(0x9f3d2254), Next: 0}}, // Future Cancun block + {40000000, 1710338135, ID{Hash: checksumToBytes(0x9f3d2254), Next: 1746612311}}, // First Cancun block + {30000000, 1746022486, ID{Hash: checksumToBytes(0x9f3d2254), Next: 1746612311}}, // Last Cancun block + {30000000, 1746612311, ID{Hash: checksumToBytes(0xc376cf8b), Next: 0}}, // First Prague block + {50000000, 2000000000, ID{Hash: checksumToBytes(0xc376cf8b), Next: 0}}, // Future Prague block }, }, // Sepolia test cases @@ -137,9 +139,11 @@ func TestCreation(t *testing.T) { // fork ID. func TestValidation(t *testing.T) { // Config that has not timestamp enabled + // TODO(lightclient): this always needs to be updated when a mainnet timestamp is set. legacyConfig := *params.MainnetChainConfig legacyConfig.ShanghaiTime = nil legacyConfig.CancunTime = nil + legacyConfig.PragueTime = nil tests := []struct { config *params.ChainConfig @@ -314,9 +318,7 @@ func TestValidation(t *testing.T) { // Local is mainnet Prague, remote announces Shanghai + knowledge about Cancun. Remote // is definitely out of sync. It may or may not need the Prague update, we don't know yet. - // - // TODO(karalabe): Enable this when Cancun **and** Prague is specced, update all the numbers - //{params.MainnetChainConfig, 0, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}, nil}, + {params.MainnetChainConfig, 0, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 1710338135}, nil}, // Local is mainnet Shanghai, remote announces Cancun. Local is out of sync, accept. {params.MainnetChainConfig, 21000000, 1700000000, ID{Hash: checksumToBytes(0x9f3d2254), Next: 0}, nil}, @@ -324,8 +326,7 @@ func TestValidation(t *testing.T) { // Local is mainnet Shanghai, remote announces Cancun, but is not aware of Prague. Local // out of sync. Local also knows about a future fork, but that is uncertain yet. // - // TODO(karalabe): Enable this when Cancun **and** Prague is specced, update remote checksum - //{params.MainnetChainConfig, 21000000, 1678000000, ID{Hash: checksumToBytes(0x00000000), Next: 0}, nil}, + {params.MainnetChainConfig, 21000000, 1678000000, ID{Hash: checksumToBytes(0xc376cf8b), Next: 0}, nil}, // Local is mainnet Cancun. remote announces Shanghai but is not aware of further forks. // Remote needs software update. @@ -342,11 +343,11 @@ func TestValidation(t *testing.T) { // Local is mainnet Shanghai, remote is random Shanghai. {params.MainnetChainConfig, 20000000, 1681338455, ID{Hash: checksumToBytes(0x12345678), Next: 0}, ErrLocalIncompatibleOrStale}, - // Local is mainnet Cancun, far in the future. Remote announces Gopherium (non existing fork) + // Local is mainnet Prague, far in the future. Remote announces Gopherium (non existing fork) // at some future timestamp 8888888888, for itself, but past block for local. Local is incompatible. // // This case detects non-upgraded nodes with majority hash power (typical Ropsten mess). - {params.MainnetChainConfig, 88888888, 8888888888, ID{Hash: checksumToBytes(0x9f3d2254), Next: 8888888888}, ErrLocalIncompatibleOrStale}, + {params.MainnetChainConfig, 88888888, 8888888888, ID{Hash: checksumToBytes(0xc376cf8b), Next: 8888888888}, ErrLocalIncompatibleOrStale}, // Local is mainnet Shanghai. Remote is also in Shanghai, but announces Gopherium (non existing // fork) at timestamp 1668000000, before Cancun. Local is incompatible. diff --git a/params/config.go b/params/config.go index 8f9e02583bf..03b797863c8 100644 --- a/params/config.go +++ b/params/config.go @@ -60,10 +60,12 @@ var ( TerminalTotalDifficulty: MainnetTerminalTotalDifficulty, // 58_750_000_000_000_000_000_000 ShanghaiTime: newUint64(1681338455), CancunTime: newUint64(1710338135), + PragueTime: newUint64(1746612311), DepositContractAddress: common.HexToAddress("0x00000000219ab540356cbb839cbe05303d7705fa"), Ethash: new(EthashConfig), BlobScheduleConfig: &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, + Prague: DefaultPragueBlobConfig, }, } // HoleskyChainConfig contains the chain parameters to run a node on the Holesky test network. From 13b157a461c88678cd4e15ca005e7b45d823431b Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Thu, 17 Apr 2025 02:46:47 -0600 Subject: [PATCH 108/658] core,params: add fork readiness indicator in logs (#31340) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #31310 This has been requested a few times in the past and I think it is a nice quality-of-life improvement for users. At a predetermined interval, there will now be a "Fork ready" log when a future fork is scheduled, but not yet active. It can only possibly print after block import, which kinda avoids the scenario where the client isn't progressing or is syncing and the user thinks it's "ready" because it sees a ready log. New output: ```console INFO [03-08|21:32:57.472] Imported new potential chain segment number=7 hash=aa24ee..f09e62 blocks=1 txs=0 mgas=0.000 elapsed="874.916µs" mgasps=0.000 snapdiffs=973.00B triediffs=7.05KiB triedirty=0.00B INFO [03-08|21:32:57.473] Ready for fork activation fork=Prague date="18 Mar 25 19:29 CET" remaining=237h57m0s timestamp=1,742,322,597 INFO [03-08|21:32:57.475] Chain head was updated number=7 hash=aa24ee..f09e62 root=19b0de..8d32f2 elapsed="129.125µs" ``` Easiest way to verify this behavior is to apply this patch and run `geth --dev --dev.period=12` ```patch diff --git a/params/config.go b/params/config.go index 9c7719d901..030c4f80e7 100644 --- a/params/config.go +++ b/params/config.go @@ -174,7 +174,7 @@ var ( ShanghaiTime: newUint64(0), CancunTime: newUint64(0), TerminalTotalDifficulty: big.NewInt(0), - PragueTime: newUint64(0), + PragueTime: newUint64(uint64(time.Now().Add(time.Hour * 300).Unix())), BlobScheduleConfig: &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, Prague: DefaultPragueBlobConfig, ``` --- core/blockchain.go | 28 ++++++++++++++++++++++++++++ params/config.go | 17 +++++++++++++++++ params/forks/forks.go | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/core/blockchain.go b/core/blockchain.go index c4caec66ed6..6667f649110 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "io" + "math" "math/big" "runtime" "slices" @@ -100,6 +101,10 @@ var ( errInvalidNewChain = errors.New("invalid new chain") ) +var ( + forkReadyInterval = 3 * time.Minute +) + const ( bodyCacheLimit = 256 blockCacheLimit = 256 @@ -275,6 +280,8 @@ type BlockChain struct { processor Processor // Block transaction processor interface vmConfig vm.Config logger *tracing.Hooks + + lastForkReadyAlert time.Time // Last time there was a fork readiness print out } // NewBlockChain returns a fully initialised block chain using information @@ -1884,6 +1891,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness trieDiffNodes, trieBufNodes, _ := bc.triedb.Size() stats.report(chain, it.index, snapDiffItems, snapBufItems, trieDiffNodes, trieBufNodes, setHead) + // Print confirmation that a future fork is scheduled, but not yet active. + bc.logForkReadiness(block) + if !setHead { // After merge we expect few side chains. Simply count // all blocks the CL gives us for GC processing time @@ -1917,6 +1927,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness "root", block.Root()) } } + stats.ignored += it.remaining() return witness, it.index, err } @@ -2514,6 +2525,23 @@ func (bc *BlockChain) reportBlock(block *types.Block, res *ProcessResult, err er log.Error(summarizeBadBlock(block, receipts, bc.Config(), err)) } +// logForkReadiness will write a log when a future fork is scheduled, but not +// active. This is useful so operators know their client is ready for the fork. +func (bc *BlockChain) logForkReadiness(block *types.Block) { + c := bc.Config() + current, last := c.LatestFork(block.Time()), c.LatestFork(math.MaxUint64) + t := c.Timestamp(last) + if t == nil { + return + } + at := time.Unix(int64(*t), 0) + if current < last && time.Now().After(bc.lastForkReadyAlert.Add(forkReadyInterval)) { + log.Info("Ready for fork activation", "fork", last, "date", at.Format(time.RFC822), + "remaining", time.Until(at).Round(time.Second), "timestamp", at.Unix()) + bc.lastForkReadyAlert = time.Now() + } +} + // summarizeBadBlock returns a string summarizing the bad block and other // relevant information. func summarizeBadBlock(block *types.Block, receipts []*types.Receipt, config *params.ChainConfig, err error) string { diff --git a/params/config.go b/params/config.go index 03b797863c8..67aa6b22251 100644 --- a/params/config.go +++ b/params/config.go @@ -909,6 +909,23 @@ func (c *ChainConfig) LatestFork(time uint64) forks.Fork { } } +// Timestamp returns the timestamp associated with the fork or returns nil if +// the fork isn't defined or isn't a time-based fork. +func (c *ChainConfig) Timestamp(fork forks.Fork) *uint64 { + switch { + case fork == forks.Osaka: + return c.OsakaTime + case fork == forks.Prague: + return c.PragueTime + case fork == forks.Cancun: + return c.CancunTime + case fork == forks.Shanghai: + return c.ShanghaiTime + default: + return nil + } +} + // isForkBlockIncompatible returns true if a fork scheduled at block s1 cannot be // rescheduled to block s2 because head is already past the fork. func isForkBlockIncompatible(s1, s2, head *big.Int) bool { diff --git a/params/forks/forks.go b/params/forks/forks.go index 02f6e5b6125..5c9612a625a 100644 --- a/params/forks/forks.go +++ b/params/forks/forks.go @@ -20,7 +20,7 @@ package forks type Fork int const ( - Frontier = iota + Frontier Fork = iota FrontierThawing Homestead DAO @@ -41,3 +41,35 @@ const ( Prague Osaka ) + +// String implements fmt.Stringer. +func (f Fork) String() string { + s, ok := forkToString[f] + if !ok { + return "Unknown fork" + } + return s +} + +var forkToString = map[Fork]string{ + Frontier: "Frontier", + FrontierThawing: "Frontier Thawing", + Homestead: "Homestead", + DAO: "DAO", + TangerineWhistle: "Tangerine Whistle", + SpuriousDragon: "Spurious Dragon", + Byzantium: "Byzantium", + Constantinople: "Constantinople", + Petersburg: "Petersburg", + Istanbul: "Istanbul", + MuirGlacier: "Muir Glacier", + Berlin: "Berlin", + London: "London", + ArrowGlacier: "Arrow Glacier", + GrayGlacier: "Gray Glacier", + Paris: "Paris", + Shanghai: "Shanghai", + Cancun: "Cancun", + Prague: "Prague", + Osaka: "Osaka", +} From 074da25f66adfd31a5ea360db9efa40ad7964de3 Mon Sep 17 00:00:00 2001 From: jwasinger Date: Thu, 17 Apr 2025 20:23:31 +0800 Subject: [PATCH 109/658] eth/catalyst: sanitize simulated beacon period to avoid overflowing time.Duration (#31407) closes #31401 --------- Co-authored-by: Marius van der Wijden Co-authored-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Co-authored-by: Felix Lange --- eth/catalyst/simulated_beacon.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/eth/catalyst/simulated_beacon.go b/eth/catalyst/simulated_beacon.go index dd9d8f90629..b84df9a4d6c 100644 --- a/eth/catalyst/simulated_beacon.go +++ b/eth/catalyst/simulated_beacon.go @@ -21,6 +21,7 @@ import ( "crypto/sha256" "errors" "fmt" + "math" "sync" "time" @@ -124,9 +125,13 @@ func NewSimulatedBeacon(period uint64, feeRecipient common.Address, eth *eth.Eth return nil, err } } + + // cap the dev mode period to a reasonable maximum value to avoid + // overflowing the time.Duration (int64) that it will occupy + const maxPeriod = uint64(math.MaxInt64 / time.Second) return &SimulatedBeacon{ eth: eth, - period: period, + period: min(period, maxPeriod), shutdownCh: make(chan struct{}), engineAPI: engineAPI, lastBlockTime: block.Time, From 9089f9461cc2a25703ee247256e1d0c48b64f815 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 18 Apr 2025 03:27:48 +0800 Subject: [PATCH 110/658] eth: add tx to locals only if it has a chance of acceptance (#31618) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This pull request improves error handling for local transaction submissions. Specifically, if a transaction fails with a temporary error but might be accepted later, the error will not be returned to the user; instead, the transaction will be tracked locally for resubmission. However, if the transaction fails with a permanent error (e.g., invalid transaction or insufficient balance), the error will be propagated to the user. These errors returned in the legacyPool are regarded as temporary failure: - `ErrOutOfOrderTxFromDelegated` - `txpool.ErrInflightTxLimitReached` - `ErrAuthorityReserved` - `txpool.ErrUnderpriced` - `ErrTxPoolOverflow` - `ErrFutureReplacePending` Notably, InsufficientBalance is also treated as a permanent error, as it’s highly unlikely that users will transfer funds into the sender account after submitting the transaction. Otherwise, users may be confused—seeing their transaction submitted but unaware that the sender lacks sufficient funds—and continue waiting for it to be included. --------- Co-authored-by: lightclient --- core/txpool/blobpool/blobpool.go | 2 + core/txpool/blobpool/blobpool_test.go | 2 +- core/txpool/errors.go | 13 +- core/txpool/legacypool/legacypool2_test.go | 6 +- core/txpool/legacypool/legacypool_test.go | 59 ++++---- core/txpool/locals/errors.go | 46 ++++++ core/txpool/locals/tx_tracker.go | 20 +-- core/txpool/locals/tx_tracker_test.go | 58 +------- core/txpool/txpool.go | 25 ---- core/txpool/validation.go | 4 +- eth/api_backend.go | 26 ++-- eth/api_backend_test.go | 157 +++++++++++++++++++++ eth/fetcher/tx_fetcher.go | 4 +- eth/fetcher/tx_fetcher_test.go | 6 +- ethclient/simulated/backend_test.go | 8 +- 15 files changed, 284 insertions(+), 152 deletions(-) create mode 100644 core/txpool/locals/errors.go create mode 100644 eth/api_backend_test.go diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 12a4133b40c..e506da228d9 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1391,6 +1391,8 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) { switch { case errors.Is(err, txpool.ErrUnderpriced): addUnderpricedMeter.Mark(1) + case errors.Is(err, txpool.ErrTxGasPriceTooLow): + addUnderpricedMeter.Mark(1) case errors.Is(err, core.ErrNonceTooLow): addStaleMeter.Mark(1) case errors.Is(err, core.ErrNonceTooHigh): diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 76d21a0c9e0..0a323179a6a 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -1484,7 +1484,7 @@ func TestAdd(t *testing.T) { { // New account, no previous txs, nonce 0, but blob fee cap too low from: "alice", tx: makeUnsignedTx(0, 1, 1, 0), - err: txpool.ErrUnderpriced, + err: txpool.ErrTxGasPriceTooLow, }, { // Same as above but blob fee cap equals minimum, should be accepted from: "alice", diff --git a/core/txpool/errors.go b/core/txpool/errors.go index 02f5703b6ca..968c9d95423 100644 --- a/core/txpool/errors.go +++ b/core/txpool/errors.go @@ -16,7 +16,9 @@ package txpool -import "errors" +import ( + "errors" +) var ( // ErrAlreadyKnown is returned if the transactions is already contained @@ -26,14 +28,19 @@ var ( // ErrInvalidSender is returned if the transaction contains an invalid signature. ErrInvalidSender = errors.New("invalid sender") - // ErrUnderpriced is returned if a transaction's gas price is below the minimum - // configured for the transaction pool. + // ErrUnderpriced is returned if a transaction's gas price is too low to be + // included in the pool. If the gas price is lower than the minimum configured + // one for the transaction pool, use ErrTxGasPriceTooLow instead. ErrUnderpriced = errors.New("transaction underpriced") // ErrReplaceUnderpriced is returned if a transaction is attempted to be replaced // with a different one without the required price bump. ErrReplaceUnderpriced = errors.New("replacement transaction underpriced") + // ErrTxGasPriceTooLow is returned if a transaction's gas price is below the + // minimum configured for the transaction pool. + ErrTxGasPriceTooLow = errors.New("transaction gas price below minimum") + // ErrAccountLimitExceeded is returned if a transaction would exceed the number // allowed by a pool for a single account. ErrAccountLimitExceeded = errors.New("account limit exceeded") diff --git a/core/txpool/legacypool/legacypool2_test.go b/core/txpool/legacypool/legacypool2_test.go index 3f210e3d1b9..deb06aa6178 100644 --- a/core/txpool/legacypool/legacypool2_test.go +++ b/core/txpool/legacypool/legacypool2_test.go @@ -82,12 +82,14 @@ func TestTransactionFutureAttack(t *testing.T) { // Create the pool to test the limit enforcement with statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed)) + config := testTxPoolConfig config.GlobalQueue = 100 config.GlobalSlots = 100 pool := New(config, blockchain) pool.Init(config.PriceLimit, blockchain.CurrentBlock(), newReserver()) defer pool.Close() + fillPool(t, pool) pending, _ := pool.Stats() // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops @@ -180,7 +182,9 @@ func TestTransactionZAttack(t *testing.T) { ivPending := countInvalidPending() t.Logf("invalid pending: %d\n", ivPending) - // Now, DETER-Z attack starts, let's add a bunch of expensive non-executables (from N accounts) along with balance-overdraft txs (from one account), and see if the pending-count drops + // Now, DETER-Z attack starts, let's add a bunch of expensive non-executables + // (from N accounts) along with balance-overdraft txs (from one account), and + // see if the pending-count drops for j := 0; j < int(pool.config.GlobalQueue); j++ { futureTxs := types.Transactions{} key, _ := crypto.GenerateKey() diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index bb1323a7d1f..2fdf8903203 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -413,7 +413,7 @@ func TestInvalidTransactions(t *testing.T) { tx = transaction(1, 100000, key) pool.gasTip.Store(uint256.NewInt(1000)) - if err, want := pool.addRemote(tx), txpool.ErrUnderpriced; !errors.Is(err, want) { + if err, want := pool.addRemote(tx), txpool.ErrTxGasPriceTooLow; !errors.Is(err, want) { t.Errorf("want %v have %v", want, err) } } @@ -484,7 +484,7 @@ func TestNegativeValue(t *testing.T) { tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), 100, big.NewInt(1), nil), types.HomesteadSigner{}, key) from, _ := deriveSender(tx) testAddBalance(pool, from, big.NewInt(1)) - if err := pool.addRemote(tx); err != txpool.ErrNegativeValue { + if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrNegativeValue) { t.Error("expected", txpool.ErrNegativeValue, "got", err) } } @@ -497,7 +497,7 @@ func TestTipAboveFeeCap(t *testing.T) { tx := dynamicFeeTx(0, 100, big.NewInt(1), big.NewInt(2), key) - if err := pool.addRemote(tx); err != core.ErrTipAboveFeeCap { + if err := pool.addRemote(tx); !errors.Is(err, core.ErrTipAboveFeeCap) { t.Error("expected", core.ErrTipAboveFeeCap, "got", err) } } @@ -512,12 +512,12 @@ func TestVeryHighValues(t *testing.T) { veryBigNumber.Lsh(veryBigNumber, 300) tx := dynamicFeeTx(0, 100, big.NewInt(1), veryBigNumber, key) - if err := pool.addRemote(tx); err != core.ErrTipVeryHigh { + if err := pool.addRemote(tx); !errors.Is(err, core.ErrTipVeryHigh) { t.Error("expected", core.ErrTipVeryHigh, "got", err) } tx2 := dynamicFeeTx(0, 100, veryBigNumber, big.NewInt(1), key) - if err := pool.addRemote(tx2); err != core.ErrFeeCapVeryHigh { + if err := pool.addRemote(tx2); !errors.Is(err, core.ErrFeeCapVeryHigh) { t.Error("expected", core.ErrFeeCapVeryHigh, "got", err) } } @@ -1424,14 +1424,14 @@ func TestRepricing(t *testing.T) { t.Fatalf("pool internal state corrupted: %v", err) } // Check that we can't add the old transactions back - if err := pool.addRemote(pricedTransaction(1, 100000, big.NewInt(1), keys[0])); !errors.Is(err, txpool.ErrUnderpriced) { - t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, txpool.ErrUnderpriced) + if err := pool.addRemote(pricedTransaction(1, 100000, big.NewInt(1), keys[0])); !errors.Is(err, txpool.ErrTxGasPriceTooLow) { + t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, txpool.ErrTxGasPriceTooLow) } - if err := pool.addRemote(pricedTransaction(0, 100000, big.NewInt(1), keys[1])); !errors.Is(err, txpool.ErrUnderpriced) { - t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, txpool.ErrUnderpriced) + if err := pool.addRemote(pricedTransaction(0, 100000, big.NewInt(1), keys[1])); !errors.Is(err, txpool.ErrTxGasPriceTooLow) { + t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, txpool.ErrTxGasPriceTooLow) } - if err := pool.addRemote(pricedTransaction(2, 100000, big.NewInt(1), keys[2])); !errors.Is(err, txpool.ErrUnderpriced) { - t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, txpool.ErrUnderpriced) + if err := pool.addRemote(pricedTransaction(2, 100000, big.NewInt(1), keys[2])); !errors.Is(err, txpool.ErrTxGasPriceTooLow) { + t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, txpool.ErrTxGasPriceTooLow) } if err := validateEvents(events, 0); err != nil { t.Fatalf("post-reprice event firing failed: %v", err) @@ -1476,14 +1476,14 @@ func TestMinGasPriceEnforced(t *testing.T) { tx := pricedTransaction(0, 100000, big.NewInt(2), key) pool.SetGasTip(big.NewInt(tx.GasPrice().Int64() + 1)) - if err := pool.Add([]*types.Transaction{tx}, true)[0]; !errors.Is(err, txpool.ErrUnderpriced) { + if err := pool.Add([]*types.Transaction{tx}, true)[0]; !errors.Is(err, txpool.ErrTxGasPriceTooLow) { t.Fatalf("Min tip not enforced") } tx = dynamicFeeTx(0, 100000, big.NewInt(3), big.NewInt(2), key) pool.SetGasTip(big.NewInt(tx.GasTipCap().Int64() + 1)) - if err := pool.Add([]*types.Transaction{tx}, true)[0]; !errors.Is(err, txpool.ErrUnderpriced) { + if err := pool.Add([]*types.Transaction{tx}, true)[0]; !errors.Is(err, txpool.ErrTxGasPriceTooLow) { t.Fatalf("Min tip not enforced") } } @@ -1560,16 +1560,16 @@ func TestRepricingDynamicFee(t *testing.T) { } // Check that we can't add the old transactions back tx := pricedTransaction(1, 100000, big.NewInt(1), keys[0]) - if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrUnderpriced) { - t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, txpool.ErrUnderpriced) + if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrTxGasPriceTooLow) { + t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, txpool.ErrTxGasPriceTooLow) } tx = dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[1]) - if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrUnderpriced) { - t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, txpool.ErrUnderpriced) + if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrTxGasPriceTooLow) { + t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, txpool.ErrTxGasPriceTooLow) } tx = dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), keys[2]) - if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrUnderpriced) { - t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, txpool.ErrUnderpriced) + if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrTxGasPriceTooLow) { + t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, txpool.ErrTxGasPriceTooLow) } if err := validateEvents(events, 0); err != nil { t.Fatalf("post-reprice event firing failed: %v", err) @@ -1673,7 +1673,7 @@ func TestUnderpricing(t *testing.T) { t.Fatalf("failed to add well priced transaction: %v", err) } // Ensure that replacing a pending transaction with a future transaction fails - if err := pool.addRemoteSync(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); err != ErrFutureReplacePending { + if err := pool.addRemoteSync(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); !errors.Is(err, ErrFutureReplacePending) { t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, ErrFutureReplacePending) } pending, queued = pool.Stats() @@ -1995,7 +1995,7 @@ func TestReplacement(t *testing.T) { if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), key)); err != nil { t.Fatalf("failed to add original cheap pending transaction: %v", err) } - if err := pool.addRemote(pricedTransaction(0, 100001, big.NewInt(1), key)); err != txpool.ErrReplaceUnderpriced { + if err := pool.addRemote(pricedTransaction(0, 100001, big.NewInt(1), key)); !errors.Is(err, txpool.ErrReplaceUnderpriced) { t.Fatalf("original cheap pending transaction replacement error mismatch: have %v, want %v", err, txpool.ErrReplaceUnderpriced) } if err := pool.addRemote(pricedTransaction(0, 100000, big.NewInt(2), key)); err != nil { @@ -2008,7 +2008,7 @@ func TestReplacement(t *testing.T) { if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(price), key)); err != nil { t.Fatalf("failed to add original proper pending transaction: %v", err) } - if err := pool.addRemote(pricedTransaction(0, 100001, big.NewInt(threshold-1), key)); err != txpool.ErrReplaceUnderpriced { + if err := pool.addRemote(pricedTransaction(0, 100001, big.NewInt(threshold-1), key)); !errors.Is(err, txpool.ErrReplaceUnderpriced) { t.Fatalf("original proper pending transaction replacement error mismatch: have %v, want %v", err, txpool.ErrReplaceUnderpriced) } if err := pool.addRemote(pricedTransaction(0, 100000, big.NewInt(threshold), key)); err != nil { @@ -2022,7 +2022,7 @@ func TestReplacement(t *testing.T) { if err := pool.addRemote(pricedTransaction(2, 100000, big.NewInt(1), key)); err != nil { t.Fatalf("failed to add original cheap queued transaction: %v", err) } - if err := pool.addRemote(pricedTransaction(2, 100001, big.NewInt(1), key)); err != txpool.ErrReplaceUnderpriced { + if err := pool.addRemote(pricedTransaction(2, 100001, big.NewInt(1), key)); !errors.Is(err, txpool.ErrReplaceUnderpriced) { t.Fatalf("original cheap queued transaction replacement error mismatch: have %v, want %v", err, txpool.ErrReplaceUnderpriced) } if err := pool.addRemote(pricedTransaction(2, 100000, big.NewInt(2), key)); err != nil { @@ -2032,7 +2032,7 @@ func TestReplacement(t *testing.T) { if err := pool.addRemote(pricedTransaction(2, 100000, big.NewInt(price), key)); err != nil { t.Fatalf("failed to add original proper queued transaction: %v", err) } - if err := pool.addRemote(pricedTransaction(2, 100001, big.NewInt(threshold-1), key)); err != txpool.ErrReplaceUnderpriced { + if err := pool.addRemote(pricedTransaction(2, 100001, big.NewInt(threshold-1), key)); !errors.Is(err, txpool.ErrReplaceUnderpriced) { t.Fatalf("original proper queued transaction replacement error mismatch: have %v, want %v", err, txpool.ErrReplaceUnderpriced) } if err := pool.addRemote(pricedTransaction(2, 100000, big.NewInt(threshold), key)); err != nil { @@ -2096,7 +2096,7 @@ func TestReplacementDynamicFee(t *testing.T) { } // 2. Don't bump tip or feecap => discard tx = dynamicFeeTx(nonce, 100001, big.NewInt(2), big.NewInt(1), key) - if err := pool.addRemote(tx); err != txpool.ErrReplaceUnderpriced { + if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrReplaceUnderpriced) { t.Fatalf("original cheap %s transaction replacement error mismatch: have %v, want %v", stage, err, txpool.ErrReplaceUnderpriced) } // 3. Bump both more than min => accept @@ -2117,24 +2117,25 @@ func TestReplacementDynamicFee(t *testing.T) { if err := pool.addRemoteSync(tx); err != nil { t.Fatalf("failed to add original proper %s transaction: %v", stage, err) } + // 6. Bump tip max allowed so it's still underpriced => discard tx = dynamicFeeTx(nonce, 100000, big.NewInt(gasFeeCap), big.NewInt(tipThreshold-1), key) - if err := pool.addRemote(tx); err != txpool.ErrReplaceUnderpriced { + if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrReplaceUnderpriced) { t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, txpool.ErrReplaceUnderpriced) } // 7. Bump fee cap max allowed so it's still underpriced => discard tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold-1), big.NewInt(gasTipCap), key) - if err := pool.addRemote(tx); err != txpool.ErrReplaceUnderpriced { + if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrReplaceUnderpriced) { t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, txpool.ErrReplaceUnderpriced) } // 8. Bump tip min for acceptance => accept tx = dynamicFeeTx(nonce, 100000, big.NewInt(gasFeeCap), big.NewInt(tipThreshold), key) - if err := pool.addRemote(tx); err != txpool.ErrReplaceUnderpriced { + if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrReplaceUnderpriced) { t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, txpool.ErrReplaceUnderpriced) } // 9. Bump fee cap min for acceptance => accept tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold), big.NewInt(gasTipCap), key) - if err := pool.addRemote(tx); err != txpool.ErrReplaceUnderpriced { + if err := pool.addRemote(tx); !errors.Is(err, txpool.ErrReplaceUnderpriced) { t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, txpool.ErrReplaceUnderpriced) } // 10. Check events match expected (3 new executable txs during pending, 0 during queue) diff --git a/core/txpool/locals/errors.go b/core/txpool/locals/errors.go new file mode 100644 index 00000000000..fda50bf2181 --- /dev/null +++ b/core/txpool/locals/errors.go @@ -0,0 +1,46 @@ +// 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 locals + +import ( + "errors" + + "github.com/ethereum/go-ethereum/core/txpool" + "github.com/ethereum/go-ethereum/core/txpool/legacypool" +) + +// IsTemporaryReject determines whether the given error indicates a temporary +// reason to reject a transaction from being included in the txpool. The result +// may change if the txpool's state changes later. +func IsTemporaryReject(err error) bool { + switch { + case errors.Is(err, legacypool.ErrOutOfOrderTxFromDelegated): + return true + case errors.Is(err, txpool.ErrInflightTxLimitReached): + return true + case errors.Is(err, legacypool.ErrAuthorityReserved): + return true + case errors.Is(err, txpool.ErrUnderpriced): + return true + case errors.Is(err, legacypool.ErrTxPoolOverflow): + return true + case errors.Is(err, legacypool.ErrFutureReplacePending): + return true + default: + return false + } +} diff --git a/core/txpool/locals/tx_tracker.go b/core/txpool/locals/tx_tracker.go index eccdcf422ad..e08384ce711 100644 --- a/core/txpool/locals/tx_tracker.go +++ b/core/txpool/locals/tx_tracker.go @@ -74,32 +74,22 @@ func New(journalPath string, journalTime time.Duration, chainConfig *params.Chai // Track adds a transaction to the tracked set. // Note: blob-type transactions are ignored. -func (tracker *TxTracker) Track(tx *types.Transaction) error { - return tracker.TrackAll([]*types.Transaction{tx})[0] +func (tracker *TxTracker) Track(tx *types.Transaction) { + tracker.TrackAll([]*types.Transaction{tx}) } // TrackAll adds a list of transactions to the tracked set. // Note: blob-type transactions are ignored. -func (tracker *TxTracker) TrackAll(txs []*types.Transaction) []error { +func (tracker *TxTracker) TrackAll(txs []*types.Transaction) { tracker.mu.Lock() defer tracker.mu.Unlock() - var errors []error for _, tx := range txs { if tx.Type() == types.BlobTxType { - errors = append(errors, nil) - continue - } - // Ignore the transactions which are failed for fundamental - // validation such as invalid parameters. - if err := tracker.pool.ValidateTxBasics(tx); err != nil { - log.Debug("Invalid transaction submitted", "hash", tx.Hash(), "err", err) - errors = append(errors, err) continue } // If we're already tracking it, it's a no-op if _, ok := tracker.all[tx.Hash()]; ok { - errors = append(errors, nil) continue } // Theoretically, checking the error here is unnecessary since sender recovery @@ -108,11 +98,8 @@ func (tracker *TxTracker) TrackAll(txs []*types.Transaction) []error { // Therefore, the error is still checked just in case. addr, err := types.Sender(tracker.signer, tx) if err != nil { - errors = append(errors, err) continue } - errors = append(errors, nil) - tracker.all[tx.Hash()] = tx if tracker.byAddr[addr] == nil { tracker.byAddr[addr] = legacypool.NewSortedMap() @@ -124,7 +111,6 @@ func (tracker *TxTracker) TrackAll(txs []*types.Transaction) []error { } } localGauge.Update(int64(len(tracker.all))) - return errors } // recheck checks and returns any transactions that needs to be resubmitted. diff --git a/core/txpool/locals/tx_tracker_test.go b/core/txpool/locals/tx_tracker_test.go index 5585589b6cd..0668d243fcd 100644 --- a/core/txpool/locals/tx_tracker_test.go +++ b/core/txpool/locals/tx_tracker_test.go @@ -17,7 +17,6 @@ package locals import ( - "errors" "math/big" "testing" "time" @@ -91,10 +90,12 @@ func (env *testEnv) close() { env.chain.Stop() } +// nolint:unused func (env *testEnv) setGasTip(gasTip uint64) { env.pool.SetGasTip(new(big.Int).SetUint64(gasTip)) } +// nolint:unused func (env *testEnv) makeTx(nonce uint64, gasPrice *big.Int) *types.Transaction { if nonce == 0 { head := env.chain.CurrentHeader() @@ -121,6 +122,7 @@ func (env *testEnv) makeTxs(n int) []*types.Transaction { return txs } +// nolint:unused func (env *testEnv) commit() { head := env.chain.CurrentBlock() block := env.chain.GetBlock(head.Hash(), head.Number.Uint64()) @@ -137,60 +139,6 @@ func (env *testEnv) commit() { } } -func TestRejectInvalids(t *testing.T) { - env := newTestEnv(t, 10, 0, "") - defer env.close() - - var cases = []struct { - gasTip uint64 - tx *types.Transaction - expErr error - commit bool - }{ - { - tx: env.makeTx(5, nil), // stale - expErr: core.ErrNonceTooLow, - }, - { - tx: env.makeTx(11, nil), // future transaction - expErr: nil, - }, - { - gasTip: params.GWei, - tx: env.makeTx(0, new(big.Int).SetUint64(params.GWei/2)), // low price - expErr: txpool.ErrUnderpriced, - }, - { - tx: types.NewTransaction(10, common.Address{0x00}, big.NewInt(1000), params.TxGas, big.NewInt(params.GWei), nil), // invalid signature - expErr: types.ErrInvalidSig, - }, - { - commit: true, - tx: env.makeTx(10, nil), // stale - expErr: core.ErrNonceTooLow, - }, - { - tx: env.makeTx(11, nil), - expErr: nil, - }, - } - for i, c := range cases { - if c.gasTip != 0 { - env.setGasTip(c.gasTip) - } - if c.commit { - env.commit() - } - gotErr := env.tracker.Track(c.tx) - if c.expErr == nil && gotErr != nil { - t.Fatalf("%d, unexpected error: %v", i, gotErr) - } - if c.expErr != nil && !errors.Is(gotErr, c.expErr) { - t.Fatalf("%d, unexpected error, want: %v, got: %v", i, c.expErr, gotErr) - } - } -} - func TestResubmit(t *testing.T) { env := newTestEnv(t, 10, 0, "") defer env.close() diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index fc4a7be6d29..cc8f74c1b8e 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -324,31 +324,6 @@ func (p *TxPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Pr return nil, nil } -// ValidateTxBasics checks whether a transaction is valid according to the consensus -// rules, but does not check state-dependent validation such as sufficient balance. -func (p *TxPool) ValidateTxBasics(tx *types.Transaction) error { - addr, err := types.Sender(p.signer, tx) - if err != nil { - return err - } - // Reject transactions with stale nonce. Gapped-nonce future transactions - // are considered valid and will be handled by the subpool according to its - // internal policy. - p.stateLock.RLock() - nonce := p.state.GetNonce(addr) - p.stateLock.RUnlock() - - if nonce > tx.Nonce() { - return core.ErrNonceTooLow - } - for _, subpool := range p.subpools { - if subpool.Filter(tx) { - return subpool.ValidateTxBasics(tx) - } - } - return fmt.Errorf("%w: received type %d", core.ErrTxTypeNotSupported, tx.Type()) -} - // Add enqueues a batch of transactions into the pool if they are valid. Due // to the large transaction churn, add may postpone fully integrating the tx // to a later point to batch multiple ones together. diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 8747724247f..e370f2ce84f 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -131,12 +131,12 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types } // Ensure the gasprice is high enough to cover the requirement of the calling pool if tx.GasTipCapIntCmp(opts.MinTip) < 0 { - return fmt.Errorf("%w: gas tip cap %v, minimum needed %v", ErrUnderpriced, tx.GasTipCap(), opts.MinTip) + return fmt.Errorf("%w: gas tip cap %v, minimum needed %v", ErrTxGasPriceTooLow, tx.GasTipCap(), opts.MinTip) } if tx.Type() == types.BlobTxType { // Ensure the blob fee cap satisfies the minimum blob gas price if tx.BlobGasFeeCapIntCmp(blobTxMinBlobGasPrice) < 0 { - return fmt.Errorf("%w: blob fee cap %v, minimum needed %v", ErrUnderpriced, tx.BlobGasFeeCap(), blobTxMinBlobGasPrice) + return fmt.Errorf("%w: blob fee cap %v, minimum needed %v", ErrTxGasPriceTooLow, tx.BlobGasFeeCap(), blobTxMinBlobGasPrice) } sidecar := tx.BlobTxSidecar() if sidecar == nil { diff --git a/eth/api_backend.go b/eth/api_backend.go index 182c081d2b3..64fb58a1fdd 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/txpool" + "github.com/ethereum/go-ethereum/core/txpool/locals" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/gasprice" @@ -307,19 +308,24 @@ func (b *EthAPIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscri } func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { - locals := b.eth.localTxTracker - if locals != nil { - if err := locals.Track(signedTx); err != nil { - return err - } - } - // No error will be returned to user if the transaction fails stateful - // validation (e.g., no available slot), as the locally submitted transactions - // may be resubmitted later via the local tracker. err := b.eth.txPool.Add([]*types.Transaction{signedTx}, false)[0] - if err != nil && locals == nil { + + // If the local transaction tracker is not configured, returns whatever + // returned from the txpool. + if b.eth.localTxTracker == nil { + return err + } + // If the transaction fails with an error indicating it is invalid, or if there is + // very little chance it will be accepted later (e.g., the gas price is below the + // configured minimum, or the sender has insufficient funds to cover the cost), + // propagate the error to the user. + if err != nil && !locals.IsTemporaryReject(err) { return err } + // No error will be returned to user if the transaction fails with a temporary + // error and might be accepted later (e.g., the transaction pool is full). + // Locally submitted transactions will be resubmitted later via the local tracker. + b.eth.localTxTracker.Track(signedTx) return nil } diff --git a/eth/api_backend_test.go b/eth/api_backend_test.go new file mode 100644 index 00000000000..049f68d8273 --- /dev/null +++ b/eth/api_backend_test.go @@ -0,0 +1,157 @@ +// 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 eth + +import ( + "context" + "crypto/ecdsa" + "errors" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "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/rawdb" + "github.com/ethereum/go-ethereum/core/txpool" + "github.com/ethereum/go-ethereum/core/txpool/blobpool" + "github.com/ethereum/go-ethereum/core/txpool/legacypool" + "github.com/ethereum/go-ethereum/core/txpool/locals" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" +) + +var ( + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000_000_000_000_000) + gspec = &core.Genesis{ + Config: params.MergedTestChainConfig, + Alloc: types.GenesisAlloc{ + address: {Balance: funds}, + }, + Difficulty: common.Big0, + BaseFee: big.NewInt(params.InitialBaseFee), + } + signer = types.LatestSignerForChainID(gspec.Config.ChainID) +) + +func initBackend(withLocal bool) *EthAPIBackend { + var ( + // Create a database pre-initialize with a genesis block + db = rawdb.NewMemoryDatabase() + engine = beacon.New(ethash.NewFaker()) + ) + chain, _ := core.NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil) + + txconfig := legacypool.DefaultConfig + txconfig.Journal = "" // Don't litter the disk with test journals + + blobPool := blobpool.New(blobpool.Config{Datadir: ""}, chain, nil) + legacyPool := legacypool.New(txconfig, chain) + txpool, _ := txpool.New(txconfig.PriceLimit, chain, []txpool.SubPool{legacyPool, blobPool}) + + eth := &Ethereum{ + blockchain: chain, + txPool: txpool, + } + if withLocal { + eth.localTxTracker = locals.New("", time.Minute, gspec.Config, txpool) + } + return &EthAPIBackend{ + eth: eth, + } +} + +func makeTx(nonce uint64, gasPrice *big.Int, amount *big.Int, key *ecdsa.PrivateKey) *types.Transaction { + if gasPrice == nil { + gasPrice = big.NewInt(params.GWei) + } + if amount == nil { + amount = big.NewInt(1000) + } + tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{0x00}, amount, params.TxGas, gasPrice, nil), signer, key) + return tx +} + +type unsignedAuth struct { + nonce uint64 + key *ecdsa.PrivateKey +} + +func pricedSetCodeTx(nonce uint64, gaslimit uint64, gasFee, tip *uint256.Int, key *ecdsa.PrivateKey, unsigned []unsignedAuth) *types.Transaction { + var authList []types.SetCodeAuthorization + for _, u := range unsigned { + auth, _ := types.SignSetCode(u.key, types.SetCodeAuthorization{ + ChainID: *uint256.MustFromBig(gspec.Config.ChainID), + Address: common.Address{0x42}, + Nonce: u.nonce, + }) + authList = append(authList, auth) + } + return pricedSetCodeTxWithAuth(nonce, gaslimit, gasFee, tip, key, authList) +} + +func pricedSetCodeTxWithAuth(nonce uint64, gaslimit uint64, gasFee, tip *uint256.Int, key *ecdsa.PrivateKey, authList []types.SetCodeAuthorization) *types.Transaction { + return types.MustSignNewTx(key, signer, &types.SetCodeTx{ + ChainID: uint256.MustFromBig(gspec.Config.ChainID), + Nonce: nonce, + GasTipCap: tip, + GasFeeCap: gasFee, + Gas: gaslimit, + To: common.Address{}, + Value: uint256.NewInt(100), + Data: nil, + AccessList: nil, + AuthList: authList, + }) +} + +func TestSendTx(t *testing.T) { + testSendTx(t, false) + testSendTx(t, true) +} + +func testSendTx(t *testing.T, withLocal bool) { + b := initBackend(withLocal) + + txA := pricedSetCodeTx(0, 250000, uint256.NewInt(params.GWei), uint256.NewInt(params.GWei), key, []unsignedAuth{ + { + nonce: 0, + key: key, + }, + }) + b.SendTx(context.Background(), txA) + + txB := makeTx(1, nil, nil, key) + err := b.SendTx(context.Background(), txB) + + if withLocal { + if err != nil { + t.Fatalf("Unexpected error sending tx: %v", err) + } + } else { + if !errors.Is(err, txpool.ErrInflightTxLimitReached) { + t.Fatalf("Unexpected error, want: %v, got: %v", txpool.ErrInflightTxLimitReached, err) + } + } +} diff --git a/eth/fetcher/tx_fetcher.go b/eth/fetcher/tx_fetcher.go index 1c192d41122..ff17ae4945a 100644 --- a/eth/fetcher/tx_fetcher.go +++ b/eth/fetcher/tx_fetcher.go @@ -345,7 +345,7 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool) // Track the transaction hash if the price is too low for us. // Avoid re-request this transaction when we receive another // announcement. - if errors.Is(err, txpool.ErrUnderpriced) || errors.Is(err, txpool.ErrReplaceUnderpriced) { + if errors.Is(err, txpool.ErrUnderpriced) || errors.Is(err, txpool.ErrReplaceUnderpriced) || errors.Is(err, txpool.ErrTxGasPriceTooLow) { f.underpriced.Add(batch[j].Hash(), batch[j].Time()) } // Track a few interesting failure types @@ -355,7 +355,7 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool) case errors.Is(err, txpool.ErrAlreadyKnown): duplicate++ - case errors.Is(err, txpool.ErrUnderpriced) || errors.Is(err, txpool.ErrReplaceUnderpriced): + case errors.Is(err, txpool.ErrUnderpriced) || errors.Is(err, txpool.ErrReplaceUnderpriced) || errors.Is(err, txpool.ErrTxGasPriceTooLow): underpriced++ default: diff --git a/eth/fetcher/tx_fetcher_test.go b/eth/fetcher/tx_fetcher_test.go index 7f3080f5f6f..c4c8cac56e0 100644 --- a/eth/fetcher/tx_fetcher_test.go +++ b/eth/fetcher/tx_fetcher_test.go @@ -1244,10 +1244,12 @@ func TestTransactionFetcherUnderpricedDedup(t *testing.T) { func(txs []*types.Transaction) []error { errs := make([]error, len(txs)) for i := 0; i < len(errs); i++ { - if i%2 == 0 { + if i%3 == 0 { errs[i] = txpool.ErrUnderpriced - } else { + } else if i%3 == 1 { errs[i] = txpool.ErrReplaceUnderpriced + } else { + errs[i] = txpool.ErrTxGasPriceTooLow } } return errs diff --git a/ethclient/simulated/backend_test.go b/ethclient/simulated/backend_test.go index fc78e843627..303e480a098 100644 --- a/ethclient/simulated/backend_test.go +++ b/ethclient/simulated/backend_test.go @@ -25,16 +25,14 @@ import ( "testing" "time" - "go.uber.org/goleak" - - "github.com/ethereum/go-ethereum/crypto/kzg4844" - "github.com/holiman/uint256" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" + "go.uber.org/goleak" ) var _ bind.ContractBackend = (Client)(nil) From 2e0ad2cb4d11cc9dd094e3e2c1d196d08079ef91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Fri, 18 Apr 2025 13:39:11 +0200 Subject: [PATCH 111/658] core/filtermaps: only use common ancestor snapshots (#31668) This PR makes the conditions for using a map rendering snapshot stricter so that whenever a reorg happens, only a snapshot of a common ancestor block can be used. The issue fixed in https://github.com/ethereum/go-ethereum/pull/31642 originated from using a snapshot that wasn't a common ancestor. For example in the following reorg scenario: `A->B`, then `A->B2`, then `A->B2->C2`, then `A->B->C` the last reorg triggered a render from snapshot `B` saved earlier. Now this is possible under certain conditions but extra care is needed, for example if block `B` crosses a map boundary then it should not be allowed. With the latest fix the checks are sufficient but I realized I would just feel safer if we disallowed this rare and risky scenario altogether and just render from snapshot `A` after the last reorg in the example above. The performance difference if a few milliseconds and it occurs rarely (about once a day on Holesky, probably much more rare on Mainnet). Note that this PR only makes the snapshot conditions stricter and `TestIndexerRandomRange` does check that snapshots are still used whenever it's obviously possible (adding blocks after the current head without a reorg) so this change can be considered safe. Also I am running the unit tests and the fuzzer and everything seems to be fine. --- core/filtermaps/map_renderer.go | 1 + 1 file changed, 1 insertion(+) diff --git a/core/filtermaps/map_renderer.go b/core/filtermaps/map_renderer.go index cd960ad31cf..23d84f0eca9 100644 --- a/core/filtermaps/map_renderer.go +++ b/core/filtermaps/map_renderer.go @@ -143,6 +143,7 @@ func (f *FilterMaps) lastCanonicalSnapshotOfMap(mapIndex uint32) *renderedMap { var best *renderedMap for _, blockNumber := range f.renderSnapshots.Keys() { if cp, _ := f.renderSnapshots.Get(blockNumber); cp != nil && blockNumber < f.indexedRange.blocks.AfterLast() && + blockNumber <= f.indexedView.headNumber && f.indexedView.getBlockId(blockNumber) == cp.lastBlockId && blockNumber <= f.targetView.headNumber && f.targetView.getBlockId(blockNumber) == cp.lastBlockId && cp.mapIndex == mapIndex && (best == nil || blockNumber > best.lastBlock) { best = cp From 4c9e7d1b187c1b42b7f3218724ad61d1ef9c9019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Fri, 18 Apr 2025 14:00:11 +0200 Subject: [PATCH 112/658] core/filtermaps: make ChainView thread safe (#31671) This PR makes `filtermaps.ChainView` thread safe because it is used concurrently both by the indexer and multiple matcher threads. Even though it represents an immutable view of the chain, adding a mutex lock to the `blockHash` function is necessary because it does so by extending its list of non-canonical hashes if the underlying blockchain is changed. The unsafe concurrency did cause a panic once after running the unit tests for several hours and it could also happen during live operation. --- core/filtermaps/chain_view.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/filtermaps/chain_view.go b/core/filtermaps/chain_view.go index a8cf53b1c0f..d6f0a727bf1 100644 --- a/core/filtermaps/chain_view.go +++ b/core/filtermaps/chain_view.go @@ -17,6 +17,8 @@ package filtermaps import ( + "sync" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" @@ -39,6 +41,7 @@ type blockchain interface { // of the underlying blockchain, it should only possess the block headers // and receipts up until the expected chain view head. type ChainView struct { + lock sync.Mutex chain blockchain headNumber uint64 hashes []common.Hash // block hashes starting backwards from headNumber until first canonical hash @@ -147,6 +150,9 @@ func (cv *ChainView) extendNonCanonical() bool { // blockHash returns the given block hash without doing the head number check. func (cv *ChainView) blockHash(number uint64) common.Hash { + cv.lock.Lock() + defer cv.lock.Unlock() + if number+uint64(len(cv.hashes)) <= cv.headNumber { hash := cv.chain.GetCanonicalHash(number) if !cv.extendNonCanonical() { From 1296cdb7484d3b6236f1d1d95381cc8ffde7d4d9 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Sat, 19 Apr 2025 21:42:54 +1000 Subject: [PATCH 113/658] core: fail execution if system call fails to execute (#31639) see: https://github.com/ethereum/pm/issues/1450#issuecomment-2800911584 --- cmd/evm/internal/t8ntool/execution.go | 8 ++++++-- core/chain_makers.go | 8 ++++++-- core/state_processor.go | 27 +++++++++++++++++---------- internal/ethapi/simulate.go | 8 ++++++-- miner/worker.go | 8 ++++++-- 5 files changed, 41 insertions(+), 18 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 7de1eb6949d..b2e5f707145 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -363,9 +363,13 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err)) } // EIP-7002 - core.ProcessWithdrawalQueue(&requests, evm) + if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil { + return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process withdrawal requests: %v", err)) + } // EIP-7251 - core.ProcessConsolidationQueue(&requests, evm) + if err := core.ProcessConsolidationQueue(&requests, evm); err != nil { + return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process consolidation requests: %v", err)) + } } // Commit block diff --git a/core/chain_makers.go b/core/chain_makers.go index 7a258dc05f8..37bddcfda59 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -328,9 +328,13 @@ func (b *BlockGen) collectRequests(readonly bool) (requests [][]byte) { blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase) evm := vm.NewEVM(blockContext, statedb, b.cm.config, vm.Config{}) // EIP-7002 - ProcessWithdrawalQueue(&requests, evm) + if err := ProcessWithdrawalQueue(&requests, evm); err != nil { + panic(fmt.Sprintf("could not process withdrawal requests: %v", err)) + } // EIP-7251 - ProcessConsolidationQueue(&requests, evm) + if err := ProcessConsolidationQueue(&requests, evm); err != nil { + panic(fmt.Sprintf("could not process consolidation requests: %v", err)) + } } return requests } diff --git a/core/state_processor.go b/core/state_processor.go index 9241d091ad4..322bd24f410 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -113,9 +113,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg return nil, err } // EIP-7002 - ProcessWithdrawalQueue(&requests, evm) + if err := ProcessWithdrawalQueue(&requests, evm); err != nil { + return nil, err + } // EIP-7251 - ProcessConsolidationQueue(&requests, evm) + if err := ProcessConsolidationQueue(&requests, evm); err != nil { + return nil, err + } } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) @@ -265,17 +269,17 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) { // ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract. // It returns the opaque request data returned by the contract. -func ProcessWithdrawalQueue(requests *[][]byte, evm *vm.EVM) { - processRequestsSystemCall(requests, evm, 0x01, params.WithdrawalQueueAddress) +func ProcessWithdrawalQueue(requests *[][]byte, evm *vm.EVM) error { + return processRequestsSystemCall(requests, evm, 0x01, params.WithdrawalQueueAddress) } // ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract. // It returns the opaque request data returned by the contract. -func ProcessConsolidationQueue(requests *[][]byte, evm *vm.EVM) { - processRequestsSystemCall(requests, evm, 0x02, params.ConsolidationQueueAddress) +func ProcessConsolidationQueue(requests *[][]byte, evm *vm.EVM) error { + return processRequestsSystemCall(requests, evm, 0x02, params.ConsolidationQueueAddress) } -func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte, addr common.Address) { +func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte, addr common.Address) error { if tracer := evm.Config.Tracer; tracer != nil { onSystemCallStart(tracer, evm.GetVMContext()) if tracer.OnSystemCallEnd != nil { @@ -292,17 +296,20 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte } evm.SetTxContext(NewEVMTxContext(msg)) evm.StateDB.AddAddressToAccessList(addr) - ret, _, _ := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560) + ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560) evm.StateDB.Finalise(true) + if err != nil { + return fmt.Errorf("system call failed to execute: %v", err) + } if len(ret) == 0 { - return // skip empty output + return nil // skip empty output } - // Append prefixed requestsData to the requests list. requestsData := make([]byte, len(ret)+1) requestsData[0] = requestType copy(requestsData[1:], ret) *requests = append(*requests, requestsData) + return nil } var depositTopic = common.HexToHash("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5") diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index ba346b132f9..9241b509da7 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -314,9 +314,13 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, return nil, nil, err } // EIP-7002 - core.ProcessWithdrawalQueue(&requests, evm) + if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil { + return nil, nil, err + } // EIP-7251 - core.ProcessConsolidationQueue(&requests, evm) + if err := core.ProcessConsolidationQueue(&requests, evm); err != nil { + return nil, nil, err + } } if requests != nil { reqHash := types.CalcRequestsHash(requests) diff --git a/miner/worker.go b/miner/worker.go index 8fb42e31bc5..d80cb8913ba 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -127,9 +127,13 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo return &newPayloadResult{err: err} } // EIP-7002 - core.ProcessWithdrawalQueue(&requests, work.evm) + if err := core.ProcessWithdrawalQueue(&requests, work.evm); err != nil { + return &newPayloadResult{err: err} + } // EIP-7251 consolidations - core.ProcessConsolidationQueue(&requests, work.evm) + if err := core.ProcessConsolidationQueue(&requests, work.evm); err != nil { + return &newPayloadResult{err: err} + } } if requests != nil { reqHash := types.CalcRequestsHash(requests) From bf6da20012f63573fff4dad19634c5bf5dbef964 Mon Sep 17 00:00:00 2001 From: Morty <70688412+yiweichi@users.noreply.github.com> Date: Sat, 19 Apr 2025 22:02:31 +0800 Subject: [PATCH 114/658] eth/gasprice: fix eth_feeHistory blobGasUsedRatio divide zero (#31663) The API `eth_feeHistory` returns `{"jsonrpc":"2.0","id":1,"error":{"code":-32603,"message":"json: unsupported value: NaN"}}`, when we query `eth_feeHistory` with a old block that without a blob, or when the field `config.blobSchedule.cancun.max` in genesis.config is 0 (that happens for some projects fork geth but they don't have blob). So here we specially handle the case when maxBlobGas == 0 to prevent this issue from happening. --- eth/gasprice/feehistory.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index 59830e9fe8c..e5a197f6408 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -108,7 +108,9 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) { bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit) if blobGasUsed := bf.header.BlobGasUsed; blobGasUsed != nil { maxBlobGas := eip4844.MaxBlobGasPerBlock(config, bf.header.Time) - bf.results.blobGasUsedRatio = float64(*blobGasUsed) / float64(maxBlobGas) + if maxBlobGas != 0 { + bf.results.blobGasUsedRatio = float64(*blobGasUsed) / float64(maxBlobGas) + } } if len(percentiles) == 0 { From 7f574372d5cc3b8d37b59dd0da53e9f779bbfdd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Sun, 20 Apr 2025 09:48:49 +0200 Subject: [PATCH 115/658] eth/filters, core/filtermaps: safe chain view update (#31590) This PR changes the chain view update mechanism of the log filter. Previously the head updates were all wired through the indexer, even in unindexed mode. This was both a bit weird and also unsafe as the indexer's chain view was updates asynchronously with some delay, making some log related tests flaky. Also, the reorg safety of the indexed search was integrated with unindexed search in a weird way, relying on `syncRange.ValidBlocks` in the unindexed case too, with a special condition added to only consider the head of the valid range but not the tail in the unindexed case. In this PR the current chain view is directly accessible through the filter backend and unindexed search is also chain view based, making it inherently safe. The matcher sync mechanism is now only used for indexed search as originally intended, removing a few ugly special conditions. The PR is currently based on top of https://github.com/ethereum/go-ethereum/pull/31642 Together they fix https://github.com/ethereum/go-ethereum/issues/31518 and replace https://github.com/ethereum/go-ethereum/pull/31542 --------- Co-authored-by: Gary Rong --- accounts/abi/abigen/bind_test.go | 5 +- core/filtermaps/chain_view.go | 61 ++++-- core/filtermaps/filtermaps.go | 8 +- core/filtermaps/indexer.go | 8 +- core/filtermaps/map_renderer.go | 32 +-- core/filtermaps/matcher.go | 2 +- core/filtermaps/matcher_backend.go | 6 +- eth/api_backend.go | 8 + eth/filters/filter.go | 252 +++++++++++++---------- eth/filters/filter_system.go | 1 + eth/filters/filter_system_test.go | 5 + eth/filters/filter_test.go | 137 +++++++----- internal/ethapi/api_test.go | 3 + internal/ethapi/backend.go | 1 + internal/ethapi/transaction_args_test.go | 1 + 15 files changed, 328 insertions(+), 202 deletions(-) diff --git a/accounts/abi/abigen/bind_test.go b/accounts/abi/abigen/bind_test.go index 195064fb7a9..b3c52e81e52 100644 --- a/accounts/abi/abigen/bind_test.go +++ b/accounts/abi/abigen/bind_test.go @@ -939,6 +939,7 @@ var bindTests = []struct { if _, err := eventer.RaiseSimpleEvent(auth, common.Address{byte(j)}, [32]byte{byte(j)}, true, big.NewInt(int64(10*i+j))); err != nil { t.Fatalf("block %d, event %d: raise failed: %v", i, j, err) } + time.Sleep(time.Millisecond * 200) } sim.Commit() } @@ -1495,7 +1496,7 @@ var bindTests = []struct { if n != 3 { t.Fatalf("Invalid bar0 event") } - case <-time.NewTimer(3 * time.Second).C: + case <-time.NewTimer(10 * time.Second).C: t.Fatalf("Wait bar0 event timeout") } @@ -1506,7 +1507,7 @@ var bindTests = []struct { if n != 1 { t.Fatalf("Invalid bar event") } - case <-time.NewTimer(3 * time.Second).C: + case <-time.NewTimer(10 * time.Second).C: t.Fatalf("Wait bar event timeout") } close(stopCh) diff --git a/core/filtermaps/chain_view.go b/core/filtermaps/chain_view.go index d6f0a727bf1..aa74f3901a4 100644 --- a/core/filtermaps/chain_view.go +++ b/core/filtermaps/chain_view.go @@ -58,47 +58,75 @@ func NewChainView(chain blockchain, number uint64, hash common.Hash) *ChainView return cv } -// getBlockHash returns the block hash belonging to the given block number. +// HeadNumber returns the head block number of the chain view. +func (cv *ChainView) HeadNumber() uint64 { + return cv.headNumber +} + +// BlockHash returns the block hash belonging to the given block number. // Note that the hash of the head block is not returned because ChainView might // represent a view where the head block is currently being created. -func (cv *ChainView) getBlockHash(number uint64) common.Hash { - if number >= cv.headNumber { +func (cv *ChainView) BlockHash(number uint64) common.Hash { + cv.lock.Lock() + defer cv.lock.Unlock() + + if number > cv.headNumber { panic("invalid block number") } return cv.blockHash(number) } -// getBlockId returns the unique block id belonging to the given block number. +// BlockId returns the unique block id belonging to the given block number. // Note that it is currently equal to the block hash. In the future it might // be a different id for future blocks if the log index root becomes part of // consensus and therefore rendering the index with the new head will happen // before the hash of that new head is available. -func (cv *ChainView) getBlockId(number uint64) common.Hash { +func (cv *ChainView) BlockId(number uint64) common.Hash { + cv.lock.Lock() + defer cv.lock.Unlock() + if number > cv.headNumber { panic("invalid block number") } return cv.blockHash(number) } -// getReceipts returns the set of receipts belonging to the block at the given +// Header returns the block header at the given block number. +func (cv *ChainView) Header(number uint64) *types.Header { + return cv.chain.GetHeader(cv.BlockHash(number), number) +} + +// Receipts returns the set of receipts belonging to the block at the given // block number. -func (cv *ChainView) getReceipts(number uint64) types.Receipts { - if number > cv.headNumber { - panic("invalid block number") - } - blockHash := cv.blockHash(number) +func (cv *ChainView) Receipts(number uint64) types.Receipts { + blockHash := cv.BlockHash(number) if blockHash == (common.Hash{}) { log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber) } return cv.chain.GetReceiptsByHash(blockHash) } +// SharedRange returns the block range shared by two chain views. +func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] { + cv.lock.Lock() + defer cv.lock.Unlock() + + if cv == nil || cv2 == nil || !cv.extendNonCanonical() || !cv2.extendNonCanonical() { + return common.Range[uint64]{} + } + var sharedLen uint64 + for n := min(cv.headNumber+1-uint64(len(cv.hashes)), cv2.headNumber+1-uint64(len(cv2.hashes))); n <= cv.headNumber && n <= cv2.headNumber && cv.blockHash(n) == cv2.blockHash(n); n++ { + sharedLen = n + 1 + } + return common.NewRange(0, sharedLen) +} + // limitedView returns a new chain view that is a truncated version of the parent view. func (cv *ChainView) limitedView(newHead uint64) *ChainView { if newHead >= cv.headNumber { return cv } - return NewChainView(cv.chain, newHead, cv.blockHash(newHead)) + return NewChainView(cv.chain, newHead, cv.BlockHash(newHead)) } // equalViews returns true if the two chain views are equivalent. @@ -106,7 +134,7 @@ func equalViews(cv1, cv2 *ChainView) bool { if cv1 == nil || cv2 == nil { return false } - return cv1.headNumber == cv2.headNumber && cv1.getBlockId(cv1.headNumber) == cv2.getBlockId(cv2.headNumber) + return cv1.headNumber == cv2.headNumber && cv1.BlockId(cv1.headNumber) == cv2.BlockId(cv2.headNumber) } // matchViews returns true if the two chain views are equivalent up until the @@ -120,9 +148,9 @@ func matchViews(cv1, cv2 *ChainView, number uint64) bool { return false } if number == cv1.headNumber || number == cv2.headNumber { - return cv1.getBlockId(number) == cv2.getBlockId(number) + return cv1.BlockId(number) == cv2.BlockId(number) } - return cv1.getBlockHash(number) == cv2.getBlockHash(number) + return cv1.BlockHash(number) == cv2.BlockHash(number) } // extendNonCanonical checks whether the previously known reverse list of head @@ -150,9 +178,6 @@ func (cv *ChainView) extendNonCanonical() bool { // blockHash returns the given block hash without doing the head number check. func (cv *ChainView) blockHash(number uint64) common.Hash { - cv.lock.Lock() - defer cv.lock.Unlock() - if number+uint64(len(cv.hashes)) <= cv.headNumber { hash := cv.chain.GetCanonicalHash(number) if !cv.extendNonCanonical() { diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index fa2d6e3ffb9..a617de8968f 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -262,7 +262,7 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f f.targetView = initView if f.indexedRange.initialized { f.indexedView = f.initChainView(f.targetView) - f.indexedRange.headIndexed = f.indexedRange.blocks.AfterLast() == f.indexedView.headNumber+1 + f.indexedRange.headIndexed = f.indexedRange.blocks.AfterLast() == f.indexedView.HeadNumber()+1 if !f.indexedRange.headIndexed { f.indexedRange.headDelimiter = 0 } @@ -313,7 +313,7 @@ func (f *FilterMaps) initChainView(chainView *ChainView) *ChainView { log.Error("Could not initialize indexed chain view", "error", err) break } - if lastBlockNumber <= chainView.headNumber && chainView.getBlockId(lastBlockNumber) == lastBlockId { + if lastBlockNumber <= chainView.HeadNumber() && chainView.BlockId(lastBlockNumber) == lastBlockId { return chainView.limitedView(lastBlockNumber) } } @@ -370,7 +370,7 @@ func (f *FilterMaps) init() error { for min < max { mid := (min + max + 1) / 2 cp := checkpointList[mid-1] - if cp.BlockNumber <= f.targetView.headNumber && f.targetView.getBlockId(cp.BlockNumber) == cp.BlockId { + if cp.BlockNumber <= f.targetView.HeadNumber() && f.targetView.BlockId(cp.BlockNumber) == cp.BlockId { min = mid } else { max = mid - 1 @@ -512,7 +512,7 @@ func (f *FilterMaps) getLogByLvIndex(lvIndex uint64) (*types.Log, error) { } } // get block receipts - receipts := f.indexedView.getReceipts(firstBlockNumber) + receipts := f.indexedView.Receipts(firstBlockNumber) if receipts == nil { return nil, fmt.Errorf("failed to retrieve receipts for block %d containing searched log value index %d: %v", firstBlockNumber, lvIndex, err) } diff --git a/core/filtermaps/indexer.go b/core/filtermaps/indexer.go index 9a5424da4aa..383ec078c96 100644 --- a/core/filtermaps/indexer.go +++ b/core/filtermaps/indexer.go @@ -44,7 +44,7 @@ func (f *FilterMaps) indexerLoop() { for !f.stop { if !f.indexedRange.initialized { - if f.targetView.headNumber == 0 { + if f.targetView.HeadNumber() == 0 { // initialize when chain head is available f.processSingleEvent(true) continue @@ -249,7 +249,7 @@ func (f *FilterMaps) tryIndexHead() error { log.Info("Log index head rendering in progress", "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(), + "remaining", f.indexedView.HeadNumber()-f.indexedRange.blocks.Last(), "elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt))) f.loggedHeadIndex = true f.lastLogHeadIndex = time.Now() @@ -418,10 +418,10 @@ func (f *FilterMaps) needTailEpoch(epoch uint32) bool { // tailTargetBlock returns the target value for the tail block number according // to the log history parameter and the current index head. func (f *FilterMaps) tailTargetBlock() uint64 { - if f.history == 0 || f.indexedView.headNumber < f.history { + if f.history == 0 || f.indexedView.HeadNumber() < f.history { return 0 } - return f.indexedView.headNumber + 1 - f.history + return f.indexedView.HeadNumber() + 1 - f.history } // tailPartialBlocks returns the number of rendered blocks in the partially diff --git a/core/filtermaps/map_renderer.go b/core/filtermaps/map_renderer.go index 23d84f0eca9..7c2aa8dc32c 100644 --- a/core/filtermaps/map_renderer.go +++ b/core/filtermaps/map_renderer.go @@ -143,8 +143,8 @@ func (f *FilterMaps) lastCanonicalSnapshotOfMap(mapIndex uint32) *renderedMap { var best *renderedMap for _, blockNumber := range f.renderSnapshots.Keys() { if cp, _ := f.renderSnapshots.Get(blockNumber); cp != nil && blockNumber < f.indexedRange.blocks.AfterLast() && - blockNumber <= f.indexedView.headNumber && f.indexedView.getBlockId(blockNumber) == cp.lastBlockId && - blockNumber <= f.targetView.headNumber && f.targetView.getBlockId(blockNumber) == cp.lastBlockId && + blockNumber <= f.indexedView.HeadNumber() && f.indexedView.BlockId(blockNumber) == cp.lastBlockId && + blockNumber <= f.targetView.HeadNumber() && f.targetView.BlockId(blockNumber) == cp.lastBlockId && cp.mapIndex == mapIndex && (best == nil || blockNumber > best.lastBlock) { best = cp } @@ -173,7 +173,7 @@ func (f *FilterMaps) lastCanonicalMapBoundaryBefore(renderBefore uint32) (nextMa return 0, 0, 0, fmt.Errorf("failed to retrieve last block of reverse iterated map %d: %v", mapIndex, err) } if (f.indexedRange.headIndexed && mapIndex >= f.indexedRange.maps.Last()) || - lastBlock >= f.targetView.headNumber || lastBlockId != f.targetView.getBlockId(lastBlock) { + lastBlock >= f.targetView.HeadNumber() || lastBlockId != f.targetView.BlockId(lastBlock) { continue // map is not full or inconsistent with targetView; roll back } lvPtr, err := f.getBlockLvPointer(lastBlock) @@ -247,7 +247,7 @@ func (f *FilterMaps) loadHeadSnapshot() error { filterMap: fm, mapIndex: f.indexedRange.maps.Last(), lastBlock: f.indexedRange.blocks.Last(), - lastBlockId: f.indexedView.getBlockId(f.indexedRange.blocks.Last()), + lastBlockId: f.indexedView.BlockId(f.indexedRange.blocks.Last()), blockLvPtrs: lvPtrs, finished: true, headDelimiter: f.indexedRange.headDelimiter, @@ -264,7 +264,7 @@ func (r *mapRenderer) makeSnapshot() { filterMap: r.currentMap.filterMap.fastCopy(), mapIndex: r.currentMap.mapIndex, lastBlock: r.currentMap.lastBlock, - lastBlockId: r.iterator.chainView.getBlockId(r.currentMap.lastBlock), + lastBlockId: r.iterator.chainView.BlockId(r.currentMap.lastBlock), blockLvPtrs: r.currentMap.blockLvPtrs, finished: true, headDelimiter: r.iterator.lvIndex, @@ -370,7 +370,7 @@ func (r *mapRenderer) renderCurrentMap(stopCb func() bool) (bool, error) { r.currentMap.finished = true r.currentMap.headDelimiter = r.iterator.lvIndex } - r.currentMap.lastBlockId = r.f.targetView.getBlockId(r.currentMap.lastBlock) + r.currentMap.lastBlockId = r.f.targetView.BlockId(r.currentMap.lastBlock) totalTime += time.Since(start) mapRenderTimer.Update(totalTime) mapLogValueMeter.Mark(logValuesProcessed) @@ -566,8 +566,8 @@ func (r *mapRenderer) getUpdatedRange() (filterMapsRange, error) { lm := r.finishedMaps[r.finished.Last()] newRange.headIndexed = lm.finished if lm.finished { - newRange.blocks.SetLast(r.f.targetView.headNumber) - if lm.lastBlock != r.f.targetView.headNumber { + newRange.blocks.SetLast(r.f.targetView.HeadNumber()) + if lm.lastBlock != r.f.targetView.HeadNumber() { panic("map rendering finished but last block != head block") } newRange.headDelimiter = lm.headDelimiter @@ -665,13 +665,13 @@ var errUnindexedRange = errors.New("unindexed range") // given block's first log value entry (the block delimiter), according to the // current targetView. func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber, lvIndex uint64) (*logIterator, error) { - 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.targetView.HeadNumber() { + return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", blockNumber, f.targetView.HeadNumber()) } if !f.indexedRange.blocks.Includes(blockNumber) { return nil, errUnindexedRange } - finished := blockNumber == f.targetView.headNumber + finished := blockNumber == f.targetView.HeadNumber() l := &logIterator{ chainView: f.targetView, params: &f.Params, @@ -687,11 +687,11 @@ func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber, lvIndex uint6 // newLogIteratorFromMapBoundary creates a logIterator starting at the given // map boundary, according to the current targetView. func (f *FilterMaps) newLogIteratorFromMapBoundary(mapIndex uint32, startBlock, startLvPtr uint64) (*logIterator, error) { - if startBlock > f.targetView.headNumber { - return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", startBlock, f.targetView.headNumber) + if startBlock > f.targetView.HeadNumber() { + return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", startBlock, f.targetView.HeadNumber()) } // get block receipts - receipts := f.targetView.getReceipts(startBlock) + receipts := f.targetView.Receipts(startBlock) if receipts == nil { return nil, fmt.Errorf("receipts not found for start block %d", startBlock) } @@ -758,7 +758,7 @@ func (l *logIterator) next() error { if l.delimiter { l.delimiter = false l.blockNumber++ - l.receipts = l.chainView.getReceipts(l.blockNumber) + l.receipts = l.chainView.Receipts(l.blockNumber) if l.receipts == nil { return fmt.Errorf("receipts not found for block %d", l.blockNumber) } @@ -795,7 +795,7 @@ func (l *logIterator) enforceValidState() { } l.logIndex = 0 } - if l.blockNumber == l.chainView.headNumber { + if l.blockNumber == l.chainView.HeadNumber() { l.finished = true } else { l.delimiter = true diff --git a/core/filtermaps/matcher.go b/core/filtermaps/matcher.go index 5738bf166a4..a5eeaaa5f04 100644 --- a/core/filtermaps/matcher.go +++ b/core/filtermaps/matcher.go @@ -57,7 +57,7 @@ type MatcherBackend interface { // all states of the chain since the previous SyncLogIndex or the creation of // the matcher backend. type SyncRange struct { - HeadNumber uint64 + IndexedView *ChainView // 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. diff --git a/core/filtermaps/matcher_backend.go b/core/filtermaps/matcher_backend.go index 335ac845510..ee18a0694c4 100644 --- a/core/filtermaps/matcher_backend.go +++ b/core/filtermaps/matcher_backend.go @@ -128,7 +128,7 @@ func (fm *FilterMapsMatcherBackend) synced() { indexedBlocks.SetAfterLast(indexedBlocks.Last()) // remove partially indexed last block } fm.syncCh <- SyncRange{ - HeadNumber: fm.f.targetView.headNumber, + IndexedView: fm.f.indexedView, ValidBlocks: fm.validBlocks, IndexedBlocks: indexedBlocks, } @@ -154,7 +154,7 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange case <-ctx.Done(): return SyncRange{}, ctx.Err() case <-fm.f.disabledCh: - return SyncRange{HeadNumber: fm.f.targetView.headNumber}, nil + return SyncRange{IndexedView: fm.f.indexedView}, nil } select { case vr := <-syncCh: @@ -162,7 +162,7 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange case <-ctx.Done(): return SyncRange{}, ctx.Err() case <-fm.f.disabledCh: - return SyncRange{HeadNumber: fm.f.targetView.headNumber}, nil + return SyncRange{IndexedView: fm.f.indexedView}, nil } } diff --git a/eth/api_backend.go b/eth/api_backend.go index 64fb58a1fdd..10f7ffcbce7 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -443,6 +443,14 @@ func (b *EthAPIBackend) RPCTxFeeCap() float64 { return b.eth.config.RPCTxFeeCap } +func (b *EthAPIBackend) CurrentView() *filtermaps.ChainView { + head := b.eth.blockchain.CurrentBlock() + if head == nil { + return nil + } + return filtermaps.NewChainView(b.eth.blockchain, head.Number.Uint64(), head.Hash()) +} + func (b *EthAPIBackend) NewMatcherBackend() filtermaps.MatcherBackend { return b.eth.filterMaps.NewMatcherBackend() } diff --git a/eth/filters/filter.go b/eth/filters/filter.go index 78c80d8f264..dd6643c59e1 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -146,25 +146,29 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { } const ( - rangeLogsTestSync = iota - rangeLogsTestTrimmed - rangeLogsTestIndexed - rangeLogsTestUnindexed - rangeLogsTestDone + rangeLogsTestDone = iota // zero range + rangeLogsTestSync // before sync; zero range + rangeLogsTestSynced // after sync; valid blocks range + rangeLogsTestIndexed // individual search range + rangeLogsTestUnindexed // individual search range + rangeLogsTestResults // results range after search iteration + rangeLogsTestReorg // results range trimmed by reorg ) type rangeLogsTestEvent struct { - event int - begin, end uint64 + event int + blocks common.Range[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 + ctx context.Context + filter *Filter + mb filtermaps.MatcherBackend + syncRange filtermaps.SyncRange // latest synchronized state with the matcher + chainView *filtermaps.ChainView // can be more recent than the indexed view in syncRange + // block ranges always refer to the current chainView + firstBlock, lastBlock uint64 // specified search range; MaxUint64 means latest block 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 @@ -182,84 +186,99 @@ func newSearchSession(ctx context.Context, filter *Filter, mb filtermaps.Matcher } // 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 { + var err error + s.syncRange, err = s.mb.SyncLogIndex(s.ctx) + if err != nil { + return nil, err + } + if err := s.updateChainView(); err != nil { return nil, err } return s, nil } -// 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()} +// updateChainView updates to the latest view of the underlying chain and sets +// searchRange by replacing MaxUint64 (meaning latest block) with actual head +// number in the specified search range. +// If the session already had an existing chain view and set of matches then +// it also trims part of the match set that a chain reorg might have invalidated. +func (s *searchSession) updateChainView() error { + // update chain view based on current chain head (might be more recent than + // the indexed view of syncRange as the indexer updates it asynchronously + // with some delay + newChainView := s.filter.sys.backend.CurrentView() + if newChainView == nil { + return errors.New("head block not available") + } + head := newChainView.HeadNumber() + + // update actual search range based on current head number + firstBlock, lastBlock := s.firstBlock, s.lastBlock + if firstBlock == math.MaxUint64 { + firstBlock = head } - var err error - s.syncRange, err = s.mb.SyncLogIndex(s.ctx) - if err != nil { - return err + if lastBlock == math.MaxUint64 { + lastBlock = head } - // 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} - } + if firstBlock > lastBlock || lastBlock > head { + return errInvalidBlockRange } + s.searchRange = common.NewRange(firstBlock, lastBlock+1-firstBlock) + + // Trim existing match set in case a reorg may have invalidated some results + if !s.matchRange.IsEmpty() { + trimRange := newChainView.SharedRange(s.chainView).Intersection(s.searchRange) + s.matchRange, s.matches = s.trimMatches(trimRange, s.matchRange, s.matches) + } + s.chainView = newChainView return nil } -// 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 +// trimMatches removes any entries from the specified set of matches that is +// outside the given range. +func (s *searchSession) trimMatches(trimRange, matchRange common.Range[uint64], matches []*types.Log) (common.Range[uint64], []*types.Log) { + newRange := matchRange.Intersection(trimRange) + if newRange == matchRange { + return matchRange, matches } - for len(s.matches) > 0 && s.matches[0].BlockNumber < s.matchRange.First() { - s.matches = s.matches[1:] + if newRange.IsEmpty() { + return newRange, nil } - for len(s.matches) > 0 && s.matches[len(s.matches)-1].BlockNumber > s.matchRange.Last() { - s.matches = s.matches[:len(s.matches)-1] + for len(matches) > 0 && matches[0].BlockNumber < newRange.First() { + matches = matches[1:] } + for len(matches) > 0 && matches[len(matches)-1].BlockNumber > newRange.Last() { + matches = matches[:len(matches)-1] + } + return newRange, matches } // 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() +func (s *searchSession) searchInRange(r common.Range[uint64], indexed bool) (common.Range[uint64], []*types.Log, error) { if indexed { if s.filter.rangeLogsTestHook != nil { - s.filter.rangeLogsTestHook <- rangeLogsTestEvent{rangeLogsTestIndexed, first, last} + s.filter.rangeLogsTestHook <- rangeLogsTestEvent{rangeLogsTestIndexed, r} } - results, err := s.filter.indexedLogs(s.ctx, s.mb, first, last) - if err != filtermaps.ErrMatchAll { - return results, err + results, err := s.filter.indexedLogs(s.ctx, s.mb, r.First(), r.Last()) + if err != nil && !errors.Is(err, filtermaps.ErrMatchAll) { + return common.Range[uint64]{}, nil, err + } + if err == nil { + // sync with filtermaps matcher + if s.filter.rangeLogsTestHook != nil { + s.filter.rangeLogsTestHook <- rangeLogsTestEvent{rangeLogsTestSync, common.Range[uint64]{}} + } + var syncErr error + if s.syncRange, syncErr = s.mb.SyncLogIndex(s.ctx); syncErr != nil { + return common.Range[uint64]{}, nil, syncErr + } + if s.filter.rangeLogsTestHook != nil { + s.filter.rangeLogsTestHook <- rangeLogsTestEvent{rangeLogsTestSynced, s.syncRange.ValidBlocks} + } + // discard everything that might be invalid + trimRange := s.syncRange.ValidBlocks.Intersection(s.chainView.SharedRange(s.syncRange.IndexedView)) + matchRange, matches := s.trimMatches(trimRange, r, results) + return matchRange, matches, nil } // "match all" filters are not supported by filtermaps; fall back to // unindexed search which is the most efficient in this case @@ -267,79 +286,85 @@ func (s *searchSession) searchInRange(r common.Range[uint64], indexed bool) ([]* // fall through to unindexed case } if s.filter.rangeLogsTestHook != nil { - s.filter.rangeLogsTestHook <- rangeLogsTestEvent{rangeLogsTestUnindexed, first, last} + s.filter.rangeLogsTestHook <- rangeLogsTestEvent{rangeLogsTestUnindexed, r} + } + matches, err := s.filter.unindexedLogs(s.ctx, s.chainView, r.First(), r.Last()) + if err != nil { + return common.Range[uint64]{}, nil, err } - return s.filter.unindexedLogs(s.ctx, first, last) + return r, matches, nil } // 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) + matchRange, matches, err := s.searchInRange(indexedSearchRange, true) if err != nil { return err } - return s.syncMatcher(0) // trim everything that the matcher considers potentially invalid + s.matchRange = matchRange + s.matches = matches + return nil } 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) + // no intersection of indexed and searched range; unindexed search on + // the whole searched range + matchRange, 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 + s.matchRange = matchRange + s.matches = matches + return nil } 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 + // Results are available, but the tail section is missing. Perform an unindexed + // search for the missing tail, while still allowing indexed search for the head. + // + // The unindexed search is necessary because the tail portion of the indexes + // has been pruned. tailRange := common.NewRange(s.searchRange.First(), s.matchRange.First()-s.searchRange.First()) - tailMatches, err := s.searchInRange(tailRange, false) + _, 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 + return nil case !s.matchRange.IsEmpty() && s.matchRange.First() == s.searchRange.First() && s.searchRange.AfterLast() > s.matchRange.AfterLast(): - // we have results but head section is missing + // Results are available, but the head section is missing. Try to perform + // the indexed search for the missing head, or fallback to unindexed search + // if the tail portion of indexed range has been pruned. 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 { + // The tail portion of the indexes has been pruned, falling back + // to unindexed search. s.forceUnindexed = true } } - headMatches, err := s.searchInRange(headRange, !s.forceUnindexed) + headMatchRange, 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 + if headMatchRange.First() != s.matchRange.AfterLast() { + // improbable corner case, first part of new head range invalidated by tail unindexing + s.matches, s.matchRange = headMatches, headMatchRange + return nil } + s.matches = append(s.matches, headMatches...) + s.matchRange = s.matchRange.Union(headMatchRange) + return nil default: panic("invalid search session state") @@ -349,7 +374,7 @@ func (s *searchSession) doSearchIteration() error { 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} + f.rangeLogsTestHook <- rangeLogsTestEvent{rangeLogsTestDone, common.Range[uint64]{}} close(f.rangeLogsTestHook) }() } @@ -366,7 +391,17 @@ func (f *Filter) rangeLogs(ctx context.Context, firstBlock, lastBlock uint64) ([ } for session.searchRange != session.matchRange { if err := session.doSearchIteration(); err != nil { - return session.matches, err + return nil, err + } + if f.rangeLogsTestHook != nil { + f.rangeLogsTestHook <- rangeLogsTestEvent{rangeLogsTestResults, session.matchRange} + } + mr := session.matchRange + if err := session.updateChainView(); err != nil { + return nil, err + } + if f.rangeLogsTestHook != nil && session.matchRange != mr { + f.rangeLogsTestHook <- rangeLogsTestEvent{rangeLogsTestReorg, session.matchRange} } } return session.matches, nil @@ -382,7 +417,7 @@ func (f *Filter) indexedLogs(ctx context.Context, mb filtermaps.MatcherBackend, // unindexedLogs returns the logs matching the filter criteria based on raw block // iteration and bloom matching. -func (f *Filter) unindexedLogs(ctx context.Context, begin, end uint64) ([]*types.Log, error) { +func (f *Filter) unindexedLogs(ctx context.Context, chainView *filtermaps.ChainView, begin, end uint64) ([]*types.Log, error) { start := time.Now() log.Debug("Performing unindexed log search", "begin", begin, "end", end) var matches []*types.Log @@ -392,9 +427,14 @@ func (f *Filter) unindexedLogs(ctx context.Context, begin, end uint64) ([]*types return matches, ctx.Err() default: } - header, err := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber)) - if header == nil || err != nil { - return matches, err + if blockNumber > chainView.HeadNumber() { + // check here so that we can return matches up until head along with + // the error + return matches, errInvalidBlockRange + } + header := chainView.Header(blockNumber) + if header == nil { + return matches, errors.New("header not found") } found, err := f.blockLogs(ctx, header) if err != nil { diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index b787f1067b8..10e433f09bc 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -71,6 +71,7 @@ type Backend interface { SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription + CurrentView() *filtermaps.ChainView NewMatcherBackend() filtermaps.MatcherBackend } diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 3bb019d105f..fa5d4fe8973 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -154,6 +154,11 @@ func (b *testBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subsc return b.chainFeed.Subscribe(ch) } +func (b *testBackend) CurrentView() *filtermaps.ChainView { + head := b.CurrentBlock() + return filtermaps.NewChainView(b, head.Number.Uint64(), head.Hash()) +} + func (b *testBackend) NewMatcherBackend() filtermaps.MatcherBackend { return b.fm.NewMatcherBackend() } diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index 4026c03e892..d6065230f8d 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -453,7 +453,8 @@ func TestRangeLogs(t *testing.T) { addresses = []common.Address{{}} ) - expEvent := func(exp rangeLogsTestEvent) { + expEvent := func(expEvent int, expFirst, expAfterLast uint64) { + exp := rangeLogsTestEvent{expEvent, common.NewRange[uint64](expFirst, expAfterLast-expFirst)} event++ ev := <-filter.rangeLogsTestHook if ev != exp { @@ -472,7 +473,6 @@ func TestRangeLogs(t *testing.T) { for range filter.rangeLogsTestHook { } }(filter) - expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 0, 0}) } updateHead := func() { @@ -483,81 +483,122 @@ func TestRangeLogs(t *testing.T) { // 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}) + expEvent(rangeLogsTestIndexed, 401, 501) + expEvent(rangeLogsTestSync, 0, 0) + expEvent(rangeLogsTestSynced, 401, 601) + expEvent(rangeLogsTestResults, 401, 501) + expEvent(rangeLogsTestUnindexed, 300, 401) 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}) + expEvent(rangeLogsTestResults, 300, 501) + expEvent(rangeLogsTestDone, 0, 0) // test case #2 newFilter(400, int64(rpc.LatestBlockNumber)) - expEvent(rangeLogsTestEvent{rangeLogsTestIndexed, 501, 700}) + expEvent(rangeLogsTestIndexed, 501, 701) 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 { + expEvent(rangeLogsTestSync, 0, 0) + expEvent(rangeLogsTestSynced, 601, 699) + expEvent(rangeLogsTestResults, 601, 699) + expEvent(rangeLogsTestUnindexed, 400, 601) + expEvent(rangeLogsTestResults, 400, 699) + expEvent(rangeLogsTestIndexed, 699, 801) + if _, err := bc.SetCanonical(chain[749]); err != nil { // set head to block 750 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}) + expEvent(rangeLogsTestSync, 0, 0) + expEvent(rangeLogsTestSynced, 601, 749) + expEvent(rangeLogsTestResults, 400, 749) + expEvent(rangeLogsTestIndexed, 749, 751) + expEvent(rangeLogsTestSync, 0, 0) + expEvent(rangeLogsTestSynced, 551, 751) + expEvent(rangeLogsTestResults, 400, 751) + expEvent(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 { + expEvent(rangeLogsTestIndexed, 750, 751) + if _, err := bc.SetCanonical(chain[739]); err != nil { t.Fatal(err) } updateHead() - expEvent(rangeLogsTestEvent{rangeLogsTestSync, 750, 750}) - expEvent(rangeLogsTestEvent{rangeLogsTestTrimmed, 0, 0}) - expEvent(rangeLogsTestEvent{rangeLogsTestIndexed, 740, 740}) + expEvent(rangeLogsTestSync, 0, 0) + expEvent(rangeLogsTestSynced, 551, 739) + expEvent(rangeLogsTestResults, 0, 0) + expEvent(rangeLogsTestIndexed, 740, 741) 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}) + expEvent(rangeLogsTestSync, 0, 0) + expEvent(rangeLogsTestSynced, 551, 739) + expEvent(rangeLogsTestResults, 0, 0) + expEvent(rangeLogsTestIndexed, 750, 751) + expEvent(rangeLogsTestSync, 0, 0) + expEvent(rangeLogsTestSynced, 551, 751) + expEvent(rangeLogsTestResults, 750, 751) + expEvent(rangeLogsTestDone, 0, 0) // test case #4 + if _, err := bc.SetCanonical(chain[499]); err != nil { + t.Fatal(err) + } + updateHead() 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}) + expEvent(rangeLogsTestIndexed, 400, 501) + if _, err := bc.InsertChain(chain[500:650]); err != nil { + t.Fatal(err) + } + updateHead() + expEvent(rangeLogsTestSync, 0, 0) + expEvent(rangeLogsTestSynced, 451, 499) + expEvent(rangeLogsTestResults, 451, 499) + expEvent(rangeLogsTestUnindexed, 400, 451) + expEvent(rangeLogsTestResults, 400, 499) + // indexed head extension seems possible + expEvent(rangeLogsTestIndexed, 499, 651) + // further head extension causes tail unindexing in searched range + if _, err := bc.InsertChain(chain[650:750]); err != nil { + t.Fatal(err) + } + updateHead() + expEvent(rangeLogsTestSync, 0, 0) + expEvent(rangeLogsTestSynced, 551, 649) + // tail trimmed to 551; cannot merge with existing results + expEvent(rangeLogsTestResults, 551, 649) + expEvent(rangeLogsTestUnindexed, 400, 551) + expEvent(rangeLogsTestResults, 400, 649) + expEvent(rangeLogsTestIndexed, 649, 751) + expEvent(rangeLogsTestSync, 0, 0) + expEvent(rangeLogsTestSynced, 551, 751) + expEvent(rangeLogsTestResults, 400, 751) + expEvent(rangeLogsTestDone, 0, 0) + + // test case #5 + newFilter(400, int64(rpc.LatestBlockNumber)) + expEvent(rangeLogsTestIndexed, 551, 751) + expEvent(rangeLogsTestSync, 0, 0) + expEvent(rangeLogsTestSynced, 551, 751) + expEvent(rangeLogsTestResults, 551, 751) + expEvent(rangeLogsTestUnindexed, 400, 551) 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}) + expEvent(rangeLogsTestResults, 400, 751) + // indexed tail already beyond results head; revert to unindexed head search + expEvent(rangeLogsTestUnindexed, 751, 1001) + if _, err := bc.SetCanonical(chain[899]); err != nil { + t.Fatal(err) + } + updateHead() + expEvent(rangeLogsTestResults, 400, 1001) + expEvent(rangeLogsTestReorg, 400, 901) + expEvent(rangeLogsTestDone, 0, 0) } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 3fbf32e22e3..aff051937d5 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -619,6 +619,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) CurrentView() *filtermaps.ChainView { + panic("implement me") +} func (b testBackend) NewMatcherBackend() filtermaps.MatcherBackend { panic("implement me") } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index c4bf2e05910..e28cb932962 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -95,6 +95,7 @@ type Backend interface { SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription + CurrentView() *filtermaps.ChainView NewMatcherBackend() filtermaps.MatcherBackend } diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index b4d11a3033f..9dd6a547290 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -401,6 +401,7 @@ func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) func (b *backendMock) Engine() consensus.Engine { return nil } +func (b *backendMock) CurrentView() *filtermaps.ChainView { return nil } func (b *backendMock) NewMatcherBackend() filtermaps.MatcherBackend { return nil } func (b *backendMock) HistoryPruningCutoff() uint64 { return 0 } From 5a7bbb423feb88ec8997dff57c3b644cc6c4f143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Sun, 20 Apr 2025 12:54:40 +0200 Subject: [PATCH 116/658] beacon/params, core/filtermaps: update checkpoints (#31674) This PR updates checkpoints for blsync and filtermaps. --- beacon/params/checkpoint_holesky.hex | 2 +- beacon/params/checkpoint_mainnet.hex | 2 +- beacon/params/checkpoint_sepolia.hex | 2 +- core/filtermaps/checkpoints_holesky.json | 3 ++- core/filtermaps/checkpoints_mainnet.json | 9 ++++++++- core/filtermaps/checkpoints_sepolia.json | 6 +++++- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/beacon/params/checkpoint_holesky.hex b/beacon/params/checkpoint_holesky.hex index f1e27b48f65..740d7aba21c 100644 --- a/beacon/params/checkpoint_holesky.hex +++ b/beacon/params/checkpoint_holesky.hex @@ -1 +1 @@ -0xf5606a019f0f1006e9ec2070695045f4334450362a48da4c5965314510e0b4c3 \ No newline at end of file +0xd60e5310c5d52ced44cfb13be4e9f22a1e6a6dc56964c3cccd429182d26d72d0 \ No newline at end of file diff --git a/beacon/params/checkpoint_mainnet.hex b/beacon/params/checkpoint_mainnet.hex index a11c3394bb5..45f065ca156 100644 --- a/beacon/params/checkpoint_mainnet.hex +++ b/beacon/params/checkpoint_mainnet.hex @@ -1 +1 @@ -0x3c0cb4aa83beded1803d262664ba4392b1023f334d9cca02dc3a6925987ebe91 \ No newline at end of file +0x02f0bb348b0d45f95a9b7e2bb5705768ad06548876cee03d880a2c9dabb1ff88 \ No newline at end of file diff --git a/beacon/params/checkpoint_sepolia.hex b/beacon/params/checkpoint_sepolia.hex index e4f2f96733f..3d1b2885b32 100644 --- a/beacon/params/checkpoint_sepolia.hex +++ b/beacon/params/checkpoint_sepolia.hex @@ -1 +1 @@ -0xa8d56457aa414523d93659aef1f7409bbfb72ad75e94d917c8c0b1baa38153ef \ No newline at end of file +0xa0dad451a230c01be6f2492980ec5bb412d8cf33351a75e8b172b5b84a5fd03a \ No newline at end of file diff --git a/core/filtermaps/checkpoints_holesky.json b/core/filtermaps/checkpoints_holesky.json index b8f6be9cafe..a56611cc8e1 100644 --- a/core/filtermaps/checkpoints_holesky.json +++ b/core/filtermaps/checkpoints_holesky.json @@ -17,5 +17,6 @@ {"blockNumber": 3101669, "blockId": "0xa6e66a18fed64d33178c7088f273c404be597662c34f6e6884cf2e24f3ea4ace", "firstIndex": 1073741353}, {"blockNumber": 3221725, "blockId": "0xe771f897dece48b1583cc1d1d10de8015da57407eb1fdf239fdbe46eaab85143", "firstIndex": 1140850137}, {"blockNumber": 3357164, "blockId": "0x6252d0aa54c79623b0680069c88d7b5c47983f0d5c4845b6c811b8d9b5e8ff3c", "firstIndex": 1207959453}, -{"blockNumber": 3447019, "blockId": "0xeb7d585e1e063f3cc05ed399fbf6c2df63c271f62f030acb804e9fb1e74b6dc1", "firstIndex": 1275067542} +{"blockNumber": 3447019, "blockId": "0xeb7d585e1e063f3cc05ed399fbf6c2df63c271f62f030acb804e9fb1e74b6dc1", "firstIndex": 1275067542}, +{"blockNumber": 3546397, "blockId": "0xdabdef7defa4281180a57c5af121877b82274f15ccf074ea0096146f4c246df2", "firstIndex": 1342176778} ] diff --git a/core/filtermaps/checkpoints_mainnet.json b/core/filtermaps/checkpoints_mainnet.json index ca30280e030..70a08b1aaf7 100644 --- a/core/filtermaps/checkpoints_mainnet.json +++ b/core/filtermaps/checkpoints_mainnet.json @@ -260,5 +260,12 @@ {"blockNumber": 21959188, "blockId": "0x6b9c482cc4822af2ad62a359b923062556ab6a046d1a39a8243b764848690601", "firstIndex": 17381193886}, {"blockNumber": 21994911, "blockId": "0x0d99ef9dd42dd1c62fc5249eb4f618e7672ab93fa0ba7545c77360371cf972e5", "firstIndex": 17448302795}, {"blockNumber": 22026007, "blockId": "0xf7cdc7f6694f2c2ed31fa8a607f65cfa59d0dd7d7424ab5c017f959ae2982c71", "firstIndex": 17515413036}, -{"blockNumber": 22059890, "blockId": "0x82b768a0dddefda2eefd3a33596ea2be075312f1dd4b01f6b0d377faca2af98b", "firstIndex": 17582521768} +{"blockNumber": 22059890, "blockId": "0x82b768a0dddefda2eefd3a33596ea2be075312f1dd4b01f6b0d377faca2af98b", "firstIndex": 17582521768}, +{"blockNumber": 22090784, "blockId": "0xf97c2eaf9a550360ac24000c0ff17ffa388a2bdd6f73f2f36718e332edfa107a", "firstIndex": 17649630983}, +{"blockNumber": 22121157, "blockId": "0xa790025235db782e899f23d8b09663ec2d74ec149e4125d62989f98829b08e2d", "firstIndex": 17716724973}, +{"blockNumber": 22148056, "blockId": "0xbe25ac4f1bdd89a7db5782bf1157e6c4378d80220d67a029397853ef16cd1a4c", "firstIndex": 17783838366}, +{"blockNumber": 22168652, "blockId": "0x6ae43618c915e636794e2cc2d75dde9992766881c7405fe6479c045ed4bee57e", "firstIndex": 17850956277}, +{"blockNumber": 22190975, "blockId": "0x9437121647899a4b7b84d67fbea7cc6ff967481c2eab4328ccd86e2cefe19420", "firstIndex": 17918066140}, +{"blockNumber": 22234357, "blockId": "0x036030830134f9224160d5a0b62da35ec7813dc8855d554bd22e9d38545243ed", "firstIndex": 17985175075}, +{"blockNumber": 22276736, "blockId": "0x5ceb96d98aa1b4c1c2f2fa253ae9cdb1b04e0420c11bf795400e8762c0a1635c", "firstIndex": 18052284344} ] diff --git a/core/filtermaps/checkpoints_sepolia.json b/core/filtermaps/checkpoints_sepolia.json index c6d3cd90869..8d799daefd8 100644 --- a/core/filtermaps/checkpoints_sepolia.json +++ b/core/filtermaps/checkpoints_sepolia.json @@ -64,5 +64,9 @@ {"blockNumber": 7730079, "blockId": "0x08e0d511a93e2a9c004b3456d77d687ae86247417ada1cdd1f85332a62dfed1d", "firstIndex": 4227846795}, {"blockNumber": 7777515, "blockId": "0xeba8faed2e12bb9ed5e7fad19dd82662f15f6f4f512a0d232eedf1e676712428", "firstIndex": 4294966082}, {"blockNumber": 7828860, "blockId": "0x62c054bd24be351dc4777460ee922a75fdcea5c9830455cbac6fa29552719c85", "firstIndex": 4362076087}, -{"blockNumber": 7869890, "blockId": "0x5664bcfa6151f798781e22bea4f9a2c796d097402350cb131e4e3c78e13e461a", "firstIndex": 4429183267} +{"blockNumber": 7869890, "blockId": "0x5664bcfa6151f798781e22bea4f9a2c796d097402350cb131e4e3c78e13e461a", "firstIndex": 4429183267}, +{"blockNumber": 7911722, "blockId": "0x9a85e48e3135c97c51fc1786f2af0596c802e021b6c53cfca65a129cafcd23ed", "firstIndex": 4496287265}, +{"blockNumber": 7960147, "blockId": "0xc9359cc76d7090e1c8a031108f0ab7a8935d971efd4325fe53612a1d99562f6f", "firstIndex": 4563402388}, +{"blockNumber": 8030418, "blockId": "0x21867e68cd8327aed2da2601399d60f7f9e41dca4a4f2f9be982e5a2b9304a88", "firstIndex": 4630511616}, +{"blockNumber": 8087701, "blockId": "0x0fa8c8d7549cc9a8d308262706fe248efe759f8b63511efb1e7f3926e9af2dcb", "firstIndex": 4697614758} ] From 14f15430bb99646f516e239271f21e4a52584666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Mon, 21 Apr 2025 09:27:24 +0200 Subject: [PATCH 117/658] core/filtermaps: clone cached slices, fix tempRange (#31680) This PR ensures that caching a slice or a slice of slices will never affect the original version by always cloning a slice fetched from cache if it is not used in a guaranteed read only way. --- core/filtermaps/filtermaps.go | 9 ++++-- core/filtermaps/indexer.go | 3 ++ core/filtermaps/indexer_test.go | 52 +++++++++++++++++++++++++++++++++ core/filtermaps/map_renderer.go | 6 ++-- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index a617de8968f..920167ca8d5 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -128,6 +128,7 @@ type FilterMaps struct { // test hooks testDisableSnapshots, testSnapshotUsed bool + testProcessEventsHook func() } // filterMap is a full or partial in-memory representation of a filter map where @@ -573,7 +574,7 @@ func (f *FilterMaps) getFilterMapRow(mapIndex, rowIndex uint32, baseLayerOnly bo } f.baseRowsCache.Add(baseMapRowIndex, baseRows) } - baseRow := baseRows[mapIndex&(f.baseRowGroupLength-1)] + baseRow := slices.Clone(baseRows[mapIndex&(f.baseRowGroupLength-1)]) if baseLayerOnly { return baseRow, nil } @@ -610,7 +611,9 @@ func (f *FilterMaps) storeFilterMapRowsOfGroup(batch ethdb.Batch, mapIndices []u if uint32(len(mapIndices)) != f.baseRowGroupLength { // skip base rows read if all rows are replaced var ok bool baseRows, ok = f.baseRowsCache.Get(baseMapRowIndex) - if !ok { + if ok { + baseRows = slices.Clone(baseRows) + } else { var err error baseRows, err = rawdb.ReadFilterMapBaseRows(f.db, baseMapRowIndex, f.baseRowGroupLength, f.logMapWidth) if err != nil { @@ -656,7 +659,7 @@ func (f *FilterMaps) mapRowIndex(mapIndex, rowIndex uint32) uint64 { // called from outside the indexerLoop goroutine. func (f *FilterMaps) getBlockLvPointer(blockNumber uint64) (uint64, error) { if blockNumber >= f.indexedRange.blocks.AfterLast() && f.indexedRange.headIndexed { - return f.indexedRange.headDelimiter, nil + return f.indexedRange.headDelimiter + 1, nil } if lvPointer, ok := f.lvPointerCache.Get(blockNumber); ok { return lvPointer, nil diff --git a/core/filtermaps/indexer.go b/core/filtermaps/indexer.go index 383ec078c96..787197345a0 100644 --- a/core/filtermaps/indexer.go +++ b/core/filtermaps/indexer.go @@ -165,6 +165,9 @@ func (f *FilterMaps) waitForNewHead() { // processEvents processes all events, blocking only if a block processing is // happening and indexing should be suspended. func (f *FilterMaps) processEvents() { + if f.testProcessEventsHook != nil { + f.testProcessEventsHook() + } for f.processSingleEvent(f.blockProcessing) { } } diff --git a/core/filtermaps/indexer_test.go b/core/filtermaps/indexer_test.go index 4dddd27087e..e60130ba4bd 100644 --- a/core/filtermaps/indexer_test.go +++ b/core/filtermaps/indexer_test.go @@ -219,6 +219,58 @@ func testIndexerMatcherView(t *testing.T, concurrentRead bool) { } } +func TestLogsByIndex(t *testing.T) { + ts := newTestSetup(t) + defer func() { + ts.fm.testProcessEventsHook = nil + ts.close() + }() + + ts.chain.addBlocks(1000, 10, 3, 4, true) + ts.setHistory(0, false) + ts.fm.WaitIdle() + firstLog := make([]uint64, 1001) // first valid log position per block + lastLog := make([]uint64, 1001) // last valid log position per block + for i := uint64(0); i <= ts.fm.indexedRange.headDelimiter; i++ { + log, err := ts.fm.getLogByLvIndex(i) + if err != nil { + t.Fatalf("Error getting log by index %d: %v", i, err) + } + if log != nil { + if firstLog[log.BlockNumber] == 0 { + firstLog[log.BlockNumber] = i + } + lastLog[log.BlockNumber] = i + } + } + var failed bool + ts.fm.testProcessEventsHook = func() { + if ts.fm.indexedRange.blocks.IsEmpty() { + return + } + if lvi := firstLog[ts.fm.indexedRange.blocks.First()]; lvi != 0 { + log, err := ts.fm.getLogByLvIndex(lvi) + if log == nil || err != nil { + t.Errorf("Error getting first log of indexed block range: %v", err) + failed = true + } + } + if lvi := lastLog[ts.fm.indexedRange.blocks.Last()]; lvi != 0 { + log, err := ts.fm.getLogByLvIndex(lvi) + if log == nil || err != nil { + t.Errorf("Error getting last log of indexed block range: %v", err) + failed = true + } + } + } + chain := ts.chain.getCanonicalChain() + for i := 0; i < 1000 && !failed; i++ { + head := rand.Intn(len(chain)) + ts.chain.setCanonicalChain(chain[:head+1]) + ts.fm.WaitIdle() + } +} + func TestIndexerCompareDb(t *testing.T) { ts := newTestSetup(t) defer ts.close() diff --git a/core/filtermaps/map_renderer.go b/core/filtermaps/map_renderer.go index 7c2aa8dc32c..f59a01c032a 100644 --- a/core/filtermaps/map_renderer.go +++ b/core/filtermaps/map_renderer.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "math" + "slices" "sort" "time" @@ -107,7 +108,7 @@ func (f *FilterMaps) renderMapsFromSnapshot(cp *renderedMap) (*mapRenderer, erro filterMap: cp.filterMap.fullCopy(), mapIndex: cp.mapIndex, lastBlock: cp.lastBlock, - blockLvPtrs: cp.blockLvPtrs, + blockLvPtrs: slices.Clone(cp.blockLvPtrs), }, finishedMaps: make(map[uint32]*renderedMap), finished: common.NewRange(cp.mapIndex, 0), @@ -244,7 +245,7 @@ func (f *FilterMaps) loadHeadSnapshot() error { } } f.renderSnapshots.Add(f.indexedRange.blocks.Last(), &renderedMap{ - filterMap: fm, + filterMap: fm.fullCopy(), mapIndex: f.indexedRange.maps.Last(), lastBlock: f.indexedRange.blocks.Last(), lastBlockId: f.indexedView.BlockId(f.indexedRange.blocks.Last()), @@ -536,6 +537,7 @@ func (r *mapRenderer) getTempRange() (filterMapsRange, error) { } else { tempRange.blocks.SetAfterLast(0) } + tempRange.headIndexed = false tempRange.headDelimiter = 0 } return tempRange, nil From 74165a8fe31847fa4161929c6a9b00ac07251c5f Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 21 Apr 2025 14:56:16 +0200 Subject: [PATCH 118/658] version: release go-ethereum v1.15.9 stable --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index c969ab479eb..53a1bae2809 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 9 // Patch version component of the current release - Meta = "unstable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 9 // Patch version component of the current release + Meta = "stable" // Version metadata to append to the version string ) From 263aef9cc41135a9207c8e788beabc39506430c0 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 21 Apr 2025 14:57:25 +0200 Subject: [PATCH 119/658] version: begin v1.15.10 release cycle --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 53a1bae2809..2bbd7e7b17b 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 9 // Patch version component of the current release - Meta = "stable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 10 // Patch version component of the current release + Meta = "unstable" // Version metadata to append to the version string ) From 1591d165c44e027c961d174c0f4ee1bb45a82520 Mon Sep 17 00:00:00 2001 From: georgehao Date: Tue, 22 Apr 2025 12:57:17 +0800 Subject: [PATCH 120/658] internal/debug: add debug_setMemoryLimit (#31441) --- internal/debug/api.go | 19 +++++++++++++++++++ internal/web3ext/web3ext.go | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/internal/debug/api.go b/internal/debug/api.go index 5e93585bf83..1bac36e908d 100644 --- a/internal/debug/api.go +++ b/internal/debug/api.go @@ -35,6 +35,7 @@ import ( "sync" "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/hashicorp/go-bexpr" ) @@ -237,6 +238,24 @@ func (*HandlerT) SetGCPercent(v int) int { return debug.SetGCPercent(v) } +// SetMemoryLimit sets the GOMEMLIMIT for the process. It returns the previous limit. +// Note: +// +// - The input limit is provided as bytes. A negative input does not adjust the limit +// +// - A zero limit or a limit that's lower than the amount of memory used by the Go +// runtime may cause the garbage collector to run nearly continuously. However, +// the application may still make progress. +// +// - Setting the limit too low will cause Geth to become unresponsive. +// +// - Geth also allocates memory off-heap, particularly for fastCache and Pebble, +// which can be non-trivial (a few gigabytes by default). +func (*HandlerT) SetMemoryLimit(limit int64) int64 { + log.Info("Setting memory limit", "size", common.PrettyDuration(limit)) + return debug.SetMemoryLimit(limit) +} + func writeProfile(name, file string) error { p := pprof.Lookup(name) log.Info("Writing profile records", "count", p.Count(), "type", name, "dump", file) diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 8ac8f44958b..aed6bbbdb97 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -269,6 +269,11 @@ web3._extend({ call: 'debug_setGCPercent', params: 1, }), + new web3._extend.Method({ + name: 'setMemoryLimit', + call: 'debug_setMemoryLimit', + params: 1, + }), new web3._extend.Method({ name: 'memStats', call: 'debug_memStats', From 846c14e21f5f7ceffe47898a34a520895b1b0f11 Mon Sep 17 00:00:00 2001 From: colin <102356659+colinlyguo@users.noreply.github.com> Date: Thu, 24 Apr 2025 03:01:03 +0800 Subject: [PATCH 121/658] ethclient: allow passing AuthorizationList to calls (#31198) This PR adds the `AuthorizationList` field to the `CallMsg` interface to support `eth_call` and `eth_estimateGas` of set-code transactions. --------- Co-authored-by: Sina Mahmoodi --- core/blockchain_test.go | 2 +- core/state_processor_test.go | 3 +- ethclient/ethclient.go | 3 ++ ethclient/gethclient/gethclient.go | 3 ++ interfaces.go | 3 ++ internal/ethapi/api_test.go | 51 +++++++++++++++++++++++++++++- 6 files changed, 62 insertions(+), 3 deletions(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 289eff0a8f5..134deee237d 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -4099,7 +4099,7 @@ func TestEIP7702(t *testing.T) { // The way the auths are combined, it becomes // 1. tx -> addr1 which is delegated to 0xaaaa // 2. addr1:0xaaaa calls into addr2:0xbbbb - // 3. addr2:0xbbbb writes to storage + // 3. addr2:0xbbbb writes to storage auth1, _ := types.SignSetCode(key1, types.SetCodeAuthorization{ ChainID: *uint256.MustFromBig(gspec.Config.ChainID), Address: aa, diff --git a/core/state_processor_test.go b/core/state_processor_test.go index a6ca2781f81..ffdf0638122 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -255,7 +255,8 @@ func TestStateProcessorErrors(t *testing.T) { }, want: "could not apply tx 0 [0xc18d10f4c809dbdfa1a074c3300de9bc4b7f16a20f0ec667f6f67312b71b956a]: EIP-7702 transaction with empty auth list (sender 0x71562b71999873DB5b286dF957af199Ec94617F7)", }, - // ErrSetCodeTxCreate cannot be tested: it is impossible to create a SetCode-tx with nil `to`. + // ErrSetCodeTxCreate cannot be tested here: it is impossible to create a SetCode-tx with nil `to`. + // The EstimateGas API tests test this case. } { block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config, false) _, err := blockchain.InsertChain(types.Blocks{block}) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 872b3b03dc1..352e6abc2c2 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -754,6 +754,9 @@ func toCallArg(msg ethereum.CallMsg) interface{} { if msg.BlobHashes != nil { arg["blobVersionedHashes"] = msg.BlobHashes } + if msg.AuthorizationList != nil { + arg["authorizationList"] = msg.AuthorizationList + } return arg } diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go index 02b2598b37d..39ac4ff20fb 100644 --- a/ethclient/gethclient/gethclient.go +++ b/ethclient/gethclient/gethclient.go @@ -251,6 +251,9 @@ func toCallArg(msg ethereum.CallMsg) interface{} { if msg.BlobHashes != nil { arg["blobVersionedHashes"] = msg.BlobHashes } + if msg.AuthorizationList != nil { + arg["authorizationList"] = msg.AuthorizationList + } return arg } diff --git a/interfaces.go b/interfaces.go index 53e2e3ae169..54a215d6e78 100644 --- a/interfaces.go +++ b/interfaces.go @@ -156,6 +156,9 @@ type CallMsg struct { // For BlobTxType BlobGasFeeCap *big.Int BlobHashes []common.Hash + + // For SetCodeTxType + AuthorizationList []types.SetCodeAuthorization } // A ContractCaller provides contract calls, essentially transactions that are executed by diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index aff051937d5..5071e2412f0 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -668,6 +668,11 @@ func TestEstimateGas(t *testing.T) { b.SetPoS() })) + setCodeAuthorization, _ := types.SignSetCode(accounts[0].key, types.SetCodeAuthorization{ + Address: accounts[0].addr, + Nonce: uint64(genBlocks + 1), + }) + var testSuite = []struct { blockNumber rpc.BlockNumber call TransactionArgs @@ -846,6 +851,50 @@ func TestEstimateGas(t *testing.T) { }, want: 21000, }, + // Should be able to estimate SetCodeTx. + { + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{ + From: &accounts[0].addr, + To: &accounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(0)), + AuthorizationList: []types.SetCodeAuthorization{setCodeAuthorization}, + }, + want: 46000, + }, + // Should retrieve the code of 0xef0001 || accounts[0].addr and return an invalid opcode error. + { + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{ + From: &accounts[0].addr, + To: &accounts[0].addr, + Value: (*hexutil.Big)(big.NewInt(0)), + AuthorizationList: []types.SetCodeAuthorization{setCodeAuthorization}, + }, + expectErr: errors.New("invalid opcode: opcode 0xef not defined"), + }, + // SetCodeTx with empty authorization list should fail. + { + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{ + From: &accounts[0].addr, + To: &common.Address{}, + Value: (*hexutil.Big)(big.NewInt(0)), + AuthorizationList: []types.SetCodeAuthorization{}, + }, + expectErr: core.ErrEmptyAuthList, + }, + // SetCodeTx with nil `to` should fail. + { + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{ + From: &accounts[0].addr, + To: nil, + Value: (*hexutil.Big)(big.NewInt(0)), + AuthorizationList: []types.SetCodeAuthorization{setCodeAuthorization}, + }, + expectErr: core.ErrSetCodeTxCreate, + }, } for i, tc := range testSuite { result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides) @@ -855,7 +904,7 @@ func TestEstimateGas(t *testing.T) { continue } if !errors.Is(err, tc.expectErr) { - if !reflect.DeepEqual(err, tc.expectErr) { + if err.Error() != tc.expectErr.Error() { t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err) } } From 7ad97a4a0372cb96c774a93a7fe45f549d79d5a7 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 24 Apr 2025 18:19:10 +0200 Subject: [PATCH 122/658] miner: increase default gas limit to 36M (#31705) --- miner/miner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miner/miner.go b/miner/miner.go index 595ef8081c8..3cec054d198 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -52,7 +52,7 @@ type Config struct { // DefaultConfig contains default settings for miner. var DefaultConfig = Config{ - GasCeil: 30_000_000, + GasCeil: 36_000_000, GasPrice: big.NewInt(params.GWei / 1000), // The default recommit time is chosen as two seconds since From b62756d1a3b8b09430f5b060caa7382c8117fb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Thu, 24 Apr 2025 21:40:12 +0200 Subject: [PATCH 123/658] beacon/params: add mainnet electra config (#31706) This PR adds the electra beacon chain configuration for mainnet. --- beacon/params/networks.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/beacon/params/networks.go b/beacon/params/networks.go index 7e73a2c69c1..51f67e0c976 100644 --- a/beacon/params/networks.go +++ b/beacon/params/networks.go @@ -41,7 +41,8 @@ var ( AddFork("ALTAIR", 74240, []byte{1, 0, 0, 0}). AddFork("BELLATRIX", 144896, []byte{2, 0, 0, 0}). AddFork("CAPELLA", 194048, []byte{3, 0, 0, 0}). - AddFork("DENEB", 269568, []byte{4, 0, 0, 0}) + AddFork("DENEB", 269568, []byte{4, 0, 0, 0}). + AddFork("ELECTRA", 364032, []byte{5, 0, 0, 0}) SepoliaLightConfig = (&ChainConfig{ GenesisValidatorsRoot: common.HexToHash("0xd8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078"), From b6bdd698a0584a8e62a34b6aa6871e8bf8b6e6fb Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 25 Apr 2025 17:57:38 +0800 Subject: [PATCH 124/658] core/filtermaps: fix deadlock in filtermap callback (#31708) This PR fixes a deadlock situation is deleteTailEpoch that might arise when range delete is running in iterator based fallback mode (either using leveldb database or the hashdb state storage scheme). In this case a stopCb callback is called periodically that does check events, including matcher sync requests, in which case it tries to acquire indexLock for read access, while deleteTailEpoch already held it for write access. This pull request removes the indexLock acquiring in `FilterMapsMatcherBackend.synced` as this function is only called in the indexLoop. Fixes https://github.com/ethereum/go-ethereum/issues/31700 --- core/filtermaps/filtermaps.go | 50 ++++++++++++++++++++---------- core/filtermaps/indexer.go | 6 +++- core/filtermaps/matcher_backend.go | 18 +++++++---- 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index 920167ca8d5..3da7f4b721b 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -85,11 +85,17 @@ type FilterMaps struct { // fields written by the indexer and read by matcher backend. Indexer can // read them without a lock and write them under indexLock write lock. // Matcher backend can read them under indexLock read lock. - indexLock sync.RWMutex - indexedRange filterMapsRange - cleanedEpochsBefore uint32 // all unindexed data cleaned before this point - indexedView *ChainView // always consistent with the log index - hasTempRange bool + indexLock sync.RWMutex + indexedRange filterMapsRange + indexedView *ChainView // always consistent with the log index + hasTempRange bool + + // cleanedEpochsBefore indicates that all unindexed data before this point + // has been cleaned. + // + // This field is only accessed and modified within tryUnindexTail, so no + // explicit locking is required. + cleanedEpochsBefore uint32 // also accessed by indexer and matcher backend but no locking needed. filterMapCache *lru.Cache[uint32, filterMap] @@ -248,15 +254,16 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f }, // deleting last unindexed epoch might have been interrupted by shutdown cleanedEpochsBefore: max(rs.MapsFirst>>params.logMapsPerEpoch, 1) - 1, - historyCutoff: historyCutoff, - finalBlock: finalBlock, - matcherSyncCh: make(chan *FilterMapsMatcherBackend), - matchers: make(map[*FilterMapsMatcherBackend]struct{}), - filterMapCache: lru.NewCache[uint32, filterMap](cachedFilterMaps), - lastBlockCache: lru.NewCache[uint32, lastBlockOfMap](cachedLastBlocks), - lvPointerCache: lru.NewCache[uint64, uint64](cachedLvPointers), - baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows), - renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots), + + historyCutoff: historyCutoff, + finalBlock: finalBlock, + matcherSyncCh: make(chan *FilterMapsMatcherBackend), + matchers: make(map[*FilterMapsMatcherBackend]struct{}), + filterMapCache: lru.NewCache[uint32, filterMap](cachedFilterMaps), + lastBlockCache: lru.NewCache[uint32, lastBlockOfMap](cachedLastBlocks), + lvPointerCache: lru.NewCache[uint64, uint64](cachedLvPointers), + baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows), + renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots), } // Set initial indexer target. @@ -444,6 +451,7 @@ func (f *FilterMaps) safeDeleteWithLogs(deleteFn func(db ethdb.KeyValueStore, ha // setRange updates the indexed chain view and covered range and also adds the // changes to the given batch. +// // Note that this function assumes that the index write lock is being held. func (f *FilterMaps) setRange(batch ethdb.KeyValueWriter, newView *ChainView, newRange filterMapsRange, isTempRange bool) { f.indexedView = newView @@ -477,6 +485,7 @@ func (f *FilterMaps) setRange(batch ethdb.KeyValueWriter, newView *ChainView, ne // Note that this function assumes that the log index structure is consistent // with the canonical chain at the point where the given log value index points. // If this is not the case then an invalid result or an error may be returned. +// // Note that this function assumes that the indexer read lock is being held when // called from outside the indexerLoop goroutine. func (f *FilterMaps) getLogByLvIndex(lvIndex uint64) (*types.Log, error) { @@ -655,6 +664,7 @@ func (f *FilterMaps) mapRowIndex(mapIndex, rowIndex uint32) uint64 { // getBlockLvPointer returns the starting log value index where the log values // generated by the given block are located. If blockNumber is beyond the current // head then the first unoccupied log value index is returned. +// // 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) { @@ -762,7 +772,7 @@ func (f *FilterMaps) deleteTailEpoch(epoch uint32) (bool, error) { return false, errors.New("invalid tail epoch number") } // remove index data - if err := f.safeDeleteWithLogs(func(db ethdb.KeyValueStore, hashScheme bool, stopCb func(bool) bool) error { + deleteFn := func(db ethdb.KeyValueStore, hashScheme bool, stopCb func(bool) bool) error { first := f.mapRowIndex(firstMap, 0) count := f.mapRowIndex(firstMap+f.mapsPerEpoch, 0) - first if err := rawdb.DeleteFilterMapRows(f.db, common.NewRange(first, count), hashScheme, stopCb); err != nil { @@ -786,10 +796,13 @@ func (f *FilterMaps) deleteTailEpoch(epoch uint32) (bool, error) { f.lvPointerCache.Remove(blockNumber) } return nil - }, fmt.Sprintf("Deleting tail epoch #%d", epoch), func() bool { + } + action := fmt.Sprintf("Deleting tail epoch #%d", epoch) + stopFn := func() bool { f.processEvents() return f.stop || !f.targetHeadIndexed() - }); err == nil { + } + if err := f.safeDeleteWithLogs(deleteFn, action, stopFn); err == nil { // everything removed; mark as cleaned and report success if f.cleanedEpochsBefore == epoch { f.cleanedEpochsBefore = epoch + 1 @@ -808,6 +821,9 @@ func (f *FilterMaps) deleteTailEpoch(epoch uint32) (bool, error) { } // exportCheckpoints exports epoch checkpoints in the format used by checkpoints.go. +// +// Note: acquiring the indexLock read lock is unnecessary here, as this function +// is always called within the indexLoop. func (f *FilterMaps) exportCheckpoints() { finalLvPtr, err := f.getBlockLvPointer(f.finalBlock + 1) if err != nil { diff --git a/core/filtermaps/indexer.go b/core/filtermaps/indexer.go index 787197345a0..3ec49ca1166 100644 --- a/core/filtermaps/indexer.go +++ b/core/filtermaps/indexer.go @@ -43,6 +43,8 @@ func (f *FilterMaps) indexerLoop() { log.Info("Started log indexer") for !f.stop { + // Note: acquiring the indexLock read lock is unnecessary here, + // as the `indexedRange` is accessed within the indexerLoop. if !f.indexedRange.initialized { if f.targetView.HeadNumber() == 0 { // initialize when chain head is available @@ -105,7 +107,7 @@ type targetUpdate struct { historyCutoff, finalBlock uint64 } -// SetTargetView sets a new target chain view for the indexer to render. +// SetTarget sets a new target chain view for the indexer to render. // Note that SetTargetView never blocks. func (f *FilterMaps) SetTarget(targetView *ChainView, historyCutoff, finalBlock uint64) { if targetView == nil { @@ -178,6 +180,8 @@ func (f *FilterMaps) processSingleEvent(blocking bool) bool { if f.stop { return false } + // Note: acquiring the indexLock read lock is unnecessary here, + // as this function is always called within the indexLoop. if !f.hasTempRange { for _, mb := range f.matcherSyncRequests { mb.synced() diff --git a/core/filtermaps/matcher_backend.go b/core/filtermaps/matcher_backend.go index ee18a0694c4..9751783754f 100644 --- a/core/filtermaps/matcher_backend.go +++ b/core/filtermaps/matcher_backend.go @@ -111,17 +111,17 @@ func (fm *FilterMapsMatcherBackend) GetLogByLvIndex(ctx context.Context, lvIndex // synced signals to the matcher that has triggered a synchronisation that it // has been finished and the log index is consistent with the chain head passed // as a parameter. +// // Note that if the log index head was far behind the chain head then it might not // be synced up to the given head in a single step. Still, the latest chain head // should be passed as a parameter and the existing log index should be consistent // with that chain. +// +// Note: acquiring the indexLock read lock is unnecessary here, as this function +// is always called within the indexLoop. func (fm *FilterMapsMatcherBackend) synced() { - fm.f.indexLock.RLock() fm.f.matchersLock.Lock() - defer func() { - fm.f.matchersLock.Unlock() - fm.f.indexLock.RUnlock() - }() + defer fm.f.matchersLock.Unlock() indexedBlocks := fm.f.indexedRange.blocks if !fm.f.indexedRange.headIndexed && !indexedBlocks.IsEmpty() { @@ -154,6 +154,8 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange case <-ctx.Done(): return SyncRange{}, ctx.Err() case <-fm.f.disabledCh: + // Note: acquiring the indexLock read lock is unnecessary here, + // as the indexer has already been terminated. return SyncRange{IndexedView: fm.f.indexedView}, nil } select { @@ -162,6 +164,8 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange case <-ctx.Done(): return SyncRange{}, ctx.Err() case <-fm.f.disabledCh: + // Note: acquiring the indexLock read lock is unnecessary here, + // as the indexer has already been terminated. return SyncRange{IndexedView: fm.f.indexedView}, nil } } @@ -170,7 +174,9 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange // valid range with the current indexed range. This function should be called // whenever a part of the log index has been removed, before adding new blocks // to it. -// Note that this function assumes that the index read lock is being held. +// +// Note: acquiring the indexLock read lock is unnecessary here, as this function +// is always called within the indexLoop. func (f *FilterMaps) updateMatchersValidRange() { f.matchersLock.Lock() defer f.matchersLock.Unlock() From 2bf8a78984d70e8d5a695879494b17193f1bcf57 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 25 Apr 2025 12:44:25 +0200 Subject: [PATCH 125/658] version: release go-ethereum v1.15.10 stable --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 2bbd7e7b17b..56c0eedc310 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 10 // Patch version component of the current release - Meta = "unstable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 10 // Patch version component of the current release + Meta = "stable" // Version metadata to append to the version string ) From f750117ad19d623622cc4a46ea361a716ba7407e Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 25 Apr 2025 13:27:24 +0200 Subject: [PATCH 126/658] version: begin v1.15.11 release cycle --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 56c0eedc310..6b9a3b2ca47 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 10 // Patch version component of the current release - Meta = "stable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 11 // Patch version component of the current release + Meta = "unstable" // Version metadata to append to the version string ) From 004526762b51f4270ce496f66a4dc2c7ac7cfa3b Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 28 Apr 2025 08:26:27 +0200 Subject: [PATCH 127/658] core/txpool/legacypool: refactor truncatePending (#31715) TruncatePending shows up bright red on our nodes, because it computes the length of a map multiple times. I don't know why this is so expensive, but around 20% of our time is spent on this, which is super weird. ``` //PR: BenchmarkTruncatePending-24 17498 69397 ns/op 32872 B/op 3 allocs/op //Master: BenchmarkTruncatePending-24 9960 123954 ns/op 32872 B/op 3 allocs/op ``` ``` benchmark old ns/op new ns/op delta BenchmarkTruncatePending-24 123954 69397 -44.01% benchmark old allocs new allocs delta BenchmarkTruncatePending-24 3 3 +0.00% benchmark old bytes new bytes delta BenchmarkTruncatePending-24 32872 32872 +0.00% ``` This simple PR is a 44% improvement over the old state ``` OUTINE ======================== github.com/ethereum/go-ethereum/core/txpool/legacypool.(*LegacyPool).truncatePending in github.com/ethereum/go-ethereum/core/txpool/legacypool/legacypool.go 1.96s 18.02s (flat, cum) 19.57% of Total . . 1495:func (pool *LegacyPool) truncatePending() { . . 1496: pending := uint64(0) 60ms 2.99s 1497: for _, list := range pool.pending { 250ms 5.48s 1498: pending += uint64(list.Len()) . . 1499: } . . 1500: if pending <= pool.config.GlobalSlots { . . 1501: return . . 1502: } . . 1503: . . 1504: pendingBeforeCap := pending . . 1505: // Assemble a spam order to penalize large transactors first . 510ms 1506: spammers := prque.New[int64, common.Address](nil) 140ms 2.50s 1507: for addr, list := range pool.pending { . . 1508: // Only evict transactions from high rollers 50ms 5.08s 1509: if uint64(list.Len()) > pool.config.AccountSlots { . . 1510: spammers.Push(addr, int64(list.Len())) . . 1511: } . . 1512: } . . 1513: // Gradually drop transactions from offenders . . 1514: offenders := []common.Address{} ``` ```go // Benchmarks the speed of batch transaction insertion in case of multiple accounts. func BenchmarkTruncatePending(b *testing.B) { // Generate a batch of transactions to enqueue into the pool pool, _ := setupPool() defer pool.Close() b.ReportAllocs() batches := make(types.Transactions, 4096+1024+1) for i := range len(batches) { key, _ := crypto.GenerateKey() account := crypto.PubkeyToAddress(key.PublicKey) pool.currentState.AddBalance(account, uint256.NewInt(1000000), tracing.BalanceChangeUnspecified) tx := transaction(uint64(0), 100000, key) batches[i] = tx } for _, tx := range batches { pool.addRemotesSync([]*types.Transaction{tx}) } b.ResetTimer() // benchmark truncating the pending for range b.N { pool.truncatePending() } } ``` --- core/txpool/legacypool/legacypool.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 7bf360ff65f..affe44cf060 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -1494,22 +1494,22 @@ func (pool *LegacyPool) promoteExecutables(accounts []common.Address) []*types.T // equal number for all for accounts with many pending transactions. func (pool *LegacyPool) truncatePending() { pending := uint64(0) - for _, list := range pool.pending { - pending += uint64(list.Len()) - } - if pending <= pool.config.GlobalSlots { - return - } - pendingBeforeCap := pending // Assemble a spam order to penalize large transactors first - spammers := prque.New[int64, common.Address](nil) + spammers := prque.New[uint64, common.Address](nil) for addr, list := range pool.pending { // Only evict transactions from high rollers - if uint64(list.Len()) > pool.config.AccountSlots { - spammers.Push(addr, int64(list.Len())) + length := uint64(list.Len()) + pending += length + if length > pool.config.AccountSlots { + spammers.Push(addr, length) } } + if pending <= pool.config.GlobalSlots { + return + } + pendingBeforeCap := pending + // Gradually drop transactions from offenders offenders := []common.Address{} for pending > pool.config.GlobalSlots && !spammers.Empty() { From c8c8d6c4039a476478dfcfb732ba83822b648288 Mon Sep 17 00:00:00 2001 From: Martin HS Date: Mon, 28 Apr 2025 08:37:02 +0200 Subject: [PATCH 128/658] trie: add edgecase for rangeproof correctness (#31667) This PR adds checking for an edgecase which theoretically can happen in the range-prover. Right now, we check that a key does not overwrite a previous one by checking that the key is increasing. However, if keys are of different lengths, it is possible to create a key which is increasing _and_ overwrites the previous key. Example: `0xaabbcc` followed by `0xaabbccdd`. This can not happen in go-ethereum, which always uses fixed-size paths for accounts and storage slot paths in the trie, but it might happen if the range prover is used without guaranteed fixed-size keys. This PR also adds some testcases for the errors that are expected. --- trie/proof.go | 14 +++++++++++--- trie/proof_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/trie/proof.go b/trie/proof.go index 2e527348bfc..751d6f620f3 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -485,10 +485,18 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, keys [][]byte, valu if len(keys) != len(values) { return false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values)) } - // Ensure the received batch is monotonic increasing and contains no deletions + // Ensure the received batch is + // - monotonically increasing, + // - not expanding down prefix-paths + // - and contains no deletions for i := 0; i < len(keys); i++ { - if i < len(keys)-1 && bytes.Compare(keys[i], keys[i+1]) >= 0 { - return false, errors.New("range is not monotonically increasing") + if i < len(keys)-1 { + if bytes.Compare(keys[i], keys[i+1]) >= 0 { + return false, errors.New("range is not monotonically increasing") + } + if bytes.HasPrefix(keys[i+1], keys[i]) { + return false, errors.New("range contains path prefixes") + } } if len(values[i]) == 0 { return false, errors.New("range contains deletion") diff --git a/trie/proof_test.go b/trie/proof_test.go index fab3a976508..b3c9dd753c2 100644 --- a/trie/proof_test.go +++ b/trie/proof_test.go @@ -1000,3 +1000,35 @@ func TestRangeProofKeysWithSharedPrefix(t *testing.T) { t.Error("expected more to be false") } } + +// TestRangeProofErrors tests a few cases where the prover is supposed +// to exit with errors +func TestRangeProofErrors(t *testing.T) { + // Different number of keys to values + _, err := VerifyRangeProof((common.Hash{}), []byte{}, make([][]byte, 5), make([][]byte, 4), nil) + if have, want := err.Error(), "inconsistent proof data, keys: 5, values: 4"; have != want { + t.Fatalf("wrong error, have %q, want %q", err.Error(), want) + } + // Non-increasing paths + _, err = VerifyRangeProof((common.Hash{}), []byte{}, + [][]byte{[]byte{2, 1}, []byte{2, 1}}, make([][]byte, 2), nil) + if have, want := err.Error(), "range is not monotonically increasing"; have != want { + t.Fatalf("wrong error, have %q, want %q", err.Error(), want) + } + // A prefixed path is never motivated. Inserting the second element will + // require rewriting/overwriting the previous value-node, thus can only + // happen if the data is corrupt. + _, err = VerifyRangeProof((common.Hash{}), []byte{}, + [][]byte{[]byte{2, 1}, []byte{2, 1, 2}}, + [][]byte{[]byte{1}, []byte{1}}, nil) + if have, want := err.Error(), "range contains path prefixes"; have != want { + t.Fatalf("wrong error, have %q, want %q", err.Error(), want) + } + // Empty values (deletions) + _, err = VerifyRangeProof((common.Hash{}), []byte{}, + [][]byte{[]byte{2, 1}, []byte{2, 2}}, + [][]byte{[]byte{1}, []byte{}}, nil) + if have, want := err.Error(), "range contains deletion"; have != want { + t.Fatalf("wrong error, have %q, want %q", err.Error(), want) + } +} From a511553e448c947a0fe8f34acf7bb6f9818c2b49 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 29 Apr 2025 08:29:56 +0200 Subject: [PATCH 129/658] core: apply overrides to mainnet chainconfig (#31733) This PR applies the config overrides to the new config as well, otherwise they will not be applied to defined configs, making shadowforks impossible. To test: ``` > ./build/bin/geth --override.prague 123 --dev --datadir /tmp/geth INFO [04-28|21:20:47.009] - Prague: @123 > ./build/bin/geth --override.prague 321 --dev --datadir /tmp/geth INFO [04-28|21:23:59.760] - Prague: @321 `` --- core/genesis.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/genesis.go b/core/genesis.go index 95782a827a4..080f8a3c5f4 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -365,6 +365,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g return nil, common.Hash{}, nil, errors.New("missing head header") } newCfg := genesis.chainConfigOrDefault(ghash, storedCfg) + if err := overrides.apply(newCfg); err != nil { + return nil, common.Hash{}, nil, err + } // Sanity-check the new configuration. if err := newCfg.CheckConfigForkOrder(); err != nil { From 0ac4a84a1fdb9e54d27d0814179856a90e0c989f Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Tue, 29 Apr 2025 19:21:18 +0800 Subject: [PATCH 130/658] beacon/engine: omit empty witness in payload response (#31739) Fixes https://github.com/ethereum/go-ethereum/issues/31737 --- beacon/engine/types.go | 2 +- eth/catalyst/api.go | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 984090ef89b..3e52933a900 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -131,7 +131,7 @@ type executionPayloadEnvelopeMarshaling struct { type PayloadStatusV1 struct { Status string `json:"status"` - Witness *hexutil.Bytes `json:"witness"` + Witness *hexutil.Bytes `json:"witness,omitempty"` LatestValidHash *common.Hash `json:"latestValidHash"` ValidationError *string `json:"validationError"` } diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index e6f29c970b5..1e6981a76af 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -373,8 +373,11 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl } valid := func(id *engine.PayloadID) engine.ForkChoiceResponse { return engine.ForkChoiceResponse{ - PayloadStatus: engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &update.HeadBlockHash}, - PayloadID: id, + PayloadStatus: engine.PayloadStatusV1{ + Status: engine.VALID, + LatestValidHash: &update.HeadBlockHash, + }, + PayloadID: id, } } if rawdb.ReadCanonicalHash(api.eth.ChainDb(), block.NumberU64()) != update.HeadBlockHash { From 947fd3a834b5ac406ab795f5f069401a1860ee6b Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 29 Apr 2025 14:42:17 +0200 Subject: [PATCH 131/658] crypto/kzg4844: add ComputeCells functionality (#31378) For PeerDAS, we need to compute cell proofs. Both ckzg and gokzg support computing these cell proofs. This PR does the following: - Update the go-kzg library from "github.com/crate-crypto/go-kzg-4844" to "github.com/crate-crypto/go-eth-kzg" which will be the new upstream for go-kzg moving forward - Update ckzg from v1.0.0 to v2.0.1 and switch to /v2 - Updates the trusted setup to contain the g1 points both in lagrange and monomial form - Expose `ComputeCells` to compute the cell proofs --- cmd/utils/flags.go | 14 +- crypto/kzg4844/kzg4844.go | 11 + crypto/kzg4844/kzg4844_ckzg_cgo.go | 32 +- crypto/kzg4844/kzg4844_ckzg_nocgo.go | 8 + crypto/kzg4844/kzg4844_gokzg.go | 20 +- crypto/kzg4844/trusted_setup.json | 4098 ++++++++++++++++++++++++++ go.mod | 9 +- go.sum | 18 +- 8 files changed, 4189 insertions(+), 21 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index f5fc94cebc5..b26f43b3767 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1854,10 +1854,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if ctx.String(CryptoKZGFlag.Name) != "gokzg" && ctx.String(CryptoKZGFlag.Name) != "ckzg" { Fatalf("--%s flag must be 'gokzg' or 'ckzg'", CryptoKZGFlag.Name) } - log.Info("Initializing the KZG library", "backend", ctx.String(CryptoKZGFlag.Name)) - if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil { - Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err) - } + // The initialization of the KZG library can take up to 2 seconds + // We start this here in parallel, so it should be available + // once we start executing blocks. It's threadsafe. + go func() { + log.Info("Initializing the KZG library", "backend", ctx.String(CryptoKZGFlag.Name)) + if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil { + Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err) + } + }() + // VM tracing config. if ctx.IsSet(VMTraceFlag.Name) { if name := ctx.String(VMTraceFlag.Name); name != "" { diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go index 39fdfbe740e..0a2478cea00 100644 --- a/crypto/kzg4844/kzg4844.go +++ b/crypto/kzg4844/kzg4844.go @@ -149,6 +149,17 @@ func VerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error { return gokzgVerifyBlobProof(blob, commitment, proof) } +// ComputeCellProofs returns the KZG cell proofs that are used to verify the blob against +// the commitment. +// +// This method does not verify that the commitment is correct with respect to blob. +func ComputeCellProofs(blob *Blob) ([]Proof, error) { + if useCKZG.Load() { + return ckzgComputeCellProofs(blob) + } + return gokzgComputeCellProofs(blob) +} + // CalcBlobHashV1 calculates the 'versioned blob hash' of a commitment. // The given hasher must be a sha256 hash instance, otherwise the result will be invalid! func CalcBlobHashV1(hasher hash.Hash, commit *Commitment) (vh [32]byte) { diff --git a/crypto/kzg4844/kzg4844_ckzg_cgo.go b/crypto/kzg4844/kzg4844_ckzg_cgo.go index dce592b4442..49a7046fe0d 100644 --- a/crypto/kzg4844/kzg4844_ckzg_cgo.go +++ b/crypto/kzg4844/kzg4844_ckzg_cgo.go @@ -23,8 +23,8 @@ import ( "errors" "sync" - gokzg4844 "github.com/crate-crypto/go-kzg-4844" - ckzg4844 "github.com/ethereum/c-kzg-4844/bindings/go" + gokzg4844 "github.com/crate-crypto/go-eth-kzg" + ckzg4844 "github.com/ethereum/c-kzg-4844/v2/bindings/go" "github.com/ethereum/go-ethereum/common/hexutil" ) @@ -47,15 +47,21 @@ func ckzgInit() { if err = gokzg4844.CheckTrustedSetupIsWellFormed(params); err != nil { panic(err) } - g1s := make([]byte, len(params.SetupG1Lagrange)*(len(params.SetupG1Lagrange[0])-2)/2) + g1Lag := make([]byte, len(params.SetupG1Lagrange)*(len(params.SetupG1Lagrange[0])-2)/2) for i, g1 := range params.SetupG1Lagrange { + copy(g1Lag[i*(len(g1)-2)/2:], hexutil.MustDecode(g1)) + } + g1s := make([]byte, len(params.SetupG1Monomial)*(len(params.SetupG1Monomial[0])-2)/2) + for i, g1 := range params.SetupG1Monomial { copy(g1s[i*(len(g1)-2)/2:], hexutil.MustDecode(g1)) } g2s := make([]byte, len(params.SetupG2)*(len(params.SetupG2[0])-2)/2) for i, g2 := range params.SetupG2 { copy(g2s[i*(len(g2)-2)/2:], hexutil.MustDecode(g2)) } - if err = ckzg4844.LoadTrustedSetup(g1s, g2s); err != nil { + // The last parameter determines the multiplication table, see https://notes.ethereum.org/@jtraglia/windowed_multiplications + // I think 6 is an decent compromise between size and speed + if err = ckzg4844.LoadTrustedSetup(g1s, g1Lag, g2s, 6); err != nil { panic(err) } } @@ -125,3 +131,21 @@ func ckzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error { } return nil } + +// ckzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against +// the commitment. +// +// This method does not verify that the commitment is correct with respect to blob. +func ckzgComputeCellProofs(blob *Blob) ([]Proof, error) { + ckzgIniter.Do(ckzgInit) + + _, proofs, err := ckzg4844.ComputeCellsAndKZGProofs((*ckzg4844.Blob)(blob)) + if err != nil { + return []Proof{}, err + } + var p []Proof + for _, proof := range proofs { + p = append(p, (Proof)(proof)) + } + return p, nil +} diff --git a/crypto/kzg4844/kzg4844_ckzg_nocgo.go b/crypto/kzg4844/kzg4844_ckzg_nocgo.go index 0662b2632f1..6f4bd3b8236 100644 --- a/crypto/kzg4844/kzg4844_ckzg_nocgo.go +++ b/crypto/kzg4844/kzg4844_ckzg_nocgo.go @@ -60,3 +60,11 @@ func ckzgComputeBlobProof(blob *Blob, commitment Commitment) (Proof, error) { func ckzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error { panic("unsupported platform") } + +// ckzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against +// the commitment. +// +// This method does not verify that the commitment is correct with respect to blob. +func ckzgComputeCellProofs(blob *Blob) ([]Proof, error) { + panic("unsupported platform") +} diff --git a/crypto/kzg4844/kzg4844_gokzg.go b/crypto/kzg4844/kzg4844_gokzg.go index b4af9b1671e..46a38a89136 100644 --- a/crypto/kzg4844/kzg4844_gokzg.go +++ b/crypto/kzg4844/kzg4844_gokzg.go @@ -20,7 +20,7 @@ import ( "encoding/json" "sync" - gokzg4844 "github.com/crate-crypto/go-kzg-4844" + gokzg4844 "github.com/crate-crypto/go-eth-kzg" ) // context is the crypto primitive pre-seeded with the trusted setup parameters. @@ -96,3 +96,21 @@ func gokzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error return context.VerifyBlobKZGProof((*gokzg4844.Blob)(blob), (gokzg4844.KZGCommitment)(commitment), (gokzg4844.KZGProof)(proof)) } + +// gokzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against +// the commitment. +// +// This method does not verify that the commitment is correct with respect to blob. +func gokzgComputeCellProofs(blob *Blob) ([]Proof, error) { + gokzgIniter.Do(gokzgInit) + + _, proofs, err := context.ComputeCellsAndKZGProofs((*gokzg4844.Blob)(blob), 0) + if err != nil { + return []Proof{}, err + } + var p []Proof + for _, proof := range proofs { + p = append(p, (Proof)(proof)) + } + return p, nil +} diff --git a/crypto/kzg4844/trusted_setup.json b/crypto/kzg4844/trusted_setup.json index c6d724efafd..6793490e2ef 100644 --- a/crypto/kzg4844/trusted_setup.json +++ b/crypto/kzg4844/trusted_setup.json @@ -1,4 +1,4102 @@ { + "g1_monomial": [ + "0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "0xad3eb50121139aa34db1d545093ac9374ab7bca2c0f3bf28e27c8dcd8fc7cb42d25926fc0c97b336e9f0fb35e5a04c81", + "0x8029c8ce0d2dce761a7f29c2df2290850c85bdfaec2955626d7acc8864aeb01fe16c9e156863dc63b6c22553910e27c1", + "0xb1386c995d3101d10639e49b9e5d39b9a280dcf0f135c2e6c6928bb3ab8309a9da7178f33925768c324f11c3762cfdd5", + "0x9596d929610e6d2ed3502b1bb0f1ea010f6b6605c95d4859f5e53e09fa68dc71dfd5874905447b5ec6cd156a76d6b6e8", + "0x851e3c3d4b5b7cdbba25d72abf9812cf3d7c5a9dbdec42b6635e2add706cbeea18f985afe5247459f6c908620322f434", + "0xb10f4cf8ec6e02491bbe6d9084d88c16306fdaf399fef3cd1453f58a4f7633f80dc60b100f9236c3103eaf727468374f", + "0xade11ec630127e04d17e70db0237d55f2ff2a2094881a483797e8cddb98b622245e1f608e5dcd1172b9870e733b4a32f", + "0xaf58c8a2f58f904ce20db81005331bf2d251e227e7d1bef575d691bdca842e6233eb2e26c2e116a61a78594772b38d25", + "0xb3c1313c31ec82da5a7a09e9cf6656ca598c243345fe8d4828e520ade91787ffb8b9867db789b34ad67cef47b26ff86d", + "0xa8ed8a235355948e0b04be080b7b3e145293accefb4704d1da9050796b2f6870516c1ebf77ae6a65359edcfd016c0f36", + "0x80e792d5ba24b8058f6d7291a2ec5cb68aab1e16e96d793128e86815631baf42c56b6205c19e25ce9727bd1fd6f9defb", + "0x816288c5d726b094e3fdf95cb8882f442c4d9d1101b92c7938a7dfd49bc50636d73ea1b05f75eb731c908c8fd8dee717", + "0xae009128d128ba2e1519bfa7a0c01ed494a7d461c3aba60f8a301701fed61fe4e31d6c79ce189542ae51df91e73ce1b3", + "0x96a866d60a9007d05825c332476a83e869e15b11d7257172a67690ea9bd3efea44bf9c8d42191454eb04fcf110b16396", + "0x8b250a2a06419adb9b611e89f7f8f2990aa301949b533ad3bf17c4a61ab5f5be0b1d5e2b571864d13f1bb75805c7795d", + "0x8450f49facf2e620fa45ee90e1801178842d927a2a25fc6ed7ba99a4eec7ae40eebfee41028eaa84f107f4a777694976", + "0x91049080cf659c0985a22d1366e59191bb89663f922e8168b9b7d85c8a73d74a6d9dceefd855d3d858b493670c750581", + "0xa1e167aeb2008087f3195926f1985c0a459d6ec57237255b1473a96de4e2c1cf766127c862c7dc853a6909e67cb06cf7", + "0xb667c0d4e26e20698b07567358625d5f003839c92de8088e12dbd74a6f6a3156b4ea8d252c9ad62af5f6c4fec1cf6cc7", + "0x8e4b5e304c0b1b161ae3e4b68b5e3ac66c42acd7c1ee2458044f6527c508a93995e50894d72d57c1350f91afe72775ff", + "0x8c642640aa7915421cdc21fd639f88a42052b1cfa358ff7702e60793a92b7b5926dae15a0c8f8f59cd3013f01c159ba3", + "0xa356f35e713cfc283056bf539de54a21731e61efb4c47319f20de4a4b723d76a33b65f4a67d298b9ec5c2a1579418657", + "0x93ce204146ce95f484dc79c27919a16c9e3fc14a9111c6c63d44491158d5838117d20851cc3227a5e8ba6ccf79e77f39", + "0xb585664cbb9a84b52f89114e1cf0cf1171bea78a136dc1404ac88a11210b2debc3b7a55e702da93ff629095c134a295e", + "0xb6dfd444ec7fdceb14c6328f26ca12c3f9fc4327d8d8c68948e92e7e61262b82d833a65a9e3af6353ffa832b6da25705", + "0xb4d4b8eb9ecfffe3f0d48fb4149c7b31aec1da7041ec03bd0750c52a2a7cbc3a7cfbf09d5bfdc56e3860826a62d0bb91", + "0xa4e248e3d61db52da9683fef188579c470d65e2df9064726847b1599fc774049ffdc6ef2ae578d5ed7874f1298ecdf69", + "0xa68a0fffc2e37d3183feb01b42234c0f4e510f9dc29d09c571e6da00fecad9da224cd0f31550070148667e226c4ca413", + "0x86adda2ffecb77236c18005051f31f9657a0d50fef2a1175dfda32e74d5d53df825c10f289eb0ad39df0c64fc9bc7729", + "0x998266d5c9c3764ed97d66fa9ed176af043999652bae19f0657c8328629d30af453230e3681c5a38e2f01e389ed8d825", + "0xa05261554d3c620af0c914cf27ab98f5d3593c33ab313c198e0c40d6c72022eb5943778cd4f73e9fe8383392a7004976", + "0xad243fb3631bf90fedb9d679fd71fc0cf06bda028591ded2bd4c634ea7b3c2bd22eca2ab318fcdaa6c2cda1e63e1c57b", + "0x89b9859a04f903c95e97fb2951f01cc6418a2505eee0b5bc7266b4d33e01b69b9fe7dc56fa9ebb5856095be0925a422d", + "0xa68d118343a5bbfbbab95ff9bfe53aeb7fdbaf16db983e6f4456366df2aa01fbdb6ee9901cb102fc7d2bd099be2f1f3e", + "0xb49301f25d5a9dd2ec60ddb0b4b477291958487efea9e54dc0e4ef388f03b8bbadd13259d191f7a0b7513876767d8282", + "0x8b93df7fb4513f67749905fd43db78f7026589b704ebb9ea3255d0ad6415437799f40f02e07efccda1e6fd5e8cd0a721", + "0xad88769ace96455da37c3c9019a9f523c694643be3f6b37b1e9dcc5053d1fe8e463abebdb1b3ef2f2fb801528a01c47c", + "0x80f0eb5dcbfaaf421bf59a8b9bd5245c4823c94510093e23e0b0534647fb5525a25ea3aeea0a927a1ee20c057f2c9234", + "0xb10ad82ea6a5aeabe345d00eb17910d6942b6862f7f3773c7d321194e67c9cced0b3310425662606634dcd7f8b976c04", + "0x82f6fd91f87822f6cc977808eeac77889f4a32fb0d618e784b2331263d0ffa820b3f70b069d32e0319c9e033ab75d3b4", + "0x9436d3dc6b5e25b1f695f8c6c1c553dab312ccace4dac3afddc141d3506467cd50cb04a49ea96ea7f5a8a7b0fc65ef37", + "0x8e0a9491651d52be8ebf4315fbbb410272f9a74b965d33b79ff1b9e1be3be59e43d9566773560e43280549c348e48f01", + "0x8809137e5d3a22400d6e645a9bd84e21c492371736c7e62c51cef50fee3aa7f2405724367a83fd051ff702d971167f67", + "0xb536a24f31a346de7f9863fc351fa602158404d2f94747eebe43abf1f21bf8f95a64146c02a4bec27b503f546789a388", + "0xb5cdf5a04fc12a0e0ef7545830061dff7fd8abea46e48fbe6235109e6c36ee6bffcb9529e2f3d0d701cf58bbfb6a4197", + "0xab15377525753467d042b7931f66f862cbbb77464212c9aa72d4e5c04375ef55f619b3a446091c1ba1a3b5d9f05e538f", + "0x905a75b943ad017ff78ea6ddd1d28a45c7273ee1c2e5e3353685813793ead3370c09cabd903fcab9d8b1c6961372d486", + "0x8147df4324faddc02fb0896367a7647b719b6499a361aecfdd3a34296fa6768ad31c34f9e873fd1e683386c44651883e", + "0xac91d08570dd91f89d2e01dca67cdc83b640e20f073ea9f0734759c92182bb66c5d645f15ebd91ed705b66486ed2088d", + "0xac6295ef2513bbea7ef4cdcf37d280300c34e63c4b9704663d55891a61bf5c91b04cc1d202a3a0a7c4520c30edc277c7", + "0xb604be776a012095c0d4ebc77797dd8dec62a54c0559fb2185d7bac6b50d4e5fd471ac2d7f4523206d5d8178eabd9a87", + "0x80ead68def272ce3f57951145e71ed6dc26da98e5825ef439af577c0c5de766d4e39207f205d5d21db903d89f37bbb02", + "0x9950b4a830388c897158c7fe3921e2fe24beedc7c84e2024e8b92b9775f8f99593b54a86b8870ec5087734295ba06032", + "0xb89ba714adabf94e658a7d14ac8fc197376a416841c2a80e1a6dde4f438d5f747d1fb90b39e8ea435c59d6ecda13dea1", + "0xb0c78e7cc60bd05be46d48fbb0421a678c7f14b8d93730deb66fbe1647613b2c62b5075126d917047820c57fc3509cb9", + "0xa860c4acc5444e9ae987e8c93cb9a5f17d954d63c060cc616f724e26bc73d2c54cd36e0492d1fde173847278e55942ba", + "0x8fb8269c9d5c15428e8d45da1251e4c4a4b600d47da0caea29fef246854d8fb6acae86a8e6440d0c429d8dd9c2dfee0c", + "0x96c5d8eb6fd5c525b348ee4335d200139e437e4be83690af0f35b7f336a7cda8c6d2958647988b84da9f2dd7bbb7710b", + "0xa7f62141c4346cc14e9823dc38ac7d587b0427022afc1498d12ee2c43f6ac3a82167057e670dd524b74137f8c3ceb56d", + "0x956aac50d06b46a3e94397f163f593f5010d366aa2d816c2205c7d0f47f90cf0f36c169e964f9bcf698d49182d47d91f", + "0xb812899bcdc0e70d79ca729cb01104bf60e1357b9085a10f64f3ba9865d57e9abd0a505a502d4de07afb46f4d266be2f", + "0xabce02c7e1372e25d40944dc9ece2904a8f59c8854c5f2875fe63ace8ce37d97881f4f9ab4f7bad070ec8e0daee58d3f", + "0x8fb13c515b2d6abb4e14ed753fad5cc36c3631dfe21a23d0f603aad719423dd5423157eefcbd9a9c6074e155b79eb38d", + "0xa9ef67304dc297ab5af778cf8afa849eeac27db4b6978963e97b95ef7a8d3264d0d07775f728c298a2b6daed2ecf5053", + "0xa9b975520adb066e2ff2a4cde53284c23bc84261a22dc43b1634d99eff8e7892e46bb6e6da7319c9e72788aa9ea7a1ea", + "0xa6eaea4ab4206294474d9b956d9d3188d558a5633de2bd05df0d3bac03dbcbe4ed85406349c1d2e660b77c6da1f5bf8c", + "0xaf4a19f77290dddee762e1e0d4bc9945aacea3f75756ae46cd3e58a8f74d1b5db73e4834687946b0f39191e32f2fed0c", + "0xaafa6523f58f1a4cabc924c86d842816d606afeea21fa4b2b8b9573425810fdcc41c98888318e868f9c05e2be12178a3", + "0x8ef38fba0a3fa4ebe985239c8b759c22aaef0c57e6f39050a651c869487803b0d1e389c3d958fb5a7f37740f050ac69e", + "0xb07dfc9f85913c608ca7596a2e361f05e4853fad00e796fd492d247de6414892ce160f627669b1ba933b6ad726415d4e", + "0x94da679ad1d78b2bff5283c938f17b2a7d6e9cbcdf59d340e6dfb652951c7a9e852ac0590f99cfee9631b9410f6f00ea", + "0x98a907c9c021a5b034d3720197c160a82c4b7146cb73d48efeed99b9d0c6b831812cf80ac7e19e85a676a8cd3ead72de", + "0xadb746595466a12929019d0048cea33236b05c1229d2eba73b259a18a786f2bc3f05fc0598d8ce253cecb80bdf679aaf", + "0xa2fbac016996d68f9027a157b0a3f6a336144a798d6113adfcda3a5d05b62c31f108f112aa915906aef22b7f83b9228b", + "0x81841dea1904406d1b6fa49b4b3f7f6cb40b7646cf44d36c9fa07e3dee29f8e47324b40d8356ddf653109673c3374e9b", + "0xa3edbb8aac5e60c775775cbdb19067341b2e2530de48738e84c2c07151241ee31f0d8333bf20c2bc9dcb7b2e638a6b5e", + "0xb8aa6890e22964828787ce86460d3a32f12a655bb5c28de500f2fcf6b61e3334640ec6ba96029a4912af0d18df4b4139", + "0x8ca43169f04243ad0fdb0152de17c60d9e31ee0ab520970fccd98590e05508821a183b4b367967e60d53c2c826ec5dbd", + "0xb179fffd9df8c00486c5a8b9327d599f5a11745ef564f06e126849b06fe2f99273c81f65bc941efb0debaadfecbfec1c", + "0xacf068f1c2b1926279cc82750ce21b0d6b0bfd0406f0d8bbfa959bd83935932957c7f6b8de318315bf0b75f6ee41a0f2", + "0xb97831da260919c856e9f71a41687f5979bc16f8a53b1037285b4a2f9ce93af5cfe70bf0ad484744827fb55c847b58eb", + "0xaff50b0bd907383b0c241727af364fe084d021221bfb1b09fb6c1a7752eeba45d662493d590f1f182764b90b25f17906", + "0xaeeef044c14e3ad41e1235c9e816e1eb49087fd3abe877b89b3bade74459186126e160bb569bcd77779e701b19b5f71a", + "0x8483deb2b7001ca7c438fcdca8ca6aba96c9cbc4becfd9b16a6062705eae270011bcaedcae69bb54630d8c78129e57c7", + "0xaeee8d24be4ac0d9784c029e239fb5e64316ce29b88f47394cfaaa8bb966a72061bff72f99d02dc51c9705854686e77f", + "0x90ae09525a16bb2422169e15d6831c87968a14ebc0d1d27e11a759839c73c655b9d33ee5b12f275d6f440688146fbd2f", + "0xa3a41fc7fefef101422465e506bea7f3ff23c26fe35f5732b86f5f2471fb93b37ebc339f84c6be1e8d22abc812c2e212", + "0x86f4b5293e8aea4af1f1fb05dcf99714cb3aff1cfc849b1bb73524061c921c9da9ad92579a852e1889da29d952f02fe5", + "0x8932ef39d4050a1e9dc0fd8afeaf159472d71c5c27f458c69d2730836606ea56e19c8c4febf2535f930d3260e9bc7637", + "0x86307b9f3696bb21c20e4558e30310389e7367803c353d437e9b696039a0ff054d9a4953b75237ab1d1dd6f71118c189", + "0x96e57730e683ef5b550c91de18b19ac73879f3e26234297db68d28747ed0953beb0f3913cfb720c602720bf9330685d8", + "0xb04a19ee70123782e47b238abde55baf60ac0c66292a998af0d14afc8bbeb1134e557b94cd17a020084631c09a0d3c02", + "0x829abc8718be8139569fcb2c398962f38f4201114d30e2b2fb23566f8a27a5c380f5605cec543415202a12ed859e33f6", + "0xa0744fa488c8fa92a722c5fc4ef5a47dfe824eccd87d26c8bab9c174cbb151d44b1b29082c48652f03d3177e5ec86001", + "0x81d4035ae9fd28bdcd78b135cb54955d3b685a527319df6ee7e904b8e6d796f5f5a5f5035ee1de750c4cb6050e452b9e", + "0xb205e8c2ec24d7104fa0106c09ad34b5a912c1adef553fb718838dd627355993c2ec01055c11d00b2c75b68e9516d44b", + "0xb12d09da7968fa7394e449624fc7174d1d76c069ccb03e140d4d87a2d3f6d1f7b9cfc930f0c80becc673406ebe63f08e", + "0xb23752c158695da85048fdf38b395681cc0e8998630af8a9ed41efbda08c9964c2dc8ae6e53377264be4467d702c0de4", + "0xb0d84582fd73628d96b8c1ec96197697c41a963542451a2ade0890af0d33c7161d0f18e1a1ce2c168ca2dc1e9119d55e", + "0x8b877e618b469aa187632e410b125d2999d5738fd66d482000706b51fd904a0c7e7daa8c9b729fa33817bbc4154cba2a", + "0xb1cfc8a7551b601723b937d497d01dec3ee7614c2bf13d430b1058d5ebc1406045009ff02c2ac15bf8cf16f860193d1e", + "0xb6d9da84f97b21e13175bbb0b5cc8e79e88b470c87a3e115726c1bd98e0288526c58f3faaa8aa170ace0cd6a60852525", + "0xad2e773c2d527671ca5fab7085dde4da31cd35f45d4315dd95d8893ff5fb900494dca08eccfc1a2fc7bf7c7fd2fcab97", + "0x8d5a79b34aeb761d4a0c73f09f02e9548e6d382c33ee6887a759ab05762b490b8a549ef2933c7e3a46415c154c0221c0", + "0xb6f2cbe81bd0a7298403be392f8456bed30aed7ef30216959357698f789affd2942ae5fbaf3f48ecebeb7c273b20cb57", + "0xb5b6c45d99cea7ce6a1dc134aff4a8f630f299b42bd59592a7592345f8cd35bcbee944e61b0723de732fcad6e4425b63", + "0x8077d64dfcb2418974e956ea6dbf8a4c05b25d2a025333ad7e2a379f1976dc036771403383a51bfa3476c9c619ef8bef", + "0xad2e0a9d479c77a5fb73b3613a177fdaad50dcb50fed50e756ba18164c153af30b07fb2565e80ff7469f1b0338b7b5de", + "0x81017d1d80a6b6df4e99d0d7f85a8180b5523e8fa2ea2672fddff604933f8a113cab27fce098dcb454d7d1f7ed266e04", + "0x852355479d68e76c7febf6dfe2ef8e80d575c0d3bd52c983803592021cfa898c571c0b884412c21e66f0dbfe03167b53", + "0x98e1bf8ad48421467c93b9f72b47dded7c41b4fcd36ea55ca43ab24b0d0b876f5a731f422579b7167c7138fad2121266", + "0x803369314abd5422019ed4b0ef652b4dbe97ef5a87b0ea373eec9628b64a12120b2c3d4eb53db405131ff786d14c7ac6", + "0xadf2613fc34f73e1160975c140e925ed84d254e03cc3bc7fc1d19957b499c9ba9d9e4c1639981b594a7095c0a52c6757", + "0xa2f6a68efdff6e4173c00692abcfdfcdaf6f8b62369afad3dafaae4f2f38c4860780b4624d185e20e4f4498b75b5fe94", + "0x8b1658aa0e119fb8401d486ed08d60240d26a8623ef9788e3b45ad09ae31259395b021bd16be395139cbb7149714e764", + "0xa7dd8bf21121285e00672ee8bb84e0cb39b2496fb53a26e35dfbca7f2b04e9a9ff9db15f53fe63fcbeafeb2deeaf2ca4", + "0xb6d8d709e44bc18f3b41d69608edce60c02bcba48d3b7e2fd420842657f0665a7343246dea149a25e8f3416284abae66", + "0xaaf744ca5e9bcb63e3e2939b7a1e96e4a93c88c76bec0cf4294dd7db95cdd3f6a7d92196e352d08680e2328bc4592899", + "0x84434b015a7c398d35f1ec71fce455d62ba4ed4f62da042ec31bb2b4db47073314354cd50bc322297a1cfe35138bf490", + "0x8d70b3a3cd9d5dfefdacfa418c0b775a112a47ce538d33a560a519660009c3f141fd6221c18539129e9c0acdaceeeb80", + "0xb8c6903412a800ec78a4c15f31c24385a267b0c0ece32fd31bbbb557fd70c3b2d60d8fc0f90fbd70f43baa1928ea30ba", + "0x8e391dd445ea06cabb433f057853f8159511b2f9bef41aed9ccd14e0a6fcd912bbaebd38fd5fb736cfde0fa34b7a4874", + "0xa40cd988f70613df32babbd1bbc2f1b29ff1ab0147b01161555a81d56c9621657999bcdb1df38485f687afc51d5d0f23", + "0xb6a008b4426b3d7b28ae04eee4698fc8ef6a35d89008ef5394da39ce582ce1a45dcfae9a33b90f6fa4237f3667803873", + "0x8987280debfb175c3b44a2f152ea82548e4f680966f1fcbee9bf7d714e31bf8080c33f52705ef3aeee70544b22516aba", + "0xa78a51a2c11eea7680a5a0ae417a2981f8c69c396e06da621eadd7510a3664ade49d065617bec67b3de779548a4f4509", + "0xa4d9163f0a1bc048385e94d5e0bcafeee1b18f28eb23505623b9e8ef16f3df76408254dfbe790e45f2884198060d388d", + "0x83dcae2568a0c518793c0f6e38b42f9ceb50673d100b556a17ec8bd9faeec84afe50b8d72422c6b2356959667bb8e2de", + "0x874731941be4474b4576226e5906b5dee89fc9b56a9870dcc7289c1a7d494d345ba6aba31f7546a16f9963283c05f744", + "0x82c1cfab1f501189ac20147fc4631075dbf1abf9125b7d42fcb4f31cf73f3d6461b1bd08fdf6e45cc54bc08a7d5d51d1", + "0xb978228286f5d4a10ce027b6bea3021affcaa805340ca4b5192c69e8c56db59f48e4a14a284ec015f53baf97389f62b2", + "0xaf125f4fdccd1c1b64fdffecb5ec7cf8c7392bbe476e1b89a5b5329c5ba4a526e58c11e72ab9de8a38d60af648d75adc", + "0x8411a41ec14295acab0d36389013535a80dfff6e024bffeb32fb3070762f61256419e8c51b2ad6de9dbe4f1e8e286912", + "0x8ea67a91112a41f9c65515cd496f4b0cdefa1400fc06568eef000c9eae6dc250fb7622eb3f2deca10b37287cd96fa463", + "0x8da99b6c55c31dee6a49aabb54da249d348a31d4416201a10c45a3b04b11e99d4ae9813632f0ee36c523b5cca62f6f49", + "0x8b44656341e039e2bd83a19c3bb9a88f6209482e274f8cd4f8557b728e5948dd80b5745f621b96f4562928689314e8c2", + "0xa02d424a615ba0dce8ed91f477e79852215a3a39d025059826fa278e7eebef19824b2a2844f5b3865a0f471b609a23f5", + "0xa1f115cebc3fff3bcf233da27cef19eae791660f155d088003460f75567a550bef0722885010ddc384acdeac635939dc", + "0xb61a55ce9d143c17876776e064b58a10baf0ba13553c785c1e47f57b5f94c0cda8bc89d43d73386e57816c15b61a8ec8", + "0xb4073f47041e20a8e548c7fb00e07ba3b9056c34eb4ab63bb0e7b48f8e338e8b56a17611a1b5f4c03b352450b86f1d69", + "0xa7b1a07b213205b682fc5b6acb7e76fdf97b280c26621d8f3b76b7c1deb3511957da33a4e358c8e8f3d98b2a8855d67e", + "0xb797e67c2670fbd9844e8a68c585f404b035dc14bd4ec75c3f95f932c777f9db5d5f5df7629164af488fc1213035cc5f", + "0x99618200797b945f595794d6468e5c618649554ad9ba896330f1cc844090eb956ae9fc23132912f9047085c5f0c3bf7b", + "0x81194aa1319abf534cb3927af9adfb178a99d0e3e8c99ab1105f1d3b4fed40ec2971caf1d6647acb0c8d681eca53097b", + "0x80673f18e4978dbc226a6cd4b128a1259d9a7f833879c6e2fbe24d69fef2c3c23a51a4f3e8d88fa4533434bbb0723661", + "0x8125bf6c7dbb2fb63aaa3f53283559f172c788223674adbeb6d5bd17cfe888e6b87a79aec774917f20ce911c1f85f8e7", + "0x884bcdb1878b14fc38adc9fb8b4dd0b3afde404fbeb664f26ddfebc81736018551f23e75ce4cfe4865f610bcd454fbd7", + "0xaec65c8d4be8316e98aa54888af01bc6703a0c5d04b69756ff39a0a947b66817ec59d76afe9f61a25749b5e890f03e02", + "0xaa457aaa1b014a4c5a8992847a187a23321bb43452c98745987d038e3b04046102ae859b7a8e980eea978a39d76a88ef", + "0xa9832ee63b08e19123f719bfe2fe742125f32463efa966c7709a98ebfc65277670e9ea1fa2d2d78b96bdc7523b0c4c3e", + "0xa87b6b1b7858f96d55064274f29fbde56067064962cf3c3e2ba3110b22ea633bc037a74d23543ce3307a46208855d74f", + "0x897cbe4ab68a753020fec732dfcc052c7ed9905342b5a6fe0aa25c631f9ad9b659e0ee75d46f0df6507b6720675ee28c", + "0x97c3b5f0d54c1fc45e79445c3ff30458959e406a069f5bbf7979d684195b4fa0406b87c1c008f4075bc9e602ed863152", + "0x921e65d582ea9322ddfad1c855331c3cac81f53c700b96db5305a643c084eb6793094e07944bfd41dc02c3b3cf671530", + "0x8f23ef1aca02a260a3b65d25b110f28d3bafca44727448c8f2d03c5e77eda620c1721b06681bd816ee6027664d76352a", + "0x946a89b132ec0795aea9ff9dde7b77e7feafffe6e4a2f093042a7e6c71cd6ab87ce0ca914a1b5fabad4e1f96a795f163", + "0xa01e2de9db33df6511172123ad6f7c64074237471df646b32dd9aff8c15278e2723108e4facaedca97e9f49503f8c792", + "0x99dcdcde45b2ea3f15279936feede5f7d3b63ca4972f335b0559c2fa6f9faabd8127aa892a36deb114357ca906553ed8", + "0xa3f8af37bfcf66b04d1896a4bd5d343f4733d4c3305369ac7e75a08f20f2004c10c642d2c7577f4e5c4d1f2cd851ac3b", + "0xb7294d15a3d674a56099f97a1adc9e82c15e90832eaf1722df110fc2abc8634c51515e5ad8522015498a3753b1fa8c49", + "0xb4f27f5062ba7a04ea0048b3025b5e3d5b5d319a9e80310c808a5fb4e8e77b38c10a0f3172cb805cadbcc8bc66d36ec7", + "0xaefe5decee0ae2dc372cc6cf4217daf97c4c908d145f100f0daf1ccdfdf641c78432c2e473e7e4b77dcdf2d4c2bb05f0", + "0xacc84af7648a535ffd218c0cc95c8f7b092418c548815f1bafc286b1fe14f6ccb51b2044db3bff864d0bb70e88604084", + "0x84d8e3dac0df6a22beb03742e1d4af684f139f07e2ea0f7fb27fc2d7d4f1e89b5e89f71af32ff115ed5e6092133535f0", + "0x8ada001e1a03a823c4c056f636e77adc0f9dc08689d28de0d99e0feecab5db13abf37b41ec268dbdb42c75419a046c68", + "0x87dac6c798d1744dff81d8bc3e0e04f3c9bf260e811685ddb9a9a8d6eda73927439b344f9a818d2103fad633de5a4a17", + "0xad9929a7d8a7d5d5954e48281a87e5c84f67e19110d73296b9989a09c76767a57a8115629239ffb4d99dfdf9c52ef6d9", + "0x81ac7cbeef8ec35a5c3b61cc887080c29e6cd3e08af37e45830d17400dbacfb374dd07bf370b979828c3875b2027d5c6", + "0x97f92c9182953b7e10f7a1bbb6b5b5c40b8275eb5a6eec1e29874c4712814749aa8c409651380216e1ff01d7b8511041", + "0xa09794d0bbe7db013045d3fd857c1544fe6231d21afa3495fa300371f6301a3a0f4b8ea175b281503dd06078ff371ae4", + "0x839bb58d320aa08116dd387a57a2b9bd9efc89c4cdfd82d0e47a00cabe644631d09be5436bd485df3b61b75ddf81a3ef", + "0xb1cdaa344f783757e8b9c1f84421da3c5be4c69f019a8fd4c1aa5bf1a63e8970c99e35c22cf3b48a0e6738bc6ba7ce8d", + "0x92af68e3216c78998208fb24b5ba0e645d0d3f5e28222b805668d7e9cdd6c033d3b22fd6df4c2d745d7f910d133cd226", + "0x87640a4ea4e605e2204e5232b29a6c1c31152d83547eef14122cb76a0da52b8653801af48455a3ed713b9dcfee7b1ef1", + "0x8147e5bf0c8f4731155ca0517ef3fae5a32b4d5d2d98ed0007b23893d8dbb7f8a1199c50c1750c2fa7c9cebe594b1bb0", + "0xa76b4473c63c3ab6103c729afd2482822e4150f3155af39983b0ff0766c71cb622455ce6304e23853661eaa322219d18", + "0xb3e2f05ca551bc3adec0067e4034aaffd72e0b64ac18ae25452c996927976c6727966e26d213b032521889be2170800d", + "0xa8414cd14cb3be658e9e0004ce511ef7063439b1cbc3166a11de030613fde4b59caad4e91d426927863c55382afbf476", + "0xb2f0f8ab99f4d0ea785ac84fdbc00b20217b1df59b30b51d9d209d489d53b69dd5d82cdacc16fd1dd15c3a4001595f50", + "0x8b2025d5fd658c9bbed619f3e3f6ac8efe7aeff8aa9401bd66a7ceb0062c44b353608ca073f95be99204f0a913bb77eb", + "0x94a46bc5a87291b42024b2137e623c70115b9c6b196604106bfbfa20f3f56ac7779763f56b580190d3cb2f1c648cada1", + "0xaca9355545118d0769cacf69c4b23d6d68d229cd8f68f1bc0c847c05569c5af6bbbd8c4dceb637b4a6b3b5c83841bf5e", + "0xb0731992cab87c7116406b283a84707a34838bfa3284b0f6082dfabeaf41c5ac2b0ddc1b420547a1b0955aee92de2dc0", + "0xb671f77588c0f69f6830a5b28e7d07ed161b81fa9791bb3a24aae6638e3aa5e186df74978a82549c370c18ebee04d4f0", + "0xb5621ed841780f3e6681d880a76cf519cdd20d35197b112eeaa686764d57b5dfa78ffe1a294b6bc76b6e3949cd2a2369", + "0xafeba2524659d00caecf089645611553187a6ed7102050f6dd20f5a19bed08ac7065912d88371ee06242897d58d652a4", + "0xb78bfb83d44ced14a20135804aba3f00128c3ce1f302e95567ce4097b0d973414153fb305b9f156882a5a0554bf25973", + "0x98510aede95d26b1adf214053eae051ffaf24894e2fa37961a91d0ff5392dd09388196648d95b73e90bd88f2587cc4bf", + "0xb35c682d49c295946b9f120fbc47b95abd9ee86d294abb003a92139fb825b509209562575015856a270eb3eea86397a7", + "0xb9641bf685571dd9c478dd2033a1f1b11cd3a662b26502c78595863b8e536a189674a9a85f7a253453ebfd1b99fbd841", + "0xb2ad37036a59b1c9b8457972665720a6868422ed8157b6810a9c0783006103be34ab732d7aeb8629653edd18fd0f1717", + "0xaf0920cff05179a3896ea6ea322c39adf91ada5bc40fe3f6fb1b1b4e121e907c904bbaa8ca00468b3749f3da144d71f3", + "0x8e269672818ef1e2f9e0c8aa65c84442fcd9151d74bb8e870cee8c0e3fe24526e1a5388b430cef47b67f79b4e4056bcc", + "0xaa29a16fe00ea3d143b1032b1dd26b8ce638f37f95c085c7e777e8e2784bd724bd5c38b1583c61a6ec7c451dd78fd3fb", + "0x87452b7435911cc5f513b0c81b15aa04972ecbe3d7bbd0a5d676c96a8a311301c0e07fac925c53a350b46fbd3d4d0fc1", + "0x869a81c351096f47748e41566ae7b77a454b1cdfaa41d34a5742f80df38fbf5cbb08924b6fdff58e3b18f05c62bbbbb1", + "0x8b7bc1b0486300981147a40a449ada9a41afc06d735cce8bf0fab3ee94ba2e2ea57b1397e3cd31bc295352beb8334ef7", + "0x93e93fc41adb2df279d95654921b4c2edf0d293dab58d0afefb221f777349ef88d0985b3447e3b935954a81f1580a92c", + "0x970fa7cdca8324faf3e62348bb50d78f580b4f43f2e1c11bd8382d48d0074a3c55c6407203a0c9cb1c5f2163ba421ef4", + "0x924983929e608d27e4a36d4ed919297869e3c64de51aca794d32d6e90aea546bf898d98ceca28a0b2187734821b78504", + "0x8d395332529c703d943d68415d443332b5c1342ca9d9a59bfa8bd4ab63e93358c4b0dde6ce1f2e8ea9dc8f52ad7ebd95", + "0x80200dda853e588256599e7f905add5d5ee7c74272780317694fbae39318ae9be05d5bcd7b20cf460069743f3d4ef240", + "0xa287d51d6359c9ef7c7ac1b20e479ce7d0146dba5606397bd04b7a622cec642508d5b45d51b31de71f9763595b6ac88e", + "0xa320396c075175d6599225cf2e1de8c7cab549f6316c07feb0f6eaa21f06b2dd29ab14fbdf2af4543b4890ec0fd08a4d", + "0xb1e9fe230418d20368691058adcbbe30011bab3000422f0371015ff8bd09c60fb5fa85d18550d35b1c900977ca48f58b", + "0x9718fc26a51783b971744933f20490e9b5cd9162f86b84788c4c5217f5409e37b5a39d628b18e5b35a757acf67596321", + "0xa0cf81fdb161f4f1b419c5e4caa36d4bdca2325f0cd25b119a30178016f171bd6fb88403e4e3aec026c4089f180d540e", + "0x8ab1e36bd04625ee794ef04c4dcb8e004d61aceb2b62438377f49ad95dcf025ba25eb799280004941e555bf7172af6fe", + "0x9257b9e3d14d37fc7efae49b0c68d36eaac546035f4a2654d566b3ce1b2c4564cbb03dc8ec66efceb768559a8a507a18", + "0x945d1123b839637ab5154a1972c3c83a0ff34a3b1a3465de6ef0416b1950f649869a3ef88d7f1036648ee385265ce2df", + "0x81449639d708860fc0229c94f754f7262e8a3c7f67960ff12dfd15df95f57a9ffcee2013e81978b7703dd42bd5d0816f", + "0xa865481deaae5a690fd53892791e5fa729db283b75a525a11cdfee1ce17e8e7f0b449d25f20b3c1b43da128dbdf98a8b", + "0x98766812a65fcd25b853546e3bba618a3edc9fd61510e4f8ab60c038a7fa50d197abeec8776109df0f2119be9445ad00", + "0xb1b8dd5379d903dc41d74e999b1ab693607a0d2905692f4fb96adf08f738e5d31f9d00df28ccb8b5856145ca552c3e3c", + "0x99d20be7b511bec78a8ed03c207aa4aa9097ba39d85e18f1b8d52f65431ab7e9a773c7b9ac3e8d8b25458bc91bd00703", + "0xb1b7c3563fe8cb33c7d3e0b89d00bdd13e86452ff507c2e69db7b3af06f247f139155396e9b0278753310dc63940a10b", + "0xb3dc9c08451b1de7c9969b1e47574bffff50490f4a16c51e12390195d9e9c72f794790caf7b0a835d64e01fec995d3ac", + "0xaaaa4761a00022ede0809d7063d3532b7bfae90ff16f45e17a340ad4ebaa2fbac40728ccc5fbe36a67ab0e707566c5dc", + "0x8319a1903314eab01f5442d2aee6ae9c3f6edfda0d9a88b416d0f874d7d1d05d08bb482102f8ca70a4fa34836d0840c1", + "0x932949a6e9edfec344932a74d4f81eec3667ece1e8b8ca840ce07ffd4b5d6d8f01657c764d64ac1b9190f876b136490e", + "0x904db1568128487e312fe629dd8bb920cecafd3bb9cad8b63e269ae0129f2f5c80cd82f0d81e7feca9835c3945a72d28", + "0xa17280693d30dcd43c85de8f6b02d5f30cb9097274ad680cede1ef105c903615b4c40f3c6aaca478642de324972514e0", + "0x8d5f76e093aee71d0cdeb017fdfcb13bd068039746de90690ce150a0bfdbe7ddc4d539df0f82c2d2890a40b191900594", + "0x96fa1f2196a3883cdd73c66d28403cbbb58f6a939a3697ee0d308d8a076393cbb4be86255af986869230ee410c01bcfa", + "0xa8b74438dc5cabd70a91bf25601af915c4418d074327a9b01e0190c27d3922c89bb9b41e0b366e82e313edda8f21983d", + "0xac9fdc1a9b2e3ff379eb2370979372e13c4177bf4574f1490fadf05a7073e6d61e703e2d8eed9ce984aba317d411e219", + "0xa45a6c9b958169f2f8df70143e6ac3e2f6f969a4eed6fd9f1c620711bc2454739bb69f0094079464790c5429c0d8aedd", + "0x8901cbdd1009864386577842c1e3d37835fddf834064d9613b4559ea9aef3084204e1f863c4306f874141f4374f449ff", + "0xb6c582161691e3635536686825be9c4d7399d668a7675738417e0363e064dfd28acdbd8dbc9e34c1dab8a1990f1f0eba", + "0x89e89ddaf3cacc78428f3168549c161283ca8337345750667c98212717b21e7d994eae4e45bbddacc832a18df1d79276", + "0x84be275627eed8e1a73c7af8a20cee1ef5cc568cfeea7ec323d7f91b44e9653e9aeed47c1896a8240b99dde545f0e1fa", + "0xa779a54ab4f40228f6e2539595fb8d509b70aab7c19e1928c1be69ec1dc19285c3898cf15e5f8b8bc725e13af177fe17", + "0x92e2a49d2b9b36349d442283b17d46f8f9bf5932c34223015ce62d2f285e7363b2c12232be4a838b5b6cf08e694c094c", + "0x8b4e28c6f3f36caa2cfb82ba88066c830f8017bd35608b077143dff236f3181230166f5a5c02fa0e5272297331726aed", + "0x85fd77d46162ffac4b8adb25baff0eb0512a53a3d01638b3a376ea34702279ce21c8e7d8884308c03e00c9bcc1a9fd29", + "0xaad5e46916ff1be29009b595d1d8fa160cc7aa01c7fbf3a68f445c87615790dcab1fcdbdceda533d182b6541f09f2f73", + "0x948df7654726250dae393325addd3c0a20431c81f00470962190335ea4b6d9f7463d6f308cda46b92084c1f24390b1da", + "0x8f577474dea132676504376c5542b730b6604fe3d965eaa194659fd11c52233bd0b11ab62e198c0f442327ff1c00e501", + "0xae2f1001546db3e0c19700adad997cd9f765fe7a51a502cbcd9a2a07a3a5db79c8f603e05cf96d80b688cb6c9b6cd3ae", + "0x953b68e5d9561088dd20406ea7fb6894cba33868a38ace38fc30b5813140cb15dd6dd2171befae5b4df2e4a9658889d8", + "0x86c52901655ff11419b084a04da8fc3596eae59d81d3461601c0baff59ba59e3d1dd0b7ce719e741a3e97c013e898579", + "0xb9a72dd5eff73f9912a28b55de073568efb3eb0241a10b77a2bfd4f30c2aa4fbfe0c89eb345c9f07fb725660873cb515", + "0x8e7353f5f2932e4ffd95811caf46c9bd1a53643c27eb41a4ebd211f230955cd71a8b27e17cfe8aa708d8514c0de67a66", + "0xa096b8e66312a92fb10839ebe60189a8d1bd34dff55f7dfae85e4d2f53a1a4a88211c19fc84494f066358ddce82be131", + "0x931c5cd82719d76596832b007969b5f75d65cffabb41b9dac7910300db677c1309abe77eeb9837a68c760bb72013b73a", + "0x8ba10f5118d778085122065b55dd1918fddb650cce7854d15a8f0da747da44d7b12d44fc29ad7dc38f174be803db74c6", + "0x8c971deec679372a328587d91fd24ab91043e936ca709c333453d7afd43ee256d08c71cb89f0ab0e89ae119831df6d86", + "0xa2ac28a58034fbd8fd518f409221bad0efec52670880f202e09c0530e2aabc2171ed95e99891790596ffad163d86c110", + "0xb3354e3dfa8068aba4f3741152b9204baa4e342c1cc77e6dd1419cbaf8da1d118be605846b8609e997d6a62a11f3423a", + "0xa12ab65a213c9d95c24865fddc2dffe0cf9fc527dd6bcdacc1bd7271e79929a4ab3427a231f4f49d0530474e6cbc88f9", + "0x90afd65b7e6973f8aafbe74da0f42441840d3c93bd69bc1bec8fa56824e7ca97ad1b427c8a85da7d588469bd4ccc50c3", + "0xa09175940c59489bac3d3da3a4091270d9118948cbbdd57f2bcc63fbf45b8010651c801d3e58dccf42733ce1d6b446a3", + "0xa843bbf286e3cecc1fe370ff1bcf5f1001bc2e95b34246625ff50d48ee62343e82fba2d25b8a4bd5f7b5ffe90920efa2", + "0xa3c4d1003219157fdbee2707ce07afa6c2a64ae8e450182c307ed7f070024071f30b12c4b0032960ff913c74e73a9976", + "0xb24af3f68d66f825d06fc3ff94fcccebe28b1a0d4ba29c48d3a3c953b9bf7ae6707f193fef25e2dcbd2b74e483c774f0", + "0xb0f657f7723184ef7d7e4381143f1ac8020d8c6c6f2dcbebb0eaf9870d61a81f2d452596503311e46d1b38f625d4756b", + "0xb90091004fc8f6205c51bec68547ac82dba0f5525631e7632cf6efe54eecd9020729fbee6105d1b8012402d3b79c54aa", + "0x8e3fa187713c60eb0a416d6900a894cdf81e6b6b69dae0bb64f6287f3c3f030cfa85c665f7aace1eab4937f380b8f728", + "0x879bf0784ccf6725c9cd1ea8c49fde31c91c605de1ea664a33c2ce24c277ee45d20b66309f98d989acb2ff3b77e13101", + "0xaf3f3a3ddc4e11abd627d5aef8adffa91c25df5f0c68b4d2b5d51e7d9af3395ba4f6f7ae2325a6672847e1ecc6cad628", + "0x973e667289e796d3a40f072e6fea575a9b371a9997cf8961677f8dd934619ddc47c1a3efe91bae9ef95acb11a8fe6d09", + "0xafa81c5606de82f46b93f4bb6db3fc0670f4e0d1091388b138a66b3827322d95a56168c951c30831d59eeadc227500bd", + "0xb83eff77db5b4c18574662942eb36f6261c59f655f8a9c3d3731412d0f257c8e80aacc995c4b2303058a1ba32522a434", + "0x912e5ac9234b9445be8260393ff08e4859a7a385e800b74d1534eeb971f58f74cfb518dfdb89f8705d89fbf721439129", + "0xab27c8ece4a51d23e22c2e22efa43487c941139b37ea1182e96efb54ca4809d8245eae0ebe8ba94f0ed4457896fe11b1", + "0xa6630585d104a745bc79dba266d9292bbdad346449c8ee8140a5e6e8a6194411df9cdbf3d3ef83468a536d4f052e9335", + "0x8b8c128244da48e7fec641a882d0005a2d05c7138d86a293e6a0a97c76bf632b44767d0ce44663c975e7f9f9679e25e3", + "0x87dbcaca67351a4e7d2297d7cdba4796d12f58857e7ee4abd0645563577ff33544a44cd84e50b3a3b420d6998de9b57c", + "0xb859ba43df259d7f8e7fac70bfd7aae546d57a5dc90e107b174a95bf7fd3cf00f740c4434848e69b2a7e6061f66c1ef1", + "0x99d6e20978fefc40c6d310187eb2ad3a39296f189ee122ed64d74f81033c3069d44f7a9d3988a1df635b609603a17272", + "0x99a5ddf3420cc0c92b21f71a805245608d4995ead447d8f73a670d26d33e26920d5f07bfe1f6230bd5f15978055b4253", + "0xb936ac0944d3c5e4b494f48f158000abb37b80b5c763f77fe856398c664b0f1ddbcc0a9a2a672db9278f08b4bafbe2ec", + "0xb4af85fbf4040e35a686dd016adec037c99b47cc2e4dfccaf7870ee9e8c97bff30f3035992def2a9d4af323c0b3af8ae", + "0xa5ee32b8bd5f8fa9000da4da0bf00565659a43285393d37080b555d0166bde64d87317b2eab2d48a0e7b287caa989be2", + "0x894d4ad58ecb1c9ebc4f5a97407082e56cb7358d7a881ba7da72321c5027498454f2c7fa2bd5f67a4b11d38c7f14344a", + "0x965be9eeaa0d450dacc1b1cc2fbf0d5d4b0dd188f2c89aaa9260e7307a2a1eb22db6092fccb662269e9a1abfc547cabb", + "0x805893c424aec206260c1c2d2509d2cb9e67ee528bd5179a8417a667aa216a3f318ed118b50d28da18e36c01f0805e3f", + "0x972d7040d4963b35260ef0cc37cd01746f1a2a87cedc0dc7b0ee7e838c9e4573784ea743f563b5267eb3905d4fa961ba", + "0x8c7156991d4c2e561888feaecf501f721b4174e7d14109e9deeac5a9d748301c07e11fb2b04b09799f0d34ff42cb77d1", + "0x894722ac35af3d507e81d737d21e16c5ba04686f8f004aa75934aae5e17acd3e065b96e229eb011c2f34096f4c62048b", + "0x81237937c247c88e8e31e2c72412189fe59c1daf65c5513489d86cf29ee922c0bb08e5f7890f09f4ada7e5262083d266", + "0x8cf62cda2fe0d9a6b42aa2a1c483f4ad26378c7cc2c2d1510a76df7560b07dba8528b33aaacb15f7f20b9d4c7c9f61f6", + "0xaaf0921fb3e1920eee5d0acb59dcc268b42f4b435d60d25d30357edd7dd758d035919691bd15311d85489dfa2e5ee696", + "0x92cec07be2247ef42002ebcaf65ec855611b8e893a5675796f2225f55412201b0bf9f4761924d0c8377b9f131e09e39f", + "0x8e514a62ac1e91773d99588415426c97ad63e917c10d762fe06ace5277a5c3bf3730e4b9e5d116f8493b9ab8687b70e3", + "0x83932df2d923a5052468a3ea87f7b55c6a80ede3594046ee4fe233046570921822bc16555b92ba6aeabaef9b1dc0805a", + "0xa2b5bfb249de3472113fd3f35bfabf3c21d5609da62a27ea6aab5f309c9068d94bc58ba03efb4ec11be06306d59e60e8", + "0x8106cf3ebe6f0507be8c6e8d137987315fe3689ecb75bb27980f36ba5efac504baccea0e7603549b6d126beccc278804", + "0xa73ee70b6fe8c082443972102c453fc0e386852476cf22224fc0bfe554735c12f96037fbf10922795f4502c4f052b5f4", + "0x932b27e175440169958504f3ed6400e7d6dcd5e716c19dcd0f15c56c04503ed133d5a993e111c016f141e32d68b29886", + "0x96f7ce4595318e0b4a6b368f788ff82226aac676aed4ace343867f751de414453a9aaaabef6e6224ce5aedc3d5cf77c4", + "0xa950c1e3bc9a14484997013d44d876374b939af437ae7c821c131fb886063ee9fe7214a25a0c7084f0b07b99412eff75", + "0xa9dba3886ed6855303106a1bdd26010f294218684e1c178afcfea3f37a2f04fd01724a31d82de3449046617e3507a115", + "0x87a2f776b32a6b550cf3ceeaf78db02819be74968d228b1d14e0d74a1cdf994bb500b7abef6619455e98d728701fac5c", + "0x8cd887b07e335edc0b27e6a660cebb64d210741395be431d79d570139687b056557159407459799a8197b6079644f666", + "0xb81a61fce00588909c13a90c1caa150f15788786af443ff60ce654b57147601f7e70b95659e01f470334a220b547611b", + "0x8aebc51141544c5f3d3b99422250424b9800031a8fdfbf22c430907a3a446fecaa2392105d66d64b1c8e847240da4a6a", + "0x90db7dc12baa02f3f86d3edadf9434e2b9318d4f6f0eca08276b765dbb38d8eb0d08be2fe70adf2bf16ceda5db08d3ca", + "0xaa1839894152d548cc6ad963de20fb6fcc843bc9af2a2bf967c63626b8ad19e900894d6106265f38f3afccca317c22f0", + "0x848e27b741496988a582515c0c8847b2bfc6a001259396cdeea1e1b1d2828ca3a626693a1bf4adf3a3d7f8b1fa3d75fe", + "0xa0aa11754d4ee136ac3ca609b17bcae77758763b2016544ca7921dddedd8aafcc7ad5f2b337c8bf53084eb8e43ea41fb", + "0xb8713b7aa1c112178195fdcc9b7024f46e6bc04c4e76c41abe620aa265287809200d98eaed6c9703fa97e81d6964f0ec", + "0x8605b5b33309e9ea6823542b85383c496794b8481c577497aaf99ba90496e794dce405be615bf92c7b6361460e6b82e3", + "0x826fa34faa7f83e063a7bf172addfc07badabada59cfc6604fdf481d29085251c0a67a1355b2cbd374e2975934b84cb6", + "0xb45d131082dc16fa53af010d43eefb79200dc23d2f3ee26af95ac6a5cebc49c84a9ed293e534ed16ff3ef9a4a25456ec", + "0x91bd6ce3c5396a7a0de489e49f0cdf6dce1cd2d0be7a410326423c3185bd1125ce1e610768be7f15f4e44b62f8834fc3", + "0x903ffbe3d33fbf106c01c727dc3a385201a67ded70d4df623934882f69a3a96c909b027a124f3d70cb072b0046a149e8", + "0xb405359db9d9ef4821a181b440ef2918c240595141d861d19a85867a5afa74d2972d22c988775eab441e734700bae4a3", + "0x8abb756d027233c83751910a832b0ef4d28d100077f1c5d656720c94906f91d85dd0ea94b1cc0ed95b692efee14c786e", + "0xa78ee77ab476a41a3454160ba7ca4085d8b1f7057c63e76db8b07cf20afdeddd2250cd00771a6329133bb4ad48ccc20a", + "0xa41810271d8c37197aa9b3dfcefe3498e42f5978d3f3d59defff4676d6402d8575b40683834f184f143b6cfbfc859b3a", + "0x90c24a0750242660bcc6d487358a3cc015730538a0a8beb00ad5ac2ef33cb8ca8a62121e50bec8f3d2f43900f8e3134a", + "0x8b96c39695d864ef5796941754978a1fd612b369f6b77fe5ae6587beac936ee28190af8f0a3822b63060af35e49a5c8b", + "0xacde2548883d0e63c0fc257bb9dadd919aba60a985b69ebcfa1bca78acca42fc1322ec30bcc8e7c188818f858d04ad33", + "0x895c86ae9ff8d95f2707d4838a3bc8ddb05b2611f0476f014b9c150d0e8332bc73285037a747426f09ac8179ba4e19fc", + "0x821761fe406e18bd86fa9ca9db99d382cd3b5c70c456f471fa3706d57763d147706304c75d54f51ce8f3115aa26e59d9", + "0xa803a80e3e8f47dc3c59ea23eafdec017458eac648b360cd42cbd075e0dde6f6f450b48c7646fb1e178c04f82ae51a12", + "0x91f40e1b6f588bd592829ce937996452c40be0fd6c43793c607866701ac6a8c7227e0891d45c6e7b1599382b0a3fbdbb", + "0x9408246d996a634a58689337f2526dfb3ba9ffef1d3ff91c32aa8cbbed900861ef25d6477308b67d76491edfcc70d65e", + "0xa492325a427f3df1c9c690c5b553daa8ac41f62f5ae55f425539222bacf959e2f67afabbba1732e120d3e7a6dcdf7049", + "0x8fd0c3e15477cae228613a171b6e9ec29ddc63ef74854d99b638adeffe39f89f34346a42851e8445e855a9f2bbef0f57", + "0xb735ed01fafa051004dbaad5e8c9e2faca8f6049ef9b590f256ea4d75b04594af12764ad4e6031735eae36f83179db93", + "0xa7d35f43fca06c86b3425dcb68a87186834ba9740664fd657915771beca4cdc0fa2fc9b4c2e9d9bdad8ec33543ddfa59", + "0xa1156e71e2db1b17df5da28747c88e091bd687bfee59d89096437ab4dc9a543fe5c5272d5023d72adbaab397a6fc94d1", + "0xab06a58bd81b33a411bade8d8c5232d38fadc2e38507159edea6e2e104b8ebd65ca02b05335118f691d44197b847a4dd", + "0x848b67a10f1e6ff8f5c228f226ef2ffeb67fb8f50925fc94cbb588d61896d9dc79726959e649898fd3354fe3ff7b7ee3", + "0xaa933397361f32b388edcf832f0db172a38e756b34d5f7a4a050fa7325058006c22cede26ee27917e8f1b0f301792bd7", + "0x89e49e7f02cfaae4a4b9c4180c9f6559d76e3a45774955859d4147970b1470dac37bdc9aedca1c32a20b045049161590", + "0xadc1825d5ab94fc719f25d8c9773f4d518134ed88eb13ac33cb910b2be3523ef9ef88d9e4aea2418b806e20108317bf6", + "0x96c4b444c8a023da644f3a343ebeeed19a8392d2ce175992461451c318a54273b76c3574d8f2dceda2947ddd34d1a674", + "0x8aa7e97e87c8c5b29bbd51a6d30396a6be1fb82b716ef83800f2c36d5b85467ade7e0f59d2db82c310fa92a9265f0b03", + "0x9146c32d99f02c3a6f764dcd9b4807f1585f528ac69dc4f84e4380f6fda4f9d5057c375671d51e7aca2b2b4140e83da0", + "0xa10760a533d9bc57536bcaf65f080302086aa50225437efd64e176841544711828c23a15c49c0dd1f357d3f10722ab72", + "0xacb0811777e17f7ae7aaba5f6fce81b759c067a4908730916195a2505c7450d0e6e2194c2ef0f241090597d58e70de47", + "0xb24f161e9bcdbad56665e2490b5e4c7768390d4668cd69a04ed74739062dbe832636dd33cda89e9b0afa8c77e93fc641", + "0x96b4d01106b831868a88ef016500ef2fa42d0ce87a37ca8ca4194a92a22c113edfe04eb2ca037329f3c1acc635148f55", + "0xaebbb95fb4f7adcc8e7a217aeb73f9e037cbb873d08c1cd9d68c6c6834511adf1af8b44567fee84327599bdcb734dedb", + "0xa9bd8b17300532fb94d028659bcafbe7bbdf32f8945baf5db4cfaa1bac09e57c94cad0ba046b4514044b8fe81ea8596d", + "0xa5557cbda599857c512533e7cadcf27bf8444daa0602aa7499cafc1cf1cf21f9d16429915db7485f0e9a1b5046cf01c5", + "0x8810307c40bc661c478a9747ebf2a30e5a5ead942d1ac0418db36ba5db0709c476f7d19685cabe6959e33ec1f3bff914", + "0x8829b741f41f2c32e10b252d9338deb486dba2f23996a44cf1dd888ad967a589d51329be34d764139f372a1043f6c2e5", + "0xa6b4728d18857c5fa082fa67bfb3b1d801e76b251b1e211a19c87cea5fe7ce757f943c85071f7a03a718388cd5690e95", + "0x86da7f397e2533cd487f962ae58e87bea2cd50af70ef2df9ea0f29f70b5843cde664d30ec207ab84fc817f3851277e02", + "0x8085776ef4ac6d42ab85b9d9135ecc6380720efd274f966544eeedf4684028197de76ecab919fa5414302597e1962bca", + "0xb05a065c733033d223ba13d16baa7a97bd8c8b8b1f0e59a9bdd36ee17e9922d48eb39bd180c168b122088a77f0bf321a", + "0xa89343fe44a93023dcc7ef71bd3bcb6786f68e1885ad260edc56a52445d34757f476395ba7ad35437f89bc573c7618dc", + "0xa114a9cd6105b524f3969c69faa2e09afe21753a93361a296f9e0e3b4e3e63726ddf2e6bfd3ddc046043e50bd44e539e", + "0x8a5611fec539cf681c05636bb580f29acc06f628bb012649ffa41ea6c1521194a5643d5dd843f09b6eb2c3bdb4d41acd", + "0xade247c4011ec73ec90b72f35afa59a999e64ba5a7e664a4b30874fea53ba6a14a76a41b58a5f891a20d019e5f091bdb", + "0x905b5d96df388160ade1ffe210d0c6d1979081bc3de3b8d93ac0d677cc2fc2dc1ef6dcd49d3947055514292a3fa2932e", + "0xa9520796ca9fccd11b7524d866507f731f0f88976f0de04286e68d7cf6dbd192d0d269f0cd60fd3d34011a9fe9e144c2", + "0x989a1edf4d7dae811eb57a865c8e64297837ffeeaae6ee6ac3af0f1044f023f1ca552bf00f1642491f0f0f20e820632e", + "0x879c8e63713f4935ed6e020559e140ea3073ced79d3096c152c430141272117b4fd9a9fc3eef012e81262df02ea14bd7", + "0x95074738ac1540c0312274333acd1ecad9c5509fee883c4d9295fa8d8200f6e637c363de395f9fa612f05c0dc58fae88", + "0xa770e4fc595269eb806b113ab3187ea75c8f96b57bf9fcfaf535f3eedc1d4d7e6285a20990575de0ff09f62d06ed0692", + "0x81283e5dfb6423439ff513eca1cc316941d196df8da2d1069d2d0b63f5289e630af2fd4119bc0144c002d33313372dab", + "0xabd1b108e743887b78f698f2aba9d5492f87a22868d1351d705d93a1084fd45be67170c68a6e18b07f400d9a01cda8c2", + "0x8509c3f67b92908cea8144f4e2a71631a66a61ac3547601c788907e52e380e5fe8ae4110aed95d13c67d3bcdd5b55a61", + "0x8fa5a790ec5cce6d4114128c295390120869aac5490a82feebd3c37a167120df2e7fdfaf2a4050a7dfebf48fb093212f", + "0x944753e1ea7d8bc727d46a7702077dc01dc0c6574e8263a16579b57ee155ca5901f71bb347a01a9a922b329d3ff75135", + "0xb46bc1fd4590b7a6275e20036d247c5909fc549c78e95b64ae7ed96e3b05bb044840f19f7650ebfe7008ba09fa83c3c9", + "0xb1e47e4d88e59a06c465348c6cc4181d40f45b91e5e883966d370c26622c328415c6144aa2f61ddb88ec752482c550ca", + "0x8bd4f8e293e3f1815c7e67167618fb3b0ea76424bc0985908957cfcede36109378e41b4d89555b8c2541b4c447e00461", + "0xa70589a867b2bfb63d0106083d58475d506637148549ed35c83f14e5c8de996e1b1f3447ecc80cf5cd134ef4db9d2fb6", + "0x8048b80ba6131d07370162724127b0f7cb17fa7f71855e55e5a75bd0a9e4fd71b0d0ea2d16ec98858e458528df8d06b5", + "0x97326cb94bae7530f4ec3235770c5a7ba042759e789d91c31fedbd979e3c0e6a2c69e2af3c1979c6fe0094274dbd53ce", + "0xa18e9c1d3eabd62af4e31a4b8e08494f4167fd4598c95d0123f39c46c53f9e93f76615900246e81a286c782ac37c569f", + "0x80309c59d4522b15aba617cd3c6238663e8b1c7ad84456346082c8f281140fc0edf9caa19de411c7e7fb809ca4fa3f4d", + "0x8e450c0990e2f65923f252311623038899eeff7b5c2da85b3a224e0ef7132588b291b782d53c477ecb70f34501466178", + "0x87843f96f41484e254e754c681a65681b9ae5c96c292140368743df9e60f7e2ada58ca2bb95fa39abe064b2ebf21eeba", + "0x858e8d5bf2a1cf26d8af5036b28b831d450a446026f58a1734b696c18f1f41482796b91cab0e5b443dd2f0b9cffa52b4", + "0x99627dd6bad8c05c5904cd23aa667d664da846496dbbb8452705c4ec01e1480e9c7295504a5a8529e4a0c842306b038d", + "0xb64b33256c18b2c886a837a0c0730fdfe73befb0e2796207c4dc592c5a33cd51f8c2ef47c584dd5773abf9ce9c1b0082", + "0x944f6da2a1546f0bfc4d98c3e73c79e935e33d208b6be26b0b5f8df6d0e3b74a5bda649853b99281bd3a3ec799a7dd04", + "0xa266d165435784d4e884640155e35b2a911b3f89e1e715986de419b166a36a341ba724877d80583fa3da566f6a828971", + "0xadff2698409d0756e78c534032ee926560c13d578cb178d5073172d049ebbce32a92692f7e2033ec781b9b0d894ddce0", + "0xa91933f110756c699c28bf9e24fd405bf432002a28c4349e0ca995528e56a5a2d101b8d78afa90a178ff1a9bf2ba515c", + "0x8e77839c0eb4da2d01e4053912cd823eddffbdc6b9c42199fba707ca6ab49fc324288b57be959fbfb11d59085d49324a", + "0xaa124517c76692036c737e987f27c2660514e12a953e63ff4bcb269dd18fc44dae95e282de8444bed09639ef6577af88", + "0xb285deae99688f1bd80f338772472fa2b35e68887c7eb52c4ef30fc733812444c5cd110050275ad999d5a9b57f782911", + "0x8877b0fa85b44ef31f50bdb70b879fa6df5eb1940e2b304fd0c8f08abb65f3118fa3d97ff93919038c1e452fb1160334", + "0x8a89f3b50dcbca655024542ca7d93df17deff5c7d01c7da2bdb69e76b3e0b4490d85c800fb3debb4b0b4d20c9527f7ad", + "0xb7e5dbe36e985354ac2f4ab7730fea01b850af00767a6c4d8ee72e884d0fe539bb81f2e34638fcf5d07b7c8d605f4c06", + "0xa85a1d78f6d4f9d5d83ec0f2a426708342d4e4a5d15625554e8452f6a843d9aa4db0c7e68caebdaf767c5b3a6a6b2124", + "0xa518078a9dac63c5bf511b21ed8e50d1ccede27ebfe9d240937be813f5ee56aef93dc3bf7c08606be1e6172f13f352ce", + "0x91144eedebda4d1ad801654ef4ecd46683489b177ba1de7259f7dd8242c8c1700e15938e06c5d29aa69f4660564209a0", + "0xa16c4657bc29d1d3271f507847b5a4f6401cee4ad35583ad6b7a68e6c2b9b462d77b5dd359fd88ea91ce93bb99130173", + "0x85b855778f4b506880a2833b8468871c700440a87112fa6a83fd3ddb7e294b3a232d045dc37dfc7100b36f910d93c2ae", + "0x8d86bb149d31bfbf1fabcae1b8183d19087fd601c3826a72a95d2f9cedb8bb0203d1136a754aa2dd61f84b7f515acfa9", + "0xacfe7264eee24e14e9f95251cbcfdd7e7f7112955a1972058444df3c2d2a1070627baefada3574ebd39600f7f2ea7595", + "0x906bd14ecca20ac4ae44bff77cc94eb5a4ecc61eba130de9838e066e8766ed3b58705f32c650e1e222b3100691b3806b", + "0x8f2cbc7b8593c4be941dd01b80dc406fe9dfdf813ef87df911763f644f6309d659ea9e3830ff9155e21b195fc3c01c57", + "0xa68eb15ed78fae0060c6d20852db78f31bebb59d4ddc3c5bdd9a38dbe4efa99141b311473033ff8f8ea23af219bc8125", + "0xa95cb76c9d23fc478c7e8a73161f2ff409c1e28a2624c7d5e026e3cee9e488f22225a0c5907264545a73e83260e3a4ec", + "0xb76f90e55fa37c9e2732fd6eba890dd9f1958c1a3e990bd0ce26055e22fe422d6f0bcc57a8a9890585717f0479180905", + "0xb80cc95f365fabd9602ec370ca67aa4fb1219a46e44adf039d63c432e786835bb6b80756b38f80d0864ecb80e4acb453", + "0xb753c86c82d98a5b04e89de8d005f513f5ea5ea5cf281a561d881ed9ad9d9a4be5febb6438e0dba3d377a7509d839df0", + "0xa664733f3b902fac4d1a65ea0d479bb2b54a4f0e2140ed258570da2e5907746e2ac173ace9120d8de4a5e29657ae6e05", + "0x9479722da1a53446e2559bb0e70c4e5bf3f86c0ce478eede6f686db23be97fcd496f00a9e174ceb89ab27f80621f9b80", + "0xb707fd21b75a8d244d8d578f3302d1b32bb2d09f2bd5247dff638d8b8b678c87d4feab83fe275c5553720a059d403836", + "0x93214c16831c6e1d6e5a1266f09f435bbed5030c3c4c96794b38d4a70871782002e558d960778e4465b1ff296ffedad8", + "0x8648f84e18eb63dad624e5fa0e7a28af2ee6d47c28f191be0918c412bf24b5460c04bf2b7a127c472914a0741843f78b", + "0xb67f61e75d6b773a6b58b847d87084b94f3cdac3daa7bef75c2238903a84250355a986b158ff96ba276ca13a6035fdd6", + "0xae9b094b7b5359ee4239d0858d3755a51aba19fce8ad82b0936cca48017523319c3309409ea6e9883a41bece2077e4d8", + "0x8d1d8e1fba8cebd7a0e1effea785a35e16b1a10842f43e2b161d75add11eccf8f942d2ae91c20eef6c1a0c813731ea9a", + "0xb82bd387458e3603782d5e2dec32ae03890a3fc156d7138d953f98eff4200de27c224f626e3648e80cd3dfc684c4790f", + "0xa6dd02a89ad1c84e25e91176c26355e21a01b126c1df4d22546159dab9d502dbc69bc0d793a017c1456516e4aa5fa53f", + "0xa9ab74a5c5459b8500beb0ad13e9cfe2656e966dc9b4f3f98bec7588023b4ddebf74e4fc722d30423f639f4ee1b2587f", + "0xb03e5f33ab7ecec12cbc547038d3fa4f7ea0437e571891c39660c38d148212d191be29e04eb2dc001b674219b7a15a9c", + "0x925df4fc6e898ca55090ad1a8f756cc5014167a042affda5b24896eeb6aac408545134920586a8e1a2b997de9758b78a", + "0x98c8580fb56ed329fad9665bdf5b1676934ddfb701a339cc52c2c051e006f8202e1b2b0f5de01127c2cacf3b84deb384", + "0xafc3765d374c60fac209abd976fe2c6f03ce5cc5c392f664bb8fac01be6d5a6e6251ac5fb54cfcd73e3b2db6af587cbb", + "0x8e7e98fb5a0b5b50d1a64a411f216c6738baaca97e06d1eba1c561e5c52809b9dab1da9f378b5f7d56a01af077e4f8cf", + "0xb724bf90309651afb2c5babaa62dc6eac2b8a565701520fe0508cee937f4f7b6f483fc164b15d4be4e29414ce5d3c7d4", + "0x9665160e7bf73c94f956ecb8ba8c46fe43ae55c354ce36da40ccc7594beae21d48d9c34d1af15228c42d062a84353a0c", + "0x8600ab3aa86b408ee6e477c55572573ed8cfb23689bbdadf9fccb00161b921ec66427d9988763a7009b823fa79f8a187", + "0xb0d8d19fd1022e7bc628d456b9bd1a2584dce504eb0bf0802bdb1abd7a069abbeeccdb97ce688f3f84a229342dbc1c33", + "0x8f447d5e5a65bb4b717d6939cbd06485b1d9870fe43d12f2da93ca3bb636133a96e49f46d2658b6c59f0436d4eede857", + "0xb94e327d408d8553a54e263f6daa5f150f9067364ded7406dcb5c32db3c2dffd81d466ee65378db78d1c90bc20b08ab3", + "0xb58c02781b74ef6f57f9d0714a96161d6bfa04aa758473fb4d67cc02094cd0c0f29d0527c37679a62b98771420cf638b", + "0x8cfa0a687ea51561713e928271c43324b938aa11bb90f7ffaa0e4a779b3e98899f2af59364ce67b73a46a88748c76efa", + "0x95d6d39c814c5362df69116558d81ce6f1c65fb400fc62de037f670d85f23f392c1451d43341c59bc342bc31842c8582", + "0xaf888b384c52d9e04e4db6c4e507c2037eb5857e9bcc33acf84fc3a02d93cbde8cce32141fce9f5fec715b5f24d56356", + "0xa7822bbc3c236fd58bd978f0fc15fe0b60933a0c953db6436a233441219418090ae0c07c490a6548e319029771cdaba7", + "0x8c53729f750922e5eb461774be8851a3f40fe42eed170881cc8024d590bf0a161d861f5c967144d15cdcdc3dc6b5cf88", + "0xa052a25a4aeab0d5bb79bc92a6ae14b5ad07d1baca73f4f6684ccecfc7ea69bc21eadeb9510452fdba116c0502dd698f", + "0x923946b83d37f60555dbac99f141f5a232728c6eb819a37e568c8c6e4d9e97a4229fb75d1de7e9d81f3356f69e6d36f1", + "0x8cab82cf7e415b64a63bd272fe514d8b1fa03ba29852ec8ef04e9c73d02a2b0d12092a8937756fdec02d27c8080fb125", + "0xb1123314852495e8d2789260e7b3c6f3e38cb068a47bdf54ed05f963258d8bcabaa36ccbea095ba008e07a2678ec85a7", + "0xa685b779514961e2652155af805996ceb15fb45c7af89c5896f161cac18e07b78c9776047c95b196362c9ad5430bcb22", + "0xb734dd88f6cc6329c1cb0316c08ade03369a11dc33191086c6a177cf24540c7ceee8199b7afa86c344d78d513f828e81", + "0xb0bf492fb136ecdb602c37636ed4deef44560ab752c0af5080a79c9f76a1f954eba60a0bf6ba8bd7b8cac21848c29741", + "0xa5c74682323e85ac20f912ab9c1d6e1b9246c4c829dca40c8a7d58ec07ea0ad3524be30623f351269552f49b65a1245c", + "0x837403b9cf830fb33ecc11a7c8433e07745973c36acdeb3fc9ea8f7d8d690d462e1250b7410f79f2f4180fe8f3962a4f", + "0xb03d64b944d49c83608f2c5b9c14070c025f7568c4c33d4eeb1da31d07f0bc5897e498b35b50d557ee129f0c3c68e254", + "0x827272aab8bf757e2483156e00fbebe1093a58070dd3af9855bbf946c7abfb9c8a850a6a8acda8c620902f391f968b8f", + "0x84c4eb863a865282d321302d06b362f8bd11c2bb0090f90ebffedd3eb3e7af704cff00d39a6d48cbea4262942e95200b", + "0xb044eb91653dc55dce75c8d636308a5a0dae1298de4382d318e934140a21ca90e8a210e06fdf93aadbbeab1c2ef3904a", + "0xa8c08955a4378522e09a351ecb21b54025a90f2936b974068e80862803e7da2b5380c4b83b4b4aad0409df8d6c8cc0cb", + "0xa763a5fb32bd6cb7d7c6199041f429782deacac22b6a8467077fab68824dd69343ebca63a11004c637b9cb3129dbf493", + "0x8c44c8afa9a623f05c2e2aba12e381abdb6753bb494da81f238452f24c758c0a0d517982f3999d2537b7279d381625ed", + "0x8613f47fda577cd3bda7c99b80cf4b2dd40699edfd3df78acb5e456dd41fd0773bc8da6c5e8cbf726a519b9fb7646ccc", + "0xb21a30d49d7e1c52068482b837a4475568d0923d38e813cea429c1000b5f79b8905b08f6db237e2eccf7ef3e29848162", + "0xb9bdf4915f3fbb8d84cdfd0deedf2c9dc5b14f52bf299ef5dca2f816988e66322df078da2c54b934b69728fd3bef40b5", + "0x993b45f389f55eba8e5ba1042d9a87242c383a066cbf19bc871b090abe04de9ff6c1438cb091875d21b8c10fac51db58", + "0xa85a95d14633d52d499727f3939979a498c154fd7ebb444b08f637b32c1caf5cca5e933a2f5d94f26851ae162707b77d", + "0xb9874c7c4be1c88a9646e0c2f467cd76bc21765b5ab85d551305f5ec0b4419e39d90703d4ac1bb01feb3b160517e97b7", + "0xad6771177fc78812904c90594712956357de1533a07fec3082ba707f19c5866596d624efc3e11773b3100547d8f6c202", + "0xa79f31921134f7197f79c43a4b5d5b86736a8d3ad5af1bdf4ad8789c2bfe1c905199c5e9f21e9f446247224f82b334f8", + "0xa7f1b6c45321222a350a86543162c6e4e3d2a7c2dce41aeb94c42c02418f0892dbd70c31700245d78c4d125163b2cd5e", + "0x92abafe3ec9dbe55c193fb69042500067eb8f776e9bf0f1cb5ab8eb12e3d34986d1204136856fb115c12784c3b8dea6e", + "0x89bc761238a4d989006ca5af5303c910c584fe7e6f22aa9f65f0718a1bc171e452c43695e9f5a591725e870770c0eceb", + "0xaa0e44c2b006a27d35e8087779411ba2f9f1966a0f5646ff6871bcf63a8b1a4a7638751b94c9b9798ccd491c940bc53f", + "0x8736fe82862b8106e7fdab7b5a964d87ec291a74b8eb1cb5a6c046a648c1b686064ef3d52297043b8940bfe870c712f8", + "0x956a3def1942f05144d8e9c3a82fd2d3610064b53b9eefde3d5594a8f705bf8f6849eb2c22181796beffeba43cc74ee4", + "0xaf27416d00cf97d5a1f4a1b6b51c010884cceca294f1151c3b684a3f83c3c8a3c30771df1166d833cbddf6c873c400c3", + "0xaac3b8dca2336fc4ffc63c362df461289e4bbd3418c621bde6c581d3ecedf66e2b3e523d4db39e3d8ba014577bf85efd", + "0x94c3a8167f62074e5b28c2bffe4b6ce645439a9a0c5da3ca1b3ee956590a465d6f84a8a4dbbe9070ffbd6bbc734e4d62", + "0x95e23ba6986d25ed4451215da05bd72c5491528271726d79a94c8cb16aef1c85b190d6c5b8a3a1191c7cafbab1dccf0c", + "0x953e3dadb5ad68f7de31ac09692948655d174fe16d88b96930ef35b331da7f1dbc4c17863cd07b4ec3135b5205891a27", + "0x915d018f18b5d63cb3301c2bb5c6e85e75a88ba80663c964d06575b6bacbbe59139d030b218ce0998271d5b28c00b26d", + "0x8c871ba3dd138a908b2f7effeea0e71df096b23e0dd47cab10b9762b250abfd1221da94a8ee884e05bdf02271fb85a04", + "0x96bad5c6ebc3080ecbe337409ae398bbeada651221c42a43ea3b7c08c21841ddbcfde544c9b8d4772de6f2ce92c0b963", + "0xb5dbcd0b1c44c62108841558ec0a48df4b327a741e208c38b1c052321eda6e6ad01af71d49dfcdd445ab6fa6f0c34e6d", + "0x97dba59219b69e8aef2659d1f10bbea98d74aefff1f6451de3f41be39acbac0122b8ff58b02e90554469e88911ec3547", + "0xb7e5682ec306478be4858296f5d03364a61f3260636a4242f984d351a02e8723378496beb30c4ca22def9c9ca193ea70", + "0x9656a7a3df4d11df3d8bc35930dff70a5e78a488ca57bba20bb06814fc390fc6c7cb3f39b22134992aad196cced577de", + "0x8b269695aa63eb56d0324ba984279dc4c88e565321f1d61d553622bd4f1910d5eff68393d3a830eb924472bd478c2aa3", + "0x9177bcd04b28c87bc0440268b4c8995c6790cad6039594971b2c177f0e197055231e776927d3fa30d98fb897a2ba401f", + "0xae0e943973482001c4f214b9da82e1c27e38aa254d0555e016095c537c835d3702bc2de5c67b234ab151e02b3b7a43a6", + "0x82fc719a7d38bf4787fe1888019ad89fbf29beb951d2fece8686d2beb9119d0c8c6d13bc598748c72c70d73d488140ca", + "0xb716dc66f87eb16b95df8066877353962d91bf98cf7346a7f27056c2a4956fb65e55cb512af278783887ab269e91cd76", + "0x81d58cd8bc6657362d724b966321cd29a1b5cdc4601a49fa06e07e1ad13b05e9f387ca4f053ed42396c508cd065c5219", + "0xb32ad0280df6651c27bb6ddbdc61d5eb8246722140a2e29c02b8b52127de57a970e1ded5c2a67f9491ae9667349f4c46", + "0xb68a2eb64cc43f423be8985b1a068e3814b0d6217837fb8fbfd9c786db9cca91885c86899c50a1242040b53bf304ced9", + "0x85887515d4e371eabb81194cbc070e0c422179e01dbda050b359bd5870449c7950e6b3947b7a4a0eb68199341cc89fc3", + "0xac5fff3c27dfbab78eb8aad37ac31cc747a82401ebf3644a4f4f5aa98d37b8bf3b3f4bd8a3428b32a127c25c9e19d239", + "0x86fceaa6fbf8913553a9e1e907fcb1f1986d5e401a7eafd353beefd1899d571454fea96ff5b2a21254d9fb693ec94951", + "0xb6778bb296d3f0de2531b67d36fdbfa21475be0ca48b9dfcc38f396c41b557823735ed0b583e525a2bae1fe06e04058c", + "0x898088babeb5b9866537d6489f7514524c118704abd66b54210dc40a1c1ddb0a1edf7fe0b6e0db53b836f1828ecf939e", + "0xb27854364b97274765f0fb8d1f80d3660d469785d1b68da05e2bd1e4b8cbbe04304804d4c8aabb44cf030eba6c496510", + "0x8c55bbf3603dc11cb78b6395ccbc01e08afcef13611f7c52956b7a65ccf9c70551bff3ae274367200be9fc2d5cb26506", + "0x947726f73cd6281cd448d94f21d3b91b96de7ad3ff039f9153befbb5f172db9f53cacb4f88c80a3db26e6a0f7a846eb0", + "0xa7b733a05e97528812d71cecb4f638a90d51acf6b8fcbc054787d6deb7e2595b7b8d1cbe1aa09d78375b5e684a2019bc", + "0x8d5ca6d161341461544c533314fe0a6655cde032c2d96f0e4ea7e41098b8b39fa075d38e2d8c74e2d0308f250d6cf353", + "0xb960e9f081393e2260b41f988935285586a26657a3d00b0692ea85420373b9f279b2f1bb2da2caae72dd2e314045f1bd", + "0x852a49c7388c10821b387c6d51617add97ba72485f52be95d347bac44c638c92e9c6a44ba0d32afc4d59178a497d944a", + "0x8412162a65147e1334ad5af512982b2b48eef565682b3f3e0bbe93fbc5e1103db9375a0c486bdb1b2c57e4cb3a8e7851", + "0x8f52c3eb5d4f1e1e82cfd2b291d4910195427603b796f6c311deb35ef14a01a57a9e6cad39619ad108f3e86f384f9e1c", + "0x88d221088f2bf0103c53e44d0d96cd7881ec2b0a965db9121a47481771a8b796edd5ac23c4f9c208a171dab301f7d3bb", + "0xb49c3235e8b3617ed08a1891b9e2bcb33dbdacceb94ca96330555b7e00904fe6a749ced9312b8634f88bcb4e76f91cb1", + "0xa85834215e32f284d6dfb0cbfd97f6cffc7b9d354e8f8126d54598bb42d7f858a2b914cf84fa664069632db2ff89a332", + "0xaa3d48eb483c6120c27d9b3e3d0178c1c942632ff54b69f5b3cfbc6ad4ff5b2b9ce6eb771fd1eea8edf4a74c97027265", + "0xa446cfded353cdd9487783b45846402b973cdeddf87e2bf10cf4661610fff35743cc25e8d3b5771dcedfb46b018a5d18", + "0x80998377b3b393ef3073f1a655ad9d1e34980750e9a5cfb95f53a221b053ddb4d6985747217e9c920735b0c851d7551f", + "0xa35ac469790fac6b8b07b486f36d0c02421a5f74ea2f0a20ffc5da8b622ac45dfccabfb737efa6e1689b4bd908234536", + "0x8fb1f6d8e9c463b16ac1d0f36e04544320d5a482dd6ffaec90ea0f02b4611aaca984828bf67f84dcc3506b69af0a00a1", + "0xb6e818d61aea62c5ed39c0a22ccbb327178feebdabda0c9927aa1549d2c5bb0637785c4aed2a6d9a7b4989fa8634c64a", + "0xb4e7208d16018bf67caafe996d436113eac619732e3f529a6efb7e6f094d8ebea55b7be0e122be075770f5957b6ea6f0", + "0xb691d38b552befac61f6d367287c38d01fec73b7f2efdb6713ca30314a37fb7c177eb111fe6bee657f2681014e07630a", + "0x9817587e418e6e7e8e97ae27067f17b55d25dfb14e98f63f530620c855d9a348c9fa571c8508e2741f902f8b9fdc0c5c", + "0xb6a6e5ca779ba140bf1d84cd5394ede8262f7479637ec0087a4b152243a1774ba916d8115ce759a3bebd1b409de5f2fc", + "0xb53d1c84ad766ff794bf497db3228efd2cc8ed5fc1958d89c1126efdff361610ecb45ea8e329b39035ab00a66c1259c7", + "0xadc31333c507c8e0f4aa2934fcdca57fd9c786722a50dbd5404e129541f7ac182cc7373bf14e1e4e06e6cf94b31b90eb", + "0xa82b7fde4642d982d95cec669efee140ad797a2442c7f6620580527d163accbf021b893446cbb8038ea82fe25b15d029", + "0x91f7acf8a8903979afa281646fdecb54aa4d2ed905748e156e92f0910de268fa29d67107d40863935d677d1de8039be2", + "0x86fea71c6d43a7d93216a92fc24dfce8521fd4534a9558b33762d002081247867a6eff54cad7116023277fb4049403ad", + "0x8ae5369a7f9f4c91f3be44b98089efd9c97c08f5bb4cd8b3150c115ecd86288fa0865a046a489c782973a111eb93966e", + "0xb6fb9e829aa2c81c2d9eac72bb2fd7f3a08e0cd763532c2ce3287444d33cf48b3621f205e9603ec58525934b61a795a9", + "0x83e35ca808d84e41fc92115e9f6e283e928c3a614e6dfc48fe78c33b6411262e7bfa731eadb1e1937bc03cff60032e1d", + "0x832fca5196c95098ad47b7d24ba2f9d042e1c73ad2273edd1c2ce36386796ccc26e8567847697f3fcc2a0536a2a2087a", + "0x8fdb7038bc8f462ab2b76bf7053362f9c030019f1b6105cf42219a4e620ecc961e3eacb16a8e581a562a97f1418b0128", + "0x8d3a5a404b51b1ad8ce3b23970e0d5cc57b573922341008e3a952a1dd24a135e19e55b79d86a70cfd82e1c0e9630f874", + "0xba00c025c1c21c57c03cdfc0bfd094b35422281ff0a64b68b240617aa58c6b18800af5f2047d3ff9068bbe987d6c7980", + "0xb468f0dd51964b3806b0aa04f3fe28a035e8f5567fc7d27555be33d02701a838b8dbfe1348b6422c4eac46d2c75c40c7", + "0x8a73a18c97da9958903c38584b08d0e7e26993a5d9b068a5e0e1ee0d8a873942745cf795f94f7a3d3ba88790a9fbb2f6", + "0x953a0a40c2c8102723736854d13b228698c14a02d85c8d2e61db1a768019ac305faf0d5db62ac976430ce087a5b20f1e", + "0x8998219da6b34f657cb8a621c890a52cb98c2bc0f26f26e2af666eebeadadc5e8bdf4f830a91d04aca8ce186190152c8", + "0x8941e08c3155ad432236ed05460420a05dd0aaab30477493ffb364b14c00ea5b9183d30d3442b6321d2d20c36e4f5c7e", + "0x93f293ff7fb56cf5b03aee6f3ad2ad78444398ed5b3be56d7bf5b56b5aa5a2b980d13895dd57a5726d1b067c20cc55e2", + "0x84a16f313e3f75e31824f58d19ab24c6611fb4c75140a7cadc3c166f68819547c1d0ff7f7d13f5d8ae30dff1d80e2aa4", + "0xb6e3e830b15039d3e28b08f5465bb089eade11ee3bd80afe39e010df7db1fcf0c56d698717677a41ddbc91eeaf6544d3", + "0x95e928e6dfff51351281568ae72da7d1edeb6e9fe01f30af0499e7505ba35a22b5bb919d41bb809a432dce83f3977663", + "0xaabeeb60ca46f9b0232ff82ea7766dcab8cc5aaf9d23539f30174f9486640bc9312868ca493b59b314519fc399973e47", + "0xb393a11e957d0bbb3ecf617b075b5906a3450b348e62916c04791b366f0a7397cccd6648440ac544bc30526e1f95aad8", + "0xabb5bfc3964a6d246da60bd809d0ea6daf4f8222efdc12ceb6730194e85f413ee7eb03bae300abf7ea900dbbc3d08971", + "0x96c1bd1d1d216a4bfbcf000c123f296c0d31e1684e9e3884c14df23bf528c8d599f82bb98fcea491716b617216a8e0be", + "0x92d1e570a56f1741fd9f3d9f488cc336421c6256c14a08d340a63720be49b0029e3780e3e193a2e22bf66cc652fa22a3", + "0x8769c08551e3a730e46f8e5d0db9cf38e565a001dfb50db3c30fa7fa0e98b19438edc23c6e03c8c144581b720d7b33a4", + "0xb850bd67fdf5d77d9288680b2f6b3bc0f210580447fb6c404eb01139a43fccb7ed20051999ae2323ea5a58de9676bfb4", + "0x80285da7a0aaf72c4528a137182d89a4db22a446e6c4a488cf3411937f4e83f7b00ec7549b0b4417682e283f91225dfe", + "0x80520368a80b97d80feb09dbc6908096c40ff7120f415702c1614d7112b0b57f6729581c71f4a3ce794ac959a46494ff", + "0x9817b4c27a490b1cd5a6337e7bc7e8005fa075dd980c6bf075ddfa46cd51cc307ad1d9f24e613b762a20fc6c877eab41", + "0xad66bda1a3034ec5e420b78107896ecf36126ce3ef9705163db259072dfa438c6107717a33572272062b9f60cb89557c", + "0x876114ef078c2915288e29c9abe6b0ad6a756b5ee2930ba1b8a17257f3f0557602d1225e8aa41ce8606af71ada2a971b", + "0xaa3d6cde4c3b9d3d5d0c77a33e67f182a3e1cf89b0921423b2024236171955b34afc52b1f25b1dad9da9b001371771d7", + "0x984d3e3a72412d290e3459339757af7520d1739c7af0cbcf659c71999328db44f407d92e8a69fea11625612c49eac927", + "0xae890d0faf5bd3280dcad20a5f90e23a206661be8842375fea2ab22aadc500849ffbc52fe743b376d46bb926cedae6a6", + "0xb1f231f3f4d710c3fe80099faeb56dac67c1baf53b8fe67a9920fe4f90e52cb9a4bf19211249a6456613b28efe337f18", + "0x8caa54b418ba609d16520af3dff2e96d5f2eeb162c065a1763beb926547b2cfb3ae41d738db2c5681a9bc8bc9e6b9a1a", + "0x932157ff56c5ac29cf6cf44f450c882b3acfbb9f43d12d118da3d6256bde4e6eb3183aea304ab6967f37baa718ffec99", + "0x9360bed8fc5b6aac36aa69473040689bfc30411d20ffb7275ef39b9ff5789f9055d149383ce9f0f7709a1f9d683adbfe", + "0x98b5b33209068335da72782179d0c7aeeabe94b5560a19d72088fe8323e56db7ce65debe37a97536b6b8a0ca3b840b61", + "0x89a385c11be40064160b030a1bb28c3921fc8078522618a238c7ea0f86f34717ed9af9b4e2e20f5128e5f7fc66ad841e", + "0xb615703cbc64b4192990cc7e4903b74aed6a0076ce113b59ef7719197ffa46fb29eb78ca56b49873487432d0625c0faa", + "0x90f0d77abae9d3ad73a218e5ccec505ad108ea098451461567ae8ef9661606ca8e78df53b5d628b20b7037bd24622330", + "0x92e0e7cc4dfadc5fa0ee6da0c8de0493030db6e54ba0317f52f232a6708b732068b6077bd13a17eb7eb40b88368085b5", + "0xa24dad20094985bfccc6df1343506ed3bf9dcbdf4b2085a87627a5d71f7568db067304e465f8f380c5c88e8a27291a01", + "0x8629a45a10619354c84bdc2f6c42f540eab5a46f53f2ae11970433d7a2aef007897590bf31dfba1c921614c6d6fe1687", + "0x84ac64040d4206f82b08c771f375da4b7d752e41d2aa0da20ce845f6bc1b880a855d3ee966bca19b8ec327b4b43e7f0e", + "0x9608e6050c25996c052509f43f24a85cdf184135f46eaac520a9a6e78e0d44a6cee50ebc054048c708aefde8cd6651c2", + "0xa32032b0e0d7cc35e480c328f315327f9385adb102a708c9ba637878deb74582ae26bb6d6e5f8c9e3a839b0e0154b82a", + "0xb7e3c78d63acc6564a49e9f00b0a820b56d4f37a2374af1f7f1d016268011df9e7af0670ed2b0eee961f15aa948328dd", + "0x8b88bfdd353acc91ad0d308a43e5fb40da22c228f2fe093c6d6904d70f69c6203f56636ed898b05df51d33f1095ef609", + "0xb1d7a430c51fc857af55047683fc18c453b013527196c5e1bf776819a3dffca802217e9249ae03f084e2ea03ad67fcc2", + "0x80558e28a819ddb5e72e97c54be0f57c173ccf78038d360d190b7f1350a19577b8e3f43fa2f7bf113a228cd3b965b2e4", + "0xb4b2ec44e746c00dfc5661ba2514930934fc805cdc29adc531c02d28ce3cc754414b0485d4ee593232cd1175f357ad66", + "0xb57cee5d32835f76572330f61ccd25a203f0e4a7e5053d32965db283aad92f287645533e8e615137208383ec51b1fd99", + "0x930256086b419a8a6581c52590d0dbd9f8a3564c79424198fca3866b786df2f6098a18c50dc4abd20853a7184b1ce15d", + "0x8e75fd01181cffcd618a983492390f486e8c889972a46c1f34a4e1b38f384e8e4efc7e3c18533aa2057da9f9623e2238", + "0xb375d927dd988429f9e2764e5943916131092c394fce13b311baa10f34b023dd3571da02553176091a0738cc23771b9a", + "0xb9e28e4c0d0477518034d000e32464852e6951c8db6f64ccdb1d2566f5094716213fbf2fc0e29ac88d0e79f725e3c926", + "0x963981e99392afbd2b8318d5a6b2b0cc69c7f2f2f13f4b38dddbfedb2b0eaf0584aecfcbda20a4c60789c15d77970a58", + "0xa7804e1977aa77c263c7c001afa6cf568032dea940e350d6a58ce4614f1a91c13ae1c78bfea740c229dce2444556976a", + "0x8787204177da3cde6d35cd3497fa8774d244f9faa9f4bd91b636a613a32ce2ea0326378cf9c4cf475e73ef751b355c4b", + "0x895aeef46a07152a04ec812f1aa1fd431389fa0ef6c6e96a5b833e70ea14073bc9984757a8ee456dbec9788e74e6f0ca", + "0x8d17f0e5826783440d1f0ec868003510a4d9952bfe4a638e44a36d94482ac18ba70ef7ff773bdf7a3b62d714dcf0fcba", + "0x810d5e36b31310b2e054a666d3b3f7ed16dfcb1765532d87ca2a3920316f0187303c27dd113db145d47e8961062a6c03", + "0xb4e2fb48ae04cf8580bb6a28095076c9b95e5f13122b917328f334d4ac8a8648ce442919e28319a40148987350ab5303", + "0xb85549a313544fa1eb3ceb78473b7d3d717fc85b808de7b79db7dbd0af838ebb020622a7503f1cbacab688dddb648f84", + "0x80665adee057088eae827a5fe904ec3ad77d8843cdce0322d535e0659b4abc74a4d7ddd8a94c27f2def5c34ac2c038ee", + "0xad72fc19c2ce99b5b717e35528fe7d3ac8add340b02ebeb4889d9a94c32f312a0b45ea84d21c54f84cc40ee4958b72e1", + "0x99d530c843dff89a47a5ee8c87303ab18f8a82b0d5b808fca050354b35da5c5a5594d55921c6362d6cc917d75bdc18dc", + "0x99c7286c293e1be21c5b2a669dfdfcd5aa587105d2886fc5a8eaf8984da4e907f7d7b8c2362d64a4f1621b077a2a08a0", + "0xb4a39e1a9ed5d80c9563c3ca3fadf76f5478c63a98f4346a61b930c9c733e002f3ff02bc16abfdb53d776184cc3f87ba", + "0x9378ea71b941979404c92d01fb70b33fa68d085bf15d60eb1c9fc2b5fcdee6379f5583389a3660a756a50019a2f19a69", + "0xb68e17344a2bc45b8e2e19466b86dc139afefbf9bad2e2e28276a725099ebac7f5763f3cb52002261e3abe45ef51eb1a", + "0x819e64dc412b2d194d693b9b3157c1070a226af35c629837df145ea12ad52fa8eabd65b025a63c1fb0726207a58cdde8", + "0xa5e8ff8748419466ff6df5d389125f3d46aedacf44eaf12cbfe2f68d218c7d5ab6de4a8279d13aecc25f3b1d98230894", + "0x91560d54a9715cfda9cf7133ae51c432d0bf7fcbaeb468004994e6838bfc5ddcfa30e4e780667d0c4c0376780b083017", + "0xae8adb3309cc89d79a55ff74f129bb311fe4f5351a8b87600a87e0c3ba60825f71fccf67eadcf7e4b243c619417540fd", + "0x8d92cc1a6baa7bfa96fbce9940e7187b3d142f1888bdcb09bb5c8abf63355e9fb942ac4b4819d9be0e0e822d3e8e2e08", + "0xa6e8b79fdd90c34735bb8fbef02165ccbe55ea726dc203b15e7a015bf311c9cac56efd84d221cc55eaa710ee749dbdfe", + "0xa409b151de37bddf39ce5f8aa3def60ee91d6f03ddd533fce9bf7bdbeac618cc982c4f1ffbf6e302b8353d8f28f8c479", + "0xb9693975ef82171b3b9fc318ca296e4fe6110b26cbdfd653418f7754563fa7b6e22d64f8025ee4243483fa321572bfe4", + "0xa039ebe0d9ee4a03ade08e2104ffd7169975b224061924cca2aae71464d250851e9f5f6f6cb288b5bf15df9e252712a6", + "0xb27834db422395bd330e53736a001341ce02c9b148c277dabac67dc422741bfa983c28d47c27e8214cd861f2bad8c6f6", + "0xa2bafaf4e2daf629fd27d7d5ac09fb5efc930ff2ae610f37519808683aa583fe1c6f37207daf73de1d8a164f79a0c981", + "0xb856cee1cfcf5e50db9af4ab0aed3db2f43c936eaea369b5bba65582f61f383c285efbda97b1c068c5d230cbe94f7722", + "0xa61ab205554c0550fa267e46a3d454cd1b0a631646b3df140623ff1bfffaa118e9abe6b62814968cc2a506e9c03ea9a0", + "0x8c78edcd106377b9cbdfa2abd5278724aed0d9e4ae5869b5d2b568fdabb7804c953bae96294fcc70ef3cd52ba2cbe4ed", + "0x8570869a9bbf6cc84966545a36586a60be4d694839f367b73dfc40b5f623fc4e246b39b9a3090694aa2e17e652d07fd1", + "0xa905b82c4da8d866a894da72315a95dc98faa3c7b3d809aef18f3b2be4801e736a1b79a406179e8cac8f74d27e71ac52", + "0xa8eb8679ff1a64908515f6720ff69434cb33d63aeb22d565fde506618908b1d37585e3bd4d044fd0838b55787af06b42", + "0xaf4d86b2fbd1684a657dffe4210321a71e6ae560c144d44668d1f324dc9630e98348c3d444622a689327c1a59cc169dd", + "0x80359c6eab16954559ab0e6a1fee9a0526c45d3cae1a371159a2e3aa9b893afdc3a785c9559a5fd9cd8cd774234bf819", + "0x8d4e5ff81eb5d17bbe8ae6416538ca51a9427ce142b311f5cbb14febbbbb9c1ffc6489fd625b9266264c366c12a9d997", + "0x92e181c66489c5fa063ba2a1a354b6fd3439b8b4365a8c90e42e169bfaa1fb5766bf3e0fe804399d18bc8fbcafb5c3b1", + "0xa9ddf229360a095393885083716cb69c819b2d7cfb100e459c2e6beb999ff04446d1e4a0534832ae3b178cbe29f4f1d3", + "0x8e085ef7d919302a1cc797857b75cff194bdbc1c5216434fa808c3dea0cf666f39d9b00f6d12b409693d7a9bd50a912c", + "0x916dc4dc89e5e6acf69e4485a09fc66968f9b292eac61a146df1b750aa3da2425a0743d492179f90a543a0d4cd72c980", + "0xb9cbf17e32c43d7863150d4811b974882da338cf0ed1313765b431b89457021dd1e421eeaa52840ef00551bb630962dc", + "0xa6fb875786daec1a91484481787093d8d691dd07e15c9c0c6ae0404bf9dc26083ed15d03c6d3fe03e29f28e20da21269", + "0xa870fcb54b9a029e8086de9b08da8782c64ad2cc2e7fdf955b913d294038bb8136193256b85267e75a4ca205808a76b4", + "0x99883f057e09b88bf0e316f9814c091837fd5c26eeb16fec108c9fed4b7a2bd1c783dac0e4242b5a906621ab606c1e50", + "0x85d89069ca3190577dab39bbec43c16bf6dbca439ad3eebd8f5e9f507d84c3c43e77fd6323224582566a3aa2c8018951", + "0x9363ba219e0003f6e8a9d8937b9e1449e4b2c5cd57194563b758bea39deab88778e8f8e4f7816970a617fb077e1e1d42", + "0x820622f25553c035326145c1d2d537dc9cfd064c2f5bdf6d4ec97814de5fe9a0fbd443345fa2ea0a9d40d81d3936aa56", + "0x87e31110aaf447e70c3316459250e4f7f8c24420c97828f9eb33b22107542c5535bdb48b0e58682dd842edea2886ff08", + "0x95bf80cac6f42029d843d1246588acb40a74802f9e94b2bf69b1833936767e701ef7b0e099e22ab9f20f8c0c4a794b6c", + "0xa46ecf612b2763d099b27fb814bd8fdbaee51d6b9ac277ad6f28350b843ce91d701371adfaaf4509400dc11628089b58", + "0x8604decf299fb17e073969708be5befeb1090ab688ad9f3f97a0847a40ea9a11bbcfc7a91e8dc27bc67a155123f3bd02", + "0x8eb765c8dc509061825f3688cb2d78b6fef90cf44db33783d256f09be284bc7282205279725b78882688a514247c4976", + "0xb5c30b2244fa109d66b3a5270b178960fdec47d31e63db0b374b80d2b626409eb76d2e8d1ebf47ef96c166743032fc5e", + "0xaab01e76290a7e936989530221646160bf8f64e61e79282e980c8c5dcaaa805ff096efd01d075a2c75917a3f4bf15041", + "0xb9d79671debd0b83d0c7c7c3e64c0fb1274300564b262771f839b49218501e7f38ef80cae1f7e5a3c34acdc74c89dab6", + "0x92c0eaceadf036b3b9dfd2712013aba3dd7c30b7760f501f52141618265baa31840fe77850a7014dc528f71f8cf39ce6", + "0xb3cdd098059980455dd5b1c04182df1bd12fa844a866f02a9f8a86aab95b59945baa9af99f687410bffc5b07153cb23c", + "0xb361b73a62f71256b7f6ea8e0f6615e14fc5a06ee98b928ab3c9dd3eef9d9d30070e9855c82b7facb639cacb3401e01f", + "0xb9c85fc0f25a3271cf28b1ca900078eaaa66cbab0a3e677606e898ac32781a2dfce4d9cbd07404599e2c3c02fa161c9d", + "0xac5b4fdac2a0b2e6430d9fc72bde4249d72183b197fc7347bb1546ae6f544426686bbe0caec3ee973b6836da5e831c44", + "0xb675aebf24b92e398e166f171a6df442b3f5919b6bee192f31675a5e8eeb77d34c6590a6f0c0857417e0f78cfb085db8", + "0xa9bef942044d8d62e6a40169f7dc7b49e40cd0d77f8678dd7c7bae6f46c46786f9b1e319a3fa408f22a54fd2a4d70804", + "0xa20d19cd917d5102ae9ca0cf532127d2b953aa3303310e8a8c4b3da025dded993a47e3a28e6b02acfadb6d65dc2d41a3", + "0xa47fdb04059b83b2afb86a47b2368bbd7247c337a36d3333b6e5ef2cc9476a92c4907e4c58a845c9ef9b497621e0b714", + "0x94a9e9ffc14b411e11a4ffa59878d59460263589003dc7b6915247c549f67feede279bf3645fdd92379022fb21e3caeb", + "0xb92e1177dd9ecdaf1370c71b14954219cf0851f309bc216d5907a4e2e84e0df3457018224150c142cc6bf86644bb4b73", + "0x8bc57fadd68a265b7df9b42227a9c0968db7b1bb50dc12f7d755505779f1ff2c408672b3091e903366acc9ce15d19fb6", + "0xb6b5efbe1ac4e1bd2e8447c45000d09397b772ca5496acc447b881022608a41c4f60388814607a01890190105bee7be3", + "0x95f7c85fd614df968f8ccf8d086579c9e1cec4644ecf06da26e3511cb39635a7326b3cec47bd51cf5646f1c660425e9c", + "0xb81765fb319bcdc74b4d608383ccb4af7dd84413b23af637be12e2827a75f7e4bcd14441cf979ed9038ae366fbb6f022", + "0xa120ea76cda8c6c50c97035078f6648afe6537809bdba26e7c9e61de8f3070d2347160f9d34010effbf2ec7e94f5749f", + "0x92c1b8631953b40d3cc77eee2c72a064b999c09a9b92c11d8fa7b4072966273901c9dba25f9f79f384d9f11a56f3fc7a", + "0xa4b00dc0ab67b2300abc9c516e34daf444d6497b066a90cfe3381ed2812304ed37b14f3b948990443dc6c1cf1bed460c", + "0xa9e9f7e13c9f031bc7b9e6f1417c7abcc38894fe7d3f54869ee277afd2efa3e6fb50757dd36c8c94d591e0abdea322cc", + "0x84f3e98f831792b5ad14bcfe62a4c9f296476c6087c4c1ec7767fc642fbca141ff6a3deeb8b4d4106a9cda5a9937eea0", + "0x8eb1a7931bbea9a714226fd74b0100ab88355287d9b0a349c095e9b5809b98f237ffd706bce7d67a770da355fb9cec7b", + "0x9738ef8739e1742c1f26b51a1621be0b89d37406a370c531e236f635c7064c661818817bb3858908986aa687b28b21be", + "0xa9cf3ce8501b003ccaf57552a4c4ec31081e44526d3aa3791d3dc4a7e438a357c0956f93c500356186d8fd4588ffac5e", + "0xa7af6a219cca59225839a9de5b19263cb23d75557d448bc7d677b62591a2e068c45e5f4457cceb3e9efa01d0601fc18a", + "0x972a24ece5eda7692cbb6fb727f92740451bc1281835e2a02931b2b05824a16b01dbe5edd03a0ed5b441ff25a5cc0188", + "0xb21d1ec7597ce95a42f759c9a8d79c8275d7e29047a22e08150f0f65014702f10b7edce8c03f6e7ab578ce8c3b0ec665", + "0xa13a1c7df341bd689e1f8116b7afc149c1ef39161e778aa7903e3df2569356ad31834fa58ceb191485585ce5ef6835c3", + "0xa57bdb08119dc3bc089b5b2b5383455c4de0c2fcdac2dcfa21c7ac5071a61635ff83eceb7412f53fab42d1a01991de32", + "0xb2968748fa4a6921ee752d97aa225d289f599a7db7a222450e69706533573ded450380c87f8cdd4a8b8c8db1b42b5c97", + "0x8718ec04e0d5f38e3034ecd2f13dfde840add500f43a5e13457a1c73db0d18138f938690c8c315b5bcbeb51e8b9a2781", + "0x82094789e26c4a04f2f30bdb97b9aecca9b756cbd28d22ab3c8bed8afc5b2963340ddfc5a5f505e679bf058cbc5dcbb8", + "0xa35b8a566dd6ab67eddc2467906bffc76c345d508e52e9e4bb407b4f2b2c5f39b31d5a4bf5022f87bf7181dc6be2fe41", + "0xa8c93b1e893d4777c0e3a1b4bef3be90c215781501407c4011457fc3240e13524b4d2bea64a6d0a3efe3f3b0dae9b8ab", + "0x877095ad18b1e5870818f7a606127ba1736a0b55b0dbcd281ec307c84b08afc0c9117e3a880fe48bfc225fbf37671a97", + "0x84405ee0421ed2db1add3593df8426a9c1fcc8063e875f5311a917febc193748678dd63171d0c21665fb68b6d786c378", + "0xa52cdc8209c3c310bed15a5db260c4f4d4857f19c10e4c4a4cfe9dfc324dfac851421bb801509cf8147f65068d21603c", + "0x8f8a028a70dda7285b664722387666274db92230b09b0672f1ead0d778cee79aae60688c3dfd3a8ed1efdeda5784c9d4", + "0xa0be42fecc86f245a45a8ed132d6efc4a0c4e404e1880d14601f5dce3f1c087d8480bad850d18b61629cf0d7b98e0ae0", + "0x83d157445fc45cb963b063f11085746e93ab40ece64648d3d05e33e686770c035022c14fdf3024b32b321abf498689ad", + "0x8a72bbf5a732e2d4f02e05f311027c509f228aef3561fc5edac3ef4f93313845d3a9f43c69f42e36f508efcc64a20be0", + "0xb9ca29b0ec8e41c6a02f54d8c16aebf377982488cbe2ed1753090f2db4f804f6269af03e015d647a82ef06ffaa8cba6c", + "0xb4df3858d61bbb5ded1cf0be22a79df65ae956e961fbb56c883e1881c4c21fe642e3f5a0c108a882e553ac59595e3241", + "0x86457d8890ac8858d7bab180ef66851247c2bf5e52bf69a4051d1d015252c389684fcc30bb4b664d42fbf670574ab3a3", + "0x86d5576ea6dfa06d9ebce4cd885450f270c88a283e1e0d29cab27851c14ed2f00355e167b52e1539f1218ad11d8f13dd", + "0x883ad1364dc2a92388bfafaa9bc943c55b2f813525831e817a6208c666829a40455dde494eba054b2495a95f7ce69e8a", + "0x8942371e6925231c2c603b5f5a882d8404d39f0c7c4232557c2610b21c2c07f145466da798ea78b7932da2b774aa3128", + "0xa799eb71496783cc7faf12c9d9804bf6180699a004b2f07fc5cc36840f63ce7eee7dde9275819a9aa3f8d92dc0d47557", + "0x8eb3fb5c769548ee38c7882f51b959c5d5a42b5935269ccf987d6ddbb25a206e80c6000bcc328af149e0727c0b7c02c0", + "0x8f3910d64e421a8f2d8db4c7b352ba5b3fc519d5663973fea5962efe4364fb74448770df944ef37ffe0382648fb56946", + "0xb41413e0c26ff124cf334dab0dc8e538293d8d519d11cc2d10895a96b2064ac60c7da39f08589b38726cffa4c3f0bfef", + "0xb46ef2eb10abae0f35fa4c9c7ee2665e8044b8d9f91988a241da40fd5bbc63166925582151941b400006e28bbc5ba22a", + "0xb8baa8b4c420bb572a3b6b85479b67d994c49a7ebfe1274687d946a0d0b36dfed7630cfb897350fa166f5e2eff8f9809", + "0x964b46d359c687e0dcfbdab0c2797fc2bd1042af79b7418795b43d32ffca4de89358cee97b9b30401392ff54c7834f9f", + "0x8410d0203d382ebf07f200fd02c89b80676957b31d561b76563e4412bebce42ca7cafe795039f46baf5e701171360a85", + "0xb1a8d5d473c1a912ed88ea5cfa37c2aea5c459967546d8f2f5177e04e0813b8d875b525a79c29cb3009c20e7e7292626", + "0xafaab9a1637429251d075e0ba883380043eaf668e001f16d36737028fded6faa6eeed6b5bb340f710961cee1f8801c41", + "0xaef17650003b5185d28d1e2306b2f304279da50925f2704a6a3a68312f29fe5c2f2939f14e08b0ba9dee06ea950ad001", + "0x97bcc442f370804aa4c48c2f8318d6f3452da8389af9335e187482d2e2b83b9382e5c297dce1a0f02935e227b74e09a3", + "0x8a67a27b199f0bcd02d52a3e32f9b76a486b830ec481a49a4e11807e98408b7052b48581b5dd9f0b3e93052ec45dfb68", + "0xb113bf15f430923c9805a5df2709082ab92dcdf686431bbad8c5888ca71cc749290fa4d4388a955c6d6ee3a3b9bc3c53", + "0x8629ca24440740ce86c212afed406026f4ea077e7aa369c4151b6fa57bca7f33f9d026900e5e6e681ae669fd2bd6c186", + "0x933a528371dcecc1ec6ded66b1c7b516bd691b3b8f127c13f948bfbcda3f2c774c7e4a8fbee72139c152064232103bdf", + "0x8568ddd01f81a4df34e5fa69c7f4bb8c3c04274147498156aec2e3bd98ea3e57c8a23503925de8fa3de4184563a2b79e", + "0x8160874ec030f30fda8f55bcf62613994ff7ed831e4901c7560eac647182b4a9b43bfaff74b916602b9d6ae3bfcaf929", + "0xae71c48d48cf9459800cdf9f8e96bc22e2d4e37259e5c92a2b24fbe2c6ca42675e312288603c81762f6ceb15400bc4c9", + "0xb05f39bb83fda73e0559db1fd4a71423938a87ad9f060d616d4f4a6c64bf99472a2cbfb95f88b9257c9630fc21a0b81f", + "0x80c8479a640ed7a39e67f2db5ad8dfd28979f5443e8e6c23da8087fc24134d4b9e7c94320ffa4154163270f621188c27", + "0x9969ba20ee29c64cb3285a3433a7e56a0fe4ddc6f3d93e147f49fe021bed4a9315266ebb2fb0eb3036bb02001ae015e6", + "0xa198c89fef2ab88e498703b9021becc940a80e32eb897563d65db57cc714eaa0e79092b09dd3a84cfab199250186edcc", + "0x8df14a3db8fe558a54d6120bad87405ba9415a92b08c498812c20416c291b09fed33d1e2fcf698eb14471f451e396089", + "0x81e245ef2649b8a5c8d4b27188dd7e985ef6639090bdc03462c081396cf7fc86ed7d01bfe7e649d2b399255e842bdc21", + "0x8659f622c7ab7b40061bcf7a10144b51ad3ab5348567195924f2944e8c4ce137a37f1ba328e4716c10806f3fb7271689", + "0xa575d610fc8fe09334ca619ecdadf02d468ca71dd158a5a913252ca55ea8d8f9ce4548937c239b9cb8ab752a4d5af24a", + "0x94744549cd9f29d99f4c8c663997bdfa90e975b31f1086214245de9c87b0c32209f515a0de64d72d5ef49c09b0a031fa", + "0x80a8677862b056df59e350c967a27436c671b65d58854e100115bac9824ba177e94c2a1bfcaa191a071b9cefdbee3989", + "0x91be9a5504ec99922440f92a43fe97ddce2f21b9d94cd3a94c085a89b70c903696cec203bbab6d0a70693ba4e558fb01", + "0x8c5a0087bcd370734d12d9b3ab7bc19e9a336d4b49fc42825b2bfedcd73bb85eb47bf8bb8552b9097cc0790e8134d08c", + "0x933aa9e6bd86df5d043e0577a48e17eea3352e23befdbb7d7dcac33b5703d5ace230443ac0a40e23bf95da4cc2313478", + "0x984b7ee4bd081ee06c484db6114c2ce0ba356988efb90f4c46ff85ed2865fb37f56a730166c29ef0ae3345a39cdeae7a", + "0xae830f908ea60276c6c949fb8813e2386cf8d1df26dcf8206aa8c849e4467243e074471380ed433465dc8925c138ea4c", + "0x874c1df98d45b510b4f22feff46a7e8ed22cfc3fad2ac4094b53b9e6477c8dfc604976ca3cee16c07906dece471aa6c6", + "0xa603eb60d4c0fb90fa000d2913689126849c0261e6a8649218270e22a994902965a4e7f8c9462447259495fe17296093", + "0xa7c73d759a8ad5e3a64c6d050740d444e8d6b6c9ade6fb31cb660fa93dc4a79091230baccb51c888da05c28cb26f6f3f", + "0xa4411b79b6a85c79ea173bd9c23d49d19e736475f3d7d53213c5349ebb94a266d510d12ba52b2ac7a62deaaaec7339b8", + "0x943b84f8bbcee53b06266b5c4cd24d649d972593837fe82b0bf5d5e1bbc1a2bf148e1426c366d7c39ab566b10224cadc", + "0x8300012096a8b4cefecc080054bf3ceb0918162ba263c6848860423407796b5eb517170c0bad8e4905ac69a383055a21", + "0x8244a1e3ad41908c6f037e2f8db052e81f281646141334829f36c707f307448b9ab79a7f382a1e8d86f877c90b59271c", + "0x8eca1b74687802ecc36a5d39e4516a9dee3de61a2047252d9ed737b49e0090c386e9d792ac004c96337681c7f29a16ad", + "0xb70fa47535f0524835039a20036c61e77f66146ad79d3d339214d8744742db41ceeb577c829d000011aeafbb12e09579", + "0x84b3abbce48689f3adbb99889c7fd1f3e15ab455d477e34f5151c5c1c358ed77a5b6a581879f7e0f1f34106e0792e547", + "0xab45ecb58c0ef0dbce3d16afc6ac281e0d90ec48741ea96a141152647e98fcc87f3a3ff07ba81f3179118453ce123156", + "0x90d231a145ba36a59087e259bbfc019fa369201fcfeaa4347d5fd0a22cd8a716e5a797f3cc357f2779edb08f3b666169", + "0xa4f6074d23c6c97e00130bc05f25213ca4fa76c69ca1ace9dece904a2bdd9d987661f5d55023b50028c444af47ff7a08", + "0x933af884939ad0241f3f1f8e8be65f91d77ac0fb234e1134d92713b7cfb927f1933f164aec39177daa13b39c1370fac8", + "0x80d1db6933ce72091332ae47dc691acb2a9038f1239327b26d08ea9d40aa8f2e44410bbda64f2842a398cbe8f74f770f", + "0xa7a08605be2241ccc00151b00b3196d9c0717c4150909a2e9cd05538781231762b6cc6994bebbd4cddae7164d048e7b2", + "0x96db0d839765a8fdbbac03430fa800519e11e06c9b402039e9ae8b6503840c7ecac44123df37e3d220ac03e77612f4e4", + "0x96d70f8e9acd5a3151a8a9100ad94f16c289a31d61df681c23b17f21749c9062622d0a90f6d12c52397b609c6e997f76", + "0x8cf8e22273f7459396ff674749ab7e24c94fe8ab36d45d8235e83be98d556f2b8668ba3a4ec1cb98fac3c0925335c295", + "0x97b7e796a822262abc1a1f5a54cb72a1ea12c6c5824ac34cd1310be02d858a3c3aa56a80f340439b60d100e59c25097d", + "0xa48208328b08769737aa1a30482563a4a052aea736539eceab148fa6653a80cb6a80542e8b453f1f92a33d0480c20961", + "0xb612184941413fd6c85ff6aa517b58303b9938958aa85a85911e53ed308778624d77eadb27ccf970573e25d3dfd83df7", + "0xb3717068011648c7d03bbd1e2fc9521a86d2c3ae69113d732c2468880a3b932ebec93596957026477b02842ed71a331b", + "0xa0ad363e1352dcf035b03830fef4e27d5fd6481d29d5e8c9d51e851e3862d63cdcbaf8e330d61c1b90886921dac2c6fd", + "0x8db409fdacfa4bfdaf01cc87c8e97b53ca3a6e3a526d794eaad1c2023f3df4b888f1bf19fee9a990fe6d5c7c3063f30c", + "0xb34d6975310ab15938b75ef15020a165fc849949065d32d912554b51ffa1d3f428a6d1a396cb9329367670391de33842", + "0x9117285e9e6762853fc074b8a92b3923864de2c88c13cea7bab574aaf8cdd324843455d2c3f83c00f91f27c7ecc5592a", + "0xb4b2e8f190ea0b60819894710c866bf8578dd1b231ae701d430797cc7ede6e216e8ca6a304f3af9484061563645bf2ab", + "0x8c493c6853ab135d96a464815dd06cad8b3e8b163849cdefc23d1f20211685753b3d3e147be43e61e92e35d35a0a0697", + "0x9864d7880f778c42d33cf102c425e380d999d55a975a29c2774cad920dfddb80087a446c4f32ed9a6ab5f22ec6f82af0", + "0x90f67fe26f11ca13e0c72b2c2798c0d0569ed6bc4ce5bbaf517c096e7296d5dd5685a25012f6c6d579af5b4f5d400b37", + "0xa228872348966f26e28a962af32e8fa7388d04bc07cfc0224a12be10757ac7ab16a3387c0b8318fcb0c67384b0e8c1a4", + "0xa9d9d64bba3c03b51acf70aeb746a2712ddafe3b3667ae3c25622df377c2b5504e7ab598263bec835ab972283c9a168b", + "0x932128971c9d333f32939a1b46c4f7cf7e9d8417bd08dc5bd4573ccbd6ec5b460ac8880fb7f142f7ef8a40eef76d0c6d", + "0x964115e7838f2f197d6f09c06fbb2301d6e27c0ecdf208350cf3b36c748436dac50f47f9f9ac651c09ab7ad7221c7e43", + "0xa5941f619e5f55a9cf6e7f1499b1f1bcddcc7cf5e274efedaaad73a75bc71b1fc5c29cd903f6c69dc9a366a6933ca9d1", + "0xa154bf5eaec096029e5fe7c8bf6c695ae51ace356bb1ad234747776c7e1b406dee2d58864c3f4af84ed69f310974125e", + "0xb504e6209d48b0338ab1e4bdab663bac343bb6e0433466b70e49dc4464c1ec05f4a98111fd4450393607510ae467c915", + "0x813411918ea79bdde295393284dc378b9bdc6cfcb34678b9733ea8c041ac9a32c1e7906e814887469f2c1e39287e80f8", + "0x8be0369f94e4d72c561e6edb891755368660208853988647c55a8eed60275f2dd6ee27db976de6ecf54ac5c66aaf0ae6", + "0xa7e2701e55b1e7ea9294994c8ad1c080db06a6fc8710cd0c9f804195dce2a97661c566089c80652f27b39018f774f85e", + "0x956b537703133b6ddf620d873eac67af058805a8cc4beb70f9c16c6787bf3cc9765e430d57a84a4c3c9fbdd11a007257", + "0x835ae5b3bb3ee5e52e048626e3ddaa49e28a65cb94b7ecdc2e272ff603b7058f1f90b4c75b4b9558f23851f1a5547a35", + "0x85d67c371d1bf6dc72cca7887fa7c886ce988b5d77dc176d767be3205e80f6af2204d6530f7060b1f65d360a0eaeff30", + "0xa84a6647a10fcef8353769ef5f55a701c53870054691a6e9d7e748cbe417b3b41dbb881bae67adc12cb6596c0d8be376", + "0x87ffe271fc0964cb225551c7a61008d8bcb8b3d3942970dbcc2b9f4f9045a767971880368ea254e2038a3a0b94ecf236", + "0x964bb721c51d43ee7dd67c1a2b7dd2cc672ce8fad78c22dcddb43e6aab48d9a4a7dc595d702aa54a6fb0ffabf01f2780", + "0xa89b3f84bb7dcbe3741749776f5b78a269f6b1bebb8e95d3cc80b834fd2177c6be058d16cacfd0d5e1e35e85cde8b811", + "0xb4314538e003a1587b5592ff07355ea03239f17e75c49d51f32babe8e048b90b046a73357bcb9ce382d3e8fbe2f8e68b", + "0x86daf4bf201ae5537b5d4f4d734ed2934b9cf74de30513e3280402078f1787871b6973aa60f75858bdf696f19935a0e2", + "0xb1adf5d4f83f089dc4f5dae9dbd215322fa98c964e2eaa409bf8ca3fa5c627880a014ed209492c3894b3df1c117236c4", + "0xb508d52382c5bac5749bc8c89f70c650bb2ed3ef9dc99619468c387c1b6c9ff530a906dfa393f78f34c4f2f31478508a", + "0xa8349a5865cb1f191bebb845dfbc25c747681d769dbffd40d8cedf9c9a62fa2cbc14b64bb6121120dab4e24bef8e6b37", + "0xaf0500d4af99c83db8890a25f0be1de267a382ec5e9835e2f3503e1bac9412acf9ff83a7b9385708ef8187a38a37bc77", + "0xb76d57a1c1f85b8a8e1722a47057b4c572800957a6b48882d1fc21309c2e45f648a8db0fcff760d1dbc7732cf37c009b", + "0xb93c996cec0d3714667b5a5a5f7c05a7dc00bbc9f95ac8e310626b9e41ae4cc5707fac3e5bd86e1e1f2f6d9627b0da94", + "0x93216fdb864217b4c761090a0921cf8d42649ab7c4da1e009ec5450432564cb5a06cb6e8678579202d3985bd9e941cef", + "0x8b8be41105186a339987ae3a5f075fbc91f34b9984d222dfed0f0f85d2f684b56a56ab5dc812a411570491743d6c8b18", + "0x959b72782a6b2469e77fe4d492674cc51db148119b0671bd5d1765715f49fa8a87e907646671161586e84979ef16d631", + "0x86b7fc72fb7e7904ea71d5e66ba0d5d898ace7850985c8cc4a1c4902c5bf94351d23ce62eed45e24321fb02adfa49fc8", + "0xa2f244e7c9aa272cb0d067d81d25e5a3045b80b5a520b49fd5996ece267a7f1bea42e53147bbf153d9af215ea605fc9e", + "0x81aa2efa5520eebc894ce909ba5ce3250f2d96baa5f4f186a0637a1eea0080dd3a96c2f9fadf92262c1c5566ddb79bab", + "0xb607dd110cfe510d087bcff9a18480ba2912662256d0ab7b1d8120b22db4ad036b2266f46152754664c4e08d0fc583f6", + "0x8f588d5f4837e41312744caac5eee9ddc3ad7085871041694f0b5813edf83dc13af7970f7c9b6d234a886e07fa676a04", + "0x924921b903207783b31016cbec4e6c99e70f5244e775755c90d03a8b769738be3ba61577aca70f706a9c2b80040c9485", + "0xae0a42a222f1a71cd0d3c69ffb2f04c13e1940cce8efabe032629f650be3ceed6abb79651dbb81cb39a33286eb517639", + "0xa07d7d76460f31f5f0e32e40a5ea908d9d2aebf111ac4fadee67ef6540b916733c35a777dcdc05f6417726ca1f2d57dd", + "0x88d7f8a31f8c99794291847d28745e5d0b5d3b9684ca4170b686ffbb5bb521a3ef6746c3c8db22e4250a0cdff7939d96", + "0x849573071fd98c020dc9a8622a9eff221cb9f889bde259e7127a8886b73bef7ad430b87750915658918dcfb6b7b4d8d3", + "0xb12d59f732fa47fad175d6263734da8db89230fd340a46ad1cdee51e577041a5c80bf24cd195593e637daf1a66ef5a98", + "0xabbcfb8a4a6d5e269ee1ac5e277df84416c73ca55ec88317f73608201af25af0cb65b943c54684a5651df3a26e3daca2", + "0xab157f589bdbaf067a6a7ba7513df0492933855d39f3a081196cf2352e0ddc0162d476c433320366e3df601e0556278d", + "0xa86c0619b92e5ae4f7daa876a2abc5ba189156afc2fa05eef464dfa342ba37fc670d0dc308ad3822fcb461ab001bac30", + "0xa3f292946476cfe8d5e544a5325439a00e0165a5f9bf3bb6a53f477baeac7697cc0377745536681aa116f326ce911390", + "0x8aecbbfd442a6a0f01c1c09db5d9d50213eb6f1ff6fab674cde3da06a4edff3ed317e804f78300c22ef70c336123e05d", + "0x834ed4b58211fcd647d7bf7c0a3ba9085184c5c856b085e8a0fcd5215c661ef43d36f3f0f6329a9f1370501b4e73b6e4", + "0xa114ea5ad2b402a0de6105e5730907f2f1e458d28ae35144cf49836e0ad21325fe3e755cfb67984ae0a32e65402aad1e", + "0xa005f12bed97d71cee288b59afe9affb4d256888727343944a99913980df2c963fe02f218e6ea992f88db693a4498066", + "0xa010f286ab06b966e3b91ff8f1bdbe2fe9ab41a27bc392d5787aa02a46e5080e58c62c7d907818caae9f6a8b8123e381", + "0x857bd6df2ddef04dbc7c4f923e0b1696d3016c8bfed07fdfa28a3a3bd62d89b0f9df49aae81cbb6883d5e7b4fadae280", + "0xb3927030da445bc4756ac7230a5d87412a4f7510581fb422212ce2e8cf49689aca7ba71678743af06d4de4914c5aa4a0", + "0xb86403182c98fcce558d995f86752af316b3b2d53ba32075f71c7da2596747b7284c34a1a87de604fcc71e7e117a8add", + "0x98dd19b5527733041689b2a4568edaf6aa0fe1a3dd800c290cda157b171e053648a5772c5d3d4c80e5a795bc49adf12e", + "0x88a3c227bb7c9bff383f9ad3f7762245939a718ab85ae6e5e13180b12bf724d42054d3852b421c1cd1b3670baddecb63", + "0xb3cfd9ad66b52bbe57b5fff0fad723434d23761409b92c4893124a574acc1e6b1e14b4ec507661551cbbe05e16db362e", + "0x923e1bb482cf421dd77801f9780f49c3672b88508a389b94015fd907888dc647ee9ea8ec8d97131d235d066daf1f42b7", + "0x8d5e16240f04f92aa948181d421006bdbc7b215648fb6554193224d00cf337ebbb958f7548cf01b4d828acffb9fbc452", + "0x8b2b8f18ad0559746f6cda3acca294a1467fb1a3bc6b6371bc3a61a3bfe59418934fa8706f78b56005d85d9cb7f90454", + "0xa9316e2a94d6e31426d2ae7312878ba6baaac40f43e2b8a2fa3ab5a774c6918551554b2dbb23dc82f70ba3e0f60b5b0d", + "0x9593116d92cf06b8cd6905a2ce569ee6e69a506c897911f43ae80fc66c4914da209fc9347962034eebbc6e3e0fe59517", + "0x887d89d2b2d3c82b30e8f0acf15f0335532bd598b1861755498610cb2dd41ff5376b2a0bb757cb477add0ce8cfe7a9fc", + "0xb514cfe17875ecb790ad055271cc240ea4bda39b6cfa6a212908849c0875cb10c3a07826550b24c4b94ea68c6bb9e614", + "0xa563d5187966d1257d2ed71d53c945308f709bcc98e3b13a2a07a1933dc17bcb34b30796bd68c156d91811fbd49da2cb", + "0xa7195ccc53b58e65d1088868aeeb9ee208103e8197ad4c317235bb2d0ad3dc56cb7d9a7186416e0b23c226078095d44c", + "0xa838e7a368e75b73b5c50fbfedde3481d82c977c3d5a95892ac1b1a3ea6234b3344ad9d9544b5a532ccdef166e861011", + "0x9468ed6942e6b117d76d12d3a36138f5e5fb46e3b87cf6bb830c9b67d73e8176a1511780f55570f52d8cdb51dcf38e8c", + "0x8d2fc1899bc3483a77298de0e033085b195caf0e91c8be209fd4f27b60029cbe1f9a801fbd0458b4a686609762108560", + "0x8f4e44f8ca752a56aa96f3602e9234ad905ad9582111daf96a8c4d6f203bf3948f7ce467c555360ad58376ee8effd2ba", + "0x8fb88640b656e8f1c7c966c729eb2ba5ccf780c49873f8b873c6971840db7d986bdf1332ba80f8a0bb4b4ee7401468fa", + "0xb72aa3235868186913fb5f1d324e748cd3ce1a17d3d6e6ea7639a5076430fe0b08841c95feb19bb94181fe59c483a9eb", + "0xb8b102690ebb94fc4148742e7e3fd00f807b745b02cbe92cd92992c9143b6db7bb23a70da64a8b2233e4a6e572fc2054", + "0x8c9ae291f6cd744e2c6afe0719a7fc3e18d79307f781921fb848a0bf222e233879c1eca8236b4b1be217f9440859b6ce", + "0xa658ede47e14b3aad789e07f5374402f60e9cacb56b1b57a7c6044ca2418b82c98874e5c8c461898ebd69e38fecd5770", + "0x89c0cb423580e333923eb66bda690f5aca6ec6cba2f92850e54afd882ba608465a7dbb5aa077cd0ca65d9d00909348ab", + "0xaed8e28d98d5508bd3818804cf20d296fe050b023db2ed32306f19a7a3f51c7aaafed9d0847a3d2cd5ba5b4dabbc5401", + "0x96a0fcd6235f87568d24fb57269a94402c23d4aa5602572ad361f3f915a5f01be4e6945d576d51be0d37c24b8b0f3d72", + "0x935d0c69edd5dfa8ed07c49661b3e725b50588f814eb38ea31bcc1d36b262fae40d038a90feff42329930f8310348a50", + "0x900518288aa8ea824c7042f76710f2ea358c8bb7657f518a6e13de9123be891fa847c61569035df64605a459dad2ecc8", + "0x947d743a570e84831b4fb5e786024bd752630429d0673bf12028eb4642beb452e133214aff1cfa578a8856c5ebcb1758", + "0xa787266f34d48c13a01b44e02f34a0369c36f7ec0aae3ec92d27a5f4a15b3f7be9b30b8d9dd1217d4eeedff5fd71b2e5", + "0xa24b797214707ccc9e7a7153e94521900c01a1acd7359d4c74b343bfa11ea2cdf96f149802f4669312cd58d5ab159c93", + "0x97f5ee9c743b6845f15c7f0951221468b40e1edaef06328653a0882793f91e8146c26ac76dd613038c5fdcf5448e2948", + "0x80abd843693aed1949b4ea93e0188e281334163a1de150c080e56ca1f655c53eb4e5d65a67bc3fc546ed4445a3c71d00", + "0x908e499eb3d44836808dacff2f6815f883aeced9460913cf8f2fbbb8fe8f5428c6fc9875f60b9996445a032fd514c70f", + "0xae1828ef674730066dc83da8d4dd5fa76fc6eb6fa2f9d91e3a6d03a9e61d7c3a74619f4483fe14cddf31941e5f65420a", + "0xa9f4dbe658cd213d77642e4d11385a8f432245b098fccd23587d7b168dbeebe1cca4f37ee8d1725adb0d60af85f8c12f", + "0x93e20ee8a314b7772b2439be9d15d0bf30cd612719b64aa2b4c3db48e6df46cea0a22db08ca65a36299a48d547e826a7", + "0xa8746a3e24b08dffa57ae78e53825a9ddbbe12af6e675269d48bff4720babdc24f907fde5f1880a6b31c5d5a51fbb00e", + "0xb5e94dfab3c2f5d3aea74a098546aa6a465aa1e3f5989377d0759d1899babf543ad688bb84811d3e891c8713c45886c5", + "0xa3929bada828bd0a72cda8417b0d057ecb2ddd8454086de235540a756e8032f2f47f52001eb1d7b1355339a128f0a53b", + "0xb684231711a1612866af1f0b7a9a185a3f8a9dac8bde75c101f3a1022947ceddc472beb95db9d9d42d9f6ccef315edbc", + "0xaf7809309edbb8eb61ef9e4b62f02a474c04c7c1ffa89543d8c6bf2e4c3d3e5ecbd39ec2fc1a4943a3949b8a09d315a6", + "0xb6f6e224247d9528ef0da4ad9700bee6e040bbf63e4d4c4b5989d0b29a0c17f7b003c60f74332fefa3c8ddbd83cd95c1", + "0xadbcec190a6ac2ddd7c59c6933e5b4e8507ce5fd4e230effc0bd0892fc00e6ac1369a2115f3398dfc074987b3b005c77", + "0x8a735b1bd7f2246d3fa1b729aecf2b1df8e8c3f86220a3a265c23444bdf540d9d6fe9b18ed8e6211fad2e1f25d23dd57", + "0x96b1bf31f46766738c0c687af3893d098d4b798237524cb2c867ed3671775651d5852da6803d0ea7356a6546aa9b33f2", + "0x8036e4c2b4576c9dcf98b810b5739051de4b5dde1e3e734a8e84ab52bc043e2e246a7f6046b07a9a95d8523ec5f7b851", + "0x8a4f4c32ee2203618af3bb603bf10245be0f57f1cfec71037d327fa11c1283b833819cb83b6b522252c39de3ce599fa5", + "0xad06ed0742c9838e3abaaffdb0ac0a64bad85b058b5be150e4d97d0346ed64fd6e761018d51d4498599669e25a6e3148", + "0x8d91cb427db262b6f912c693db3d0939b5df16bf7d2ab6a7e1bc47f5384371747db89c161b78ff9587259fdb3a49ad91", + "0xae0a3f84b5acb54729bcd7ef0fbfdcf9ed52da595636777897268d66db3de3f16a9cf237c9f8f6028412d37f73f2dfad", + "0x8f774109272dc387de0ca26f434e26bc5584754e71413e35fa4d517ee0f6e845b83d4f503f777fe31c9ec05796b3b4bc", + "0xa8670e0db2c537ad387cf8d75c6e42724fae0f16eca8b34018a59a6d539d3c0581e1066053a2ec8a5280ffabad2ca51f", + "0xac4929ed4ecad8124f2a2a482ec72e0ef86d6a4c64ac330dab25d61d1a71e1ee1009d196586ce46293355146086cabba", + "0x845d222cb018207976cc2975a9aa3543e46c861486136d57952494eb18029a1ebb0d08b6d7c67c0f37ee82a5c754f26f", + "0xb99fa4a29090eac44299f0e4b5a1582eb89b26ed2d4988b36338b9f073851d024b4201cd39a2b176d324f12903c38bee", + "0x9138823bc45640b8f77a6464c171af2fe1700bdc2b7b88f4d66b1370b3eafe12f5fbb7b528a7e1d55d9a70ca2f9fc8e6", + "0x8ac387dc4cf52bc48a240f2965ab2531ae3b518d4d1f99c0f520a3d6eb3d5123a35ef96bed8fa71ee2f46793fa5b33b3", + "0x864adec6339d4c2ba2525621fceabd4c455902f6f690f31a26e55413e0722e5711c509dc47ce0bcc27bbdc7651768d2d", + "0xa0a52edb72268a15201a968dabc26a22909620bda824bd548fb8c26cc848f704166ed730d958f0173bd3b0a672f367bd", + "0x949e445b0459983abd399571a1a7150aab3dd79f4b52a1cd5d733e436c71c1d4b74287c6b0ce6cc90c6711ba4c541586", + "0x858966355dac11369e3b6552f2b381665181693d5a32e596984da3314021710b25a37d8c548b08700eea13d86cb22f21", + "0x974bcbb8d38c5e6518745cc03ad436e585b61f31d705e7e2e5085da9655d768ac4d800904f892c3dab65d6223e3f1fd6", + "0x8092b6506b01308bf6187fde5ebd4fa7448c9a640961ba231be22ac5fa2c7635ef01e8b357722c7695d09b723101ea2a", + "0xa5b8ef360bf28533ee17d8cd131fff661d265f609db49599085c0c7d83b0af409a1b5c28e3a5e5d7f8459a368aa121e8", + "0xb031b6d5e3ceab0f0c93314b3b675f55cf18cbc86f70444af266fe39cb22fd7dad75d8c84e07f1c1bfa2cb8283e1361a", + "0x93ad489e4f74658320c1cceed0137c023d3001a2c930ed87e6a21dbf02f2eb6ad1c1d8bcb3739c85dcfbecb040928707", + "0xb15e4ec2cdab0d34aec8d6c50338812eb6ecd588cf123a3e9d22a7ca23b5a98662af18289f09e6cdd85a39a2863c945c", + "0xb304f71a9717cf40c22073f942618b44bf27cd5e2ed4a386ad45d75b0fcb5a8dafd35158211eaf639495c6f1a651cedb", + "0xb82d78d3eaaa7c5101b7a5aae02bd4f002cd5802d18c3abcda0dd53b036661c6d3c8b79e0abe591eab90b6fdc5fef5e3", + "0xabbd1884243a35578b80914a5084449c237ee4e4660c279d1073a4d4217d1b55c6b7e9c087dfd08d94ac1416273d8d07", + "0x92f4b61c62502745e3e198ec29bca2e18696c69dcb914d1f3a73f4998d012b90caf99df46e9bb59942e43cce377fe8fd", + "0x906e79df98185820c8208844e1ba6bd86cb96965814b01310bd62f22cbec9b5d379b2ef16772d6fc45a421b60cfd68fe", + "0xa0eae2784ef596e2eb270dd40c48d6c508e4394c7d6d08d4cc1b56fde42b604d10ba752b3a80f2c4a737e080ef51b44f", + "0x94c084985e276dc249b09029e49a4ef8a369cd1737b51c1772fbb458d61e3fe120d0f517976eba8ffa5711ba93e46976", + "0x83619a0157eff3f480ab91d1d6225fead74c96a6fd685333f1e8e4d746f6273e226bad14232f1d1168a274e889f202f1", + "0xa724fe6a83d05dbbf9bb3f626e96db2c10d6d5c650c0a909415fbda9b5711c8b26e377201fb9ce82e94fa2ab0bf99351", + "0xa8a10c1b91a3a1fa2d7fd1f78a141191987270b13004600601d0f1f357042891010717319489f681aa8a1da79f7f00d5", + "0xa398a2e95b944940b1f8a8e5d697c50e7aa03994a8a640dfad4ea65cfb199a4d97861a3ec62d1c7b2b8d6e26488ca909", + "0xa2eedfe5452513b2a938fffd560798ef81379c5a5032d5b0da7b3bb812addbaad51f564c15d9acbbfc59bb7eddd0b798", + "0xab31c572f6f145a53e13b962f11320a1f4d411739c86c88989f8f21ab629639905b3eedb0628067942b0dc1814b678ca", + "0xad032736dd0e25652d3566f6763b48b34ea1507922ed162890cd050b1125ec03b6d41d34fccba36ec90336f7cdf788ed", + "0x83028a558a5847293147c483b74173eca28578186137df220df747fccd7d769528d7277336ea03c5d9cdd0bc5ae3d666", + "0xab5d182cd1181de8e14d3ef615580217c165e470b7a094a276b78a3003089123db75c6e1650bf57d23e587c587cd7472", + "0xa4793e089fbdb1597654f43b4f7e02d843d4ab99ee54099c3d9f0bd5c0c5657c90bb076379a055b00c01b12843415251", + "0x98bdc52ee062035356fb2b5c3b41673198ddc60b2d1e546cb44e3bb36094ef3c9cf2e12bbc890feb7d9b15925439d1ea", + "0xa4f90cca6f48024a0341bd231797b03693b34e23d3e5b712eb24aba37a27827319b2c16188f97c0636a0c115381dc659", + "0x8888e6c2e4a574d04ba5f4264e77abc24ccc195f1a7e3194169b8a2ceded493740c52db4f9833b3dbf4d67a3c5b252cb", + "0x83dc4e302b8b0a76dc0292366520b7d246d73c6aebe1bdd16a02f645c082197bcff24a4369deda60336172cefbcf09af", + "0xa4eb2741699febfeb793914da3054337cc05c6fa00d740e5f97cb749ae16802c6256c9d4f0f7297dcdbb8b9f22fc0afa", + "0x8b65557d5be273d1cb992a25cfce40d460c3f288d5cb0a54bdef25cbd17cdea5c32ec966e493addf5a74fd8e95b23e63", + "0x97c6577e76c73837bcb398b947cb4d3323d511141e0ddd0b456f59fbb1e8f920a5c20d7827a24309145efddee786140f", + "0xabcc0849ffe2a6a72157de907907b0a52deece04cf8317bee6fe1d999444b96e461eac95b6afde3d4fe530344086a625", + "0x9385c0115cb826a49df1917556efa47b5b5e4022b6a0d2082053d498ec9681da904ecf375368bb4e385833116ea61414", + "0x8b868c1841f0cdc175c90a81e610b0652c181db06731f5c8e72f8fafa0191620742e61a00db8215a991d60567b6a81ca", + "0xa8df15406f31b8fcf81f8ff98c01f3df73bf9ec84544ddec396bdf7fafa6fe084b3237bf7ef08ad43b26517de8c3cd26", + "0xa9943d21e35464ce54d4cc8b135731265a5d82f9ccf66133effa460ffdb443cdb694a25320506923eede88d972241bf2", + "0xa1378ee107dd7a3abcf269fd828887c288363e9b9ca2711377f2e96d2ed5e7c5ec8d3f1da995a3dcbedf1752d9c088fc", + "0x8a230856f9227b834c75bdebc1a57c7298a8351874bf39805c3e0255d6fd0e846f7ad49709b65ec1fd1a309331a83935", + "0x877bcf42549d42610e1780e721f5800972b51ba3b45c95c12b34cb35eeaf7eac8fa752edd7b342411820cf9093fea003", + "0x84c7a0b63842e50905624f1d2662506b16d1f3ea201877dfc76c79181c338b498eceb7cad24c2142c08919120e62f915", + "0x8e18b1bd04b1d65f6ed349b5d33a26fe349219043ead0e350b50ae7a65d6ff5f985dd9d318d3b807d29faa1a7de4fe42", + "0x8ea7b5a7503e1f0b3c3cd01f8e50207044b0a9c50ed1697794048bbe8efd6659e65134d172fb22f95439e1644f662e23", + "0xb1954a2818cad1dad6d343a7b23afa9aa8ad4463edc4eb51e26e087c2010927535020d045d97d44086d76acdb5818cbf", + "0xa5271ea85d0d21fa1ff59b027cf88847c0f999bbf578599083ff789a9b5228bc161e1c81deb97e74db1a82a0afd61c50", + "0xaa2fa4c05af3387e2c799315781d1910f69977ec1cfea57a25f1a37c63c4daaa3f0ecd400884a1673e17dd5300853bcf", + "0xb1cd2a74ca0b8e6090da29787aef9b037b03b96607983a308b790133bd21297b21ca4e2edec890874096dbf54e9d04c3", + "0x801931607ec66a81272feaa984f0b949ad12d75ecf324ba96627bd4dc5ddead8ebf088f78e836b6587c2b6c0b3366b6c", + "0x95d79504710bdf0ad9b9c3da79068c30665818c2f0cdbba02cc0a5e46e29d596032ac984441b429bd62e34535c8d55b0", + "0x9857d41e25e67876510ff8dadf0162019590f902da1897da0ef6fc8556e3c98961edb1eb3a3a5c000f6c494413ded15e", + "0x8740c9ffe6bd179c19a400137c3bd3a593b85bd4c264e26b4dfb9e2e17ac73e5b52dfacc1dcb4033cfc0cd04785f4363", + "0x977f98f29d948b4097a4abdf9345f4c1fb0aa94ba0c6bf6faa13b76f3a3efc8f688e1fe96099b71b3e1c05041118c8d1", + "0xa364422b1239126e3e8d7b84953ce2181f9856319b0a29fcab81e17ac27d35798088859c1cfc9fc12b2dbbf54d4f70b3", + "0xa0f6ba637f0db7a48e07439bb92ddb20d590ce9e2ed5bab08d73aa22d82c32a9a370fe934cbe9c08aeb84b11adcf2e0e", + "0xa2c548641bd5b677c7748327cca598a98a03a031945276be6d5c4357b6d04f8f40dd1c942ee6ec8499d56a1290ac134d", + "0x9863e9cc5fbcdbd105a41d9778d7c402686bfd2d81d9ed107b4fda15e728871c38647529693306855bee33a00d257a7e", + "0xa54173bf47b976290c88fd41f99300135de222f1f76293757a438450880e6f13dbde3d5fe7afc687bdfbcfc4fbc1fc47", + "0xb8db413917c60907b73a997b5ab42939abd05552c56a13525e3253eb72b83f0d5cc52b695968a10005c2e2fe13290e61", + "0xa1f8388ef21697c94ba90b1a1c157f0dc138e502379e6fc5dc47890d284563e5db7716266e1b91927e5adf3cde4c0a72", + "0x9949013a59d890eb358eab12e623b2b5edb1acbee238dfad8b7253102abc6173922e188d5b89ec405aa377be8be5f16d", + "0xa00fdb7710db992041f6ddb3c00099e1ce311dea43c252c58f560c0d499983a89de67803a8e57baa01ee9d0ee6fa1e44", + "0xa8b1bcbed1951c9cdb974b61078412881b830b48cd6b384db0c00fa68bcc3f4312f8e56c892ea99d3511857ef79d3db9", + "0x8f3ee78404edc08af23b1a28c2012cee0bdf3599a6cb4ea689fc47df4a765ef519191819a72562b91a0fbcdb896a937e", + "0x8155bbb7fa8d386848b0a87caae4da3dec1f3dade95c750a64a8e3555166ccc8799f638bd80ed116c74e3a995541587a", + "0xabfe30adbc0a6f1fd95c630ed5dac891b85384fa9331e86b83217f29dff0bd7cad19d328485715a7e3df9a19069d4d2f", + "0x89d0783e496ee8dbb695764b87fb04cee14d4e96c4ba613a19736971c577d312079048142c12ce5b32b21e4d491d281b", + "0x856b8dbc9c5d8f56b6bb7d909f339ca6da9a8787bba91f09130a025ab6d29b64dbf728ba6ed26e160a23c1cdb9bc037b", + "0x8a30dd2ea24491141047a7dfe1a4af217661c693edf70b534d52ca547625c7397a0d721e568d5b8398595856e80e9730", + "0xae7e1412feb68c5721922ed9279fb05549b7ef6812a4fd33dbbbd7effab756ab74634f195d0c072143c9f1fd0e1ee483", + "0xb7ce970e06fa9832b82eef572f2902c263fda29fdce9676f575860aae20863046243558ede2c92343616be5184944844", + "0x85ed0531f0e5c1a5d0bfe819d1aa29d6d5ff7f64ad8a0555560f84b72dee78e66931a594c72e1c01b36a877d48e017ca", + "0xb8595be631dc5b7ea55b7eb8f2982c74544b1e5befc4984803b1c69727eac0079558182f109e755df3fd64bee00fcaa5", + "0x99e15a66e5b32468ef8813e106271df4f8ba43a57629162832835b8b89402eb32169f3d2c8de1eb40201ce10e346a025", + "0x844c6f5070a8c73fdfb3ed78d1eddca1be31192797ad53d47f98b10b74cc47a325d2bc07f6ee46f05e26cf46a6433efb", + "0x974059da7f13da3694ad33f95829eb1e95f3f3bfc35ef5ef0247547d3d8ee919926c3bd473ab8b877ff4faa07fcc8580", + "0xb6f025aecc5698f6243cc531782b760f946efebe0c79b9a09fe99de1da9986d94fa0057003d0f3631c39783e6d84c7d5", + "0xb0c5358bc9c6dfe181c5fdf853b16149536fbb70f82c3b00db8d854aefe4db26f87332c6117f017386af8b40288d08f9", + "0xa3106be5e52b63119040b167ff9874e2670bd059b924b9817c78199317deb5905ae7bff24a8ff170de54a02c34ff40a4", + "0xad846eb8953a41c37bcd80ad543955942a47953cbc8fb4d766eac5307892d34e17e5549dc14467724205255bc14e9b39", + "0xb16607e7f0f9d3636e659e907af4a086ad4731488f5703f0917c4ce71a696072a14a067db71a3d103530920e1ec50c16", + "0x8ed820e27116e60c412c608582e9bb262eaaf197197c9b7df6d62b21a28b26d49ea6c8bb77dfde821869d9b58025f939", + "0x97bc25201d98cde389dd5c0c223a6f844393b08f75d3b63326343073e467ac23aacef630ddc68545ea874299ba4a3b4f", + "0xb73c9695ad2eefd6cc989a251c433fab7d431f5e19f11d415a901762717d1004bb61e0cc4497af5a8abf2d567e59fef4", + "0xadaabe331eea932533a7cc0cf642e2a5e9d60bbc92dd2924d9b429571cbf0d62d32c207b346607a40643c6909b8727e2", + "0xa7b1bbfe2a5e9e8950c7cb4daab44a40c3ffab01dc012ed7fe445f4af47fa56d774a618fafe332ab99cac4dfb5cf4794", + "0xb4a3c454dcd5af850212e8b9ba5fe5c0d958d6b1cabbf6c6cfe3ccbc4d4c943309c18b047256867daf359006a23f3667", + "0xa5c0b32f6cef993834c1381ec57ad1b6f26ae7a8190dd26af0116e73dadc53bb0eeb1911419d609b79ce98b51fdc33bc", + "0xac2f52de3ecf4c437c06c91f35f7ac7d171121d0b16d294a317897918679f3b9db1cef3dd0f43adb6b89fe3030728415", + "0x94722ae6d328b1f8feaf6f0f78804e9b0219de85d6f14e8626c2845681841b2261d3e6a2c5b124086b7931bf89e26b46", + "0xa841a0602385d17afabca3a1bb6039167d75e5ec870fea60cfcaec4863039b4d745f1a008b40ec07bca4e42cb73f0d21", + "0x8c355f0a1886ffced584b4a002607e58ff3f130e9de827e36d38e57cb618c0cb0b2d2dea2966c461cb3a3887ede9aef1", + "0xa6a9817b0fc2fd1786f5ba1a7b3d8595310987fb8d62f50a752c6bb0b2a95b67d03a4adfd13e10aa6190a280b7ee9a67", + "0xa1d2e552581ecbafeaef08e389eaa0b600a139d446e7d0648ac5db8bbbf3c438d59497e3a2874fc692b4924b87ff2f83", + "0xa1b271c55389f25639fe043e831e2c33a8ba045e07683d1468c6edd81fedb91684e4869becfb164330451cfe699c31a8", + "0x8c263426e7f7e52f299d57d047a09b5eeb893644b86f4d149535a5046afd655a36d9e3fdb35f3201c2ccac2323a9582e", + "0xb41c242a7f7880c714241a97d56cce658ee6bcb795aec057a7b7c358d65f809eb901e0d51256826727dc0dc1d1887045", + "0x93001b9445813c82f692f94c0dc1e55298f609936b743cf7aae5ebfa86204f38833d3a73f7b67314be67c06a1de5682d", + "0x82087536dc5e78422ad631af6c64c8d44f981c195ddea07d5af9bb0e014cdc949c6fa6e42fce823e0087fdb329d50a34", + "0x8e071861ceba2737792741c031f57e0294c4892684506b7c4a0fc8b2f9a0a6b0a5635de3d1e8716c34df0194d789ae86", + "0xb471c997e1e11774bd053f15609d58838a74073a6c089a7a32c37dd3f933badf98c7e5833263f3e77bc0d156a62dd750", + "0x8d2d8686fb065b61714414bb6878fff3f9e1e303c8e02350fd79e2a7f0555ded05557628152c00166ce71c62c4d2feaa", + "0xae4c75274d21c02380730e91de2056c0262ffcecf0cbdb519f0bdb0b5a10ae2d4996b3dc4b3e16dbaea7f0c63d497fef", + "0x97140d819e8ca6330e589c6debdee77041c5a9cedb9b8cbd9c541a49207eeb7f6e6b1c7e736ec8ba6b3ab10f7fcd443a", + "0xaf6659f31f820291a160be452e64d1293aa68b5074b4c066dac169b8d01d0179139504df867dc56e2a6120354fc1f5be", + "0xa5e5d8088a368024617bfde6b731bf9eee35fc362bed3f5dfdd399e23a2495f97f17728fec99ca945b3282d1858aa338", + "0xa59cfc79d15dbdde51ab8e5129c97d3baba5a0a09272e6d2f3862370fdbaf90994e522e8bd99d6b14b3bb2e9e5545c6f", + "0xa30499b068083b28d6c7ddcc22f6b39b5ec84c8ee31c5630822c50ea736bb9dca41c265cffc6239f1c9ef2fd21476286", + "0x88ffe103eca84bbe7d1e39a1aa599a5c7c9d5533204d5c4e085402a51441bb8efb8971efe936efbbfa05e5cb0d4b8017", + "0xb202356fbf95a4d699154639e8cb03d02112c3e0128aab54d604645d8510a9ba98936028349b661672c3a4b36b9cb45d", + "0x8b89bb6574bf3524473cff1ff743abcf1406bd11fb0a72070ccd7d8fce9493b0069fb0c6655252a5164aee9e446ea772", + "0x93247b1038fa7e26667ee6446561d4882dc808d1015daafb705935ddc3598bb1433182c756465960480f7b2de391649e", + "0xb027f94d3358cbb8b6c8c227300293a0dee57bf2fee190a456ad82ecfb6c32f8090afa783e2ab16f8139805e1fb69534", + "0xa18bb1849b2f06c1d2214371031d41c76ffa803ee3aa60920d29dbf3db5fbfac2b7383d5d0080ba29ce25c7baa7c306b", + "0x827bf9fd647e238d5ac961c661e5bbf694b4c80b3af8079f94a2484cb8fba2c8cf60e472ebcd0b0024d98ae80ad2ff5a", + "0x838e891218c626a7f39b8fd546b013587408e8e366ecc636b54f97fa76f0a758bc1effa1d0f9b6b3bc1a7fcc505970a0", + "0x836523b5e8902d6e430c6a12cff01e417d2bd7b402e03904034e3b39755dee540d382778c1abe851d840d318ebedce7f", + "0x850a77dda9ac6c217e2ef00bf386a1adec18b7f462f52801c4f541215690502a77ef7519b690e22fdf54dc2109e0ca38", + "0xa8265c6ae7b29fc2bda6a2f99ced0c1945dd514b1c6ca19da84b5269514f48a4f7b2ccbab65c9107cfd5b30b26e5462f", + "0xab3d02ee1f1267e8d9d8f27cc388e218f3af728f1de811242b10e01de83471a1c8f623e282da5a284d77884d9b8cde0e", + "0x831edaf4397e22871ea5ddee1e7036bab9cc72f8d955c7d8a97f5e783f40532edbbb444d0520fefcffeab75677864644", + "0x80484487977e4877738744d67b9a35b6c96be579a9faa4a263e692295bb6e01f6e5a059181f3dd0278e2c3c24d10a451", + "0xaae65a18f28c8812617c11ecf30ad525421f31fb389b8b52d7892415e805a133f46d1feca89923f8f5b8234bd233486a", + "0xb3a36fd78979e94288b4cefed82f043a7e24a4a8025479cc7eb39591e34603048a41ee606ee03c0b5781ebe26a424399", + "0xb748b3fc0d1e12e876d626a1ba8ad6ad0c1f41ea89c3948e9f7d2666e90173eb9438027fadcd741d3ae0696bd13840f1", + "0xacdd252d7c216c470683a140a808e011c4d5f1b4e91aeb947f099c717b6a3bad6651142cde988330827eb7d19d5fb25c", + "0xb9a25556a6ca35db1ed59a1ec6f23343eab207a3146e4fc3324136e411c8dba77efd567938c63a39c2f1c676b07d8cdb", + "0xa8db6aef8f5680d2bdb415d7bcaae11de1458678dcb8c90c441d5986c44f83a9e5855662d0c1aace999172d8628d8fe1", + "0xaf58147108e9909c3a9710cc186eab598682dca4bfd22481e040b8c000593ecb22c4ede4253ac9504e964dfa95a9b150", + "0x8dd8bb70f1c9aec0fcc9478f24dfc9c3c36c0bf5ff7a67c017fa4dab2ec633fbd7bc9d8aa41ea63e2696971ed7e375f5", + "0xaa98d600b22aff993a4d7a3ccabd314e1825b200cb598f6b797d7e4d6a76d89e34a4d156c06bddfc62f2ef9b4c809d1d", + "0x8a8fc960d6c51294b8205d1dabe430bef59bda69824fa5c3c3105bef22ac77c36d2d0f38ffc95ce63731de5544ccbeff", + "0xb6d1020efe01dc8032bd1b35e622325d7b9af9dcd5c9c87c48d7d6ebc58644454294c59b7f4b209204b5b1f899f473bf", + "0x8a750dc9fe4891f2dfe5759fb985939810e4cdc0b4e243ff324b6143f87676d8cb4bcb9dfb01b550801cedcaaa5349e2", + "0x98c13142d3a9c5f8d452245c40c6dae4327dd958e0fda85255ea0f87e0bcbaa42a3a0bd50407ed2b23f9f6317a8a4bc5", + "0x99f2b83d9ec4fc46085a6d2a70fd0345df10f4a724c1ba4dee082a1fde9e642e3091992ebf5f90a731abcb6ec11f6d9b", + "0xb218546ab2db565b2489ea4205b79daa19ef2acbf772ccaaa5e40150e67ea466090d07198444b48e7109939aa2319148", + "0x84f9d1d868e4b55e535f1016558f1789df0daa0ead2d13153e02f715fe8049b1ce79f5bc1b0bbbb0b7e4dd3c04783f3f", + "0x80d870d212fbddfdda943e90d35a5a8aa0509a7a1e7f8909f2fcb09c51c3026be47cc7a22620a3063406872105b4f81a", + "0xb5b15138ff6551fac535d4bbce2ea6adc516b6b7734b4601c66ec029da2615e3119dc9ad6a937344acfd7b50e4a1a2ae", + "0x95d2f97652086e7ceb54e1d32692b1c867ffba23c4325740c7f10d369283d1b389e8afa0df967831ade55696931e7934", + "0x8a5b580403e1a99cd208f707e8ce0d3f658c8280417683f69008d09cc74d835a85f7380f391b36ead9ac66d9eedd1cbe", + "0xa8b0c90bff34c86720637b5a2081f0f144cfe2205c1176cacd87d348609bc67af68aed72414dc9aa6f44a82c92c2a890", + "0x865abbdd96c496892c165a8de0f9e73348bf24fce361d7a9048710178a3625881afb0006e9f5ee39124866b87904c904", + "0xace67bb994adef4b6f841cdf349195608030044562780a7e9b00b58a4ff117268a03ff01e5a3a9d9d7eff1dd01f5f4bf", + "0xb9371d59185b3d2d320d3fefeadb06ba2aa7d164352fb8dc37571509509fa214d736d244ac625a09a033a10d51611e2e", + "0xa8ef992771422dcf2d6d84386fde9fe5dba88bfded3dfcd14074ca04331b4fd53a7f316615cdfaf10ed932cbb424a153", + "0x868cbc75f8f789ea45eded2768a1dac0763347e0d8e8028d316a21005f17be179d26d5965903e51b037f2f57fe41765d", + "0xb607111bcdfd05fa144aa0281b13ee736079ebbbf384d938a60e5e3579639ed8ef8eb9ca184868cdb220a8e130d4a952", + "0xaca55702af5cae4cae65576769effd98858307a71b011841c563b97c2aa5aeb5c4f8645d254f631ed1582df3dbbf17da", + "0xb9b5cbace76246e80c20dfcc6f1e2c757a22ab53f7fd9ff8a1d309538b55174e55e557a13bf68f095ff6a4fa637ef21a", + "0x8571b0a96871f254e2397c9be495c76379faf347801cb946b94e63212d6a0da61c80e5d7bebbabcd6eaa7f1029172fe5", + "0x902540326281e6dc9c20d9c4deaaf6fbbbcc3d1869bd0cf7f081c0525bea33df5cfa24ead61430fda47fb964fcc7994b", + "0x841af09279d3536a666fa072278950fabf27c59fc15f79bd52acb078675f8087f657929c97b4bc761cbade0ecb955541", + "0xa1f958b147ddf80ab2c0746ba11685c4bae37eb25bfa0442e7e1078a00d5311d25499da30f6d168cb9302ea1f2e35091", + "0x863d939381db37d5a5866964be3392a70be460f0353af799d6b3ed6307176972686bd378f8ad457435a4094d27e8dfb7", + "0x835cd4d7f36eff553d17483eb6c041b14280beb82c7c69bca115929658455a1931212976c619bafb8179aed9940a8cc6", + "0x8d0770e3cb8225e39c454a1fc76954118491b59d97193c72c174ecc7613051e5aed48a534016a8cf0795c524f771a010", + "0x91aa4edb82f6f40db2b7bd4789cc08786f6996ebed3cb6f06248e4884bc949793f04a4c5ea6eefe77984b1cc2a45d699", + "0x8fb494ca2449f659ff4838833507a55500a016be9293e76598bbae0a7cb5687e4693757c2b6d76e62bd6c7f19ed080bb", + "0xb59b104449a880a282c1dd6a3d8debb1d8814ef35aab5673c1e500ee4cb0e840fb23e05fa5a0af92509c26b97f098f90", + "0xaca908e3bad65e854ae6be6c5db441a06bcd47f5abafdfa8f5a83c8cd3c6e08c33cab139c45887887a478338e19ceb9f", + "0x806f5d802040313a31964fc3eb0ee18ac91b348685bed93c13440984ee46f3d2da7194af18c63dea4196549129660a4e", + "0xae4b2dca75c28d8f23b3ab760b19d839f39ff5a3112e33cb44cff22492604a63c382b88ec67be4b0266924dd438c3183", + "0x99d1c29c6bd8bf384e79cd46e30b8f79f9cbc7d3bf980e9d6ffba048f0fc487cac45c364a8a44bb6027ad90721475482", + "0xa16e861c1af76d35528c25bf804bfc41c4e1e91b2927d07d8e96bffe3a781b4934e9d131ecf173be9399800b8269efac", + "0xa253303234fb74f5829060cdcef1d98652441ab6db7344b1e470d195a95722675988048d840201c3b98e794b1e8b037c", + "0x905ac8a0ea9ce0eb373fb0f83dd4cbe20afb45b9d21ae307846fd4757d4d891b26a6711924e081e2b8151e14a496da18", + "0xb485315791e775b9856cc5a820b10f1fa5028d5b92c2f0e003ba55134e1eddb3eb25f985f2611a2257acf3e7cfdfab5e", + "0xb6189c0458b9a043ebc500abc4d88083a3487b7ac47ed5e13ab2a41e0a1bee50d54a406063f92bc96959f19e822a89a7", + "0xa30e15f995fd099a223fc6dc30dad4b8d40bee00caa2bc3223ba6d53cd717c4968a3e90c4618c711ed37cc4cd4c56cf3", + "0xa1b1ed07fcc350bb12a09cd343768d208fc51a6b3486f0ece8f5a52f8a5810b4bc7ab75582ec0bc2770aed52f68eace5", + "0x88aa739fbae4bece147ba51a863e45d5f7203dbc3138975dc5aef1c32656feb35f014d626e0d5b3d8b1a2bda6f547509", + "0xab570f3c8eabfca325b3a2ea775ef6b0c6e6138c39d53c2310329e8fb162869fde22b0e55688de9eb63d65c37598fca3", + "0x89d274762c02158e27cb37052e296a78f2b643eb7f9ae409f8dac5c587d8b4d82be4ef7c79344a08ebec16ac4a895714", + "0x99c411d2ad531e64f06e604d44c71c7c384424498ecd0a567d31ec380727fb605af76643d0d5513dd0a8d018076dd087", + "0x80d0777fa9f79f4a0f0f937d6de277eec22b3507e2e398f44b16e11e40edf5feff55b3b07a69e95e7e3a1621add5ed58", + "0xb2430a460783f44feb6e4e342106571ef81ad36e3ddd908ec719febeb7acaf4b833de34998f83a1dab8f0137a3744c11", + "0xb8f38ccfc7279e1e30ad7cefc3ea146b0e2dff62430c50a5c72649a4f38f2bac2996124b03af2079d942b47b078cc4f8", + "0xa178a450a62f30ec2832ac13bbc48789549c64fc9d607b766f6d7998558a0e2fad007ae0148fc5747189b713f654e6ba", + "0x98c5ede296f3016f6597f7ccc5f82c88fd38ed6dc3d6da3e4a916bfd7c4c95928722a1d02534fe89387c201d70aa6fd2", + "0xa8cc5e98573705d396576e022b2ba2c3e7c7ece45cd8605cb534b511763682582299e91b4bb4100c967019d9f15bbfaf", + "0x848480ea7b7d9536e469da721236d932870b7bbee31ccf7ae31b4d98d91413f59b94a1e0d1786ee7342295aa3734969c", + "0xb88ea38f9ee432f49e09e4e013b19dff5a50b65453e17caf612155fff6622198f3cba43b2ea493a87e160935aaaf20a9", + "0x949376934a61e0ef8894339c8913b5f3b228fa0ae5c532ad99b8d783b9e4451e4588541f223d87273c0e96c0020d5372", + "0x96f90bb65ca6b476527d32c415814b9e09061648d34993f72f28fae7dc9c197e04ef979f804076d107bb218dfd9cb299", + "0xa4402da95d9942c8f26617e02a7cef0ebc4b757fac72f222a7958e554c82cc216444de93f659e4a1d643b3e55a95d526", + "0x81179cbc26a33f6d339b05ea3e1d6b9e1190bd44e94161ae36357b9cdf1e37d745d45c61735feed64371fe5384102366", + "0xad4dc22bdbd60e147fdac57d98166de37c727f090059cfc33e5ee6cf85e23c2643996b75cf1b37c63f3dc9d3c57ffa18", + "0x8a9b1b93dc56e078ce3bb61c2b0088fd6c3e303ba6b943231cc79d4a8e8572f4109bbde5f5aa7333aae3287909cb0fe2", + "0x8876ef583bc1513322457a4807d03381ba1f4d13e179260eaa3bddfede8df677b02b176c6c9f74c8e6eab0e5edee6de6", + "0xb6c67e228bf190fbaeb2b7ec34d4717ce710829c3e4964f56ebb7e64dc85058c30be08030fa87cc94f1734c5206aef5f", + "0xa00cb53b804ee9e85ce12c0103f12450d977bc54a41195819973c8a06dcb3f46f2bf83c3102db62c92c57ab4dd1e9218", + "0xa7675a64772eefddf8e94636fb7d1d28f277074327c02eea8fae88989de0c5f2dc1efed010f4992d57b5f59a0ab40d69", + "0x8d42bb915e0bf6a62bcdf2d9330eca9b64f9ec36c21ae14bf1d9b0805e5e0228b8a5872be61be8133ad06f11cb77c363", + "0xa5b134de0d76df71af3001f70e65c6d78bed571bc06bfddf40d0baad7ea2767608b1777b7ef4c836a8445949877eeb34", + "0xaeadbc771eaa5de3a353229d33ed8c66e85efbd498e5be467709cb7ff70d3f1a7640002568b0940e3abd7b2da81d2821", + "0x8c28da8e57a388007bd2620106f6226b011ee716a795c5d9f041c810edf9cf7345b2e2e7d06d8a6b6afa1ee01a5badc1", + "0x8ed070626a4d39ffd952ddb177bc68fd35b325312e7c11694c99b691f92a8ea7734aeb96cf9cc73e05b3c1b1dcad6978", + "0xada83e18e4842f3d8871881d5dbc81aed88a1328298bfdc9e28275094bd88d71b02e7b8501c380fa8d93096cbc62f4fb", + "0x8befc3bec82dcf000a94603b4a35c1950ba5d00d4bed12661e4237afa75062aa5dcef8eac0b9803136c76d2dd424a689", + "0x97c6f36c91ca5ca9230bfcbf109d813728b965a29b62e5f54c8e602d14a52ac38fa1270de8bfe1ab365426f3fc3654c7", + "0xb01d192af3d8dbce2fe2fece231449e70eb9ac194ec98e758da11ca53294a0fa8c29b1d23a5d9064b938b259ea3b4fb5", + "0x819a2c20646178f2f02865340db1c3c6ebc18f4e6559dd93aa604388796a34bd9fed28ad3ccc8afc57a5b60bb5c4e4ec", + "0xa9ffc877470afc169fecf9ec2dc33253b677371938b0c4ffa10f77bb80089afa2b4488437be90bb1bcf7586a6f4286e3", + "0xb533051c7ce7107176bcb34ad49fdb41fac32d145854d2fe0a561c200dcf242da484156177e2c8f411c3fdf1559ecf83", + "0x8fe2caff2e4241d353110a3618832f1443f7afe171fd14607009a4a0aa18509a4f1367b67913e1235ac19de15e732eb1", + "0x84705c6370619403b9f498059f9869fdf5f188d9d9231a0cb67b1da2e8c906ead51b934286497293698bba269c48aa59", + "0x899dddf312a37e3b10bdaaacc1789d71d710994b6ee2928ac982ad3fd8a4f6167672bc8bf3419412711c591afe801c28", + "0xb2f7916d946b903ded57b9d57025386143410a41a139b183b70aeca09cf43f5089ead1450fce4e6eb4fba2c8f5c5bbe5", + "0x8d5f742fe27a41623b5820914c5ca59f82246010fa974304204839880e5d0db8bc45ebab2ad19287f0de4ac6af25c09e", + "0xb93d4a1f6f73ac34da5ffbd2a4199cf1d51888bc930dc3e481b78806f454fcb700b4021af7525b108d49ebbbaa936309", + "0x8606f8d9121512e0217a70249937e5c7f35fbfe019f02248b035fa3a87d607bc23ae66d0443e26a4324f1f8e57fd6a25", + "0xb21312cdec9c2c30dd7e06e9d3151f3c1aceeb0c2f47cf9800cce41521b9d835cb501f98b410dc1d49a310fdda9bc250", + "0xa56420b64286bdddda1e212bba268e9d1ba6bdb7132484bf7f0b9e38099b94a540884079b07c501c519b0813c184f6b4", + "0x80b2cf0e010118cb2260f9c793cef136f8fa7b5e2711703735524e71d43bce2d296c093be41f2f59118cac71f1c5a2ff", + "0xadcb12d65163804d2f66b53f313f97152841c3625dbbda765e889b9937195c6fcd55d45cc48ebffabb56a5e5fe041611", + "0x8b8a42e50dc6b08ab2f69fc0f6d45e1ea3f11ba0c1008ee48448d79d1897356599e84f7f9d8a100329ed384d6787cfc4", + "0xaaa9c74afa2dec7eccfbd8bb0fc6f24ed04e74c9e2566c0755a00afdfdf3c4c7c59e2a037ec89c2f20af3fae1dd83b46", + "0xaa9f6e8fd59187171c6083ae433627d702eb78084f59010ff07aff8f821f7022ef5fbbe23d76814d811b720a8bfa6cc3", + "0xa56a3ded501659ad006d679af3287080b7ee8449e579406c2cae9706ef8bf19c1fc2eb2a6f9eaf2d3c7582cded73e477", + "0x81971e077c1da25845840222b4191e65f6d242b264af4e86800f80072d97d2a27a6adc87c3a1cb1b0dd63d233fbafa81", + "0xa6fa5453c4aaad2947969ee856616bf6448224f7c5bf578f440bcfc85a55beb40bef79df8096c4db59d1bd8ef33293ea", + "0x87c545adbfaaf71e0ab4bac9ae4e1419718f52b0060e8bb16b33db6d71b7248ae259d8dd4795b36a4bbb17f8fae9fd86", + "0xb4c7a9bc0910e905713291d549cec5309e2d6c9b5ea96954489b1dff2e490a6c8b1fa1e392232575f0a424ba94202f61", + "0x802350b761bcaba21b7afe82c8c6d36ee892b4524ab67e2161a91bbfa1d8e92e7e771efb1f22c14126218dd2cb583957", + "0xb4e7ddb9143d4d78ea8ea54f1c908879877d3c96ee8b5e1cb738949dcfceb3012a464506d8ae97aa99ea1de2abf34e3d", + "0xa49a214065c512ad5b7cc45154657a206ef3979aa753b352f8b334411f096d28fd42bca17e57d4baaafb014ac798fc10", + "0x8a80c70a06792678a97fe307520c0bf8ed3669f2617308752a2ab3c76fdf3726b014335a9b4c9cbcfc1df3b9e983c56f", + "0xa34721d9e2a0e4d08995a9d986dc9c266c766296d8d85e7b954651ad2ca07e55abb1b215898ee300da9b67114b036e0d", + "0x8cfce4564a526d7dca31e013e0531a9510b63845bbbd868d5783875ed45f92c1c369ce4a01d9d541f55f83c2c0a94f03", + "0xab3f5f03a5afc727778eb3edf70e4249061810eba06dc3b96b718e194c89429c5bfbec4b06f8bce8a2118a2fdce67b59", + "0xaa80c2529fc19d428342c894d4a30cb876169b1a2df81a723ab313a071cba28321de3511a4de7846207e916b395abcc9", + "0x82b7828249bf535ef24547d6618164b3f72691c17ca1268a5ee9052dba0db2fdd9987c8e083307a54399eab11b0f76b1", + "0x8fbcb56b687adad8655a6cf43364a18a434bf635e60512fad2c435cf046f914228fb314f7d8d24d7e5e774fb5ffb1735", + "0xa3010a61a2642f5ebbce7b4bc5d6ecb3df98722a49eb1655fe43c1d4b08f11dfad4bcec3e3f162d4cc7af6a504f4d47c", + "0xb3dcc0fdf531478e7c9ef53190aa5607fd053a7d2af6c24a15d74c279dbb47e3c803a1c6517d7e45d6534bb59e3527f5", + "0x8648f6316c898baaca534dff577c38e046b8dfa8f5a14ee7c7bc95d93ae42aa7794ba0f95688a13b554eeb58aeedf9ba", + "0x89fca6fc50407695e9315483b24f8b4e75936edf1475bcf609eed1c4370819abac0e6a7c3c44f669560367d805d9ba63", + "0xa367a17db374f34cd50f66fb31ba5b7de9dbe040f23db2dcc1d6811c0e863606f6c51850af203956f3399000f284d05f", + "0x91030f9ca0fff3e2dbd5947dcf2eba95eb3dbca92ee2df0ed83a1f73dbf274611af7daf1bb0c5c2ee46893ab87013771", + "0x84d56181f304ce94015ea575afeef1f84ea0c5dbb5d29fb41f25c7f26077b1a495aff74bd713b83bce48c62d7c36e42d", + "0x8fe2f84f178739c3e2a2f7dcac5351c52cbed5fa30255c29b9ae603ffd0c1a181da7fb5da40a4a39eec6ce971c328fcf", + "0xa6f9b77b2fdf0b9ee98cb6ff61073260b134eb7a428e14154b3aa34f57628e8980c03664c20f65becfe50d2bdd2751d4", + "0x8c6760865445b9327c34d2a1247583694fbeb876055a6a0a9e5cb460e35d0b2c419e7b14768f1cc388a6468c94fd0a0f", + "0xaf0350672488a96fe0089d633311ac308978a2b891b6dbb40a73882f1bda7381a1a24a03e115ead2937bf9dcd80572ad", + "0xa8e528ec2ee78389dd31d8280e07c3fdd84d49556a0969d9d5c134d9a55cd79e1d65463367b9512389f125ed956bc36a", + "0x942c66589b24f93e81fe3a3be3db0cd4d15a93fb75260b1f7419f58d66afaa57c8d2d8e6571536790e2b415eec348fd9", + "0x83fe4184b4b277d8bf65fb747b3c944170824b5832751057e43465526560f60da6e5bbee2f183cb20b896a20197168c7", + "0x88a71aada494e22c48db673d9e203eef7a4e551d25063b126017066c7c241ee82bedaa35741de4bd78a3dd8e21a8af44", + "0x8c642a3186ca264aac16ee5e27bd8da7e40e9c67ae159b5d32daa87b7de394bf2d7e80e7efb1a5506c53bfd6edd8c2c3", + "0x81855d6de9a59cef51bef12c72f07f1e0e8fe324fcc7ec3f850a532e96dcd434c247130610aaee413956f56b31cbb0dc", + "0xa01e61390dcd56a58ad2fcdb3275704ddfbedef3ba8b7c5fce4814a6cdd03d19d985dba6fd3383d4db089444ea9b9b4d", + "0x96494e89cbf3f9b69488a875434302000c2c49b5d07e5ff048a5b4a8147c98291ae222529b61bb66f1903b2e988e5425", + "0xb9689b3e8dddc6ec9d5c42ba9877f02c1779b2c912bba5183778dc2f022b49aed21c61c8ec7e3c02d74fe3f020a15986", + "0xa2a85e213b80b0511395da318cbb9935c87b82c305f717a264155a28a2ea204e9e726bae04ce6f012e331bd6730cbb9d", + "0x91b70f44c7d8c5980ce77e9033a34b05781cbe773854d3f49d2905cc711a3d87c20d5d496801ad6fd82438874ce732b8", + "0x884596417ff741bb4d11925d73852ffeea7161c7f232be3bdce9e6bbe7884c3a784f8f1807356ae49d336b7b53a2b495", + "0xae2aed8ab6951d8d768789f5bc5d638838d290d33ccc152edfb123e88ba04c6272b44294b0c460880451ad7b3868cc6a", + "0x89d8ebfb9beebc77189d27de31c55f823da87798a50bca21622cbf871e5d9f1d3182cf32ee9b90f157e6ce298e9efccf", + "0xafd00a4db4c2ed93cf047378c9402914b6b3255779f3bb47ded4ab206acb7eaebba0fd7762928e681b1aebcfee994adc", + "0xa2e49b6cd32e95d141ebc29f8c0b398bb5e1a04945f09e7e30a4062142111cd7aa712ac0e3e6394cfb73dd854f41ad77", + "0xae8e714ab6e01812a4de5828d84060f626358bb2b955f6fb99ae887b0d5ce4f67ebc079ab9e27d189bf1d3f24f7c2014", + "0xa3100c1eebf46d604e75ebf78569c25acf938d112b29ccbe1a91582f6bd8ef5548ae3961c808d3fb73936ac244e28dbc", + "0xa9a02dcff0e93d47ead9cdddc4759971c2d848580bf50e117eb100cafca6afeaa7b87208513d5f96b1e1440ffc1b0212", + "0x894ab01462137e1b0db7b84920a3b677fbb46c52b6f4c15320ef64f985e0fc05cec84cd48f389ce039779d5376966ea3", + "0xb1e40e8399ee793e5f501c9c43bde23538e3ce473c20a9f914f4a64f5b565748d13ab2406efe40a048965ee4476113e4", + "0xa5a7d97a19e636238968670a916d007bf2ce6ae8e352345d274101d0bbe3ac9b898f5b85814a7e4c433dd22ac2e000ff", + "0xb6394c43b82923231d93fd0aa8124b757163ba62df369898b9481f0118cb85375d0caac979a198ece432dbb4eb7cc357", + "0x82d522ae3ff4fe2c607b34b42af6f39c0cf96fcfe1f5b1812fca21c8d20cece78376da86dcbd6cdb140e23c93ae0bcb2", + "0xb6e0d986383bc4955508d35af92f2993e7e89db745f4525948c5274cfd500880cb5a9d58a5b13d96f6368bb266a4433e", + "0xb0b4325772ec156571d740c404e1add233fb693579f653b0fae0042b03157d3b904838f05c321d2d30f2dbd27c4d08ad", + "0xac41367250263a2099006ef80c30bac1d2f25731d4874be623b6e315c45b0dc9a65f530fce82fb3dc25bd0610008c760", + "0xb6c0b1ed7df53da04a6f3e796d3bfa186f9551c523bc67898bc0ecfc6b4a4a22f8c4d3bfc740ebf7b9fa5b0ea9431808", + "0x8e78fca17346601219d01e5cd6a4837161a7c8f86fe2a8d93574d8006da5f06ae7c48eea7d2b70992c2a69184619663c", + "0xa21f91f47e04fafbfafacf3185b6863766a2d0c324ccac2c3853a4748af5897dbbe31d91473b480f646121339c9bae2d", + "0xa464d68786ab1fc64bd8734fce0be6fbe8dc021d3e771ff492ada76eedff466577c25e282b7c8ab4c1fd95ef5ff3631e", + "0x829a24badc7714081e03509ccfb00818ce40430682c1c0e4a399cd10b690bda1f921aabcbf1edfb1d8a2e98e6c0cedd6", + "0x87ccf7e4bbcb818ef525435e7a7f039ecbb9c6670b0af163173da38cbdb07f18bc0b40b7e0c771a74e5a4bc8f12dfe2c", + "0x94087bd2af9dbeb449eb7f014cfbf3ee4348c0f47cde7dc0ad401a3c18481a8a33b89322227dee0822244965ae5a2abb", + "0x896b83ed78724dac8a3d5a75a99de8e056a083690152c303326aa833618b93ef9ec19ab8c6ef0efe9da2dbcccac54431", + "0x821e6a0d7ccf3c7bd6a6cc67cde6c5b92fb96542cb6b4e65a44bbc90bbc40c51ff9e04702cb69dd2452f39a2ff562898", + "0xb35b2096cda729090663a49cb09656c019fef1fc69a88496028d3a258ad2b3fd6d91ab832163eaa0077989f647e85e7e", + "0xb7857ef62c56d8bce62476cdb2ab965eddff24d932e20fc992bd820598686defe6cc0a7232d2be342696c2990d80721a", + "0xb343d974dfda3f6589043acd25d53aecf7c34b1e980ae135a55cda554ff55e531bc7c2dfe89b0d2c30e523c7b065dad1", + "0x8d139e16a73cd892b75f3f4e445a10d55d1118f8eeafc75b259d098338419e72e950df6ca49cb45677a3c4e16fb19cdc", + "0x817b8535bd759da392b2c5760c51b3952ecf663662a137c997f595c533cd561ed7e655673c11144242160e41d1f2dd71", + "0x817ee0f0819b0ccb794df17982d5b4332abff5fec5e23b69579db2767855642156d9b9acccf6ceab43332ccc8d2744dc", + "0x9835d2b652aec9b0eba0c8e3b6169567e257a6a3f274ec705dbc250ee63f0f8e4b342e47b9e0c280c778208483d47af8", + "0xb78c40177f54f0e6d03083a4f50d8e56b5aafdb90f1b047bb504777d6e27be5a58170330aee12fbaa5f1e9d4f944acfc", + "0xab8eebacf3806fac7ab951f6a9f3695545e2e3b839ca399a4ef360a73e77f089bb53d3d31dbd84ddfde55e5f013626e0", + "0x96c411fc6aecca39d07d2aff44d94b40814d8cfc4ee5a192fd23b54589b2801694d820a0dd217e44863ccff31dda891b", + "0x8249c424a0caf87d4f7ff255950bbc64064d4d1b093324bfe99583e8457c1f50e6996e3517bf281aa9b252c2a7c5a83a", + "0xacf6ed86121821a3dd63f3875b185c5ebe024bdb37878c8a8d558943d36db0616545a60db90789c0925295f45d021225", + "0xa37f155621a789f774dd13e57016b8e91b3a2512b5c75377ec8871b22a66db99655d101f57acaecd93115297caabfc21", + "0x92e60ee245bd4d349f1c656e034b1a7f0c6415a39ac4c54d383112734305488b3b90b0145024255735e0a32f38dba656", + "0xacec614e562ccfc93366309cfdc78c7d7ee0a23e3a7782a4fc4807b8803e6ebfb894a489d03e9a3c817ff2ec14813eba", + "0xb912f9dd26ed552cb14b007b893e6ed2494d12517e5761dbeb88521270144f8c3eb9571a0ad444b30a8a65e80bd95996", + "0x8375408dae79c547a29e9a9e5d4ec8241b36b82e45e4ca3b0c36d2227c02d17bb171528d3778eac3bbdc75d6c4e8a367", + "0x8c2d0e6e4406836da112edbbb63996408bb3cda4a2712fd245e4bb29a0100fdc89a2746d859b84a94565bc1cfa681813", + "0xa7431bf59e111c072d28c97626cd54fcdf018421d053a787d2aef454b91251ee8ff9d3702d06b088f92b9ad2bbebff15", + "0x8f3659b0fbeb90b7f30b7a49233325e806551a32911a654dca86e290b314483bbb33fe6482387bc48c35d85c1dd0441c", + "0x8dca5ba23f0bb76f7dacabf12886053552ba829a72827b472a2f01e19a893155cdce65f1fb670000f43e8c75ba015a31", + "0x8c1514c083c77624eeb5d995d60994a2866192e15c4474d0be4189fae0e9dbd62494ebb4c02fbc176b53be548abbc5a1", + "0x80498d2ed153381baf3b0f81da839ed0eea6af5796c422b8e59be805dba48c4395bb97824ac308170bb4f14f319c5ddf", + "0x84f5ebc3bf96362457993e9fa31493c31c4283075e2403f63d581b6b0db8a3df294b2085643f2007f4de38cb5d627776", + "0x958e6e38774da518193a98397978dbc73d1c3827b4996ec00b4183da2c305a187a0ada9aa306242814b229a395be83c9", + "0xab8b8fbf73845615e7fab3e09e96cc181159eab09f36b4c1239b3c03313c9aeb4bbb51e16316fe338b2319ed2571b810", + "0x977e4e33b33bd53394e591eba4f9a183e13704c61e467d74b28f4ad0b69aa51501a5221cb1e0e42bcb548ca518caa619", + "0xa9bb7ecb9846cc30d04aad56d253c3df7004cebb272f6adf7b40a84adef9f57291e0d08d64c961b9fc406cdb198aab9b", + "0x8d2b72dc36406a545a9da44e1fddfb953d4894710ca026d6421a4ac91e02d0373a599f2acfe41d8258bc9679cf6f43d3", + "0x904192fc8fe250f61ecb8a36abbbccae85f592bbf00c10039c30b5a1c733d752a04e4fd8a1000c6578616f8a16aa83a3", + "0x87f5fdfe20bbbf931b529ec9be77bbfcc398cad9d932d29f62c846e08a91d2f47ae56ad5345122d62a56f629f9a76c4d", + "0x84cc3a53b2e7b7e03015f796b6cb7c32d6ded95c5b49c233ac27fafa792994b43c93cda6e618b66fce381f3db69838ba", + "0xaab58da10d7bbe091788988d43d66a335644f3d0897bbc98df27dcc0c0fcee0ac72e24f1abdd77e25196a1d0d0728e98", + "0xa10ea8677c2b7da563d84aa91a314a54cab27bb417c257826ebdd3b045d2a0f12729fe630bbbf785d04874f99f26bee8", + "0xacc4970ef2a4435937a9b8a5a5a311226ca188d8f26af1adfcd6efb2376a59155b9a9ff1cff591bde4b684887d5da6e5", + "0x8dc7cf6fcca483c44eb55e7fb924bf3f76cf79b411ae4b01c6c968910877ac9c166b71350f4d935f19bdffb056477961", + "0xac2dd1182ded2054c2f4dbf27b71a0b517fb57193733a4e4e56aca8a069cff5078ffd3fd033683d076c1c639a4de63c7", + "0x932ec87c450cd0dc678daf8c63cd1bf46124fa472934e517fbbfb78199f288ff7f354b36e0cc6c8739d3f496cfe0913b", + "0xb0d631ced213e8492be60ea334dbe3b7799b86d85d5e8e70d02beef3ae87b1d76e1df3bdb5f7ba8a41904c96f6a64455", + "0x929d7239ead7575867e26b536b8badf2e11ca37840034d0e5c77039f8cce122eff5a1bf6e0bcadde6b3858e9f483d475", + "0xaaae5d372d02ee25b14de585af6fbc48f2c7cd2a6af4f08352951b45aa469599eff41e820df642ca1a0f881120e89dbe", + "0xb23c411741a6b059f04fa4f5fd9dd10e2a64915f2de6ea31e39c32f2f347a776a953320e5f7613fcb1167efe502f5c5c", + "0xa4581b0ae633fe29c6f09928e5efb16db019eeac57f79fef2fa1d3c9bee42ce0e852bc60b9d0133265373747e52a67a4", + "0x81b33afffd7b2575d4a9a1c5dd6eee675c084f82e06b9b3a52a3c9f76e087f12dca6e0ffddc42fb81ce1adb559d47a38", + "0x89cc890f06b424591556aabdfdbb36d7a23700425e90c9cfed7d3da226b4debe414ac5bdf175273828ce6c5355712514", + "0xa4399438be75cfae2bf825496704da5ed9001bed8538d8ac346c8cf0d4407808e9ee67573eb95fe1c6872ac21f639aaa", + "0xad537f7ce74a1ca9a46fc06f15c1c8a6c32363bd6ac78a3c579ed8f84252e38a914cac16709fe65360e822ef47896de4", + "0x8e53b69f5e3e86b86299452e20ea8068b49565d0d0ab5d50ce00158a18403ae44e1b078a3cfd3f919aa81eb049a30c6e", + "0xa59f2542c67a430fd3526215c60c02353ee18af2ff87cb6231a2564fe59b8efec421f18d8b8cc7f084675ecf57b3fd05", + "0xb8d9bac93ef56cb4026dd1c731d92260a608fd55b8321e39166678e1dab834d0efddb717685da87786caeb1aaf258089", + "0xaa2df56f4c6fe9e0f899116c37302675f796a1608338700f05a13e779eb7cf278e01947864a8c2c74cc9d9a763804446", + "0xb0108ff2e327dcb6982961232bf7a9a0356d4297902f4b38d380ff1b954bfbcae0093df0f133dd9e84d5966c7b1aada7", + "0xb06b813b01fe7f8cf05b79dc95006f0c01d73101583d456278d71cd78638df2b1115897072b20947943fa263ddab0cd6", + "0xaa41e6c4d50da8abf0ea3c3901412fe9c9dff885383e2c0c0c50ed2f770ada888a27ea08bbb5342b5ff402e7b1230f12", + "0xa48635dbb7debac10cb93d422c2910e5358ba0c584b73f9845028af4a763fd20da8f928b54b27782b27ca47e631ebf38", + "0x80a574c208e994799e4fa9ef895163f33153bc6487491d817c4049e376054c641c4717bda8efbeb09152fa421a7268a7", + "0xb592bfd78ae228afc219c186589b9b0b5c571e314976d1ed5c1642db9159d577679a73c049cfc3dcfefcd5a4f174eeea", + "0xaa1f08af3918c61eadf567a5b1a3cdcdfb1b925f23f1f9e3c47889762f4d979d64686ce1ce990055ef8c1030d98daa3b", + "0x857df4cfd56d41c6d0c7fcc1c657e83c888253bae58d33b86e0803a37461be5a57140a77fb4b61108d1d8565091ada1c", + "0x8fae66a72361df509d253012a94160d84d0b2260822c788927d32fd3c89500500908c8f850ef70df68ddaeb077fd0820", + "0xaa1dbefc9aef1e7b896ff7303837053c63cfb5c8a3d8204680d3228ac16c23636748fe59286468c99699ae668e769a0c", + "0xb64b1cb2ba28665ed10bad1dddc42f3f97383c39bad463c6615b527302e2aaf93eb6062946d2150bd41c329697d101be", + "0xb6d35e3b524186e9065cee73ea17c082feff1811b5ab5519dd7991cdff2f397e3a79655969755309bd08c7d5a66f5d78", + "0xa4dae7f584270743bbba8bb633bdb8bc4dcc43580e53d3e9e509ff6c327e384f14104b5bdfe5c662dc6568806950da37", + "0xaae84d3d9ad4e237b07c199813a42ed2af3bf641339c342d9abf7ebec29b5bd06249c4488ce5c9277d87f7b71b3ddd37", + "0xb82a463cf643821618a058bddf9f2acb34ac86a8de42a0fa18c9626e51c20351d27a9575398a31227e21e291b0da183e", + "0x8b6c921e8707aded3ea693f490322971b1a7f64786ef071bc9826c73a06bd8ae6bf21bc980425769627b529d30b253ce", + "0x80724937b27fc50f033c11c50835c632369f0905f413b1713a2b0a2274bec5d7a30438e94193d479ba6679dbe09a65ef", + "0xa1d9b259a2ca9cff8af6678b3af0a290c2f51e9cf26d5fe3c6a4fa3d28cbf33cb709b7f78b4f61cb9419427983c61925", + "0x96a3e69a5ed7a98ce59c4481f2ffb75be9542122ad0eb4952c84d4536760df217854d4ec561ce2f4a79d3793c22fa4f4", + "0x990c4d9a4a22d63a8976d34833cafc35936b165f04aed3504e9b435f0de1be4c83b097bbaa062483cf3dee3833b4f5b6", + "0xb9bf5e4b270aec4a0dc219457b5fed984b548892c4b700482525ba1a7df19284464f841dab94abfabcaa9a7b7a757484", + "0xacaecf49cb4786d17cf867d7a93bd4ffee0781766e11b5c1b29089ae0024c859d11b45828fbff5330b888543264d74a9", + "0xb0e1a0865b1e6f9e4a0e31d0c885526ac06678acc526fda5124742a2c303bd0e8871a0cb7951ec8ed9540fc247c8d844", + "0x82b3d327b3d1a631758451e12870816956cd0cef91fcf313a90dd533d5291193a0ff3cc447054564ce68c9b027a7ffd7", + "0xa2843602abb98f0f83e000f3415039788da1e9a096bfe8fed6b99bab96df948c814560424ffebe755cb72f40436fb590", + "0xab1c7b43cf838798d1e314bc26e04fc021e99a7bfbfc8ffde62fa8d7f92139de86a377289d5177021154229de01ede15", + "0x95e5cf5dd87ae3aed41b03c6c55f9dfad38dc126b17e7e587c156f7745c8da0bd1d60acb718fc1a03b61344f01e3de4d", + "0x86f021a3762bb47167f80d4ef1b1c873a91fe83409f9704f192efeebbc3ece0729cd2f92f63419907ea38ae47bc907d2", + "0xaaa1445dafbbcd645d4332d9806225e9346ee5ac6b22ad45e8922134fe12f3d433f567a6a4c19efdd9d5775a7de1e92f", + "0x8fd7e15688eef75df7b8bca3d61bc9fca4f56e047cdb6d0b864e7d1c4966eac27d6094b0c8482b49739f83ec51050198", + "0x80aab8b4d394eb011d4ec6a4c2815617308c9b847c6fa6a3d7e6af1c79420ef6ff2a13934a398581c40ee4cf1cac02ac", + "0x8970b97ac076a1d8a321ce00eada0edf974a46bf3cc26f6854e4218cdfc8d2b0c32199d9658f254b4fbae5a2c5535f41", + "0xa1aa2ec5b03df0a630e73dd048680ed6d3032c324941423f45cd1f16038789e5e75b876a13948732e9079a422f66a9fc", + "0xb5fe5f5e2f2ae2beeb8e95859a02fc45f01f9fb0ebb2bd8ec9ec976b3e806228821a9775096d341d662bc536c4d89452", + "0xa2bc1f170b62d0d5788b02391337b2ab157c38e725694e80aeead7383e05599be0e2f0fa27ef05db007061809356e147", + "0xa8a69701d4a8d0d972390e9f831fd8e9f424b2c2ef069e56bd763e9e835b3ce5f7cf5de5e5c297c06ace4aa74df1067c", + "0xb43d551af4ff3873557efe3f3fb98e5ede9008492f181f4796dd1a6bcda8b9445c155e8146966baa812afae1abe06b48", + "0xb4b1dae44fd596813f30602ab20e9b1fb20cb1bd650daacc97b7e054e5c0178b8131d439a9e5b142ca483cc012a362b3", + "0xb95b8a94c30a831eaaebea98c65cc5d0228c78afd6603d4aa426d8186aecc951f1a11c33951f51df04c7e6fa43ffb5ae", + "0xb100059624cf9db371bec80013a57a8f296d006c139a8766308f1ea821c7eccc26cad65bc640ab3f6cef9062653bf17d", + "0x8e5a2cb76716e0000d13bce5ef87acac307362a6096f090f5f64e5c5c71a10fddfdee8435e7166ba8c3ad8c3f540f3e4", + "0x93d2c43e21588c1e83c4255c52604b4ac3f40e656352d1827e95dd5222a45aebff9674e34fbbe7ed21eca77bd9b8dcbc", + "0x8aeaed611546bb9073b07512a9a1f38a7f436ab45e11775a0f9754baaf63e9bcc7bb59b47546a5ded5e4ba2f698e3b5f", + "0xaf9e6792e74a1163fe27612f999a2f3cfa9048914c5bef69e3b2a75162bb0ce6ece81af699ad7f0c5278a8df0ba000d2", + "0x850bf2d5d34791c371a36404036ad6fdcd8fb62d1bb17a57e88bda7a78ea322397ce24d1abf4d0c89b9cf0b4cc42feb3", + "0x87f7e2a1625e2b7861b11d593aaac933ed08a7c768aebd00a45d893ed295bbb6ed865037b152bb574d70be006ddc1791", + "0x8dcce8f4ad163b29a2348ea15431c2c6ea1189ece88d2790e9f46b9125bd790b22503ec391bc2dee8f35419863b2c50c", + "0xb4bf5266c37f12421dd684b29517982d5e4b65dfdfba5fc7bd7479fd854aabf250627498f1e1188a51c0a88d848ec951", + "0x8651623c690247f747af8fdffdc3e5f73d0662bc3279fa2423a3c654af9b6433b9e5e0155f1ce53857e67388e7e3401d", + "0xb155120f196d52760129dde2e2b1990039b99484cdc948fa98095cd23da87679850f522e5955eae34ac267d2144160d3", + "0xaec8115e8d7b6601fbceeccf92e35845a06706d46acd188452c9f7d49abef14c6b3a9a9369a8bab2fd4eb9288e2aaca5", + "0x998a8ca4dc0f145f67a8c456f1d6a7323c4836fe036dcbb0f27eb1c596d121eb97369638a9908cfaf218c7706f266245", + "0xb235fbafac62802742ee3d26b1f4e887f7d2da4d711ba7f9bb6ca024de7beec1de66bb830ce96d69538f7dcb93c51b26", + "0x9258d2ddc21ab4e3edcde7eb7f6a382a29f1b626003cc6fdd8858be90f4ad13240072d8a8d44ef8de51ca4f477fa6c45", + "0x99d038487821c948142c678acd8c792960993dd8cb5e02cb229153a1ee9f88249f4ad9007f08e5d82e2a71fb96bb5f32", + "0xa88ee9dbc73d3d8e0f447b76fdb3a27936bde479a58d5799176885583dc93830ac58bca9087075950ea75100cf51af23", + "0x88b9b15816e5a0387153c1f4b90f613beb3ea4596037da01a81fdd2bcbd0baf5598db99f77e7694e5a0d35e822758108", + "0x907ae4b637d06b15846ee27d08c9c9af42df261c5bdd10cf5bc71f8e5ca34b33ac2405307023c50bdb8dc7b98a2cd5fe", + "0x9393d6900e1d2d1a1e42412fefd99578d9ac1d855c90a3e7930a739085496448609d674ca9b34016ad91f22d1cac538e", + "0xa28ac56b216730b7dcdb5ab3fc22d424c21a677db99a9897a89ed253ea83acfd9d83125133f5be6d9cd92298df110af8", + "0xb027590ee8766f1e352f831fda732adbaf77152485223ad5489ef3b0ce2d2e9f98d547c111fe133847ebb738987fb928", + "0xa9cc08fbd5c3fee8f77cf6eb996a5cafa195df5134dab000e4d0312f970a5577942ee89794e618074f49841f1f933a42", + "0xa8b3535c3df0b1a409d3fc740527ee7dd5ac21756115cde6f87f98cc7623f50cfcf16790689cab113ee7c35a5bd4879f", + "0xb61420227b97e5603ae8a716c6759b619f02b8fdc48acbf854352aa6519dad74b97bacc1723ca564cbf3ca48539ed773", + "0x853762498de80eebf955a6c8ddd259af463e4e25f0b6ba7b6a27b19bdbf4c585de55760a16e2d9345cdba6b2a02610f3", + "0xa711c1b13fc6c30745203c5d06390e6c82bd7c50f61734aa8d99c626faba30119bc910be63ec916c91ba53f8483c05a8", + "0xb488c0a793f4481f46b5875d96eecd73e46209a91677769f0890c5e002ecd7d4b1c9f4ba68c47fbed40e3857b1d8717a", + "0xa651c5e812ae65b1c66d92c607e80be330737ea49c1dcfe019c0ecea0f41a320406935bb09206a4abff0d1c24599b9ad", + "0x85e34e7d96e4b97db98a43247b6c244383b11ca10bf4777364acf509a6faa618bc973e2136a4693fbc8ab597e308fd5a", + "0x99837214102b394fffa7f3883759554c6bb7a070f5c809303595a44195e02b9a169460dc6bbffb62bdc0e7ced5f0a5c1", + "0xa952f89c0afb4bdae8c62b89cc3cfb60d0576ba4fe01a5d99534792f38d8848d919b3fc7577435d8443a044d2ee0bcfa", + "0xa1ac1f81acb29798acdfc493854663519e2d1b0e9d23d286ce33882c34b4c1c0bb43dd9638166d8026315a44d9ec92a8", + "0xac9c58aa38219ae659d23007cc7b97fd25b7b610b2d81a8f9f94ddb089efc49c049a8ea4c56e6eaf7b6498f422a97b3c", + "0x87e61d501c242b484fb9a937ef21d485f6678d75257fc8fe831b528979068cadbe7e12b49c34058ec96d70a9d179ab14", + "0xaa45f6852f35cc8b65a4a8b5380641d2602a4fa4e3a035db9664df3ac2e170b1280c4a8b7b55161430063e54de4158a6", + "0xa46975614ddde6d134753c8d82c381966f87203d6e5a5fb99a93b0d43aa461466b37f07b8d0973a1abd6ee2b40f24348", + "0x8d35f97297773422351f4d99564c1359ef1a10cfb60aa0e6c8985a78f39b4268486312c8ebf9dd2ef50a771aa03158eb", + "0x8497c6242102d21e8b3ade9a9896c96308ab39171ab74cbd94e304c47598e2c2a7b0a0822492ac5c076ba91d4176481d", + "0x973f8fcb5f26915b3a3ef6fe58cc44bc7f4e115cd0ad9727d8d1b8113e126ae2e253a19922c5433be4ab2311a839c214", + "0xae3ee9f1d765a9baf54b4617a289c3b24930aa8d57658a6b0b113bbf9b000c4a78499296c6f428bbb64755dfd4f795d2", + "0xa5be7a8e522ef3dcf9d2951220faf22bb865d050f4af2880b8483222ff7aad7c0866219fcc573df9d829c6efbb517f98", + "0xa5f3c7fabd7853a57695c5ad6d5b99167d08b5414e35ed1068ae386e0cb1ee2afbbe4d2b9024379b6fc3b10c39024d36", + "0x978d5592d4798c9e6baceff095413589461267d6a5b56cd558ec85011342da16f4365d879b905168256f61d36d891b1f", + "0xb7b6eaffa095ecbd76d6e1e88ceebabaf674d9ef7e331e875c6d9b9faa1762c800ff1ea597c214c28080f67a50a96c1e", + "0x8a1ab53ae5ceaa42e06e58dd8faf6c215fc09ba111ca9eeb800612334d30d5971448be90fec62ed194328aadd8c8eecc", + "0xa9ca532cac8ace9a9e845382f8a7840bf40cb426f2fcad8a2f40aadbb400b3a74021627cc9351b0966b841b30284962e", + "0x8dddeda8854c8e7ddc52676dd1d0fed1da610ed5415ddd7d25b835bd8420a6f83d7b67ec682270c9648b2e2186343591", + "0x888906aac64fd41d5c518a832d4e044fdc430cfe142fd431caf4676cafc58853ce576f098910d729011be0a9d50d67b5", + "0x96a3f886a2824e750b1e2ea5c587132f52a0c5e3ff192260d8783c666206bd8ebd539933816d7cdd97e4bc374e0b1edf", + "0xa150a29ffb2632cc7ec560983d9804cd6da3596c0c25956d27eb04776508eae809659fc883834269437871735de5f9ed", + "0x81f7ad4d2959d9d4009d1dfbc6fee38f930f163eb5eac11e98dc38bd2f7f224e3f5c767583f8e52d58d34f3417a6cf90", + "0x97ccac905ea7d9c6349132dd0397b6a2de9e57fd2d70f55e50860e019de15c20171a50b28a5c00ef90d43b838253b3d1", + "0x95694f00c21e8a205d6cbda09956b5b6ec9242ec8c799a91f515b07dcc7de3b6f573e2c0ba149f5a83700cda2d1df0f5", + "0x82bbc3c4a3b3997584903db30fffd182a266c7d1df3e913f908d5a53122fa12cf5acd11d915d85d5bd110fcc43cee736", + "0x8d3f24b4949aa1b4162c28dfbb9f813dd1d8b330f71325448dc45ea34d59b69ca95059402aae011e1b5aba6e536bc6ec", + "0x92c734c19752d24782331e74c9af97a8399ddfdd32954e91cda7363dba876aca4f730b451c50a8913950420682da8121", + "0x8653d2c79f77b8c7dcdf7e8dee42433998aeedf1b583abfca686d47a854de1b75e9a4351580c96d1a2a9532659203361", + "0x886f0e414cb558c1a534a1916d3531320a9b6024639712ffe18164ce6313993a553e2b9aafe9c0716318f81a5d0bb1da", + "0xb31b5efaba5a5020c3bcea0f54860e0688c2c3f27b9b0e44b45d745158f484e474d5d3b1a0044dd6753c7fb4bf8ace34", + "0xb2d615bbdfdc042d6f67a6170127392d99f0e77ae17b0e1be6786ff2f281795f1bf11f83f2e0f8723b5cdd1db1856e09", + "0xa6e014cca531e6ac2922239b5bee39d69d9ba6d0fa96a4b812217dd342657d35606f0b9c5a317efd423cdb1047815e3d", + "0xa8921736b69c9fbb29f443715174bac753e908251804620c542fad6cfbfda7bdfe287f2902f30b043a8a4b4818cfdeef", + "0x8d73a9949a042ec2dcefa476e454cd9877eee543b1a6b3b96a78ffcff87421e8b26dd54d5b3192ac32073cb36497acc3", + "0xb936a71ee8df0e48867f3790adf55dc8efc6585024128de2495f8873bd00fd9fa0984472125e801ed9c3cdce6698f160", + "0x82f69c06209c28f64874e850601dda56af44ffc864f42efa8f9c6a0758207bf0a00f583840982dec0a517ab899a98e5b", + "0xb7a0a14411101473406f30e82f14b13e6efc9699e7193c0be04bb43d1b49e8c54812ce0f9b39131a20379c4c39d3bbe3", + "0x81159c969f38107af3b858d7582b22925a7ccced02fae3698482d7e9cdc6c568e959651991c6cf16c53a997442054b61", + "0x8bf1116a206e0ce9199fcab6ed2b44a9e46e8143bff3ed3f1431f8d55508fe2728b8902670cfd8d9b316f575f288ed9d", + "0xa279b2149824b64144eb92f5a36b22036d34a52bd5a66e5da4b61fbc95af6eda8e485c7914f448abd8674fc14d268d9d", + "0x8b98279b5f3588d1a2f8589d2756458690a502728800f8d94b28e00df842a101c96ab9c5aee87c5bbe65552c0c383b80", + "0xb4a27a351ec54420f94e0a0a79d7c7a7337940399646631baca93eeab5fd429d7fb39428be77dcbce64a13eaa3c8ca1d", + "0x90c08baa29ec8338ffce381eae3d23ce3f6ba54e5242dec21dc3caaed69cac13f2ab5e8d9d719bc95720fa182eee399c", + "0x85156d65bb4fef69ffd539ab918b3286105ca6f1c36a74351ab3310b339727483433e8f8784791f47b4ba35ca933c379", + "0x923005013c27209d07c06a6b92b0cbb248a69c5e15c600bbcc643e8dcd2402adebd94dd4cafb44ec422a127e9780aaec", + "0x863b23eb5463a6ef5a12039edc2f8e18e3c97b244841bc50af02459b1bcc558367edf2f6e4fe69f45f37887469dd536d", + "0x87a4a7708a112724ff9b69ebb25d623b5cae362ae0946daed2ec80e917800dbfcd69f999c253542533242e7b9a5cc959", + "0x8bf4347ceea7f94b53564f26b1a4749a16f13bf71a9e03a546f906f7c423089820ff217066159b0637d9d6824e9c101c", + "0xab07eef925d264145971628a39e4dd93ff849767f68ed06065802cf22756fc6bf384cf6d9ab174bfc1a87bcc37b037aa", + "0x8e3f10a42fad43887d522dc76b1480063267991c2457c39f1e790e0c16c03e38a4c8e79a0b7622892464957bf517ebd8", + "0xa8722fc7b1acf0be18f6ddf3ee97a5a9b02a98da5bc1126a8b7bf10d18ee415be9a85668eb604ef5a1f48659bc447eb5", + "0x878d6b2a9c0aca8e2bc2a5eb7dd8d842aa839bbd7754860c396a641d5794eab88a55f8448de7dbddf9e201cbc54fe481", + "0xada881c167d39d368c1e9b283cf50491c6bfc66072815608ba23ab468cfbd31ca1bd7f140e158e0d9e4d7ebfa670bc2d", + "0xa2b48578fa899d77a7ee1b9cb1e228b40c20b303b3d403fd6612649c81e7db5a7313ba9702adc89627b5fd7439f8b754", + "0x8e051280e10551558dcb5522120ac9216281c29071c0371aaa9bde52961fe26b21d78de3f98cb8cd63e65cff86d1b25c", + "0xa7c5022047930c958e499e8051056c5244ae03beb60d4ba9fe666ab77a913a067324dfb6debcb4da4694645145716c9d", + "0x95cff6ec03e38c5ab0f6f8dccde252d91856093d8429b7494efc7772996e7985d2d6965307c7fdfa484559c129cca9f9", + "0x993eb550d5e8661791f63e2fa259ab1f78a0e3edad467eb419b076a70923fede2e00ddc48a961d20001aaae89fad11e8", + "0xabb2826e4d4b381d64787a09934b9c4fe1d5f5742f90858228e484f3c546e16ee8a2a0b0a952d834a93154a8b18f3d16", + "0xa922ca9f2061996e65ef38a7c5c7755e59d8d5ce27d577abcdd8165b23b4877398d735f9cb470a771335fc7d99ecb7fc", + "0x90f22862216f6bc1bbf5437740a47605d1ff5147b1f06f7b13fec446e4c5a4a4a84792cb244a1905f3478a36f8d7065b", + "0x87f3d9a86afef5b79ea1ca690ee1ee4bb9754b66f7c50a42ad6b99af7c222c853ca161f440a0a2a60b3b5a54e3493240", + "0x80a9ca9a2d33b9cf61976b3860d79f5d00de89a06ef043d2a52931809018aeb4ce70423cbef375b29c2c750c2c8704c2", + "0xb4e798ef1d615896108dae37ac50c1e859216ab6dbac11653e44d06ce5209057b4b0dd6d31dcfcda87664a23c8ef1cbd", + "0xaaed6d1e7c5b1db06f80dae6c24857daadfb0268f20e48a98fba4b76de1ebf65fb84c3be95fd6a418b498f8285ec63bd", + "0xaeceaa316c6369492c939f94809bc80e0857abac86c0d85be8066bbf61afbaaec67e28c572437a8d35c49dd596b3134f", + "0xb791c3d53ed34a7d1c8aa89b7953e3684c3cd529230824dc529739a5fbe74b58b87f01e56e7a169f61c508237ef67160", + "0x9351f8c80634386c45c0050d2f813193f9d839173be941e2092d729be5403632a2f18dffdc323d69eb0dc31fa31c5866", + "0x97693184d5c0056ae244dfb6709cafa23a795dc22d497a307a7f9cf442d7452024023c54a8d6bda5d90a355ba2c84f3a", + "0x85362daa003d23511ca174a8caafe83d52b6436dc4e43c4c049e5388d9211b5cbef3885896914d86d39be0dd1f910511", + "0xa2511b5fa34b24eeb0e1bcbcf872a569d1ff5570fe7b0fb48f5542f7fe57bad808d34b50afa87580866a6cb0eba02f27", + "0xb382e3327eb1401f2d378dbb56ac7250adde0961bd718575a64d264ffd44772c20752d4035c3ba60eb435e160b375e20", + "0xafad8a5d40b536c0720556845a6b257ed42165c14fb4b4a874717d107752f49ed9380c5b048df3aca67287bb8fc411a8", + "0x8fad0c98434ca5373c2d767868f679b76b4a8d04bca8240ea3f388558262c2d61b73b16fc1160932652b5688c25fffcf", + "0x83898008b5cbb6f08f8ef3ec179427869682bb4e8d38f6e6a687a214d4a307436afc64ee67d70a5a8ba9730bf839aecc", + "0xb85232e79913785fd82b06890706972b4ad7a309489930ae23390d51aa5189731f8a2df24800409a8c36b3dd6fc91275", + "0xa24ff26ec792f3701da4c5638c1fca4fa4dae95b01827d6200d583c4caf17ea3171393ba2a8c23d1ee8b88402916f176", + "0xadc5c7a7ff6b41d6cc386b7fc69d7bb04179bdf267864f9aa577f0f6a88438191fa81ebaf13055c2f2d7290be6421ace", + "0xa05e835abd502d31454d40a019010ff90b6b0b1f993075a35c9907aeab7a342ac0ba6144dc9379aada6119157970e9b2", + "0x85ff07ba58463e7f153fc83f11302e9061e648a5cbd272bb0545030b20e11facd8b3ff90c9ac8c280a704fbda5c9d1b0", + "0xa6c735ada8f4587da8cdad7ea3ada01650b5a3ecab8d81daa7a5f5de51ef4a6592b524692584306f06be3f6701f2870c", + "0xb138deee4e53ae8d677fae104f713ef1b8babfecec16b6a85785a66a72784eb09d44c3b63567222ade714e98f7d1604e", + "0xae79c1a49dafcdd972acd95d8ad0a35c02adc7fd736d4c44c3cd13df5789d339b5ea16bddbbd43e486a061ab31baa5c0", + "0xab3cf2371a1d7dcd0ffe3869a0178230964b06694bf258b2073ea66a2afccd845b38485da83d02e1d607d4c5c36b78a8", + "0xab9609f28a325fd01cb39540e3a714506c44e52ef28ee640f361deb5760aadbb23e804663b0fa20a66e239c33f8d8bb8", + "0x8ed95ea8e76e1b42823d7915a6aae77d93746f846bf602841dfce0e47543a36efb9ee7e5b42c73c3209d911225cc471b", + "0xa80b6162036d43811482323f0ce59eb18740e33a63d7c7bbbf3be206985919e5342d53a69df537d43e8b7d7f51e8892f", + "0x93c03d0a5083408ba00c125a8a9385213d4c860072f0297857b1235045819b904e07f2425c13a661d0a01d2e53347f4b", + "0xa6581200f00f96c461621e1d26b14a23687dd97eb9f7df4ba641a84340ee7306dc1796248fba4804f185947ad13b4385", + "0x8be174018fa40f7e0cedc5ae68f38969eb7695f2205e9c573641e533d56f68c20abf38a23d2f0dcac371e60b21b18615", + "0x857ad4ee3218c647c58f09b8ab22bcc8976f00a768ab1f708618e868e6143474be846422ce2710a0ed39b5155b6f13a1", + "0xa490bec40f322d599f26bcefcdddd8f2ef6576aa737d5ce7e8d5d422741abe749e3e6a48489aed8c560633f72857e3c2", + "0xa9c0ee339621f1c4a2410f9b4d2f03f1b558dae2973807b8bccd920e8feb7f65dfde3e79986b72ad21fcc4567240381d", + "0x8592251568e750a430f7d2c6ddbb3ec82a4dd9fd83efe389e69aa177fd97ac2c96c59a6e86db20d8e6f125d65b46c4d3", + "0xa4e2f4aa6a682913b423b097c4069c4e46a1f3af9556b1bfd0580d0fc01e3991488458049e0735b2a629684a79271c8f", + "0x8c4f6a3e738cf74112b08b1680be08158013ef8a515a81215d8a36c9b756786d1b4cb4563923463f3329292f4b48bf6d", + "0x8bace547353c02ea00dd547eeda7259aa354d4772dd5e0c486c723cf88627b7112e196b879c3c92a9561b674d9fc486d", + "0x8d372f4901e25e8db64fa098148d4a4e709b0e9dcb756d0f90dad99dea393054193ae1a33d292a3dd772ff7ba05e4b71", + "0xa8c7ea6a6a031ed23d65639f01f5423190775558f479700597df7ae7e338a6ae5e9b32f470aff20787ac8b7eec84df6c", + "0xb6e9dcba240fdbbf66033410a79a2dd3e9e1ffdf2eae949b3a9ed720e939d92339991dc3e70a5ac7d5253f317daf0b7d", + "0x974dec4cd61af75721071752c664d9c2a5121f06ff1515c56139a177a3ca825f763b69d431d4607e393fa74dcc91cc58", + "0x958863e6ad583a9d370a6db3639066982e44766904e7afa849b132f6666b7d08ab931131b3bec7a506d6583e93d56767", + "0x8b93a33b5da9b3300c20a96d80b894e3789c77041183c2cb21751579c8c96857f60cfc2f075201b64e95a78985c5b321", + "0xb726cb9f7ef34ddbc2fad82b3b0af0b30cc913e26c5a614ae5c19cc9c55c8e6dae069db5315a8dcb6d987415bb550ca8", + "0xa730f515398a71bddd66cab2ff996659d4e47dfbb08ce7958a41021f76d269b91c7498b708cd14b183a8ef469c772803", + "0xa4eb3b18132eb0f5337f14e01d63ca0bec0db6a43870f800e5491db756c2f5fce519d8dba5528b4bcef550d06b33699c", + "0xb1ab6621eec1ee6784e632e214693f39a14f3715991996b883d66200963e065c86fa0667f7bc36b93b40b5d90ff708c2", + "0x80486a26c3532ad6e19f76d8c9344e2626c07363fd495264927cb5935fa9565ece670dc98767afb04af6a9a5c9231075", + "0x8ee20e0df3c84a1c6b0e21bcc325cf99235b747ffe47f17fdfba548a358ca75cbcc331dd50db2311b400ae882256a608", + "0xaef4268959e5541e7ec69c921a1e81a8374d7e44bf1bb2debf4101cf3cd6b7d6ca7f441758b388de96b3e0edb5b97be9", + "0x8793629bd29d689ec94b016de8886cac6e2ca6638911babb22db4a787661422da0639a4e4089ebeb689d173abfe75950", + "0xb487b3551c20a29e9a5abbda8c50ff594826283e443c09e3ae09b914e46060b3f9abf70434444ce1487e2a74e562616b", + "0x8f11531cfc5997dd04b997cb87ba1831aa7041d5434fe72de66304e3f165d882fac891391fbb1eb955c65319e65293b6", + "0xb195136875fd02a75676c33cb3e60504d5964f7a9e81f4c8c8fd38af62e2145c55f765b3158664566191188ac678f381", + "0xb374174b0b3eb04fa49eb4ece45173f0db5d829eac370a20a62309566e0f98b18f72f3633626893c053b7be6bfbd2366", + "0xb2a2f6b0cf652775679b2d677048f2ed8c31a3269e6cddcc7a10e3e6fee89e486b50d9d55fbe452b79c4157c0270fb77", + "0x892177c364dc59032594e7a6fd032286ffdf4fa0b9e3baeb37ec839faebfd2fd46c57b2c9bfe9977b59c93a9cc0ead1d", + "0x8ab7c0038a7dbb2ef200dbbe9acbc875829ecad4883792d5c6ce283de67ccd9aa935a9cc7b30b2bd9de7fca7bf2a9a05", + "0x83745cfc78ca709835aa6c6a233c2b86fb31e3f9f6a8becf63e501f2841c4366fb7d131b746c9d3291afda714ff05579", + "0xa723dcb67925ef007e8339dc578d2622d9bb77cfda87cca0088854a59414c02338752c56116a6c1281917842e8467c38", + "0x8a098142da0af2254c425fdbbd0d1b1a17b2bd781391ab37f181775524b8563c64ab8a1602aee2ac6c0a82ba11a8b1d1", + "0xb13bd7529a9b351c5d395c794c28bcb0a3167f1c992e8c062eef47be9be27895945231d249c73a0b6949daa295e14944", + "0xa20dcd2fc2222eaae467d9f5db861040f58bcb991a26e5663ac3aa5e1ff13d0010657c5af586cc4621757add2b905073", + "0xb818f660c3cc4e9f273c25ceeabe562c8afa8ff88529c26f2cf45ae6b2813cca5f350e3cbd56f6257c4df41722dabd25", + "0xb225d5987108b24411bc389276f12509a45e86d5ad6b6d929af5274df0be11109c0fed329669a0acafdf3b0beaa8f2ec", + "0x91fcb6d04576d3c6bae947bb7843b430e5fb0592ae49b0a65dfa5791f4eaa4bf2c7f436c8de7360f217001c2b4e5c67a", + "0x8821f7a1424ca3fdc5d4a5606ad10dfaba6094cf36669fa9f84cf7617e50425405d14980780e1e18a1ecea7913cda896", + "0x990dcb7f38f56521a70cb71bf4522649fcd46ac052c7feabb0748dfcac9f9c0f95d29e070d32af3cd0adbf869535e17b", + "0xb0fac1029fe2c1100f24e2f4bf10c7672199fce53513c7dde2e8d9b00702edf0143e0e1dc7ceae7dcc6994edc2422b6f", + "0xa514ebb1a33451b4915c05114db0b10168393613744df848b24e43e09f0bda23baefd9d731075198aace586615ac7911", + "0x8b77f7953c2e67049fdca3653b8d8cf3f799677f79b954da02bdad8cc4d6c855c1c7c16b4f6f9ba35f46426ec28b2d84", + "0x875520cfbda16ec5b1d1d00f578a910d0fc052f17870ba093e22e310bb07648d34817cc2b8811b6f52de535f7046a0d0", + "0xb8c77b4be0b430851c4ff69e91cb770db1935d848198601393810ef395efab52deb9d5c6525472bab720273d5e0e7a79", + "0xb6d4d437146671bdea62fb6545395ea3df39f1cdef21b8476b68e7a25aa7354f847740576d6c9f187bbae9941f0ae450", + "0x95c642f1bccdb62cd6a2212dcdd6ff8d49aee426ca08b7cf3a9d15249d24a9eed5533f92a70c84498c0797f8a57efa27", + "0xb617978047ed0f748c305aa7f30c2dacd0db00baa67fe0c5ce346ef0e6991dc7e05f18dcb2702467421f8390f27aa815", + "0x86411c7a00b3e8b43bf22fb061b1f54ad9bbf632cd74395a478218389c0f544668acf3dd7726532d080ca7da9a5f8608", + "0x97bf684a8849626c4710a6992f6c11f6b5406fd4dfe9e6aa502425aaafe9827e2c435aaf9a5d3d2ba3a4c0e8aec79ba4", + "0x8b178e2a125b461d3180906ffba0af3dce614c64058501fdd35243ababf892d6fcdea4834ce42c25d5569452b782a709", + "0x8ebed2c8a25c61da6a6a8cb0d8f5ea179e28869753eacc728f2c076f7aed8598cd3aa0981f120f9e7ea55b3a689ae882", + "0xa6f235b8e655ca3d634740b53d8c0a757ecc75d2b8838b7948997c1985473d01943d935f687b86cee56cd47c8e773443", + "0xa7959c465a9646908b9d8032a589e41a7dd999f2ffc54bb42f22e5f8a4d8c493a31bcc7ea2cac6c8dbcc59acace7181b", + "0x96d0532df2e12da20a57cadb6cf5f6c4ee1aa4775629358c25f1d51677a3e96d1fe3b232532324b4f02f941952d4cc68", + "0x90f493473d686b639a30d1ddc9c72eae6e983f1236e162e58e967a477c0654973ea2e1bdf4ba1a44d7247bc1befc2cab", + "0x8b2d87876d9c4085102a07ebb41c565ba69acab99ffc03efc18f20e48d3f3bbe4fc6ddab9c78fe479d9ada80504d85ba", + "0x829a0fb3200a28e09cacd6c5346000e7786116ddfd898f37dfd17bef454a8abc0fe939ed8735c00769f7f2f33cd4f906", + "0x86194ec9e88ddb7150e8b03e7a535b6e99863fc6762835601efd03615aa97aaeb413cb210e86035086ed852b39c9d019", + "0xb02efd116a7189cb317ceae392bc301ae55470f0489fa89934e182aeb8c67e280299b975786fe9a470bff46827defb9b", + "0x87d7c3903bd22b12d815506f150373f518d47dfc6e5fd74347d88b518124c9923d1e4c98defeb3a45d53d50b423e2175", + "0xa1a430406b28254a7d6348bc98e697e9bab43839aa05d53faee97546f84541ea0b559162619b2045182938f69bf61cae", + "0x99d243c226c61c6697fb3d2594f3533fa5dfd7cfc87107908cacde337d7a077fa5a9dc702d26081b065edb1227498e65", + "0x800ee5006ab6217161f42db0cfc552a81728bb4fbd7af6e4620ea099a65ef6664184af3f65a07fcec7e965529c5b49bf", + "0x91bfd307579cadc8f81009558605be3edbcb8dbba271475803484017f40130b2b216aef4f620d960193be681877d3a53", + "0x96a060459dec458d19a6f8af6e49dc6c7c58c55dd18915c5fce5e0f4b4a422fce3b9632f6059388fe760289abf70f173", + "0x9921a37f3e657222c7fda3588418a9071409711d9f1fccede7494429f02a45fbc52d79fbb64e9ccd518f60d06d0520d3", + "0x81052b0d15773cb75975ca9230ebb2579700e489c7e3f07cd9cde206fef38b8139bd4976d2b4a7840495fc645f96df03", + "0x88ac37ba66d1de5e23878c992e4d54023729e97e77351f50dc5918d738b5a73faf1dc6feec7e85784761836ba1c6f778", + "0xae1e6072c13060775f6086d1ae1f88b627ffcb810fc0e0e97deea1f3a15ef0aaa52a6dce2563e4beedadc131af2a8281", + "0x8b60a340f5e4f90badf83001b495ac9f13974c3d2054ddcb3e6b8ca99dec5cd63a263e05c282454191ab2e087d5a2911", + "0x832e2d56ba69dbf817b2b9dbd25c1538d5b8dbf5d9bc05e6be85054a423ebb66a71b157e166e0b9444ac171b34b7ccc9", + "0x8586036fc7dde1e7e3ecb61663130c4529866ae9f5f5095b9fccd24a4c70eea899aae5f10ea1ba66d1665b2d83be35b0", + "0xa77969453b5c083a207913272b5b69d4ccbd8718bdf54be8fbe11b4bd0a2168aae3ba8f9362afa69c0ffa28d7e5a2340", + "0xb7fe9568c214baad0ac5f83745611b481f744ec1c4fa78a549b180dcf79633e5ba75dc20055012a13d849eb7a9be57d3", + "0xb01cad1d2a6c51c0ce88243d1f52f95fb5ee315a905079688027511f0c4ecd0563a3a81846709d272fa5ccb9665e8043", + "0x8eae0a21adfc569aa57237654021c2bdb2c6f0f52ccc90a126682c21a1f9413c63d285f92b2b2f8649150a9284bf70b7", + "0x942acc947192b5f3cf60e92383e5d35f79e7a5904e8e9fd1c8a351676c83ad29b0afb6578d555457cf909f8f4d27adfd", + "0xa74e092f8628fba9abcabc27e2e9f3d5a9a941dfe50a2dfde2ad179aabc73afd196676925c2d98643ab8b3d02bdb66ad", + "0x896159daa2afd757cf3f9d34af248ad68bb3c62e4c9ac49919422727479cf669098f270b9e645607a7d11adad4c889b2", + "0xa428d8370813d78e7a2a24eebd36e9da2f8bb3605e5a39b5fcda939b531c35a8ebaaa642ba556250a37bddeec90326fb", + "0xa5fa04eb60a1d5ee9820e78f42f7be15e1c02757b539aead995768c6209684d6c183c71d282e0c12a4c15c03f9a89d4d", + "0x93c77d5d220e40affa7269a6915c076c9aef4db552c643ae5d560a79c955b491c6346ca4cf11cbb7fe1894e28d47b065", + "0x802e605d2de745eef6981d88e7a57ef4046a2062725e8080995374cea2b3273c27f35b7774d0dcba014710d8d6c501f2", + "0x82f7169e6ec9b3e2bd450f35ea2e66d06bcf900acf5b73139677b48e078ce2e16599103027b2326770c99c0a690f2015", + "0xb0c8581879439f9b997551233fe2de71aa03604f9cec37a7b18c5854342d9b67be468f3cac4bf6f64fe8a0066248c498", + "0xa3f626848a4db6e9fb01cac90d3362ec521e969ebd5228af694ea3671061476149f13d652942ac1e39f65591fed740f9", + "0x88a8e759b9cbe16a7c16e43f4afa2de6100d2eafa4dee75ccd653ec38c919013d0a6b35c1ee1eaee7c1985b58bcc9e92", + "0xa3d5fc7aaea072798490616552d947e95f49cf02a420314307aafb555287ec607d75589ba24b009cd68299dc6f7942fa", + "0xa809cceeb84f9bcf3c3ddafde3041e7bc3b1d14df8830ab849002176a0725e6f16f70774d8962cb0b8ac0dc43c4ac66f", + "0xb8f2e46c031cc8fa160a08c2ebdfa85345ed14771b06daa9636b0e7792b7fddbc501dfc85cc626a01104a43a7d3230c3", + "0xb5367e2a521c318b802ce16ceac80c4b8139f73ddb10ddf38433397cda70a86ea1f051cc55626a4e99d27f30f3975ff5", + "0x96d963660121c1441cd13141279cd371a6a0aa18b6a20761b18df60aa9c14e13489afd83695a0921d5232efe72045f07", + "0x80818d492fd85d666bd91aaf6257b86527fdd796773c793407df1d4a0f91d74649a6bab4d15155c36ed4c6e0a32c5636", + "0x931e22918905fd6c230d3d867ea42861f3074d320d14e1929031924c8ac209a5c552b679b24563bb12f9749b4ee983bd", + "0xa4de2c333e74ed9bfa3c0bf6a0beb90427abd9aa4221294cda74331646b58ef46ed57cccc8798ba2b9309894b17cfd69", + "0x883881554c1d88c0ed8d3b6dec3d200f6fea69a77ace3e4d6f86b41506a23724b4394ec8384075f9c75c3868ba8a8e8e", + "0xaa0539ecf6ec9bf06f24443027f8f24b6b3d8c5b2084248eecd4bcad3c9a69716e1a0d01057f09a65bff1006ac5e157a", + "0x856d74d44c943c9e809b42dc493dff20eca03cb0cf5ed45108c69b1f90d8592a53ae8100e99380a274fafad23e74cdfc", + "0x9188257446661c88da093b7c5ce998135913f63842d7c1586065377b169ee35b062d925367fb9b909ca971f1188667b1", + "0x8d3aa57cdafbe998938787479f5d590c1484c6dbe94e6c487e57a746ef5252be0eaa5976d6270de7db64b6b92e57a0f7", + "0xb8f4d6997240f9eda5aca0c43323a828d1563c491b3db2087f60ac4120a3fcd06075fb42bb19d0339ab5ee3fb7db25d2", + "0xad247ea94b8ae1e81eae4c9fd7b39e6601b53cff47b2547ff90a3cca87192eae28408082774a1fd14bf9ab459b7a4f1f", + "0x9598598070f8bdbcc49056c40971e673726cd8c1bc4baa0b5124dfb5fb750e7baa7a7df18eae2bd91955ddcb1ec67955", + "0xb874131ab1608667fa60ea29092d090859eed1812e90c609afff96d79e82c5ba546f617f4c96fc32c9bba97431c1e9af", + "0xb00750a9cdc75c2a54f0d3cc99b0fe02300754f25166f7ac85ff41ab5e9cfcca33a29be76a480f12a2d410c7cd5032e5", + "0x84b5bd1c90bb6c66755b28ba4af493ca1b0c3a4df9f436aac67d2e07289053f925cf6a149a84e74e1027dc8758150179", + "0x99caf64bd9d193ff306e8ab5da3f1bb2a190a60c3a82099b8d03d17fa810dc53d176c21379f479e828f60d25beb3ffd0", + "0xa8fd9de502f1c261d5733430e5a18d8b7892a98c9529a016fc2ee53892ae965dcd9c75850bcda4c7edb980b8d88e60ea", + "0x848c02cac636e047028a3fe8c1bf4066fb7591b96b0340f8fbd476ff01b35fa3e37d309333771a134f24800e5f3f9289", + "0xa1eab1a06dcca3439f0166441e7e7f2f5b56f5f8aa9f45e411c561f556e0fb71c514c06c26ac53b49a576caca5faac3d", + "0xaa603f970dcbe953e700e61c151182c8d32cbbb53ceef572ac93383db33a4b098b5c7b267e42d514ca66b740c0925efe", + "0xb55fd5301bd700ddb0b4f72fabe9a91ad49759506101fa802ed1677e9553595aa4d2c66f7574e78d21ce882ce0120ae7", + "0x829137bc4da7b4886d3d04d2c39cbf4b1dc40c813ac1adb425c7b9abf9142b516314cab79c68454df5d71994ce416144", + "0xb83a3a22735001f783dd48a01c4fb3598a51ff3987e842b8045c71c035b9e43645a55254ca5911a5676ef4a8af12d056", + "0x8ca8d463deb13f9eef5e533bc39efaeb0c15631282c5c0deee1673b0053a7cccd514af09801dd6c158caa159fe9351ac", + "0xa9ffb1427828f3c456b9c8cc50782de1ab0029b9233a0fd998bad0fd014d27e15c4a32d1e16ad41bff748378b5abdf49", + "0x9627e29f725ddd86456aff813976bbc4a836f4deabf5ad9f73d1a260ceb30948824df9c8841e6b3c529652202be181b3", + "0xb52c988647fe3d9276eed3c262e1044f57fbb116c64cf4f207235c205b3fda0f3d789bf90f5217401b468d85fdfda404", + "0x833bbd6e2924f5c4446cb76b881d1434a5badce9eb9b003f85d076e297ad7ef45b822069fe54d17427a348c3263fb838", + "0xa067a36352db6f82a116cb87d3db5f60b18576852409e2076cbbfc7843af78866313a4969385a40271051dd195d51116", + "0x902b99545971f9a103f99d7399acc347ac46fe156166e51deefc0e92aebf5893460c69aeeae11f5af9f49418e289ce6c", + "0x9206a0e9ce9b9880f29ef0417c96931985f5d83bb17cebdbba4ff2af81a3d37155b04649426f698aed372e4f669599e6", + "0xb54a5d7c976e45c0b1d44433595eae9d1ae9aeabfd58cd5ecb0c5804756a7b01c9a517754423b4714a3695533a3114c8", + "0x91b612131e84580ece228b81ace83da0269b53f94d3c02a1a0879ebbd81bdc252064b3d03a7e140b43a90f237d9a45a0", + "0xa6cead3b8607eaeafe37135bd6de8fbd16f806c131eb71c8d36bfbe295d45b070255e50dabf076e2c3f6b8699be71d6a", + "0x931da21e67b11ba6ce438546a24d063bcd51aebe39b4220a78d9c0aab88b2d37969b5ef3502d835507f9c8d6d006714c", + "0x8fda408caa9daf01122a2308b7b9d328f52e1e2f138a8bec30492488f4d710e5e52524a6455a3a2ae2818ec8a610b650", + "0xad8ad5c189644352d90c462731c46145410e5adf38682bb80f95495dd64d9d13782537d68690847bbb06c6be7175dbc7", + "0x87bb5cc466ade60feb0961421c3fabdc8a7e20f11df8437bfff63d3f8bd25305002a396c9d0fa4fb9a9986d4717f12c4", + "0x827cff72870ba00c29064a7d2b4973f322d6b6de7924c93d8bf8825e7a0e8478c7748f90f5c716bf83c55b2795d315d8", + "0xa225895a8e94229776ceb51b05356291f2dce748be17a60d5aeb33ef8507c368bafe5d1d6eea927f28b9d1422b661b9a", + "0x8e011323ce670ff51c964241a6b72e0e0ffbb3ff9bb2762492323fc3a4abf4718091be0945287c7329850e4f74462cde", + "0xa2c03c2e5f4e9d3ef361f68b188451994ad1b24de9f323370559c8abfcdc7bffd289d92e78a5f6b104b0a12c84dab2ef", + "0xa22b4771116ce22276fab1fec6826610707ce8a342f9f60b079c4e0259dac3cc41c96c560dfd0ada6edd2828f7c0e8d6", + "0x97c17441d0af9be83b42097aa8b7cec84a253b9a2b957214b8fa93c26d2add46144faffa7b8a55312059b10690f711f1", + "0x94bdf348849f31a2737cbae5e5848aee711067bac85c11c2e68b44c398cfafbf3493a3226cd1ddf7a916e7613fc7b6f6", + "0x838f59c6e8469a8ec6fd40b978a3607439aaebe1e50ff707eec72c0b8278af05b477bf12a384b56d03e3d4eb91e56f67", + "0xa1940f0db58185e2b3aedd2b0bc2b73b4a65c68e09b046f38e9dcd4e13c94f5406bea92635190bf315e48ec64eceef2f", + "0xb2f4e0ae44e1f1210a91d8f280f17091fa994034ba8c991583f8182a323e9b3001a712e3584fc2d64ecbf2d319d076b2", + "0x9342b89c721338d02c7854cd7466fb24d93d7313b6114ea591e6607439c8ddb911d1cf35f01898e9c557982bdff8f9b6", + "0x8583fcab15be1dd14d5a415f4b14d706c8c62f058500f1344b37730c8be6741779691f87ded3cbcf6516468b373cafb0", + "0x8fa9587c7989646571ad9032f34cedd353caee14f5be5cde1e9e0a1710f90c08faf6fa96a60e1f150f761c9c8ae7417d", + "0x8d9ff904cc08141f5a9879f5f77dc600e6edbe859082231a4d819953890199bcc5f940b730ea688332f07e5279d49e1c", + "0xb5f82b46e5ef9a2df8d144202d6e2e4f3bdae8e2048d2af5ea7deb3f722fbe6d370401954e74ff0d8cb1010ffb1f38d5", + "0xa3b5b57d435b06ed70530e060002a8fea71746ad07d969ca23f22b5e52624527595b6a6d54b4e953fb7b7596bac378f0", + "0xb90f89390df6d4b7879b915aa3c29b8d779d035033f8873bb7ac54a14ec98f0d08c0e3bf696e2ffa7b5730d736f571f8", + "0x8e81e371b92887e43d95c0dbdcc9575282b26ccebdc8cbf46587e4f2a83b61e9bc0c6d7d1f114b9d21e04fd6c180b12a", + "0x8d682947c51dffc6e0fe0a486293c9ed121f441805168236393087cf62f2a429cca60bf0e472564844347d32c6bea27e", + "0xa8341ec7dd189fa7168759240224192c58209b53fc961c18082deba217928c399bde08ceae42bffd37c1135b4d14a845", + "0xa94bb076dcc5ee5ec82fac57c5b384c690df12631882bd1b960e1eb8c04f787bc22b7bac315b9dc5a8a098f17f051a0b", + "0xab64e1c6f01b87706c88a3bd974454a438722768de7340b834ccf93ea9880c14ee7c2181432acf51f980d56de73832ee", + "0xb7b0058bb724d879e5ad7aed6230297c54cb599ef659e86bf2cc84c38225899fb388391df9b2e6fdf063171937fd8c72", + "0xae856f4fb74c27cc98b67429186e7df4feb01278cd57bfd3170af6e52e0a23b9e926bf9565a890cfb4ae8f2d590b2cd5", + "0x804b9c6702f0596d328f92fc1ed5a30a7ba17b9204524135001b569233fc4937035031d079f52fd04968f37c24013898", + "0x84274ed1af6bd6a968583995622b4d18c6a2bc703ce0d0edce45bb736529b4836343dcd11911a94a134dca7877e6cab8", + "0x88808098463f7505034c3b6328c8a08186b33f7a981c08376e429dd64b79b97753170531ed078dd265ded4ec0a1ed8d5", + "0x92823bfb23a4eb84d3759e7d717f0c8641ece0927cd2ba8c728c26bb35df2629a838002f353c8d3d75eb19520aab5f25", + "0x8db36bae4d960cdb9c51f419d7ddc81f372e56be605bc96a9d4072b829f05527c37c8f255cc6115300a2a0d2e6568d89", + "0xa8fcdbd7f3b4d7ff04149a209feb75e97149e7efceaa42d66a6b8e432590fe7bd01f1a77fa8b47108f670b612e33fee9", + "0xa9f4c53c62db7e5dbdea6918862d3c6d24b5bd8732a218edf0ba61e9d1861182323d8ecd7bef8f895b42970b492f6e40", + "0x8b95bc7f07818f4d7b409aff8da0b2c2ae136cde386f53a71565cae9fd14c73c13cc1cfd79c0f97cd77839fb738c5b9a", + "0xadbd1d11adc756b51a571ddbcbf4392415231ddad93da09acfafee03a9e4f9e1ce3826110619e5271feadfaffce3e793", + "0x95d327c8bb195cdf25fd79c98f9406a6b0316214b1630ebcce95bdaeffafa36fc1accc6882e0e5d13a8db5c0f3c0e61c", + "0x8cb2f1e2fb25558869afdacc7bb866544cfdd566cefcd048b48d458a886130bd086ecb7600a960a7f2563c61cb326510", + "0xb3aa8c4bf5b933d89cd74ca7f7176d6624d562d7d58b041328b49d7562a30b489cb606abb3c49e85baf04c28e9cd1f44", + "0x97f9053a85250c420599827297453c2cfde087065b823d9e43139e6a9cac3a2ec40a1b6e2f0726bdc870fff215462f0b", + "0x878d5dbe6b881389c2ca126ff66d87127c9aaa3f62f0d2c1ec0ea2b279ac95f8a06710dce166415db227655e2345a04d", + "0xb2c33a6b4203e3ca5247f0890e475518317ffc44cfbb1da9a1ba02114e8b752bea618050b876de5cf3b1906140a64471", + "0xa56170c8313d2b5541a795bea9934d4425b185b5c409f0484df6f44f0e4bcbf50b860ff46b7245cd99c1cfa8fc1965b7", + "0x96e2b658e2876a14147385fc423d2702a3cb76962b6b437222cf9cea39ebf4bdc03bbf434b747866d4bf72b4ceefa639", + "0x89c4a74fa2f067e7ae49c84ef782c331bcc9245db7e941804e2e99d12e987b4d25cb827778ad4c3566c4fc68018650b6", + "0xa01d30cea7d01c80ff26650020fab02e78fc3842e2398a81b44b21d58d4e9816166ff4ed2418831fa995a28ff35cb6f1", + "0xb960c80b55a8845bbf24bc3f23b0110ca701f9544ab6a5bb7929330213cb471321e55c390ceca3e24bff69bdb0d331c0", + "0x802c5b13f22be7be0e5db11eb3be0f0ea7f9182c932265060ba05fba20ea093dd2810d3b969ee3e387e60fe6ee834e8d", + "0x92478f88ef7435d15e39a97916c736abb28ea318394b88678fddbbaab3eaf31776110936abad116a8ff6ca632dd12043", + "0xa6d3da0370c303001d5ed99d1db8bce1f26b0e442f0f042e36db9674e92dcd6e80465e772f1e669f99221caee3392fe9", + "0x938f04f70a8f947d6df2f0c0e9af3cce0c06edbb3c131970dd60884fc0b0a0959c504a2a36c3ff76dfe919905671626a", + "0xa7117e55224230822e9983df2132347eb7208cb6798f291df926ab51e04b1a1f78d5568c9a8924ee6f57426134360f20", + "0xb91074c77ad93fe48dc2b10c0c5a62ca3ab7d98345b919c52d84a9dc419b59fc1b267e1c2d4b2e120016ef84bbdb0cbe", + "0xaa175c6b6edf02fe8778762c9575581c0ee6efc9dbf99c291a41444a23a056b893be6c45333d907d0bbe9fb0eef84d08", + "0xad36dcb4e2ab425aa339ae464b038d550cb11186741dcf257f1b8b80ed4f32ffabbece45e2dc1525d4c3eeed819ea04f", + "0x91cb35c1ffa9cd5aebef523edb8325078da3eb5cf9e95c675a76446fc7692aaee6f949de064ca2f3e0f082cc3fa93e20", + "0x82622f9410c143a86bc4d756b3c7b324dc295231ce865de020d61cc0868f2c150a473cea3a5b756b36771ce1032415a5", + "0xa5c29996ad3a53468ece9356a5b4ccb68971ea1c89cf39644f1da2d4a477c2ea99bf791ef902b87c225d8c53d67c4c92", + "0x92893eceed1af34fa92b23dcbab175b6a0188a27dbac9ad3317c4e39955a763cb383ab13fb1c519cde311d8a4d12e8b3", + "0x8a093cb191b94b0200e38d31955f9d240e2be1edcd6810a2396a061f17c3ddc9c4f4d56766ddff4e121be7110e03b869", + "0x93981473df0cb1f4b47c7d9b64e3123dcf1593845b401e619f5d7c70b5dbea375d1ca43fca65845fcf0a6b2e0af43791", + "0xa6beb6b0697070f9562910add88d9ba91992f8da127b27be81868b1596d1012f09ea7ed601b4a6474c921a1a1a6d866c", + "0x92026b1ee30f2ed61c9f30337c3356844217926aabdff383c19ca3c21e0bc49811ca5b308012bee4ef250cfae1615800", + "0xac0ebaea6d35f84dac4ce648af096305ba68a7a0aea0a11ab2fbe3162075444a158433c98141bc92ef3b3400d6deb46a", + "0x83046f482dee24ac3ca83373f0d1b82ac1c4beda0f229a9011a81ec659ff5fc1fb105e219975b5c744308c77a24f71e4", + "0xaa5a312c47ff7248dcb9c6ffbe5a0628ccd565c07365c4413734d415cd4fb35772622ed833862dddff520a67c509c6a5", + "0xa02fb88805c34018ac33582e19ed0a7e4616acc3dd0867e5f21914c2031c05c6dca30b8b35b57c2b137750f3878a6f8c", + "0xa60528f1f14bf0c496491d46a0fbbd6c343e4eb3f1631e92f96a3c5e5c684091aabe5801df7a67f7c6dfd1b0d35269d4", + "0xa1fd8e7fad8ca05a340c05a051bb0eb4197eed345f4104629a9e38e234b09d789cc5537024615feb4a6177d32d39e39e", + "0x8e70e36c1aa070815440e19443f1f04aae23b1b59fdbcba43b47b94a026c82c8f66c5dfe54f826f4d95ee1930cdb8008", + "0x8234c1969fa7e9079661e4ca309b71b1aaa10f4372be0b963205c23a81f5a3d52ec08ba9ff65b37f832b52d631580d61", + "0xa18cb4134127fb37c4abca328cd0047378a2e1423490af2bd3eba9ffcc99ca81a3c22404c0886f21f65c7b93c41d7981", + "0xb46fa45fe538816de776eec086e040005706cb3eca097e290abfb6864e745c879868aac8361894f3c3564373ef9ad55c", + "0xb96ca43b96c59e95439f75d1e726a35a9362f0dbd34963b156e103e080a8126a8dc3501f9fd541ff3bcf4677f5c4a86b", + "0xa8e8c87c7301613818d57387009e601a7ab5cbdc2890f63d985c30c74f9cea2d0584c116baf0d9cd5594386ee93fc661", + "0xb47e4f1b9153ef0981f813948150f283b47a7346fd9921d51fe8e4daedaef78ddeb4fd467c2ccb7cebd9816243da1c6e", + "0xa370c202a99c8441ffe96fad0f801086d4d7cc7b960f6e98cca29ceedf492afddfd0f351c9c4d29ac008bc255ec1a2a8", + "0x8f5e6ce1655d1c059b006174e3f5a55c88e1821c97f9702ad8e8455d46c2a83ae4482f2d43edda74a835686ec45a8a15", + "0xa30421e694930a3b65d397b2720d5f8e1eec2b6e2bb5a28d3f9b0a84db9aabd83850268bae64c2b10e313cccf120151b", + "0x8abe87163046f7a9b18e2a3c0b66e258facc1b31431420e0b70354b7a60ebd250a784634a76692e7d6f4330b62114945", + "0x894f033cf077d4eb312e3258d9dca414356271abce1d6094ecce6d018c5fadb1c15d8d69451574ad0701a2876db191c5", + "0xb0923d64f88ffc872654e1a294bb1af8681689c21cf08f39afe51448a68e60a9a0a74ccce9969276a932a52c07d095a3", + "0xb9ca23b5be8725fae7fa710eefd45522889c50c29c26384e00b78a962384f0aeff9d15cb5910e9565da12a577eb7e5ba", + "0xb242ccf292757197a9f470f2d80ccddc48c7f1235ba026bc68a93be2738bc968e8a200aff3e2f4807216442eb3fc50dc", + "0xadc2c3b375b308524b79a024ff87d122055440643fea6fc0a651bdb312c7cbe6a456afa9d342bc76446d77d8daf08bc2", + "0xab645955356c2ebf2f3df9da275e01daf0b44a52afc309277d6d9ad1b05484e5ae0d9d41ad485fe481e5e362826a86ae", + "0x8de96ac587a4449fcc8b7fd0a51b4b5185d9c2eb3434f94cbadd092de1e26b0f6b3f7b15a37e8424b1429121ddca0ecd", + "0x94c70ad4e9b871566f3da98170b665a09788d421818299857cde0853789fb943cbcf7d4b2c95246ea7b72edc56a8e36c", + "0xb2574be63497843340700b701d5cc8be6d23125bd62058802ee67cce1f3b5f5602b27c93fea5611f27dc695ac563f042", + "0x869ec89da7850cedd88bcb3a50a15cece233119b31b64a61bf6b2310892ce42d8b473b584b11e61db29ed24ce8033f83", + "0x8fbaa269da8e28e9adf4c1b08f109da786dbe9cba871c32eecbfb10619b7a5d65a26f9bb33e201a8ed20b3de94003fbb", + "0x8bf7a059c37242caf7f821a6314e4e4adf799e0dd86b37892a7172598892c07272acebd05b534755c57b51556b2d610f", + "0xb4e72645fca459898cdd9214892ed08b5c99f82049c0a30d72bac0b9717caa9c6cc16c3dc7aa6ea4d42dcd2a6c175df6", + "0xa39170da87a3495da55bbb9701c5461f3403447174ed6a4af75712f7ba4ac35f51a4234bc4b94da888a0959ee109c0c7", + "0xb45675b2774ea7696089dbf7a0afe6c22e85fd0e4ef3db508fbaf96c9d07f700c991789206da9309fd291be696357c5f", + "0xb52899e3e3f6341eefcbe1291db6664bf3b6e8021d32fb9c3e37b6258a35c1da927747b2ce990937d6f4c6c3e7d020d2", + "0x84e5bdb3dfe19700d79dd3fabb0159ccfa084f7288db836c855b827613ce8071067c8d7ac5cc2b4e88ed7f84b690f6e1", + "0x801477d200b6d12fc6e0a9bab1c8211193ab06e44551e037a9b4c36fc2d4f67760b9ff4eba9a3bc7b6e177e891f64ff6", + "0xb6b71a5116d3c22af26a7530f535e9b7851f25a84e562a8f17a125d55b9b3fc1bd8cfe65bdcbeeb328409521e802051c", + "0x8687e21c34d7804c12489d30680d131ce2133e2981bfa993afd8a8eeda958ebd5e6881d342d725338659882d9f21cf98", + "0xa024e97a7c4de32b6383c34431994abc533ecdbd6be9bff836ec1af022f5a86773bf345c6f33273797a61fb70a8fd5d6", + "0x83f784f095da20ce5b31f54d6cb14b32a8a12675f0029289c9cd036b7c87a8077be2d04a62618685720e6ee69c875e97", + "0xb4e9dfe7cb9d9efd3fe00d99ae5e48769d4af4bf43d4e05c0b54c9cfd8bc854de96b8d3ebf4dcc06b9dac66b7471a0de", + "0xa08b79f9d4673afcf7f38b57f484f88feb7c908f597663a2417f92c348150c2be6b5603f914eba0d9d5bdd4e5c5572c1", + "0xb0eaf919589988798cb01ba0610cd1b7fa3c08715675ece8ecd5f9ef6d5d7b2c4c8ae1ea7dfd202237171aa3e6f9de74", + "0xabff99a98baae4dd0954052503ce81827781694a5ea8c1149f96a3adde75dc2d630e138598cd2ae7fdc7a654aa17df8f", + "0x83e369b8680d8b9d995222b033b4f4f3e3b20e782113c941325c7fa9c742feef8747e4a212d9aa23285a259cc4faef8d", + "0xb16d5855dd2716613697eba36e2fae0872aaea6999e91cf6552f93f9a0b85ed4f6ff922a91b50816bd6cf8e7a4513fc9", + "0x848373db600e32e741aa1d37726bbb28956783f89ce2d781e95fb1ee1adf4359968a141678af268077eae4c25503204e", + "0x93a0dd0fdac18a31875564505b4e28f9e8bb2915faae666538597731ac56cd77f23f2456461e2f672983fb24ad91f6e0", + "0xab1ebbe49fa56524b564bc2e43784147073e6ea5d27a9540fbf2e04d0f87c645ed2fd28b3e4982cc4c0af1734ee47a6f", + "0xb3ee30b733839edab6f61f0738e3f4afaeccf700d8dc7415684f193b36d70d07acd5780cf539f12e0fbf8d4683be773a", + "0x88388f2cbdec47a6b3ae460b69eb0d2130ac14de950c22fd86de03e40d02292bb93cebe62432da39d509c1289f785fef", + "0x9370c41a54b68ff486b4cc6329c3a851716ebf1d088d77a6c56dec93a18b8a77b596cde74cc17d2adb2b2f411a2e4bbb", + "0xb9083b60dc16531f77b05a955b51a237a8f8c0173d72c352c5ca441b55abbc890b14937e457aaec4be5cbbf80cae0099", + "0xaafff8f6c6ebaad952c65054dfc7c829453ec735331bf8135e06406b7a9f740c9a200dc48bb2175516b41f77dc160121", + "0xb43d31fbbaf10526809e9e5bd8bb47a76e0fabd7852ee7744404559ab89f0f215ff518f3271a6aa972a459cab82ac558", + "0xb581ede48c6ef34e678f91dc4b89507413e00e70712e3e8c32a80eed770ec8d8b98caee9702d068aeaca6f704be57bd8", + "0x8cb0a137e68b001a5ccac61de27cac9fb78d4af7b2f5a00b8d95d33ac19cc50c69e760c5e0330a85c0ded1edce0fe6f9", + "0xb947fca07c7aa6c2bf13048275402b00b77b28f1d0ba4b589fbcede13f93b5b931c588560ab8ceba23bb8e748031b55d", + "0x81753cced5ff819901740a9a584334e355b497cb699f0be5a52cd555a4c9f149535c7bb355b54407f7f0ec27de6c2e19", + "0xb3d59273951ce97838c4853ec329782a255b5fc7c848e7992ded1be28a5ada7fa3254123afe32607b9991ec6e0659b08", + "0x86b253de246f82be1cb0cef01e87c3d022ca1829d2cc7e6a160a5afbd3ca6b94d75739b122e3bb16f8bde28a8f3223ba", + "0xb728b659fa2d8487e061a37f7d14a4c2d70cc37497a8715695d8d332cb274deee2ce23b9b5f6a7408516c02c3d526a49", + "0x81277b46d98848a45abfbe39842495659dcbb80dee985a4fc91d77d52b815487aa8bb455f411fcce4c3879c7a075a93f", + "0xb05b6f1fb4a6e654f0ee6b83e08b58b57059bb0b7c490405bc8d963c4a2d6be39c558917977e554e1e9e3169961cbf3e", + "0x88f75fa7d016fb6442551ec071cc1e2beeb3ccd213d16d744f573a82f5d70f41dd1b18af71d5f9e73d87f2f6b7dbe889", + "0x81a46434f1bbd65a661a0ff45a0295b8fd8a42a7969c5953721bc98698b64bddee3f806876d1e9983063fdd0c11f99df", + "0x8b4f6d33c510a4c9c7d623d9ae0c9aa631fcb987704726b2a4d8519372123bce3c439202f25b5b47045ec14ce39a21a8", + "0x8d5112b330fb63cf6ef3d2164b404c14ff9907d685015701399a260951912b19b8f270f869df317e9050a127763d7980", + "0xaadab394e84dfb82db15ecd2427f39b62352c3e1647c3bcd14fb24ae830ad0116f0fed87ddb63963b424a4741961386e", + "0x81ca4e5600d00a3bda24cbdea7a532a4cbbd893c10e7ff10667c15ffa8138b91667abe5466b31a3dcdd60155c48538c1", + "0xad943af1b8a5fcfcf309ed8f2f916339f254cd555c71a407a47365a139306286a05a8314e1c70e20a65fccd75d36fa12", + "0xb16597a0b437060a390467bbfab94c0bdd695ae898894f4689f939e30cc2119cc08ecb594546304adf876f4e275ebcd9", + "0xa44a4e0a6693be356065891c27eefa040a1a79475be53d54d5fdcea7e0668ff9b35f850974000ed119f6865aa6faa721", + "0xadef27d1b6e6921f4eaf69c79e2e01f5174f7033eaafdd33edcfa5119af23f3a834ffe1bdf19576581b797abd1865b34", + "0x90c1e9202f3ffe28f8e1f58e9650dc4ff4dbc158005b6f2296ec36147e524b4f2f87f8aafc39db5b006fe0c491c92f45", + "0xac817cd54288b6f7fe6338415344fc9e7b669414051631ab2f27851c052c044be06bf7235d668e194bef695923256368", + "0xab14944ef653a14456d4ebc12e3196df3f1b4707c4e50b317b5ccc8ca3a0720f0330609f0e7e71793f6ca01583f38c70", + "0xad5353f2f380837e5ffdf079350b3d42935a0517861d03af98db5ed3ea8501abd68885c8c65f5a66e944b1874826a450", + "0x8b5583863f84af8443ce8970b02e26cc5d959e47efbf8a66a54106ab165f1f76b36423aee74c7b5402fd1c4d7c1adfe6", + "0xb3b46037eed9fc30e4f8f0da8bdbdcc40a38e22e876ce9fde981883017854aba82c18eb00887d92ad847d30082fe7271", + "0x98a2b6fc90b7ad172e4368c1e54675b75c8bf2096d91c9f2b60b3397d3be3b705aed5389845dbd68f0f84438cd0f7687", + "0xb155e800852a5f90a2eac69cc4483428da1dc2c31588a13c924e60a7616ce9baeb7d4b829c772b260277cadd8ed84719", + "0xb8b92c520a1302b0cf7d993a52e1dacd7f27bda9868d59c55687d995ae676b7070af4c0792a9bc1c2635d44a4fee01bb", + "0x96dfe9bde526b8fc829eda825f55168b88e8f4e43d4d708cc3060df03437b46e12a8ac70d7788aa75760f6294d3e84d8", + "0xa3fa66c54e2fa084ced3bd838614c6c33042f492a5745d167a723c60d5e7d6020ffd1747981a23f8b68df21ad8f0fa77", + "0xb573ca10cc41fc04a642f6f62c355a4fda69b94b8e95dbb02fd1ccce4bce1191356e1fd66d372159944eb36a7071f005", + "0xacd0a1c9abddfd0ea223eda1722aaada362d34234455bd1c6be115d41e535b16f12ca428da7820a757fa4c98884a385d", + "0x96f242eee99c4db383b8754fa7987c0c159652e1866faec905a8d3f010e0a1ad05bd77b9ea8dfd653738959180f58430", + "0x9215a9b672a5d6e435e0e0a45156e0e20f75cbbdf1d14940fed3ddb63d433bef643796c7a4fff881829ebb2b2eba9460", + "0xb8ad9bfceaf08dc5a874387219ddd1170bc3a5e25ed72d321d59ae713be5ddf9fdfbd3aa7ab163be28dfa0dd14614e19", + "0xa19a1050590bc500b32c502f393e407abc3d8e683d6f6b978873aff3e3299b18b1f6b59e2b0fe237d819dbdfcfdc98ca", + "0xa6870fb11d4429686e52e1f44c8dcfc7ea24a020df9570c021578dbc1f9bdc8cf797cb3a72d7fc52805dba35d59f2cd0", + "0xa7be733b64d5c06c127bd1c87250e42bfe30ca91ed8ce51e0b6e377f454e8f6fef7f99bff650695df2fd10c375da349b", + "0xa1b97145dab30330eea2cdc8739b2446a3704b64505fcea3dd8a9b4a72edf222e98d967d6fd7f76794acfd97aa091065", + "0xb2127049907d2a3b654d1c940b740bfba3dbaf660f86ea79c2f909af7c9fe2a07a1caeb1be12370aeffaf8faa50f1582", + "0x8a207701214bb28e99b0784e9228b1c34afa701966267fe7110f6f29f5bb41eaae6cdb98844d0400787978fabd224de8", + "0x9925147a383b6f5f814520220ffdbf20b214225882c3ef49b1a1ca677709176ec82466fb9c4be2dfbe5640afb63b014a", + "0x8416ad93871623fb555b5390b80de99edaaf317350cc0c1ae9d54d59517074d40061f315cce8ba2026d9c1e6f6a1009f", + "0xa315f943deebbf0a2cdbcf3f8323e215a406e9cbfbcc3f6288714cb3a6befb1bf71b2a21ff7a2ec4731c65044c45b6b5", + "0x8213e0c2539c24efd186ffa8b6dd401ad2233bc19166a0623b26dd1e93614bbf792823f5599ac116231e2efde9885709", + "0x8e5cafd2f34a127a4a896f05e4d929eef06972a1826b3566446942198df26d62f7679b987db2b3765d9d8058b1cd85c2", + "0xb5302b399c9cdf912fd59007ad4737255552663b1e56dbe64a7b2ddd88d2093c73ea319b45db2dd49d1e03f5bef1a0ae", + "0xa0c2bcfbed4b008e1a56e5d2f2419aa59d7dd0ebd990f1c18588de702ad0fa79f445d69965fa9381e700eda13b309378", + "0x80a44eea1ffe24c26b16b8e2e70ee519258b9ad4b3e83cc4e5cca88ebc48d0160066f8b91d0581095b0de2428390c8b3", + "0x84a90cb9c7d2f799f1c4ed060387a4b793ab41c5c3eaffd3b60face9b9c3bae93cd2017283bf3de1e3dac63d0d84dd42", + "0x81d22febca276a05ba9bbc5591ee087b0491beb35b4d9f8fc0d041d642a574667ddc57660b20f5c568f7d61fdcb41bda", + "0xa3ac965ac27a28e102a439b74fbfc157e75fd57620e4c0750a466165f8aeecb2191dcf8e656f7525aa50d9c7c69b0b5c", + "0x913c17434ff0d9fc52e2ece4fec71b37d4474a18f3ea26925c1be2b250434d49759f58033ba0fce1c6862c6197930dc4", + "0xac430559c151a5e461f67b49c7786c97e1653fa8698e9759ddbdd99f5daf17fc5a012ae6330739440880728f24eba7c9", + "0xb10d8e9f8aed9361b042d1398ec74364f7c7c1cc5c7f917060572761138bdbe89bf409389ee3879f93bc8032dd67b308", + "0x937271005a4cc6a6ec134870c1b56471aa84ed4f4af1b3d5f334bc0c42762fae0c9a6a2828d3de6151a76dad7b72781c", + "0xa10e4dcf51889f69e6bd4c052f8d4036b9571ced98a3d7d779cbcb9fa5c3a82228566ea7cc1d012bf56dea0a40c5a64c", + "0xa0ed026528d9a8bb3201bc9dcd20598933e8c72fd315deea8da63d06e97392aa729d98a55a8a60fa4d5573513ba5c9fe", + "0xb723fcd04cddbd4c36feae827a03746ffef251c4f4c55a88beedaeeee194430a99f566f483668a0d88b13e7a4a37f1de", + "0x84a2cdceed44828c7c05a6a762edec0165e434e7029df617d6646aba48776e6c3b823f40689cee136536f8c93e08a629", + "0xb786264e3a237ac3a1d56c9f4e87438dfed620c867100fd38b01287f5b755c7820937403bfb86644e082094d3e410a00", + "0x92cc35b2065fca157c7bba54410f8bd85907a01c9f760aa0ddb7a82cb55811d24cb4dc6b725367a6a1c293b809a48ead", + "0xa12bbf22b117f00164a42515bc57cc9e6c43cc77fb737ee3d0c0cad94cb50cd3847d61cab469cf8ca76f7958bdcfc771", + "0x85985b00de533bde2a757eddf53be79ea39091d16af3fc92327bcd1cd59bf2bf4411a334da29ad775e8ffaf3cea7d7b8", + "0xaf9eb24185b0d330d0ea1d0b0fa78af0dcf42ced81cb0128f16cafdea687a9c5582bb6d7c5744117b271cd0b3303f0b5", + "0x8c8aaa1d85ed6327f85d579767c7a9158d209171b3efcb3e8a9d9e534c078e821b6aade255101d2c9ef6d67ba66f10be", + "0xa450518a03ffb40e1df89e0f88fd55b5b06f4872cdfb7ec55f40dc40d9424b3b289866336c195bdd54597d95569e0096", + "0x81e61cc69f93c435bd77f155e80626a9c764dd92b6c76af15c41346527948d8a6ca87d6351a0fe7987e2ee3aa66a9625", + "0xb615e0cebf4fdff4cb23a20c8389c370915ba26aa703b28efe4ab070b1603d1c5b6541684acf46b52a915f6aee447539", + "0xa7f51885c7a71885cc84ef734ecd107e8bf5f7a25131415f671d143cc1de92859e65001125323c7985799993af6c410d", + "0xabfbf7a46f32066989c32f774edcc68163f085ca81e94fe8c9fb32f8d451bbb2c20ac45cd8d97f9e618ab40186933b1a", + "0x8cf35a522b5cac1934004aa9dd236bc77198d43272888afa860cfc79b4b28dabf7a3c74098f84510897566fdd609aa45", + "0x86aa927df78f7a06a4985eb0a4f0b93529cef14f9fd2812d46abffbf25e618ead14d99c70e3c3bb2e17f3f7fabc9c264", + "0x860f1b4f4a398e9a8bb4739587cf96979cfbbe1687b7e91e5bd1198db726391b09b1a261bf12e96698818f60b5bd3537", + "0x8e7c4ee19ff115881051e8637dce1f5d6c65e865d0c757e8ce41b6d7bcd86c7070cce60649692bbf28c868c7e2e1e2f4", + "0xacf7ba01b0220419f09169ac8d16e5cc13dce08e88c90b8fdfaa33aab417f011a20b79a178d8a9f7211589d2e0affd7d", + "0xb404bde8e715aefbb9f20a353b911b79173ef3e2cf0aba98b5ae6190b90597d65043b0b4e014ad9ea6c77da2d213ea12", + "0x97e3615d1c77a402253bb55da2d1cdf82de316cefffe42b1022c94b4818d6dc4a313731db85321c537914bdf716a875c", + "0x940e950b96a4096a578c6874d747515936652b9b113a5f27f5a834a610867b05f9881e2679b0b289b8527baa0009b6dd", + "0x8de15a13ca236a3a285ce6e6826c502ae7365bbe468b6e8ac67b15b0bb49be0e996f1eec81ef69e4b7f54f8e4779a054", + "0xa12244777eacb08ecd42b5676b3a51153022ab97e9353ace0f47c6054c22de9ba60d2a60f59a36841c2a791cb1b7c288", + "0x94f7580203e39a2642ee2e7c969b9911f011d7f3a90c398e1302d26edb3df03df1d0c43baa1c6cf90dde95296d49e742", + "0x82ead33144aaecab965faf63af384565992f38fc1066e71e33d53f43ac93892e27fe78c4eaca1cccbc53364e26ff31e9", + "0xa0c129e9706d354249a7f8aa664ccd7ede89aa1445c5547410814b56d10dc086720953363ab1da8ff5f1ed5d8e575104", + "0x93b3057bf3f74edc95237781ae012cc4b1d3fd0455565ceaac7110290aa518ac32478ba4eb9851555fa87270fcc84f1f", + "0x949c2fd0b94f31f7cbf00c679bd3f6ec1a2f4056654708d39edf1a450b4e19a6e251d0bb24eb765087e698f61d3fca2c", + "0x99fd2e50e211ccb66b895eb2fc42f260f3ad5767f04c2fe238b81dae98aa6e3977443a51f4fe7b43f499caabe45699a5", + "0x84fe19626503218f327b5325bfd7c0c3d2614b47d34964aa0259d564e769c6c81502132cc1765b0b31fbe39852706927", + "0xb43287ec29d9010bec4284de58fed48dd1e129bac79f09d45153c9949131782f77b11b0c9f8ee06a39e5e9bbaa8e2c6d", + "0x908902f3ed45482df2f94415fc8e5a308057a40c8905d7cbbd58ec4848e19276577b7f7e69e5e684a8b981738e10f7ef", + "0x85cc7d9c1eae372b4f88758cd6e21604b4bc9f0794e1e74b6d9de96347f81944d01331385fae7a38e5f6096c1dc23465", + "0xaf60288c702082fc258b3dbd6952c6b75c1641a623905f491b1e72f49b9d39b33d150a336450abd3911a4c128166acdf", + "0xa7d8ac7e589558c4014369ab6f4c1f2196205b03e4278152ec0dbbd7ba54e803c3369a71d364a773aac8dbbd117e4a13", + "0x9833aed34e48c206e9328073597aee1123f5bec085339b4e6839a389a429bf3042798a31fac1464ce963204adface76b", + "0x84631a4f012bbb62133030224b57deb32dcf464cacc8ffde7775adbe68707263ab5527a1c75e597e03aa703ba658b889", + "0xa686a61f6467858a2a4c13e70ad81b1901290d3e51bbc0c6e366f9e652f575e91b11c75f640ccef8b0c6c1b05a43c9a0", + "0xb585f0ffd5144907703b41539bfad7f9f058f5985f63db911064ba6b07af8da2796b84b16db42b8d11135c3f846cd9e2", + "0xb525539516c7bb25f1d7e165f269dc8c9eedbba74df44887e178ab8fd798e2a31f39812ca922d6b64d91564f14012a64", + "0x91e480d7568fd2fae39c35b0a8d623e66a3160fee1dd4e9097255004938b11ac1cd3918dc6a1e5fbcb700c95a547e5e8", + "0x936ef55c69b842b6177de71fa48dc5442bf5132116b214302f8f242ca36a273a6bbfbfaf373777104dadbe8e7da5e970", + "0x8e950c0f6688abdff8a3b8bd77be6da6f2565c7b55711f5860ea62a3ab1d51aac31821c602bc11a45e33c69e7dde3ea4", + "0x90eed4595104a0527f8db1e028ff622ff70db4eae99cf47f6c2a0246ec7b103570a6a9a877e32e9647cc74969006743d", + "0xb756344f6c4ea05b792e416d9bd9ce9dd4bd904e7622761f28a85628506bfc9d88a25e5f04db62fad30a92fb1d8d8556", + "0xad79ba76534c1a02ac3e9b7308d390792984cd75b7e1d0e5e4ff123642d99d4ea1825643091aa8117336333c40d5bd94", + "0x832b08144887de0c0341d84f6945450af8d7a4eb32367d7703118186c1be525df9382ce61fed5f3b65a0bb3449185f7f", + "0xa322fb944e46d8e47994820890c94af423674716da810ea1da71e0a7733ad72c22114ca39a4b59c98ce4291a5684c154", + "0xb982851a65140dbea79bd3b5487e236feccee051deddcc17c2853032efca289ddb6eaf64be3dd85a73012fdbe9d2d4f3", + "0x8eed5e230e201830b44b9fadca4e156fe1a16bf840cf29da0f381ea0587b20c226de2465c67e6268973e776809af68e1", + "0x81c8f1c04490f36e41a53ee1b5185cb8adbb37c258fd6c3be8c56835bf574c37183a94d55b6554fca35d6e6dd9af0133", + "0x8c4928724107cc16d36f2976677eac0b852fc4c3c0bb2f9cd4d59cd24a113faf33b2faf405c3fcce25be51d41e42c2c4", + "0x8e4ba842636fdfc4d71f0983538ea5037d420acd26abd12efca48c252eea85544b2fa9fccdfec4e7c2a6359baffa112d", + "0xb4315b84700e26dec26f3488d308430fdff4809c10d4c24309627911cbb769ffaad0d1ecccd622dd02194eaf5ba59f91", + "0xab888308f757faef32648c1db01650dbc9aea248b09d06e6efcc996d395f48ec96f2d54a02de441d753fe8737862d991", + "0x805094cfd77e207d5c75f3cad99f41f763ec15443052cfd758c6a82ba422d831a1103a7f9b100da49c28198279c3d3dc", + "0xad857f33243e4a2cd2a773700def21fc7f94939d1a6d2c2125ecd58fc206ccafb07a2c02a1cfce19857d3654aca2c70c", + "0xa4d12d40149953daa70b89a329e918e9d93efb4e8004a9357fe76682dab9662c8507e16db83e849340f05cdb4933a373", + "0xa0dbac2ed4b5d03606524245e8a31080eb5bd3e9a0c51dad88c3b18e3e6bc5d64953a81c8e60425b80107ee6b62b1fb4", + "0x86da05355900f327164a78901f6e3db857531b33b1e855df1a67a9ba222c6b05fdb6b0ffbacaeb1ba5b45ff8979b6b68", + "0x932c9873aa3e226dd922b5a616c75153bd0390ce8f332a414b9c8cb6606c2501a37a2aa88097bc7d8e2c4261706eb38c", + "0xaccd9cdf07ccdd42033ce3b105e00bfd39e2304b1e3d66f8b1128645634452c20f759ec45adcef2fdf04408f62c4cc04", + "0xb75cfdfc1cb48918752eab17eb579820ee6e71e6667abdb64df834ffc8c1362fbbc23ca2c80dee248fe1fbb72d87dfc8", + "0x88b998c73b00638fde7d3dd650a08c5ab996dac6ac34251337fbff3fb5ae4a25dd20c1a16c987ad7ded19eca23cea891", + "0x8afef0956c942571a27f504553fb312cca9e50ce41b44e0466d0516c5abe4d8acf4594cdb03b1ccdbe3f2e6a9093b713", + "0x9042cd83c5ff261e9ebda26398caa16cac2cb840d19062fa8ae50e044c27104972948318f4c866dc4d578798272d3e49", + "0xad536719a64570a2cd1d72b6590ea1d02c8c49f259a7867be26c8191445165954bcfad50ea12688ace3fdfb0e98143bd", + "0x97c86328d63d297b6bc9718dc1ad5a05b908a750d1c455c700d84315589128ce4eea958aef2bcf0fcf4adbd8e3ce58d1", + "0x8e592cf0802e6a9541eeb654dc55055e11f3d757847285197132935ca35bbb1a9156829a39384dfa6f645ff89eb36738", + "0xac16c614998944f77590bf3913a010e13f2d3bbf6a172293baf5983506c1a2d89989fb72e598f5bba1ea10a691377c93", + "0xab8e6f5b46baa6632de3621497bcbdd584decb999fe7d8a3364843a1e0b76497600630b6a24dd30119d8bcbfca29f335", + "0xabe1d3af5279e60122d9cea8cc6581c819d7a0e20e3715da0f6da7e02d13a7653db643bd946e2fa9ba338eca81fbe140", + "0x8c33bd831ecfb18d1d0713e16beba768e9c42df62170c1f8a16764912be77f2ac5915623d1d25e8c462aa9c2f6669ca4", + "0x903692becae4a6409f7bdb127d9b11de57a5739fe24218dcbaa0092648d5332dfeef29a908ee9e43e5e0a51a4c3639bc", + "0x92591e90347ae286acd365eba32cd9ad8f20f4c9cad2dc579b195147ff290adf0d776bcb3d4b04a25d68a941fc0c781b", + "0xb64bbccf860299aec16e1f95c768a1f337c740bde612e6ba260e393edb8b04540127194761c42597abb9bcb771c576c3", + "0x9194f056ccfdfeb78a11c5347e2255d7a7ebd1251f9aebc0b58feb68d3e03a7dbbb74e3ef7309455853adfb4694bd01a", + "0xaa4f15f6d6a53ae65b7f6f91e8981d07a5919d2138679a561f7bb608dc4596e45ca06c9441d51fb678b2ad89ae7a17ae", + "0x90e3d18507beb30bde08c5001faf489a19ab545c177efb3f73fbf5605f9a0abcdc8bfbc44f832d6028e3e0a834bea98f", + "0x8f31dc0118c8c88a6e79e502d10e57652b7aba8409a5bf572ca63fed6b7cbad7f28bbc92ac2264f649792fc1d0715085", + "0xa307d1067ea4c56437b6f8913aa8fcbf4a24580fc1e3336e7f6518f0f3adb9c4733090e459a3f737414ec0048179c30a", + "0xb7cc41fdf89595cd81a821669be712cd75f3a6c7a18f95da7d7a73de4f51bb0b44771c1f7cd3cd949e6f711313308716", + "0xa9dc74e197fe60e8c0db06b18f8fe536381946edecdf31e9bd90e1ebfcad7f361544884e2fe83c23b5632912ec284faf", + "0x8b3e1e81326d611567e26ed29108f33ddb838c45bbd1355b3ae7e5d463612af64b63fff9fa8e6f2c14c8806021a5a080", + "0x92f6537bca12778866335acc1eb4c3dfc2c8e7e5cf03399743dcea46aa66cac92ac2963b0892784263ad0ebe26ffdbf6", + "0xb5cc0061f7a3e41513199c7dd91ac60d727366482a4c7328527f7bd4fc3509412f711bb722b4413b3736a219b843d15d", + "0xb3e9711d68d2c6f6e2cc27e385d5f603d9a1c9a96edeefa1ffdf390439954d19504d6aadc566b47e229ad4940ef020d2", + "0xa09d0d3f0e5dc73a4a0827b72710b514bbfce4a7fcd5141d498a5aad6c38071077f50d3f91af897d9ab677b7041dedda", + "0xb177fe260f3b86e9ac21f1bfbe2682ae5dd8c9aecebb84f37054bdab6e39094e611ce582210ceeddde66adf759dadb6d", + "0xb0ac6595eba9f5dc4b2fd21856267cfbcfb5b12aa34ec69ca32b80071c5b652e85c25a224d80443d503bf25fbbfe07e9", + "0x81f3c0e11b196bd4a2e8f07f8c037002566dc9037da81f3988add458a520c24dd1be3d43d851e28c0c6a85de4b57a542", + "0xa44308c95615f7fedb2d2127012924468c015df9f48359cc2e36ab4223870b0bfc1e9040baabefdf5266f93afaad896b", + "0x8493ec4c32d5a13b81039f1b436eb83f259945dc950e3c6c2ccf5087ec56dd2f60890ed4edf01728b6a54950e19b35c6", + "0xa1a439ec2a6a95bdac9aaa925ff337ba956c0d236ab5318354270e73ed6b73b4ae2d27b4c1686cf97b6526d04e65be81", + "0xb4659b7b53c55a4b2bbe210b53520b392f893500e18990d843b72d7379d45fb44dd1dd2184348d6fd853d6b9ecc6b7c6", + "0xafb2c68d75d00130b0e1b4f250001920213121791698ec04262db714cf7b1408d39f6cc10421f954845aad5b8250b77e", + "0xb22b843b40a97210f94043b552f348f66743055a3f274856a738e7d90a625b80e9bbb80cbbb450e1666eb56b8bd5c60f", + "0x800895ced82fe13d5fff65a93b0051c3df698bf1221b682accfdb63e3970f669ca37025750697f4e8ff2a3322ad57be4", + "0xb21f598c50d7b9f4a584d548f85e42055ef8e24991906d973749090261584c7f4f5e984b528926f7e75375dd84d51af8", + "0x849b1c68192d18274598dd6d0bf48fb5ee3b1ba25b331cff2d06f345bef3bed49760ca5690848cf33388f6a9a32cd646", + "0xaeb6fd9478b10ef456f6bbb1e6dd19b14475e65497772d12cfc097948383d3fbd191bf95f046b8bf1989954118e483d0", + "0xb1b5e0ea2835f7fc8b66e7731e392b43d16cbce04b52906b6751ab1b91978899db5fecbdabc23a19dabb253005468136", + "0x91b6b1284770cf6f7ef35bc0b872b76c7763ffcfa68f9c8cfabcb2f264a66d47598bb9293f6a40f4c3dd33c265f45176", + "0xb9ffed029846487c2cfb8a4bb61782bd8a878f3afdb73c377a0ebe63139fa070e3fcdc583eec3a53fdc5a421ff1fa877", + "0x998007249d041b0b40ff546131cfc86d0b3598dcedf9a8778a223f7ed68ba4833b97324cbb1de91292b8ff51beab44b3", + "0x8eb77ce9e0e406bf6f002870fb2fd1447646dd240df9bd485f8e0869298a1fc799d8a41b130c04370e9a9cc5c7540ca5", + "0x853db8157462c46f2af7e8f94f2ed1c9b9a7ba2896b4973296898ff3d523d6e29e0b63a5d26cecd5e490b33c87a4cecf", + "0xb1436b6f3278768f0979ee852944258f2599977d255bea6fc912ba17c5dff5bdc850cf3e1fc52be9d6d188e868670f4f", + "0xa76acbc5832019b3b35667ab027feff49f01199a80016620f5c463dfcbfb51bf276ed17b7b683158ba450660cc7973eb", + "0x94540cdb051faf3ae8b8c52662868c2dab66bd02505c4f5f8eb4d6b2e2e5fd9a610890c5dcf8fd887eee796d2b5753a8", + "0xaa35099666bceccf4eb3b65b13bba88e30a8be93693ab6761d8e5523343e8d6dd42d977e66499352fe4e9e9784a1dd0d", + "0x894471aad17be54319083c4b5e40adcfacf7c36c4aab0b671030b7ef321c53590a25eccd836efd20f32a93185fd315bb", + "0x8f52a9f705bb0dea958fcfbd52e2b6c08ad0f89a07a6b2942c1b4c37eead0d97a38a9e9aeb08d5d59b7fa2a9347f738b", + "0x9031c16b4f936c9cab55585dc5064739f696c3347ee2c0792320c9f749e760d120e396e8485ffc79d81c9f3337ad3d1c", + "0x82090a0d0d9b05459ec1c328ecd4707c333b784e3aaa0ef0072cee1eac83f9a653a75d83b9f63512a8c41200494826b4", + "0x92c3a9553001f9ea4d67236b8ad1a33275378202cc1babc03f313895458f4b2549bfbbbdd37bfb8fbff0decb6b9f820a", + "0x88651868f4da37338a22bc553388df5dd1dd0cb78c4d7d07c637d8f6faef4bed72476fdcd4304d5bedf3514011135f08", + "0x83fa0141bfebd88063f1d787719721b4c6b19ecf565b866de9d7d5d1a890e0e3d859b364bb65f8f8e688654456a40263", + "0x90a7fab753e5d56dfc0e53a6b4e6ab14508220f3a62b3f3f30570c4c9ad225e74122635826c92e8e3227ec45e551432a", + "0x8fa375b0345bf6e5e062d108f9feaec91029345ecac67ccf1264eac77b8654cbfdda1f10579f481889c0e210254eadde", + "0xb83f06116da9daebdb013b26724523f077debaf6bc618b48a7a68858a98d275f7899c4ec73a0a827219b9248dd81c8c9", + "0x8be1cada55e0c5ebb4fd460b2d209ae5326285a20c8bdd54ed9d1a87302f4063c8730bfda52d9d40e0d6fe43a0628465", + "0xa68ad6f813743ec13a811f2ef3982c82d9d9ac1f7733936aa1e122f8dc7f4a305cc221579ab8fc170c3f123a1576f9ab", + "0x8878f1128214fdbbb8a0edd85223741e021508ab6d36c50d38680f2951ee713ea056ed03f62b9461897963d50ceefe0b", + "0xacc0d43d1b0260528b7425b260a5dea445b232b37240759fc65fe26f7c9d8e51569c5722bc33e94de6492f4ba1783504", + "0xad80b1dd717b076910ee5ceabcb762e75e4d094dc83b93b65c16de1f75bc712cef223c05d5579c1561829406c07a97d9", + "0xa6fc9803f9c09d95fc326cc284f42ea5566255eb215dba8a9afb0be155ea11bcc55938b2d16f01cd2f2eda218c715efb", + "0x83ad733dbdfbaae8095a403dbf09130513f4ed4f08dcf8dd76ce83d1ea72999b7eea3a7b731da0d2bc80a83c6ee0e3e0", + "0x8748912fbd08cb34a85416b0937d9c4327e9eed20d6e30aeb024a7253f14f1e0d774f3326e54738d71aae080e28da0fe", + "0x8997e78d8acf23051428af67183ae9b2c4aa42b503745ffe33df35a35103c589987e1473ab14dcd28ee78ebcb10d8e95", + "0xa2f340502a7eb3c4a36412e6f028321372c4fa18a4743945607424e932af1271fa3e6598a162c872072529576eba6283", + "0x868ccf19b5044ab93b45c9ed3ae34fcb504fe1453d6c4a1d12c325032cf01eb90356de82080ed897e97dba13cae33a02", + "0xac8867005fe4354d67aa37b866a7e581d2f94f7bd0b9f4efb5c2d1370ec13147a60692051b02fd00ae60b512bce9b1ff", + "0x8fd01886b046819c83c12bb779e432b25ba13713f9227be702074ec3abb2bba6be37220a0a26a4bd4171b99b14e32bc4", + "0xa128981ed199f92b5959975c150a93a62fec50b61c80a3fa0634d90fc8058f76f5cbee77aae6889af12d296b30e613cd", + "0x81fe618552ff7a36c9235c6d4066cf2f930b5b38de4089e18166e4a06ca5723eadd1976d25e34b74b3ce942300b23e5b", + "0xab1223ea049e6e0fbf9b611de7fd7c15e5e9637cbd73aa0e36aea08a7503ba6804f2aa807186fdc9aa7f4f9195f72e24", + "0xb97285286981b2665f898abc13f3243b63005bef8db4cab3f658bf6167036b61af400f08db0fc3c640a9c623b760690d", + "0xae3ddff7c1f0fbb6a13dbbc667a61e863c2c7c51c2051e33cd61620142e7e30a7e0c4c1f8fbb512aa3a8640267c6ac26", + "0x99c2a89d5bef236060e51c4f952664094c20fbfca647e5d24a55c1fb8df2f3df58244fbbf3635db07b1c29ee3234fa6f", + "0xa5010764d4b9cd3b410638334d1f70c5f4843f45b4f4a9316aaea5fbb2c510a97449dd7a07b49f47334a69d37d9955d3", + "0x86706d011dcdc9e9d165d01fea1df68dd74bedaf15a39f92893c030cafe96f4498c4c1fec2d2136354341b3f440a1462", + "0x88fd57eb62bd7dc35722f3a0576c2138403a2f663a2603482e8974a895cf56ddbb02657dc6b89eb2cf5c1f9d1aff6426", + "0xb0dfd4c68e3acb6bb8a776adaa421fc5e268ed4d5964bb90a727091e5113b55b3f9c6d33cedb3ee47ff7acc5df8b1749", + "0x93b92bc942e1a636fc5c2dc1840de5faf158a113d640d5a475b48e2c56ccccaf9db0e37e90ce74c4b3f5c9ac3b2eb523", + "0xb29a16fa1ea95cbfc1873c435ad40dc8495ba6341801b72bd95d908147dcffb1b4bb426dd635f3af4c88984f56594dd8", + "0xb8f367105e1a2d554ac30200c66aeb579d3d30a8953d20fb6ebba2d876ec39c52ea5d654f1bb89b8ddf3d9d651f31cdf", + "0xb5fbc228c983d08adf8612eba5b3db3acff604439226f86aa133b02cce4ffde2f977c8dbb8b446b4375673f71634c89d", + "0xa399bea37d3056e0559f6644faa0af93063b4b545d504d7e228d3dbbc294af83d3c4cf37fe026b63899b4e7d50fd08f5", + "0x928ef411a36414b24aea26fdbed4bdb1bb6bdc2d967e2553ce54c7c4e077e76869cea590257645c9129dd55ce025295c", + "0x9684a4adeed416a9ce82ad79b55c4a3adcfbd43950bc442ed8a340381caedb70f4baaaf821e3a152f483f965d8f56162", + "0x92558a37f214d6f4cb6d72cd2f4ad24dff9d17611b9e4a41ee5c741a5d1ca9e4053b0584533ef4da206110b5dc3e2a35", + "0x973bf0724d1785cc5e85d2a8ee8c354ad4cf557217ced0b7940f6f064024c20b2bfc5b144c820b5083da4bf70690de4d", + "0xadaf1389dfa528210ca9c2657c5ff10d51f7e3b18e93a59c37211be0506c3576cb2c04ec80cd0f82605e53c5a3556620", + "0x85b58b223b09fda6f3ab674d75e780c49eb2167837243df049281e8f4fed653811138b398db9cdfe7405fdb8485602fe", + "0x849504d3db408d80745a07e850b0a804607b91a59922a5d3bc40da2748c029c029419cda38d2a4485cc0824c6b2504f0", + "0xa3f4afcb353bc2582a02be758ebf0cd18752410ca2e64231176bfa23828423e0a450a65f241a9ed8eab36cae8d9c567b", + "0xae362786cdf121206537af9590d330abbc6dc328b53cdd145dbed0e5df1364c816aae757c4c81f9d619e3698dd32bcdf", + "0x9024cfa5b0101eb02ab97866d5a3832944e5aa6888484cfba3d856576b920787b364fba5956bd7c68a305afedc958201", + "0x8a116df09fed923acefb2aecf38a4fbc4b973ee964d67f03791d70bee6356af43ffca117d4e9463ffaf0e0d5d5e5a69f", + "0x9163016175c73f1bbc912ddfe03bd4e1db19c64951c8909ee6befe71a1249d838e0db49f03670bb4c5c9b2ab0fb4fef3", + "0x8f6357318d8d16e7240a02b05ce5a4976b6079d49daa258789c6dbf4a47950ebe9de6411780fab06c7c1f35651433380", + "0x8e63cbae8be7341892dbedee3111adf0307c4ee9e375181aa53478f5ba9cdce164d6ae890e5f480119a3a51c6e989165", + "0xa9782f30674a4874d91bfba7eda63aeb5dbe66b040c768d6a925d8ee135f0655ea56276b105239cc0668fc91ddb68cd1", + "0x8d9d94b61ab84ec08665cbe0244ea41756785df019e453ef078c19380bd44c39d2958e8465c72eacf41eed5696037805", + "0xb1470e6f5d2e314474937cb5a3bc30c8bf5fc3f79014945f6ee895fe20028ffc272f9d3a7320aac93e36c96d8a5454e3", + "0xa444911bbafc71179766594f3606b6eaff041826607fd3192f62dec05cd0f01b78598609a530f6930e8440db66f76713", + "0xa9823d44e2638fca7bcc8796cc91c3eb17f46ad6db9f7f6510e093727614aa3a4f9b2c4011ef91dc1c2d224d08d8d05b", + "0xab86020972c359ab98294212558b4b14862040139876c67fc494184b5c9bcea1dbe32fe0c8dd9e60be9daa304acd599a", + "0xb7e5cb685bbdcfdb1e48259a5d68d047846c8a35c5b3f90172fb183d1df40d22eaf0edaca2761a07c29c577000ccfed0", + "0x8c88319dae4b28989817e79e6667fd891181e8d2ed91b9c6b614985bca14b12982462ec58b17be0463c24bbb79dd62a1", + "0x8c1c6867e7107fb2178157c991b9c8b0f90c8d57a51220bf3650438ccabccf62da4db8a9916491e730ff3d0c106496e3", + "0xa00a79bd58da6528b9af033087260f9f3d00519eafb4746b355204ee994e89481591b508eaa5402821083e250d38467b", + "0x8785abd7c37690f6aa870ee5c799eef72e398a7898b6767f698515be277b9c2fc1af12ea89b0620a848221343a3b5ec3", + "0x8aadae68543db65cef71d0e230a09508d72061398ef2fabec0f856aacff2125b79c70e620744aaf331faf3dfc8afb9bc", + "0x8ff0cd437fcad9630b8a2333176a55e178db4142ec841581590594d74d5b53baeac5fb903fdf7bcf83e245b95b58285e", + "0xaf274e8fad6b190be4e5dc92d2705ba6ac0d7e1ea29e958a5cdd4cb764de46a56d9eef62c999a16e7c50a50b2d9fe3a8", + "0x865e6ec7d1aa848786d6a7a4e87a24d442311f0810b01ef5a74928ab59fdfd651e48880b49680047e5b0df6b3c7c2ecc", + "0x800706baaeb35bf3bc33bdea9a8b5cb00d82df407b3b7e1b781a9359cf44fb410ed311591080181b768aae223d9246aa", + "0xa9496389d0780b309c6998374ae159f58a8d0fe9a1c24c36cebcb45b27d818e653b51a8ee1f01e30a9b2c46a548126ef", + "0xb5fccf4fc3186661939fbee2e89c2aa0e3a6ad4907bcc98c7750520540c4c183b1bbfcdf47f2f1c5e75c3a30cdf30c75", + "0xa90028e39081b736e628c2230cc1338f9210ed01309a40fdf08d39c10cced2cdf71271013bea6dba3a0444fe47963106", + "0xa0815cbb325a8fecf2e1bcc5046644be32d43a8001bd5d8cf0022e4572cd0d481b3e717002f7ab21e16da5f5d16886d6", + "0xb2024787fcda52abc4138150f15e81f4a5be442929b1651ddccbfd558029912be4d61c3c9b467605fff640edf7392494", + "0xab5aa60032304a584cc9245a33f528eae7157808dedd1ad83ebae00aadc25dbe1cd5917eb8b6b2c800df15e67bdd4c4d", + "0x866643847ef512c5119f2f6e4e3b8d3f4abb885f530bb16fcef0edb698a5b0768905e51536283925b6795a5e68b60ddc", + "0x806aa99c9a46ee11cc3ebf0db2344b7515db8c45b09a46a85f8b2082940a6f7263f3c9b12214116c88310e706f8e973a", + "0xa6eada8b9ff3cd010f3174f3d894eb8bb19efdbff4c6d88976514a5b9968b0f1827d8ac4fe510fb0ba92b64583734a1e", + "0x98480db817c3abbc8b7baedf9bf5674ec4afcfd0cd0fd670363510a426dad1bcf1b1cb3bf0f1860e54530deb99460291", + "0x81ab480187af4a3dfbc87be29eca39b342a7e8e1d1df3fc61985e0e43d8d116b8eac2f1021bde4ae4e5e3606c1b67a21", + "0x8a37df12dc997bf9b800f8fd581a614a1d5e32b843f067d63d1ca7fde2e229d24413d3a8308ec1e8389bf88154adb517", + "0xb045a55ca0bb505bd5e8fcc4cfdd5e9af1a7d5fe7a797c7ede3f0b09712b37f493d3fcf6ef0e759d7e0157db1f583c95", + "0xad502e53a50691238323642e1d8b519b3c2c2f0fd6a0dd29de231f453be730cf1adc672887d97df42af0a300f7631087", + "0x80597648f10c6d8fcd7421caf4e7f126179633078a1724817d2adc41b783723f302eabc947a7ba7767166dacf4ce8fa1", + "0xaefb56427966c81081999dffbe89f8a0c402041929cd4e83d6612866cfbb97744f4ab802578349fbecc641fa9955e81b", + "0xa340e493fb3fb604eab864d4b18a6e40ba657003f1f88787e88e48b995da3d0ab4926ce438bdc8d100a41912a47dace0", + "0xa6d777bfc0895eac541a092e14499ff8bf7156689d916a678b50a1460583b38e68158984bea113a0a8e970d8a6799a85", + "0x90ce469410f0e8cfff40472817eb445770833cdcf2895a69bc32bcf959854d41712599ceb2b0422008d7300b05e62e02", + "0x815c51be91d8516d5adc2fd61b6600957ed07cf5fdc809aa652b059bea8ed179638a19077a3f040334032f0e7900ac8b", + "0xb3ec6c0c3c007c49c6b7f7fc2ffd3d3a41cdff5ad3ac40831f53bfc0c799ffeed5f440a27acc5f64432e847cc17dd82e", + "0x823637abeab5fb19e4810b045254558d98828126e9a2d5895a34b9e4b4f49ab0a5b3ee2422f1f378995ea05df5516057", + "0xac05412bcf46c254f6548d8107a63928bba19ab6889de5d331eb68cf4d8ce206055b83af4cb7c6c23b50188391e93f84", + "0x88514163c587068178302bc56e9a8b3ad2fa62afd405db92f2478bb730101358c99c0fe40020eeed818c4e251007de9c", + "0xb1e657d0f7772795b3f5a84317b889e8ded7a08ea5beb2ab437bebf56bcb508ae7215742819ed1e4ae3969995fe3b35d", + "0xa727d4f03027fe858656ca5c51240a65924915bd8bd7ffa3cfc8314a03594738234df717e78bb55a7add61a0a4501836", + "0xb601682830fc4d48ece2bdc9f1a1d5b9a2879c40c46135f00c2c3ae1187c821412f0f0cfbc83d4e144ddd7b702ca8e78", + "0xb5cfea436aa1f29c4446979272a8637cb277f282825674ddb3acac2c280662fb119e6b2bdd52c4b8dbf2c39b1d2070d6", + "0x85c211645ff746669f60aa314093703b9045966604c6aa75aae28422621b256c0c2be835b87e87a00d3f144e8ab7b5f0", + "0x867628d25bab4cb85d448fd50fdd117be1decdd57292e194a8baa0655978fae551912851660a1d5b9de7a2afbb88ef5c", + "0xa4e79c55d1b13c959ff93ddcf1747722c6312a7941a3b49f79006b3165334bab369e5469f1bddebadb12bfaff53806d5", + "0xac61f0973e84546487c5da7991209526c380e3731925b93228d93a93bce1283a3e0807152354f5fe7f3ea44fc447f8fe", + "0xa1aa676735a73a671a4e10de2078fd2725660052aa344ca2eb4d56ee0fd04552fe9873ee14a85b09c55708443182183a", + "0x8e2f13269f0a264ef2b772d24425bef5b9aa7ea5bbfbefbcc5fd2a5efd4927641c3d2374d0548439a9f6302d7e4ba149", + "0xb0aacdaf27548d4f9de6e1ec3ad80e196761e3fb07c440909524a83880d78c93465aea13040e99de0e60340e5a5503cd", + "0xa41b25ae64f66de4726013538411d0ac10fdb974420352f2adb6ce2dcad7b762fd7982c8062a9bac85cdfcc4b577fd18", + "0xb32d87d5d551f93a16ec983fd4ef9c0efcdae4f5e242ce558e77bcde8e472a0df666875af0aeec1a7c10daebebab76ea", + "0xb8515795775856e25899e487bf4e5c2b49e04b7fbe40cb3b5c25378bcccde11971da280e8b7ba44d72b8436e2066e20f", + "0x91769a608c9a32f39ca9d14d5451e10071de2fd6b0baec9a541c8fad22da75ed4946e7f8b081f79cc2a67bd2452066a9", + "0x87b1e6dbca2b9dbc8ce67fd2f54ffe96dfcce9609210a674a4cb47dd71a8d95a5a24191d87ba4effa4a84d7db51f9ba0", + "0xa95accf3dbcbf3798bab280cabe46e3e3688c5db29944dbe8f9bd8559d70352b0cfac023852adc67c73ce203cbb00a81", + "0xa835f8ce7a8aa772c3d7cfe35971c33fc36aa3333b8fae5225787533a1e4839a36c84c0949410bb6aace6d4085588b1e", + "0x8ef7faa2cf93889e7a291713ab39b3a20875576a34a8072a133fed01046f8093ace6b858463e1e8a7f923d57e4e1bc38", + "0x969ecd85643a16d937f148e15fb56c9550aefd68a638425de5058333e8c0f94b1df338eaab1bd683190bfde68460622b", + "0x8982f4c76b782b9b47a9c5aeb135278e5c991b1558e47b79328c4fae4b30b2b20c01204ff1afb62b7797879d9dee48e2", + "0xb5098b7ba813178ced68f873c8c223e23a3283d9f1a061c95b68f37310bca4b2934a3a725fff1de1341c79bb3ba6007e", + "0x97b160787009f7b9649ed63db9387d48a669e17b2aba8656792eb4f5685bb8e6386f275476b4dfbb1b4cb0c2a69bc752", + "0x88b69369c71daad6b84fa51a0f64a6962d8c77e555b13c035ad6fa1038e7190af455b1bd61ae328b65d6a14cf3d5f0d5", + "0xaf88b87801361f0de26bd2533554ee6f4d8067e3122b54161c313c52cc9eafea00661c5c43e2d533485d1f26da4e5510", + "0x98ab18e3bbcb23ac1e34439849e56009bb765ab2f2558ebfd0a57cbe742169f114bceb930533fb911b22cb5a8fe172bc", + "0x9027507f1725d81e5ac0f0854c89ab627df3020fe928cb8745f887bf3310086c58fca1119fd5cd18a7d3561c042d58de", + "0xa676583f8a26e6f8991a0791916ce785b596ce372812f5eb7b4243ba9367ea95c797170fdac5b0c5e6b7f6519cc2b026", + "0xb91b0ab32638aef3365035a41c6068e36d2303bfee8640565e16c9a56c21703270fd45946ce663238a72c053eb3f2230", + "0xaaf4cd1ac0a30906dcd2b66b37848c6cc443da511e0b0367fd792887fdaf1500551590440e61d837dbee9d24c9801108", + "0xa06f20a02d3cd76029baad5a12592f181738378a83a95e90470fa7cc82a5ae9d2ed824a20eeb1e96e6edc0619f298688", + "0xa465d379c3481b294efc3f2f940b651c45579607cf72d143b99705eae42103a0279eb3595966453130e18935265e35d6", + "0x892a8af7816a806295278027a956663ea1297118ede0f2a7e670483b81fb14dccacc7a652e12f160e531d806ca5f2861", + "0xb480917c0e8b6e00de11b4416a20af6c48a343450a32ee43224559d30e1fecdece52cc699493e1754c0571b84f6c02c2", + "0xb3182da84c81e5a52e22cebed985b0efc3056350ec59e8646e7fd984cdb32e6ac14e76609d0ffaca204a7a3c20e9f95d", + "0xa04ea6392f3b5a176fa797ddec3214946962b84a8f729ffbd01ca65767ff6237da8147fc9dc7dd88662ad0faefdb538c", + "0x95c0d10a9ba2b0eb1fd7aa60c743b6cf333bb7f3d7adedce055d6cd35b755d326bf9102afabb1634f209d8dacfd47f1a", + "0xa1a583d28b07601541fa666767f4f45c954431f8f3cc3f96380364c5044ff9f64114160e5002fb2bbc20812b8cbd36cb", + "0xa1a0708af5034545e8fcc771f41e14dff421eed08b4606f6d051f2d7799efd00d3a59a1b9a811fa4eddf5682e63102ea", + "0xab27c7f54096483dd85c866cfb347166abe179dc5ffaca0c29cf3bfe5166864c7fa5f954c919b3ba00bdbab38e03407d", + "0xac8c82271c8ca71125b380ed6c61b326c1cfe5664ccd7f52820e11f2bea334b6f60b1cf1d31599ed94d8218aa6fbf546", + "0xa015ea84237d6aa2adb677ce1ff8a137ef48b460afaca20ae826a53d7e731320ebdd9ee836de7d812178bec010dd6799", + "0x925418cda78a56c5b15d0f2dc66f720bda2885f15ffafb02ce9c9eed7167e68c04ad6ae5aa09c8c1c2f387aa39ad6d1b", + "0x87c00bba80a965b3742deacafb269ca94ead4eb57fdb3ed28e776b1d0989e1b1dba289019cfb1a0f849e58668a4f1552", + "0x948d492db131ca194f4e6f9ae1ea6ebc46ebbed5d11f1f305d3d90d6b4995b1218b9606d114f48282a15661a8a8051ca", + "0x8179617d64306417d6865add8b7be8452f1759721f97d737ef8a3c90da6551034049af781b6686b2ea99f87d376bce64", + "0x918e3da425b7c41e195ed7b726fa26b15a64299fe12a3c22f51a2a257e847611ac6cfcc99294317523fc491e1cbe60c4", + "0xa339682a37844d15ca37f753599d0a71eedfbbf7b241f231dd93e5d349c6f7130e0d0b97e6abd2d894f8b701da37cb11", + "0x8fc284f37bee79067f473bc8b6de4258930a21c28ac54aaf00b36f5ac28230474250f3aa6a703b6057f7fb79a203c2c1", + "0xa2c474e3a52a48cd1928e755f610fefa52d557eb67974d02287dbb935c4b9aab7227a325424fed65f8f6d556d8a46812", + "0x99b88390fa856aa1b8e615a53f19c83e083f9b50705d8a15922e7c3e8216f808a4cc80744ca12506b1661d31d8d962e4", + "0xa1cbd03e4d4f58fc4d48fa165d824b77838c224765f35d976d3107d44a6cf41e13f661f0e86f87589292721f4de703fb", + "0xb3a5dde8a40e55d8d5532beaa5f734ee8e91eafad3696df92399ae10793a8a10319b6dc53495edcc9b5cfd50a389a086", + "0x996e25e1df5c2203647b9a1744bd1b1811857f742aee0801508457a3575666fcc8fc0c047c2b4341d4b507008cd674c2", + "0x93e0a66039e74e324ee6c38809b3608507c492ef752202fff0b2c0e1261ca28f1790b3af4fdb236f0ed7e963e05c1ec0", + "0xb6084e5818d2d860ac1606d3858329fbad4708f79d51a6f072dc370a21fdb1e1b207b74bc265a8547658bfb6a9569bb3", + "0xa5336126a99c0ecfc890584b2a167922a26cae652dfc96a96ab2faf0bf9842f166b39ceaf396cd3d300d0ebb2e6e0ebf", + "0xb8b6f13ce9201decaba76d4eca9b9fa2e7445f9bc7dc9f82c262f49b15a40d45d5335819b71ff2ee40465da47d015c47", + "0xb45df257b40c68b7916b768092e91c72b37d3ed2a44b09bf23102a4f33348849026cb3f9fbb484adfea149e2d2a180ff", + "0xa50d38ee017e28021229c4bb7d83dd9cdad27ab3aa38980b2423b96aa3f7dc618e3b23895b0e1379ca20299ff1919bbf", + "0x97542cf600d34e4fdc07d074e8054e950708284ed99c96c7f15496937242365c66e323b0e09c49c9c38113096640a1b6", + "0x822d198629697dcd663be9c95ff1b39419eae2463fa7e6d996b2c009d746bedc8333be241850153d16c5276749c10b20", + "0x9217bc14974766ebdfbf6b434dd84b32b04658c8d8d3c31b5ff04199795d1cfad583782fd0c7438df865b81b2f116f9c", + "0x93477879fa28a89471a2c65ef6e253f30911da44260833dd51030b7a2130a923770ebd60b9120f551ab373f7d9ed80aa", + "0x87d89ff7373f795a3a798f03e58a0f0f0e7deab8db2802863fab84a7be64ae4dcf82ece18c4ddbefccd356262c2e8176", + "0xa3ba26bd31d3cc53ceeced422eb9a63c0383cde9476b5f1902b7fe2b19e0bbf420a2172ac5c8c24f1f5c466eecc615d4", + "0xa0fe061c76c90d84bd4353e52e1ef4b0561919769dbabe1679b08ef6c98dcfb6258f122bb440993d976c0ab38854386b", + "0xb3070aa470185cb574b3af6c94b4069068b89bb9f7ea7db0a668df0b5e6aabdfe784581f13f0cf35cd4c67726f139a8c", + "0x9365e4cdf25e116cbc4a55de89d609bba0eaf0df2a078e624765509f8f5a862e5da41b81883df086a0e5005ce1576223", + "0xa9036081945e3072fa3b5f022df698a8f78e62ab1e9559c88f9c54e00bc091a547467d5e2c7cbf6bc7396acb96dd2c46", + "0x8309890959fcc2a4b3d7232f9062ee51ece20c7e631a00ec151d6b4d5dfccf14c805ce5f9aa569d74fb13ae25f9a6bbe", + "0xb1dc43f07303634157f78e213c2fae99435661cc56a24be536ccbd345ef666798b3ac53c438209b47eb62b91d6fea90a", + "0x84eb451e0a74ef14a2c2266ff01bd33d9a91163c71f89d0a9c0b8edfcfe918fc549565509cd96eed5720a438ff55f7f2", + "0x9863b85a10db32c4317b19cc9245492b9389b318cf128d9bbc7ec80a694fcbbd3c0d3189a8cad00cc9290e67e5b361ee", + "0x8a150ee474ebe48bdfcac1b29e46ac90dcded8abbe4807a165214e66f780f424be367df5ef1e94b09acf4a00cd2e614d", + "0xa6677a373130b83e30849af12475e192f817ba4f3226529a9cca8baaefb8811db376e4a044b42bf1481268c249b1a66e", + "0xb969cbf444c1297aa50d1dfa0894de4565161cb1fc59ba03af9655c5bf94775006fe8659d3445b546538a22a43be6b93", + "0x8383167e5275e0707e391645dc9dea9e8a19640ecfa23387f7f6fcaddff5cde0b4090dfad7af3c36f8d5c7705568e8d8", + "0xa353ddbc6b6837773e49bb1e33a3e00ca2fb5f7e1dba3a004b0de75f94a4e90860d082a455968851ef050ae5904452e0", + "0xadeccf320d7d2831b495479b4db4aa0e25c5f3574f65a978c112e9981b2663f59de4c2fa88974fdcabb2eedb7adab452", + "0xafa0eacc9fdbe27fb5e640ecad7ecc785df0daf00fc1325af716af61786719dd7f2d9e085a71d8dc059e54fd68a41f24", + "0xa5b803a5bbe0ca77c8b95e1e7bacfd22feae9f053270a191b4fd9bca850ef21a2d4bd9bcd50ecfb971bb458ff2354840", + "0xb023c9c95613d9692a301ef33176b655ba11769a364b787f02b42ceb72338642655ea7a3a55a3eec6e1e3b652c3a179e", + "0x8fa616aa7196fc2402f23a19e54620d4cf4cf48e1adfb7ea1f3711c69705481ddcc4c97236d47a92e974984d124589e5", + "0xa49e11e30cb81cb7617935e8a30110b8d241b67df2d603e5acc66af53702cf1e9c3ef4a9b777be49a9f0f576c65dcc30", + "0x8df70b0f19381752fe327c81cce15192389e695586050f26344f56e451df2be0b1cdf7ec0cba7ce5b911dcff2b9325ae", + "0x8fbbc21a59d5f5a14ff455ca78a9a393cab91deb61cf1c25117db2714d752e0054ed3e7e13dd36ad423815344140f443", + "0xa9a03285488668ab97836a713c6e608986c571d6a6c21e1adbd99ae4009b3dde43721a705d751f1bd4ebf1ea7511dfed", + "0xb2f32b8e19e296e8402251df67bae6066aeefd89047586d887ffa2eacdf38e83d4f9dc32e553799024c7a41818945755", + "0x942cf596b2278ad478be5c0ab6a2ad0ceafe110263cc93d15b9a3f420932104e462cf37586c374f10b1040cb83b862e0", + "0xaaa077a55f501c875ceae0a27ef2b180be9de660ef3d6b2132eb17256771ce609d9bc8aaf687f2b56ae46af34ad12b30", + "0x90ac74885be1448101cf3b957d4486e379673328a006ea42715c39916e9334ea77117ff4a60d858e2ccce9694547a14f", + "0x9256cdfc2339e89db56fd04bd9b0611be0eefc5ee30711bcece4aadf2efcc5a6dcc0cfd5f733e0e307e3a58055dff612", + "0xa4c7384e208a0863f4c056248f595473dcde70f019ddaede45b8caf0752575c241bac6e436439f380ac88eee23a858e9", + "0xa3aa67391781e0736dddc389f86b430b2fc293b7bd56bfd5a8ec01d1dd52ed940593c3ad4ce25905061936da062b0af6", + "0x80299275ec322fbb66cc7dce4482ddd846534e92121186b6906c9a5d5834346b7de75909b22b98d73120caec964e7012", + "0xaa3a6cd88e5f98a12738b6688f54478815e26778357bcc2bc9f2648db408d6076ef73cced92a0a6b8b486453c9379f18", + "0xb07c444681dc87b08a7d7c86708b82e82f8f2dbd4001986027b82cfbed17b9043e1104ade612e8e7993a00a4f8128c93", + "0xaf40e01b68d908ac2a55dca9b07bb46378c969839c6c822d298a01bc91540ea7a0c07720a098be9a3cfe9c27918e80e8", + "0xabd8947c3bbc3883c80d8c873f8e2dc9b878cbbb4fc4a753a68f5027de6d8c26aa8fbbafeb85519ac94e2db660f31f26", + "0xa234f9d1a8f0cb5d017ccca30b591c95ec416c1cb906bd3e71b13627f27960f61f41ed603ffbcf043fd79974ec3169a8", + "0x835aaf52a6af2bc7da4cf1586c1a27c72ad9de03c88922ad172dce7550d70f6f3efcc3820d38cd56ae3f7fc2f901f7a0", + "0xae75db982a45ad01f4aa7bc50d642ff188219652bb8d521d13a9877049425d57852f3c9e4d340ffec12a4d0c639e7062", + "0xb88884aa9187c33dc784a96832c86a44d24e9ffe6315544d47fc25428f11337b9ffd56eb0a03ad709d1bf86175059096", + "0x8492ca5afcc6c0187b06453f01ed45fd57eb56facbeea30c93686b9e1dab8eaabd89e0ccb24b5f35d3d19cd7a58b5338", + "0x9350623b6e1592b7ea31b1349724114512c3cce1e5459cd5bddd3d0a9b2accc64ab2bf67a71382d81190c3ab7466ba08", + "0x98e8bf9bed6ae33b7c7e0e49fc43de135bffdba12b5dcb9ff38cb2d2a5368bb570fe7ee8e7fbe68220084d1d3505d5be", + "0xab56144393f55f4c6f80c67e0ab68f445568d68b5aa0118c0c666664a43ba6307ee6508ba0bb5eb17664817bc9749af0", + "0x827d5717a41b8592cfd1b796a30d6b2c3ca2cdc92455f9f4294b051c4c97b7ad6373f692ddafda67884102e6c2a16113", + "0x8445ce2bb81598067edaa2a9e356eda42fb6dc5dd936ccf3d1ff847139e6020310d43d0fec1fe70296e8f9e41a40eb20", + "0x9405178d965ee51e8d76d29101933837a85710961bb61f743d563ef17263f3c2e161d57e133afac209cdb5c46b105e31", + "0xb209f9ed324c0daa68f79800c0a1338bbaf6d37b539871cb7570f2c235caca238a2c4407961fcb7471a103545495ef2c", + "0x92ae6437af6bbd97e729b82f5b0d8fb081ca822f340e20fae1875bdc65694cd9b8c037a5a1d49aa9cae3d33f5bad414e", + "0x9445bdb666eae03449a38e00851629e29a7415c8274e93343dc0020f439a5df0009cd3c4f5b9ce5c0f79aefa53ceac99", + "0x93fdab5f9f792eada28f75e9ac6042a2c7f3142ba416bfdb1f90aa8461dbe4af524eee6db4f421cb70c7bc204684d043", + "0xa7f4dc949af4c3163953320898104a2b17161f7be5a5615da684f881633174fb0b712d0b7584b76302e811f3fac3c12f", + "0xa8ac84da817b3066ba9789bf2a566ccf84ab0a374210b8a215a9dcf493656a3fa0ecf07c4178920245fee0e46de7c3ec", + "0x8e6a0ae1273acda3aa50d07d293d580414110a63bc3fb6330bb2ee6f824aff0d8f42b7375a1a5ba85c05bfbe9da88cb5", + "0xa5dea98852bd6f51a84fa06e331ea73a08d9d220cda437f694ad9ad02cf10657882242e20bdf21acbbaa545047da4ce5", + "0xb13f410bf4cfce0827a5dfd1d6b5d8eabc60203b26f4c88238b8000f5b3aaf03242cdeadc2973b33109751da367069e1", + "0xa334315a9d61b692ad919b616df0aa75a9f73e4ea6fc27d216f48964e7daebd84b796418580cf97d4f08d4a4b51037cd", + "0x8901ba9e963fcd2f7e08179b6d19c7a3b8193b78ca0e5cf0175916de873ca0d000cd7ac678c0473be371e0ac132f35a2", + "0xb11a445433745f6cb14c9a65314bbf78b852f7b00786501b05d66092b871111cd7bee25f702d9e550d7dd91601620abb", + "0x8c2f7b8e7b906c71f2f154cc9f053e8394509c37c07b9d4f21b4495e80484fc5fc8ab4bdc525bd6cfa9518680ba0d1a2", + "0xb9733cebe92b43b899d3d1bfbf4b71d12f40d1853b2c98e36e635fdd8a0603ab03119890a67127e6bc79afae35b0bef2", + "0xa560f6692e88510d9ba940371e1ada344caf0c36440f492a3067ba38e9b7011caac37ba096a8a4accb1c8656d3c019b3", + "0xac18624339c1487b2626eef00d66b302bdb1526b6340d6847befe2fdfb2b410be5555f82939f8707f756db0e021ed398", + "0xafd9a3b8866a7fe4f7bc13470c0169b9705fcd3073685f5a6dcff3bdbbc2be50ac6d9908f9a10c5104b0bffc2bc14dad", + "0x97f15c92fe1f10949ed9def5dd238bc1429706e5037a0e0afb71c2d0e5845e2fed95a171c393e372077a7c7059f8c0e0", + "0x9453a1d4d09c309b70968ea527007d34df9c4cfd3048e5391aac5f9b64ca0c05dde5b8c949c481cfc83ef2e57b687595", + "0xb80e4b7c379ad435c91b20b3706253b763cbc980db78f782f955d2516af44c07bbfa5888cbf3a8439dc3907320feb25a", + "0x8939f458d28fefe45320b95d75b006e98330254056d063e4a2f20f04bcb25936024efe8d436d491ed34b482f9b9ae49c", + "0xa9ead2e833f71f7e574c766440c4b3c9c3363698c7ade14499a56003a272832ee6d99440887fa43ccdf80265b9d56b97", + "0xb6547a36934f05ce7b779e68049d61351cf229ae72dc211cc96a2a471b2724782f9355fdb415ea6f0ea1eb84fe00e785", + "0x828bfb3099b7b650b29b0f21279f829391f64520a6ab916d1056f647088f1e50fac9253ef7464eceab5380035c5a59c4", + "0x8d714b9ea650be4342ff06c0256189e85c5c125adf6c7aeca3dba9b21d5e01a28b688fc2116ce285a0714a8f1425c0b8", + "0x8a82eda041b2e72a3d73d70d85a568e035fbd6dc32559b6c6cfdf6f4edcb59a6ba85b6294a721aa0a71b07714e0b99ae", + "0xaf5665ebc83d027173b14ffb0e05af0a192b719177889fadc9ac8c082fda721e9a75d9ce3f5602dbfd516600ee3b6405", + "0xa68fdddf03d77bebdb676e40d93e59bd854408793df2935d0a5600601f7691b879981a398d02658c2da39dbbf61ef96c", + "0x8c001ebc84fcf0470b837a08a7b6125126b73a2762db47bbdc38c0e7992b1c66bac7a64faa1bf1020d1c63b40adc3082", + "0x8553889b49f9491109792db0a69347880a9cf2911b4f16f59f7f424e5e6b553687d51282e8f95be6a543635247e2e2c2", + "0xa2c269d6370b541daf1f23cc6b5d2b03a5fa0c7538d53ae500ef875952fe215e74a5010329ff41461f4c58b32ad97b3d", + "0xa5dae097285392b4eba83a9fd24baa03d42d0a157a37fae4b6efc3f45be86024b1182e4a6b6eadcf5efe37704c0a1ae5", + "0x89871a77d2032387d19369933cd50a26bda643e40cfd0ce73febe717a51b39fae981406fd41e50f4a837c02a99524ef9", + "0x8a76d495e90093ec2ac22f53759dc1cf36fbb8370fb586acbd3895c56a90bbf3796bcc4fc422ca4058adf337ead1402e", + "0xad4eb7576c4954d20623c1336c63662c2a6fb46ec6ef99b7f8e946aa47488dcb136eab60b35600f98c78c16c10c99013", + "0x894c2b120cec539feb1d281baaadde1e44beafedeeec29b804473fe024e25c1db652f151c956e88d9081fb39d27e0b19", + "0x9196bd5c100878792444c573d02b380a69e1b4b30cb59a48114852085058a5fd952df4afee3ecceb5c4ede21e1ed4a1a", + "0xa996fffc910764ea87a1eedc3a3d600e6e0ff70e6a999cb435c9b713a89600fc130d1850174efe9fc18244bb7c6c5936", + "0x8591bb8826befa8bee9663230d9a864a5068589f059e37b450e8c85e15ce9a1992f0ce1ead1d9829b452997727edcf9d", + "0x9465e20bb22c41bf1fa728be8e069e25cda3f7c243381ca9973cbedad0c7b07d3dd3e85719d77cf80b1058ce60e16d68", + "0x926b5ce39b6e60b94878ffeae9ff20178656c375fb9cfe160b82318ca500eb3e2e3144608b6c3f8d6c856b8fe1e2fbcf", + "0xa1ef29cbc83c45eb28ad468d0ce5d0fdd6b9d8191ba5ffa1a781c2b232ed23db6b7b04de06ef31763a6bfe377fa2f408", + "0x9328e63a3c8acf457c9f1f28b32d90d0eeadb0f650b5d43486a61d7374757a7ada5fc1def2a1e600fa255d8b3f48036f", + "0xa9c64880fcb7654f4dd08f4c90baac95712dd6dd407e17ea60606e9a97dc8e54dd25cb72a9bf3fc61f8d0ad569fe369d", + "0xa908eb7b940c1963f73046d6b35d40e09013bfbfbeb2ccd64df441867e202b0f3b625fa32dd04987c3d7851360abdffc", + "0xb3947b5ed6d59e59e4472cdb1c3261de1b5278fb7cb9b5fca553f328b3b3e094596861ea526eca02395f7b7358155b7b", + "0x99da7f190d37bc58945f981cf484d40fcf0855cf8178e2ce8d057c7f0a9d9f77425fdbce9ef8366f44f671b20fd27d0b", + "0x913976d77d80e3657977df39571577fdf0be68ba846883705b454f8493578baa741cfaede53783e2c97cc08964395d83", + "0x8d754a61e5164a80b5090c13f3e936056812d4ae8dc5cc649e6c7f37464777249bc4ae760a9806939131f39d92cca5bf", + "0x82ffd098480828a90cb221a8c28584e15904bad477c13b2e2d6ef0b96a861ce4a309a328fe44342365349456ad7c654f", + "0x89ae3ce4b0357044579ca17be85d8361bb1ce3941f87e82077dd67e43ec0f95edd4bd3426225c90994a81a99e79490b7", + "0xa170892074016d57c9d8e5a529379d7e08d2c1158b9ac4487ac9b95266c4fd51cb18ae768a2f74840137eec05000dd5a", + "0xaafd8acd1071103c7af8828a7a08076324d41ea530df90f7d98fafb19735fc27ead91b50c2ca45851545b41d589d0f77", + "0x8623c849e61d8f1696dc9752116a26c8503fd36e2cbbc9650feffdd3a083d8cdbb3b2a4e9743a84b9b2ad91ac33083f2", + "0xac7166ddd253bb22cdbd8f15b0933c001d1e8bc295e7c38dc1d2be30220e88e2155ecd2274e79848087c05e137e64d01", + "0xa5276b216d3df3273bbfa46210b63b84cfe1e599e9e5d87c4e2e9d58666ecf1af66cb7ae65caebbe74b6806677215bd0", + "0x88792f4aa3597bb0aebadb70f52ee8e9db0f7a9d74f398908024ddda4431221a7783e060e0a93bf1f6338af3d9b18f68", + "0x8f5fafff3ecb3aad94787d1b358ab7d232ded49b15b3636b585aa54212f97dc1d6d567c180682cca895d9876cacb7833", + "0xab7cb1337290842b33e936162c781aa1093565e1a5b618d1c4d87dd866daea5cebbcc486aaa93d8b8542a27d2f8694c7", + "0x88480a6827699da98642152ebc89941d54b4791fbc66110b7632fb57a5b7d7e79943c19a4b579177c6cf901769563f2f", + "0xa725ee6d201b3a610ede3459660658ee391803f770acc639cfc402d1667721089fb24e7598f00e49e81e50d9fd8c2423", + "0x98924372da8aca0f67c8c5cad30fa5324519b014fae7849001dcd51b6286118f12b6c49061219c37714e11142b4d46de", + "0xa62c27360221b1a7c99697010dfe1fb31ceb17d3291cf2172624ebeff090cbaa3c3b01ec89fe106dace61d934711d42d", + "0x825173c3080be62cfdc50256c3f06fe190bc5f190d0eb827d0af5b99d80936e284a4155b46c0d462ee574fe31d60983d", + "0xa28980b97023f9595fadf404ed4aa36898d404fe611c32fd66b70252f01618896f5f3fda71aea5595591176aabf0c619", + "0xa50f5f9def2114f6424ff298f3b128068438f40860c2b44e9a6666f43c438f1780be73cf3de884846f1ba67f9bef0802", + "0xb1eee2d730da715543aeb87f104aff6122cb2bf11de15d2519ff082671330a746445777924521ec98568635f26988d0c", + "0x862f6994a1ff4adfd9fb021925cccf542fca4d4b0b80fb794f97e1eb2964ef355608a98eec6e07aadd4b45ee625b2a21", + "0x8ce69a18df2f9b9f6e94a456a7d94842c61dea9b00892da7cf5c08144de9be39b8c304aeca8b2e4222f87ba367e61006", + "0xb5f325b1cecd435f5346b6bc562d92f264f1a6d91be41d612df012684fdd69e86063db077bc11ea4e22c5f2a13ae7bee", + "0x85526870a911127835446cb83db8986b12d5637d59e0f139ad6501ac949a397a6c73bd2e7fba731b1bb357efe068242c", + "0x8552247d3f7778697f77389717def5a149fc20f677914048e1ed41553b039b5427badc930491c0bae663e67668038fd1", + "0xa545640ee5e51f3fe5de7050e914cfe216202056cd9d642c90e89a166566f909ee575353cb43a331fde17f1c9021414e", + "0x8b51229b53cff887d4cab573ba32ec52668d197c084414a9ee5589b285481cea0c3604a50ec133105f661321c3ca50f5", + "0x8cdc0b960522bed284d5c88b1532142863d97bbb7dc344a846dc120397570f7bd507ceb15ed97964d6a80eccfef0f28e", + "0xa40683961b0812d9d53906e795e6470addc1f30d09affebf5d4fbbd21ddfa88ce441ca5ea99c33fd121405be3f7a3757", + "0xa527875eb2b99b4185998b5d4cf97dd0d4a937724b6ad170411fc8e2ec80f6cee2050f0dd2e6fee9a2b77252d98b9e64", + "0x84f3a75f477c4bc4574f16ebc21aaa32924c41ced435703c4bf07c9119dd2b6e066e0c276ff902069887793378f779e0", + "0xa3544bc22d1d0cab2d22d44ced8f7484bfe391b36991b87010394bfd5012f75d580596ffd4f42b00886749457bb6334b", + "0xb81f6eb26934b920285acc20ceef0220dd23081ba1b26e22b365d3165ce2fbae733bbc896bd0932f63dcc84f56428c68", + "0x95e94d40a4f41090185a77bf760915a90b6a3e3ace5e53f0cb08386d438d3aa3479f0cd81081b47a9b718698817265cd", + "0xb69bd1625b3d6c17fd1f87ac6e86efa0d0d8abb69f8355a08739109831baeec03fd3cd4c765b5ff8b1e449d33d050504", + "0x8448f4e4c043519d98552c2573b76eebf2483b82d32abb3e2bfc64a538e79e4f59c6ca92adff1e78b2f9d0a91f19e619", + "0x8f11c42d6a221d1fda50887fb68b15acdb46979ab21d909ed529bcad6ae10a66228ff521a54a42aca0dad6547a528233", + "0xa3adb18d7e4a882b13a067784cf80ea96a1d90f5edc61227d1f6e4da560c627688bdf6555d33fe54cab1bca242986871", + "0xa24d333d807a48dc851932ed21cbdd7e255bad2699909234f1706ba55dea4bb6b6f8812ffc0be206755868ba8a4af3f9", + "0xa322de66c22a606e189f7734dbb7fda5d75766d5e69ec04b4e1671d4477f5bcb9ff139ccc18879980ebc3b64ab4a2c49", + "0x88f54b6b410a1edbf125db738d46ee1a507e69bc5a8f2f443eb787b9aa7dbd6e55014ec1e946aabeb3e27a788914fb04", + "0xb32ee6da1dcd8d0a7fd7c1821bb1f1fe919c8922b4c1eeed56e5b068a5a6e68457c42b192cbaef5dc6d49b17fa45bc0f", + "0x8a44402da0b3a15c97b0f15db63e460506cb8bef56c457166aea5e8881087d8202724c539ef0feb97131919a73aefca8", + "0xb967e3fead6171fa1d19fd976535d428b501baff59e118050f9901a54b12cc8e4606348454c8f0fc25bd6644e0a5532e", + "0xb7a0c9e9371c3efbbb2c6783ce2cc5f149135175f25b6d79b09c808bce74139020e77f0c616fa6dcb3d87a378532529d", + "0xa54207782ffc909cd1bb685a3aafabbc4407cda362d7b3c1b14608b6427e1696817aeb4f3f85304ac36e86d3d8caa65b", + "0x98c1da056813a7bfebc81d8db7206e3ef9b51f147d9948c088976755826cc5123c239ca5e3fe59bed18b5d0a982f3c3f", + "0xae1c86174dfafa9c9546b17b8201719aecd359f5bbeb1900475041f2d5b8a9600d54d0000c43dd061cfda390585726ff", + "0xa8ee5a8be0bd1372a35675c87bfd64221c6696dc16e2d5e0996e481fec5cdbcb222df466c24740331d60f0521285f7d3", + "0x8ddadbe3cf13af50d556ce8fc0dd77971ac83fad9985c3d089b1b02d1e3afc330628635a31707b32595626798ea22d45", + "0xa5c80254baf8a1628dc77c2445ebe21fbda0de09dd458f603e6a9851071b2b7438fe74214df293dfa242c715d4375c95", + "0xb9d83227ed2600a55cb74a7052003a317a85ca4bea50aa3e0570f4982b6fe678e464cc5156be1bd5e7bba722f95e92c5", + "0xb56085f9f3a72bea9aa3a8dc143a96dd78513fa327b4b9ba26d475c088116cab13843c2bff80996bf3b43d3e2bddb1d6", + "0x8fa9b39558c69a9757f1e7bc3f07295e4a433da3e6dd8c0282397d26f64c1ecd8eb3ba9824a7cacfb87496ebbb45d962", + "0x879c6d0cb675812ed9dee68c3479a499f088068501e2677caeae035e6f538da91a49e245f5fcce135066169649872bee", + "0x91aa9fd3fed0c2a23d1edda8a6542188aeb8abee8772818769bdee4b512d431e4625a343af5d59767c468779222cf234", + "0xa6be0bb2348c35c4143482c7ef6da9a93a5356f8545e8e9d791d6c08ed55f14d790d21ee61d3a56a2ae7f888a8fd46ca", + "0x808ee396a94e1b8755f2b13a6ffbedef9e0369e6c2e53627c9f60130c137299d0e4924d8ef367e0a7fad7f68a8c9193c", + "0xad1086028fcdac94d5f1e7629071e7e47e30ad0190ae59aaebfb7a7ef6202ab91323a503c527e3226a23d7937af41a52", + "0x9102bdaf79b907d1b25b2ec6b497e2d301c8eac305e848c6276b392f0ad734131a39cc02ed42989a53ca8da3d6839172", + "0x8c976c48a45b6bc7cd7a7acea3c2d7c5f43042863b0661d5cd8763e8b50730552187a8eecf6b3d17be89110208808e77", + "0xa2624c7e917e8297faa3af89b701953006bf02b7c95dfba00c9f3de77748bc0b13d6e15bb8d01377f4d98fb189538142", + "0xa405f1e66783cdcfe20081bce34623ec3660950222d50b7255f8b3cc5d4369aeb366e265e5224c0204911539f0fa165e", + "0x8d69bdcaa5d883b5636ac8f8842026fcc58c5e2b71b7349844a3f5d6fbecf44443ef4f768eac376f57fb763606e92c9f", + "0x82fce0643017d16ec1c3543db95fb57bfa4855cc325f186d109539fcacf8ea15539be7c4855594d4f6dc628f5ad8a7b0", + "0x8860e6ff58b3e8f9ae294ff2487f0d3ffae4cf54fd3e69931662dabc8efd5b237b26b3def3bcd4042869d5087d22afcf", + "0x88c80c442251e11c558771f0484f56dc0ed1b7340757893a49acbf96006aa73dfc3668208abea6f65375611278afb02a", + "0x8be3d18c6b4aa8e56fcd74a2aacb76f80b518a360814f71edb9ccf3d144bfd247c03f77500f728a62fca7a2e45e504c5", + "0x8b8ebf0df95c3f9b1c9b80469dc0d323784fd4a53f5c5357bb3f250a135f4619498af5700fe54ad08744576588b3dfff", + "0xa8d88abdaadd9c2a66bc8db3072032f63ed8f928d64fdb5f810a65074efc7e830d56e0e738175579f6660738b92d0c65", + "0xa0a10b5d1a525eb846b36357983c6b816b8c387d3890af62efb20f50b1cb6dd69549bbef14dab939f1213118a1ae8ec2", + "0x8aadf9b895aeb8fdc9987daa937e25d6964cbd5ec5d176f5cdf2f0c73f6f145f0f9759e7560ab740bf623a3279736c37", + "0x99aeda8a495031cc5bdf9b842a4d7647c55004576a0edc0bd9b985d60182608361ed5459a9d4b21aa8e2bd353d10a086", + "0x832c8b3bfcd6e68eee4b100d58014522de9d4cefa99498bc06c6dca83741e4572e20778e0d846884b33439f160932bca", + "0x841f56ebefc0823ab484fc445d62f914e13957e47904419e42771aa605e33ab16c44f781f6f9aa42e3a1baf377f54b42", + "0xa6e40271d419e295a182725d3a9b541ffd343f23e37549c51ecaa20d13cf0c8d282d6d15b24def5702bfee8ba10b12ac", + "0x8ac00925ac6187a4c5cde48ea2a4eaf99a607e58b2c617ee6f01df30d03fafada2f0469178dd960d9d64cbd33a0087d8", + "0xb6b80916b540f8a0fe4f23b1a06e2b830008ad138271d5ba3cd16d6619e521fe2a7623c16c41cba48950793386eea942", + "0x8412c0857b96a650e73af9d93087d4109dd092ddf82188e514f18fcac644f44d4d62550bfa63947f2d574a2e9d995bbb", + "0xb871395baa28b857e992a28ac7f6d95ec461934b120a688a387e78498eb26a15913b0228488c3e2360391c6b7260b504", + "0x926e2d25c58c679be77d0e27ec3b580645956ba6f13adcbc2ea548ee1b7925c61fcf74c582337a3b999e5427b3f752f2", + "0xa165fa43fecae9b913d5dcfc232568e3e7b8b320ce96b13800035d52844c38fd5dbf7c4d564241d860c023049de4bcbc", + "0xb4976d7572fd9cc0ee3f24888634433f725230a7a2159405946a79315bc19e2fc371448c1c9d52bf91539fd1fe39574b", + "0xa6b461eb72e07a9e859b9e16dfa5907f4ac92a5a7ca4368b518e4a508dc43f9b4be59db6849739f3ef4c44967b63b103", + "0xb976606d3089345d0bc501a43525d9dca59cf0b25b50dfc8a61c5bd30fac2467331f0638fab2dc68838aa6ee8d2b6bc9", + "0xb16ea61c855da96e180abf7647fa4d9dd6fd90adebadb4c5ed4d7cd24737e500212628fca69615d89cb40e9826e5a214", + "0x95a3e3162eb5ea27a613f8c188f2e0dcc5cbd5b68c239858b989b004d87113e6aa3209fa9fad0ee6ecef42814ba9db1a", + "0xb6a026ab56d3224220e5bce8275d023c8d39d1bdf7eec3b0923429b7d5ef18cf613a3591d364be8727bb1fa0ba11eabb", + "0x949f117e2e141e25972ee9ccdd0b7a21150de7bbf92bbd89624a0c5f5a88da7b2b172ba2e9e94e1768081f260c2a2f8d", + "0xb7c5e9e6630287d2a20a2dfb783ffe6a6ff104ff627c6e4e4342acc2f3eb6e60e9c22f465f8a8dc58c42f49840eca435", + "0x872be5a75c3b85de21447bb06ac9eb610f3a80759f516a2f99304930ddf921f34cbffc7727989cdd7181d5fc62483954", + "0xa50976ea5297d797d220932856afdd214d1248230c9dcd840469ecc28ea9f305b6d7b38339fedb0c00b5251d77af8c95", + "0x80b360f8b44914ff6f0ffbd8b5360e3cabe08639f6fe06d0c1526b1fe9fe9f18c497f1752580b30e950abd3e538ad416", + "0xa2f98f9bf7fac78c9da6bb41de267742a9d31cf5a04b2fb74f551084ec329b376f651a59e1ae919b2928286fb566e495", + "0x8b9d218a8a6c150631548e7f24bbd43f132431ae275c2b72676abbea752f554789c5ff4aac5c0eeee5529af7f2b509ef", + "0xaa21a243b07e9c7b169598bf0b102c3c280861780f83121b2ef543b780d47aaa4b1850430ee7927f33ece9847c4e0e1a", + "0x8a6f90f4ce58c8aa5d3656fe4e05acccf07a6ec188a5f3cde7bf59a8ae468e66f055ac6dfc50b6e8e98f2490d8deedc5", + "0x8e39f77ca4b5149ffe9945ceac35d068760ba338d469d57c14f626dd8c96dbe993dd7011beff727c32117298c95ee854", + "0x83bd641c76504222880183edd42267e0582642c4993fe2c7a20ce7168e4c3cbf7586e1d2d4b08c84d9b0bf2f6b8800b8", + "0xa9d332993cf0c1c55130e5cf3a478eb5e0bfb49c25c07538accc692ef03d82b458750a7b991cc0b41b813d361a5d31e3", + "0xa0fc60e6a6015df9bee04cea8f20f01d02b14b6f7aa03123ab8d65da071b2d0df5012c2a69e7290baae6ed6dd29ebe07", + "0xa2949dde2e48788ceaac7ec7243f287ffe7c3e788cdba97a4ab0772202aeef2d50382bed8bf7eff5478243f7eabe0bda", + "0xa7879373ea18572dba6cf29868ca955ffa55b8af627f29862f6487ee398b81fe3771d8721ca8e06716c5d91b9ac587cb", + "0xb3c7081e2c5306303524fbe9fe5645111a57dffd4ec25b7384da12e56376a0150ab52f9d9cc6ca7bdd950695e39b766d", + "0xa634a6a19d52dcb9f823352b36c345d2de54b75197bcd90528d27830bd6606d1a9971170de0849ed5010afa9f031d5be", + "0x88f2062f405fa181cfdb8475eaf52906587382c666ca09a9522537cfebbc7de8337be12a7fd0db6d6f2f7ab5aefab892", + "0xb1f0058c1f273191247b98783b2a6f5aa716cf799a8370627fc3456683f03a624d0523b63a154fe9243c0dfd5b37c460", + "0xae39a227cc05852437d87be6a446782c3d7fbe6282e25cf57b6b6e12b189bdc0d4a6e2c3a60b3979256b6b5baf8f1c5f", + "0x802a1af228ab0c053b940e695e7ef3338f5be7acf4e5ed01ac8498e55b492d3a9f07996b1700a84e22f0b589638909cd", + "0xa36490832f20e4b2f9e79ee358b66d413f034d6a387534b264cdeac2bca96e8b5bcbdd28d1e98c44498032a8e63d94d2", + "0x8728c9a87db2d006855cb304bba54c3c704bf8f1228ae53a8da66ca93b2dac7e980a2a74f402f22b9bc40cd726e9c438", + "0xa08f08ab0c0a1340e53b3592635e256d0025c4700559939aeb9010ed63f7047c8021b4210088f3605f5c14fb51d1c613", + "0x9670fd7e2d90f241e8e05f9f0b475aa260a5fb99aa1c9e61cd023cbad8ed1270ae912f168e1170e62a0f6d319cf45f49", + "0xa35e60f2dd04f098bf274d2999c3447730fe3e54a8aff703bc5a3c274d22f97db4104d61a37417d93d52276b27ef8f31", + "0x859df7a21bc35daec5695201bd69333dc4f0f9e4328f2b75a223e6615b22b29d63b44d338413ca97eb74f15563628cb7", + "0xb2b44ad3e93bc076548acdf2477803203108b89ecc1d0a19c3fb9814d6b342afc420c20f75e9c2188ad75fdb0d34bb2d", + "0x941173ee2c87765d10758746d103b667b1227301e1bcfecef2f38f9ab612496a9abd3050cef5537bf28cfecd2aacc449", + "0x92b0bea30ebed20ac30648efb37bac2b865daaa514316e6f5470e1de6cb84651ff77c127aa7beed4521bda5e8fc81122", + "0xaf17bf813bb238cf8bb437433f816786612209180a6c0a1d5141292dc2d2c37164ef13bfc50c718bfcc6ce26369298a2", + "0x8461fd951bdfda099318e05cc6f75698784b033f15a71bce26165f0ce421fd632d50df9eeced474838c0050b596e672c", + "0x83281aa18ae4b01e8201e1f64248cc6444c92ee846ae72adb178cef356531558597d84ff93a05abf76bfe313eb7dbe86", + "0xb62b150f73999c341daa4d2f7328d2f6ca1ef3b549e01df58182e42927537fc7971c360fe8264af724f4c0247850ef12", + "0xa7022a201f79c012f982b574c714d813064838a04f56964d1186691413757befeeaada063e7884297606e0eea1b1ed43", + "0xa42ac9e8be88e143853fd8e6a9ff21a0461801f0ac76b69cca669597f9af17ecb62cccdcdcbe7f19b62ab93d7f838406", + "0x80f1ca73b6ba3a2fbae6b79b39c0be8c39df81862d46c4990c87cbf45b87996db7859d833abc20af2fcb4faf059c436a", + "0xb355943e04132d5521d7bbe49aea26f6aa1c32f5d0853e77cc2400595325e923a82e0ff7601d1aee79f45fd8a254f6ae", + "0x87142c891d93e539b31d0b5ead9ea600b9c84db9be9369ff150a8312fe3d10513f4c5b4d483a82b42bc65c45dd9dd3bd", + "0x823c3d7f6dda98a9d8c42b3fee28d3154a95451402accadb6cf75fc45d2653c46a569be75a433094fa9e09c0d5cf1c90", + "0xb3c3497fe7356525c1336435976e79ec59c5624c2fb6185ee09ca0510d58b1e392965e25df8a74d90d464c4e8bb1422b", + "0x88c48d83e8ddc0d7eea051f3d0e21bc0d3a0bb2b6a39ece76750c1c90c382a538c9a35dc9478b8ceb8157dcccbbf187a", + "0x93da81a8939f5f58b668fefdc6f5f7eca6dc1133054de4910b651f8b4a3267af1e44d5a1c9e5964dc7ab741eb146894b", + "0x8b396e64985451ac337f16be61105106e262e381ea04660add0b032409b986e1ac64da3bc2feae788e24e9cb431d8668", + "0x9472068b6e331ea67e9b5fbf8057672da93c209d7ded51e2914dbb98dccd8c72b7079b51fd97a7190f8fc8712c431538", + "0xac47e1446cb92b0a7406f45c708567f520900dfa0070d5e91783139d1bfc946d6e242e2c7b3bf4020500b9f867139709", + "0x896053706869fb26bb6f7933b3d9c7dd6db5c6bd1269c7a0e222b73039e2327d44bda7d7ae82bf5988808b9831d78bcd", + "0xa55e397fa7a02321a9fe686654c86083ecedb5757586d7c0250ec813ca6d37151a12061d5feca4691a0fd59d2f0fdd81", + "0xae23f08ac2b370d845036518f1bddb7fea8dc59371c288a6af310486effeb61963f2eef031ca90f9bdbcf0e475b67068", + "0xb5462921597a79f66c0fec8d4c7cfd89f427692a7ce30d787e6fd6acd2377f238ec74689a0fdbe8ef3c9c9bd24b908dc", + "0xae67e8ea7c46e29e6aae6005131c29472768326819aa294aaf5a280d877de377b44959adb1348fa3e929dcbc3ae1f2c0", + "0x84962b4c66500a20c4424191bdfb619a46cda35bdb34c2d61edcb0b0494f7f61dd5bf8f743302842026b7b7d49edd4b5", + "0x846f76286dc3cc59cb15e5dabb72a54a27c78190631df832d3649b2952fa0408ecde7d4dfdae7046c728efa29879fb51", + "0x8f76c854eaee8b699547e07ad286f7dadfa6974c1328d12502bd7630ae619f6129272fdd15e2137ffef0143c42730977", + "0x8007b163d4ea4ec6d79e7a2aa19d06f388da0b3a56f3ee121441584e22a246c0e792431655632bf6e5e02cb86914eebf", + "0xac4d2cecc1f33e6fb73892980b61e62095ddff5fd6167f53ca93d507328b3c05440729a277dc3649302045b734398af1", + "0x92d2a88f2e9c9875abaff0d42624ccb6d65401de7127b5d42c25e6adccd7a664504c5861618f9031ced8aeb08b779f06", + "0xa832c1821c1b220eb003fc532af02c81196e98df058cdcc9c9748832558362915ea77526937f30a2f74f25073cb89afb", + "0xb6f947ab4cc2baec100ed8ec7739a2fd2f9504c982b39ab84a4516015ca56aea8eef5545cfc057dd44c69b42125fb718", + "0xb24afacf2e90da067e5c050d2a63878ee17aaf8fd446536f2462da4f162de87b7544e92c410d35bf2172465940c19349", + "0xb7a0aa92deac71eaab07be8fa43086e071e5580f5dbf9b624427bdd7764605d27303ae86e5165bed30229c0c11958c38", + "0xb0d1d5bfa1823392c5cf6ed927c1b9e84a09a24b284c2cd8fcb5fda8e392c7c59412d8f74eb7c48c6851dff23ae66f58", + "0xa24125ef03a92d2279fb384186ca0274373509cfec90b34a575490486098438932ee1be0334262d22d5f7d3db91efe67", + "0x83e08e5fba9e8e11c164373794f4067b9b472d54f57f4dbe3c241cf7b5b7374102de9d458018a8c51ab3aed1dddf146f", + "0x9453101b77bb915ed40990e1e1d2c08ea8ec5deb5b571b0c50d45d1c55c2e2512ec0ceca616ff0376a65678a961d344d", + "0x92a0516e9eb6ad233d6b165a8d64a062ce189b25f95d1b3264d6b58da9c8d17da2cd1f534800c43efcf2be73556cd2ff", + "0x958d0b5d7d8faf25d2816aa6a2c5770592ad448db778dd9b374085baa66c755b129822632eaabcb65ee35f0bf4b73634", + "0x90a749de8728b301ad2a6b044e8c5fd646ccd8d20220e125cba97667e0bb1d0a62f6e3143b28f3d93f69cdc6aa04122a", + "0x84bd34c8d8f74dec07595812058db24d62133c11afed5eb2a8320d3bfc28e442c7f0cfd51011b7b0bb3e5409cb7b6290", + "0xaecc250b556115d97b553ad7b2153f1d69e543e087890000eaa60f4368b736921d0342ce5563124f129096f5d5e2ca9d", + "0x977f17ac82ed1fbf422f9b95feb3047a182a27b00960296d804fd74d54bb39ad2c055e665c1240d2ad2e06a3d7501b00", + "0xaf5be9846bd4879ebe0af5e7ad253a632f05aedfe306d31fe6debe701ba5aa4e33b65efc05043bc73aadb199f94baed4", + "0x9199e12ec5f2aaaeed6db5561d2dcc1a8fe9c0854f1a069cba090d2dff5e5ba52b10c841ccbd49006a91d881f206150d", + "0x8f4a96a96ed8ceaf3beba026c89848c9ca4e6452ce23b7cf34d12f9cc532984a498e051de77745bdc17c7c44c31b7c30", + "0xaf3f2a3dbe8652c4bfca0d37fb723f0e66aab4f91b91a625114af1377ad923da8d36da83f75deb7a3219cd63135a3118", + "0xa6d46963195df8962f7aa791d104c709c38caa438ddd192f7647a884282e81f748c94cdf0bb25d38a7b0dc1b1d7bbcf7", + "0x86f3de4b22c42d3e4b24b16e6e8033e60120af341781ab70ae390cb7b5c5216f6e7945313c2e04261a51814a8cb5db92", + "0xb9f86792e3922896cfd847d8ff123ff8d69ecf34968fb3de3f54532f6cd1112b5d34eeabdca46ae64ad9f6e7e5b55edc", + "0x83edfbcbc4968381d1e91ab813b3c74ab940eaf6358c226f79182f8b21148ec130685fd91b0ea65916b0a50bccf524ea", + "0x93b61daca7a8880b7926398760f50016f2558b0bab74c21181280a1baf3414fc539911bb0b79c4288d29d3c4ad0f4417", + "0xad541aeb83a47526d38f2e47a5ce7e23a9adabe5efeae03541026881e6d5ef07da3ac1a6ed466ca924fa8e7a91fcff88", + "0xac4bba31723875025640ed6426003ed8529215a44c9ffd44f37e928feef9fc4dfa889088131c9be3da87e8f3fdf55975", + "0x88fa4d49096586bc9d29592909c38ea3def24629feacd378cc5335b70d13814d6dac415f8c699ee1bf4fe8b85eb89b38", + "0xb67d0b76cbd0d79b71f4673b96e77b6cda516b8faa1510cfe58ff38cc19000bb5d73ff8418b3dab8c1c7960cb9c81e36", + "0x98b4f8766810f0cfecf67bd59f8c58989eb66c07d3dfeee4f4bbce8fd1fce7cc4f69468372eaec7d690748543bd9691d", + "0x8445891af3c298b588dec443beacdf41536adb84c812c413a2b843fd398e484eb379075c64066b460839b5fe8f80177c", + "0xb603635c3ed6fdc013e2a091fc5164e09acf5f6a00347d87c6ebadb1f44e52ff1a5f0466b91f3f7ffc47d25753e44b75", + "0x87ec2fc928174599a9dafe7538fec7dcf72e6873b17d953ed50708afff0da37653758b52b7cafa0bf50dfcf1eafbb46c", + "0xb9dbd0e704d047a457d60efe6822dc679e79846e4cbcb11fa6c02079d65673ee19bbf0d14e8b7b200b9205f4738df7c7", + "0x9591ec7080f3f5ba11197a41f476f9ba17880f414d74f821a072ec5061eab040a2acba3d9856ff8555dfe5eaeb14ca19", + "0xb34c9d1805b5f1ce38a42b800dec4e7f3eb8c38e7d2b0a525378e048426fed150dbfe9cc61f5db82b406d1b9ff2d10bf", + "0xa36fdc649dc08f059dfa361e3969d96b4cc4a1ebf10b0cd01a7dd708430979e8d870961fef85878f8779b8e23caafb18", + "0x88dfc739a80c16c95d9d6f73c3357a92d82fa8c3c670c72bee0f1e4bac9ec338e1751eb786eda3e10f747dd7a686900f", + "0x84a535ad04f0961756c61c70001903a9adf13126983c11709430a18133c4b4040d17a33765b4a06968f5d536f4bfb5c5", + "0x8c86d695052a2d2571c5ace744f2239840ef21bb88e742f050c7fa737cd925418ecef0971333eb89daa6b3ddfede268c", + "0x8e9a700157069dc91e08ddcbdde3a9ad570272ad225844238f1015004239c542fceb0acce6d116c292a55f0d55b6175e", + "0x84d659e7f94e4c1d15526f47bc5877a4ef761c2a5f76ec8b09c3a9a30992d41b0e2e38ed0c0106a6b6c86d670c4235f3", + "0xa99253d45d7863db1d27c0ab561fb85da8c025ba578b4b165528d0f20c511a9ca9aff722f4ff7004843f618eb8fced95", + "0x89a3cacb15b84b20e95cd6135550146bbe6c47632cc6d6e14d825a0c79b1e02b66f05d57d1260cb947dc4ae5b0283882", + "0x8385b1555e794801226c44bd5e878cbe68aeac0a19315625a8e5ea0c3526b58cdd4f53f9a14a167a5e8a293b530d615a", + "0xb68c729e9df66c5cd22af4909fb3b0057b6a231c4a31cd6bf0fa0e53c5809419d15feb483de6e9408b052458e819b097", + "0x924f56eda269ec7ec2fc20c5731bf7f521546ddf573ccbe145592f1c9fee5134747eb648d9335119a8066ca50a1f7e50", + "0xb2100a26b9c3bec7ec5a53f0febbf56303f199be2f26b2d564cfee2adc65483b84192354f2865c2f4c035fa16252ae55", + "0x8f64dbed62e638563967ec1605a83216aed17eb99aa618c0543d74771ea8f60bbb850c88608d4f8584f922e30a8a0a72", + "0xb31b9e1ffe8d7260479c9413f8e680f3fe391ae8fcf44fcca3000d9b2473a40c1d32299f8f63865a57579a2d6c7e9f08", + "0xa5b1d136142eb23e322c6c07cb838a3f58ab6925472352ebd0bb47041a0d8729e1074ca223922f3a7a672ced7a1e562d", + "0x8d9470a5a15d833a447b5f108333d50f30aa7659e331c3f8080b1e928a99922edc650466a2f54f3d48afdb34bff42142", + "0x866368f5891564e5b2de37ad21ff0345c01129a14ea5667f9b64aad12d13ec034622872e414743af0bf20adb2041b497", + "0x88ef9c2ebf25fd0c04b7cfa35fbac2e4156d2f1043fa9f98998b2aa402c8f9a4f1039e782451a46840f3e0e4b3fa47d3", + "0x94ba04a4859273697e264a2d238dc5c9ff573ebc91e4796ea58eebe4080c1bf991255ab2ad8fb1e0301ce7b79cc6e69b", + "0x86b6bd0953309a086e526211bf1a99327269304aa74d8cdc994cee63c3a2d4b883e832b0635888dff2a13f1b02eb8df4", + "0x843ea6ea5f2c7a1fd50be56a5765dcce3ea61c99b77c1a729ee0cd8ec706385ac7062e603479d4c8d3527f030762d049", + "0x8d3675195a3b06f2d935d45becc59f9fa8fa440c8df80c029775e47fe9c90e20f7c8e4cc9a2542dd6bfe87536c428f0d", + "0x8978580b0c9b0aa3ab2d47e3cfd92fa891d3ddee57829ee4f9780e8e651900457d8e759d1a9b3e8f6ae366e4b57f2865", + "0x890112ec81d0f24b0dfbb4d228e418eff02ae63dc691caf59c1d103e1d194e6e2550e1bec41c0bfdb74fed454f621d0c", + "0x97da00bd4b19d1e88caff7f95b8b9a7d29bc0afe85d0c6a163b4b9ef336f0e90e2c49ce6777024bb08df908cc04ea1ca", + "0xb458268d275a5211106ccaa8333ce796ef2939b1c4517e502b6462e1f904b41184a89c3954e7c4f933d68b87427a7bfd", + "0xaac9c043ba8ba9283e8428044e6459f982413380ee7005a996dc3cc468f6a21001ecaa3b845ce2e73644c2e721940033", + "0x82145013c2155a1200246a1e8720adf8a1d1436b10d0854369d5b1b6208353e484dd16ce59280c6be84a223f2d45e5e2", + "0xb301bafa041f9b203a46beab5f16160d463aa92117c77a3dc6a9261a35645991b9bafcc186c8891ca95021bd35f7f971", + "0xa531b8d2ac3de09b92080a8d8857efa48fb6a048595279110e5104fee7db1dd7f3cfb8a9c45c0ed981cbad101082e335", + "0xa22ac1d627d08a32a8abd41504b5222047c87d558ffae4232cefdeb6a3dc2a8671a4d8ddfba2ff9068a9a3ffb0fe99b1", + "0xb8d9f0e383c35afb6d69be7ff04f31e25c74dd5751f0e51290c18814fbb49ee1486649e64355c80e93a3d9278bd21229", + "0x8165babccd13033a3614c878be749dfa1087ecbeee8e95abcfffe3aa06695711122cb94477a4d55cffd2febf0c1173de", + "0xa4c1bc84ecb9d995d1d21c2804adf25621676d60334bd359dac3a2ec5dc8de567aa2831c10147034025fb3e3afb33c4b", + "0xb77307cab8e7cb21e4038493058fb6db9e2ec91dda9d7f96f25acbc90309daf7b6d8a205682143ee35d675e9800c3b08", + "0xaaf7466083cd1f325ba860efe3faf4cebe6a5eecf52c3e8375d72043a5cfc8e6cb4b40f8e48f97266e84f0d488e8badf", + "0x9264a05a3abc2a5b4958f957f3a486a5eb3ddd10ff57aa6943c9430d0cfa01d63b72695b1ade50ac1b302d312175e702", + "0xb3f9e4c589ad28b1eceed99dc9980fac832524cfcbe4a486dfeedb4b97c080e24bdb3967e9ca63d2240e77f9addfaefd", + "0xb2c1e253a78e7179e5d67204422e0debfa09c231970b1bfb70f31a8d77c7f5059a095ca79d2e9830f12c4a8f88881516", + "0x81865a8a25913d1072cb5fd9505c73e0fde45e4c781ddd20fb0a7560d8b1cd5e1f63881c6efc05360e9204dfa6c3ce16", + "0xab71c2ea7fa7853469a2236dedb344a19a6130dc96d5fd6d87d42d3fffda172557d203b7688ce0f86acd913ce362e6cd", + "0x8aa2051bc3926c7bd63565f3782e6f77da824cb3b22bb056aa1c5bccfa274c0d9e49a91df62d0e88876e2bd7776e44b9", + "0xb94e7074167745323d1d353efe7cfb71f40a390e0232354d5dfd041ef523ac8f118fb6dcc42bf16c796e3f61258f36f8", + "0x8210fcf01267300cb1ccf650679cf6e1ee46df24ae4be5364c5ff715332746c113d680c9a8be3f17cacaeb3a7ba226ce", + "0x905ac223568eedc5acd8b54e892be05a21abbb4083c5dbec919129f9d9ffa2c4661d78d43bf5656d8d7aafa06f89d647", + "0xa6e93da7e0c998e6ce2592d1aa87d12bf44e71bec12b825139d56682cdce8f0ba6dbfe9441a9989e10578479351a3d9d", + "0xacde928a5e2df0d65de595288f2b81838155d5673013100a49b0cb0eb3d633237af1378148539e33ccd1b9a897f0fec3", + "0xa6e1a47e77f0114be6ae7acd2a51e6a9e38415cce7726373988153cdd5d4f86ef58f3309adc5681af4a159300ed4e5b5", + "0xad2b6a0d72f454054cb0c2ebc42cd59ff2da7990526bd4c9886003ba63b1302a8343628b8fe3295d3a15aa85150e0969", + "0xb0bc3aea89428d7918c2ee0cc57f159fba134dad224d0e72d21a359ca75b08fbb4373542f57a6408352033e1769f72c6", + "0xaad0497525163b572f135fad23fdd8763631f11deeaf61dea5c423f784fe1449c866040f303555920dc25e39cdb2e9b4", + "0x8ce5d8310d2e17342bf881d517c9afc484d12e1f4b4b08ad026b023d98cba410cd9a7cc8e2c3c63456652a19278b6960", + "0x8d9d57dbb24d68b6152337872bd5d422198da773174ade94b633f7c7f27670ff91969579583532ae7d8fe662c6d8a3b0", + "0x855a1c2d83becb3f02a8f9a83519d1cb112102b61d4cdd396844b5206e606b3fefdbcc5aa8751da2b256d987d74d9506", + "0x90eb7e6f938651f733cf81fcd2e7e8f611b627f8d94d4ac17ac00de6c2b841e4f80cada07f4063a13ae87b4a7736ca28", + "0x8161459a21d55e7f5f1cecfc1595c7f468406a82080bfa46d7fb1af4b5ec0cd2064c2c851949483db2aa376e9df418e6", + "0x8344ccd322b2072479f8db2ab3e46df89f536408cba0596f1e4ec6c1957ff0c73f3840990f9028ae0f21c1e9a729d7df", + "0x929be2190ddd54a5afe98c3b77591d1eae0ab2c9816dc6fe47508d9863d58f1ea029d503938c8d9e387c5e80047d6f1e", + "0x856e3d1f701688c650c258fecd78139ce68e19de5198cf1cd7bb11eba9d0f1c5af958884f58df10e3f9a08d8843f3406", + "0x8490ae5221e27a45a37ca97d99a19a8867bcc026a94f08bdccfbb4b6fa09b83c96b37ec7e0fd6ee05f4ae6141b6b64a8", + "0xb02dbd4d647a05ac248fda13708bba0d6a9cd00cae5634c1938b4c0abbb3a1e4f00f47aa416dcd00ffcdf166330bff9a", + "0x9076164bb99ca7b1a98d1e11cb2f965f5c22866658e8259445589b80e3cb3119c8710ede18f396ba902696785619079c", + "0xaacf016920936dae63778ad171386f996f65fe98e83cfcdd75e23774f189303e65cc8ad334a7a62f9230ed2c6b7f6fa4", + "0xa8031d46c7f2474789123469ef42e81c9c35eb245d38d8f4796bba406c02b57053f5ec554d45373ab437869a0b1af3f0", + "0xa4b76cd82dc1f305a0ee053e9a4212b67f5acc5e69962a8640d190a176b73fbc2b0644f896ff3927cd708d524668ed09", + "0xb00b029c74e6fdf7fb94df95ef1ccad025c452c19cddb5dccfb91efdcb8a9a1c17847cfa4486eae4f510e8a6c1f0791a", + "0x9455e5235f29a73e9f1a707a97ddb104c55b9d6a92cc9952600d49f0447d38ea073ee5cf0d13f7f55f12b4a5132f4b10", + "0xae118847542ed1084d269e8f3b503d0b6571a2c077def116ad685dcca2fca3dcb3f86e3f244284bdcd5ae7ac968d08a5", + "0x8dcb4965cd57e8b89cd71d6fc700d66caa805bfd29ab71357961527a7894e082d49145c2614b670dcb231ab9050d0663", + "0xadd6ed14f3183f4acc73feea19b22c9a330e431c674e5034924da31b69e8c02d79b570d12ef771a04215c4809e0f8a80", + "0x96ae7e110412ee87d0478fdbdbaab290eb0b6edd741bb864961845e87fd44bcbe630371060b8104d8bf17c41f2e3fca0", + "0xa20db17f384e9573ca0928af61affab6ff9dd244296b69b026d737f0c6cd28568846eca8dadf903ee0eecbb47368351d", + "0x937bfdf5feb0797863bc7c1be4dcc4f2423787952a3c77dfa3bfe7356f5dbcc4daebde976b84fc6bd97d5124fb8f85c9", + "0xa7050cc780445c124e46bba1acc0347ddcfa09a85b35a52cc5808bf412c859c0c680c0a82218f15a6daeefe73f0d0309", + "0xa9d9b93450e7630f1c018ea4e6a5ca4c19baa4b662eadfbe5c798fe798d8a3775ed1eb12bd96a458806b37ab82bdc10a", + "0xa52a4d5639e718380915daaefad7de60764d2d795443a3db7aeab5e16a1b8faa9441a4ccc6e809d8f78b0ac13eef3409", + "0x8e6f72b6664a8433b032849b03af68f9376b3c16c0bc86842c43fc7bf31e40bc9fc105952d5c5780c4afa19d7b802caa", + "0xa107ae72f037000c6ee14093de8e9f2c92aa5f89a0a20007f4126419e5cb982469c32187e51a820f94805c9fccd51365", + "0x9708218f9a984fe03abc4e699a4f3378a06530414a2e95e12ca657f031ef2e839c23fd83f96a4ba72f8203d54a1a1e82", + "0xb9129770f4c5fcac999e98c171d67e148abd145e0bf2a36848eb18783bb98dff2c5cef8b7407f2af188de1fae9571b1c", + "0x88cc9db8ff27eb583871eeeb517db83039b85404d735517c0c850bdfa99ae1b57fd24cf661ab60b4726878c17e047f37", + "0xa358c9aadc705a11722df49f90b17a2a6ba057b2e652246dc6131aaf23af66c1ca4ac0d5f11073a304f1a1b006bc0aa5", + "0xac79f25af6364a013ba9b82175ccee143309832df8f9c3f62c193660253679284624e38196733fb2af733488ab1a556e", + "0x82338e3ed162274d41a1783f44ae53329610134e6c62565353fbcc81131e88ce9f8a729d01e59e6d73695a378315111b", + "0xaa5ddcabf580fd43b6b0c3c8be45ffd26c9de8fa8d4546bb92d34f05469642b92a237d0806a1ad354f3046a4fcf14a92", + "0xb308d2c292052a8e17862c52710140ffafa0b3dbedd6a1b6334934b059fe03e49883529d6baf8b361c6e67b3fbf70100", + "0x96d870a15c833dddd8545b695139733d4a4c07d6206771a1524500c12607048731c49ec4ac26f5acc92dd9b974b2172c", + "0x8e99ee9ed51956d05faaf5038bffd48a2957917a76d9974a78df6c1ff3c5423c5d346778f55de07098b578ad623a390e", + "0xa19052d0b4b89b26172c292bbf6fd73e7486e7fd3a63c7a501bbd5cf7244e8e8ce3c1113624086b7cdf1a7693fdad8b5", + "0x958957caf99dc4bb6d3c0bc4821be10e3a816bd0ba18094603b56d9d2d1383ccc3ee8bc36d2d0aea90c8a119d4457eb4", + "0x8482589af6c3fc4aa0a07db201d8c0d750dd21ae5446ff7a2f44decf5bff50965fd6338745d179c67ea54095ecd3add4", + "0x8a088cc12cf618761eaa93da12c9158b050c86f10cd9f865b451c69e076c7e5b5a023e2f91c2e1eed2b40746ca06a643", + "0x85e81101590597d7671f606bd1d7d6220c80d3c62e9f20423e734482c94547714a6ac0307e86847cce91de46503c6a8a", + "0xb1bd39b481fc452d9abf0fcb73b48c501aaae1414c1c073499e079f719c4e034da1118da4ff5e0ce1c5a71d8af3f4279", + "0x942ae5f64ac7a5353e1deb2213f68aa39daa16bff63eb5c69fc8d9260e59178c0452227b982005f720a3c858542246c8", + "0x99fea18230e39df925f98e26ff03ab959cae7044d773de84647d105dfa75fd602b4f519c8e9d9f226ec0e0de0140e168", + "0x97b9841af4efd2bfd56b9e7cd2275bc1b4ff5606728f1f2b6e24630dbe44bc96f4f2132f7103bca6c37057fc792aeaab", + "0x94cdad044a6ab29e646ed30022c6f9a30d259f38043afcea0feceef0edc5f45297770a30718cbfec5ae7d6137f55fe08", + "0xa533a5efa74e67e429b736bb60f2ccab74d3919214351fe01f40a191e3ec321c61f54dd236f2d606c623ad556d9a8b63", + "0xb7bd0bb72cd537660e081f420545f50a6751bb4dd25fde25e8218cab2885dd81ffe3b888d608a396dfcb78d75ba03f3f", + "0xb1479e7aa34594ec8a45a97611d377206597149ece991a8cef1399738e99c3fa124a40396a356ab2ea135550a9f6a89f", + "0xb75570fc94b491aef11f70ef82aeb00b351c17d216770f9f3bd87f3b5ac90893d70f319b8e0d2450dc8e21b57e26df94", + "0xa5e3f3ab112530fe5c3b41167f7db5708e65479b765b941ce137d647adb4f03781f7821bb4de80c5dc282c6d2680a13d", + "0xb9b9c81b4cac7aca7e7c7baac2369d763dd9846c9821536d7467b1a7ec2e2a87b22637ab8bbeddb61879a64d111aa345", + "0xb1e3ee2c4dd03a60b2991d116c372de18f18fe279f712829b61c904103a2bd66202083925bc816d07884982e52a03212", + "0xa13f0593791dbbd360b4f34af42d5cc275816a8db4b82503fe7c2ff6acc22ae4bd9581a1c8c236f682d5c4c02cc274cc", + "0x86ba8238d3ed490abcc3f9ecc541305876315fb71bca8aaf87538012daab019992753bf1e10f8670e33bff0d36db0bf0", + "0xb65fbb89fafb0e2a66fe547a60246d00b98fe2cb65db4922d9cef6668de7b2f4bb6c25970f1e112df06b4d1d953d3f34", + "0xabb2d413e6f9e3c5f582e6020f879104473a829380b96a28123eb2bdd41a7a195f769b6ac70b35ba52a9fee9d6a289c3", + "0x88ec764573e501c9d69098a11ea1ad20cdc171362f76eb215129cfcca43460140741ea06cee65a1f21b708afb6f9d5b0", + "0xa7aaec27246a3337911b0201f4c5b746e45780598004dac15d9d15e5682b4c688158adffdef7179abb654f686e4c6adc", + "0xa1128589258f1fbfa33341604c3cb07f2a30c651086f90dce63ae48b4f01782e27c3829de5102f847cde140374567c58", + "0xaaf2b149c1ca9352c94cc201125452b1ed7ca7c361ed022d626899426cb2d4cc915d76c58fa58b3ad4a6284a9ae1bc45", + "0xaaf5c71b18b27cd8fe1a9028027f2293f0753d400481655c0d88b081f150d0292fb9bd3e6acabb343a6afb4afdb103b5", + "0x947c0257d1fb29ecc26c4dc5eab977ebb47d698b48f9357ce8ff2d2ed461c5725228cc354a285d2331a60d20de09ff67", + "0xb73e996fa30f581699052ed06054c474ebdf3ae662c4dc6f889e827b8b6263df67aeff7f2c7f2919df319a99bdfdceb1", + "0xb696355d3f742dd1bf5f6fbb8eee234e74653131278861bf5a76db85768f0988a73084e1ae03c2100644a1fa86a49688", + "0xb0abca296a8898ac5897f61c50402bd96b59a7932de61b6e3c073d880d39fc8e109998c9dba666b774415edddcff1997", + "0xb7abe07643a82a7cb409ee4177616e4f91ec1cf733699bf24dec90da0617fe3b52622edec6e12f54897c4b288278e4f3", + "0x8a3fae76993edbc81d7b47f049279f4dd5c408133436605d934dee0eadde187d03e6483409713db122a2a412cd631647", + "0x82eb8e48becfdf06b2d1b93bf072c35df210cf64ed6086267033ad219bf130c55ee60718f28a0e1cad7bc0a39d940260", + "0xa88f783e32944a82ea1ea4206e52c4bcf9962b4232e3c3b45bd72932ee1082527bf80864ce82497e5a8e40f2a60962d0", + "0x830cf6b1e99430ae93a3f26fbfb92c741c895b017924dcd9e418c3dc4a5b21105850a8dd2536fa052667e508b90738f2", + "0x990dce4c2c6f44bb6870328fba6aa2a26b0b8b2d57bfb24acf398b1edc0f3790665275f650884bd438d5403973469fa2", + "0xa2e5b6232d81c94bcb7fed782e2d00ff70fc86a3abddbe4332cb0544b4e109ae9639a180ae4c1f416752ed668d918420", + "0xb4cdf7c2b3753c8d96d92eb3d5fa984fef5d346a76dc5016552069e3f110356b82e9585b9c2f5313c76ffaecef3d6fd8", + "0x83b23b87f91d8d602bff3a4aa1ead39fcc04b26cf113a9da6d2bd08ba7ea827f10b69a699c16911605b0126a9132140f", + "0x8aae7a2d9daa8a2b14f9168fe82933b35587a3e9ebf0f9c37bf1f8aa015f18fb116b7fba85a25c0b5e9f4b91ba1d350b", + "0x80d1163675145cc1fab9203d5581e4cd2bed26ad49f077a7927dec88814e0bed7912e6bbe6507613b8e393d5ee3be9be", + "0x93ddeb77b6a4c62f69b11cf36646ed089dcaa491590450456a525faf5659d810323b3effa0b908000887c20ac6b12c80", + "0x9406360a2b105c44c45ba440055e40da5c41f64057e6b35a3786526869b853472e615e6beb957b62698a2e8a93608e13", + "0x93bfc435ab9183d11e9ad17dac977a5b7e518db720e79a99072ce7e1b8fcb13a738806f414df5a3caa3e0b8a6ce38625", + "0x8a12402c2509053500e8456d8b77470f1bbb9785dd7995ebbbe32fd7171406c7ce7bd89a96d0f41dbc6194e8f7442f42", + "0xaab901e35bf17e6422722c52a9da8b7062d065169bf446ef0cbf8d68167a8b92dab57320c1470fee1f4fc6100269c6e2", + "0x8cad277d9e2ba086378190d33f1116ba40071d2cb78d41012ec605c23f13009e187d094d785012b9c55038ec96324001", + "0x85511c72e2894e75075436a163418279f660c417e1d7792edce5f95f2a52024d1b5677e2e150bf4339ad064f70420c60", + "0x85549ca8dcbe49d16d4b3e2b8a30495f16c0de35711978ada1e2d88ad28e80872fca3fb02deb951b8bcb01b6555492e4", + "0x8d379ab35194fe5edf98045a088db240a643509ddc2794c9900aa6b50535476daa92fd2b0a3d3d638c2069e535cd783b", + "0xb45cfebe529556b110392cb64059f4eb4d88aaf10f1000fdd986f7f140fdd878ce529c3c69dfd2c9d06f7b1e426e38f3", + "0xac009efd11f0c4cdd07dd4283a8181420a2ba6a4155b32c2fed6b9f913d98e057d0f5f85e6af82efc19eb4e2a97a82df", + "0xb2c2cdffa82f614e9cb5769b7c33c7d555e264e604e9b6138e19bcfc49284721180b0781ecbf321d7e60259174da9c3c", + "0x95789960f848797abbe1c66ef05d01d920228ca1f698130c7b1e6ca73bfda82cee672d30a9787688620554e8886554ee", + "0x98444018fa01b7273d3370eeb01adc8db902d5a69b9afc0aa9eadfeb43c4356863f19078d3c0d74e80f06ecf5a5223f4", + "0x87d20b058050542f497c6645de59b8310f6eeec53acbc084e38b85414c3ea3016da3da690853498bde1c14de1db6f391", + "0xa5c12b3a40e54bee82a315c503c1ce431309a862458030dde02376745ec1d6b9c1dbeea481ae6883425e9dae608e444e", + "0xb9daa3bf33f0a2979785067dcece83250e7bf6deb75bb1dbbab4af9e95ddfb3d38c288cbef3f80519a8916a77a43b56c", + "0xb682ec3118f71bde6c08f06ea53378ea404f8a1c4c273dd08989f2df39d6634f6463be1d172ac0e06f0fa19ac4a62366", + "0xa4f94fd51ecf9d2065177593970854d3dce745eebb2a6d49c573cbf64a586ae949ddfa60466aaef0c0afb22bd92e0b57", + "0x86cd5609efd570c51adbc606c1c63759c5f4f025fcbefab6bc3045b6ad2423628c68f5931ff56fdda985168ce993cc24", + "0x981192e31e62e45572f933e86cdd5b1d28b1790b255c491c79bd9bb4964359b0e5f94f2ae0e00ef7fe7891b5c3904932", + "0x9898f52b57472ebc7053f7bf7ab6695ce8df6213fc7f2d6f6ea68b5baad86ec1371a29304cae1baadf15083296958d27", + "0xb676c4a8a791ae00a2405a0c88b9544878749a7235d3a5a9f53a3f822e0c5c1b147a7f3f0fc228049dc46e87aa6b6368", + "0x9976e10beff544e5c1645c81a807739eff90449df58ffdd8d1aa45dd50b4c62f9370538b9855a00dd596480f38ebe7a5", + "0xa0e91404894187ec23c16d39d647ada912a2c4febfd050a1ea433c4bfdc1568b4e97a78a89ba643aca3e2782033c3c58", + "0x91a6ea9a80476ed137eb81558ff1d55b8581663cccd41db4fc286876226b6515fd38661557419e1e46b6a3bc9cda3741", + "0xb9e8a1e23c60335a37a16f8085f80178a17d5e055d87ffe8cf63c532af923e5a5a2d76cf078164fb577996683796caa6", + "0xad8e151d87a37e8df438d0a6a7c02c3f511143efb93fde8aef334d218cb25932baf9e97c2f36c633620a024a5626af3d", + "0x978f942f210e8a482015e6fdc35a4c967c67b66e6e2a17a05cc7a0f2163aed227b775d4352b0c3cca6cbf4bd5bafaf75", + "0xb5e2e3d8b2e871c07f5899e108e133f87479959b80cb8a103fbecde00ccdbfbd997540eef33079c5cc14b1c00c009fd1", + "0x88a164b3fefd36857f429ab10002243b053f5d386466dbb9e5135ed3c72dd369a5a25e5e2aaa11f25488535e044e2f12", + "0xa66091c0db4e7cf05a089ec2b9ff74744354d0196968201f5e201699144b52bb13b4e68e12502727163e6db96e3565f2", + "0x8e65aff8e37240461b7374c20bfd1d58b73a525c28994a98f723daed9486130b3189f8efe5c5efcd7f5390cc366038da", + "0x8b37c21dd7304c3aa366959ba8c77ea8b22164a67e136808b6f8e48604297f7429a6c6ecf67b1d09b8b7ec083eacd7e0", + "0xb689b1277ad050f53da91a702516a06d7406ff33a4714ea859b3b2b69f8d0aa8f983c7e039b19c0759a3815d841fa409", + "0xb17f7a0a182ed4937f88489e4c4e6163dcf49fd2ea4d9efbba8126c743bea951cd769752acd02e921774dc8ebcfae33b", + "0x8b7fab4f90be825ac5d782a438e55c0a86be1c314a5dbc3cc6ed60760a8a94ef296391f1f6363652200cce4c188dae67", + "0xab8410c4eaa2bb43b0dd271aa2836061bc95cb600b0be331dada76ddb46711ff7a4ad8c466cc1078b9f9131f0dc9d879", + "0x9194bd7b3cc218624459d51c4d6dbc13da5d3de313448f8175650fa4cfab7cc4afcda5427b6676c3c13897dc638b401e", + "0x980f61a0f01349acd8fc9fdc88fc2c5813610c07eecb6ab14af0845a980792a60dadf13bb4437b0169ae3eff8f5984ce", + "0xb783bee24acea9c99d16434195c6940cf01fc2db135e21f16acae45a509eca3af6b9232a8aa3a86f9715c5f6a85cb1c3", + "0xa3079931c4b90966d1faa948db847741878b5828bc60325f5ebe554dcab4adcc19ee8bce645e48a8f4a9413bb3c6a093", + "0x801f61ac9318f6e033a99071a46ae06ed249394638c19720831fff850226363a4ae8486dd00967746298ee9f1d65462f", + "0xb34dbbed4f3bb91f28285c40f64ce60c691737cc2b2d2be5c7d0210611cd58341bb5bda51bb642d3ee2d80882e642a13", + "0x8750af19abfb915e63c81542b13d84526a0c809179bbcc1cd8a52b29f3aba3ae0f7cf6f4f01790bf64ef7db01d8ee887", + "0xa6ea10000eb2dd4efc242ac95bc3b3873cdd882fbeb7c9538c87e3143a263ca3a2e192b2159316a625cfb5fb0b6cdcb3", + "0xaa40ca54bc758a6c64cb932924917581062e088b3ad43976b28f2e11d8a7dea73f1fb50aeaa0e70182bb2dc07d805bb9", + "0xa4779dfd25b5ec9d75dfb54a4bb030364899a5e75c1492403acb19f2adc782c7ac4daeb66d2f5aeb74135afe9f318e3f", + "0xb4551e2805d63ca453f4f38b1921ac87ff687e1d70575ad38f3469d6f0608ef76b7b1b98ae1e6b1e7d928773aaab6e3b", + "0x99490ee722f96aad2743b08dd37bfeb75a8c59efaee4c9b694eaa05eb8a6bb23861a4480544c7617d04d23fd5e2543b4", + "0x8a7050d964d295fff98ae30d77ce730a055719313457e773fcce94c4d71a9b7cf63db67e54a8aab20fb1335b0130b5d5", + "0x903144e6bbee0a4fec17ff80fef0d2103981140c3d41776cfb184ced17f480a687dd093f6b538584327e6142812e3cd5", + "0xa5b30f7c6939bdc24a84ae784add927fec798b5a5ee3dd156c652df020728dd6d43898be364cf5ee181725fbcffc0964", + "0xb43d97ec2bc66af92d921a5c5c20a03ef2be2bc2c9b345f46d8287409fcbfd88ebc49d4509d64468222cd1d2021bf236", + "0x82dc23c7f5086c9ac6b4566359bfb830d203544b0d8332a210775670f899cd9ff48b94bfeba40040c25664ebdd5cfad8", + "0x9294cd017fea581dabb73dcc8c619904d7e022b664b0a8502c9d30f3807668af279948e7e41030ae296d492225297e95", + "0x8d6c9dc636c8e884f9a4299e5cff06d044ebc94ad783a4b71788347ea4a336d4d048b8a9ecabae789e8fcdc459723dfb", + "0x801a80bc49e882ec81b04e37407713f033f7bdac79252dfa3dc8c5bd0229fcbd4019890e402cf843b9378df08f72ab84", + "0xb4313ca32569d973900f6196363c0b280ddfa1b47c88d019e5f399b805b444a777950fc21ae198fc23ece52674b94abf", + "0x96f06056fd255fdabf78986e315e7c4fdf5495cf850536b7976baa97a994cc6a99c34609c33a0f2facba5e6f1026dce6", + "0x983ed80220a5545ffd70ef5e6ac10217d82ec9cd8f9a27ee77a5ff4074092308c0e6396fc4e9932a77ddd474e61f8b55", + "0x872a059aa630af73c4abbd076e8b333a973ffc5bdecf5dcc0600b00162184213cb19d4f601795030033beb808d5810ce", + "0xb040f318d9d3b8833da854014a44296dbd6762dd17cab13f91987256c54353b7f0800547cb645a7cc231997454209fdd", + "0xa8c4731a555308e8ce0b8325eb7a4cbf6113d07e9f41932df04480b72628d313b941c7055f1cc2ac45c7353b56e96ca9", + "0x8c24031440b77637e045a52e5ea3f488926ab0b426148975edf066c40a4581beecc1bfb18fc4cf5f9f96dc6681b4bd28", + "0xb39254b475abf342f301298feaa17a4b3051f30ea23a18acf59e003e2704ac96fe40691f1da387913bdf7aee6389f9a8", + "0xa1dbf938b604ccc6d60881cc71f38df568aa02752aa44d123514154017503f6c1c335ae43e359f1487bc8934073cd9c1", + "0x8d52aa1be9f429ece0580498d8fe9fef46d4a11f49436a82b8927f9503dacc41245907f126594c1cd30701286f8c092c", + "0xb826f396486942c0326d16f30a01b00a682c30a75553dc6ac34fd5b3e96b13c33b94738f522eebaffb59ff8c571c76e9", + "0xaa89f51cbf6e6c3e2aa2806187b69ab3361c84e89f393f3ed284fe84db46fc3944aa44f8928e3964f9c1a1ec27048f68", + "0xa254df0efa4203fb92b42a1cd81ca955922e14bf408262c8f7cb7dc703da0ca2c71556bd2d05b22ce9a90ad77309833d", + "0x93263c507e4d5f4e5df88e85b3d85c46ea729fb542a718b196333e2d9fb8a2e62dc1347cf146466a54ba12d200ef09d9", + "0x922e3c4a84246d89a07aa3e90f02e04b2cea9bebc0e68b742156f702aed31b28c6dfa7ac936ea2fc2e029adf68361f98", + "0x9a00628eeeda4ccbed3ef7834149aec4c77aac1a14bc2491ba5d1a4a2c5d29afb82ceaa5aac1c5ce1e42cdcaf53e30ba", + "0xab3a88df36d703920f6648a295a70ffa5316c96044f39ff132937bfda768937cb6a479e9ba4a4e66b377f3a9996a88c4", + "0x966b11526ab099d550ab33c6a9667e5cfdedf255da17a80a519d09acd78d2ea24ec18bd1ea7d8d63cf0a408f1c1fe0b3", + "0xb5c21b9817dc32f3df9d9988aa3560e1e840d586d01cd596bc0f850ab416b6013cbf7dbfd05ac981f26014c74bd2d2b2", + "0x9040abef5e2523e7f139c9f744a64b98fea3a57952059ffe4d5ed77fa87068203c090ef4e7f52c88fb82ea8a6fdca33e", + "0xa0dcdaeb7d3f5d30d49c004c5f478818c470187f4b0b4856812dcd1b3a86de58a99acb8ceb44c6b80c3060cf967c43a4", + "0xb5f4be9a69e4a6719ea91104820df8623b6d1073e8ee4168de10a7e49c8babea772bcbc6b0908185e98d607e49cd3609", + "0x8634020a5a78650015763c06121c606d2dd7b324aa17387910513dd6480fb797df541fc15b70d269b2794ad190595084", + "0x9504d1d0fb31ff1926c89040c04d51fd1f5cddf9d7ca3d036e7fd17e7a0f767ef33cee1d8bf7e17e2bc40949e7630417", + "0x812c72846ef6d692cf11d8f8c3de8fa78cc287303315114492667b19c702cd24d462020f1276895df26e937c38f361f8", + "0x8c97aa5e9ef2aa9a1435ef9ddfe62e850f0360864ed5fb82bf9fef4ef04d8fb4f827dc078bc911ee275e4501edd6617c", + "0xac5f7af5e23c8e429aaa6b6825129922b59d25b4608f07b65f21388a9ac3aa89096712f320afe6d56e44e1f0d51a4eb9", + "0xa8c84d9a8593a0cb5be1e450960f59878a4e6b70da54a7613dfc25911b7cc9e6d789d39401b0a0d6471ab9dcdc707976", + "0x8c9d5fd89611392c0f085ffa4fa642a181f0b9b23593deb5e10fdd1642722ca75ef34a037e88a8d03f2888fe7461f27c", + "0x8c74b05f91fb95c85e7bd41f6d9a1e41e667e68f3d19b325c1f25df1767019919edab89b92af237896cbc4e6d6dc1854", + "0xa3caecb91640821f0b2c4981b23f2069df8d2b98ce026c1538bc096b292f5f956a5d52c1c8d6a8165a1608083ba6494b", + "0x8ae8e0c36f8b79a69176ff29855df45d0fcd9e4d1dbaed8899f8fcdece676e418ec034a6c161e2a894f0c834aaecbfd1", + "0xb88d18c67dc3b1b6ed60ee437c441c1ed14ecddebccf43683605716f30058b1aa4ba05ff10cd8171ee97d8f58d70c094", + "0x94f43d84dcdfd9cd19115c7d8e9c1e856828eafbfdec93b876cf0007e317e30b2ad951dbabc186aa6ef90fdee4d91990", + "0xb44e4723f41fc1d5b0057f371e3381ae02566590b3f964b6eb07b2104f66ff78410c407235fa98d04f635694f3baca09", + "0xaddd8390173d29ca0811534d389253831fed75fed135398617836b6e70767269eacb1560b39a58f02042ca3b97fe59c4", + "0x80bdbdacc0c358c7ea52aeacdc5f9ceb6928bcf6e7dee7c17d8ae3bf7c2372aa7a0372363888968fc0921aaf4776d5d0", + "0xa486e2b6f04f403f9e609d69dfb3cfb992af56ecad1683271df3e3faa3b86638b81e73b39978fb829ee7133d72901f2d", + "0xa19472da57457e10c6a6307895393ddaec8f523760d66937fe26a025817319e234eaf69756ffdf1b84c81733424a96d7", + "0xad6a195397cbc2d75171f5e82090441eed60bd1ba42c39ef565b8b5a8281b04400678625b1dc46d617f694a7652a8e5d", + "0x8f98e721c06cec432e2221f2e1b06bb1469d916a8d88d6973acf68d1e003441d00390dafcead8ecdbf9eae4509baf5aa", + "0x91d62a0f9d13c59adfe1376ed6d057eae244d13c6b3d99be49a49e0075cf20f4085cf127774644ac93615be9ac9e5db6", + "0xaf45dec199245e2b326a0d79c4899ed44b1c0219db42602a4a6184ace0ff831a3276297af28f92e8b008ba412318e33e", + "0x8754bde54e8d2d169e6a7d6f0eae6097bc0461c395192bd00dd6f105677ea56ab384c02553ea5eeac0a65adcb0df77ee", + "0xb676afd2f5afc37a314c943d496e31b4885efcbcc2061036e370a74cfde5642bb035622d78d693bfc3136fc036c7edb4", + "0xaab6ffe6cc234397cf1822e02912bc282dfb314e92fb5a9e10d0c34ee9b5856d4b76e166bc2bb6fcdd66aabea35ec4ef", + "0xada6e62f90ee6b852ec4b72b22367acac2896f0df2c105beda27096583ddbedddc710d171330569f111c6e44a5b57ae7", + "0x802139dd15241a6de663d9b810121bdd9cf11f7f8c8ca6de63f4f8e731409e40d1fd3558b4f619ed42ee54929dff1c7e", + "0xad8e70531cec21b4e6f55be1751c2d025bd2d7d8158269b054cfe57fa29252d052ce4478ec7db6ec705789e2118d63b3", + "0xa8e4a4271769480e1b33a28c87a150ecc0b48bfe8a15ae04152197881de4ce4b03453aefe574842424edbbe4173e1a3a", + "0xb98c65726296610cef16c5b58da5491acd33bd5c5c5af4d934a9840649ef85730fbce8018dee09ded14e278009ed094a", + "0x8e213a7861223287b860f040e5caaa563daa0b681e4e09ec79ad00cc459238e70bbeaf7486bbe182fc12650700034ec5", + "0xa2879f9e1a556cf89b9b5b3bd8646a8cce6b60bcbc8095df44637f66a2da5858eee2dc9091475a8f64bb5aff849389cd", + "0x8a17cdb4077b9b0bcf28b93294ac5ae4c8bba8839fce0f1012b53187ac008f9858b02925fbfc421f1123afcdbd8b7753", + "0x86fd9c11528aa43946e4415ff64a3ca6409ee6f807368c68997b18605da65e415ccd85ad913820d450cb386593de666d", + "0x8ed55923b963c3d85a91aca11c40ff9c6c7f1e2b9bc199d1a270e5fb16aa62dec0136e97866145ae9d58a493e8b1cbbb", + "0xae32af5b5d418668ae123c639b149e5eed602404e8516da4a61db944b537a3620545e8e3d38cf10cdaea980ab2f80973", + "0x95cb8d9e9d6762d78dde0ad73869ffaca904a7d763a378b8cc11a7933d3e7d1c8aec4271a079b1b00f8887ee5b1ea21f", + "0xb5ea20b42a3ca247f00ab5328c05f0cf194973d5f7271c66c41c5055b1ffdca136be179709e0c1de209fbe07b9820bf3", + "0x98682f7cce471c92a8d6d15fee4ddf4d43dd97c3e3811d2913618ecacc6440b737717c07736ae4558c910e11ee98104e", + "0xa67da2c7cbba48e929ca4e4b9a6299fe01ef79eff8cc5cd3fdbdc0721a68130e4079f30ae151a573a7dcca8ecf2e684e", + "0xa9981c9f9dcbb3b0f6996f664fb2acd7573189f203be37b2b714662aa273551396abfb1f612ccde4e4c8127a050dbe4b", + "0x92d55eff8da600f886da9bf68e8eecf482faa4b268f3f286b3b3e5cc91b19604081498d4905b201bb4ec68e32b5591d9", + "0x963e3f1728de9d719c86d390f3eb9c3f99d1928347fab0abf10dbb37d76b59ddb64d4734c977863a6cd03ffece5ca895", + "0x93480e2de83c921056b6d8628ac37cd5ef7555ba43b0308fc13386cb0515d42c12ecd06057137aa71a7931beaf90b9ce", + "0x8feae57ff0e6a162cc81c99f45c6187d268fc0bee8c2bffc92142ef76c253d201f0e932943cf2fa312982b281ce1066b", + "0x8f8f4bd4200fb87afcd743274480220d77571928000d4197410dbb75439d368df6a06d941a6152206371d2ca9cac99e4", + "0x8ee7f11e79af4478e0a70eb424fe8078237ad99ba6d7e6bf1a8d5e44e40abd22d404bd39b718ad6fdf4c6601f2a47665", + "0xa98acfcec612b574943195b9ba95bebcc9c0b945c9f6b3e8760b2a4635909246a9d73b0b095c27b4ecb3339704e389b7", + "0xb520efd19f65e81dc285031ea3593f8c5dad793e4426beb9196ab46e45346f265fd71e50adb0da657977c60ed5724128", + "0xa3d9d0b7415280ce4dfa2429d47b2b8e37604a5157280a72cc81d541ffe44612dbb3ef7d03693fc42a569169d5842dc3", + "0x8c29e2d0b33801f6d9a9c065a76c5cad1fb0a001506b970307e21765ee97c732a4cbf1d7c1b72d95e0ad340b3b075224", + "0x839e21f292892a6eb596b9b1e9c4bd7c22a6fe71d3d04487c77840028d48392c5cbe73140a4e742338e0c8475cd0c1ad", + "0x8bea5c68e7743998619185bb662e958f1b4d3ca81019d84ac43c88911aab3abe4ee9bcc73cb95aa3ae87c0138801bde3", + "0xb8f262d21a94604049e008ce03dc857848168e1efca4522acb0ccc827ffb37f545e1947843a356563a76bc6489605b66", + "0xa7bd0842b0bb38d9943b82aa883f36f4eb8a6e8a7790d4f87faf306608f51d250a19b73984f1156cef5dd2581664614b", + "0xa993e649bd953627a88a2539dac3a12ec7f37a4c65b01425d9d34edf7ee10a71aa98f65c9e013107f824faf8aee041a9", + "0x8e07eced75c67cb4d2ec01857f6ac1408482e6b31cb2faa249e8cf99f180575587df530c7782a7539b5221121ef48aa0", + "0xb2f4578f26c05ecb9e2669ca744eb19d4f737321ac7d04fafd18beb7866e0fec9dd063953ae1f077b44b9c6f54db1279", + "0xb6b3788a6c7bcaf467d19daf6ab884d549aa866970c05a9181f544ff190d043192c84fe437a75a30b78b425461cca062", + "0xa270684903c61544b85a7041e81f65e787e1c1e23e57538fa8a69836bed0ca1673861dd29f743a1280f2f38eddd3aa83", + "0xa9c2397c4773dcad2821266dadfd2401d013d9f35de6744f2ec201f3507700adb1e6ec4f5a453be4764da8bf68543f26", + "0x83a3025ed6fd5df9d98be32a74e10a0d9728b560942d33ba028536fb148fc34ae87e92be2df3e420a8dfec08da495982", + "0x90dc70c183a90bab988b4a85b7b921c8070af0e5f220364fe11afa0722990b2c971e1e98eef62d3287fedfd9411f1df7", + "0x82d940937a6c636224d04f8e2536f93dcf20dc97a5f188875ad76c21b804aef9af10839419b61143c1f88a695959a6b4", + "0x8017f9473ce49d498d6f168137e77e62fe553e5a51e75b519cf2cbd1ab9afdafad80fd5e6fd0860e640b0d78ca8ed947", + "0x80573a0ec049fe1f7b3013b2839e145cd87e07c0e43826a29ef8c92516f9a30896c2ffcf3ed77ed22a6cf3101b1789d5", + "0x953349abd2559f9824db07cec857ad54f1a05018f3076425f8dbae37f8d92a46af2c04ab7c8ec0250449541187696e98", + "0xab7bd2c4f05ee9a9f252c4e16a20993a12c535c3809d124bae24642616521a9768d3f19eceaf8524583f47ae1f527684", + "0x9883b77ee834ee0112ca2f366d2a6fc213e0cf454e061438c2901a5ba35b7378f64da8adf6a476eb1562991ef5b4a5bc", + "0x89291811db308637356dbf7ed22cf07bfce33eb977734ee346e8c15a231b35d8b4443574f3fa97a40867b3e23b0bbfa4", + "0x93d753849d7d9588d39e38217500b123a6b628a873876612d9f98b5d611f52c89c573432d2176752b5d1cc2d94899b8b", + "0xa45add3c4844db3b7a237295fc85fddc788ac1ec395a0524d2fc90a539571a247146aea4aa10eec30a95e9617c85b98d", + "0x90f94578842db7a4de672da1e483858ece5e466c73c12f725a0fc71f42ff880c9447a33fa9096839bee817536f2591e2", + "0xb2c1b6fb031bb30460f157356562b44b4de096a0a112eab4fb3cc500aad38bc770da1fc2e73caf687a0da5e8537049c0", + "0xafb15e15fd930929c0e3c66482068a5afe0c7b7f82e216a76c5eb1113625bfa0b045a52259d472284cfbaf4796c71456", + "0xad222a9a3d907713418c151b8793d5e37634354322068f8206b9d0da1a3f53b0004193713d23ec35990639a1b6c2e075", + "0xb44a128dce97e8c4b178cdbca0a5c1b3f6e164490fac0fd68dbfe0aafa89920bb4ea420a8527e06c80dd19c2f135e3ef", + "0x8596e993ef18b8d94e9c42a90cb7060affc586b8e9b526820d25124285de5590134e2e86592e9dc4dd45ccf5d578fa60", + "0xb71bb0ad138141ed506b2253e84110d2db97cc2d24a3fd0d096b0022d9f38f87aa74e2f505074632d64e90bcc491aa30", + "0x84841eafd357309de47b92ca5ec163dec094a2e5271bc65898c31932e0160bee165e4decb23af339cfe09c83e1cc5441", + "0x8a2915ee39a6fd4a240b98533d7690ef1773ce578ed1fb05ed414ebe36f7ef289fa46f41768df57190438c356331e329", + "0x90bb337165386f1990cbd8ed2e8321ef21bc18125b015b4da0c37e5fcc446b26005379ee4fad8ce9348ceb4ab49e82e2", + "0xb707b50ea2ab05c6d183671587f25fe29eef23fe569d731459a1ac111a0b83a2cd65b88242876b34aeead3b05a15d745", + "0xae1f159f79b7996315c4f9acce7e21a6ed59d4ef76331196fc86911fda3035edd5c11d568b105175a36c948d0263b382", + "0x922bc525bace05e5dff6b5cabde5469ddd2c1c601f7131abc04ecefdd35095e6ac015b1aec3c3b25c5dee8d139baf60d", + "0xa7b060405b2740f82db64683187b1bb89e5f40c8438663c7cbc8ef2513929fe5f92625667a7f2f599a72a96b1fc8f08a", + "0xb9dfe94a08651db5efefbb813269bce80d814e3089b80c0654491e438d820bf521f8a4a4477909344ba88f7683eebb43", + "0x841817a9729465743576950b6e8eea32ebf39cca99ace86c4792f9f35926e2d6830c52854a3b2eaeb61694e6845008bd", + "0x934128034bde8fc7b93b952aa56e0ed28b36cfa04cfa1f0d5b38266dd40beedff5e0bab86e4717b0fb56c56be2eae26b", + "0xaee9d64caf28596308782cd8f3cf819506daf3378f86157ff775e618596411adf94efd0e9542787ca942066f02cbd332", + "0x85871184db314411a49575fee088c52ed5dba4e916ee001ec24d90898a0154d9790a06aa8a707ca7a8b986c0293b8d89", + "0x8d3d87edcc0187a099c97b581a598d357a41ac152303bb27c849eb78e72e15cb97cf9a0468fc36f245c3e152c76bb7dd", + "0x900475d165dec18b99eb7b5f9e9ad1d2d4f632e55fdcc4c5ecd7775fed462990e6aaafe9c669f40508f9b15f00bda31f", + "0xa25b5954edd57e7811a0d18532043d975c7b44b80f65cd630935d7b16ada05f30fe2b7be7ae8a2f54c25957faf3f1950", + "0xa089019afa3a7a15f7e7874e73b6773c0a824e6d3379b4c928e173321fb165ad979a6be004d394c28d19d410b2655d3e", + "0xb28f46797dee0c538bd3de815df641a0ef718ad3e52b2764aec380d6905b38b50ad6f60d0f68e096ca39960ba7734355", + "0xb0ac155d3d05851b04104e6b459f1a68e9e155437c92421a7c0e4dd511ef89cf71dfa3cc920769492ee283a65ebf029e", + "0x813c69a810745580d43d5b5480f0ba81000fbef0071e6b655c7346bef5ed774e9214a7816d40eb1774a5bd033767a046", + "0xb176345ca75c64f10ec33daa0dcf1f282b66a862fcd3d8d66c913f9a02db4c9d283dadc02eff13aaab94bc932a42234e", + "0x92560f67e5b995db4a489bb86ee78b4aee0800143b3535ad557a53e9e08716bd0202d9f5714722c2a5e8310046e3f5b3", + "0x8adb427bad9cc15fc6c457a96a6750dda8c46d859c5f69bf0e7ab8fc0964430b33967fd47cf0675b6ba1757f91255e6e", + "0xb120f723b80389a025b2daa891b140b3d7b8d520ae2a6a313f6e3d365a217af73292dcb249dca1f414ec05e865e3cdc7", + "0xa61a5d261a8dfe5996c42ea0a5ae703a2adcfda80e86837074d868eee16f87d38da19596c48b55dbd7a7cbec1a9b4996", + "0x99dc921eacc6bb867c5825ad4c83bc4af9dd78a18b3d0e1a60ad493e3805b8fb9b7922b577da1adb3d805edfc128d51d", + "0x85455fa165a07282aaab4a5bfb88027f47b9532e4af8195c048515f88b0db7e80f42e7a385fd4944faaa7f2a6544ad17", + "0x96dff2d1c8a879d443fe576d46bcceaf5f4551d2e8aad9c1a30883637c91090de99ad5eec228eb5febf93911502d3cbb", + "0xa87eb7f439377fb26c6bfe779701f4aea78dd7980b452a386afec62905e75217a1996c5234853432a62ef8bab21c31c3", + "0xb598278293823e9ccb638232a799211173b906444376337fdf044d0227d28fcc4c5867e6ecb3200e59ca0b139e71cac9", + "0xaa6fe147edc95027654d68140f428ec53cede3552c5f49c09d18bc6f6ae8c739a63042eb7291d14d717a4e1f0778abcb", + "0xae8ee18913d328b2fba71efe65526d3ee9c81beda53cf776baec4019ea30212010758cbb5dc85ed6620ce04b189f01f2", + "0xae9fb686777e88dffdd42805fe4114aa0da1b350d92a27ff3f8a817fb25af1fcfc9a06155affe0273bf13caad16a5351", + "0x95d372ba3a2ee38371538f34aae91b4844488e273f70c02f1992370f89fc2343eff95692d52ce9f21206abbee4959958", + "0xb15260376f0a34ca2827ff53acd7eaaef94c9acc2f244b36500423069cb1cdaa57ac8dd74adb5b53d0fd4265fcbb28ea", + "0xb0ffce6a8059537ef6affdbbc300547ef86e00109289239b0c6930456c562b4ed97f2e523963af17736dd71b46c44ac7", + "0xb5499a1277d34f9892f7579731ff53f423f2ffffa9ea43a6e929df8c525e301396249a2324818a6a03daa0e71fcd47b3", + "0x98dbfb8e97a377a25605a7665d4d53e66146204d8953afda661ae506858c5cd77ff7f21f5f10232e06dbc37378638948", + "0x84177e27e6da0e900c51f17077f5991e0e61bff00ca62c1623e627c5aea1b743f86eef6d55b13219a1947515150bade6", + "0xb50407bb5c61b057ab8935df94fd43ca04870015705b4f30ceac85c1035db0eb8293babc3d40e513b6fb6792ecbc27a9", + "0x988699a16917514e37f41ab5c24f4835ed8a2ca85d99972646fcc47c7e2a83c2816011144a8968a119657c4cda78d517", + "0x920c43fdcb738239ad542cb6504ab34498bce892311c781971d7db4dec70e288676de4d8697024b108cfa8757fa74035", + "0xaaa106329aac882e8d46b523f126a86d3cee2d888035ce65c0be4eaae3e92fd862f6ac2da458a835539cccafaba9e626", + "0x96e4c1562d14b7556f3d3e8a1b34ea4addc5a8170e1df541dc344728bcb74cd1630eb7ba4c70e9c68fd23c5c5d5a729b", + "0xa616ac5016d4e68e03074273cd3df9693ee0ce3458e8758b117a5c1bc6306dd2c7fad96b1bb37219c57ac62c78ad7a3e", + "0x8db7d9b20abfb1445babd484ae9e38ff9153ac8492230d7591e14e3fca7388a5ca6ef7d92ed445c8943cf5263e4a6ad7", + "0x88464134221aa7134878eb10928f31c8bd752ab68c27c9061c1de3f145c85731a4b76acdc7e939b399b6e497f9e6c136", + "0xa5f7c794f70b7c191c835dded21d442b6514bab5e4d19b56f630b6a2f1a84a1d69102d7a0dcca256aab5882d3f30f3ca", + "0xb96b6f98b6817b5fa6b1b1044e2411bdf08bf3ffaa9f38915d59e1d2b9bed8b3d645eee322ee611102ce308be19dbc15", + "0x92c26ade2e57257f498ac4ff0672d60b7ea26dad3eb39ed9a265162ccd205c36b882dba3689758c675f29e20836b62d9", + "0x8379a0299e75774930577071d258e89e471951642b98e5e664c148af584d80df4caa4bd370174dae258848c306f44be5", + "0xa0e53beda02bd82bf3d24bd1b65b656238128e734b6c7a65e3e45d3658d934f909c86ca4c3f2d19e0ac3c7aae58b342e", + "0x8ca5ceaeaf139188afd48f9bf034d8baf77bbf9669791c7e56ebf783394d7fcdf2a25fa4bdfcddfde649aa0dc67ccccd", + "0xa8060e6448844e9db4e9fb4da1c04bcf88fda4542def5d223f62c161490cf1408a85b7c484341929c0f9ce2a1d63e84b", + "0xaf6e1a5ecf50b754bb9eb2723096c9e9a8e82c29e9dcaa8856ab70074430534c5395534e1c0ed9ce98f4b84d4082fa67", + "0x81c8dbbef98f1b561e531683d5ae0f9b27b7f45dc6b2f6d61119ca0d559bf4ceb676d320afc5aba1811eeef7547a59d8", + "0x85b46cd64d605c7090a2faf1a2aadf22403b3692b3de1d83e38b2de0108d90ac56be35b0dca92c7a41c4b179a3567268", + "0x8dd3cc3062ddbe17fd962c2452c2968c73739608f007ad81fa1788931c0e0dda65032f344a12249d743852eb1a6d52a9", + "0x8630f1707aea9c90937b915f1f3d9d7ba6bda6d7fdef7a40877a40c1ee52471fd888f84c2b2c30b125451b2834f90d3b", + "0xb4a747e0bd4e1e0357861184dacec6714b2b7e4ee52fa227724369334cf54861d2f61724a4666dae249aa967d8e3972f", + "0xa72de682e6f9490b808d58f34a0d67f25db393c6941f9342a375de9ca560e4c5825c83797d7df6ed812b71a25e582fff", + "0x8d5ea7d5c01f1f41fffe282a334262cc4c31b5dcf31f42cc31d6c8e37c9bd2f1620a45519dab71e108fe21211c275b6c", + "0x8ccdc7e3642c2894acbf9367f3e99c85963cea46dc5473d175339a2391be57dd8815feacadec766e13645971213b9eb8", + "0x858e9b5fc8c13b651ff8eb92324bdda281db4cf39f7e7bd0472908b3e50b761fa06687f3d46f4047643029dc3e0ceeaa", + "0xae20d36c70cd754128c07cbc18dcb8d58b17d7e83416e84964b71ccff9701f63d93b2b44ec3fddc13bbe42ebdd66221e", + "0x860dbf7013da7709e24b491de198cb2fa2ffd49a392a7714ad2ab69a656ca23f6eafa90d6fdc2aa04a70f2c056af2703", + "0x8f809e5119429840cb464ed0a1428762ba5e177a16c92581679d7a63f59e510fdc651c6cc84d11e3f663834fcafeafdd", + "0x8d8a8dce82c3c8ea7d1cb771865c618d1e3da2348e5d216c4cbbd0ac541107e19b8f8c826220ca631d6f0a329215a8d6", + "0x86e3115c895ae965b819e9161511540445e887815502562930cedc040b162ecb1e8bdc1b6705f74d52bf3e927bc6b057", + "0xb9833b81a14115865ca48c9c6a3855f985228e04cbc285f59bf163dca5e966d69579ea4dba530b1e53f20bd4dccdc919", + "0xa71f5801838a6dbb162aa6f0be7beea56fadac1a4bcd8113a0a74ab14fc470a03775908c76822d64eb52a79b35530c05", + "0xa77ab73ae94b6d3378884f57eee400eff4a2969aa26e76281f577a61257347de704794761ea1465dd22a6cc6304fbc4a", + "0xacd1c5df3c487c04cf27f002e81f2348a0119349b3691012526a7b0d3bf911cdd3accbc9883112ed2ba852145e57fe68", + "0x8a28515a48832ac9eaf8a3fb3ad0829c46c944b4cb28acbcdbca1d0d4c3c623a36cda53a29291b8f2e0ea8ee056b1dee", + "0x846bafca11a7f45b674237359b2966b7bf5161916a18cf69f3ec42c855792d967d3bf3f3799b72d008766206bb7a1aa3", + "0xb24b341675b1db9a72c3405bbe4a95ccdfd18fa96f876ec946ccb5108f73e8816019998218a036b005ef9a458e75aeb3", + "0xb99c267b4a09193f3448bc8c323e91ef5b97e23aeff227033fe5f00e19bab5583f6e5fcb472ec84f12b13a54d5c0e286", + "0xa088aa478dbe45973b04ecafbcbd7ee85c9a77f594046545cdb83697a0c2b01b22b1af0b97dd75d387bb889e17f17aa7", + "0xa0c6b0cdff2d69964134a014e36c3709d9e63f6463c5cd7b01b6f0be673731b202d577539d89dd57a888326da1df95af", + "0xb4e6dc4ef11b2b41794ece70a8968e56705199d183366759568b6fa845d2cae127486e926b5b27ae9118bb21d1682c1d", + "0xa007804353f174098f02540a57e96227232444d5ae0a24232c244647148b6c049848cbd2b50d0a25af3ca9164bfff8ee", + "0x873fb034cc39c9cee553ece908fbf315f62efbc412b9afdde6a1889326b7f6f813e050b0601ba9921688e958cb75942e", + "0xb5676c90f0106c40d8683299e59d564f505ec990230cb076caef3ae33f2021e6aa5c9b27bb8fead05fc076df034c28f5", + "0xb5a67fc4c5539ad1ddf946a063110f824f7f08d2e4d30762c9d437748c96c9147a88efc22260573803ab545c18b108f2", + "0x817ff2b748a949973a91b69b0ec38efbd945aeb26a176d19f0fb76e261c7526c759e6f5516f9ed34de6eb1ac7838c9cb", + "0x99b76bda3526a5d841e059010fdb14eb2fa035a7d10463373a062a98c3c1a123e2da0848421dd7546d776438fd05e304", + "0xaa0d363270f90d56bbee7ea577b0c358532bda36d9247af6c57d000044a97ba41e35bb0db438f4c94551c6350e4e0674", + "0xacdae205d05f54b9544be96c9032350511895ccf413dbbc56d1f03053185df22a6d5b7ffcc3fbe96c3e2ce898ccfa73e", + "0xb091c220a1de18d384f50dd071dca4648ca4e708162c52a60e2cedc0188e77c54639f75bce9a468a64b2549119c07ded", + "0x878676133e5c700b1d4844564fa92a9930badb5293d882aa25ee6721a9f2cfab02088c31d62cf1342ae3edaea99a1ea0", + "0x9756d0793e6aba3b4dff48100bb49a5ec08ec733f966cb438379b91caf52fc2a5930830ec3f49aa15a02c82c1914dc7a", + "0x9722f760184d3b2d67cb2cea7fa41b1ff920a63446006bd98c6347c03d224d2d8328fa20ccd057690093d284b9a80360", + "0xb5a68489de4f253715a67f0879437bfe8f4dfc4e655ca344848980e6153b1d728acde028bb66fd626fa72eedd46ff683", + "0xa8cfc900b34835d9fd3add08044636f69614eff9ae929eac616c39bd760fd275ee89bf24b0f275dd77a66e54fd6b94e5", + "0x89967479bebf70b2893cad993bf7236a9efe4042d4408022fdbb47788fabedcec27d3bba99db778fcde41e43887e45af", + "0x889235938fcec60275c2cf0f19d73a44d03877d817b60bb26f4cbce09db0afae86d42d6847b21f07b650af9b9381fa82", + "0xb7fc321fa94557d8fbdd9fff55ab5c8788764614c1300d5ef1024290b2dbb9216bce15cb125da541f47b411a2e7e3c2d", + "0xb11b0c4dc9477176b3cda6b17858dbd8c35a933ed31364801093f310af082cb5a61700f36851e94835c5d4625bf89e32", + "0x9874e54d2939ee0600f4194f183877c30da26d7515e9e268fea8d24a675dd2945d1565d9016b62b1baab875ac892f4d2", + "0x90df3a77280d6f1fa25a986309bba9d5b89c3cf13656c933069bc78e6c314058716b62eacfa7ab4aff43518b8b815698", + "0x962b08299a287d77f28d3609f39fd31bc0069f7d478de17539e61fcc517045050644b0307c917208b300ce5d32affcca", + "0xb30eedca41afb6f083442aaa00f2e4d5dc0fda58e66aaf0f44e93d4af5c4bf8ea22afec888cacbf3fae26d88e8d344cc", + "0x847747a22fab3fe3c8cd67f3f1d54440f0b34ce7b513225dc8eb4fa789d7d9f3577631c0890a3d251e782a78418fecfa", + "0x8d1ef3cb5836e4039b34ee4e1b4820128eb1e8540e350309e4b8fea80f3ae803d1f25f4b9c115482b324adf7c8178bc7", + "0x8f8a2b0b0f24f09920b58c76f7d99ec2eb2e780b5a66f2f30a9ed267dcaea0ec63b472282076c7bf8548211376c72f6e", + "0x831ee6dc8889bbf4d345eaeb2f425959c112d2190764abbbe33bc44e1d9698af87ff5a54d01fac00cfee5878dee7c0f6", + "0xa7eb2479ac80d0ee23f2648fd46c5e819ad3a1f4752b613607ae712961b300e37f98704880ac0a75f700f87d67853c7a", + "0xaa4d1b9cec62db549833000d51e83b930db21af1d37c250fdc15d97bc98de7a5af60dbf7268c8ec9c194d5d5ccda3c1d", + "0x87396fd7e78c4bcf270369c23bc533b7fb363ca50d67262937dab40c7f15bd8448a8ba42e93cf35fb8b22af76740d5e1", + "0xa958b2a9ffccbca13c0c408f41afcfc14d3c7a4d30ea496ce786927399baaf3514ff70970ef4b2a72740105b8a304509", + "0xa5963a9dd3fe5507e3453b3b8ed4b593a4d2ced75293aee21bfed7280283348d9e08bf8244c1fce459aa2470211d41ea", + "0x8b06ddc3359827558b2bb57caf78b3e5a319504f8047735fcc8ec0becf099c0104a60d4d86773e7b841eb5b6b3c0cc03", + "0x9437e7278283f6d4d1a53d976c3c2c85c5fe9b5aec7e29d54a5423e425b4be15400ed314f72e22e7c44ee4bacf0e681c", + "0xb56067ee26a485ed532c16ec622bb09135a36c29b0451949aa36fee0b0954d4bf012e30d7e3fc56e9f153616b19349bc", + "0xa5c72f7f5d9f5b35e789830a064a59c10175093a0ce17654da7048827d0b9709b443a947346b0e5d96b5ea89b8d7c575", + "0xa8318d01182d4c9af2847a29a6b947feef5795fc12e487a30001cc1ec482b48450c77af4837edfa1aedf69f0642c7e5e", + "0x82ea421c091552d3dafa7da161420cb5601b819e861dd2ba1a788c3d1b5e8fa75cc3f2b0db125dde8742eb45b335efa2", + "0x8679fd1c7771ea3b12006d4a972f4f2892e61f108107d4586f58ee7f2533d95d89b9695d369cdace665f19c6bc3bc85e", + "0xb5ab3e8adee4c950fce4d33a0e2f85d3d886e60a6e2f4454b57bc68725f0cf246372d863167482cce1ea10a7c67c3af2", + "0xa85696927075ec188979180326c689016a0dc7a2f14ae02ea27c39ef91418cd44177d3fca5752cf6b298fd75fa012e26", + "0xa44f87b7232f102cd092f86c952a88afb635484a984da90a41a57a3d883c9469064bf105b9026024090486b6c6baa939", + "0x866ac91a437db945bbfdc11fcee583f3669fa0a78a7cecf50fbfa6ed1026d63ad6125deba8291452bf0c04f2a50e5981", + "0xb780d5a1e278fd4eef6139982e093ceafea16cb71d930768dea07c9689369ff589d0c7f47d5821d75fe93b28c5f41575", + "0xb025d0046e643506e66642c2c6a5397a8117bbfe086cee4175ff8b7120e4f1e6794e1e3f6ec11390993cca26d207ae43", + "0xa04a22b6e28c959ab265c7f48cde42bb6a00832c6beb2595b5df2879080a9424890960417d7d7ceb013d697d0ebf7267", + "0x81de9c656ac27f54d60d0252e33aff4e9e9e9c3363a50740baf15a2b9061f730a51ae1704e8c4a626153cf66d47f19b1", + "0xa15fab90599df889df11fa60c752948b68fba54005491180dafb66c5775547976d0eef33945e55d4818653e0818c6f92", + "0xb06f9be44ddb103a72fa4ebc242c8ee1975fe9bf9ef7124afeda9967ff3db644dbf31440151b824869406851a90984a2", + "0x99abdfe6806ae5efa2d11577da17bd874d847c5f810460148bc045bcf38c4fd564917eacb6ed61bb9164ed58055cd684", + "0xac53231077f83f0ae5f25e52b70bb6105d561c0ba178040c11c3df8450c508ed5df34f067fdaacf716f90b4926f36df5", + "0x99e3f509af44fc8d4ebc693d3682db45fd282971659f142c1b9c61592573a008fc00502c6af296c59c2e3e43ed31ec7a", + "0x98f2f5819670aff9a344e1c401f9faf5db83f5c0953d3244cfa760762560e1c3a3c7692bb7107ea6eaf5247ac6fd7cc8", + "0xb5b9f90391cec935db8d2b142571650fcbb6f6eb65b89c9329e84b10bfa1c656026674d70280ade4ba87eeaf9333714d", + "0xb0696b77ca8a0cdbe86cad12f358880926906fb50e14f55b1afc1e08478ae6376215cbb79bc9035de2808c7cd2b13b85", + "0xa51d746833062a65fd458a48a390631d5d59e98e2230b80d8f852cfc57d77f05eefcfd3c395ade1e86d4a39c2141365c", + "0x812d67654319f4ef3c9e4a2d4f027a4cb7768f1ea3f5fdde8d1b79187a4b874ff9a5c70f15b7efa079c2dc69d1b9b1fe", + "0x968978b653c6416bf810f6c2ffa3d1abbefbd06f66b6686e9a4fdce3f869e0ab1e43cce14dc83786596761c100ae17e1", + "0x98e1e6ab562ca7743783b802faeb0a24f1341abfb9655f106920aef08964a3c0e8083e1acda7ae28fed7cdd5478decb6", + "0xa91c0b982a0a7085a103600edf99e9d0bee4c4e7db6d9f8f376c215c7d42476218462a3765f2928e12c3dd49d688e4fd", + "0x8a43395b3124fab9e2438635bf88952e8e3084dad7ecb3a9927f9af0e0887bce4707084043671fc98ad03621e40a149e", + "0xb0b37626143d4a8c6f5693d5f1fe871525b4dd946c4239cde032b91f60a4d7a930d7ba28959737550d71c4a870a3a3be", + "0xb01c74acae1715c19df08d5f4a10e0c19d1356264eb17938d97127bf57e09ced05ba30d0fc1a9f32d6cff8b0d5f91c9a", + "0xb4c2328eb8a5a673406faed8f0aebb8540d2791646e37ce46e0e382506570ca276eb6f8e166dbbf9e0a84064873473b9", + "0x85cb9f769a185e3538e4a4beda9a008694e1bf8dfeea9dc07c5c40a9ceb1d31fcb13cacfaa52849ba1894b5027cb8c30", + "0x8742f91cddc9a115ddc73982f980f750d82d3760f2d46ee4490d5b17c6c3bb57c7d4c7b8d6311b7b41e59464c009b6a5", + "0x948ef86d17128a061e1bdd3ea7fcc7348e3ec87ec35dc20a58dd757d5d18037fe5e052bb359e27ab4c2320d9a52a6a0b", + "0xa70f6a214097c271e0d2d95e30fce72d38c30a2f186271fdff0e38e005aff5baed53739b8c4f9501aa7f529c5cb2da59", + "0x892a7574cf6704ad75b346c95ae6f2668904f1218c35b89b07a0c2dbf3c62173c348f6fd9473926eef56a37c0f635c04", + "0x837e85a41f39b4ded1420aa8fc3be46a7adb99305e0928c6d7643b7c44434b72984cea08eb68f5f803661df0db78c87d", + "0x94e495329f2aab3eeb68f347961d1006e69d990095877a4dcc376546233adf29a14bf6b16a0c39aa477e15368e87014c", + "0x851860a8fdf76a97048396553262637dade27f1f63f926997e74c7c72b14b10293eae7824e8dedffad1aead57c124f79", + "0x90481017a250972055ab1cf45ff17d2469517f10f18c9d4ef79a9bdc97a49093289bbacfefa8a1e491bbb75388b34ac0", + "0x983db15f7463df28091c691608ca9c51095530fa6b1b7b5b099c612e673d29e16787cc9ae1c64370ba6560582ce623c0", + "0xa477dab41014c778a1b78a7ce5936b7b842124509424e3bfc02cc58878c841c45f9e04ccc58b4f2ff8231488fff0b627", + "0x868ebba1c85d1f2a3bf34c0ab18721ea725378b24f6b6785637ee4019e65d4850e051c8408fe94a995cc918c7b193089", + "0x93cbf4238a37ccd4c8654f01a96af809a7d5b81b9e1eab04be2f861d9d2470996fb67367e5bf9dcd602dc11a3e4cf185", + "0x83113f4e696030cca9fdc2efc96ba179cf26887c677f76cde13820940ad6891cb106bb5b436d6b0f8867f2fd03933f7d", + "0x90c709f4e3359a6d215d03f45ad5cf8067aedd4aab03512dd62229696485a41dcd64e2acce327fda390e0352152fce13", + "0x9945cfced107a36f3cf028ba04c653360afc5013858b9a12fac48802efcbc198c9baf3a7f9b23dfdd5036e88bc7274c8", + "0x832ae60192b47fc735a8ddeaf68314b16256c90ab68099f58e43073e249c6939895c544a02fa34e40805bc6b5db33461", + "0x8b12c335818b643c1d22cbc2869606cf64e7ae54a7713617fc4dd3b2f052ebd6b920ca59ba2e9c7aa8cf71bb4f40f9e8", + "0xa2033eb7a373931c65d66989644aa0892ac3778b9a811b2f413d8bf534e282c339717979f9aa742162abb3468c195f87", + "0xaba2b4c37dea36bed6d39323e5f628ab607699c66767f9bf24ef5df1bfcad00c2664123c0d8d5bd782f1e14a06f4c769", + "0xb71963777535b4d407286d08f6f55da8f50418486392a0018ee10f9ae007a377b8b8336f33386b0eb01c45695c3ed2da", + "0x88dc87826941340913b564a4f9b74985a311371c8e7b47881235d81c081f1682bef313c2f86561a038757fb7d6a1a8dc", + "0x869e13e3fcf91396750150f9dc9307460494c1d365f57893fd06fb8acf87ac7dddc24e4320d9cad0414119013ea739b8", + "0x92194e292303d32b91ae9cecb8d6367c8799c2d928b2e2846dab1b901371a4e522fc4089aad8f4ee676f0614ff8b19d7", + "0xaa589a3e512cb4f8589bc61e826a06d9f9cb9fdfd57cf5c8a5a63841435b0548e30a424ca3d9ef52bf82cc83c6cb1134", + "0x81802e0194bc351b9a5e7a0a47911d3a0a331b280cf1936c6cf86b839d3a4ab64e800a3fe80ea6c72c3751356005a38b", + "0x88e5e9e3c802314ddd21cb86f2014948b7618502a70321c1caf72401654e361aac6990a674239afa1f46698545614c93", + "0xabac1e0f85d5c3ff6d54ed94930c81716d0ac92be49e3d393bed858833f4796c2b80bf7c943e7110de7b2d148463bfbf", + "0xb7eb416004febd574aef281745464f93ef835fd65b77d460b6ad5d5a85a24b536b4dec800cfe80ae98489e54447e8bb6", + "0xb3fd8ed1c30e7c15b0bc0baf0d9d1ecad266bafb281cd4e37c55edc76c202fb1e4ea315a91a2848f40f481793ae35058", + "0x86ef674ddf4b7d303c68bbfb53db00b925ccbf11d7d775ca09e458f4ecd868ca828103e8e7cd9d99672a193e81b83923", + "0x95ef414e9f7e93f0aaaeb63cd84eb37fc059eb8b6eced2f01b24835b043b1afb3458069c45218da790c44de7246860c9", + "0x93ec8f84c20b7752bfc84bb88c11d5f76456136377272b9ac95d46c34fce6dcfc54c0e4f45186dd8df6e2f924f7726ab", + "0x95df5f3f677c03a238a76582d7cb22ed998b9f89aecf701475467616335c18e435283764fb733fb7099810fec35932ae", + "0x8cda640695c6bc1497d19b9edc5ff4ea94c1c135d86f573d744358758f6066c1458901f9367190dcd24432ae41684cf0", + "0xb19aedf5569435ff62019d71baa5e0a970c6d95fe4758081604f16b8e6120e6b557209cdea0ccd2efec6ff9e902d6ce6", + "0xb3041f21f07d52e6bd723068df610aa894dfdde88094897593e50c5694c23025e412ef87a9d16cadd1adbb1c6e89ced4", + "0xa7f8d6ab0a7beb4f8d1cfef6960ebdaa364239eca949b535607dee5caeff8e5dfc2a9cfb880cc4466780c696cff2c3a6", + "0x99a565b4796e2b990bfcb234772d93c5ffdbe10453b5aa94662272009a606ba6ea30cc0c3c26aa22982c1e90738418a5", + "0x90c54b55ff19157c1e679d8d4f7f0687a70a27d88f123179a973c62565adfcc9347cfe31f54539038cf2f34556c86870", + "0x8612f34bcd018d742202d77d7ce26cf9bc4e0d78e50ddf75250b9944583b2c6648f992b635ea13fdaae119764e7c28d5", + "0xa04fb38e5529bf9c76ec2b5e3a1ef3c6f9effb6246c7f67301cfed707356ba1bf774f2867c77a5805933f0c8ad0ec644", + "0xb4800e7b503da0164885d253135c3b989690794d145182572181995e6fa1989f3d0324993e871bbd5f48fadd869d8a18", + "0x9981cd4f28ae7b7dadf454fb3aec29746dc2e0ca3bd371b2a57cd2135a7d93559e02132528ccd2d305b639d7ac51613d", + "0xa3ceec012dd1fbad3ef9f9f1d6fe7618e13d4d59e3f50540d2a57010d651092979c75442ec8b38a1ab678505e30b710d", + "0x8b97b8654d067fb4319a6e4ee439fb8de0f22fd9db5569ba0935a02235cb4edd40a4740836c303ec2394c59a0b96308b", + "0xb3d1bf4410fec669a269622c3ce63282c9ac864620d7b46c9dfcec52d8e79b90c4c90a69c32763136a7f2d148493524e", + "0x93174eba1e03f879e44921084aa0ee3562e48c2be49085de96ed7621c768ff52324d14c8cc81f17d7ed50c38ffb2c964", + "0xaa2194cd0fb7aec3dac9a1bd8ea08be785926ed6812538be6d3c54218ea4b563646af1f5c5f95cb914f37edfae55137d", + "0x93f2c0dd59364f6061d3da189e04d6c64389a3563b062e8f969a982cd68cc55b4f38b21546c8a67c8df466ff4f61f9c5", + "0xaa7dd497cc949c10209c7010ba4ce8a1efd3cd806a849971e3e01716ea06a62e9d5e122ad1d2b8e5a535fae0a01a7761", + "0xad402424b2a32bca775a66aa087580d7a81f0867f293f1c35580b9e87ccc5a2bab00c29a50fd0d7bd711085ae2248965", + "0x96237843d8e29ac77fc6ebf4acc12946ad11697de8e5f152fe5776f2475b790226a7d156ac48968dd68b89512dc55943", + "0xa45c25cdbb9fc327cc49a1666988af9ab4c5f79cea751437d576793a01c3eeea4c962c05c0947852fe0e4c63e1c84771", + "0x93dcf834a614a6f5484cc4ba059e733ab5dcc54253229df65ff5ad57b447353ebbc930736a4c96322e264e65736948dc", + "0xb9a94f82a82c0c5a26f2c1d5381afec3645e8ee04c947dc3b7ad59a73018db1e9965ab3642f2bbf60f32c430b074fb22", + "0x94eab29b3524ccbe0c4b928e5fa5dd8f684074b332fcf301c634d11083653ffee4f7e92ddbcb87ed038024954ad1747b", + "0xb8dca5f679931d6abef0674bad0639aefad64c2b80572d646aaab17adf5ca1ab2ebeecd5a526cadc230bec92ed933fc2", + "0x944d394958e539251b475c4304f103a09f62448b7d8a8eaef2f58e7de4f6e2e657d58d5b38e8513474115f323f6ec601", + "0x8a5ae1f13d433962d05df79d049b28e63fe72688fc3e6660aa28e0876a860c3dbc5fc889d79f5c4dec4b3a34cdf89277", + "0xafa5278724998eced338bb5932ecf1043d2be5dd93f4d231d05d2ea05b4455f2ffdc0eadcb335dcace96dd8b2b4926fb", + "0xb91153a2f4647ae82fc4ee7396d2ca23270ec7f8884ce9eead7e9376270678edd42dd3d4d6c003dfc2dde9fd88cc6e7c", + "0xadc932f1c679bf7889cb1ff4a2d2897d7973483fa283979a0ea3640c80ed106ea0934c1961dd42d74b22504be49851f2", + "0xa82e90761fae684d1415cee0649bb031bcb325ae0b28f128ab8e3650bccedd302a70de1a341ca8decfdda76f3349cad0", + "0x8ae353188b4b98835f4ef0333cccb9e29e1ac3ec11d554bc96f5880c101cb3c84b8eefe72f2287b0812735339fe66cfa", + "0xb8b41135bb1a1ffb64afbd83e2189e755f2c350e1273cf47c38ae9b8c4800d831436a69458b8ef9fa8b95a148d8ec9fd", + "0x96f75a04d8752fa93dc1eaf85ad333cff4eeec902a345576139e16de3a88eeb71b6726224349bb9844065cc454d959e9", + "0xab82b05e3923ad4c26f5727c60dc0d23063c03f5a4fd8077da66aa87042cad1bd99586d4ab35aa5e4ce6f4da6fecf3c1", + "0xa50c83db91c26ef7bf1720d8815b41bd056b49fd99710943679a162ccf46097a7a24585750ece886e38eb4fdb866fa37", + "0xa719f667914a84f62350dcc6f4f30b9ab428eac6837b70318c3ac491c1e69d48af5e1656c021818f377d911fe947c113", + "0xa148807aafddfa0a5624c7cb9e42468219e4bdb9994ec36bc19b6e6d7c4a54d3a0763d13ca80624af48bbd96d73afca5", + "0xaa012f205daf22a03e9fb13a63783dda7666f788a237232598d02a4d4becec7a699ab493f78d722ce68519262924c708", + "0x97fc15fab5952c5a2d698fd6f7ad48aff1c8aa589f7d3b14285fea5e858c471cf72f09a892e814104fa2b27eb9771e73", + "0x8da8840236812667c4c51c8fc8ab96d20dae8e2025290b9cde0147570a03384370b0fcbe20339c6aff09cca5d63e726f", + "0xb477d85359a8e423fed73409f61417a806cb89c9a401967622aba32bf85b569e82bca1b3394c79e180114a0d60b97316", + "0xb3d6ee2ed1e4c5cf8ba2c3a4f329832e41c7fdcbcda8a3fcbe8f60967fdb1717665610b7c1ac65582534d269d762aa09", + "0xa0b3b30b1b830b8331ee19f96b4a4321a6b93a3395b95d3a895682c65ec6ea64774b878b93514eaf353f2e4be28617b8", + "0xa2b88e9617f4d30ef4e686d1932ad43cd555fadcb5102e51bea19e6fca649284ccf4debb37b5cb2090ef386fa5bf5327", + "0x8a4446f7e8463ea977a68d6217a9046ad4356d6fc1c18d46c5d2ab681ea977b8faff136d65abea6bbf8936369cb33117", + "0x91e7464bc56e03f436228104939ddd50caace5a38f68817bb2991e193b57adf6835152bbf3dbcdebf0382ac9823f60c9", + "0x961a441e6cdf8106c4f45e5b47190d35644faec701c9cfc41ced40cfdd1fa83752fd56c1ac49131a47f1970a8f825904", + "0x94b7b165cc71c2ae82976b8f03c035fb70e90028992b853aa902c0467b384c7bcf01d56166bec5def4453e4d0c907e52", + "0xa5d32cffabbf547f900026b34ef46f08075b7a244565f615370d2f04edf50b094c95088a4a139ce07caf55bcd99afa07", + "0xb4e06e73660745f75ab2f34d9f6d2675b58f80f911ab6dd4c5a6ce1095f9a2b50d86f6ff9a05394190bdf96af0827920", + "0xad3fd8f83c0103b29d41319209dffca201d2b98094362da08da3fd6ff0ba96796b49d6bed525c9adb96c2954858e7f48", + "0xb0c27430695f0fd20ae31e1ec621da090094f2203e17411db9384695ffcf5c7c6badf461ba49ba70164aacebd6f278ee", + "0xb9bc6e972fc3b532fd2b1eeafc4bceb77604885f32132af6a9a842fa2440df452f49ec0cd9d86da1180e8deb0723b260", + "0x9729e22d6104b0174c136a854920f542b384d375040adcebe36acc253bdb55845eb43e34dc5a7cc27d22c417973c24d0", + "0xa8b420b36d48786c9231d454468a6e855dd7f71dcfd095efc9855ee70dbece0f06ad277f7829c5813fc30524c3e40308", + "0x8757dff5499668c93fc5d9cea0a8db61817b8ed407200d623030b5849a913d12f8371b667cfde8d8082026eda7407e8c", + "0xb859ad747ca5af661fbd03a1a282df6e84c224ecea645bc2d4ba5e35fa06cbf047387319fca0cbc76b712398c0798968", + "0x8e3173c27875f1460297af0fa736c945dc842ec3e476a973d3d5f790bf183ad3ffe96ac13868c5101d8e299890791864", + "0xa9d725e2b92c878be42b5eecc2c3081c63c7231ccc7e2dee17ca6a4caaeae22788fab1f1465fcbd7fc236613fc2bae4c", + "0x86f6c4f04a354cb2470ef91914816fd740f8d5795ce7ff981f55a2634695fde5951bbae7a4bbc4c63747040f8644170a", + "0x851773cb26f320f0c3f252d95ea7e058ffcc795dd0dc35e459aa1b6b448238909230d809e82022e64b7fca5d40b8324c", + "0x8962641e0306220d9892fe2d452caa286301a3c465185757be7bce2d9b2c9beb3040280099606cc86773e43941fd3439", + "0x8beb6e08c440b0de5fb85251d39d9e72db4e556a2dfe3dae59efd8b359d08492064cebd8d8993254b43bde8bd67d969a", + "0xa7e047894466ffe3dec4ab8d5462f2b1d8ac0df006b1d2dd26caf499ea857d93a811cf42233f9e948c9cb903beec004c", + "0x92eedd95557a91691a5e2835170390ce2401e223da43b78615a804c49566f9d31cbb7f10c8a8390c4bdcf691544fdba9", + "0xa5e5b5d8fa65824e958bbae98d146b4b332f97ed50e0bc2c58851dc2c174ab71bcbb1ae015cd2955c26b368487dd862f", + "0x853a494eafb308175629d581ed04bed71bbc3af9ca4c0dc483d03d27c993a2bbd88cea47c2085a6928d166fe6938fb77", + "0x83f06b88d29afbfbe8f61811690322ac4fdd6abb9a23612162e7a2dd6bcbb5f14cee298ebebc1a382484f7346dc51e60", + "0x8c9cf05735ea5a0e563490bdc7ed29a4426643711c651e35c8551ca6f855c8458ae8f0933a022d0bb9a952edfed411f6", + "0xb906b48d807748a26cc2a8848455a76ce502261afe31f61777b71917bdf7de2fece419db636439478c7582058f626c29", + "0x97efe1fa7c9b25d8bea79d74b6cdcf88f63f1e865f54b58512a2e60428630b0b40b8b6af1b5f71df47520507548c3cad", + "0x8ef5ca6e753818906bb3fc71405928d8e4108854ef0ef01c1009071b353bc2852e771fcb619d5fea45590e8f61003d7f", + "0x8e4d901661e2913740d70ba4d0745df5e8c9c0a260149d9362beadc7e669630ba909ff0e8a6cc85c54d6b7435d0d351e", + "0xb7c6ba3bebbd9592967954e3a480ee8df1d9f5965f04e7d78a5415b645128deae7ddaf6ed507c8877bfca91ce078e529", + "0x840bedb0ad4e25acf6cd25dee4f98fea495b2312dc5cb7a8388c5ab00b2acb9cd25da08e9fbead145a3107972b1ccd5d", + "0xa8d4578dbafdb27f3911af59962d89e75dea74db55346720357790da677312c203107d9c7911535aa563446fde7d4c47", + "0x86d3b77f231bfa09251b7fd2ce09c27ac520ec35d783e912476f9a4863f83d269eb175790d6e735da9260293d707f8ee", + "0xb34909f1cc033232652da0c34051a769dc76adb1aee00674a59dc1b860f6e610974c3b4bb69a69ccc73e01f042431242", + "0x90799854d0cf34e1d91ff8e101bc7c5007423d34d2f3bd9adea2ecac57e83f3a65a506bb93d4caea49b29f6d18149957", + "0x8ef94cde29b037e19a1ce7bf4418ad3c95cd9457412796ea385750c19a6690f13a3bb5bb6a9ee81e7a40face1e0a8bca", + "0x97053d21ae8d75972fb37f6fe516c38c32ab162fb56b9f510f954858f4e3ef6ac8c3a9557ed3f41b7b6aef05fe97f931", + "0x90a9f9f0f40991f3bddc58b92d40382147db22cce50d092d4a05aad251b46b94e71ec9f7107a180243288059fcc5ce29", + "0xa14265b1344ac2921b0f890d13bcfc432e4f648ce403e261fce4d3bb32ffee9e2794c02830346054f998e82784c77040", + "0x91928402ae121e56a3e64cd6f390127e6e92fbfb1967ec6efa4f52f3e8058f1f41a0f4fe96b5bcc11641c1139e790b2b", + "0x921c8c92b6d40da6c5a7b592acc74fc0f577d93767b9aa4a1cd302a72dbf503a1ea5b2c29fa0d0359bff3b8f252246d1", + "0x93ae0ebe0e8e133fd80cf67a499047e30ec4c4660ccec9d49098717ef57721a030f423e00c5e74af4ff4acf014a10497", + "0x82c865e21905aebfe0496af1c6ac7e342b5f446a9edb4f7da0f2fb0340abfd8e6fc545da874459d9aabe6bce0dd9bfcb", + "0xaee3961d8d2687c0f134b9c28b920bdc4021d925fbe14323c84224a9fe161248789249fb85436a5891d0bbff42c2a3e9", + "0x91aee420b98b6949482b8ff4be996b97245b4e8f583a6e085226539074f42aa89818395efd1a6699735a569bfe19d623", + "0xa48eec22c192e495b01722d0016a54acc45ff837e2a95c4294ce81d5a4e43e0053a6f0ead8a4fb3ddd35faf6607275b0", + "0xa26e15937c11faa30ffa64817f035e294cab0e839f73d29de8a244ad039be4e221eb47ea08d9a4658b0152fc3caf6110", + "0xb84450f948aa7c8682fccb9cae84d8e3558adf2d0ca5fb81eb200415291158720f8f3470542ab5b88c6873ad08e7fa9a", + "0xa8e8ec27d0608d020169a85d6ecdb40eb402f006a3b97afe32cc01987721b3a68a92ec693aeb4d357e189e05fadf699e", + "0xac87cd535ef5699312cc26f86adb71baa0be42e858bd5a2d94ac05737dac63430691e29b9a30d2559ad581a172519b2c", + "0xa4481e67b524f8cddf2046625efd3d75efee6aab87ddd2c1b22835647e918157e5e924ac760db2195c86d326f3db1615", + "0x891f29ded231486ee826840c8895cb325f7e84a5a6d2eac246cb3573612cde274720233b1978318a57ed337a046330a6", + "0x906b6e750e6178289012769807d2598925d7e51c260c14497d8af978b1695990e3352e6e809a752f376597a68083870c", + "0xb7a056898ee1e46f7f29702fb39232f678ec173eccd170303b3b0a30c8d8cf1a5321384e3513e3b03bb742c238deaa54", + "0x8f2f035fd96c3a336354c89ec9b8222803bf42e95fb2412c28d4e75eec99c1d4d402501ccae17357b757db8bdb0bfeab", + "0x81228625ffcedf977fba9cfa13f6edead3985e2651d5974789c394a69401cd7face9e20ae6694be4c0d4bab5e99c61a8", + "0x885a83eae25e61439ad809567a2ab148583402e01cfdd77b0e37ab4038935425c64b4e0886949bf06438c35e80aa13f4", + "0x8926387f48752f6933899c48e038cf14e7941ec6a58bcc0a436614b396296a17aa53e6873803dd3041dae470bd493fcb", + "0x95d0d3fa061f4d856eca78a569aa132db14cede7646f97e2aceb6da0c8ea53195d3b7a566fe5ec8c41b95ecdd89a1c6b", + "0xa3c817f4062ed6aa94064ea695d76c1825f3bf77b310fe1db28b8bedc9aaacbf1019dbd128adfd53042fb943d863a2b7", + "0xaf1208417aa584052da309169854149ede38a3ad63c76cad6e43afb6f1a7b854edf8310a0b00088c039259cedf0f859b", + "0x8b713fc3196bad35dbf364089049ada5477e540d78d76a5f0a9df98f7ba4a0e65dd0644509c149f9b07887298bf74b04", + "0x89c09c43c5b733c4a417cd9ebc0795cc3348b72778d31828a9171427779a82ef023c1a4fcfcdc919ae25056f9c826fde", + "0xa0759c850ed320c8c874435e90ace6edfb8e7b3f2a09d942b8ad8339c508044ee2ee26c70f1b626ec49a77971433b6a8", + "0xb85cbc58d4fd52286e714ac4eaaa0b2743a1de06fa03ddf8f6668ec6f1d204acccce93b10620272afb8c0b49bc4b0a43", + "0x814e0a87384e159892a8d23036985fa3f489c53bce192e107bd2d64f57b1bf5ea0acc1ef46c7a42bbc5cd0924d92b4a0", + "0xaa6821da96ad89d7881b878e141076522f104ea9a5bbdd1fce9f641898f7d6232c518a87a0f666871d7e3165c26081e4", + "0xa9041d714bfc067b5427252186fa3557bad598fc0067dc8521aa9bc1ae298f6e96113db5ac9f6bade9a85d5a950c9755", + "0xb8669340f3064692625e1bf682d34fbe69a61689e3aa6d6a3e822c781d406b0300dba9c3f7b8152a8c2513f1310d4291", + "0xa78c53316ce768a1dc5968030bf4fc885f4029b1ddb6a5d84a61c85af686c73727f62823891edfcb6ccf4545de366cff", + "0xad1d3aa29ea28292ddd438c865e2b5d93f32cdf009e6d5f5dc726de996583925727e6348bf1c28c22dec0bd86aaf867f", + "0xae1447a2062e9e28af5f38aecc60fe150cd10c2edeaf2110034aa144f6235ed7fbce432a58805d4fe1f6b12652d6e1cd", + "0xa32146634332d3303934550705353c6d4fae5fa5985105bba35041e74cd71e2aad67b45da171221f6ed80f36bf6dffa3", + "0xa232e8286184196ea77427b53d8b52c44d758ecc42d22556529db3136379b4989dec61cff610cc6cf6700a450a847a94", + "0x8a72c7255125a736da52dff5f77e44c3de29f88fc05f5ff9227c69df296930caaa11446595e6bea3bd946baac5ef957c", + "0x9688a981a9457678067f629f8efa6b522e7318b529f88d37ef56c5bf8f1c34fb9bb3a918ab73caab82bf5abb0c03518b", + "0x88286f3eabd71115fc3b17a6bf6981340a81cf7e5f96b0a1a016d4ec8c18fb486d46c70919123d0c189a6f5d6ff29a1e", + "0xb535e701b40d793c02ac0d625ca91620d3f4a512aa9741f71389e58381008b2f93d597586d06213c4e103d67d0ddf6c5", + "0x80d0c9dd941e8d8d3700cc51a434a5aaa3308cf8ebfd14128ccfd258f826b27cc3cf5c3ad7851340393abb1eeab3a157", + "0x87049225fa2380d93f18d3d90cb0697a56b373b66d7f24ab209966aed8b55a2790194d5885399db29dd5b1f189eda64f", + "0xa52df158ce8670e0290551e8878d63dd33b4759d6f50e448e63fc7fe6ea99dddb6f180be5fc0fc3918ce54c05f80b356", + "0x8b2a728b39c465fb0f60b0c486e5dc8d5845ccec03d3dd93b393cedeeb3fe1b44518359f1ed55fc770a8f74bfeb9923d", + "0x91fc05419dba718fa4a910dcf256ebea356bbea00522d8d5ec3e7ba4271a26035aac15e8d9f707969df1d655d92dac55", + "0x97c8779ae80c24c1f82d5a714762d6ee81069224e39515e41d8a71c9310dc5d1c55cc92bc5c6a4bd391ae4c321d1d4d2", + "0xb5e5aedba378c4484e3a7a4ed41b75b0844f674261c2501497de6f91f7274b5a4c1be0e055f2e0c0cab843d891169fbf", + "0x8a26212f27211b295beea500abc8e9d430a8500d3a350cc62f895d39e8b4668aa638c17633804ba353010000165637ae", + "0x864a95118e5d394e00e99efebd505df0125525c9ebe165764c453b80ad3edc730feebde3d93850745dfd88a27bb8f20b", + "0xa092e0b78290e826cc1ae56afffdd08f7c10954f549a3ea6666f3db1b6cdaeb7df53db28dd2a92446342930fe60a27ce", + "0xa1720224c0626a081b6c637b2a6d37da85d9a82241e5efef3bc15699b02a69f6304e43d8ff3144d60c16e00225d6b39e", + "0xa7b3d098cebea9cf32e19c5195608182b6afe9d4af6b9df532c047eb7a941a971279b2ae6a4b80f2f9d9313a6d788ce3", + "0xa3d2451e6788944802c5077a778d7b7299dbb9d1612676bb6baae78f39976e0fd879493cc4a4d737b8174b472a456850", + "0x930121b73da844571b1411d56760e80923a4ee09917b3e9cff4d3dcb0bc27026ff2c4e2c44e7aca7d3f8383f129c7f9b", + "0xb4b0119d163ee00a2b74bdf188a5cdcf054daaa48c483b94bbb4d09ff615afb4a91347db6363bc7535e2af9054ec2214", + "0xa5846decee706780201095a8cdd48fbf3d3a2eac8d089a818e5e22c29457494bbfb4399323b067f3d2be2197c33dbd98", + "0x96ba600df10ee7af5a9df29c0ca31dbed275d647faf9c66c7342de927ceb25b5bdd852dd7aae0228b27897f90fdd5d62", + "0xb6ac51ddc98edd9fb9f54ef84bf372a041d58dfdf0dfdbdc4b08ddc1a7ba93ddbb1413dda3c1545a3fd7386c6b85975c", + "0xb35f3efd91a0723e0d486188ea9675a3462106470455118392d7610470b623caca2fa33829721c05fbeb0fabcf570bfc", + "0x87f49e85df5f8055714a8ce7adf37f6a278e64e76ed74c60abe3edfc3611ef5b0426d4c6da45e5f3b74d30be1dc6f539", + "0x8ff8bb06902a71b1e9177a77367318b2e3e0a88f5d74d6907ca9943f4f9f1ceb5f297132c2a025259d17a67e880d1bad", + "0x85eb6de6c70fe5c53ab0ab27aa0fec439f136c979c557d317337cafa6e6c5cb3169679c9169567dec5f6c72b3c057d83", + "0xac18715ed1080771d760cb7066c6328faf65d9b30517903f8a5cad8d66d5c6381156b521107d7cd75ebb8c30e250706c", + "0xb95b9eae4703727e4ac9ddf2ae675906487bb78905a5f9cba74a4cbfd118d96b7afb6ef3ed5edf14fd963b830d71338c", + "0xa3b47b52fda16b62b11c8aa4daa56b0b669c4d5c56a3059b7d063284d8a91f6fff9ccccab23d6ceb9650483b2d353039", + "0x96a95b3f327df94c85e92f2e406f1649ac621533c256b062738f3c3ee137059a735a3e6072247acf57b1b0d8c219bd7f", + "0xb19b33cc04570be94eae8e943d5bb17bb0c96e9de4ca84f9f41b37320a1a03d397d53747dc13275fef1b356de557214f", + "0xa1faa3dcb931dd91507f3f12a17c43f6627fa2bc5c71fbdd27548e091eaaaba262477949cd51290e81196bffb954a492", + "0xb060a16079dca1d28a1fb33cbc26f368630ee042d980ce305230005d5b9ab533a7a695281ab76e9214458303932d8bbc", + "0xb303783196a858fe45d67e0520c30576da605fd69964449c20009fbd5099cf1de52a32d326d7c3b864de07440195ef40", + "0xaa550a4c20d1003d137ffd8fbdc1196d09ad53cfa0e202302093a80fa3bbc4c9aff83f34f2151785cc1ce5f30255693b", + "0xa7f8585f45566a351058e10c6f1ff4a7ba24811f1482a47202f581525615ca770da93f2f58878788b45b92cb446ef4ec", + "0x8206f63a9a5b59bd68e64a843e68fcdf706f4c13bbfcdfa9928298e5b9251006ae0bbd80c715aa3c9957d2c0148b5059", + "0xac9490abe1241319658f1c2c645cfa01296f5d4106020c7894b7ba4a65cdd52f6c5401bd3b3cf1c9863e088cd8c9a16f", + "0x85dd6d9c80a1b58c24c4d2cb7590d33d2454f381f58e820979948e5831972360cde67bbd56e1860077ef5192fcacb904", + "0x8b0285944c676fe2519cb68da0973275fa29c0718d838d363ce46651b068d29f867cf9fe579ff8da0bb8b37d202bb23c", + "0x95147275da658d43a758b203b9ca1f1c1478853e9bf77b5218593142e2bd9c0bf46d2206ab64cef99295de6e9a268edc", + "0xb8efa187fdd3e1f46c15cd596e9567690c10e253b5beaa5be8074b6ea4e6d3d06e0f2b05323453239e419ae1e7128521", + "0x8340464f52c92e31806fd3e8e65f56e27194d1f6daa4a0f0b3831e8102aba16f88bb5a621633ddb7dd0342e1d2d12343", + "0x8615d87dcab85a78dc052f05a01e751176b756b5dc9985014347454ce5752f459dd6464e1c5aff36cb6c51b783fa2692", + "0x80c6e35c0d3defbe4d3968792724a23f0b8830dd2fac58663583a49339ea20f1812cc4140e3ee867c7e716177319bbbe", + "0xa7aa63dbfc201dde8f29bb6e23d7aa5020dd35bd18a0cc93c8a10c35d695913fe25b9e8cf9b5fd1899e9657b22bc8863", + "0x97c2a4ba80c4caba2e729a603d2faa0120915e3fe64cbb065f7ff33de5f877f1ec9461cf455e88ec9e9ded9393939dba", + "0xa54bd1419f0e2d2d87757870f37c476c7e3a13502f1ada82fd7394fd29f8a00c4986473d753034d0954a2550badbac0b", + "0x8d3e2bf900d0d2b9b46e6e2f37620f0cc90526dbbcfaad4e4a37ed53f39fdd23bd3a6f21aa7e800eaec937d9710dd6e3", + "0xa88d2b1c7802b2dc216c2b6532406c091bfb12f29121b9a82c1154470e250188413ddd3e79f7e009ea987a4c45b332e5", + "0x8c552c2101dfdc3f99c2da436115452e4d364eefe029b12946f05673c5ce1cfb48d39a579625849236dc6c8e7277dd30", + "0x8415c252d52a26a6400c3189c928a98559bf24162ecf3eef1d10e439269c31d854b0b4f6ec7a2430e3f11b5d77de78d6", + "0x8b38905bad93a8d42339dbdb5e510003c51fcaf05e04f88fd7083753353bc1c4c00a5dd4a67431cd4456d0669c7040e2", + "0xb1d0ed8862250d0f0d9ef9dcf0cd16d84313d1a795dc0c08e0b150dadf9ce73d32d735e04632b289cafa69a6ee75dc89", + "0x9434e18a5fb631b10edb02057f2d1fe16000ee55ada3c26a079c9fc3943e29d6de99e52829fe7b333e962270c712e51e", + "0xb1b9f3914007e6fca8ad3e7e848a1108988cb2318da36df24767d804e95d1272943fda948451135cc1b5052a3953b081", + "0x8c02947a76d7b6c0a700a83dfb971dc105bfe996e18c521445f036310914b349ab28e57571e36ae08d13a46fb01c2f43", + "0x893472fbc225f973a0ac6a0a0130b9cfb7ab6869dff80df71a62b1f6beb4afd069bbf35b4f327165bc31dff39e4fcaa4", + "0xa7c176c0903175f3540d62f9afee994d5d9bf37081e094644b22f017e94c515afefde7bb07f638342abef7de657f8848", + "0x860186c2b1d3b1e657729bc804275fb5f5ee89eaa60848fcabd3871289665ea9f0efc8a95792d884972bcfa2de96223b", + "0x865b38aea6386d0ac8f501a7d934e23d01dc50105324e354d4c4fa3cb1d4c29c26f4566df7b1a728e10cfaa9d24552e6", + "0xb4eea5548de6969dada658df604b5d9c49002e2258352838003e0fdf7b299d81fb025807a7f37cf5b547cebd7f2c1f93", + "0x8982de11ba68d63a649a3b296d4d56c71e3c3eec016db250d733ab7c3b9a620c09c5a5d0b64fd30d3bc03037ca4b17c9", + "0x84d8b8a10d67eda4716673167c360fc9b95717cf36ef1d5bc6f2ef5b9d2624f0e76c2a704d016adf03e775ea8e28d83a", + "0x834d03ebd51aff4d777714783e750b84c16cb6627f8311bd8ff17c3b97fc4a5bba57d6c8f6d74f195d3030bcb5f07612", + "0xaaf49e0def0c4d5f2c1e9c17b51e931d2f754b19e80070954980b6c160178349f6d3c8d4808801d362e77f41a0008918", + "0x8ef4115edec841854e89f2bbd11498dac7396bca35dda554290d3db1c459ffc17be671f4a46d29fa78cbd6064cc2da20", + "0x9641dc8a64f4acd38e343a3062787c48c312f1382f7e310ccea3e95e066ab6dc980f6ed90a633236a435e68bf6b3c625", + "0x8a84cfc2cbeb18a11dd6c2a0aebb3f6fd58a33bb4b26101e826add03748595022e816afac79a4e7c20b3805252839dca", + "0x9770782d729017659844421e1639ffcda66a2044df9e19769b90292df87dcb146b20c6b9141bb2302029d84a5310665d", + "0x98c7ec9696454868ac52799d1c098c15ec4e08b34884dda186ebfe87d32840b81fd3282295df141c91137faf4cc02da8", + "0xa3f6eb921247617292162dfc8eec5b830ddc294a0fb92f5b4828a541091ffdaff34c392c1d7168259d6204405d90ec72", + "0xb185f77a468f07a54222d968a95635234e74fc942485604909308a9028ed2753b15902b9134749f381f7cd6b89cc8c3d", + "0x867608a682d53bd691dbc92eeb460d1c300b362ca49c11a280f6768ccec217f1145f9d59fe50d994f715ce89d38a74e1", + "0xafaad630ad8827cd71aade80edf3d7aeb65a344878db12fa848759e6233f6fceca563aa437e506ea9e0f1e47b126d45b", + "0xa12afbc84e3441594aecf85d089423dd3bb8bb33a1a384ddf7cc14caa72284caaa56aa179c15e3140fd56bb532491a67", + "0x98757b0b5e5837ddc156a4a01ce78f33bb1fce51e0c1254ee9b6d3942268d0feb50b93edbf6aa88f9ea7b3c0309830d8", + "0x89573f4a4ae752e9f964e42bec77d28a41840c28e4bcdf86a98a131d0b85367b885077823a6f916972de6ac110821bd2", + "0xa17f2745052de5de9c059307308fc49f56cb5230e7a41cb7e14a61c9efa742ee14c41023ce90c7f2261adc71e31045f8", + "0x914b07c53a41c0d480083f41a61c10429ea42dafea9a0db93862d2269ff69c41db8b110b4768687b88089b5e095523cf", + "0xb380cc3e0d26370976fe891d24ea4eeb1b6be8cfce01f47fd68838a27190e644fd57b049d3aa0a9589370de20e276944", + "0x906385fdfad60feec79eb1c303e750c659ceb22d9c16a95faaae093daadd53e7aa039a45d57e20951d6e1ca0dc899ef2", + "0xb5211ceee31b194dba60b616bfd91536e71b9213a3aaaf5aaf9b2f4cbdeb05191861d78b97eec58e3c81abe4f0488c04", + "0x97878e9e38c2f69d697800e7a2f132fc4babaacf471c79c26a757f771606e55fe696ece68a3163a0ffeb2f72274cf214", + "0x959431c1f54c46500c05aaa9a2bc4230531dad97ae768fa92bb85436c0ecc6374cf20fb0ef82d122db116820a943b401", + "0xb69e5a1c6798f30d33e42cb8d124f025d2c77c993c4c7107a539aacddf44d8d4d2239e802ece32e60ee4dbfdce201bdb", + "0xa8b09e5e9f802ad273b2efa02bcbc3d4a65ac68510510b9400a08d75b47b31c6f61ffdb3704abf535a3d6d9362fc6244", + "0xa41ace7f1efa930564544af9aa7d42a9f50f8ba834badcaf64b0801aaed0f1616b295284e74ca00c29a1e10c3de68996", + "0xa8f2aa0bbbc19420a7c7cec3e8d4229129b4eb08fff814d959300cd7a017ddb6548c9a6efebad567d5a6fde679a6ac6a", + "0x9683da74490a2161252d671d0bc16eb07110f7af171a1080dc4d9e4684854336a44c022efe3074eb29958ae8a1a14ace", + "0x8ef44d78d10795050c161b36afa9ab2f2f004ccf50fdeef42fe9cdc72ebb15a09389ca72a00001cd6d9b1d7b3bb766c3", + "0xadca54f3b14fb18298098970b0267301b7312afb75894deea1b2afa3e85b7a3b4efac9971ab54c5cbecba2da9f18507e", + "0xac5d4528f06fdccfc1370d5c3d03ed982fed0861a93a3f6453aa64e99360b124926d1892faaf72d89459e663721dfa99", + "0x98aa1c801bd615b8cba728fa993021e181e0ad717ba01c0290e7355694155407083eb53cb70819c4775da39d33224db7", + "0x8b3aea4c7c2bfe1020de3261ec085d79c7bf8a7903b825d2c70ebbb84af197bcc54e3653c5373a2045c3021526b63b66", + "0xa29f3de4cb3d99afff1daf7d431b38a33a9804fedc41626618928ed059df6f6fe9f298a046b594ffee951ed4d4e1400f", + "0x803fd346be540c5242667c18ee41b26bc812456ab13ff117196ed69b90ee608c8cb6554396b64066a546ec87a71ed6a9", + "0xa9c18d81ffd029c0339c72c499bb51685392253b996b6eabd8b76f05c6191ed8444a1397d63b9923743661a319517f7e", + "0xa048d5c390d08f07161faac71c5994baf152c883b205f3bb10d3501709d6516ae54d491b486303a11b751857a31f0052", + "0x9156fb4803e40e28d8d57d928481a8de4373687288da44fe88c5676a8ae013ed1fcc09d56a31140bf74e7f767253810e", + "0x98e289c725b18e0085afdfaf2acbc674dae7b0a2ecc2537a7d0b87e20eb785404ab05973a787f0495d2adb3e5565c09b", + "0x8a7237b249325bd67cdc1f9fb278710069033c304afbf270b7ea24dbc10c8eabe559a484d3edc733c77b4384932deb41", + "0x9056f2e5b02e5c2e04a69fa1323bbf1859d143761268d18e74632e43800a2a9c76fd681e924a19bc141de0e128d3e462", + "0xb9f2bf9e4e7263014296a82b9ecbb05d3f1efa4b2e675e3b38d3eace59da06a89c859256e1b77847886d6aa15f98f649", + "0x83b22949cca19030289bbf7cd2a0d8b84e1d468e78bc85271a6753241b89122627632723bc293cf904a5eb2b5dc6c3ae", + "0xa919aaf35dd0116168d2ee845122026416bec9633df113fbd913d8db5996221e234f98470d029a8ff182825b59fda20a", + "0x91726901f49d32b41afa15219073842278f60dcee223640903d871e318a1c2b541136b7b38a7b2ab7d31e4242fc29674", + "0x942b77666545bc9a858d36cfe857ab1a787c9528f4a0b87918a06bf510793264dcafd12ae6bd3ee300179dab7f40aed0", + "0x80adc1f2f9c47a96d416e44fcba41628abc0fae1f88f6a26aea4648419ab726f7fcc2187c7d5145e3d8f5a75c03937f4", + "0x8041e0f66ba9dcee01e336dd4d16ae5e4e1618512fc147cc8230003aa2940848162dc2187d4130bf550dc1f3559849d4", + "0x999e8adc51bab54386af1c5e8822986ad1b7ecaf1f8a4c2baa5bb2fe9d10710e49545c5a8bd89ed0e61a3d73a908e5ef", + "0x89272ffd39b6e9f99fafdd58bd9dc00f66f26a1d36b38a1ac6215e3546d966739eecda7fc236335479207cef95cce484", + "0xb8e0b7532af13f15dc04a0eb4ea8abd67e58f1b1c6ad2e70c0ffa04a5c18ec2018b5d7f4be2f9f86db5e0b3986f639d9", + "0xb96bd11b0f6ead4abd5fe1e4c6e995da7583b901afd01cc05e87d04663fb997997d6d39dd9fb067c62cb1b1cbb67516f", + "0x94ab08914088b973e8dbd5685decb95f3bf9e7e4700d50a05dbf5aaac9aea4be2c10c83096c02252e9238ceea1351d05", + "0xa188de419b062af21275d976494c131ba18d2b2ead8bdbfa38a777832448e64d4d9725c6a1d530ffb6513f18d5b68d9d", + "0x8f73c8c118fa25c76a4ec5611351953c491452743056a819c8c82ba4737a37d88da0b55f837e7239a5f46d2c05a1bbba", + "0x894a44769e0be1c26648b0d89c4c9f46dbdeb3a71b90c493093bee372bb9f2d3f319850fd886d51f4f58db0de5641742", + "0x87d239923b0db024a8d9b0281111d47b0761d81c50652268b074efa3ea70d793e30f874a91ce33a4acecd0cf38c01951", + "0xb1b48b75a97f9fc2dc9530dc69f6268829dd0ddd574516e7eb1b9f5c3a90058889a7bcf3d378738e6d4b02f5fbfa44db", + "0x83e3ee9526ffcb60c6e75b75550fc017912ec0daf96d0a0d5f58c1b229cce90c684ac7c3e17fb998def8e7e2e155d750", + "0xb9b7bba579e474b0abdc7775ff5f84c9f117c6ca17788cf5a5f01b2c35a14aa39036031c8d799fec2cfb371d9f7471fd", + "0x90d7faf4891fbc368a32f575dfb69f13e37161ab4f63a7139be103285a49490c2851a907f8d36e09e7d1a190dddbc6cd", + "0x968c8b9affe18fc34a4e21f0d8c5518341c566099e6b45b8721c9912bab3693c9cc343406fe90279692a1eef2a3f7311", + "0x8735baaf4704207550f77df73fb701d9a63329993a8cb355ccc0d80daf950145f37e9b4b22be2aba29898e974f9fd552", + "0x90f52b2dccf525b9191d836b205ffe966d9a94f6c5800f8f51f51f6c822619e5abdf1257ee523597858032d2e21014ec", + "0x831209f8f5257bb3eb452d3ee643d5f063299f8e4bfea91b47fc27453ac49fd0ba3cf9d493c24f2ca10d3c06d7c51cd6", + "0xa5a4db4571f69b0f60fb3e63af37c3c2f99b2add4fc0e5baf1a22de24f456e6146c8dc66a2ecaafeb71dce970083cd68", + "0xb63da69108fad437e48bd5c4fc6f7a06c4274afc904b77e3993db4575d3275fce6cffa1246de1346c10a617074b57c07", + "0xa449448d4156b6b701b1fa6e0fe334d7d5dd758432a0f91d785b4d45fb8a78e29d42631bc22aaa4ea26f8669e531fed7", + "0xaabe43de1350b6831ef03b0eef52c49ffb0ccd6189cce6f87f97c57a510ac0440806700ce2902e2e0b7a57b851405845", + "0x91015f144fe12d5d0b0808c61fa03efe0249058e1829bb18770242f5fb3811e4c8b57ff9cb43deccfc70552e4993892f", + "0x8e9c570811ce44133ce3e0a208053acb2493ef18aade57c319276ad532578a60d939ed0bde92f98b0e6a8d8aabd60111", + "0x8b21839b5dc1c9a38515c1076b45cedec245d1c185c0faac1d3d317f71f1bfebba57c2559bcdb413d9d7f0a2b07f3563", + "0x90413bbd162be1b711e9355d83769e6aac52fdfa74802d628ff009325aa174c68f5329ddd552ef93e8fdcb9b03b34af3", + "0x8b6b02e3f9dd1031ebd3df9a30432a3c86e64306062ef00a6d1243620d0cb66dc76f8d0d412eceff877ff8768c2696ce", + "0x9894b41d9fc715f8f6addace65451f41dc5ce7b983dd8cb33757b4d7259bef12f144e0077d0b662aa847d5a45f33c563", + "0xa353a9740f6188d73aa4175a6c5f97898a05ed7aae9d2a365f15b91dfa7c28b921fdef0a32d90b6fb82718b33d3ddb8d", + "0x984eab8faed87c403c9979f2d2340fb090cc26d00cb4092aeb187c3f4ee1df3f57cb8363f7764073188790b16dfc464b", + "0xa5c5ae0ba435fb7f3ddd5ad962358da326239ff236fc3b51bd22e88296236b109951cee1b98f444302badc58d1b5bfbe", + "0x880be1006b0156f2788813432f450f613d235f41aba52a6000d2ad310408ad73d86b79f6081aef1e8c51010d404ba670", + "0x937da751aae68f865c7a33fa38d718f20e2a1c65cb18c8e08f8441f0cdc77662789d2793794dd0a427cad30cd0b33f42", + "0x9496fde66c834ff86f205897db12bbf9a9bb78d9ba8b5fb539cd0a2c927cc6b4120c017b0a652750b45edbe5f650e5dd", + "0x97a6f409ffeb593e149307a14bc47befb632412d70565c5f13d6b7d032acd2e3ed0f7b6af701b387f11d69ee4a8094d7", + "0x97ed94934263dc0260f4f7513745ed3483cdddb9adb85dc33193c3a8b4d52affaf1ded23b59c34651afbffe80d40dc36", + "0xb2b26378d44f916bcf999db218b9892e06de8075f205c7dafd6d37a252185c2d1b58e2e809c717963d25627e31f068e4", + "0xb8f9fa1fb45fb19a45223f7be06c37d3a3501dd227c3e15999d1c34b605f888123026590697d0ae24d6c421df8112520", + "0x997aa71e3b2e8c780f6855e94453c682bee1356b5ce804619ef14834475511105b1e4d01470fe4e2215dc72182d9909c", + "0xac2cb2a7cf55aaf990cfada0218453853047e813d3f51f5a623d09f4714da79de6592671358a5edf938a67f905b6cb5b", + "0x8d8340d0c3081cd30d34f3ff6191e1ff6ad7994b4ebac19e5936f1157ca84e1813228b7605ee226366d6bab1e2bf62a2", + "0x9693b17669086003cb46c75fed26ea83914a54901a145e18c799a777db1df9c9ca6b2ea3ee91e7b0ab848dc89cf77f19", + "0xa6b6b2a6cd8c4922d78c8ba379373b375d66ac6ea04b830a23d5a496cf714a9439d81c865da92d52600aa4e2e43afcf1", + "0x89cb665020abc3f5e11a03c7ba5ec9d890fa9ed2630f1443a8e45a28c32786ed980b5343ffffaea60eeff5b313bc0d66", + "0xb37b989106594221bc6cf33a1a83c3e65ecdef279e90333a9e105b8139dc28384bb2277edd4b77c9e59d15e6afe074c5", + "0x98ce5aee5918d18b2326b30c1ba41669cce20bc7a1d1b585363305fbdea66055164a7ac398ca0f0e670291a3061022eb", + "0xb57f472d5f34beb4cf430d7c0f8ac5bd1c0621a284633ed36e6f7804bc2b7847f54b469c7ea163a436510d9e3b32f97e", + "0xae673a6579dbf0504c8fd0c8fc0252d2f7ae8da615a06f4d215c2f8a8f516201f24e5cc42967630c252905e5dbbd6377", + "0x97c1501835a31091a5a83f0546e01c85ee847a0ca52fb3cc0653f6a826e13d25ddc623a5dea139108f7270a1fd7043ea", + "0x9376ee667f3834f6c0da4324fdcca5c04712e0649877ee19da79a2d23be24640c38758fce562470ce2134ca34148ffe3", + "0x818af89c40379a10074cfaba6d5968ecf667f1a68a7edaa18e8977ccb34e0829f237c5634fbd079e7f22928b277f1096", + "0xb8e0af0be0a252b28df25d4a509f31878bcddf702af0e5553393c3dfd4a1f1247ad8dc2668bc8dedc9b41f6ad8e71b15", + "0x811667ffb60bc4316e44bd04573503f5b4dc44d1ec824393a699c950e5fa085b146537ddd6a08a3fede7700396a0df7d", + "0xad834cbf850b2f61ce799c4a0f8ab0c57039d4e1113933c50b0c00175171aadee84894d1376cf325bfd434c3deb44315", + "0xa8b7dfcdb40373ba4d55e751ccfb9070554434df9e359fc165284ee3dc35db6fb6055657ecf5a9e9b7b8e2e1abea4375", + "0xb56a5b9fd41c9d3f65532aa58bf71a38fcf07782e1ae0084dc537862fa02e6d66658b19d6f71c39cd5dbfac418da1837", + "0xa935af5ed224b9533b41a7e79f872f6851591da9e9d906050ccd1b2c772a1d6d010c5fc7160c4f8cd7d3aa14c3bcdc26", + "0xa81e580fc98692567b28323fc746f70c3139d989fb6aabf3529504d42d0620f05327e3385c2bd5faea010d60dd5c8bdf", + "0xa8b352054cdcde8ddb24989329a249b71498a5593a13edad1e913c795dcad3d24789abca9c7ed1d57efcc9e3156da479", + "0xb0de8a2bd7f93284b2bc700e442f52ada16a22ad8d86329591547411c23fff0333b2ab0c9edf82bf7903ebf69916eed1", + "0x843e9781b653d1a427f3534b2e86add49d308ca247546f9fcf565f9e08df921e4d969e1b8ed83f3f849e98c0f63e39be", + "0x84a4098c5dca9f73e827d44025473096101affd7193c40a0307e3215e850e753e9a08e6e74a442d57626ff26df77faac", + "0xb463eaaa2f3315b511c22a97fad353014d840a6a95fe0d457d0677e63e571407d7f5268f8775381a5e7adc3b4163eb88", + "0xad0417edaa16cfddc288eef4173aa7057ca4f81e815541ac588ef5f24b98d56fed6845deb6ae1a9740a28bb1cd8780a7", + "0x9271963b8fb2288a96e07eac13c0543ec41abdc6d978bd7c44ae08251ea49994412b542c77c8208cd71fd8e7852d4a70", + "0x8b68b6db9044d8bafc155d69e0daba95cd59d6afebb085791e999afed4f33a2479c633d31d534ff767b8cd433d591a23", + "0xa6a06a0e433e385437d9996ce823abda9848754aa9cdd25ec8701af35c9ec15df999825669bbc2e17cedb597a96e8eeb", + "0x94d414bff8b6b8597634b77a77d1060db8e1af0d0ddfb737a9bf1c66c8430e93a425510af2464bce4a7b29bc66cf325b", + "0xb6514049562af1c6fb7d0e8df6987b020f0b7a6e721f4862e36b1ba0e19af19414ede04b346be22d348b50875803d1bf", + "0xa42c7fb34f2fbee8aaccd1d86672d0acdf4e6bb083ff0456512d7e1e43be041cc0924322fcd986e6e1bce5d5ecce6f92", + "0x867cbdd169a52440ae0a75d33a28c7d00aa92b4b65aaac5e62aa53a8fc367c08ab8828cc8fa18b6e7d1f908d158e3382", + "0xa6fe0b768fff3e4a6153e59a7b7508eb2ee8165eaf5274d41ac2812bd4563c4ca2b132f0e27ea2f1c98759cc3589b61c", + "0xb3eb1dba43d10b9e17ffec8def053fc96f9883bacb49330a089a0ca5b9ab0182e8b5111ad4aa55c1ce1b6f4afa5c70a3", + "0xa1531351098bdfcda566ff4d811301c0305626c77f954a38420c490e7c684f517eb1a4e4bd2c3904a10bac889cba314a", + "0x92278d106ad2f27eacdb86bdb1faa0a07a93765bb79dcff191873c52253af83480114b2299ffe5324f9c31d0abbdbbd1", + "0x8900ba95a90c447fb6fa1f528af3d7a378aec25feb0620516b6b97e54b328fc31af42e46a8ad5e6e3029d83a6f2bbe5f", + "0x86053d481179c1ac910d5e7b9a5de82794b442f20e854583512ce1f9c3f09e71d1bf97d6700fe776debfe1527ab97a82", + "0xa32a60de492fc4340336416bccbd2591b5e414fca0aead82281212e24490acc01747537b3da783684e27aeb987245cc8", + "0x9820fe8e0338f21797143f368177e3669a1f3894b40ae9fa3b353125f7c8e85cc424dcf89878f2c7667f65db3b1e4165", + "0x934d64711b4348ac5e1395cc6a3215e5643b540f591380d254165486b0ec2a1d0d21c7d2c6310f9e0eed3d08ecf4b57c", + "0xb9fd32d589432eddcb66dc30ad78981360915854cc44b2afeb826b5d48a08e377dc91be66f5bf1e783d1a8bb320f7ccb", + "0x98c972cf01efff4fc2e485b47572e2d8dde22461d127ef401b71a111b0603203971e3cde40912643affd7341cd27e57a", + "0x8db6c1620760063edabd376f4399b6e1355462e04f5c81cdcb3989fdc00f9a466bc85ed899e886c89c149adad69edbad", + "0xad7b7fda0aa6e2aa66a27235ac5cc680aa04b85dce329fc4be84f75c9c961120a3d9e446aa44539aaac8ea203eecb4eb", + "0x8ccb01eaf41d816ce69ebd57754859e263530915e775c4e7d9dac37b2457a9099b9ae9b4c6cb09eb5ff246e3c9320c59", + "0xb895b83b5f7ca46e02697dbaa6157df6c7571864c83e504a8c77d965bc2ba97bf9353a71c56a020df64498bd40e30b21", + "0x8018c07a81c522fbc25f2cb14f2321c61b98bd8962ed8eb7d5823dbe5d1958a5ec2fb5622fd0868e991bcb6cae016ea1", + "0x95b16364e94d01b3664812264d7185032722a4afc23bdd33bc16ae87ee61816c741657c37138d9312cebfb5fcfbb3b2d", + "0x94a709209990a8b09bfb4b9581ab471aae3a29526eae861108b28edb84aab6d28f1d7a25dddd8150b70af34bee4ca2e4", + "0xae06c80839c5a13269b984ff4d8a5938c6f4d8d647b1b1daa8cf7f6145340b76a286cd615ec251a65501e6290162da50", + "0x875cbd0694eeb90d3567da9dc7f570d97b02bd9cf17bfa011efdd48f1d580608a3213bff4006603b8b4079fa66bded10", + "0xb27f88c455f025e1cd902097d6a224d76bdf9c9195adee30bef4a0b0411fff980787285896e1943a62271d0aca531446", + "0x8024880cde783cdb2b863e3dd856be92bacc5b2a1347e96e039fe34279ce528560d2df7d4d1624a4595dbafb40529697", + "0x8883d02c2a5c0e026d941c785128d4ac6f7a9de625ea735b7d6ff27a5ba10fa4d6370d450d99a855d919f40d64f86afc", + "0xa1beb985c45fdc30ac536f1c385b40b6113ef6fabc2f76d255490fe529468847a776efa674ba8fed72180f07d3f701f1", + "0xab83bd9b007561695210e3276fde72e507456ba277ad4c348a2aec7a6e9ebdc2277cb4bd0bca73bd79bd2240a1fc4456", + "0x8db27f516153812149854fd6bb1250e843a3ae1c9637df818b08bd016a769d0497ab6087fe3b2fd4080882713607bf46", + "0xb3891dde4e00d60386aeff161b4a0fbc30bb31ee7918ce5fc0b49aac3238a000ced192c9c4c08d90de3a0ba973d7cfd6", + "0x90a2049a15c02e59024a7a1cb0adea97501c60b1c7442fbbe560054c3d69264e69627ac57b7d9be01bef498bb2a60198", + "0x87df67a4bd72444b5faa4f3b067204c4927c869dd3b29ad192d859589a9b2c1d6d35ed68310081e140add254a9463092", + "0x8f80986a8dc8a0d6408ebbcb4f234e76413c11cb0d66067f9436bb232373100f20a4fded60f08dec3525315abfaa8523", + "0xb061e10beb12ba3683688a4ae3a91600d14878ef78a308d01b93e4918efc666450e3f7b0e56283468e218934231df98c", + "0x86b9e55f3783d62e381659d3e06699d788b88aab1ff99848db328a83c97d223f602201bf2127c5ecf419752fed0a224d", + "0x858d878e29925c87243e010020007f96fa33264e89c8693af12857b362aee3fac2244057e159651c476ebe1dfbd67bcb", + "0x8fd47cdef87d7a569ffce806d2c2dad100692d6c53e5f5dfc6e274f897dccadcee30fc6c6e61373961bbc1f3ecbfa698", + "0x892f2822daf3df3a759bef03168c1cb07408df62e024747a788e94d2da325f880bb9c6e136c7f6643f45b021c6ccb654", + "0x8714e37ac24f5a198f219e7c88a92172fc3db129e044e914663ac708d8101851e7c53fce79d32d0e6da74f2ccd1d30ff", + "0xae95e1dbba8b9e2c8dfbe1c202e9ccfd04fa396470035a699b902fbd86d5e6a31732a7c8cae00b9a4f6e51c8d560c7c3", + "0xb0cd058e77498e860fa20c5f8d9bd09bb249add1badf84ba8d1bd49e704b9b4bcd67a5c3d211840a2c8fefab3fea639b", + "0xb78e468d3a7da0dd481f333ae56534e2ef97587be2e259a458e25aa37952aed1cc5f835640f812d8052f5bada8f57b12", + "0x835de7965c6b26e7ad1b92eb6f0261d1f376fa12d61eb618d9b342b597c9c117a5a8f6a36269aeea88072b4641e6b5bf", + "0xb4d0eb99136b3643468c9c48a20fad62785a60fbdd3c054efac4bd1fa7979b4c9ca6c2c0b18069c0912bea2f19832790", + "0xa00c47315dc0700a850966836a95f3cebfde04dd094bde0742dee77b89a05b5ad655921f86fafd1e902938ff34d4c58d", + "0xab13fa0afaa92229a71ee91efae6d1b15f14b6eacefffb7401d41d0d6db24e24a8dbe8ee19b4680ecb69d2a0cb4e84e7", + "0xaa56c0fb18401210062dbc653df8e3732aa8921a1280e9737e99b26a0100a13a9cba8ad0317a69bba16193362ee0f030", + "0x8b410324a6406b345df0fa25f541ac20b7313fa55832752f70cf4c79f43b0bd3d5b4cdc447e6ba7bca08d0edffa8e29c", + "0x893362241ae412d9e5df46506407595c58ffbd7fb1fdaf0694c3432470599291238997abe118bf7737e56a4f5c9dc292", + "0x921618194a756be81cb49d6357cb392b32cc62d96c8ffb7e16d9659a0f226a0436bd378da7b835054dbe0de2c6372ef2", + "0x94a2904f10994928ff5367b777e1430047736fbece33442cf452018bfdeae62e84cd75cf80f8468285e347d504c94111", + "0xb4b81545b767f380bfe10e0fea9c3cc62ca8db40b43c83ffb245259378731298e3eb6c3bdc3a16932f88f5d8a86edc4d", + "0x936203c2453ff01c6fc635e4d54320d69e60047d805daae3b75633c2259108497b778f011e5a057249f11b2b888ea76c", + "0xb90bf6378d29339443c3f2008b1e2b5f0345f86e393027f14a295e583bf6e6c2b10f54b6dcc42079ff0d356c405b03bb", + "0x916913f550d327de2d8d6c7723dcef2e3869efaf95fd963d95c8980b97748c61ad8e2e629cead8577266d93fe39203bd", + "0xa033c6f3d5ecbabeb83eb363e54e5faa7ed2d7f4fb771b161762c4f003eac4e1afb236806b784baf2222cad54e2d3cd9", + "0xab289d4a5771147e6c29ff9ac2bf65d70081ea6c6af2d9b728c3c144574a31b5fd8632af57c18c389aa2cd994938bb0b", + "0x9488da2019ff13e290eeac132b491df58b5b7b23c2898ff1a67bffd7e9c9464c39bc8177a57950fd28589e3d9ff9c6c4", + "0xa5abe42b2e0891851440fb2aa6c1d8a86b571bce8b80c8e9e2692e5cb6d45a1b2f055c9fc4c74a7cd292871604129ea9", + "0x90bfef698e83c2ba4dc9304aa01edd274169a978b7154bca518daef394f55857d0d1922ebef3d91fc5ecb3b895d9e0ec", + "0x92328f1372b6406ec80786041b6d57018b8507e3881a08727aadfecfdfcfb0824394cbb1150117ac5da5d71b89e895ae", + "0x9719751c5f7a65ae2bed8aff7b4b8c34539ff011b259b7ff54f63f9d987b3fbdce5c99534ed561aadaf07bb6e939e208", + "0xa151816774aa9379fccec21cf212429a1c68cf91b055cbb9d931f461a8d5616c693331a11ac5c6fcfbd17d84ee0b44e4", + "0xa72977b1285618a45943ad00f33f37102e2885eccd2f76785254eeca495068fb1d8d49865343e9e8313c6c2c3b2024da", + "0xa6f5ad2e023a1585d90625c9f7094f0e8851c79f0eede8ec582ee8e063407cc5b8298e5fdc4c786e4fbbcecaf33e787e", + "0x82901e008febcea0c0a14ae21d985a397630e18ee6e346f4a449f23be228e8f338df567d30211a11180b94fbc5204bec", + "0xb9b57fdb8d14d1be87a25f89553b3966eb7869e0519ffdf4cc4d51f4cec90d68f7b81cdc0450e04207276e9c63ace721", + "0xa06eabcf43585a001448f3dc30411f3d5b74fd0a695c81eda9981842ba2bb0081d3f5a8360aa18b6d43ef13ea78b293d", + "0x926fe48a7e8f07559b7237beff9504476dd97b5b4d67acd01a3633358a6ba4c7abed5c87683a11209aa2ee759888e00e", + "0xa716cd3a84a963e2a5a46145b6ef4ebce705de52bf2945c374152a1e41c228a9c4eae0b6d1e222c1eea8b9c13c002177", + "0x8a9b5985df6fb32cdb06ba1591a977545444478f2fe985ed1b10de61c630f0a4693c2185d63f0dc0256b208072c43b17", + "0xa8eab26ae0ebcdf96a59fad1dc2d5e83b94abb2ea1774b607023f9d9e0fe065853b1e2242e794f989a80a47f550c0bd9", + "0x84adbf38164cd04f3d770a7f4b8eae7a5d25b4a803fb63c02b95b71b33e454319c44e07a760d22bf5f58e7e372d09a16", + "0x90f443a3ba1b9129a0bee400b5b29d42e50bb2aa56b0022bbfc3c6f8d69db40299871ec7c1b68421cc89e1af6b13a39a", + "0x81c5a94b379eb98c494a8d0067c748ba47e87a2ada0105202ed7651eb4e5111a0cd8569b06ae68d392c4fd74a37833d2", + "0x8f92324b14a1549ee0b186073a26691088e41556d33b54258fc6e0b000e9624156db4e97861a0ec22960e6c47ca8a1dd", + "0x8b021cd0fffe055068cc460aec3cc455952e2ac32be5fa060e0d1b6cf30ed15381618f801249e893b1b9f10dd82077b0", + "0xb3e9f0dcb3d6f0b138f589fa54dfb01f849890ab97016372d004aac55103f363a64bc0e606ddf75430f1534a30fc522d", + "0x8fdfe64af891db89b25daa859864d479cb7599486bd6f36e593f8f2f839f942261ffc3eed5001a93fde44cbcdc24c583", + "0xa9e4554373c5073e135874e2bacbee69c65308eb0785532fec6a37834e8d0b437b77a2f11cc63c87d7183b82cd9b6bc9", + "0xb4c47daca723ad7193ac5098cad4dcab654186ec5ea5c0fd014a3ac39726be954565a901694ba211820c011fa1c59e18", + "0x8835427e86cdceb4c11cbea331ed724e4e78af15e3bab5be54f6b926bf66b5d99bcc40dbc456d86342c9fa83a033c2d5", + "0x8ea84590a400cedba047c2661378921a42f5ca0421da58c1bcb37bc686a2aed98afab3fa5e6ba3a51029390ef3cdf4d4", + "0xb48551170fc479d69fffb00fae4fba301e92e37cae08f596db6f6489c3b7020edc074f9e8d7465b84e9dcef1b6b3aecc", + "0xa6f318b1eaab00836a330710e88bfe400395b3081485f6a212e3cba9463f6fe7864ba4f71e57a411ecdf2bcb4d189f96", + "0x848d5137a39999141a79f4bdf91150796ba36352d8525821bf3bd6e070b352792d79147341b8254dd60fa8c36e9e2618", + "0xa8526f8904b1eac4ae2a25534aa91e8031e9aac7b8f58d8f49897e920c36c0232f4a30aa6eed305deb0f7793c115b267", + "0xb8b6a727c44c37a8388383e959d195d1d0e51a657d4ba360633d219d43c5df645383e2406c25f1d418e72b862c3a6e9b", + "0x92e64adf65b42c978f36dd03ab22ba983bfbb61944efccdb45b337ceb486beda99818bf20d32a545503c4572bb0a4983", + "0x9653bb83df66260a0bd059cd4244ef7c661b089e403d26ba777d2090783ff31f963f5d3a9c125b1ad1a1d19134f3fc8d", + "0xa74e72355e71ae5eb36dc75191643500ca3e67f18833ee981010e7e7e60a68e1b01b05901eff05014b9ef29aa4829f45", + "0x8b2139a5da14524cf6acc593144db23db424b95b8c7041d8f6c7a14a6725dda1cd09c42bb3ae26a5a3650affaa742800", + "0xa60ddff4300ca44a7c7a00a1f98441ad1438e07c30275bc46551cee1b681926d2c825cc8f90399ee5f36bb9fbd07d3dd", + "0xa04e5e9958867a5acc15fdea0d88951cfebd37c657102f6ba1dcdaa5e46cf1c823ad0d98718e88e436f260b770599102", + "0x95e977abeb70d46fe8d7584204770f14c856a77680607304ce58077550152733758e7a8b98b11b378540542b1175fecd", + "0x8c9ec93ed35a25ce00d61609e92d567459a45e39922ccd1c64ab512e292787125bd4164c00af4cf89fd3cf9deddcd8bb", + "0x819819ad0338250d9c89aceda9e217df12ac54e940c77fb8420575caa3fa78930689d0377ba88f16d38179a807135dc6", + "0x8baafb379d4150ac382b14a64788d819146480d7a1dccd3deef6889686ded375900f5df069843ef14d754ad3d7540401", + "0xab827236996bb79b447714c6993af941c5ae66248df4d9a6f3650d44b853badb5c0cb67804210e07a7b9d66ca43092f6", + "0x927656c3eac8d2eb575e3daeb77f9605771170c325bee6aeade10c083d42bd8dcbf3bcc3d929ea437001c7cf9a95e2da", + "0xaf22b212d5ee44fd4197966b9690487c38a119cd6536cfb8c181f38a94610dd9e057f95774047a446504dd96dd11e326", + "0xa44bd94b9e01e3ba36340f2ac2201ecb477495d4f1fb6726a6b439302deabb5a35d237c6a6aeb7e3b0a65649f8656716", + "0xaf367aeeae3bba14fbdb05bcc1a521000dd9d37f5c34ae56fb306d3dfda201d0329a8b6e89d98e15825cb3c6bfdb1194", + "0xabcc4fbdea43e50ded9e2fb01464f4e87fb136e960141e8d39214f92794cfab5634f22cd40b18d8c0e501f2307aad23e", + "0x920786cbd674348b9853689915dfcab02cce2a4596d117962bce36aadddf4bdd143891e22f2c8015517039a64e8aede3", + "0x8cde63b9bd57cb3ef743f1f3e8250669eed739e5fbd68c500a3cc0c12f93862a69aebcdbc69dd8f476c2eb307f572a53", + "0xb967e65a5f1cd8d5d570f5e87e7e186fba51b9504f8e466392a76d8a971fb91fd9b7565bcc1647f50d7d15e48b93bc95", + "0x8d5a87b25fedf5edd57d870304bfd9081dc78c3e3e3b38b997260a92edac7feccdaf24feb51822d2edc223b70bb4ed5f", + "0xb6cd5d340a57f8ec73723c4f3ecd6601620dc8137a3e75a5d3c578bc79a9cae86b379950c644dee2ff99dad780d025c1", + "0xb6f0a8e754b7f52a85a2a2e6512cfd017f7fb0418d19bb318308951c4e242d3c65bbcb9748da9cbc91a738f9ca577332", + "0xa89dcf7d410bccec385400dd96b1cc6af89026a431d0f531aa992cbd7bc8bfd7c5f360bcb665bda1d72efa17bb982551", + "0x97788e7522427a46c4b6258d15623ef7a565712812fa80d001e1de8dc1791392702f3fa3cce5a8cd1c5755625a0ad10a", + "0xb5338fb5e137ff625b27c5148298f27ce8f493e2527c5d0facaa49f29cae34580d0d6c3c1074a2e46cd8db3f56004ea9", + "0x8962f006d7b1095dd0dd132ffe7e87e328510c95ad893cf3b2ab21c177c5cf2c27f47d8856f87e9762c547be009d25c0", + "0x87fee9ce9c26aa476e67e0791a809e0a06a8a98facf3faea730d438d3e516cdf75d645fa75c906e4e44ab9237a22c016", + "0xb75ab972e1a1214bab0b38cc3e973d44bb233acda5b4291f5e110b6fb78fdcab93dc63f01168debd898e165f615be1f7", + "0xb5a0fb52bca279d3853761a94b206acaf313df33ae6303d9b71edae90b66fc507adbc60fb11e758888736c81d5d80c0a", + "0x849b8f0005010e684701cd3a4e59e8c89e5fec59af6d2de5b6332cde03b865ea84f07f0b80ec3404380b0e148fbd2c24", + "0x96e2b0b6fe78408f9208f809f5c40398100b2dac202c8c5c33c2189560dea868270a598c419871a5a2b67783354f6014", + "0xb234b81f996142d0df2c719760bf996544820a03195a6dc0ff6a72543692f5a369bf63d1f0b477ef2fe7b3234e41f685", + "0xb85e39bcf40da1a12a535740176f4de749a93824079deb5fdaa004f3282fdefaf5275e3418c88c419bd42a3dd2ed2b3b", + "0xa27279304b89a18a4e2b443246f2368fb8b15f46a34533179b6bd2ef683f6e98e222b7a32880b39b8fac1afa90133803", + "0x8923c22cf15c9c1964213d725b337ece9ea854775a06f75f232c4859c7142a3942f418354e33066298aedfba3cb27e62", + "0xb109f714311fb9bc431ef57911e2cad6a3949455b9f23255cd7edea35be629e07f845fe53e2b12a32305ee2f4f264f27", + "0xb51e82ae5c7d48050e405897d0053e9ea4b2714d002e88f78c9a307cd50b9c6b3ee7cb86f86527be9d964b01895fab20", + "0x90db256931c7f98bcf3bffff4d496739185e7a20f329ee7bffd4e0850a37739948ec745285703967f4ca50ec370cf68b", + "0xa0485ac0445d88dafac56bfba2563b020cfc370f54c1606c89d12cfd8a4d1336d2ba50306e476155a6f5b0e0a1f2d092", + "0xa00754c3462e74bda928da855bbf90f9077db395e32f03cce9b2955546d900b72330d247b7d607b65e130f5b0d883de0", + "0x8547d56727c3ad8b5c8ce622ed9ad86fe8cd78e6e4848c9845914b5063b17330bd10b46d8d3f18f83ca09ecb28d1afb2", + "0x95b937b2a979bce0e159ac75c7d5d659be8599c92305e73e942aab414793364a3ec28c7c1c8491a5750ba84a29828d8d", + "0xb011e150f0294e45a0f4c69409999d0c2e602449dbd67ab95e8258466687cd733a0329083a31b03722f4e2580ddc95e9", + "0x924651a733ad5e5d9adadad3ea6a6babb8e455c8d5f2cb5bdc83fa422e7752592190ccedaa827b866861e73506a6968e", + "0xa4d5180122f8e31503ae027e54da50f72f5cfb910a6f7309bd882b5cd666f454672591f1f20e461e182a47d03b47052a", + "0xab19ae659c4f73ea3d21895269dbec583c7029955a36469124ebe295027010faab56c4a475973497f28e9a77c03b8fd0", + "0xae7ea1a803d0f439e91494f8f35fc1167dae23834c0c699ffe65d3da8b09f8df5a53195a99ca7b8558242279e69578fa", + "0xb9d63cf0e30f9800101b43b980bcd2f229758e74b21ad5354866b4e684791c08a184330dc316228a0d67fe0210f2bc4d", + "0x8c41629744391ddb96dcbbf9cd99b13d36e57d65962e0aeb92ebccf1c4cc769626feb3ec0363def08eceb102b3dd4ad6", + "0xb2848ff24faf9e667a8c19d050a93896e9e75b86595f7b762c7c74ccdfb9db126ae094961fee7f5d1192776c1ac1a524", + "0xaf013bc29206743ce934d5887b8d0fb3667c89bda465d2321835a3618513fba6a459dd7566268220ffce7e0c97e22b2c", + "0x8bb799e36db1132da8e8b028ea8487dd3266b4628c56dfae4ea275f3c47c78e3d7445ab8d0aaee4cbf42148b3a148175", + "0xae2b81fd47c038b5195a52ab8431f0d3cab4cf24c4237252d955aad2156adc16dda9d3270157e0bfe5a44022e5c051ef", + "0x8e0129213b1698d2ec6df132356805a8633ba79e672e586dfef664ffccca71834253ba14f296da962651fcba2c002622", + "0xa1ae30b500ae77cd9bbb803d737b4a5991cc780618ac22b5cc179efd8fe10afb8c135457f2e7b86ded485ea12eae70e5", + "0x8a39723077b7c0df6e3bf6548afa3910c214ee275951fbe5155a39473be98099626ea14d844630a6fa90292b9594665d", + "0xa628386c79b61aa7314b01d9814aeec20c2a66e3deda322a39957e7135c2e52b1da486d1b9cd61c87afb22c1d10f6462", + "0x97867f469b01249820aadd9a54e12d4fdadd4555f2d530450e1f8f6d2dae57360578e2c2c8ba41e3b5950df596537a98", + "0x97f192d0457c217affa5a24267dd16cb4c01de8fefde9df4884e1906d2f22e73382dcee6c7d910bf6430bb03f4a4f1e1", + "0x86d5b5739de8442dc74d0d8dc78e49210fe11bf8c6ff0f0faecbc47b64812d6b28c8afddf6d9c0212f1988451d6ccb1c", + "0x8ff3312ce9693cd4a9f4b8e75bd805f65b0790ee43fd9e075fe4cebc87185bdf161335049819f22530f54fed2779a5b9", + "0x8dc41d85548bee5d51941d55752a500bde3c5a8f3b362da4eec307a963968e26605048a111c9166d448b8dddf6f53892", + "0x996bdfd004b534151e309ac925fa5ee7801c9da4f6b4c43e156d1158b134535a2a3956e1255e0dd72ac2af6bddaebcaf", + "0xaead652704b788bf4983c8f725c644c327a6e9f6683215f5c826c09f82fd2e40631791f51d14e6aded91fdc018d45501", + "0x991ffab58a82b98ed8fc7b00c3faca153589fe09cebf6a137ad506387a1ca4dba475b0e4a1b9bdad829f1422facaec39", + "0x9652e6c4ae084221d6bad855ec0bc11b5f855c6efba67f644e0902ab790a98861cecc6ce047c68273c3aa7eeb2f4c7d9", + "0xb88b816507aaeea6dc92b861eabdc96988b74d7883f20a4b30ba249158acaff3c50d261742fc9ad2e9eba888a8d59065", + "0xacd028a51e16c07a10d2073b9d03070457ac5f1246365295a1359d015c460b92b4861125fabe6f114de8197045df408d", + "0x806d3cd9d02d41c49179fe7dac5b05dcfc9a205a283135d4f008d0771c58e6f963d7ad0f6798606edda718eb5c7ff3ed", + "0xb9b71f1657a6b206fc40159a941e127f252a7b324dea864ecd804f48c0ed86da9778a925fb65491204a92bc2a26fef32", + "0x80ed67bd0e74350c875abedc0e07fd42ce7cb926f0f3fb1949c6ac73f2300b5a14a5c6f6ff8aed99d5ea5029bb8e7ae6", + "0x9875f67a7a473714e4dd75ee0c763ddf88101532d9680724b3848fef69e218b04a96b90f88e0f4409aa40b9a21507ecc", + "0xb4a2bb1b421e5243e5e7576a0672dc19f9f70315a03f6411c19f76616ffbb70fc5dc0e57fd4ab85e24ea2261b7ce38ab", + "0x879723002ce43e6c75ba2246f51436efe3376242beff987d025c3c4476495af32d52a54fad5d9ec329a442b93bcff1ce", + "0xa4121efbefd9c3eb143619afa52a916f199c75024908047763b29466cdfc837c2fcc894aca63044c33c41c777e529b5b", + "0x895f637b497a9766714a3d9e3c275a1f0c9ddab105bf4c8b7e663f36cd79492022415bb4938c1a4849bda73106ace77c", + "0xb119acb8b161ce4384a924645a248a656a831af526cd337d97e08405415b9dd22060849c76b88a4785eb5e7214961759", + "0x802e712f4c0a17009c4be6c1e5ba2ca3b82adcb68793ec81f4489b7985babd8a3873d544de63d5e5de0cb4dc5048c030", + "0xab111051e4651b910c68ecfdc33f2d99e7bf4182df68cedbdbbcac219a543e04d93ecb2763fe32b40c095c7ca193c331", + "0x855c73ef6afc6bcaab4c1e6388519fd5cbb682f91995bebd558167715db454f38012291beccea8186a3fb7045c685b67", + "0xa29d02ec6d9baf84c19dfd0eb378307703bfafc0744b73335550f3cd1b647275e70215f02d1f4ab82a5df4d4e12dd938", + "0x91510a45b8a50cac982d2db8faf8318352418c3f1c59bc6bc95eab0089d5d3a3a215533c415380e50b7928b9d388ff89", + "0x8286e7a2751ca4e23ea7a15851ad96d2cadf5b47f39f43165dde40d38ddb33f63a07bc00600c22e41d68a66fd8a0fa51", + "0xa413d4e619b63799dd0f42ac57e99628d338b676d52aec2bb0d1bb39155ad9344b50cdfe1fe643ff041f1bc9e2cec833", + "0x85524e5bb43ae58784d7e0966a664717289e541c8fcaff651541718d79a718f040a70aa8daf735f6635dabfc85c00663", + "0x97f0d48a4028ff4266faf1c6997b6ad27404daa50ca4420c00b90f0b3e2d82ef8134d0a04108a74955e61e8dfeac082c", + "0x8df6145c6cc39034c2f7331d488b8a411931c8faa25d99c5432831292637fd983d4f6b1a6f55522b4a42a462d63c6845", + "0x98c2060f67a916991b391e67fcf23e5f305112807fe95bdddb8ce6c4084126557e4c5f003afb32e30bc6808b30d4b526", + "0x8964246b3c2b8f7312f0a99647c38ef41daf70d2b99b112412356e680185da6810ab8ee0855ad7409d334173bcc4438f", + "0xb56c2c416a7069c14bdb3f2e208c5a6ad5aac1cbe5b1faf99dc89c7141d0259d1c6250be9d9195500c4a41182ad2ec3d", + "0xb7864583a4cae3b1083dcdcff7f123d24a69920a57d6594d0b7219e31bf0e236682442b6499a1f6795cfeb4f5f236695", + "0xa064f94139bf1b70d476bde97099631b1284aa6b4d87f16bfc65c075e58b2f1b3c2d057605259f806e545674a1169881", + "0x80d1bc4acf14c0f487cd57c5d6157b7f38917e93cb660f1c25e474fcdcac3c3dfda50f6bcccfd6676bae25c4b6b5014e", + "0x8ad9a4976c4e3e282843518149fcf5d454240740f4b91466f6310b7216d23d70b9b47c42870293252f29f092f330967a", + "0x914197593d2d99d784c704cad7ecd3f0b9f55dce03fc928d13e1a1034566c4de754f1c2a5ade047b0956415fe40399ec", + "0x8d77f5e29c572ec3c0ca39cbae2072ba4102403265b3d8c347a00386da9c0b8688d6e3280c96037c300d57b3545f3773", + "0xabfdf79d935fd4f06a04938d6580a8cbf9735f0d498f49677f26e73d3b34b7075d525afcb4f14ef1632cb375bef7dd55", + "0xa97a8c446e3edc86efac7bda5e2e5d0158c909552a3bf86151df20ece63b8d18b608f477286fb1c7f05605ab7e6a7c2c", + "0x8618d946c7fd62486551c35486fa466bdfcdc63c941e4cff5a01fbbe566b7ea9dc763cbe73e2acae063060b619a212a9", + "0x8d03ee468070936004b06acf64b868963f721f37faa09887f8a82c155ad5c5732572a6855b531db58af03b1afe034a18", + "0x8d3247f75966ea63935ef6049f7c889c1651374adb446f49499fc9191dbcde7ea33cbc1f1e2d3d1756b6e69870404643", + "0xafc853c3a3facb4ba0267512b8242327cd88007cef3bf549184ee891b5ddc8c27267bae7700758ad5bc32753ebf55dae", + "0x80df863eaea289de5a2101f2288046fdbfaa64f2cf1d6419a0e0eb8c93e3880d3a3fdf4940f7524ea1514eef77fb514e", + "0x8434b5888c2b51d12d57da6fb7392fff29393c2e3bfee8e3f9d395e23ddc016f10ebe3e3182d9584fddbd93a6effcefc", + "0xb78cbb4c9e80e3808c8f006dc3148a59a9cace55bcbb20dd27597557f931e5df7eb3efd18d880fe63466636701a8925e", + "0xacb140e44098414ae513b6ef38480e4f6180c6d5f9d1ca40ae7fbadb8b046829f79c97fe2cc663cbccd5ccf3994180c6", + "0x936cb8dc959e1fc574f6bb31f28b756499532ebb79b2c97ff58b720d1cd50dc24b1c17d3beb853ba76cb8334106ce807", + "0xadda2116d9fab2c214ec10c0b75f7f1d75e0dd01e9c3e295a0a126af0ea2c66373d977f0aefdda2e569c0a25f4921d0e", + "0x89a5cefb80c92dcad7653b1545f11701d6312aef392986835d048f39d5bc062cabc8a9501c5439c2b922efc5f04954d0", + "0xb9acb52747ce7f759b9cdc781f54938968c7eeacb27c1a080474e59394a55ae1d5734caf22d80289d3392aab76441e89", + "0x8564f72ce60f15a4225f1a223d757ebd19300e341fd9c1fe5a8ece8776c69c601938fa2d5c21b0935bd2bb593293272b", + "0xa5567d7b277c4ebf80e09c7e200c20d6cb27acbaa118c66ef71cbccb33ee3ddce0e0f57b77277ae1db9c66ed6e2d8f30", + "0xb82e9c2d8df1cdd3b2417bf316d53e9f3cb58473c4cb5383f521ef53e0af961ef916e4f6557a6d8b4655ec01415231cd", + "0xaa816dfd2814c8a25bd2cbaf66303ee49784df471bac4b3188074ea30816f00f425234454d40d8ad8035aa925d74da36", + "0x9919f384df20faaa2d226b521cab207dd2b62420d25ebbda28c9b2ca76a2a52203b2ad7844c1a25f5c75f005c5a83149", + "0xb24a6aa35c2d0f87e36598b36224c64427cd69642b6f9c1bd478a62c70f8ee69f85028648f6603b4f04fb21355f2afb1", + "0x892e044bdb1276b455eac2204be105e1821f987c2570494b1f32aa09506caba7ed343cd09b1bc126fed5e0fda3d0eaad", + "0xaf0e01a3ad954dc048de18bc46bb1c4971db2467e839698e4dd05cd1adcb9261013fe9fd0cafb946c0b586f6aad86d4e", + "0xac152f0a9ace425378daf02510eb7923ff1ed2c0f8d1deb918e4efb63655de1ba58c96438e9aa23abdf2431dc771370d", + "0xad8c7419c097709347e2394195924e09617b47ac5c7a84aeb9deab8975f22155de0f70cf20d8a976551b14e3a2683a2b", + "0x808f14f67ae801536fb70a5898ab86e50ad35340cffd0648daed2f2c4564c9ad538034b2a179a6a8bfa27e9d93b4cbe0", + "0x80a74ab7ce4769db93cfa695a166db95f0a9c47885ff826ad5d93310f36d6b18b5351c67c858b9837b925e85a1995b63", + "0x95b88c3cdd64401c345828f4e4754b1a88b4875a14c08a668b90acd499b3b858842669ecd73a46c5d9f1de32ec1a0120", + "0x8ddbd770b7b18a5917eb43926fa05004e819f1d1ead05b915269e4a86b53e0633a90559007e59f6705a3769e2126ac56", + "0xab6db5fc220754f19948bef98844e6e38dd623565d1695e1198040c228ac4fd863c1f168cac1d036bbfb718d9d8dd036", + "0x97bef628e977c069e60c395a17740e0e1bc1828f5607ae7f30ce5a0c95f02b53af2ad062700a75212e462aa22c3c5465", + "0xb68d465e04fd17ca98501e61eccb0ce30401855e98046e0c1debba71c2153d6a7a704aa36a6f12454696e78e87181cdc", + "0xa79cfdd048f4181e005bd0fbac0a8424495474956b58ce858d2b700fb0f931c406282bd33bfa25c8991bc528d12a69c1", + "0x843f55fa0a6a0969daf2b48080738f30b269b2e7ec123a799e5b203c0b3b4b956dc95d095bc6550b0013918cdff8a225", + "0xb683cdf2823036827e5b454bfe04af9bec1850d25a7a7a44aee7696b6ff0468b7ed6885a41dde2b8f3ecc4aec880c3d2", + "0x8b500796e82acdc89778e0c0f230f744fb05f762000fee877bcf57e8fb703d212dbc2374887bdc2e7b7a273d83a85798", + "0xac35a8ee87bafecb1a87f15abc7ccf4109aab4ac91d357821e417f9b1474d196c38cc41cd13667f68d1ffab5e79a6e92", + "0xb6e517739390cfed5b395d33b14bce7cd7aaece57fe79a7eb3cbf150dc10765c3ea9fef7976a21a2243687e6eea38ef6", + "0xb53901eeee26692273365b789f2a60afc9b5f0df229c6d21b07016cf4c0e7985beec748aeca52262f68084393ab038e1", + "0xac4804f33d8ba2b4854ca3537bd8bf2dda72d4e94ff7ecaaf9bd3b7f098343d74d765471ef80072ae34f860b052cbfb1", + "0x8c6a30a93f1dde18039bbdd1ef294552bf79856e20bce863e4b8dd72d906be3ff22468ff3610e06b5a7d1745dde7ead9", + "0x88f0607fa3b7cefe20a02115572b16fc3222be86bb19e592c86c48afbe7e0dd523492b0c29a3bceb9a20f5538bc3134c", + "0xa660b801bbddad725975ddf9a8f606f76ecef831f954be224d6178c368e1c72d346f00c4a4c95c289b62d36f2af323cf", + "0xa75b9a6aea9542b698938dcd6cc2f6fe0c43e29f64b2f54aeb05d35fac73d41aa7fd750af4fa9333644aab8db90775b9", + "0x83e1b7129d963d1cd076c3baa5fe422148e939273db173e4d59d1858a7d841eacac7fe817d15ab8f8a493bf46c2045e6", + "0x9060a2e9c24de11f9c70e039b5ffe9e6d32f1ae39f3dda263610df2265d917679e689898e4a8bd84ad34613dca5e3761", + "0xb42fc8b863a2af15e04d1fe6693c09b46007c0b8298973fb4762b45b4590ad7fe0aa758918b2fe5ed1ed0359754fd955", + "0x83e6de7860fb256ecf7b47506a5e557d0fb0aefe57fb513c7dee2bd9604712d08ca26adca7ba9a54b712372a7c585a26", + "0x90586e9cbbf71475ecd3e7b5753b286804dcce61e165502a82b960099e79272de8b7494b8877b54ae838eb5d0f71af2f", + "0xb2e4b0d21208f73b7b75e08df80cde20c4578e117d37092a490af82354e2afd3a7dbab46fa2d12fcb731cdaece69c2ba", + "0xa010961239bb8809fc7fb4aa08fa30d33a130f9f417ee9ea60f587dcc5ef4e1b7abcdcbf8e848ecdcb7972ef6af46e78", + "0x8f511fd58d1e3403a5eefdc0a4ba6b8af848c7efddbf9575ee84449facde05ae9a24aa41a5725416467f6fbd11369c52", + "0xb24ebbd2d4482eb618cea1ac4fbfd9ed8c46c0988a27259300a7ce5ce1bb256aeca0357828cbbc4cf0dfafbf586040e1", + "0xb3ea29e9cca55250e9b7b9bd854edae40f0f0cc65fe478cd468795d1288cc20d7b34ced33bd1356f1f54a4291faa877d", + "0x8a8b20f222d9e65bbde33638033972e7d44c6a310b92a9d9c5273b324c4ad1a94f2a10cbce8300c34dbd9beb618c877d", + "0xb2436a9a647dc3f12c550e4ddc5b010e6f9cb3f3504742d377384b625fc38f5b71710a49fb73ffaf95b9856047c98201", + "0xa13f8b77c70621e421be94c7412454adc1937b9e09845c2853ef72cdbe500e5c1bf08e3c8b8d6b8eff4bce5b8dec9213", + "0xb25de8780c80d779e6c2e3c4e839a5a107d55b9cccc3ad7c575f9fe37ef44b35db4c1b58f6114a5f2f9ca11e1eb9c5fa", + "0x96ba6ad4358c7a645e5edb07d23836cbd35c47d9a66937d09486570e68da3c8f72a578bd2e14188d3acc17e563a652d7", + "0xa7f55989814051fda73f83b5f1a3d5385cd31dc34baf94b37c208b3eaca008ff696fd7f41e2ecffc2dd586de905bf613", + "0x882d0c7c81e58eb9560349f35c35e4498dcde7af7be8d7974b79d262304c26ab67ffa5ed287bb193d5f0ab46b4096015", + "0xa607158f0c1fd0377a8ee5e9715ac230abf97406c19b233d22f5911ebe716967cc10425546dc44e40c38bd6c2b4bca2e", + "0x87e8cde50e5d852d3f073a43d652f7186bac7354612517cfaecd4a1b942f06fef6f14546279c0dc0262e2997b835b2a4", + "0xa1c93acc6db9d5ee426fb4a0b846bb7a7b8d5915bec777a9fe6907246b0beafb8938941c8c79ed6082155f75dbc1e332", + "0xb1e4f61457b86f76cd93eafd7536f72baf239ce5a62bd5a8085a34e90576b1e118e25002d2de49b01d6e9a245ee7d3a2", + "0xa0435fe9a4bd1031ec5973a103ec9396b2ce9fd982f6d9ed780fa80ac06a6e47a0a6eb2daf52df1dc9292db622ee9fa3", + "0xb66d8e8a1717e4bfa42083b6ef4490e090a73168b2912f2111743e089027be0a4945a229ecf5d0b5eec11b23f0e11303", + "0x8eb764f26904eea4f4169be6e75beaa6a39e4eb524625a15a78befe3d8e3cc82692d9b135590c20ed460d6e4ba630ef7", + "0xb7e4aea6bb09829e53fe83e53f49a7a331a6d7bf76e0073d758577e6d6fbe63dab642b23657355cad48896ad8715119c", + "0x8f94207982373a99ffa282673f192aa98d0c4461fb77c31dc4549628bd9687a249f1b3c66b1840929341e42516c5c64a", + "0xa9c673cb247b13e17fa5e616f0399b7f5c7ad043e143e44ae68855a840870ab3d2aad737ebcf74c2cc9688d17ef3a794", + "0xb02635104dd28c02068985256975c0af783899eb996e37d021d9a35238deeea9e836760db21869be7b6c82aa687ded29", + "0xb33bc0966389710812b5f6698afa3e9c84839a1b85492ba11e6ded26695260abf66be6fb355d12d3a8524966f0f89e0f", + "0xa79c0dd09506951c33da3cbc23843fd02d641fc24c640a205e6e8150240372847312b9381fb03c5d301fe4dbee8d0da2", + "0xb74de6f3a2c502b5b658ebe8a9b7edd78afd036f5a2736aa06502863b6865d131b9e3542e72a86fa2e1d2db4927661ed", + "0x99e365def1452ff9fb4b9eccd36ff4154d128469ba5bd73e83ae457ab53977cf6fc04a5d05bdcde357ab539e34bd9fe0", + "0xb4f2bfb95abb47c67870aa6ca38ac8f3ae1b1a2bed064b1be7ff90865ea12e4930fcf66429c7ecd1183fae4a01539386", + "0xae4bde87f36b912e92398bf72e11d5389e93b2de1b277d7ed4b6fb5a9ab9f71a959ec3bcb734c11079440fe42b86fafd", + "0xb826459e568efdeeb66688482b67ef5020787275123fd3192f979b6175e3b0ed59e17cb734a0a052bf13f0afc7bd237c", + "0xa99dd735f4a7c85cb23dcc7f4835f9ab32026886909aaa95876b98029c37dc4d621726c872d3a9e50403443c958f4029", + "0x99083545034768010988bf8a9f34486c2cd9da27a1d10db3ab86eb69a1dd9c8ee723e7da4ef2aced63c1dbd53ccc52cb", + "0x8ac3209349f0142546c714ef7e9d1b094aab5469b8f080c0a37cb0362da5349e108760f272fbba770aa468e48d9a34c4", + "0xaf5f48ed74b21e3f2c1430192adb4b804dc873cd7e8f07130c556c30e7b78df0ef5a14b205368848fa9185e5a68dee0d", + "0xb8b741b65d68df89443523ba74203226f1e0d13bab073d183662d124e83e76cd318b2bfff09879c04d81b577ac895638", + "0x914abe4282d11176d4f2f08c6f15e6c2d0cde1ab4de00bbe888015c205f51929d97296a0a8d3ca5641f085a29ea89505", + "0x83ec306b2a9a6780efafe799df90b1aebdbff7d47921a136ea8a5648b9708a97231245a1082fea38e47ecafbbe000528", + "0x95d6b58d70b388dfcee4eda0c9805362ccfb60a87603add565b175b2c14ed92999dfdb0d3724ee3e5d30535f282641e9", + "0x97eeb4de607c8306e1d4e494f0d5db126d53fd04983ab5674ec5996b971899e734fa4011f2c889da21154ea1e76dbd2f", + "0x84ff21977fbd873ea06bec444d4ec9ff0e3902edc29dfa25f3bed269b3709e3116e99dc06cc3e77f53c53b736bf8fc29", + "0x8ecf483874a040a4a1c293af145094fedf203a5eb37c3e165857e108cce3e1210e0bfc0f26f4ae5e2194024929ba034d", + "0x97d9b92b2ef34609d69402167f81bce225ed3a95718a3b403f702b93e96a121a8f7f072d0ff47e8b25164e204d1576bf", + "0xab87c39cca1803b4e84b32e40ff30289e3cbbcfbe16a70f9e025643824752359be1f10c3e5398df402b6fec64d5a3537", + "0xaf84ca57e6944332884b5c84750afe0d5950015e127acec161853d55d48fd864c7da8d59cc5aba4ceceac650b813fcc0", + "0xb1d23d98edbe7089ce0a8432e0eb3b427c350fb4bb39eb2aca3c2bef68c432078cb9b4b2c4966255e00e734fa616638b", + "0x8e2b5252e0ea96d40835ebfb5693af49946509975682d68651396d6bb1463f09e75fd0afa04ccea49893b5b9c3e77e40", + "0x8db25e762f1d4a89a9a1cbc61c01698e775906bc88a921b2905735457a35df9ab84bae12e1b1b8dafadd50212f1acda1", + "0xb5f7cd163a801770a4034e2b837e00191b0ac63a2b91032ae9a99ec182d748798df48a14644935fabdbac9a43a26749a", + "0x998e7232e5906843d6272d4e04f3f00ca41a57e6dcc393c68b5b5899e6d3f23001913a24383ed00955d5ec823dbd3844", + "0xab2110a5174ae55ebb0a788f753597bd060ee8d6beafc5f7ce25046ea036dba939d67104bba91103d7838b50e36703d1", + "0xa211972a4f6a0303bec6c86f5c23c0d25ab4df0ba25876cbaad66ae010b5a00aa0c5daded85e4326261a17a563508a25", + "0xa49f53496a4041a01e07f2c2cf1e84e2ee726917bb103fd267451b9b7bb1331c0afde85a79a55409bfde27328b2a4745", + "0x934e915c67c7fc47adeabdde49f63f04644fe234672003be2aa0a2454dc8d9288f94293478936a450f2e3f249d395b5b", + "0xb6e69e9d6808ff7f60a01b7aea6781495d7a20f5b547852d3f0af727a7434209d3015a9dd04cbe3e272918e32e345508", + "0xb348d3462092b5c6fead7e515e09611438db8d69650876dd3b56226e303252bbeb9e9f3b888fb911445b0c87132a1d0e", + "0x8d6510334a905efe5a32001e167f1ba06f9bc4af7ffbf11b7f7bf3c0076b5cca373d8c47e98c1ba8755bb22632bfe0e7", + "0xa2d5200f20985dcd473d119ee97e1c0fafafa0f191185bfed9cac429cef8198d17665dac4f70342eea66e6e4a7370d58", + "0x8dd7eb6b1841b3f33425a158d33a172b79b2dc8a01378e4174e67a1a4c8f4b887f02c7c3a8f354ed9eac718155bcdf37", + "0xb16ca19388642f71afcd9f7007b490d82f83210ac1a989da9d4bf4c419de07af8c048cd301ec7e01b9d06abda7c169d5", + "0x93cb2d847d1a88de8c1c9d5b3c83efd0b7afb3682942bd2c8ab5ef35b33dc31a097a3e181daab8630d4e840b677216dc", + "0xa8b648c769e77a7b41c0c689fe2fba9bc585067e004bcb1732cb7b1618e97b317781c36c23a00680fc780b58c301a789", + "0x918c321100d57712866bdae84edf7e42df30a32853af257e0cb4da028842a43b49e775f3cecb85cd817269c728de7319", + "0xa7b0f6ce42e00c519e69b2c78fd9b75a2e7103e5892d3c1afd70c9b5b9e706180a4bf73dbb2d3eed52bfd521103ec5b3", + "0x90041994af3322b010891356afd8115340bd7fd7ba328716fbc4fe458236c8cad8c7564ae473d6091ec3a54bdab524c0", + "0xacb1ac83809573846231f9be2dc5f3e986cc36dd9574a620b1cced45bad0b11ea957ce8c6cbf964a0af916781c574f05", + "0xac54677dc002698fc4d454c7beb862ad085d0514f92576f3485a44c0cb47afb9db2c085058918a3508f9b3de0137d97c", + "0x8dea56e1bfa150e442f8484b2952b116781d08cfa3072d08657cc09b0217276efc4ab6f5fd726bfd826f6976ced8da29", + "0xa2b09e25baf01d4364b5205fa0c4dea84ef8fe03709113b034f88a0f0a502a81bf92c1d4641e2ac9f3a6f4203d3645ee", + "0xb95fe37aa351b4292691a9c2e547224c37ec2751a31ecce59810cb2ae0993da6fbe5efe0ab82f164462fa3764b6eb20f", + "0xa3498947e91a3a540e86940be664fc82f1e83ff41a0d95eb84b925e820602a41b7393c8b458bd4ebbe574a754586787a", + "0xaa2516d3620c832e5728fefdb1af0be30c871cbad4b166a7a4565af676e73bddc2f2f51acc603b3a022056daad2b330e", + "0xa9251b56467fb55f64c70729e2ec77a59d7eac79cc0b4b25ee405ac02aea46bf1cbc858bc773934a6d9bea57cb528185", + "0xae8c0a4ca7ba6bdca8764bac98df0581f00358db904e57867e6ffdf15542e55f7bad2dedac152ef88038b466ed901934", + "0xb0881e27e52cc6a57c4f3f278dffc7f63a9174b68bc867c16d8a151d9cc4d0aeb703d1074d1927faa9ffb43e10912c9a", + "0xb67138465d6654ded486d18e682f11a238d6a65d90f23d6b13eb6a1b7471efbac9ada6345dfb13e5432196d2a256829a", + "0x944c69a6f1126edd38f6eef60b8a5bd17147ab511e44e8e0a442e87244d8f35236ee0b8d3dac0631f8598f16486a5f74", + "0x995679dbe03dec775da26708cb9200dabcad983825f1ba601eb9395f9da350ca71e8af61dbff4c668fd0eebac7e4e356", + "0x89de362f02dc14de6995d43cdea3c854a0986c605ba5eb5dacf24e3a85983229bc99a2fcf50aba3df59f0fb20daffe29", + "0x84607f0e2d078df22d0866285614f5d78cf7697c94a7d1b5e02b770101ceecbfd53806b377b124a7320d9fed65000b97", + "0x93e3faab60050dac76ab44a29bcd521813e76ec8e4ae22712d77bb489bb49f98f9087acfd6a77016a09a42ddedab2d73", + "0xb7d64a7a35f21747b8e6a874be31ba770c0d13cbd41448411994e8cebb59591295a26bacbf74ee91e248a5b111aacca0", + "0x8dcad429a2b0d66b9eb8c1c3924d7a72979727db6a535526a3518bed2a9532d12aad1c5a778824ca4cb98e3e513f85f8", + "0x980882895faa347bd2fd1dda7b8ee7ed49e69843afe646f677b371eecc7a10e0f4e40bb55f28995a40080df471876816", + "0x89e8e7fb51df79971e2f7bf65783614abbb0d7f3f1b4a15d3f0d160deafa7ed1c446d9a5ae1a77160d4dd94ceed8af13", + "0x93fda8d350392e9c4d4ffe6534f7e7be53f32483d9319093e8436fbb8166a3c01085dc858373e65c7f4d014e0dc2bab7", + "0x897521a87b7ebf7152de5260c0875e3c7df1c53e734c672569219ee6f9bd196c5ecef159b6a1d3b7cd95e91b9b8803ff", + "0xb59affa408a0f7bd7930fa3b88750fd043ce672c10a3adeba95a12f23f0dda1793f761a86f7409ce1e6fd3b3b7195381", + "0xb4422ccc12f4fe99c530cda610053af9ffe635b633d52492fd81271d1f6f91b87171d572d5bd0e46ff63e221fb2fc4a5", + "0xa4542cdf3346ee0867c08d630c2aefc57442f1c05c0eba52d223bfdca5e9d0bb80775cff6ce2e28aa2730231fd7b1bb1", + "0xa7d297bb09118b914d286e5d1e87bdf13f7d174b988e38fb5427902e8e8c674072f36b19055a1070abcf357f8668f35b", + "0x9213b0ae24b7cb43ae95e25c09fead8bdbac55141694137d67eb5eab5e90a348a13d4d4d2cbc6436fc4f4f9f7334ced2", + "0x8aed71a0d116d832a372b42a0bb92a1980f3edf8189bdbaed7cde89fc0418b3ab21a04f5c6e1d3b8edf73f1f62bd6b15", + "0xa6c47d77d714c285c84c6b9458cbec5e3b191c0502dffd10ce049cf1ea27ddf868ef0cff13a2377289fa6c932b8e4f28", + "0x92f45622ec02483f2c1e07075a6695416d3768c8984856f284f40734346d56cb5b3322f20c2c9f0ef8e58ddc294a309a", + "0xaf6450d02b79ac9fc79f35655b58fd3619cd5d38c5317564b453f5f2d79d7a030bf767e399fe01b658a72fbd2cac2356", + "0xa3c01fed5240eb8a61ffa8ff4a120dbcebb53b8e19845949c77fb4f9b2c3dd52c7001df6219ad2f76c785a4ee0f64a2a", + "0xaf3136bfe8f774187bdf87555a1ac505322a956229a285d28bab1c88d4f4d12245af8dff35914a62e90e49f3dce6acb0", + "0xb20e21d28444fc96737958cd951858fda324b924b4d3d08932540fd4b87150f053db6985b96903906ce83dde0578cbb2", + "0xb7978101071268d1f485134b4dfd1e35f89b82c7d99ae91f58b6745f5e0273b7e06f3b23009033ecc3e41b2e9e85219b", + "0x9104b7d75245b784187175912cc0ad869e12f1983b98e052710fb33663224362bffd69ceed43e7d4ad7f998c0a699eb7", + "0xa7624cd71b92699ce3fde0e747976ee04ee820032ac45dd27d769edf3b3379a4b8db358e50c9d057c63b5a9b13d76bcd", + "0x9354a76f294005de8c59db10e638ae6e8c6d6b86a699d8da93143da8478d36116211c788d8285d8e01ea6647dfcaa1aa", + "0xb85935c04cae14af9848db5339ab6420122c041075ec1549314e3c9c5a610d9b794ea3617c50ca7af6b4aec8b06bc7dd", + "0xad6835a62311c84b30ce90e86c91c0f31c4a44bf0a1db65bf331b7cf530cca0488efaac009ab9ed14c1d487da9e88feb", + "0x80339f0245cc37a42bd14cd58d2a8d50c554364d3a8485d0520ea6d2c83db3597bf51a858b10c838bfc8b6bc35619638", + "0xb370420ac1a011f6d8f930511b788708ccf2fe23ca7b775b65faa5f5a15c112a4667ed6496ae452baf2204e9ce0dbf09", + "0x8ceab3dadca807a1c8de58ac5788313419c37bc89603692c7a4d96e2311b7fe9e813cc691a7e25a242828cdf98f8bbcd", + "0xac1526ebc6bd4ac92ee1b239f915e494d0279fbd065e4cab1f1b8a1663f67daa89560f6c99bbc3e63fa845520316d2e6", + "0x8240ab0bc36a29d43ec3059c7e6355ff39567e135f93b243145d3ada97fd1c970743819e0d58bd5171967daec144e7a1", + "0xa99743192a6f1967511b2d3038cc73edacb7e85f84b2926d8880d932d2fa12f5215592311a7548494b68a87ec70c93eb", + "0x8ffffc31c235997e59ab33c2f79f468399eb52b776fd7968f37a73e41949111957434f2c0a27645ab34c741eb627cd1f", + "0x8949d955309415d6d2cf6ee682ccd0427565142c1bfe43b17c38de05cd7185c48549a35b67665a0380f51aef10b62a8e", + "0x9614f727a9dac8ecd22b5b81b6e14d34f516db23a1a7d81771ddaa11f516ed04d4e78b78fda5dc9c276a55372f44c4d4", + "0xaa85d3ef157407bd8aa74032f66bc375fddaff90c612470b5ff5d93659f8c3523b2d1b6937b3cc4201c2aa339621180e", + "0x86f8fe8bf4c262dc6a04620a848e3844f5e39a2e1700c960f20ee66d4a559a90141ef4e5091d0f32acb1e915af1e0472", + "0xb3af2eb785b00588371beb3b49536b7919a3f2175d4817de5dcbf7fcc20c512852ef0f313327fd0589b10173f77b92e0", + "0x8388703c512eea59190351f3bd2cce83ff8bcb3c5aefc114cccf9e9b3f78200d8034c3ebe60448aaf6c912f0ff8f0cc4", + "0x95d0dbbbf08ec1ed3975fe7dd542be0a05156a2b3db5092825d918a849411ee536ed958201f74a5513e9743674d6658d", + "0x8d1a48802f1a2db247e633ddf61d3ef7a2c062c48dda59bf858916e04f56651a7d51e367d6535964ebf3ae6d2b21b421", + "0x971436871bfe868f25247145a55802945409b3150008535b372c949760d7949dd2fdb40d9b96ae7473bc8f6e9b83ecdb", + "0x8ca431728ac0f156763090828a7b6d860bf591e5b9dd3bb3b7f3ba0ca74191f9710ee55efd32db7d18eab5b479cee8a4", + "0x81e28f1a506e84c2b9aba1df720cb50e0b597b2c22f98acc34e710c934cc6f97dcaf33d589e845c2c1f6d8716d05ccac", + "0x8f43b11d3f00c41d16c9bc9bc0c44227c056bd77de4f1ca9a799418c5601e744f99066bef47da2d9088ae88eb259327c", + "0x8d330aa52744c08ef98cc5599eec8b9b4dd18aa01b803f1d1ca0e29b74f1aa2886ed0224390fc377af25852851fbee03", + "0xa06f5b203b67134c685039ec2bdbcc787353e2575ce73a415db24a517c0c31b59d1de89f12b97cbef0219fb6a1e90a20", + "0x9269a5f49bbb8fec1a387b5d105df88a027de615d5ca6afae20fe89b11746f8d23880db78dac238c955fc8bb3de18046", + "0xaf5074b3bc0656421c314547b45b5abd3045ca1b17f5e34ba39d8c1f7928a55d4ca5ea9c2ab59a55909b25255233e04e", + "0x8e7ee5d733c8e08f3fb7d85f0628de3de6835121672c65374905dc6d19e02fa2df14c13d5e9835dacd609a4df09abd26", + "0xa9b9aaf83d31e879dfb8e73a0708801b4dbdb5d7c8654b27d2c0f5797ebcacc8d00a82143e2060f0917c9d41f1a03de6", + "0x904872aa1c093cb00e1c8e369a3bdae6931c5b1ed705dd3bffba243dc4f42df3e7d7cf70303d513b34d2245743d765cf", + "0x8a4d6b3b1d6afe67383c66693f70b397e510be28e3d97dbc8ec543d699b6cbb0e72eb90a7f65e83cf9f7ef50fb18b128", + "0xa914de13916e6a0dc0e0fefecb3a443cca80d83276513b70c22c6e566a2d41acbd33a0e2836ee09abeffd3a4894e437e", + "0xb9c408f5f05934b0aefab301ba22f8254c5ebbf5405b6aa788f76e4b328c150b395f441e3566015a0deb3eca89afe9ff", + "0x8d32aa2c81b2a8b89f347c2e0b6567b2117ddbb778fda8a3f19004b7f5aa9dd814b9b3ad35f9223715d2447b2d12f159", + "0x8230e8b9c84cada1bf14ea6aa9ecdadd978d893cf5962fee6c7167ed21239210ea491987f2c8f2e8cfea8c140704ca28", + "0xa5d7b6285fea51c6f21d0976a7c3a97baa3d733a201bfaac0994db6c65611d91c5fc0ebc2a7724ee02b371e575573649", + "0xa54f00a9530f6930069f5e3a8b8b1d52ee1def0aad1763e3c609ec07f25410969b43d5943a94c235ed5eb207b33a402e", + "0xa8dc6e96399b81397734c61c3a8154e55a670fa25fa5854b3c66734cbb4ec0d8f6ba650ee3c71da3773ffc9e37abf8bd", + "0x8841fbfae1af4d400d49f74495f864804f043416c09c64705251d021b3ab7881f134a00b0241e61010617d04979d747d", + "0x95acea7ff4861cc969c1d8cc8775c5eae014ad6e2e0e2d0a911dd916c34ae69f53eef779cc24ff1eac18c2b478d3ba2b", + "0xa5dce74abcfb8c68031b47364bd9baf71a91db01e45514ab6216f5eb582ef8fe9b06aaa02f17be8b93392d9b19ab9c06", + "0x89e111169e4ae2f4016c07c574a3bdacd8d2f359561fbbdaa3474de9bc24ef8936784dfe6fe0e29a13cac85a3e622b61", + "0xa4c511af6bdf3892939aab651828259e4ef6ebecfdd503ecc14e61001575b313a89e209cb55a77ec19a64d29ada066ef", + "0x923c62156fbf3a44926ffb5dc71f7cef602dbe941a98c61f019a27a18a50c16b6135b6099fe04a2e1dc88a6cad989fb7", + "0xafb9191c541b61afa0ef14652e563cc5a557842ce2afea13e21507dde0ebbe6da5233af949c998c00865c79bb3d45ec8", + "0x8a1f0ad65cb2b225931f41dc53547d756111ecbf5bc57c5ee2cc1ffd61b126d0389d311ffe26cf06eaead95af09c5ca3", + "0x9040b20b5ac2e1a9d30abf7a4eea1ec2db8f3077cb2cfc8736b37222d8d3937f5d9f421167086dc5551e9f0bd2522d07", + "0xb6d888b8c6bd448dccaf99c3f690d47f802e134709ce102fb6f6fc68156943c0762be6f386338163e01eed2d1dd5f734", + "0xb94f0e27bbcda793e4a272603b3dcc739d3bf3207798df7319f8dc9d37cbd850e3724bdd30498c929debad971950223c", + "0x9769827767be9d7bacba1b687289e0794c6fe630d33c9b607da1f6a65e3f34cb8bd65327d9287c8c5f3c8b5f6d3d133e", + "0xaaac72c993aa2356c9a6a030950441de42b2d746bace29865382f0ef54835bc96958b2f00237d805ee6a69ca82117c1b", + "0xa2b1f027d80c1b0e79bfc7dd252e095b436fba23a97a1b2b16cdd39fd39a49e06a1ca9a1345c4dbb3d601ffa99f42bdc", + "0xb3fa0ad1478ca571e8aa230921f95d81aed7eca00275a51b33aadabd5cb9c530030691d1242a6ff24e2d4cfd72a47203", + "0xa43ed4368e78daad51b9bf1a685b1e1bfe05bed7340d4a00df718133f686690c99198b60031513328fc353c6825a5f2f", + "0x965e145711ecf998b01a18843cbb8db6b91ff46f668229281d4ca52236c4d40804ebc54276e9c168d2a2bfc299bcf397", + "0xae18e6efc6f54c1d9230210ac859c2f19180f31d2e37a94da2983a4264dbb58ad328ab3cbc6884ce4637c8c2390f7fc1", + "0x83a9200486d4d85f5671643b6daf3d0290b2e41520fb7ea7030e7e342d7789023da6a293a3984308b27eb55f879ad99d", + "0xb925fb6ca83479355a44abbcdf182bfac8a3c7cce6cfc7962be277ce34460eb837c561257569be3cb28023208dea80dd", + "0x9583dd991b62ae4bd5f379ccd3cec72cfae1c08137ddfbacc659a9641e7d5a82083de60005f74fc807bd2acd218d0789", + "0xae73bc32e9ff5926e1e06c07a3963080881b976c9875777f8e4cf96af91bf41bdbed4bd77e91253b8ec3c15b4a6d3977", + "0xb2a3ea90aa398717ba7d8c46743e4c487b63c5abb140555d8d20e5115df2f70d3c84a2cb9a5e0536b2d93d24f271b38d", + "0x91d119d3bf1d34cd839eb69c6de998b78482ab66bc93fa97e31fb9592f36cdfcd673f52366f8c8e8877e313b92d4a2ad", + "0xa1907e20120902cf68912cc3046f8806cabbd7673e80218814cb088e080dd93b5dccba395b13e0025f5755c183276c3a", + "0xb2e2011df72504065ec4c12cbc2137b95cfcd1355509671feb7b00dbf7f8d500476a49754cb7fb9219cb5cba7c8afe01", + "0xa48589fb7a74a3dfd782cb3503e6294a81dbb6adb412887569f9408e9079371edbd9822388e0b7ec8d3297ba270f53ef", + "0xa203909bfe196ac65ed3e6800d577b6ca5c8fe1d40f7f925a43852951e38883f2ffd250a9e16fab3ed3dc1249650247b", + "0x997ac293722a8b98f7e819f8e6c2d4c5bd1103b82d489d8b8aabeb905e95450b9b75bd61442cf68cc957212ec1c55617", + "0x9895a3de62395c33509b153b7820bd94fd2b011f0cac135fcf916482f1eda272ecc79f83a61837e99c3a3c4ab2c5c2a2", + "0x98c2ece4d49a64ec8e06407a0585081003bcef88af35210e22eab91169f8f0c044d611494b755e5bd915804b1d857747", + "0x8bc6dd083b36d076ddf0e0bb1bb87cfd059283ddabb3886f02eb7e27f1f0539b2819527b56b5c13436523c4603ac1d12", + "0x85ab8b7a696333c82dd5e179e12b2e127e67d911de609ff9a03cab95cbeedb1f364aa1f2b5e59353e4ba0d177f996151", + "0xa9478e214afa68c395aa2c7daf8ba1627feb71ad6d8bc7339734cdcdd5a42838e032736c28e6251c808d5a4875ef0d06", + "0x8c53f62cf06a35321c8af3871ee4459768d0745ebf48942b9f464206309f42fc7b2c50f196ae1e43b664f0e2e718a23a", + "0x8ba80662f6642d8866e832ec8082a4204ebc993fc304c4b794666856de0407620131a18dc053597bb40a3de0bf8aca22", + "0x8c8fac6b911785d1561a985580c03fb2ebc613ae33e486a92638aa7d4493374118d9a6d9d99121e29c68c3d67ee4e3f3", + "0x90f2c793eee07ad90157040b30558bb3b0164e8ddf856389d6742cf5bd1c712e4c6a8e5678da70a8e9e242ec7864117e", + "0x954abed8f6d58896b7f6438c9780236c1c83b02d60a29fa7361559e619e5bc9d67b3646ee39ffafe2b3019bb3357fb50", + "0xb79874f757a33085e1e751544de8fe3afbea92e0234f9c00254c2b36115a16ee46f085f22aa66e0c9177e5106f51b03b", + "0xaa148b287cf4f60c64f774282b421aae075f0eaa93a45aab4927750f47e2ef0b811d1846bbb15eeb2f293c80a7612e83", + "0xa588d8825e7b0168d45499dcff6faf0dfe1ba4f090fdc7c06d50344960c0121f10ad109b0b9d13b06ef22de5a04eef87", + "0x8f61ec93d14ebfa9c31731f9ef0fb8907505fedc79378e9a3f65c27bed4d74b41e129c97672ce5f567d897befbceec8c", + "0xa008218633f1da10efd01c155f7ed739faec902da6dc48e9f19ccbc8d32bb318d71806285cf2003de2c907bbdd4f8b22", + "0x88ad82c66f7085632d7e348d69da84200c53594553acf5432b50dd1e87f410c802dfea91be3cf804e3117ce13103f23e", + "0x8498dba17de0318af227a3f9ed86df37a5c33f9a538be9823f8dce4efc3579e8296cb3b7200cee7c5e0bfd9da23a4b69", + "0xb3c0342231dffe4c9bc7d9265597bc8cc4a82e2980ac6d1407108db5b00349dc91d5116fab51cf2802d58f05f653861d", + "0xb3f2730455f9bf5a058598bc60f47740117ba51f6a767e1134516a4e42338b513f377027acf8825da5c4d047a62984fd", + "0x816360914fbc9d8b865157bfab07aeb7b90bb5a7c5cd64847b1c3184a52266cd3f8f8f3ef99309ba2edc4622304bacc0", + "0x8fd21b2315b44a52d60b39ebc45970a47b9495f42b88217ae057bebcd3ea0e2476c0c3d13de7f72016ae12ae966a008d", + "0xb62014485bc217a0fe892ef1aef0e59604ad5a868face7a93f77a70ba3d7413443fbe7a44552a784d8eae1acb1d1c52b", + "0xa905822507e431b35f56724f6c8d2e93b0607ed7a4533073a99cce2b7c1c35367382447073a53036dfdb0d04978ccf2a", + "0x81672e39c2b31845142963351de3d9cd04c67c806fdfe77467867463dbbd8a9b0e2400ccc55016e57cbedb02d83a0544", + "0x90919c970ec668de8ec48a2a73bb75cb94f0f8380c79a7909fd8084df61ecd631476ddd474b27103c6817c8f3f260db9", + "0x8fbe37dfb04bf1d3029f8070fd988fc5e4b585e61eab6a8b66caf0ffef979d3ed6a662cd99468ce98ec802e985da5fad", + "0x950939aabb90b57a3d667f9820880eb0c4fee5c27fe211ce8ecd34663c21b5543c810b3676111d079ac98644c75ee0ae", + "0xb06201ec3c3cfdaf864a66af128effee8ec42d25f1e173c1edf9207979fa52c871757000c591d71a9b6cde40f5001a06", + "0xa79054e8febd0450c96ac7a5fd6bf419c4b17a5926f3bc23a8616f0cfbc2849d97470174cd1baa7c739b12615334b6b7", + "0x81c7391b2a1844ed26a84f054b5f03865b442b7a8d614cd44805b5705fe6a356ac182b66a3c8d415132e389efac5f6b2", + "0x825af1563d0fe53925ec9ac0df65d8211b333474e59359bf1bde8861eecd03f2ac74534d34b7e61031227c2fa7a74e1e", + "0xb60dd9bf036f1825295cd2014ef1f6d520cf729b4d6cee0b42cb871b60ae539b27c83aa3f96ee3d490ec27ce7e915115", + "0x89ca43d5b7f3622b42df7887572297a7f52d5204d85e2e1ac6e5d7aa7f8aaea5e3a07280477d910db025d17cd2e7373b", + "0xb93a2bc9b1b597f0e514fde76ce5bfb6e61eee39cbf1971ea6db38c3ecb055e7913ec8cd07fb0b0ffae3ca345883101c", + "0x8d45546bc30266b20c6c59fc4339eb633155aa58f115a8f976d13789eaae20a95b064fedead247c46665cc13ba856663", + "0xaa8eacfe00e8a4d9815de3f7619d9c420629ada6489933ca66a571bf6c044d08b391e0d9eec7d1cbebe8def1e7523f1e", + "0xb32fefc59a0d0319ccb1946b351ed70445d78d9fbb536fa710d3162b9659f10288f12d82b32ecc026d55f16cbad55441", + "0x99c7c45c34044c056b24e8f57123ba5e2c2c039e9f038a66899362840cffe021733e078866a8708504cdc35816cb335d", + "0x80def162c134540d5ec071b25ccc3eef4efe158be453af41a310b7916c49ec0ce06bb43dfee96b6d77339e11587de448", + "0xb5f2fa4f68f6a26bcb70d8eab62ad73509c08ee7aa622a14b3d16973ffff508ce6f1aff9ced77b8dcfef7319245cf2de", + "0xb4d0436019e779c789464716e1741c189e8945dab7f3072720bd9aa89882fa5b085a1755c48da21541f3cd70a41b0a71", + "0x931e798ef672e1472f4f84c727a101e70d77b3a9f0c0803a5220958d6bbeb8aeeb56c769ab472a3d6451249a13a3f56e", + "0x918c10a84de268aa8f1ba24b38fe55ff907be07b1e86b4a4adbf305c0d705c1cf5f65ce99e03e11676cedc89f1a4f331", + "0x8e55a8413b823715ccd92daee357cedd797e69a0e78b6fcdacb7318646b9903dfe05e5501f47b3c52e74055b9eb619a4", + "0x8b329bb63e6c985d7d072dff4680b3f8b1217ed20543277386bd30ec25240d9dc378837dcd5cf4fd9548658635f4c537", + "0x8c2be5386052b22986b33dbc63c5afacb6d0095495564ba4aa28fc8c880a3c78242fb083248d788ed928deb1e30a82c2", + "0x83a2b7bdfcbd25d6b059f27218e009ecb5ecc4da68ead885e00216411d8222062ca42f21c4d9cfa19c31522080af677b", + "0x9620334d2633e85646b2e2fc48dc6c3f09c64ef1706ed78a3bb6ce1f6b274a727364df71e97531dfdcb392f70f27f536", + "0xb6c84970ec04545121ec3b79376f4e45053c97e8bf2b11922cc2490a429c38735466097ecb81cc9d9692c74d2fb8abc8", + "0x8e55d707dcf265c5ae29a32c27ce66f200fddb724faa5bbf145ef42280ef645fa2f0cc3cfe2db8599b26c83b91e077df", + "0xb910b96b763966402bbebd68a32c15a225ec21e1357fa298478c5981a4310e556103fef0c73bd8903e11c4ed2c065647", + "0xa8fd933a0e9fe8c459809bd93b8ce153e2af55df94b61a1490736b19c89469954da8b72dbd072d798fc06fc3d7a3d60a", + "0x811b279c113828e114fd82c2070caa7eb089a46c8cabf865f9c77354a77ebebe0c4c6400dda0e66dd017cfc44d76851d", + "0x8ed03e91c331afb3ad6e42767e1b3e8d3a35fb831805ff1b5fd3e91878e04027ff5af1165a3ac295f1578faf2c83b581", + "0x95bf53683d64a0621bf1ca6ee17446783f6c535b7a54d6ea57723487a215759a54f886597a55dfdd560424e368ab2759", + "0xa9bea378768fb1d7ba365a16531c51fc1975f1c73caf2a0891da28509805fa84e2a8db7c6ccfbc620e9002317abf174c", + "0xb8308250891015deaf851c4e5a4cf4704d104f94064418488d7e3076d49f36240dcf6fdcf83f45fe8a1d97fb02e3db59", + "0xadcda6b63da21f4074f142f8e7f3a2274f624c733e3a4001054a1809711529c61356aa087f73aed877a58ccb41d38d12", + "0xb80e7869239ae26d1da2e6683f064d1dc93cf4a2b66e9439b3ad9b25324e969bf98014760d29e6b8de7ff152ef498d0f", + "0x8e9bf968911df3bb5e3a7655e9d8143e91ee87f14464d7ba9c86e1e31b03ab31b91eda121281b79cd974d9ed2657e33e", + "0x9007277e8335a43e6bc3c2f5f98c0ba7024a679b7156aeefe964f1a962e5ac82154ac39d1ffbad85a8f2440f3c1e354b", + "0x9422b9d670e997b7c919a429499f38e863c69c6a4d2bb28d85e36ae0895c620f68b71e39eba785e3d39a45be91507757", + "0x926094e01132938000d82dd9a571fef5ef104cd25b4015a25e3442af0329e585aaad5472f0e7a69899ba2d6f734b40aa", + "0x95552d8057f7e32c24d69e4d6c51c98403f198a20c5be8826254d19cab2f84d5758e2220cea7e38b7c8a7a23178fd564", + "0x8abcf8dcc8488bcc9ab23c51b9e7a0d91dfc7bebe88b7ed370ee68eceba643e939c5eae66a4aa5fe85120751780e351c", + "0xa91bf8198f029e6a4cf6f0cc39b629e9aeff1c77b8739e1d5c73d8c1d3fb5c8f6f23e27b435bf10b5b4ec1cf6a7249ed", + "0xb932d87ee3a4b81341511f90fe5aa36c571e8b914f25abcc33dd40ca67a3f6444fe9362c1434744e4af18d6e045c54a3", + "0xa8e960c2be9b1d805d387b3ebe2134d421a65f1fd4c1b4cccdce78f9926f139eea78e3afb449b3d6dd19b5d16ace48fe", + "0xa7e2f57cce509fe66707eaba9b4c042c1be93fd6034a9b51d1d30c45c4363eac79d54663d525c9873ab0eec0b1cc4ed3", + "0xaa162a31c2078f4b080199debf24494a8dfdfb9d8fc85b198a861b12a629c73128c55a883e4c2de3dfed6e0e1b83eeab", + "0xb5a4d075433eaf4115717a84b4dc37f843d44bba0bf820c92ecdedd5afb61be60f7708c8a151a678d9d5c0ae531bffb7", + "0xb56ab96f7a463c0079e05dc766f3a6a31cae5c5044947734ebe0a26e01367c6763cc8de6c2ee2f3b8218f05bef217474", + "0xb60792ac506b901065a8bc0180a86e028fe34b62ceae1ad640c759538ebf3a2ad9c8c927d662deed6f489ff3ff7813c4", + "0x8c8c2cdf075504d12d441a58542e1f8e4bdf92b3ee4775e836b2734c5ec1e3df919b931386417d04489a1dca806c87d2", + "0x8ed78e91e5c4a68894cefc2f7fa71f02e5e12d40f1bb74332139bc7be4d92c24e07d5ece0e82150ed474aa1337af4c18", + "0x87119c22ff8aa31150bde537d863cad661cc5159b12f084cc319224c533f0deb28526ed8568d00a1441e7d8bb4f05673", + "0x83a60ba5a9cccf22cebadf7318b706c9f29abd25db0e2fc1c802965351b53cbf316df72ee3e9b2d3ae7f3c4494cfdff1", + "0xb73b6a9fdd3e7463fbdaabc9a885b7c82201ad867d1bced1c2484300a01cbbb3f1e21afa95d4c7cbb6cb983416b63b90", + "0xb1d89ad16981ff9217708090d4017662d8838f21f3a3296cffe14590b533905fa06a20e40dd497bd291fa4dfd1bfc511", + "0x8abde560083e071a402e3c7bf31930f537f67d2a7bbc734a7480b1b760aa712ebd1cbcb65b00e11e384e980222fe14a9", + "0x89c731d8f31afea8bdc9c32527bdca257f2a840764d40f6e49403b8e75ae51017d505ea4fff91bf28b6f3a1bc65b8bbc", + "0x80e9ac8e077e86ad050ee73dfce268a69564ff1b8419e9c236d981fe7a5f0c2bc756e8603ec604b3b9e36da8fe10a49c", + "0xb4f1eea0f304898b1323c6382732e6f40e556bfc68af9ce73f6d54e92f5f23cc4f78eb3f43d578d81e7627fb40f092b3", + "0xa0e3a8d1348f8f153e08ac4839232d75d1d6e81b5de184ec4724f8213baf98d3fe739a96f6b39d79a053b628c3a09981", + "0xa6915ba0b52ffe4a381bbb8ff3791d9d3b848bf89b3bacbb2a7d2e5ae21f1353cdc304b3cb6e82416f7e604035c27d7e", + "0xb2c4c9cdfdd2fc9a340ba3ade9423344b9f429e8c7e20a8abbf26400376e312f3ae35d1c456be99dfb5c02fc8a36cbfa", + "0x9657d57ca0641825a0aa5687f3f87659d893f33aee819bafa5b1ca1db554811c1c844f971e278606e3a2f096defdc67c", + "0xa4ad24d0a557704ada24d8e27a15604bca28679e260b2c69ccc8e6cae5499866724b700605a90df7dfb35130756939b9", + "0xb18d9ea6682f73a1f99a9a4fc98c38fcda02c1a18e8c5fc080cf935a2ac877dc5223fca273dcde190b906178d0fd05bc", + "0x8ea5fefad0799c885f50ff10d94bd0af5b99b0a446cd1f367ae5ff529cc47e09f3018115f3c0ccac2fa05bb65b84945e", + "0x92450d52e6c7d13ebfcdf5674d6761bbae2fc5aabc865d35d031b588c383e0a64cf69a73dc93948632e2b98f74a5ed86", + "0xa356f171a98df4ec5a96d556eaccc6ad34b4238aafcf0e94ece27cdbb491749fc9692e78b84dfe80bdef2914079d34b5", + "0xb918703a4d3507d266414712ba8eb7ad17da07cc5f952b5c62ef130cc6ed1ae3bf01237fc8848c179725bdddd465b301", + "0xad2b0554570bfc9d97510cf59bc38e10ca54a93649c30ac9919bd0255e43bf525ab11b74f78a51ac0973cd0c5a5dcb54", + "0xa7ecaf4b631d179d32ac1632390d95196a0035e00da6c0e6e13b5c09ae44b15ae6c21538b5a31b73bc5f650ecd979b59", + "0xa37704eb4d728df2a367e59fcb6c26023136230e37f3b8a2f3ceeb1467f5cd30186fc0116f98b64a8146fd2c5903e8d9", + "0xb09373ce92314678299ae10ec1f93c702911beb4115c6b5ba6efbcab9c7afb599f59793912df70a98868bce6545a33dd", + "0xb52a878a1393094fd2b93f2d1eccabf2830ab10800ba4cc24dcc7849cd0978733263aef2fcb766a7cb575a7a99383db8", + "0x8dac097e006fda4fb9d6d7ae52adabd9448ebc8d5bd5b38ac0c4ed38ceb510763174f7adfb0b473c38e52147ccab4239", + "0x86b19c41efb949937d74a7875549ee5e997f9fdac7f7198085afda233cf74341a38d0ca3767c76cd35f875b89a35f78c", + "0x99f0d927e5ad25cd134f1c70b72631cc6b5cb4ddb86c0642b900464e33d971213a5239dddaf71f7a42f2d6d02a12dcc6", + "0x8355c38806c335d747d4e97f0083fb96585677da18b409a85175ec35dc3f74671817b34203eb18c2f729717ce083ede8", + "0xabb3603adb061a036eae0afa5f23d79c3b62442e0e3bcdeef896f88995585c1105cd3065410368456a4d36b5b0485a83", + "0x9051c5c0011784885187d04749f774b9b4f6bc594b0e4e18226de79dedc4d7aefa3529c3d2c728e180f96f3e204d578b", + "0x91888213e7d321d0bfac884edbd5cb756b280753bb5f8bc6acfc208f525757beca24bdf86fc68d3d8736ef176a960b49", + "0x91258bd7ce6e3b7516fe2f5391a368d826da299e0e99b1f82eaa44b62b110ab696adc92debab8ba098a52f38dfb3c5d8", + "0x96e3907340dffa9da3602d3b94bacff7e1bb8649edd3b9bbd06e1bc6781e78f91ababab12c0b9be7c66dfedc7001b66e", + "0x9513555688fcfb12ba63952ab36a67b36affdd71f7b843e8eb99ccbd45421698024608233efbdc905eaeb26b334b33af", + "0x9913ca9bcf11eeb408da02e4317c5ca0010fb2f4490b282ddb758001c08b438c3b35351a8cbe10b7fffc1293ccd22d4b", + "0x85dc2471860ebca88e5a2766161fdd77f926d2a34825d1134a30418f91a741759668e32fd1e37c415d07ab5824338e8a", + "0x8b128917e828a0b5eb6fa8ed72b52fae2dfaf74febee69a2e2f87e8df702f0c5bc0fb620c8d1d2a07f35a15ec9c0f5a8", + "0x964c39e7840c130b01bb481ae7bfc92682b0f124c9c383f9dbf3027f2249151925f4faf36905af476a54778d69da3f48", + "0x80671ece658cf850e522d46d25678f934ce6df043f25f8707235125765d40c2eaaf39eda6092f75039b22cb58bf2c29d", + "0xad4bb0e79fdaa340b1347a46b0f64e801c72a89770dda0a6e4bfd35f2df5146fce9934e4baecb1c2671077c771eb8089", + "0x80b3bd3adc6cf198fcd997f8867d2839a2eb28f57390352ec423b8a14cc1f2ab21c6e286505d6a21fb134dcd8d8f11cf", + "0xa26d46a6b8a75748895a1d599e7fd120d896340e79813167a400b2fe463452532a4cab419074663fe1d29fa716b76a33", + "0x82b1f3a8a1df29207d7ff020809113ab06080a7f0c631f76ad33f47cdfb6a567143144df97b4ed7f676d929195b04bba", + "0xad96633a3744648ff0a2e4491e8219c9c6ba6e655cb058c36320a8f72cd5f72c00bddf97083d07650ea9ddc005fc1ff4", + "0x91d0783788626c91662359dc3ff36a8bcc6831e3f4114f85c99910256b1d8f88a8612f53c7c417d55581dea486f38926", + "0x84edd9e87ff3d193ebb25f43474c33fe502a1e2100fd3f93fda6520f5e42214cc12e9f8045f99aa2423a0ee35e671854", + "0xb55e06a4b1fc3ff9a5520e0b7c8b5ac11b28385cce78d91ce93b82f1bd7f7afdd4195d0c13a76e80d0ed5a4f12325fa7", + "0xb0b15c7ddede2b81d9c835ecaa887650622e75d0d85f81b8bbec7ef24e9a31a9c9e3de1f382d8c76d878d1b01373f6c8", + "0xb1adb47c20f29784116b80f3670182d01b17612d5d91bd6502b0dcecdcf072541f582aafc5e7dd9a765cad52151684f4", + "0x8efd1018df9c9e9814a9c48f68c168551b999914a6719229f0c5bf0f20a288a2f5ba4a48ba966c5bffb0fbd346a4fcc6", + "0xb34ea2bd3269a4ddb2fbf2514401d2712fc46c22642f3557e3b9c7acbce9b454dcf789573ede9aa14f39605fdd03f8c4", + "0xa9e1428ce24eacfc460aec2e787c053327ba612f50d93510d58b2cb0f13291ca3d16358325ab3e86693fe686e4f526f7", + "0x91eac7361af4c66f725c153da665a3c55aca9ae73ead84ca2662cf736fe6a348a301be1954723206dda4a2120202954b", + "0xa6f02db89739c686407825fa7e84000ceedb9bd943e8a0908fef6f0d35dbc33c336072ba65e33e15ecfcd5714d01c2f0", + "0xa25666faa12e843a80365c0fef7d328a480c6e3cb7f224763c11d8cbabd0e7e91a5b647585ee905cc036afca14842bae", + "0xb4348576439cd2e48c01cb9cded7cc4a0ea364ab936dd679ddc7d58b48807e7fab070f2f1ea88595b11af4500849026a", + "0xa8c6c731e0d0464ef7e4fc1b049065eb4ce100c01e1a376365c636a0b23851022bf55805963bc15eb57434a837e81167", + "0xb0952937b154e3a4c206f96cd96c76ba37624956b0e4d43470bdd97b4af878326b589e3eaee82fc192437123096799a2", + "0x97d07ec31ecc9923192e48d37df2cf08750050fb452dcfbdb350fbc43e146bae3590c5b732b31ebfa1ce5d884ad5ad57", + "0xa69359aebbfe4cbc4d39d178150039fbf284cbc0edc68a6bd635ee3a1c76569a4a575c907fff691b2a4d82a384c2945f", + "0xb321c2c0f6b5902ee9056cce7404d858da9a573d27348c1a6bfea29b2746f2aee7abcb6192504e5a583b0caeaba117d7", + "0xa74e738aa6eb4eea58855ae6f422af22812fb388c83aacca5bd5fa4a88d4c01463174a229aea2830c348dd9ab9307854", + "0x94306a3b106bc1644346bc45c05cdc8287811d5c86cad691bde0c65d6a686eb9c0ce79ad91baa4547e5d058ae8bf7310", + "0xb64140fd77a07633e4ca8d60786452311dcdb8ce7095ba51dad8486f57c3bf4e69bced92603f71da992a48ad817ab275", + "0xaffe7f4310f1dc68e5e3cd640bedf864f51bfb46bb752063bfc18e95930021f784e509261ff9c560f53000c361b142d1", + "0xb0d2fee222c6f963ba3385547f921a48964da031d737892604f8f2677d4905dbf615046db57eae6c6dd756709ae6932a", + "0x81700c66aad7c2e51168e028b0fe086dea75d3b17d93a4dc1f47a6a0f025df0bae1c8c997901837ad859a84197e7bb00", + "0xaa4ac5fdd602f8b79cace18690e67bad557a93d00c0e295074185e8c6b4059a65495d9971685de2fc01d2171ac8b706a", + "0xa8becb3a64fdf35d65d2857898dcf8053b5057a73ab8c5bb5324af1a8015cff47efb85dc3eae7364cd5c850b7962bedf", + "0xb72ea09bd0b72f8cde3466f359ea69b194ede93dced534efba1b9ebc6f3bd53942fe2965e992e82edb6050cac4ed88dd", + "0x85bb8dd7eef023a251fb6f220af54687747f4c91983ff728163c4618ffac40ee6edc29a0aa6d455276bbe017f63757c2", + "0x85a485254a11b4c4a943d9ec509c0dd1cbfc0ff5273a00cf5c9f0babec973efb15348e5d9451b548293d778e3a2b62a5", + "0xb109f3ac809391e772b589c196b013db69a9b2b10ac3898feb70b986973731f30722b573cd0c9324158ec20416825385", + "0x8a4eb579a840d438bed008644f373ea9ba2f28470d50cf1d70af38ba0e17326c948527b1719dd1bd9ac656ebd5aedd10", + "0xa52e9d66ead5ee1e02ce6108e4ded790d8ec83164a0fa275ab1f89a32200726c8e988d66df131df9e62dd80203c13dce", + "0xb541cee9febf15d252475507e11d65c4b7819c26cf6d90352f5e8a8f5c63e254eddf22df0c35a7be5b244233e8e4ee5e", + "0x8153c297772adf4603c39349142f98cc15baeccaeae10c3230ee87d62255f6814d88d6ed208c368d2c02332426589748", + "0x970dc9782f1828474e9fab7dcdec19aa106725465a5844caed948eef5c9e48199c1b6bc1a637ed7864116927e84bc65a", + "0xa975a920624967f4ecc77ea5d9869c434caa64c330024194615a8d0640c5d4d4fb139ea11a0c73a5c6ae6dd3fbf0ab5d", + "0x811f0f9e0c12acfb4b9dca359eaef3bed18083bad96188befc036ad3143b121fff4777ca6dc70a835bbc4921bd25f5ff", + "0x82341c6ebdb97c8b72910da95c7eebccd1308b6a92999886aab552f0642882d5c7cc60931577d200efd6066530c998dd", + "0x860f7162c2f5fd1c0953c6ce75bd8c52eaa48032b914410681b8cc05e00b64130d1f96ec5a52df66a04c78a9f9f42981", + "0x8a578e674875571fe1a0459843495a5ee1d9fb6cd684b244feb9488f999a46f43363938cd0542879ea18ed14fba10a6e", + "0x8df217aba4da6781f0f5139aced472025523ed6e17e504511c04b677ca8197488e237d8bb5dff7b6b3898cd5a6393dd5", + "0xb2c9230ad35d7b471d3aee6f771517cf3145ad26200bd6fe9c7cf28120e2945fed402e212d2330a692f97bb9ac4dcf12", + "0xb78b89e29e8b782603b222cc8724eeb83b2d9d56bc02f59a3c899ab76429dc721358b07dcdaf422f59520b7e7ab4fb55", + "0x82682a5617843c4ac8d4efb4c3ce715c76c1da2c3bab1ede387db503f3489c1bfdfc07d9231d96f955df84fd225bc81b", + "0xb0f53725cc610e78b8e8a4e6823a2ffe44dd15a9a5bc8151ab7a3787ddd97e1d7f2f0e6efd2876e5f96417157143e3bf", + "0x92c5a93233085e2b244519078770c7192af62f3562113abc8902f9d72591eacf52bd15ce78653ab9170d5067606287f8", + "0xa43ef97dcd9b6ad288846bf31fccf78df72f94bc7ad768baf5bf0d5dfa27bd74ffcc6b6c6ed1d1f09e09be3afa5eaedf", + "0x817d43bd684a261fb30f709f7926cc4e1a31fd3a1a5e7e53ba4d664856827b340d7867e23d55617ab3514c8a26a7040d", + "0xa599e22d3286b32fafaaf79bd5b0c5b72f6bf266ec68948478f055391336d756b58f9afea0167b961fd94234989f0f02", + "0xb70db7d8e8356df2e2070f8d658e560081442f3f3b95e20f4bf30106835d76161101163659d5d12cc0f335fb042dc66e", + "0xb8f725b70c957aa3cd6b4bef0d9647393f7c9e0b7343e92439372f0e9aa3ceddd0cb9c30be331742b87c53f2eb030593", + "0xb2fb5e7762f26036e7e966f4454f886758804d1f4c2da17f3d13b0b67ca337f1fd89fd3cc798b07da6e05e8582c9537b", + "0xa377f944dccc300921e238ed67989872338137fe57f04cb5a913c787842e08b8a1adcfb4d2200abdc911fc1c766a7092", + "0xb82e98a606071c2a33f2ad44e7ace6d9471d5434500de8307b5d4e0083e3a5cbc67f0609ca8055f0ea0ee7501b9ed916", + "0x8e58f9a04d33a41ace4944615041662dc35057e645f63e127cf0d70f96ac307d33a62ce98f164d6eed8536c1a747dcbe", + "0xb5b11388071ffbf57ac47fc195736613b964ebb91cc8e2c17b32646f91d64ea506282b881897fca96c317364d3290de2", + "0xa40ee9b7551133856cfb3904837f9949a9558e59a418898affb78adf1500fd6ef6328fc4422161909aea2c79ad08c14b", + "0x81f9eb4ef28aacdb43e11dfc9aa92ba990be4d3c14b484fa677edad3a3fbfeaa859a7f9322b5e95818240d7326215abf", + "0x84939b2b6bc859437d1a7a8d6ec9a357c6b716c4b4cc22abc274af872655940cfc72c99f5d0283d90e05191fcdb1c232", + "0xb78a5b74a90a805410b6225fb9576d6d73752520f25cc3fd1edf8ea9f6559d3080f9acaa2246809b6a66879cd2ae446b", + "0x8d0a92baa88bf38dce5385ccf15d345b28e2e5d0a2d469e689353d80eaed8e8408933816d70ad752f226c59a0d5b5f0c", + "0xa7e15f8a8c1655b7b346c9488cff278c793505379b781b31b273b4bf09b3bdfca1c8ab2334746075d636b2e05859f215", + "0xb70daf14f2adce03c7b92d6aa181f0c507a80a37493d8dd12419d5ed5f943a98099fefb46ac827d6e4efb9b8233c99d6", + "0x8c2480814661744d116fba7355bc6b1914975e44cf0e976d50b6a20092bb1c636b7b44ed3fe8d63b5555ffc89fa759d6", + "0xa6059528a4fed36abb74ab992b22a4f9bf1d05c5de2bfe6837b9af1adfed98bc37ed7481b5a99675d432743021fcfdb3", + "0xb7e19f1b25bc159e5a769811e773c3a8ffe8be8ac77ed0b711540915e5c6e7bafdb407cf9b85c551f67fd621ce8142a5", + "0xa2f66d4f7d16ed3e7ef5fc90b42676c61a98ff18bd26ccce91de03b6a0130c1db17a6bc57be135e410a76d2255b15813", + "0xa139c916927dc3d3fb83598da9217ca64f0ae127215332e9a7ed82be923b89a801c44580d5617297175f9dafb1c4eaf3", + "0xaf08e1e1b04ec95366a12d99c80a9a9ac40ac984a575dd0230cdf4eb346a7686da55ef0a276f3356f814af31f9cbf1aa", + "0x98840aefe287369221c0721cd7c1b15b1d670c3cbbfda191cdb5434bcad757e59c30ec82b2d8c75947405888d44da435", + "0xb7c61c8d42daf2e278a12d8f6eed76090b71c82275f8b33504aba75d95103840e8acd083e97a5a5aa79897876a68940d", + "0xa0264048d2a2061d32eee4f661957ff351e78436bf49ef973c059612874ce9c91970869d011dc13a5b7c754476880a68", + "0x897199a4d8db8aa2db5d9be3d4f4312e41fa0739eb06c62e2e046c4b9be829a447e5d47227e2d96195d3b7b66eb59da6", + "0xb512a9082881f5dc90b02f8bc4f38b133348c2e933813852f6a8e7d8c270c9ce68a5524af7d1d3123e53b2d02a53d465", + "0x80b332469254a96f53c95ec79bb5a8bb1c387d40e58b73d72f84384c696ba0d3c81d6ac90be2979c364c44294e90432e", + "0xab680c2e547ea5cbf95bf813020beb461d50ee4341dea944eb48f6a8584d35682d20186e3b190b849a1ba25625a7f499", + "0x9070581993a0531d6be372d370c2e4ab2ee53f30e04a75ae61ea0fc2c320914506c4d2d4b4487c1f8fa88356fc45c895", + "0x8424303dad6b4051ab633ad27ee51783b2ead61c5a6dae1eb3ed72fc1f36e2a9b1f315504a4bd90f9664091f2f403d4c", + "0x82225611eee626556553b9316dab4043aff241a81826a33aebd9864a91e299b765ba1fb43eea2c2047e6b75b6d7fe3de", + "0x8a3fb221c616ad55c352dd5e0c09ee892022013d6965aef40d4f277a42e9fa01226fe973cb99aaf6ffe4f4f348fb54d1", + "0xb07c07679aa51713e8a7d7bc304dc15ed5664b66bd371877023f3b110b3927e09e259ef22895c4001421a69c6c013cc6", + "0x83556c76bdac0dd8db6da231b863c335be076e7299802eebc259e0818c369f933a4a4b18e2df8ca07e82f60767b462e0", + "0xa516f659b7915d2f7cd0f0f5ea2491b15f0c84dcb191e7671b28adf7cf14a56d42cfc0da94b3c269b45c535f6eeded49", + "0x80d7cc6f26066f753041b17ff1bd27f6d4b5603a43729d33d596e21a67356db84ca9710158089def425f6afaf3207f9e", + "0xb802a47f9009dbd48851209ea1e2739020e717f0ae80671d9f97a0e43de923273f66b7fcc136a064c8467372a5b02d28", + "0xac92fec1864a8a911633f377df87aab56713876316d48240fefeee49ab97f7406c22e70f4938b5912c5c4e766146b7a5", + "0x89224225b9835d04428b0a74edbff53dee2be285ddd1e5a3a8c37307c0500578155f0c4052e4bc8be04c56862fac099d", + "0xb1d3c8492fbf22ea60732745edd3b0163ba5a20d1a3315e3773f2540ee38cf308d42ec72cbb3e3dcea457d1d132c3904", + "0x8bd00e38ec30ee6c44a0e5b222f1f737c9ed2a4bb9225f1741d6334df966318c8a0fd2fbb109557fe8c9479694b8d8dc", + "0xa930ce5454efc0b247dc148aff869963fc5c240241d5590415cbd36634801a04d3873d93635911bb9c0c42ecb005cc63", + "0xb83d4f80e9e0fa47b42175df74935ba8aad2e559b80e84478ab1685bc3eb65d51b93e5738d5ca968cc055ca0c552a03c", + "0xb3ae21258f98051f13af3878b8103bc541fe6f20b1c3f8fb4689ddb8800b3c25cca9b55f0a4104bdf15dc4d5844abb8c", + "0x831ef8684c1cd446c58c59d0152aeade5cc305bca6aa296b92162615f052ba280fe289edd62fda6d9f0667c186445f52", + "0x97bf9659b14f133885916733b7d4ac7e215495953caba970fa259f7bf6b79e661090ec8d79e1c9ce8dfb17e8552f93af", + "0x84d5a89cc2332baaaf3d19627a65f4b107f8dd9228a1434b327732f59883bb54fb8ce60d6acd026ed4b0e94e545d1c33", + "0x8e66cb743f95ca5486400b0d89d02e20b98044be1e3a12983ff9fe086179e5a0ebf4dcd5098703191552e9aa660a6de5", + "0x87b4cfb35bacec805f8148786788db84eb8f4bcecdd0570ecb592c705450ce1a90b6d183d37ef58780ede3995be67497", + "0xa72a4fece5478011973afa543f6d8a8ea06a64b241cf7d8bd81fa3740ac2a4cf10e5120abcc1c1101f94da89507a40ca", + "0x89dc6001a96adcd2679916f43dd19ea00508c8d5dd6b0090eab7982fd2f3571b62f3029588a0649e73f49124525407ea", + "0x8ca75edf1259599e873530eff6151c822a4018e71a340534219ef8641cb6683215891df41d4e3c0ca2560e57a7aa913e", + "0x9282d32f868e5ee6f7fc229dda5b94b603476de30cec0a44a30edf396b52dc0ebd472b8f726d4b67d76179fecc1666a1", + "0xafa24704223707db89690bcf9761f07a093f6009ca9fc945e0a8801fc29f9f51292bf95243e466fe736088af36c55ca6", + "0xb51332508ddd9a2610edd2b0ad120272ca342e96c28baae37a2c4f07e689303a46c237712d07e446b1d67c75aa8ce32f", + "0x9219249f3799dfa4eb4770ee323f821e559e7406bb11b1f1889286221b22c8b40ccacbd9ac50ea3fa9ed754860bc24f0", + "0x993515270c128ede64fe6f06755259105d0ec74947b7eb05924a375fa5c6d14822f3d7d41dd04fa5df8aa2aa205a1dec", + "0xa83be4c2511bae430034ab15b194ac719d7b7041f9c0e321317f513a97db39e97b9ee1df92a1962f265b7a3e98cdd753", + "0x8ac7feaecd26f7b99fda3ed0b8a08bd6dd33ed5ba687c913ec0ffc64bbbefcda6f265072add4d944f2005634601ce68b", + "0xb4e3ac6b09299db9e1a469f3a0b2d8d724ee47a417a517bebc4c2ac3efc5cde086b57b9aa4efccdef2bcf8f456d973f6", + "0x9262a24a84fb7b2a84d700f98dcf3fefab8b47293778c20bfc356860cb84e0bf102bae9facd9986d92d1762e0a955836", + "0x97be2041c42bd25e5eb519279163b0857f8bef627492c27b1182f8bf0033769246be5886422cbd2409c08a2615352465", + "0xb0b87d059a00e3effa2e5e4925da913b245785f2932ac3ed364ad19a064d3561b8aa6afea22c951316074f0df179af36", + "0x891644b7b3321b06a2a40cd96c2b8b29d81cde5b48546483fdda439000982a9cbf1f6333fb6c089d39da6492cdfaefe9", + "0x8da9149b7f4783a24240b7b9c7e6df4abf8d699d3834e31ee591489bf4744141ab199c173db64397c1f9bd5f9c862ca1", + "0x8ad7f9fb2742654aa2964fd468e7645436cefd1308b064fd63fdf0d3adb4caf6cfe5426354f6cc284f208b03d6b2d918", + "0x8435e4668f7aeb027100d21e4e0b6ee22b401d21966a3736b95610de86c7e2f2c9ee5d0f901353675eee5ff458dad69e", + "0x9010895f045538bd11b47bb8996f27198c8d6cffd3220569e6b7407f68f35c47d1efdbcecbf9b5e241c3c2879a4f6936", + "0x92a9aa443b5ee7bf13b6f43f2d8d8db7f6f33fd4073a606ec5772421a55f464831419726130dd97829a7d4bfeb1ab078", + "0x843f3266560be6dcbe0258c3c7d7e332330e10630c069892954290288eda301e247f479505a8a1bf7e59c99ccafd104f", + "0x915bd1dad808f8a568725bd243f80b5476a2999d0ef60ea3ef6e754155bc4121b2b879d01570725b510c5a3f09cd83ef", + "0x97250d781815b1825be192714884630e9f564b9bd737d55b8ac79ab48d0fb3ca53bd21ead7b2fa82a05f24083f25645d", + "0x81e2d52333391ff2faab39611689a62d6ead77039e8703f4e012d53eea17a4d46f2e3342e44b6edbe73a542b461bda45", + "0x89c9f9fd5f638156b018831c1bb70c91215f4a2f5a73c84b1208bdf6ad652a55df7213336ce12bd910a0e1a726474f95", + "0x92bd02984d090ea7e2f3eb7d36d1e7b9d731b6b047e3cdd4af7cc4ee177415fea7a145205e484b366d84191f06af85c9", + "0x85a86fc61d5d916ccbb219db52953e1495230aaaca63237e9165276405f07ad9644e253ae394f1ccdd231944e7143313", + "0xa2ca5b3fbc9f3530f88c0ed7071ec3d89b272174c366eedb5d15d2b648c65d23c0faa4e92c776357e7c6883a0084d03c", + "0xad171f5badcc99c8ffc9d8b707d792046f86cd0aa478e0e2fbb32fe095f96cd134ca548d1f7713057694dc6b26465315", + "0x96bd15d57da9980870fbadc98c68db76824407dff2700c45b859bb70d98374d4a4ba99e3ed0b0c17f480fe08f16c6b8a", + "0x8300bac69ca088c3ff35749b437215e9e35a16393e9dc094f520516ba57a485def7029d30adfc72bca36eeb285c19301", + "0x8a09e20be64f346668fcc7b07fee9c0ea8094c935cbf4f3a4cdbb613d4b936c1edb9256b7c884efb72393d97c0da00e1", + "0xb1f85827ee6f041f93ab174d847a55710824fa131c9ade9561168c3962a25c617475ebc4105eba6e738961a754442bc8", + "0xa131558f92e215969f41b6a57d1e2f424149eea531723821dd4cf8c54325cbe66b002de2c8287de6b41ab4b5c35f060a", + "0x81ba492b8956f73557f361a856c6c884ebb300d828287d5699e22e0cfa75c8e77a61616551d0be5178263898c461d6f7", + "0xb2608f44d3c22fac8e13cb59e4ade8b9a98c4eb1ec0959ea400c97eb937ae3f66837e91917057148befade8389af2f6a", + "0xa6ff0323b5a18a4becb2cc6b376086b47cb2baffbfd1b0f2229ef2286fb4a34c5cd83a5faed5def7bbad519fcab8a856", + "0x857d879cb9eff22501d883071382832730704bfcc5cd5b07cdce7ab8dc41c565a1eb0e7e4befce8e0e03a4975d3f11ef", + "0xa2879a20c0360c516811c490289be7dfbf7dbd41d2f172c9239f99e3d091957e0446854f9d0f753d90384a80feb6fa56", + "0x83518624f33f19f87096a47d7b8e5f2d019b927e935a9021823fac6564c4f2328dcb172e25bb052748191e75ac682bd0", + "0x817ec79132faa4e2950665712b2c503d7fb542aa57b7b36e324f77cda79f8b77bde12314e2df65c5b5296a6bca9bb0b4", + "0xb2abf8fb7c3690816fa133d5b4aa509cd5a6e3257cfeb7513d1408b12371c4d58c44d123ac07360be0d0dd378e5bcf99", + "0xa9fe1e4fb1574c1affac5560939face1af6657f5d6abce08d32fc9d98ef03186dbb2dbb9fd1decd6d8f4e4687afecce9", + "0x89b2f41e51f33c3ca3e44b692e8a6681eb42a7f90b81c9e0a0bc538341df9e2039ee61f26d2ebe9e68df5ed1bccf8cdf", + "0x8b35aa7b1d9e2135b35a1d801f6c9f47c08a80e48603f3850b425f64e7fb9860d1adda04f92a1ba22d00dd0a26e781ca", + "0x960574978cadedbd4cd9f764bee92f94e08b7af65403de36b21bffc9424bcee845b3b028af2e9e545dd77cf1e69a6a7d", + "0x840aa0f34b5b6c39471f54d9e85f1eb946468c4fc01963a9027cd7864df01f73c2e864f1f07aeed4b1b1af72808dfa07", + "0x834464a84a11200e3c60f816044c254a7d9baed64aed45a17325cef7fd62338e0a26da78d199d30ac3411714dc813223", + "0xb4ac6fe2f5059546f4ad9a361426ead33237b6b9030b129bf0122085c85fe4ccb33cf90f5a7f23c5b708a5ac64b487f6", + "0xa12aa9035464795f2a67f3eaba478d5ebc838ed9e997c7dfa241e1ed60a94b367d3f969ccf0ef02028c35215698b309f", + "0xac8d926492ec2bb68c6d8aa9bce49085d3d266f3d5f1f924032b87c42b44e41da7c047eeb01e4618f9d0f123dcaa537d", + "0xa5142425825d813ed8ce1849d81aa40b11f1cc3daa89a9f798dd83065c74820b4da6122b3308f528b074531df66e1a5e", + "0x87ff55c9f5aae079e7bf24084dd9c6b3bc260727d942d79cbe8dc13341d98525b4ece3ed8169994b56a387642f09134a", + "0x88e680f148ef2ecdcfed33b61f9e0224790fddc9069bd6999e9bede1791e761637c0fd60b52990b6c93e6e5429e483ce", + "0x94bc20bf5aac6e9f1060d02eacd06c42aeac9a1c5635b15a83985dfb03938ddb4999a822e865635201489c7f75601b29", + "0x849221cab7599f25f0b114df092bd5e8c2430503ae959bef1543a101de0790a78245db6a145e26f40b5f9bcf533219a3", + "0x88b6f2c2e7a7954fad11009d839ce50780921f80292320868d481e38d26aecd80fa607e82219a99532d88cf33b39f562", + "0xb0d82947dc23c0b88b86c321b582c15decdb825ed909a731b42d46bc895009515a3dc646c98dbec7d71b0722df82392e", + "0xa2cfb9f7c1a76c8073363c1c3bebe5dc29fa76533caea41046c51ea9bbdc693a121b957cd96be5b6da18704d1865cff7", + "0x8f0ffab9a83355a22683a9d998d1c1089449eb308711eaad4265f05927ec6d0d1ca39217082a0b372e02234e78dbaaad", + "0xab024661e2b2937ad374c8cf2e3669f1dc55558a3a881e9ec4d461f27e0fa92e2bc88230f038bfb051cf2145ca747a07", + "0xb98d9b9ec9eefa56d38cca959ce1aee7b6d4b41a8dbbd34b3f50c0a5f97f84ed2502ded1ce8cdb5895872360d4ba6d61", + "0x851244158b3184a62d2c98d148e2b1102cf0d5500906bbc2deda95acc5e3bc4b4a3344febbb31ce05a56dfee86a74913", + "0x860d9e2cb886bd3620b5d7499d14b415532482569bd45fd76e3e8052d78a73ae4b2b41f139f9cfb136564108cd93c0f3", + "0x8305a052a0fb2bcd41f3aca075c5f7f233bd8f861451d03f3a6e6e31f7d08dd89fe1eb4dd7b238a78b12ddceaad9768c", + "0xadb703e4778c7e14fb83541ab00b5fc344108243ec6827c5d9b302ee68321aa569da1718424e6a57979ab7536d5eb43b", + "0xb1a754b87b9e21aeb86217ec5b4fadb7535344567f1bd15e88ec12a833fed68e26bfbe03b7709ce24ba6c925ea0a0e07", + "0x8c1e2f6bf820e1653f3b8213e9d959d8649196223c2aab57b7ebda094f4919f88d883bcc6a0cd0be335f26f5a2a9c962", + "0xa082deb9865fe8668e91db0e4fd7fb50fb3fdae3e7bf1217ce0aa6f286a624624cf936d762bb2b6c3fead6826694f846", + "0xa10540ca05fbcccdd0a2a66aabab3b36e9bb525794cbae68bc3dace6116f58942218e9d5e9af10d67b5f6fb6c774fdd4", + "0xb81d22c4ab0ccaf447cc5fc2ff3bd21746617e6773bf43257c0d80331be2e8437b88c9c45309ee46402b38d3d4911caf", + "0x84c7c6e924713cab3b149f641dabf63ad5abbc17c1d8ee7802a6630507aa1137f7e034ba1d12ec13f1e31efbab79bf13", + "0x8773b9d236e5fcfa8c32e471b555264692006bf9a869a3c327aed33da22dfbf5780ecea7158904d4d6ac4acfe9789388", + "0xa4c2c1bb7290eb7af2013f7dde78282148593f066b09faf42e61a3fcf81297caa5a00fdbf6b93609c8c5782a0f25341a", + "0xa7bfa6e3f273da3dcfac7cb9906bbe9fa4fc2872b184d79813ee273e6cc4d7f37f46164362707a1976f5b6a2c5d7ed1a", + "0x8b71502019e4263fcda354a0fd10aaa7da47f4abb7a0c715c7b017e9eea14f2b64009b29b467394668c7ca995adedf82", + "0xad7460fba7deccc3f9a7d204233de47ce30ffa55e1e164975cdf06480a6108720bc397b93ca8c959df77d44a1e1f05f4", + "0xa5b8df96ccb7b078a3918e74b1b10da21df982538d2c9313f5129b2797c8a6db9ff8707241ff72d3e9d5983397321736", + "0xaa6cfa6386660c01879656da6c4e72497690708bae6c5cd1d088f443cb5bbbe75561d6eec256a72b9728377eb83ef973", + "0xb9699ce7c5c878e44114ab7a598646c6c7616b8e08a9ef8ec291189ef9945c1a538d2abf1ce3b0da0f8eecb303b81b43", + "0xb8d0fd1d278f53c455de92ec4357885fc6648dc5f276930263da7dc885b4a9628a2113e28b66b1e64fd08189427c614f", + "0x84ad8d262f6ef5d93e82ff6f4af995148eedf6d8e079124daee9b99f506e2968922eac2c7d4aea741fceb7733f20b2d2", + "0xab5e30ab54641e3a44450118b8235554e0fcfffdfbe1430ceb3f7ef33325725741995fbbbb0c16f0875aef0f1e0c98ec", + "0x80e2cf8bf386ebda46045852751611f2af80eca2e910d9ec5f6e2c7376611534604ceafa639272b3d503b02bd66525a6", + "0xaaac69af8fbb87da1c1b7c1b9e59942887ae839a91f0c1d191c40fe8163d7f1dbe984e4fd33619c73e63abfa7058f1e3", + "0xa6194224ad838ab86e84dc80e9b8abb121ae6c3c7fddc476463d81f14168131e429a9757e18219b3896a667edda2c751", + "0xb68f36aa57aedc7d65752b74761e49127afa65466005a42556230dd608ecc8f5efdb2ce90bb445a8466e1fc780eea8c3", + "0x886c3fa235d6977822846b3d6eccb77f1e2cd8ba3dc04780666cf070cae208b7513dc4525d19a3fb6385cb55f5048e2a", + "0xa9801273ef850b99eb28f3dee84ba4c4017c95398730c447efe8c1146b0719f252709d3397ce60509e05da74ed0f373f", + "0xa58c2a5dd13e08ffa26a6c5e5eb18bd8f761ab64a711e928e6101512401ef2b1c41f67ba6d0823e16e89395d6b03ebb7", + "0x91318b564ec8b2d8c347ca827d4d3a060272aec585e1acd693b2bafa750565c72fec6a52c73bb3ae964fdaa479700532", + "0xa058db5d76f329c7e6873e80c7b6a088974522390ccaf171896066f0476742fd87a12fe9606c20d80920786a88d42cec", + "0x9838e07f9ed8b3fbca701be0ef32a3f90752bbe325aca4eaea5150d99eb2243332745c9e544fd1bb17e7e917202edab9", + "0x85a9ae7dd354f36e73baa5ecf8465d03f0c53b24caf510036b3e796e4764a2bc17f0373013af5b9f1b8973226eb58cd1", + "0x896a4ff4508d069a7da6ef7bed66e1080991daee8b227f3c959b4f47feaf75fd1b9e03d0917b247c2db11e105395d685", + "0xa36d9a6a037bf498dfc0e535f2034e6cd433c7b52e520469811eb2e9f04499a6ce40257d2905300df7d81f38d1bba075", + "0x97aac3c5492aca879b4c06db1834b30b8850a244d29296046a84c637d9580c8521ab4752ef814c96f255a139660d7639", + "0x8552bf592a84ab4b356d01643c90347377ebf1f2b38a8c2e55a3f34537b8c7dcbd62e6776d6c2114f2bc2d4344d1567c", + "0x84474ad163db8e590943ccd1dc50b4f444beb8275919b33f53d42cba89831e9d42ce2de52b26f4412e2a0676ce913277", + "0x900799dfaf5eafeb297c7b4f892438bf2a65ce04034d66f8e5cc3836e4eaffe782fba4f4455a0fcab49102a240d1780e", + "0x817176415e35ad4a204b9fd5771bae6cc270f6ff050996cec89efbe461b2940ae5dd3c6c7d7e31b1da5285b207efed27", + "0x965e5791c927d47569bc54ec9b4c5305788aecd87a26e402aabeaeccc03480df46f0586ca2e2a9918885cd03332af166", + "0xb96d9ada4b5a04a94807d71726bd557de94fbd44042d7dba40560eebe8658d1da49eba54499360619f3b2c38e8b5ed6a", + "0xa07b6d641a43e02e7868f30db4dd5069a2f221b4f122ce9b11eac04abadc4f25f3207f1d2d86c7935b1a3d9992ea9814", + "0x8250d4d8ccac846a4b1a9fa392d9279b5bf2283c8b95d8164c3c0d199fec8849eab85755f2a2a99d584a0407742e3200", + "0x8324cf49f56fc14162f9a9ebda1ebda0388d09d8688f1938aef7dbf9505fc119069efc552f68cc7cd9213f96fda2c6de", + "0xa98e6f1e85268dccbe3bf4e92c9f455c58dcb53de1dba3b78589adf2e50e79f8e245f956e0d098eb46f5d3746826c6dd", + "0xb103ec12f266b4153d67b54d8fc079357ee342cbe5008adc3e0689a7f788534c4601e60e939731f49e4a1e24fd589f82", + "0xb2d7681e866420413cc98eae67614d383943e3762d5742cb3c57e26157633c20880eea1209feaf68402d5d33dd699708", + "0x99fed0ae4112ec9ed74baac70d202a885aa51cb555a3886b49016744dd4017640dd5dd564998c4d842a9f38f3e004e68", + "0x95c35401314467219c8bfb1ccd1f1eae6ef4fa9e48fbea14f70d5315e67b16c46cd03554471840e4a5030b077d2a3856", + "0x8d029380e0c294400d6b8673a23aed43697cb6460fc1bcf217aca3b47cf240886644ed09521d6a05f6abf56f99722d84", + "0x8ef54d1dc0b84575d3a01ecba8a249739edfd25513714dd4d1941fbde99dbbc392f7eb9fb96690d7052609af23aa57f7", + "0xb8ad2b7af4812417aa8de8f33a26547f84bb84f39501d4b7c484cc8bb54c7e166c849b95240fbe459a4719a6e3bf1651", + "0x9858545de898721d19930d8b360cacc5ce262c8e004867a050f849f7a2f2aba968c28d51f24a9af56aaba23a9ded4349", + "0x94ea5043b70df1db63f9b66b4f9d8082776f721b559f27d37b45e0a84faf47f948d7c4532dfd854a4bac49fb2ec8e69e", + "0xa2fd88d7b15e3c2778f6c74470d0f9e1a1f979a4d58bd205361eacadab9973d585a6508e685e640b272d6f8a448eae05", + "0x88defd6bccd55db8ca84e3c8d0fc55a3456b41788f1e209d0aec19c9c70febebf3ae32cacaa1dbbf796d7ddea4b17995", + "0x88b8cde2449d5ee7de2ee2f32e845d27e171a51ef64f1d3d8a5fd7dbb9f898ea70eb7f6410cddfd7b7ae70ea8073cc2e", + "0x8e044fff6ec557824866ac76301b6d93ed19b7177aa6baa95046330f5d69b572b59200e3653cf2f2b559455e782e8960", + "0xb5446b4d6741c824885790d2d26258729dc0ba2f469c85a47d38886d933b785a4f38a951d37f3ef4bd5091c03fa3a071", + "0x956c8afa8056e9a71ab2e8be5241ddbb3a8b3cff2110cb0e7389493d9fa45e6c4b769ebef540a952db6dcd8bd55baf64", + "0x925950cae25615246e29d594ebf34fa7d52f78a9867338648158f2131e6eb4dc17e18f9db8a5fdd76d017b3a9798b3a7", + "0xa17ea4b43211ba990270c21562690b3ef154a46c3d669c4674c80bd424cdfa95d8850c8e882b8d06504f929cba3d93af", + "0xb315ec723973a138508afc387ef651fd8a8804f93975fc36c2eeb796a304eeb1508518d8703e666a74d14318253f526f", + "0xa995742d7433b3f230e622de23cb2d81cac76de54831491cc29768eb4a56da60a5cbd573e1da81fddc359b489a98f85c", + "0xadb2e89f0d15294d7118fc06d4fdbd9c51d3ecbcc23c69797e5b8197eea0d6cd1240910cf22fcab4ef1e2dc2dd99da91", + "0xb5ec9f9fcd0b5d176b643df989bb4c4c1c167112373d662fb414875662d1a93160dc0b5cdf540e8a30e5fcbe6cfbbd49", + "0xb1291b53f90aed275df8b540c74a1f9c6f582e16c5df9f5393a453a3e95624ab7552e93d6e2999784e164046e92ef219", + "0x8bc7b7b1a584a12d5ae63d0bbe4dc1b63c9df9c89bdd1095ff4b8e7c822bf8c1994c92310a3644033c7c9689f4b7d2b0", + "0xad7fc45506a10ca48f991714ecc055cea376c0cbe667f3b40ee8dad8446218835439ae59bccc474cf47b053748ceba6d", + "0xb134756828a5f5725c0b95109e09ca450e3834b127163a0aeeb544e63cc0cdcdf66f8ed98c331c7c98758f46af369a84", + "0x94535bf1636be0974b112fcec480ed8eafc529933f3065c40e417e608e43a392206cfde8bb5a87b720263446c90de663", + "0xa4df4f6efbc3701000fb072e5cbed2754b9ef5618386c51ff12f95d281d1b700fea81fc1365f4afc66a7c83bd0228fbf", + "0xb0336b3552b721087c7e2194976a9119aee13ebed9f1c3c494353707fffde52d004a712965f460062ec9443620716302", + "0x99a39d1d1ee4283b75fa8c1fa42b6a3836b734be48bdd48050f9b05e48db6354fef509623c6ec8d447d630a9b3352b77", + "0x8e3dc3583d40956f9e784e8bbd0b5e65671d2ff2a7c387b20fcb7da9b969f2d122aaf7f054d450dc611737604548c03a", + "0xb5068ec5b7bcb5d8583d51cb25345990f50d1f7b82fe535a6a6b17756355885047916f466ea3ab09eef5516bbf2dda90", + "0xa8284ec1eb1d21e693f31a6c074199ee85d8a8da2167bffab5fe240defa2773971c8437e358a18f7e58d1e2954f57f6f", + "0xaa7415639d29081acbaac3e9c6b059d68e8702db3f430b86bb6e220d476fa74841c875e9d471c8a5423c58b6fee3cb54", + "0x8afcfe6f65fa6e07c2cb3e1756c0ef2c589830be96edd50c3c248e3b17f51a4b08ba92ef7eed7991d81667ddfbf2bf7f", + "0x83b9c8dec8ca8f9b85f0e36c08c5523cfeafb15a544398e6f93b48b5fc4b15a0bd05c0f176a9c2469664acab8dffb0a8", + "0x82a128a89ea46b9debe5c903b950c0ab30cd7570b979ca911500b5c2cca5c4ee6b2c2fa414b5f28e367f4671ffce60f4", + "0xb79fd0ccd2629a361cd6f9307c02ecd4d1f07e4ee03ce4b542997e055b07a026cbc0ba05fe3da309efc58db2e401a8fe", + "0xb190751141093823b4b5324cc26c4f3258552f7893241201f2fca1ae9b1a1d4d4964a9abdde8642cf308ded61ce5ef09", + "0x935fd48b95aa6f9eada0cf9a25a573f0ffe039888b3410788c41d173747bf384c0ec40371bb4383ddcc7d9f2db3d386b", + "0xb9affe100d878491ff345636ffd874ce1f27852a92417694afce4163e6a80c78b2f28d78102fd06c3283ef273ad37642", + "0xa877670276d49ec1d16c9f1671e43ade11c0c1a1413755f6b92be9ad56bc283e4bd2ad860367c675d5b32ff567301fc4", + "0x8c660d16464878590761bd1990fd0fc30766e7e49e97b82ec24346937856f43990e45aa8ad37283cb83fa16080d4a818", + "0xae1412087da5a88f3ccc45b1483096aeb4dcf4f519ff3dbe613f63712f484bdd8b2c98a152a9db54cf1a239ae808f075", + "0xad83cead97a9c3d26a141604268f8a627a100c3db7e5eefaf55a1787ddc1dd5ffc7544e4947784cb73b90d1729003c8f", + "0x97c3140ce435512a509e6ff3150da385fdf9e0883a5dc7cb83d616ec8d0a0014e4e0fa57a4d12c7997cd84e07d49a303", + "0xa353773ff68f1615454555bf658eabdcca40a9c7bced8537ea6fa8d54764fd1f032889e910d2a2a342835513352e2d2e", + "0x89e8df0c17a36ffe08149c2ef8b27306d04cdf437135aaeba697abc65e3c8e91bcf1817919a8a826acdbbe7dce79a18a", + "0x9928c2da15ac6cb20b15859c22508cfcd452c5643cd22eb84abf5f0a1a694fdefcd8fc329c9b40babc52630743d6b65a", + "0x99d837b556f8d13108eef6c26333a183f59383b39958dd807b10590c3d37f62ade6c4a320ca2e70567e0218b0ad5807d", + "0x9272da080e4aa18720b634640b01bf1fe506c7c8a89dee8759a53e2ca5cdbbd4a4f3aca54924c46b935362cf1eca066e", + "0xb4d39752c882de1c1daf3854202c1d58c2bcf35c882006eb640fe54a97be2655281cdb91c30d1a41c698617c2cf64b01", + "0x8bf827f4a7d47e07374d338a3d8b5c2cc3183015b5a474b64b6086fcf0cdcf4852046c9e34d7917d69caa65a9f80346c", + "0x901bffc7db9c9416e06f593a76d14f6d9e5dea1c5f9557bd8c93b9e70aa4782bab3518775c2a5b285739323579f7cf0a", + "0xaf7e204388568627ca23e517bcf95112ca8afd4c6056b7f2c77c4da4b838c48791191565fd38398587761c8047d11c47", + "0xab2576b5366e6bd88b347703f9549da7947520d4e9de95d7e49966d98249406ed9270fe69347c7752dad47e42c4ea2f4", + "0xb12e3b228b761dedd99d02928105494ded6d4fea3026d73d65ebffa2e85e2cd75b6d091135d418dd95ac102c22b5ee31", + "0xa20b4a752685d5e31ee7e2353c8a1b9a5265f12bb775004d282a3ecd9deda44831bac1ac5151646428b66909b2a423f5", + "0x91a1d4bc0062a86cc6786a96fd3eb4436d8a4a187b7cbba02190d1cd6ed3c3797d9ae7d6ddc413f1c94a21f62bd04ef5", + "0x977f18da1a5df5cfdd0276f583cfba2b2a0fc6139520664e20068f8dfdde33e29d179abfd722f142448f4677aa47be6c", + "0xabc3ece90f0f7b1d80fd917de27ab0d88cca584ef959da520825e54cb5a71336b15f8b348532d08d47a6fa600527ef25", + "0x888d36a2c7cc13a1c1aa338a183a74a1f57713e76cb825f9837f43279ce4741999b76a16928147537bcc20f2e0195b0f", + "0xaf3f5dfdc2dcfe19de893f385f39f550cb1dab67c2e97f1d5fa735e5ec96d6680066803e8a0eb010dd4399f654195513", + "0xa0fb4e08ff56530a940a86c28830956eb6dec2f020f7faaea7566faf0a4fafe0cffe01480e87763ec22f201be51a6451", + "0x92343c5b107910b203c64a79c93d354f7ee5b7d1e62e56732386776e275285561cb887019cc00d3fdbe3b5d54460bec1", + "0xacfe7df83c4624188a1011ad88c1e1490d31a8a8c8016b40aebcdd7590d9c0793e80d2d7ce6a7048876621c252a06a5e", + "0xa7da001dc1e33e0e129c192d469d2bd6e5d2982eb38f3ba78bae0670690c8e70f40e8114a57bd0718c870ca5dd25b648", + "0xa903de5ff97dc83628290d781e206ef9d7c6b6d00cadc5bacffb31dc8935623ab96ade616413cb196a50f533e63641d6", + "0x8f9658d42ad14a60bbf7263f6bd516cfee6b37b91a8f53715d69f718a090ad92484061c2cef999816760a78552fae45b", + "0x8c15b72b3d5fcb9ffd377fd67d9dfbdd706593fba9629002639973db12aac987bd1db70250ded31c88e19efff612cdb8", + "0x88a2a4034decd854fb557960194ff3404e239953818a8a891bf72a0b26a8e570a65c4a630884de991ae7452b3234f31a", + "0xa09cae5c4c190537bf1dd75bd7bce56f7b799762af865bb9d1ee970f6a133c27cce0dd0f14a0e0516ceac41054e6998f", + "0x9760ebb1b40f9a97530c3b940d4ef772a225e5b63bf18283f8e302b9436c5209f6294980fd37058060e429fb7fdc3a56", + "0xadaa9400eb86d857dc591b25dbe3bc8f207b69e77b03cb5ee01f7e4b006b5c8f6ba2b51b5a45687479885708509363de", + "0x949efe6b00b3248846747a9ad4a934d6e4255994c2b540a59fbbde395fe96d69bb67908441cfadd8c8bbb561fe52da03", + "0xa19a45504b6b1dc3a0fe0e6a1384734a3dcd5a7cb8fb59eb70e49426c4fc44946547443d558e5719a04884ab3a2811ca", + "0x8934c9ee21e8d1435426fd0f64232a0670a7946ec524c054cd4f2cc8b1be9f89cc11002ca8aebae646a2050d91716b10", + "0xb1150ff8ffb34ffdcf7d603348c0aed61e5f90ee0a1b814079fc2a41325c75f2f9ee81542797ede3f947884266a772e0", + "0x86ce8cc7c1f92af68de2bca96ccb732f9b3374dad6657dfd523a95e8a931a0af2a80df74098514a06174406a40c16ba5", + "0x90faabb9ace9e13fd9584932846ab28a618f50958d2ce0d50310a50c3bc6b0da4338288e06e5fcbaa499f24a42c000d5", + "0xaf4a935c2d8df73332a16dc6da490075cf93365bd0e53e2374ef397514c30c250bcac569b6df443985cf3720a4534889", + "0xb7f948ee90f394789eb0644d9f5ad0b700c8e44e5e9ed0e49da4cc18483676d25740710b1c15a557965da635f425b62e", + "0xa917913091245beed6a997ff7043ecf60c4d655c4db0b1ef1c704fd9b0e1ea1335ce8b9f45d6e120f81805ce31555e30", + "0xa48099da8406399bfb1ba834f6f7d864111d0036969a5cb64089947a63dd9467d3857b605e9f57f5ad5f4ec915088d9b", + "0x9784c3f9be42eed354542b1446d734521f8e3f01cd9d495ae98f2e4a3a16767fe2ad909e0def5d9a6267f3fc6a172cd2", + "0x8d9afaa323847a3226ad7d7b60d87322ffcda2e4a8df89f58a076f7972d896588de685a2e155e243bcf9456b0a0d6d1f", + "0x994413faf0b843f4ec1842c706c45ea5f24351c68674a27887bc8b182eda756856e507a4e8bbfd937e2c4c581b629ee6", + "0xb3e72d9d1ddaa00c7d22f25462d6e9f2faf55e30d138dce8bb1517eb0b67132db758668aac26164fd934d732633bdea5", + "0x8e95875e338f714e9e293df104f0ad66833bbd7a49d53a4f7f5fd5b18a66a61aa0a0f65cc31d55e0c075e0d3e412cb90", + "0xb980091862b1a9f9334b428eae14bbf1cecb4849e3a5809773b0d071d609727270f6ad97f329eca896c178ce65883db9", + "0x915d7ae5ae780bdba27ba51a9788a8852a15355b569581d1f18f0d94bcdfed2c1ed5a4f58e049e9825cda11f92b2c2d4", + "0x83e581058edf9259d0b06128282327cacbb6afc939578223cbf93544599f799a8dce1fb21d52464f990a877086f42506", + "0x803612a38b6f6efb97941997e101ac1878e192456f8fbddb3359aa7f3023434ed8fa92e60ec8e7b4473b1948850e4311", + "0x864a1bf4ac046161617dde282e44ab3cc1843da01a09ca58aa00ed00eaea9351a07a9ec16d910819e7dcc28b8d2c8ada", + "0x922eb142845975d5f6f7dcfee6cac8c299b3730400e6bf82cc0bdd9888de21de9d9f1530640f702c003e1ed63b140cc7", + "0xa7db03c5be647dce1385ebc02f4825a654447fa8c4c8d4b22e635dbdd2b3ccdf219384e49a80cfb1e9e6182b6e4227ed", + "0xa167289ff0f0967bbab6479e4a8a6f508b001bbe0d16cad36ab4c105ad44f3f180e39a6694e6cd53bc300fe64dac1e8c", + "0xb7766431f6379ce62cba22ab938cdbb1b0c7903dfb43980a417e0ee96c10b86b447241e9dd4722fa716283061b847fb3", + "0x90cda18c5d66f5945c07c8c7dc453dee1370217ccb851bbea32578599aa669b4dd245dd8a9711b27c5df918eadf9746c", + "0xac690cd2af39932874385fbf73c22b5d0162f371c2d818ec8a83761e0a57d2db2fca1d757343e141e1a0348016d5fc44", + "0xabac820f170ae9daa820661f32a603ed81013c6130d1ca1659137d94835e1546c39a2be898b187108662cdcbb99d24fe", + "0xb2ea5a5950096772f2b210d9f562f1a4cfacc021c2e3801ac3a935f2120d537471307d27b13d538dcbf877a35ff79a2e", + "0xad94af4d0699cd49ba8ca3f15945bd09f3f7d20c3aa282a3113cdf89f943d7793e59468386b067e3c1d53425dfe84db4", + "0x83788367ec97cc4bbc18241cbed465b19baa76fab51759355d5618067009298c79d0a62a22e2a1e6dc63c7b90f21a4a5", + "0xa3e142d879096d90b1e0a778e726351fa71996466c39ee58a964e6b5a29855123d4a8af47e159027e8e6be0ca93d9955", + "0x860831f8d3edaabd41be5d4d79c94921625252aaec806251fb508e364e39fde8808d38b10d557e487603a1b274c9bc3a", + "0x88da39f334bd656a73c414ec17dda532059183664bbbac44eb4686c2601629ef8ff9da992c337a842e3885b684dd0032", + "0xb50addbdf7164e8303f33de5ce854d6f023d39c1c1984b214d9e5fb6f6001cd5bdda816f048a438ff3d696872672f805", + "0x999e58c4c69a912b84561cb09610e415b43832beeb95897eca8c403ef4754f4277754d492eef3673afd4362f50060fc9", + "0xb88ea0f60f8119c5a1fd9294796d387472dfad22442b29659713d1d88e7d854cb7cf5c9ef773627781188626bb2fb573", + "0xa068b3844e9dbcf74b54fd55904d56af754d8ce4c619fead7a07f9bfb9d02118db7c512ccec2489d2a84374ec1d1fb6d", + "0x871dee023768636003c799e6f6fd8d31315a4c0da7286345cd64264a016693b3485e0732be1bbd34dd5fa04dfa58a983", + "0x8021e8f508680df12e4a5a1bd49f2d7142df65158b0a7198ffa83abd16053a542fb93ffc33e5279020ba8c6a26feacf2", + "0xb5d3cd64df5bc965228b0bd4ce9e5797c409f7b64a172ba165e44a8e4b38e3d5fabc3e0b9a19afbfe427f887c40a315d", + "0xa54fdebbb594bafcefb1a03697711e0091c072e1cc24fb441fefd4e0a0518675a1d7b0966cb8294051d7ec0ac175d0cd", + "0x93922202337f72969d6d6e14a29c9c75e0420dfba712029941d1504b9f6f9761d706cbc0652cd09a1aa5d22aec766af1", + "0x9711ebf1c7c7426190d4afd5dd03b014a456bbd9d90ed101623866a280550df26a629dde400c03ee3699f7d827dc0bb9", + "0xb4d686d8bc5c1e822a50124c1cc23c6bc3a1577a3d0b8d4b70d1797418aaa763283c09e8a0d31ae6d4e6115f39e713c4", + "0xa533ea2ac683e4ba07e320501a5d82a1cfc4fa1d65451000c3043f0fdac0a765cc1125d6cc14fe69975f3b346be0fdde", + "0x94ee563134fe233a4a48cf1380df55ead2a8ec3bf58313c208659003fb615a71477e5c994dc4dcfb2a8c6f2d0cb27594", + "0x93e97d3f3f70664d0925be7aee3a358e95ae7da394220928ae48da7251e287a6dfbd3e04003a31fab771c874328ae005", + "0xb57440d34615e2e7b1f676f2a8e379e1d961209fe00a0cf6798f42b7c28dbd03172fce689305e5b83e54424bc3f4a47c", + "0x97644084c6f7b4162bc098bed781dd3af6e49e7661db510975528f1dea8154f3d87e979bcae90c3df3a7752eb0752889", + "0xa923b27b225b2a6dd5bdc2e3d295b101cac5b629a86c483577e073cea1c7d942c457d7ff66b42fcf33e26c510b180bc2", + "0x86698d3b3873ed3f8ab3269556f03ac8d53c6e2c47e5174ec5d14b3ed5c939750245441c00e2e9bb4d6f604179f255ef", + "0x87946826d3aa6c7d53435c78005509b178fdb9befc191c107aee0b48fbe4c88a54cebf1aae08c32c3df103c678bad0ca", + "0x860864896c32b5d4cb075176f4755ea87fea6b9cb541c255a83d56c0a4092f92396a3e2b357c71833979b23508865457", + "0xb78fa75d687349e28b4ddfe9e2d32bb6a3be13220b8f3ff1ded712088bd0643da9b72778bcca9e3b103b80097f48bdd0", + "0x8a188b940446598d1f0e8c6d81d3cada34c4c1ae0118ec7e0eacc70d1bced28ae34b99667d5793d9d315a414601c3b22", + "0x842ac6f7dc14191ab6dddffcbc7cb9effba42700a77584aa6a8e17a855cd444c5d138f9d61bf55f43c6ffbcc83f92bc9", + "0xb6742902c3d145a6af9738c01cf9880dd05c85f0d0ef7dbe93c06fdd6493333d218339ebc2a02be1895436a2f734a866", + "0x98bf18488483c627b7181b049d3e6f849fce1f15794de59dcde6e5a9b0d76fd484a46e48822a6a93001d3aa12f48bc6d", + "0x8769cac10bda8c53a1c19419ef073a5998f73dcf2ba1b849561615a17cbc0a49bfe3eb4ff8801dd36a22fa34b9a3a7e2", + "0xb45c084d58028fdfae792210fcd183abc4ffddeb4cf52ebf3f8a50e4c4eec2a2758f1241b0920bebcb24b757c778577c", + "0x85c1216eec8e1fbc1af9b36b93c5d073a81d5fba86a6daae38748ec1573eacc6bef209e76c87a6efbd7a3f80e11d4c3c", + "0xb8007e34bb3f927ec06a050b51e633d7eb9e9a44715d5b39712e69c36177a03cd68391090cc3293098e54f6cf65f6caf", + "0x8e85527b27c9152b1ba3fdd532a76a79064ab097570508f233e09978761dfe3012d537411b47d0e4b65265eb32cea2ae", + "0x899779f3c31a20b76068ec8d59d97a64d2249588ddfd69dcbaac6bfaee8ce0ff3c5afc4e17c934ae7cd041b760eb555d", + "0xa5dac3d8f5fbef018509612e25d179f60d2a62451c76426bf546e9666fcdc73263d34aa6fa7e2bfd4c9947bbf5095eff", + "0x896900eeef9be2b2e755128e7b1c436af6fb3984f1e66c444bc15fcf3959013b4902c381f0eab1247f878a6ebd1f4ee0", + "0x8cb17f4b0af2e9b2cbb56f46e6a5d6874ea0daf147aae77303020b4e592ddc92e0dd058def7da96258b3a68b223bf22d", + "0xa1b6d3f09a9fa7ecc021ab7c5396541895da6e9bf1f9a156c08fc6f2b815a57f18c337ccfe540b62d79e0d261facb2be", + "0xae70888811434ef93da60aeee44f113510069fd21161e5bb787295492eb8df85103794663fc9305f04adcbcf11ff0c5e", + "0xa84bbc8624100acfae080ba8cfb48fd4d0229a60b62d070bd08fade709efc6914dc232d3f7bed76a59204f9252321aad", + "0xaea47d54652abd8ca213cfc623c8e30780f37b095b59ac4795252a29c2b6bc703a5203acff8831314478b8ee8771d4d7", + "0x8dd438eb8be14935f759aa93021c2b24e1d588f7a162c42c90ec3a647b0ff857f60e24c0a8953eb7bb04e04be70f11ce", + "0x922b07b5469680a10e7532766e099896f4dc3d70c522d8add18f5f7765d4ddb840df109146607b51ceddd2189fa7b9c0", + "0x83ef6ebd0ae6c569d580093e8b0b78daa964760556272d202d343e824c38eccb424262e5b7809d3c586f9e2e9c5c5f22", + "0x97f98bd357db6e093e967fe180cf67ed09fa711580a5ad48f07cf095b2e8fabbe6319f97d1f15d62c0ec2227569d8dbf", + "0xa1953a4a22fe6c2beaf2a5e39666b0eb53018af6976e3a7aab5515550ff2efa89400605a43fb2c4ac1e51961dbd271d8", + "0xa5cbd67f4c0bc98e20aa74c09e6f5fb6f42c08e59aaa477b4b4e61434c8884bc14f17cf11faecf46dc4b6c055affbad2", + "0x87d96818f2c4f12fd7705cf4060a97bd28037c5ac0f0cc38f71189ec49361e438ce863e6617651977708094d5336d1da", + "0x85e7c2daae5fe59f8a1541c94df50402a671a17dbb8838113fa4b7aaff6114cf2bb5969410cf21e6a162857f2f7a83a8", + "0xa19575083e1731bb04bb4a49414e97aaadb36d883aa993d1f6847db50007315444814740e67e10177a14e0e074fd4c7d", + "0xa00ebfb5bcc3a6da835078189038a1e56b7dab6be74332b5ff7440e53b0f9e1eb9973effecbbf37000021fcf50c7c1ff", + "0x8969d7943abd3b1375fdfc7d6124dde82b0f7193068ed6ec83bcf908734daf3487a6a30f7b322e54a4818ae5f86d91c0", + "0xb959c8d210fa43af9b20d1fe0ea8c4921280eb4544ef6ea913309ff9d61c9327096707e84dc1662960519be8e7d080a4", + "0x9011d8ac651c42e0cb03931a9e960f58e02524c6b666047525e3b9097e9f35fb2b4b278efcce2bd5ad463c6d7fd56694", + "0x937e3b22ed0fcdbd9ea5a1b97b84bbe86b7f5b2de3866a930611112f2217f4ee7d9822c4ab1253823f77bceeae0c8e10", + "0x828997e5d121f4c305e018a0a0ba338bd6a34a7b4dc3c5ceab098ee57490311c130e2c045b9238a83908d07098d9fc32", + "0x8d114808eac0f2e1a942d80dad16756ec24f0276763cd6771acb6049472e05a9bb1d3bbd5957f092936b415d25c746b0", + "0xa063c5c26267ae12887387cbebbe51fd31bc604630b3a6e8e177e71d4f26263be89112cd12d139dd4c39f55f0e496be0", + "0xab1e1582c8d67196d10f969eeb44e6e16214f1316aa4a2a821f65ba5834326da6cba04373eabfd3b3072e79e5c9717e6", + "0xa17b1dbaa11d41457e71a9d45d032448091df7a006c1a7836557923ab1a8d7290ec92a7a02b7e2a29fcea8f8e374c096", + "0xa1ed7198da3591771c7c6802a1d547cf4fcd055ca9010756d2a89a49a3581dfe9886e02ee08c4a2f00b2688d0600509a", + "0xaf09aa60c0a185e19b3d99ffdc8c6196d8806169086c8ff577bf3801c8ab371e74165ba0f7329981e9252bfe965be617", + "0x98c04cc8bb26ffce187fa0051d068977c8f09303a08a575175072744e0a5fb61191b1769f663a426c30d405515329986", + "0xa542bf1c9c3262d488ea896f973d62923be982e572172e2461e0146190f2a531f62acd44a5e955a9f1e242b3e46d63ae", + "0xaef7b7f30efd50e4a66c87482386f39f095bff6108e68f74fd3bb92156c71c75757912b111060cdee46a6b3452eed657", + "0x8afe1e0ccd00079702f16ab364a23bbbd3da1889d07c4f8cb04fd994bf9353216360dbd364492932bfe20b8b69ae8028", + "0x9896c690999db3c08cd7b25efb1b912c3e0f976db98a3e830f086aef93222d06ce570a7b2babcd7c81d8f9955169669c", + "0xac7bcab6a281468907ef1ea8a6c1cd624159c88839131bef6aa0c22f331fc87ec6128a2c2a333fb79df549e4587e1a12", + "0x987935c08a30b099d19f96901315a2e60591baf898581c40bf5eddcda806ff24a4536e30ed1e6c0b128a83fc77b6e81d", + "0xa0a6945bbede3bb09a4a09ef27baa20619d3e15af5673b9350601bcebe952597c989870746cf75767ffb73b32c6c9c6f", + "0xb0f5590079f0a0302b08a0cc1b7a5f39cc6900c2a5cdc7baa333d8328a731b2df5dbb67e27a154d3c44ed1a795fc4adb", + "0xa7294bdeea210e528f277f3d50e89e6d79950494478998181ecb38de675020130256f2f2a075899170be964d478458b0", + "0x8ab3041b895a631869b439d5599a66facba919226ca9b39d915f19d59f9fc82393ea781377e9bd3bcc5a310e41376914", + "0x8da399b59151fd48b2579948bb82698e3c9804d70ec7d6f3cc7e82901f9f2de5ee850349a7d6f43e5e9ebd47bd78620f", + "0x80e8c32de83d1083916d768b11a982955614a345d26d85b457f2280ff6c52bb776958add7c1c8878f7d520d815b8e014", + "0x81bbec7bd99d2917d2dcd8a288722fb33ad5a4bf5416fba8609fa215fb80e0f873535349e7dc287f892aa56eb9e39c4a", + "0x9665796fe04c8519206fba58496bc84a8b9113e7ea8e152b65f7f732e88beea271dc97b1ea420dbc8257cc4b18a77463", + "0xa97e342aaaf693ddc87e02790278e4bb50117af4413cd703bdf3b7cad2d1facf31fde1303b43ab2e0265467474f97a8a", + "0x925549ebebed348886e37773b05cd8ad04906eca4536bfed951d1ee41b3d362ddc6e1a302c21ff3a2d1e70e95117922c", + "0x818fdf74d7903502101551bbf48d3c7819786b04b192d9e94362d2fcb85760d8b6f45165a5443aa5221bef400525ddb4", + "0xa9d29de7e8fd31b59f4a087168d062a478b1329cd3c81c31e56de4fb40de7a5be9a5269ef0be452c487443a0b097dd50", + "0xa85286ad573db4c9aa56221135da1e31d742e0f6ff01d6b159086d7258f78b08dad55ec8eb5c91ee9d3404b2eeb67e1e", + "0x92a79b37db5e777f9ebbebde24a95430a199e866e56597c7d0b0e7fb54c7b092c2f6cf61fb24470ddf250cf609898281", + "0x8d79f5ca67ed67d52c82949af342a9fc60fb793c47c76d84b4863c550796fcae2dd59e285897c6fb96fe31cee1efa62c", + "0x8ad2e0bda03415ab86324992bb62dfa3612d2d003765bcad1468087c27971d08bdbae5252681f0115a184f4885d444e4", + "0xa08815af979286538c31b4aa5ec805053790af1ca58a8c4341be51136d094a8a05e569d876a079033298ad355ccb7ca8", + "0xb96c2978d0165d619d08281d295e90df78bc2375d0afbc3142ebff9c2cd4b0f0aa97a9a0e3740bc4dce0ff8a9fac8252", + "0xb7752cd0e582f35ab0d0036ca9c0a9fe893a6ad325164d78d865a604a85d3d23729e0362553e8b8a3d51816beeaa30cf", + "0x99cef1fafc29e7adfe247c753c475ad4bda7a5f9558b79c86e8a65968ede67adb38dc30071925c9d66a13860027a6735", + "0xb9f6c65af178c791b6137d71980651fb09cb5b42f268999c728c6e129985a9c7d77b3dc3b50751bd29ec9ee0b3111dfc", + "0x8d73ae61fff5be883a281782698075c5650083f00399992688738856d76d159803be0059fbd9dec48f4f0432f0590bbb", + "0xa8a4a2865226de9bbf19e12c7e75318439fa6cf1cbf344d5e79a8f363439d3bc5bcf4df91b54581e7866e46db04eaf0d", + "0x894582aeff222e145f092ba15c60d3207340c38f2c6792ee2ab4d82d50fb544ae366c2985cc2b6c2f970bcc5f4b46385", + "0x956014ba2d20a056fd86cb8c7ceeab9a2c6f905dae24fc1c5278fa5b84335148ebdefec5dcde8eb9b084700724fc93d7", + "0xaf217fe2b654eff6d11a2a79fe0339a1d4cb3708b7be9f09d852158b5a44b4f9b04406d6d67c4f144fb6b69a41ae9d0f", + "0xa90752a784bc00df94d960e523f5596695d16a534fc806179e0f878fc0e82a91b25e758e91a165debd815dd1af5f1028", + "0xa697606fb32979549ad822b31df8eaaf50de4ead984439a0a33e955937d326519bb9f62c8243ad37f764655f8d32cc80", + "0xa3ad4a30922e45a3e665551e5611384f1c2d414f6fa806184b0c826af05f014dc872585e255543794ee41e43cdadd856", + "0xb29c255843a82ea74a013bac6c36a694646e61e6b9cefc4c130e2ee261e3bb5da3e0fe3ee7e6fbb009deed0530bc1c82", + "0x87e1cc7febefa829cf050aa2aea59385d1048f8617abba691f7ea9ef58eb90ad12eeb9c439af228b0e34897ba1cf1b47", + "0x994d3222f89e9c8c154362190be7167c8c2662f0cfa9d50eb4d8175b255ff0de09dc548ee312fc8226963c8c16f43e8b", + "0x8f1a980be640820f2d1e953264ca4c30330878971669852be3d5d6b41c488be1628b935388bfa2bd4de484acb0fe661d", + "0x854d90d0721579c8c88e147a4aa83553c960617b18075f8224b975562dccb30b0e02e81fa9df7070f356a0eeffc3b14f", + "0x8e156da9d4330a03e32a25a2f0b861fd3ea5c719fa4f834119baab6e5fa5236a9baaf0d44147bf0841418900037f6eac", + "0x96586fc49e53a6799242ddf617000db5a0ad20c6cb1686af2102623d64a71aaddb8e468b15fa6d100d0384e448548db4", + "0xb44d8d85c8df95d504f82d597f8c515866d4d4a326fa1b816dcc5bb0cc4ef1a52647aa5d2e84c62e194c01cae0885d21", + "0xb75c43e676a7efd199f8b32ae31f176ec667e714df355e9eecee97246f72af5bef9c5b04c11e7e90fc37bb9163f957ec", + "0xa49835ac0565a79f6a9078cf0443c5be20561a68b448289589721fded55188583f1d301925a34eea647f90a6e66c6774", + "0xb47c17ff6824a00b8f29df0adb7f06223208d062bd703b0f763c6eee4ae62d4217eef2da4f4dde33f0b469c2f2db9e42", + "0x957cf039cea6f6d41e368e2bd0cf77315938a0738f15ed9ca342f0a28658b763659ac1d1a85ecb362f13de12b77bb582", + "0x903a52f8d2439fa63f59e1e9aba864d87b0464ded63814474947112375236a6f84e8fa003cc4433c8208d80e05fbd1b0", + "0x8afd524209ff08d1eb6312b078f7afeb8e1155af649e930ab711dedda226dc2db6b0354aab9652eea7f433f90015bf7b", + "0xa95c3c9277b11bc8fe191773bf567641be57c0549913b973fb18740ff9cd7b3f7ce198fa4dc1086b2b8a446012459193", + "0x9455ce8163fce04aeff61e7808ef3aac4725e51404f0858fe5d39d7344f55dcc7871ca332aa5cb1a63a4399529e48907", + "0x809fa35b6958f94e781f2c584438b33f5ed528a6b492d08960cf22ecf63ea3aa1e2d29bc879e17296e0a6cc495439cb6", + "0xb0f50774de212dd33e5837f6b496556215c665437e657f674fc5117e5c07dadbd0d057e6ac4c42d50a8eb81edfebf315", + "0x844c65e263891d0b2fea7db6934cc4b7fb6bee2c1d0b9ab4c47f2eb3e9c5d7197dad828d38c54139123740151420280b", + "0xb13c78c9efcbb3b28eb3fe0b971380b7d5151c80948a99cd93c78b4c3ab0e86df6226a64d91e0a2ea4a1c0a46bc0404e", + "0x90300a541decad460c348b8f4257f7a29687b2362ebee8d92fd03cc0e85b285ccb0ab1cb2ff5e29c5cc5295e351017cd", + "0xac49b409ded770c6d74f6e70104c2cdc95b7b90609da0743c9923179e8e5201ead03becc0ab10d65b3d91a5be0d52371", + "0xa257b815bd8289dfdfc21af218aaba12ccfd84ebf77642cc4cf744d9b0174ca0b0d7ab2a545c2a314fd5f63c140f41ab", + "0xa34778d8446e4d74d8fe33de64b2694ef1e50bc140e252af6eff3ce7b57acf8b6577a02ba94b74a8ae32e5113cf0a29b", + "0xab9e935bcf0d8607e3d66f013d9bce7909962cb7a81174923db02dc89e485c2b1c33d6065bdc7bbbe0450b5c49fbe640", + "0x94d2c5c5c309c9eac04be4636f61bc47fd9579b47aded57cc6c736fefb8dfd8f8a5de32210f7baf2052d04c0219d3b4b", + "0xb8dda9046ae265214086355101be3460421f7cd0ed01bde9c1621da510941d42bc93cd8060fd73f374fb1b0a5f38d45e", + "0xa6674649dab5f92ab9fa811d9da1d342cf89ff6eff13ad49f4d81de45438e81a384098d3ae5ccce4c67bda5dbe246d95", + "0x8d619f7564677bacba29c346c4ef67c211f7a3a14c73433dd1a7692e16a7e2562f1d0532454af62fc04c2fd2bb1789b0", + "0xa2b93d2fd4c707f5908f624a0fc889e20164d3c61850af9125f47a1719757a6ce6375aa1910eafa4c1e8b6e20c312775", + "0xa07d5585447654d82817ef4d199984542328b238157976eb9a267f0bdb2229acc25aee510be68f65a312b68fdd9e0447", + "0x8ef55cf95e2b24d8ec88e4136399a7763bd1b73d5e90ea45e9845123e9d39a625cc336e9b67988374b8ebcbc75f2ed21", + "0xb62c1fc32e27c767c461411b02fe9aa44a86586e1427406f4ef0b346d077db91952abce79318b382ec75b7be23058cac", + "0xb252900345f5fa15a4b77fb6af6a2d04db16e878b7bd98005333f7f6e3c8e6e46cf38fc5d1b2bc399c5c2ff4af730dc6", + "0xa4ab5ac0cc15d3d17b1747c6e3133d586870eae0a0d9c8fa7fd990ebd4fbb62e9090557ca2792a6bc6271856aa3c9a05", + "0x8e706b3f2e902faee10b22742c6c33bea6f670a8937c243db96885143c1db5c979e33ab73a38359b52b8d668ccd092a9", + "0x8a6792190ee6c959d79f60c22980ca140c638d88d75660adaf9bcbe6dc4692ab5f01e0c460170f09f74d5e582e85ff1f", + "0x97ffeedfc94c98ec85ea937e064d7b290a326838e62cebd407facd1ab4f08d9c0c109d79af7cb6170fccfa6c8243c127", + "0xb79970b67c09453614ffd83a0c923c17f857c6ce3c87a356298f8351cab0def7ed83efd4f6638f48df67e07bef4ad9d8", + "0xb90f1931c7cf1822cc0a97401119910cdfd0482daf09a4d7612e4e05046295cfb4cc50d5214b31676bb1a1c9d15f9c7f", + "0x922921ad813c01fb5d12fa7fb7ed8e0b0abbf7b19affa190b36013c55b88fe3c7df0ae663c970eec7725ba37b95a7cb7", + "0xa124f33e7f28feabb4089a063a08d52b7395d24eecd06857a720439dd9414b7073bb86fbd0b04e7bfac62d3dc0fdb2f2", + "0xb252fe50bc6677c004550f240fe670974a33ffe7191ed7675da6ac36c780c2f8d02be7da5d92cbe2d0ce90147847f8b1", + "0xae5f8c9c56070f919f3df2d2284348fa4b2e39881f7bc42c9b2f5b7cb1ebeef8ecac000f37329bbe04cc1680cefc7f4e", + "0xb432a4575caf7337f11eecfcbd34a6705d0f82c216301725ceae2b3c9df20fa53d1ebef65513e305013d1e0c2df522b6", + "0xb7c016fbbc4614cdbb12db1c9ac41f9a45d5e5ce82594d568a30cd2c66c3cc9d91a2c959697b67c582a0913de661505d", + "0x8f6f3e5e0347dddc1b2a34ec0dbbbb7cafbf976f19c9c902efb5c1427d1bbd4b71abd9f3fba20dda75c35a39393c989f", + "0xb0042a1d33a1ee9fdf3fad2299b8d70c4f1862d8393b5ebe3ac2189a2c5a58bb826128cd7a39b70d524a6dd976097e26", + "0x85297c4e8ae8d9b44c3fe51aa926c77d55db766c2a9f91b659040de36e34c9a4fc6f44380f8d61704498f6fd52395a49", + "0x8c61a988b6a00fe5a277450f30bf6daa932e42a2eae844568e3babf8815e09311f3c352dae6eb2d57a98d16b7beb2d22", + "0x990be28aaecd932e7edb2a97b9be2789a3905cb88737b1c79881302585801c69a3dd5fb230808b39db1352fc06e0b4a8", + "0x82fd14bdb335aa46f022dfe0ed4d631911e6b6f5eefb10d11e9e2e02a7df55012ed8162249d10b58eb76ced5a7b06cda", + "0xac39cb058df764e161db9c39b185f09aa210bddbd66f681f1697ddbe6b305735612d5dd321d3ffbb4876771bdb321e2f", + "0x858a3f7e57ccb81387caf8e89f9b6039e9aadeab06886d8688fe6427151a59ab2e77e85ba850c67d099965426c97779a", + "0xb57fb9ea623cec432946819937c6bded0b5d03c8c67b52b44a4b67d34adfb055e6cabca67a48e4d859b4be45162c5083", + "0xb84d2990b563d6d7fe1f4c1894989db25b81745090b94b1fe2ef708ac3b2110ef93d647820b2a51fcf78e3f00fef5412", + "0x817d85b9f5e1521733d2b1fa6d4f4957ac445dc803f97fc495e20b819b14e651332f9e0573d684b854fd47824c53f0e8", + "0xb09e18e97e93a8523101af594422fb71afc5b8826002314269016fcc1b44002d91bcb7c90d923d460f0cc03bddfe9af1", + "0xb867cbede82102de7cf6cd0dae68506869576eaa66c3fc806e73585310602682fc912dc37adf5ff6f0f34a07831735b1", + "0xb1126255798368b692f2796a3470ed16e5ffdee2d8c9e0f7ee3d2e92950c3e6365c32895171c3494aff2a6d6356f7e25", + "0xb05f0a0996dec16335c770a5df3f0b08e20020c838c2caaa1d3a4a2490ede98552f5de349de2ce6e4c4a839731d80919", + "0x98c512bb91c8fa191120ddf5d63c88076581cf41e15eec3c168822f12b3dd0ce4d6df74a7e3093d3e35cad1cb3135421", + "0x84ce38fd97f7f90012c2c1e59a67bf9f465a7ccfb6f308bdd0446cc82b8a26ff7c30e5c7cc375011718cad1b31adaa9f", + "0x93139db52c9fb96dee97a0825f21e34c5d6d36838e1e42f4d12d01eacbe94426c85a811fe16ca78e89e08f1c27383d28", + "0x81454037b1e7a1765f67e4288b8742eebf6d864d9b0f508ab44fa3243168ce0ed30cb5f33dfcdb995cd2c2710ff97a6d", + "0x828deb2a26efb2ff1842f735e2cc27162360f619b6e3e27a85bedf384912d4726bb2759a3016937973092ece1bf90540", + "0x87e5a7d4e7bd301078f625d9a99b99e6e8e1207c9f8a679f8ebbbfb467bfa0b5f7ef4a4d577c7d2670efa88221153012", + "0xb9dc9d0ea48deee201e34379447bec789c8924aecd030eeb93db159af77eff230976ef60ea9f4b4a9e9e95c1f9f4284e", + "0xaa6528268d46bf0627d87d58e243d3ac34b863513c725908a2617e4c6a46ccb1d8c8334bd6dd0eea7ffebec44259dae5", + "0x8d26c9ce07293f6a32a664d31e6df9a7ace47e6c38001635918efd9872aceab62de7757b13b783d422eb67bd28ce7bbb", + "0xb0d3ca88d9829a7459b89b0dcbdb8bbb5180b00d750bd959bd110f53c2dd5d4db554b6005c4765fbe7ec5903669e5ebc", + "0xa94d1c72bf3b2dc6bfebc9dee40f6a89a516b252bd9f4fad96f156e3dbfc151a9b8a02324d764c7656d59230a18eb61f", + "0x88996e79171e30b16505638d8ecb25afd875e5f3cc3e29860937f2b5e751c66e78dc77f744a0cc454a8a655142a93ffb", + "0xaf4d94f342665fe7ecda318de6cf1bc1c40c37dd83d060fedaf827459728152b5f0e280286ff5e6a0012036f6715f53f", + "0x96beaa7a2d565ec14a4e5cb895d33624c69da56b75c8d06ac729cb6d0cb64470ed4f9b0387083cd827b1609c8cabde8c", + "0x96b773fa2fcb7377bf71a7e286f37f1f24ee42cba5b4f33903c4566e5e5bcc501ea360e3c8435749107c3de84e272d8e", + "0xa69ac6218454c3f40ad0beb48821a218fb0a4f33ebade986d2fffd9a3900d8cfa613bc71676c46cfeaa5f644d1f239a9", + "0x857f139c08fcc45370f448ce3e4915bcb30f23daa4134407fc6d78efac7d718b2cd89e9a743eec7bf2cc0eccf55eb907", + "0xadeeba36af137fd3c371a2adbefea614c3ae3a69f8755ce892d0dd7102fb60717f5245d30119c69c582804e7e56f1626", + "0xafa97ca3548b35aeda6bfed7fbb39af907ed82a09348004d5705b4bb000173270ce44eb5d181819088aa5a2f20a547a2", + "0x8423bd2d07073b0e87819b4e81997e4d3188b0a5592621a30981dc0a5a9d0578fde1638a364f015078a001afb00891c2", + "0xb92e9d4ec3966981ee574695d6e4865810b8e75313e48c1e4bc5eebae77eb28740e97ecc3e5c42040f9eb1ee4b13b0ea", + "0xb07b218321d54cecfcd2ed54a5fd588a6be8d7a5b6a66dff7facfe061222c40553e076e57cbdfa0bdb08e0a009c94ba5", + "0xa71e1ae4d6096eac9ea4c21f621c875423de7c620544e520fb6ec3cb41a78554aedd79493cbd2c2ba4f0387f902ddd2a", + "0x807cdac291246a02f60c8937532c8969e689b1cfe811f239bfdee0791e7aa0545e9686cfb9ed0c1df84748e5efa5e3da", + "0xa1faeb4504c057304d27d54fb3ec681462384a354a4f0b6c759d4fa313253a789250c6b0f44f751b0718592637438a19", + "0x996bcd3215182d49f1cd15a05e1e0a4bf57e264400bf14f7253c6611d2571de7130cce81fd28e0411e0a80e9054f4f98", + "0x89d15b38f14bcd46f4b2dcae82b0e7bf9a35e40bf57aa947e9c4a8f87a440b5cea95229708de08ca596762062c34aaa0", + "0x8d8ddcaf79374c750b8b0b3d196acb6bb921e51b4619876a29d09161ba82a42271066187211ef746f9f40a5ca17b75f7", + "0xa3dc7f70f3a6c7edc483e712770abbaa94bfa3174cfee872b2cc011b267e0ef9baa1ab49e4a6c6c30dbba0e0a1237117", + "0xaa9e958bbdcb192b19c43fc6fd34afcd754949fdada98e9f4848e8db0e23acb27d19dd073c951a8819000f2356aa22e1", + "0xa4714e45ec853eadfe5c3bee7f683b81f97857bbd7833192a48936dd1460aee68f700a21658658b74b737c4fecf90c7f", + "0xa1ecab4215c1892e4a8ff3405d710163875e5dfef8a8cb84f5cac4e317d89c7696e3f496ed1747ca6f52b304190f4ba1", + "0xb9b48943eca3686219575026d395b969e6ff8159dc5317005df090e79d26901984e40ae4b1af060ed3ff6f42e0417d76", + "0x9644b9f90a66edb0396abd8c00066886f978ebf56fc22081031fbc9ce371bf9b04aa5a4ef59e59319b3a05bb7fb88b43", + "0xb2bb14f1c055a78596488e4e2d4135a6470c1ee43961952160b8498f674a4d23040606e937c02c1fc23dbd47e9bd4633", + "0x8c61f2fce9a42b94a389c7e52d7d093fc011099d0f4914f6d6f05b631df7b88182826edf9bbb1225971a080ca5c0d15a", + "0xaa6a7b8499cc7d256043eacad18528d38bf3be970bea4c6d4cb886690280bdb373688ceba3e506471e1d9493dc76f3f4", + "0x8127703363b3b35b06762c2353d4de82b7b85bb860db1028d3640f46bdb78f2d104fa77ee3e0d9db83833d2b12a966f8", + "0xb7b01f5909f2c66ae0fab156be5d79954e3a304615e1fe55945049dd4bd95f973bb3821117eb54db7e9ed1ee9a527652", + "0x8be47ba5dfe212420649193490838670c40540e0ea24adbab18c4a66e7ac3dcf94f068dec2533b60e08c1f64e7533e54", + "0x905a6c7e24b86aa54a05c329a6b4616d335bb0b1f1e9987562eee0acf82ad302c7c44981a1dd6b24c6121ca12fb92996", + "0x86969ccfd91deed93b355a2c21319e3bb08cc652b741463bf68c626b7ba2afce3f7cc397f2fb74588c2893477c948ae2", + "0xb5a9d20eb12c331d0d300fd4b85b0ac0bb74573178a5fac8ec9dce5e95acba07fab444260355ece442a846737a2dcd1c", + "0xa13497c11df21b11fc1a63b0ffdcf7f432da4dc2c98f8d07d36da4fa68aceb57af2158088e5b05e334fe0f264aeb7a97", + "0x882e4597cc66498a45e86a2ed9ee24652da4699af00ad35f73b5e74fde6ac3cee70630962d5ddd86162d4aaf11bbc11c", + "0xb748858c2bafa4a14ce44af35195e9c52aa75e109719243bbe278095acbfd6a7ae7e084caf8dae6939039b5a4e8fd675", + "0x83a2e0524507e74f51fe976441108f8226ba1b3a33f4e16ec45c5661ce80cb1840a93d17122cb8ca9e0f80d14f69877d", + "0x846cd2946c93ee5f24243d9ebc69936b3a1a6d59f45fec6c79b1eddf15ce30a8e73ad03cf606ee66baea3d8ff115f70f", + "0x8d98d0a3a94f6efe158f8423c041b546416145c5c2254bfa157efea0d1c99fe58acc7df6424ef29f75960b18d664ea4e", + "0xa39fa47e4b79f54dbf59d0b1726f1e78bc219fcfc56ad238c84b4b610e7892ff1e65d537baf5118a32f5e2eb80d5ee0c", + "0x8c30969a4519131de5e30121c84c04f67b98c8ad109fa4710dd3149cae303d51778add3f258f0482f1c89c169824dffc", + "0xaf7f80d141ceb78b4762015de17fef49d7ff6202d292e9604deb508272ee7569f7fd5be3b2438da1dfecf0c26533ef86", + "0x97cf82f70128251944d79b8845506975405bd720e150d836205b048ff36ba8801eb74cdcc6425f28f6bc0acec0a81463", + "0x8c276c876eb88688957d1868bf3a1462375e608ff72b49870a5dac82cbf6584e00e3f36f236f732348a47502ccf9539d", + "0x964765f1a5c8a41d8025ddf56dc01b78424703d8a64a4e5539e477cb2445cb541c70127c561e717256d13f91a830ba83", + "0xa2aacd9e21b8c8efaf2319611addea1b9f41430aee42e7f2a640cc693aa395287cc8fdc2806b76b577d84fbd05378ead", + "0xab11eabbf5be4345a77323a3b75f9ee93b011fd2a9d0154e88183cafe47f82a7888666af16b40d3cb677c94bcc755ff7", + "0xa0bfe715a7af5a29b1b6148b8cbee585d2b49fa6ce59bcd173ea3bbc60d71a62f9da27ffcbbd5a6da75502112fe44d70", + "0x902e6cc38ee42245103d90b65028a471bc7a48b825599d361aa81d8c56e0fcf9fbe8d4c13802040d2cfb85b7e022eea1", + "0x8832e2b5014fdef4003bdbb87e3298fdbdbbe49673f6b66e2373f1cb2605f9c4af2cdf9bfd45d1993208681d29ee1c9d", + "0xa7d39d3fa1ec1e0c87730fa43d4900e91932d1cafb36c76b2934907becf7d15a1d84d7234591ad4c322b5a24673bba8d", + "0x836ed5f09d99624204aa3aa7ac601980fda223f3b4b96b4a8fb235c574a3545d518787c12f81bd5851987f2860d41886", + "0x94235e94445e6086f6e9331923262070a4c2ed930ec519eabb8a30133bd4fc6debb99185f4b668431fae1b485c5c81b7", + "0x9828ffe20b9405f117dac044159be2d3c6e2b50ecdd1651d6a73f7633e6e2a7ba3d783ae939973604446d3a1ef0fb20f", + "0x92f03dc365dfe9154743ca70e6dd2758f064e3286fc543cf8c50f68effdf7c554bd17b3507c6ff4127046d9bbb5522ef", + "0x91ed07df479d8eb3d31292a0e987672a7f3d45ecafe72935b7abbc3f23493605134ce573f309e226c9efe830b6868220", + "0x93bee582661e6d6cefeff29002afc2f36dd2c13dbf33f0574c35b290ddc426170a5f7f196369ad592efcd72cfb6f8fc0", + "0x89a51467d966f48fed15dea5a12dda54d0015f69e2169b5e34f44c7b5a5d4c282d6f138116a0cd06a8476980e420f8d8", + "0xb8ccebc14b6679ba2399370848864f15f63512fd6139df7359b7b93e82c1007fd85137ecb0597294b46643e1a9e7ab5e", + "0x841fa301567fc57b2cd09508ce75326684e12bfb8add671dc208f579b2500b93d5b641e9f59bba798ed4ed1259757f7d", + "0xb3cb45c15eb00b4ccb7013299f761cb8fefc17adf6db50e9ecb8abe927a3bc7f28e359e64693813e078e1dac800ad55b", + "0x96e55d3b9f445f5679e34fa5425b3e87cb221cfbdd07f8353868c7f7f4ba388ee3841cb9a1d638583bc20d03a9d071f2", + "0xa7dee9377de740270c5b57cf86699004ba8dc2766af56b388b5cb0814ec71bb99ecf43ee3d82a552733854ecc7def0fe", + "0xb129dfff23b3c1c95ddb214c4711961fcb129efe2b6557ec9e116ada909593d0d2eec2c628434493393c58c52aa86847", + "0xaed2670e201cb3e38a8be3c86735a4d76255e1e5a4c67b91df6ed262d09c8d10b0a3891da3e6ab934058cc9a7178931b", + "0xb20b8921ae52e5b3c94fa3a8b46489044174f7b897779e7763d6eb419e808d76705b7e7ba5131576f425aa81b6b0de53", + "0xa7e45bbc3ba1bc36617291ba7663806e247f1b57a89e31520c64a90cbf8d426cac2e2f381338baf78c8f92fdbbcb7026", + "0xa99e651e73a507e9e663e2364fcc193ec77e8afdc08c2bed6ad864e49b537ec31e9114ee72291a7657899f2033a849e2", + "0xaf966033636c2e9e8280d173f556fe07f8b6940bbcf6b2df7e2165c30bea66cced2596f6c17ca7c1aa0e614174953ba9", + "0xb69ca7a79e3d55ef21e0ebdc6f0c4bd17182d30cf6290cccca7d2551c91c12b966020d8e40e4ee4179488c9809c03ae4", + "0xb981cd36244e035fef043f70b1d7188d7cd045b4de0581c459fc5730e10eb7f3d5893b54cc4243849c0855e4e621167a", + "0xb20fea858a36921b35a3051ce787b73f70fdecd3fef283c15a2eb1bffb1dcba5991eee4a047ce4e87802da923fd9457b", + "0xb040e6f2e56dc1860274c263d4045837456f74b354a679f6b5ea70919835ebe5d32bf1f519e218730096c98ff396dc9d", + "0x8d2dd60e702c923a7204b530e7d6c193c6f93ca648c4f7bb38f4edbeb0aaed84184213afafb8db6aeb9197c24364276c", + "0x95dfa7348709e43d71285b28a0bfad3ca805b6ed4ae99753e9f736c79d58a35a3a50b42760ccdd03eda50f6e59494968", + "0xb8585632a13f18c139a411bb2f02df809591834d127cd1ff081e26d0abfe0e3fbb54abea26538b25a0dcb4d7e969590e", + "0xb46ba47858a29c6d523c9982660949567666daf2582b93393a4802a9e077eedbc0d49d454731696bc8e46ca50c7caa40", + "0x84b756b901b98a4404e58d70f39f6ccac877146c866732ae65e7e82727448d1550343bf7cdff1bfd4ee1ed73793db255", + "0x83e5be888eaf877a2c755897410865f64a6d1169a8ccf0336092f3932abab915e542ab75a35ffe016042340d581ee987", + "0x8cb274fc39285aed451a7def72cfbf73168ee10be02affe355a2bf87cf361a81ad284e9334cf00c5bf99a13d9f75e116", + "0x91ff6220924b94ae13f50eeac16a159232e4f16a73fbd5c22c0e185cd1998403904d36bad203baa82b85819ee4a8ac10", + "0x87f46e08e09aea2ab37b55fc300689d9b58ff3e72f1cffe023386035888f714fac4673c7c5193d3f3f3c568c640694f0", + "0x835d7d84ca7641e1b15095830114aa6072fe12260d2202456cafe2308c22651af9ffbcf6b7e56af97167dd0c4e2a4cf2", + "0x91202183f79794f114fd9e3b9bd05553c0e8985919965101a57d97ef666b028863e6cea9735af016dc1864f1542dee51", + "0x81ab2b02a9b0a490a74ae615ddd4fe560734c1bfdde6b8dd13303c1481ba0e8ab14473535a93cfe4e824a0ab29445f8c", + "0x8a32d73f4fc006551d4e2c61eec6130355ec9b8c39a65c24ec1edc00e80155ca83a8ef2455e892521a3d47634d82a987", + "0xaf70d7b8f13bc90193cc1cfb0c400c4224cf10f1887848aa93e6380f7087782fc41a159926ab53c53eb95c2383b1a849", + "0x989bf42f9d357c51774f1c7c0f7c0c46a8cb7398a74497141c32685be098e38b4230ffe833a6d880ec391a35b1a747b6", + "0x94cb6715ee95700020c630b8c19e35f231de970219bd7e6ba7ced01899197da473b6c45cacfab0d652ddaf547b4ea58c", + "0xb12e3331f1f7d7458393a785e22e9a5e1d1daea521b4e78c0ee8ca59b41ade1735a29820e18f6afb2f2c3c56fecc16b6", + "0xad4b7cf654349d136fb41fb0dd65b588199f68b462b05f5c4e5c2b468bfaa6c26329033e3c3f7873dc8ace89cf873ea5", + "0xa3279969e1ab596df0559ffc5ac7a6dc849680354e01c3f4fd34c6413a3f9f046f89c1e1be0b315d8b6dfab3d23d5c14", + "0xac74cc5562836ed89d09a9ae6a3644c936d64bdda9e77659d9982f1be29541b03ef2723236d5465e398373ea19a4ccc6", + "0x98138ebce1af531dd8b631b3e74c84f0c700355a2a9bde31e5e51bb10c8bbd766559c63f6041f4002568803fe08438e0", + "0x9006445da131349fe5714e0777a4f82a82da343612589a0c1596393e8b6894ce1cf42784f95ff67a8384ffe1f1a4ad76", + "0x88502a84a85e4ce54cfed297b5d355867cc770a8ffd0714a6f23b1ab320a9903c6e42809e034bb67dbf94c4fc0d9c790", + "0xaa8b4bf123d1a6ccaa44b86be8f980005f2a0a388a76cb111b0e85cd072ef64167fb0c097c7b23c4bca64c0260f6cce0", + "0xad49eb35dfea9feabb513a78dd1152ad7eba22fbb02a80cefc494a7037699c8df81202dfec12acc1b9e33ad680cb72d2", + "0x8694da730231b29afd5196371ddcb15b4dcc499574bdd063f4864ab80749833ea38ab8b0ca1629a367fe378e87a60a86", + "0x8eca7b488e810c479e7e32e24b8afcd837f7df183fe4f621a0336b53a9ed77603c84bdc365d8be68179a32b71a1deb7e", + "0x8875cd3e23c7e1af55af1b091025a08255743984186770bcd43f30b4a58d175cfdf1984bad97a15e08dac2da27198c3d", + "0xabdafcf58ec72997e494d4714645f40d09dcd0fbd0733e640eca44eeea67c25bb0c270299c459991f2fae59d13b4f4d5", + "0x8f040970141e61489284f3efd907705eae6ec757fe8e1d284eac123d313e9ac1e8dc14ae3f04d281e1effc49d5d2f51d", + "0xa7ff115f0d2dbf66c0e8770b3d05157b37357b9e33e9a447f0f3fa9da69ad04e371fd1e4848cfb9e8d05e3165bd969d8", + "0xa39b1a8c39d317fcc97bf6c396e6ed4a85640aeeadbf45166bd02bc3bdfb6266509159c03afd492e642384c635b824c0", + "0xa2e1b90f3dd2d0038eaa5be52127844ccf35d997143179d95ffd3749c0896398b130094d01eb1bb31ffe80ef34b42b48", + "0xa2bbe31f89b0c3c375ffaf63c8b7831860a921d5e388eb7907dbf61f2601ea40db86bb3952ecaa26a5eca4317a848ff9", + "0x87d885bb0f2ce04b40ce94d2557c15f1698dc652e938f9a2d69a73ccf4899e08eafa1a59a20cae92823795f5b94f04b9", + "0x8f7746370f8a24a2889d351f3e36b8a7d60e75e50e8f5abeea7dafc75441e95915721654e61ceac51bb6f112780d352c", + "0xa7272847526ed3d9e0d0fea1d8685b07b5b908971490bf8a46748c8b1783c629b8644feb5bac772ae615daae383d5e72", + "0x978c9aa2996d8bd6fda7e0393fa8b38747f8f99712427705c00f6e9a12c36f8d8b4cedb03fcb9867155cbddb5200e6e1", + "0xa4dec4a2354b2b32434c5bcdc380bf84580c6f9940f94dc0498a5bfe89c675a0921e66b807a3d859a6059a464cb2a9ac", + "0x99459ddecc7abce437f68722dae556d8ffaf8ed974f459e52e6d4a64f176caa4d42c2f2ec57e8a5b5f2034638e8acb0a", + "0x928c68c0c9213fe6258ab5bb0c693d97203d15da359784de7824dec143212da57d062a1fc70a79172cee31adc7aff382", + "0xaad3f318f1622ea87e12541dfd982d71629b8f1ded4c301f9f6b6af9432716ad057773c33bdaa6f15dc151b0ee4505ea", + "0x8eb8e978f149a983fd6ad01773f9aacf57bd0cc622d8a301e404184b37e610123dd081faeda571a0ab1f149a3960af10", + "0x851e7191d7b94bd422bcece5b92609fc1b1c8556229bc53e32963b2d2fd1cacd8ce5da9040b599eca6e610540f8a7987", + "0x9414157fe9d50e5a0b5a7397417681bcb3a651eec1cab63f2a88d5df68ab1fef6e4c1d7ba657cbaf241a7cb790297633", + "0xb5cb2dafdc5408959780754a58b2da55b2a9136672ebca42f34da4e329ddc89360e7218cde3efdbf784ddb390deacc57", + "0xac6b70f65503a8e94b773fda3e72615745824930114fe72b6d833484285462392617c1b2eea4a250fedbee88f503f3ba", + "0xb0829a5312f9ac6c06fddee2f835a3452fe994f6d42c9edfc390d7d5b3240ca544433b544cbbddd6516b38a6d5d7c21d", + "0x95f8e2c59905957e34d53be3d6fb85732f834e2cb9ab4c333fea2f502452a87ccd035fc9075d7c0bd8530bb0a0c96527", + "0xb93f279b7045f2d97c674495f6e69a3e352f32f43cc60300193b936c2850b2805c15457251f7e3f633f435cb2b60405c", + "0x915abf16cba1a0b655b92a8a70c03e7fb306b86f3bbfb66967ca63e64c003b59c7a5953675efa4fa0bce9bed536b6700", + "0xac2047f50a319d09df1ec44d71afdcec5ac3bd2765dc98aba347734aa780863545df9f6d71214d443e3f37edc0dae45a", + "0xad49c74ddb24c8a26b14ec08bc807313c77c5967fbb36237f55994d7511bbac8d7e7b9b8ec53eb1b3b066989f078dbd9", + "0x961483105f605e959213fe9e8a52b76dac62d7efd2319ec71fc4e92d68fbe44cd2f65d7adefb2eb64d591b91648b8085", + "0xb67fcafc97d8df2b3075bbff7b3d7471dbf1f3048f309e55d5e2c5bcbc7a73aebcb0697859be9f387cbc7ce98041e154", + "0x8da70ac16468cab6066992389cb37c79ff5e0babbe67d76878aef9408b9597a3dc2eb5de87428bc761a0d78957b0eb28", + "0xaec0ce89770d299b631f15ae12f94b1e1014ac57d38fcf037c2c7712d770d074affa06e97c60691bad8733874b6ad2ed", + "0x8b702c85fa4c915a09fc86507f44d7aeda0993b77af87780d70cc98d580c6e996b64b7c16cdb4dd4562cb0f75da36ee7", + "0xaaeb43aa472aac2253e211fd1066c3a5422ea041cef20168702d0618a1a742a44f7fb30a76677640fea1a24e7fae1996", + "0xa8820e92825d6e02b9b4ad5ebc86161d3244cddd3d244333ba1576b6ae10948145b68d9e926bf6b7a2c25dab4cf43f3e", + "0x8ffdae28a1f1d15d7ffa473628a66ee9a739073f59ba781248286b39cb8f7255f66d62337064246713cbb5017e615174", + "0xadfc5dd142b7911326d8424881d5d92006f3b17de4cce91674d6ea37f00fbb266c791ac13f6c7a0f61d04f2a952e6a04", + "0x87f98982444bf661f539bec73a10256f079a4baa88a1cea0351ae3de929e1c500485b2d1b5d933063cd7d9123d5050e4", + "0x8f217ba4dd404c5ee384f0c9a126686db001ff0344c01c82174c5e5ef89d1a241b146008c534b13a0da6c8afe7450fbb", + "0xafc85476dddaf1cbb4ba8b22186789f3818c7964f9f613e55010278800cd95422702248bdf9c73760702ef24854795ec", + "0xa59e0f6ac2ccdfbd01f002008034390c0ea78716f5e0de4e474e3558755705c9c7afb6e3c5c4370e7bbc85958a9c7a63", + "0x97c0695c58d792ec31d9b86d3b2fc1382f0855057b24d5f6a54c41f76f9e2f52882cadc89a8b2f121530e7f1393faa95", + "0x8e49112de0b2649c08a96cf737af68fa8055f1af594846a2d0534c94df6f926f200405edaa6e6ac9db7e380707a2571d", + "0x99a1bd83a7ac5f8d77ddf044c80ebfc5745b998714696d67b94d185c97e9d6db989bacac646d9def463127a8b2febc00", + "0xaba80725f9f9f7abe10760eca73ba427ca8df864a157122eb9af828a05b0199de3add02019a297750bdab5380e505c58", + "0xae18f62573275c1eb268f74c5e54e8958547f9e7d1d36a05b084eb53e5704fafe2200b8aff95cc7e9af5be2391c42b7c", + "0x908b8031d09d22b2aefeaa876a998e0a97c7a1070aad9e9c97836cc5aa6d2d5ef94230e1222074837b5e21b4e6490f01", + "0xb3132282e8b41ca6789ec5c43c1fecf3a65b8eefbc2f3d10f746a843b9ba4ce6db664678e75e424f7b11a00c1440de15", + "0xa1eb49440cc106ebc09cf198c93e8070271eb5a936d31c04858a2b311a037350100c7957d5545c9653f396aa968b91f4", + "0x81df6ad1bdd5eee4cc2f94318467b8602d15cc1be2b48b09ade12cc46ee05cbaaf77a20397e5015030b1f1db5dd9dac0", + "0x87236c68a2a93c8442d15d7f1d1dc01d1fd123439c183e1d843f4ddd2bcf638c128f66f1ef9b710e5d1f64a52726007a", + "0x84f2e7f85563bb2f61b10a712c7605d63f79af5be0dba056814fd3efebc20e9c53227c56577b72c68d185571b775eff6", + "0xa36d4ae06688ece2927aeb2c7f058a3cd2aa1de1601282d4e688e1d76ef20728b892928deda2314eba41675eba3912f1", + "0xb8326dcbcdcfce017b263c456c47692fb476c4225c95981666fff0b7d4522fc23b7f12273f0f47cf0442662124e6648f", + "0x84c66463ab277cda2cc7007d0509269e89cdd41c5e0d3773a92615f0fc5da63811186b05d7a11088048a5d4834a7e0df", + "0xb20d3571d970712ef4699b0e7034fd269c361f53e1572e2ea2676b4245e992d43b8b5931a801439a44d977a988cc360b", + "0x94dba6007e6d4998ca1eb84aa8e2a7e9f5c164b9d80df2825f2208ce5640a05aacac2e4f08918268990f43ae1ccab69a", + "0xa1c25f0b3ef9d1982153207570d9ce8d692e1b6963b509958dc4d9bcd80074bb221c46804a6d9a29e76149cc7787c282", + "0x8857748fcdab1199fc96084323a81d3bd8b5a7f0b1abc5bc3b5252a19268344e2e7d2d086c90fc9b5fa4b92feedb93a4", + "0x8b9c1d841447354b6c086549e4d1d435ab64c13933488c34bc30f0f6eb36c5c5b838b7b6bb018542247edd1ada091045", + "0x8f5b655416da0e719a204fc567e93792c301acb4374cf7bbabc6ce51dbeaaadfd75c2db0e16ce073ab8e91fd3d7ea9d4", + "0x90f2846b19be46a75c5cd0cafefcf9192e6fd80c479e8d6320c4b8d8d7d96703c9e77ff31a67afa9858e6b7bde1f7cce", + "0xa53e383947fd98aa1a55ac956214b46b20a52758461e8ba41341a23a835ebb713038bf048edb1202bbfd0b56a96bf292", + "0x9542d7debbcfb9cda6fa279c699a7b655c03b9a9b456a5d3cfc41a826c94eafa43e01155a29e39ff0bcd965f4c0c512d", + "0xa43792864ec5fc549f7afc02622454afc0e425c310c4039ba615067243ebb26a4c7ebfd19bd4d57ff412a4bb2a7958a0", + "0xb85123950e30c048465bf32365d24a5d4b21fffc6183cdbf71643a07b87463989b72dd9a6a47f134856f704909a6b38f", + "0x944ea689aec1376f855c0bc9c51378ad06ff758a2c075b95a60b535b88b36eca0be11e4edb5152e98cb2137d6e749f27", + "0xa6bef52cda22325e4c62d323e2a0e3fa91c5552fcfce951edfd52ad6f652bfdcc2341f1cd349e6b5d447924dc569bfe2", + "0xb56bff8ffe981bfcb30791836da10b87f2ccbe17ed969e7f7a650af07d27ae0223805b1264d985148208483be50578a6", + "0x8b209cac898dd580c82d854a553e2517497ad1a4cd198e1360b8b50639b380aee70ee4b87625d9b2278228ff644cd25c", + "0x877cce233fec74c7158b3c5bf108365e98238418b8a71f058f1aca44a0fd3a1021e3e9025bd11fe244d9fe0f5034ce7f", + "0xb1b871aeedb03d6f6accc99816b89f5958178738d8d8cd9717527d04363c80fdb5f6848122ae19fdbc450cfa11e753c8", + "0x858aca51b9e5b0a724e88688d5124eb24c9faf01a3d465e74d31de6da315f311143f22f60201ea09f62c92f61f09d889", + "0x8521d409615dfc8c8289e00f6aaa6297c2c4e1439b25952afd76aac641b81c70b9cef07cd58c1c0198382bddd2bd8544", + "0x88647c3e41666b88acca42505f1f5da226937e0522b538fe0cebb724e9a99730ca2522989e94a96cac94109aef675c0f", + "0xb417fdaf719caf38854e89ce52031b30ce61a632e6c3135adec9002280e022d82ab0ea4ac5ebdb21f1f0169e4c37bcda", + "0x9367a6feb5e23ea2eab8ddd5e7bdf32b4d2419fad1c71a1ed327b77362d8942dad971a1c2e6f7073885149cdf0a0c339", + "0xa71c5c08d50c57d094d6a4f02e97d3799bada92f238ffc07bd223bbe8379507b7310d20b28f5bbbf331e5e153515e491", + "0x9630a9a3bcb044b51299c4d3d3388a4ff47308dd27be3229601985478c0f6b55faa7e20815d8694f910611396a9d0d45", + "0xb0bfaf56a5aa59b48960aa7c1617e832e65c823523fb2a5cd44ba606800501cf873e8db1d0dda64065285743dc40786e" + ], "g1_lagrange": [ "0xa0413c0dcafec6dbc9f47d66785cf1e8c981044f7d13cfe3e4fcbb71b5408dfde6312493cb3c1d30516cb3ca88c03654", "0x8b997fb25730d661918371bb41f2a6e899cac23f04fc5365800b75433c0a953250e15e7a98fb5ca5cc56a8cd34c20c57", diff --git a/go.mod b/go.mod index 5ec51dd1ea7..96826859379 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,8 @@ require ( github.com/cespare/cp v0.1.0 github.com/cloudflare/cloudflare-go v0.114.0 github.com/cockroachdb/pebble v1.1.2 - github.com/consensys/gnark-crypto v0.14.0 + github.com/consensys/gnark-crypto v0.16.0 + github.com/crate-crypto/go-eth-kzg v1.3.0 github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a github.com/crate-crypto/go-kzg-4844 v1.1.0 github.com/davecgh/go-spew v1.1.1 @@ -21,7 +22,7 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 - github.com/ethereum/c-kzg-4844 v1.0.0 + github.com/ethereum/c-kzg-4844/v2 v2.1.0 github.com/ethereum/go-verkle v0.2.2 github.com/fatih/color v1.16.0 github.com/ferranbt/fastssz v0.1.2 @@ -91,14 +92,14 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 // indirect github.com/aws/smithy-go v1.15.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.17.0 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect - github.com/consensys/bavard v0.1.22 // indirect + github.com/consensys/bavard v0.1.27 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/deepmap/oapi-codegen v1.6.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect diff --git a/go.sum b/go.sum index 40800ed257f..1ac65bc01d3 100644 --- a/go.sum +++ b/go.sum @@ -90,8 +90,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.17.0 h1:1X2TS7aHz1ELcC0yU1y2stUs/0ig5oMU6STFZGrhvHI= -github.com/bits-and-blooms/bitset v1.17.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= +github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= @@ -124,12 +124,14 @@ github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwP github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= -github.com/consensys/bavard v0.1.22 h1:Uw2CGvbXSZWhqK59X0VG/zOjpTFuOMcPLStrp1ihI0A= -github.com/consensys/bavard v0.1.22/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs= -github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E= -github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0= +github.com/consensys/bavard v0.1.27 h1:j6hKUrGAy/H+gpNrpLU3I26n1yc+VMGmd6ID5+gAhOs= +github.com/consensys/bavard v0.1.27/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs= +github.com/consensys/gnark-crypto v0.16.0 h1:8Dl4eYmUWK9WmlP1Bj6je688gBRJCJbT8Mw4KoTAawo= +github.com/consensys/gnark-crypto v0.16.0/go.mod h1:Ke3j06ndtPTVvo++PhGNgvm+lgpLvzbcE2MqljY7diU= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-eth-kzg v1.3.0 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg/0E3OOdI= +github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4= @@ -164,8 +166,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= -github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w= +github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E= github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= From ba0a61bc2841e9aef1fe89a34ca1b073e05c6f3b Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Wed, 30 Apr 2025 11:00:17 +0800 Subject: [PATCH 132/658] cmd/geth: print crit log if chain config is not compatible (#31743) --- cmd/geth/chaincmd.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index c57a9a947dd..1c82ac7c099 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -246,10 +246,13 @@ func initGenesis(ctx *cli.Context) error { triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle()) defer triedb.Close() - _, hash, _, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides) + _, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides) if err != nil { utils.Fatalf("Failed to write genesis block: %v", err) } + if compatErr != nil { + utils.Fatalf("Failed to write chain config: %v", err) + } log.Info("Successfully wrote genesis state", "database", "chaindata", "hash", hash) return nil From 21341f6c0b03327612b66d0145071c1044eb128a Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 30 Apr 2025 05:02:11 +0200 Subject: [PATCH 133/658] eth/fetcher: define BatchSize as a constant (#31742) --- eth/fetcher/tx_fetcher.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/eth/fetcher/tx_fetcher.go b/eth/fetcher/tx_fetcher.go index ff17ae4945a..98a1c6e9a69 100644 --- a/eth/fetcher/tx_fetcher.go +++ b/eth/fetcher/tx_fetcher.go @@ -69,6 +69,9 @@ const ( // txGatherSlack is the interval used to collate almost-expired announces // with network fetches. txGatherSlack = 100 * time.Millisecond + + // addTxsBatchSize it the max number of transactions to add in a single batch from a peer. + addTxsBatchSize = 128 ) var ( @@ -329,8 +332,8 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool) metas = make([]txMetadata, 0, len(txs)) ) // proceed in batches - for i := 0; i < len(txs); i += 128 { - end := i + 128 + for i := 0; i < len(txs); i += addTxsBatchSize { + end := i + addTxsBatchSize if end > len(txs) { end = len(txs) } @@ -372,7 +375,7 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool) otherRejectMeter.Mark(otherreject) // If 'other reject' is >25% of the deliveries in any batch, sleep a bit. - if otherreject > 128/4 { + if otherreject > addTxsBatchSize/4 { time.Sleep(200 * time.Millisecond) log.Debug("Peer delivering stale transactions", "peer", peer, "rejected", otherreject) } From 76128727617dc50487c51039d08583edbcc338bd Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 30 Apr 2025 09:23:08 +0200 Subject: [PATCH 134/658] core/filtermaps: do not derive full receipts during rendering (#31716) This changes the filtermaps to only pull up the raw receipts, not the derived receipts which saves a lot of allocations. During normal execution this will reduce the allocations of the whole geth node by ~15%. --- core/blockchain_reader.go | 8 ++++++++ core/filtermaps/chain_view.go | 14 ++++++++++++++ core/filtermaps/indexer_test.go | 7 +++++++ core/filtermaps/map_renderer.go | 4 ++-- eth/filters/filter_system_test.go | 7 +++++++ 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index a8c2e26d18b..b4ba5d9fd88 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -234,6 +234,14 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts { return receipts } +func (bc *BlockChain) GetRawReceiptsByHash(hash common.Hash) types.Receipts { + number := rawdb.ReadHeaderNumber(bc.db, hash) + if number == nil { + return nil + } + return rawdb.ReadRawReceipts(bc.db, hash, *number) +} + // GetUnclesInChain retrieves all the uncles from a given block backwards until // a specific distance is reached. func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header { diff --git a/core/filtermaps/chain_view.go b/core/filtermaps/chain_view.go index aa74f3901a4..63df2cfb6d2 100644 --- a/core/filtermaps/chain_view.go +++ b/core/filtermaps/chain_view.go @@ -29,6 +29,7 @@ type blockchain interface { GetHeader(hash common.Hash, number uint64) *types.Header GetCanonicalHash(number uint64) common.Hash GetReceiptsByHash(hash common.Hash) types.Receipts + GetRawReceiptsByHash(hash common.Hash) types.Receipts } // ChainView represents an immutable view of a chain with a block id and a set @@ -102,10 +103,23 @@ func (cv *ChainView) Receipts(number uint64) types.Receipts { blockHash := cv.BlockHash(number) if blockHash == (common.Hash{}) { log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber) + return nil } return cv.chain.GetReceiptsByHash(blockHash) } +// RawReceipts returns the set of receipts belonging to the block at the given +// block number. Does not derive the fields of the receipts, should only be +// used during creation of the filter maps, please use cv.Receipts during querying. +func (cv *ChainView) RawReceipts(number uint64) types.Receipts { + blockHash := cv.BlockHash(number) + if blockHash == (common.Hash{}) { + log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber) + return nil + } + return cv.chain.GetRawReceiptsByHash(blockHash) +} + // SharedRange returns the block range shared by two chain views. func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] { cv.lock.Lock() diff --git a/core/filtermaps/indexer_test.go b/core/filtermaps/indexer_test.go index e60130ba4bd..2782b2cbe6b 100644 --- a/core/filtermaps/indexer_test.go +++ b/core/filtermaps/indexer_test.go @@ -515,6 +515,13 @@ func (tc *testChain) GetReceiptsByHash(hash common.Hash) types.Receipts { return tc.receipts[hash] } +func (tc *testChain) GetRawReceiptsByHash(hash common.Hash) types.Receipts { + tc.lock.RLock() + defer tc.lock.RUnlock() + + return tc.receipts[hash] +} + func (tc *testChain) addBlocks(count, maxTxPerBlock, maxLogsPerReceipt, maxTopicsPerLog int, random bool) { tc.lock.Lock() blockGen := func(i int, gen *core.BlockGen) { diff --git a/core/filtermaps/map_renderer.go b/core/filtermaps/map_renderer.go index f59a01c032a..74baec6a1a5 100644 --- a/core/filtermaps/map_renderer.go +++ b/core/filtermaps/map_renderer.go @@ -693,7 +693,7 @@ func (f *FilterMaps) newLogIteratorFromMapBoundary(mapIndex uint32, startBlock, return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", startBlock, f.targetView.HeadNumber()) } // get block receipts - receipts := f.targetView.Receipts(startBlock) + receipts := f.targetView.RawReceipts(startBlock) if receipts == nil { return nil, fmt.Errorf("receipts not found for start block %d", startBlock) } @@ -760,7 +760,7 @@ func (l *logIterator) next() error { if l.delimiter { l.delimiter = false l.blockNumber++ - l.receipts = l.chainView.Receipts(l.blockNumber) + l.receipts = l.chainView.RawReceipts(l.blockNumber) if l.receipts == nil { return fmt.Errorf("receipts not found for block %d", l.blockNumber) } diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index fa5d4fe8973..122bdaeda42 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -80,6 +80,13 @@ func (b *testBackend) GetReceiptsByHash(hash common.Hash) types.Receipts { return r } +func (b *testBackend) GetRawReceiptsByHash(hash common.Hash) types.Receipts { + if number := rawdb.ReadHeaderNumber(b.db, hash); number != nil { + return rawdb.ReadRawReceipts(b.db, hash, *number) + } + return nil +} + func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { var ( hash common.Hash From 701df4baad3bbdb0bdf4c837f19d25cb07ffc3af Mon Sep 17 00:00:00 2001 From: ericxtheodore Date: Wed, 30 Apr 2025 18:37:48 +0800 Subject: [PATCH 135/658] cmd/geth: fix compatErr in initGenesis (#31746) --- cmd/geth/chaincmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 1c82ac7c099..a947f35f2f6 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -251,7 +251,7 @@ func initGenesis(ctx *cli.Context) error { utils.Fatalf("Failed to write genesis block: %v", err) } if compatErr != nil { - utils.Fatalf("Failed to write chain config: %v", err) + utils.Fatalf("Failed to write chain config: %v", compatErr) } log.Info("Successfully wrote genesis state", "database", "chaindata", "hash", hash) From af9673b143daaa0fbbf5528fe2aae8f2479ab83a Mon Sep 17 00:00:00 2001 From: Shude Li Date: Fri, 2 May 2025 21:19:54 +0800 Subject: [PATCH 136/658] ethclient: fix retrieval of pending block (#31504) Since the block hash is not returned for pending blocks, ethclient cannot unmarshal into RPC block. This makes hash optional on rpc block and compute the hash locally for pending blocks to correctly key the tx sender cache. https://github.com/ethereum/go-ethereum/blob/a82303f4e3cedcebe31540a53dde4f24fc93da80/internal/ethapi/api.go#L500-L504 --------- Co-authored-by: lightclient --- ethclient/ethclient.go | 10 ++++++++-- ethclient/ethclient_test.go | 33 +++++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 352e6abc2c2..9d0e0d5b529 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -131,7 +131,7 @@ func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumb } type rpcBlock struct { - Hash common.Hash `json:"hash"` + Hash *common.Hash `json:"hash"` Transactions []rpcTransaction `json:"transactions"` UncleHashes []common.Hash `json:"uncles"` Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` @@ -158,6 +158,12 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface if err := json.Unmarshal(raw, &body); err != nil { return nil, err } + // Pending blocks don't return a block hash, compute it for sender caching. + if body.Hash == nil { + tmp := head.Hash() + body.Hash = &tmp + } + // Quick-verify transaction and uncle lists. This mostly helps with debugging the server. if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 { return nil, errors.New("server returned non-empty uncle list but block header indicates no uncles") @@ -199,7 +205,7 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface txs := make([]*types.Transaction, len(body.Transactions)) for i, tx := range body.Transactions { if tx.From != nil { - setSenderFromServer(tx.tx, *tx.From, body.Hash) + setSenderFromServer(tx.tx, *tx.From, *body.Hash) } txs[i] = tx.tx } diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 29e311c1b4e..8e70177944b 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -307,6 +307,12 @@ func testTransactionInBlock(t *testing.T, client *rpc.Client) { if tx.Hash() != testTx2.Hash() { t.Fatalf("unexpected transaction: %v", tx) } + + // Test pending block + _, err = ec.BlockByNumber(context.Background(), big.NewInt(int64(rpc.PendingBlockNumber))) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } } func testChainID(t *testing.T, client *rpc.Client) { @@ -619,6 +625,21 @@ func testAtFunctions(t *testing.T, client *rpc.Client) { if gas != 21000 { t.Fatalf("unexpected gas limit: %v", gas) } + + // Verify that sender address of pending transaction is saved in cache. + pendingBlock, err := ec.BlockByNumber(context.Background(), big.NewInt(int64(rpc.PendingBlockNumber))) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + // No additional RPC should be required, ensure the server is not asked by + // canceling the context. + sender, err := ec.TransactionSender(newCanceledContext(), pendingBlock.Transactions()[0], pendingBlock.Hash(), 0) + if err != nil { + t.Fatal("unable to recover sender:", err) + } + if sender != testAddr { + t.Fatal("wrong sender:", sender) + } } func testTransactionSender(t *testing.T, client *rpc.Client) { @@ -640,10 +661,7 @@ func testTransactionSender(t *testing.T, client *rpc.Client) { // The sender address is cached in tx1, so no additional RPC should be required in // TransactionSender. Ensure the server is not asked by canceling the context here. - canceledCtx, cancel := context.WithCancel(context.Background()) - cancel() - <-canceledCtx.Done() // Ensure the close of the Done channel - sender1, err := ec.TransactionSender(canceledCtx, tx1, block2.Hash(), 0) + sender1, err := ec.TransactionSender(newCanceledContext(), tx1, block2.Hash(), 0) if err != nil { t.Fatal(err) } @@ -662,6 +680,13 @@ func testTransactionSender(t *testing.T, client *rpc.Client) { } } +func newCanceledContext() context.Context { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + <-ctx.Done() // Ensure the close of the Done channel + return ctx +} + func sendTransaction(ec *ethclient.Client) error { chainID, err := ec.ChainID(context.Background()) if err != nil { From ed93a5ac04e2234055cf922b45cba579486a46d1 Mon Sep 17 00:00:00 2001 From: Abel <1033309821@qq.com> Date: Fri, 2 May 2025 22:31:50 +0800 Subject: [PATCH 137/658] cmd/devp2p: test for non-existent block request (#31506) Add tests for GetBlockHeaders that verify client does not disconnect when unlikely block numbers are requested, e.g. max uint64. --------- Co-authored-by: lightclient --- cmd/devp2p/internal/ethtest/suite.go | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 8ebbe2a05d3..a16d308dfd4 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -70,6 +70,7 @@ func (s *Suite) EthTests() []utesting.Test { {Name: "Status", Fn: s.TestStatus}, // get block headers {Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders}, + {Name: "GetNonexistentBlockHeaders", Fn: s.TestGetNonexistentBlockHeaders}, {Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests}, {Name: "SameRequestID", Fn: s.TestSameRequestID}, {Name: "ZeroRequestID", Fn: s.TestZeroRequestID}, @@ -158,6 +159,48 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) { } } +func (s *Suite) TestGetNonexistentBlockHeaders(t *utesting.T) { + t.Log(`This test sends GetBlockHeaders requests for nonexistent blocks (using max uint64 value) +to check if the node disconnects after receiving multiple invalid requests.`) + + conn, err := s.dial() + if err != nil { + t.Fatalf("dial failed: %v", err) + } + defer conn.Close() + + if err := conn.peer(s.chain, nil); err != nil { + t.Fatalf("peering failed: %v", err) + } + + // Create request with max uint64 value for a nonexistent block + badReq := ð.GetBlockHeadersPacket{ + GetBlockHeadersRequest: ð.GetBlockHeadersRequest{ + Origin: eth.HashOrNumber{Number: ^uint64(0)}, + Amount: 1, + Skip: 0, + Reverse: false, + }, + } + + // Send request 10 times. Some clients are lient on the first few invalids. + for i := 0; i < 10; i++ { + badReq.RequestId = uint64(i) + if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, badReq); err != nil { + if err == errDisc { + t.Fatalf("peer disconnected after %d requests", i+1) + } + t.Fatalf("write failed: %v", err) + } + } + + // Check if peer disconnects at the end. + code, _, err := conn.Read() + if err == errDisc || code == discMsg { + t.Fatal("peer improperly disconnected") + } +} + func (s *Suite) TestSimultaneousRequests(t *utesting.T) { t.Log(`This test requests blocks headers from the node, performing two requests concurrently, with different request IDs.`) From 86a492471a772fbd9ec71daecac55293f249a364 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 2 May 2025 23:21:17 +0800 Subject: [PATCH 138/658] node: avoid double close resp.Body (#31710) The functions `rpcRequest` and `batchRpcRequest` call `baseRpcRequest`. And `resp.Body` will be closed in the function `baseRpcRequest` later by `t.Cleanup`: ```go func baseRpcRequest(t *testing.T, url, bodyStr string, extraHeaders ...string) *http.Response { // ...... t.Cleanup(func() { resp.Body.Close() }) return resp } ``` --- node/rpcstack_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/node/rpcstack_test.go b/node/rpcstack_test.go index eb0bbac93f0..54e58cccb2b 100644 --- a/node/rpcstack_test.go +++ b/node/rpcstack_test.go @@ -570,7 +570,6 @@ func TestHTTPWriteTimeout(t *testing.T) { // Send normal request t.Run("message", func(t *testing.T) { resp := rpcRequest(t, url, "test_sleep") - defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { t.Fatal(err) @@ -584,7 +583,6 @@ func TestHTTPWriteTimeout(t *testing.T) { t.Run("batch", func(t *testing.T) { want := fmt.Sprintf("[%s,%s,%s]", greetRes, timeoutRes, timeoutRes) resp := batchRpcRequest(t, url, []string{"test_greet", "test_sleep", "test_greet"}) - defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { t.Fatal(err) From 79807bc3b16ee1dadb506c87917fcf042d4e186d Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 2 May 2025 23:43:06 +0800 Subject: [PATCH 139/658] core, eth/gasestimator: introduce MaxGasUsed for estimation (#31735) This PR improves gas estimation for data-heavy transactions which hit the floor data gas cost. --- core/state_transition.go | 26 ++++++++++++++++---------- eth/gasestimator/gasestimator.go | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 0f9ee9eea51..f9c9a2ab5a5 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -34,10 +34,10 @@ import ( // ExecutionResult includes all output after executing given evm // message no matter the execution itself is successful or not. type ExecutionResult struct { - UsedGas uint64 // Total used gas, not including the refunded gas - RefundedGas uint64 // Total gas refunded after execution - Err error // Any error encountered during the execution(listed in core/vm/errors.go) - ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) + UsedGas uint64 // Total used gas, not including the refunded gas + MaxUsedGas uint64 // Maximum gas consumed during execution, excluding gas refunds. + Err error // Any error encountered during the execution(listed in core/vm/errors.go) + ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) } // Unwrap returns the internal evm error which allows us for further @@ -509,9 +509,12 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { ret, st.gasRemaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value) } + // Record the gas used excluding gas refunds. This value represents the actual + // gas allowance required to complete execution. + peakGasUsed := st.gasUsed() + // Compute refund counter, capped to a refund quotient. - gasRefund := st.calcRefund() - st.gasRemaining += gasRefund + st.gasRemaining += st.calcRefund() if rules.IsPrague { // After EIP-7623: Data-heavy transactions pay the floor gas. if st.gasUsed() < floorDataGas { @@ -521,6 +524,9 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { t.OnGasChange(prev, st.gasRemaining, tracing.GasChangeTxDataFloor) } } + if peakGasUsed < floorDataGas { + peakGasUsed = floorDataGas + } } st.returnGas() @@ -549,10 +555,10 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { } return &ExecutionResult{ - UsedGas: st.gasUsed(), - RefundedGas: gasRefund, - Err: vmerr, - ReturnData: ret, + UsedGas: st.gasUsed(), + MaxUsedGas: peakGasUsed, + Err: vmerr, + ReturnData: ret, }, nil } diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index fc8e3a2e424..98a4f74b3e4 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -144,7 +144,7 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin // There's a fairly high chance for the transaction to execute successfully // with gasLimit set to the first execution's usedGas + gasRefund. Explicitly // check that gas amount and use as a limit for the binary search. - optimisticGasLimit := (result.UsedGas + result.RefundedGas + params.CallStipend) * 64 / 63 + optimisticGasLimit := (result.MaxUsedGas + params.CallStipend) * 64 / 63 if optimisticGasLimit < hi { failed, _, err = execute(ctx, call, opts, optimisticGasLimit) if err != nil { From 341929ab962c46b910ce3bb4a0f4d22f2048b1c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Fri, 2 May 2025 17:50:22 +0200 Subject: [PATCH 140/658] core/filtermaps: fix log value search range (#31734) This PR fixes the out-of-range block number logic of `getBlockLvPointer` which sometimes caused searches to fail if the head was updated in the wrong moment. This logic ensures that querying the pointer of a future block returns the pointer after the last fully indexed block (instead of failing) and therefore an async range update will not cause the search to fail. Earier this behaviour only worked when `headIndexed` was true and `headDelimiter` pointed to the end of the indexed range. Now it also works for an unfinished index. This logic is also moved from `FilterMaps.getBlockLvPointer` to `FilterMapsMatcherBackend.GetBlockLvPointer` because it is only required by the search anyways. `FilterMaps.getBlockLvPointer` now only returns a pointer for existing blocks, consistently with how it is used in the indexer/renderer. Note that this unhandled case has been present in the code for a long time but went unnoticed because either one of two previously fixed bugs did prevent it from being triggered; the incorrectly positive `tempRange.headIndexed` (fixed in https://github.com/ethereum/go-ethereum/pull/31680), though caused other problems, prevented this one from being triggered as with a positive `headIndexed` no database read was triggered in `getBlockLvPointer`. Also, the unnecessary `indexLock` in `synced()` (fixed in https://github.com/ethereum/go-ethereum/pull/31708) usually did prevent the search seeing the temp range and therefore avoided noticeable issues. --- core/filtermaps/filtermaps.go | 6 +----- core/filtermaps/matcher_backend.go | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index 3da7f4b721b..ffe2bfcbb62 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -662,15 +662,11 @@ func (f *FilterMaps) mapRowIndex(mapIndex, rowIndex uint32) uint64 { } // getBlockLvPointer returns the starting log value index where the log values -// generated by the given block are located. If blockNumber is beyond the current -// head then the first unoccupied log value index is returned. +// generated by the given block are located. // // 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.blocks.AfterLast() && f.indexedRange.headIndexed { - return f.indexedRange.headDelimiter + 1, nil - } if lvPointer, ok := f.lvPointerCache.Get(blockNumber); ok { return lvPointer, nil } diff --git a/core/filtermaps/matcher_backend.go b/core/filtermaps/matcher_backend.go index 9751783754f..e19a63e18b8 100644 --- a/core/filtermaps/matcher_backend.go +++ b/core/filtermaps/matcher_backend.go @@ -82,13 +82,26 @@ func (fm *FilterMapsMatcherBackend) GetFilterMapRow(ctx context.Context, mapInde } // GetBlockLvPointer returns the starting log value index where the log values -// generated by the given block are located. If blockNumber is beyond the current -// head then the first unoccupied log value index is returned. +// generated by the given block are located. If blockNumber is beyond the last +// indexed block then the pointer will point right after this block, ensuring +// that the matcher does not fail and can return a set of results where the +// valid range is correct. // GetBlockLvPointer implements MatcherBackend. func (fm *FilterMapsMatcherBackend) GetBlockLvPointer(ctx context.Context, blockNumber uint64) (uint64, error) { fm.f.indexLock.RLock() defer fm.f.indexLock.RUnlock() + if blockNumber >= fm.f.indexedRange.blocks.AfterLast() { + if fm.f.indexedRange.headIndexed { + // return index after head block + return fm.f.indexedRange.headDelimiter + 1, nil + } + if fm.f.indexedRange.blocks.Count() > 0 { + // return index at the beginning of the last, partially indexed + // block (after the last fully indexed one) + blockNumber = fm.f.indexedRange.blocks.Last() + } + } return fm.f.getBlockLvPointer(blockNumber) } From 8868ad6d6e09252238fefa303f7262e2b855f4c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Sat, 3 May 2025 18:40:24 +0200 Subject: [PATCH 141/658] core/filtermaps: fix log index initialization (#31750) This PR fixes an initialization bug that in some cases caused the map renderer to leave the last, partially rendered map as is and resume rendering from the next map. At initialization we check whether the existing rendered maps are consistent with the current chain view and revert them if necessary. Until now this happened through an ugly hacky solution, a "limited" chain view that was supposed to trigger a rollback of some maps in the renderer logic if necessary. This whole setup worked under assumptions that just weren't true any more. As a result it always tried to revert the last map but also it did not shorten the indexed range, only set `headIndexed` to false which indicated to the renderer logic that the last map is fully populated (which it wasn't). Now an explicit rollback of any unusable (reorged) maps happens at startup, which also means that no hacky chain view is necessary, as soon as the new `FilterMaps` is returned, the indexed range and view are consistent with each other. In the first commit an extra check is also added to `writeFinishedMaps` so that if there is ever again a bug that would result in a gapped index then it will not break the db with writing the incomplete data. Instead it will return an indexing error which causes the indexer to revert to unindexed mode and print an error log instantly. Hopefully this will not ever happen in the future, but in order to test this safeguard check I manually triggered the bug with only the first commit enabled, which caused an indexing error as expected. With the second commit added (the actual fix) the same operation succeeded without any issues. Note that the database version is also bumped in this PR in order to enforce a full reindexing as any existing database might be potentially broken. Fixes https://github.com/ethereum/go-ethereum/issues/31729 --- core/filtermaps/chain_view.go | 8 ----- core/filtermaps/filtermaps.go | 63 ++++++++++++++++++--------------- core/filtermaps/map_renderer.go | 18 +++++++--- 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/core/filtermaps/chain_view.go b/core/filtermaps/chain_view.go index 63df2cfb6d2..874ff19e31e 100644 --- a/core/filtermaps/chain_view.go +++ b/core/filtermaps/chain_view.go @@ -135,14 +135,6 @@ func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] { return common.NewRange(0, sharedLen) } -// limitedView returns a new chain view that is a truncated version of the parent view. -func (cv *ChainView) limitedView(newHead uint64) *ChainView { - if newHead >= cv.headNumber { - return cv - } - return NewChainView(cv.chain, newHead, cv.BlockHash(newHead)) -} - // equalViews returns true if the two chain views are equivalent. func equalViews(cv1, cv2 *ChainView) bool { if cv1 == nil || cv2 == nil { diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index ffe2bfcbb62..b2c63f2e581 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -50,7 +50,7 @@ var ( ) const ( - databaseVersion = 1 // reindexed if database version does not match + databaseVersion = 2 // reindexed if database version does not match cachedLastBlocks = 1000 // last block of map pointers cachedLvPointers = 1000 // first log value pointer of block pointers cachedBaseRows = 100 // groups of base layer filter row data @@ -244,6 +244,8 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f disabledCh: make(chan struct{}), exportFileName: config.ExportFileName, Params: params, + targetView: initView, + indexedView: initView, indexedRange: filterMapsRange{ initialized: initialized, headIndexed: rs.HeadIndexed, @@ -265,16 +267,8 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows), renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots), } + f.checkRevertRange() // revert maps that are inconsistent with the current chain view - // Set initial indexer target. - f.targetView = initView - if f.indexedRange.initialized { - f.indexedView = f.initChainView(f.targetView) - 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.blocks.First(), "last block", f.indexedRange.blocks.Last(), @@ -303,29 +297,40 @@ func (f *FilterMaps) Stop() { f.closeWg.Wait() } -// initChainView returns a chain view consistent with both the current target -// view and the current state of the log index as found in the database, based -// on the last block of stored maps. -// 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.maps.AfterLast() - for { - var ok bool - mapIndex, ok = f.lastMapBoundaryBefore(mapIndex) - if !ok { - break +// checkRevertRange checks whether the existing index is consistent with the +// current indexed view and reverts inconsistent maps if necessary. +func (f *FilterMaps) checkRevertRange() { + if f.indexedRange.maps.Count() == 0 { + return + } + lastMap := f.indexedRange.maps.Last() + lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(lastMap) + if err != nil { + log.Error("Error initializing log index database; resetting log index", "error", err) + f.reset() + return + } + for lastBlockNumber > f.indexedView.HeadNumber() || f.indexedView.BlockId(lastBlockNumber) != lastBlockId { + // revert last map + if f.indexedRange.maps.Count() == 1 { + f.reset() // reset database if no rendered maps remained + return } - lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(mapIndex) + lastMap-- + newRange := f.indexedRange + newRange.maps.SetLast(lastMap) + lastBlockNumber, lastBlockId, err = f.getLastBlockOfMap(lastMap) if err != nil { - log.Error("Could not initialize indexed chain view", "error", err) - break - } - if lastBlockNumber <= chainView.HeadNumber() && chainView.BlockId(lastBlockNumber) == lastBlockId { - return chainView.limitedView(lastBlockNumber) + log.Error("Error initializing log index database; resetting log index", "error", err) + f.reset() + return } + newRange.blocks.SetAfterLast(lastBlockNumber) // lastBlockNumber is probably partially indexed + newRange.headIndexed = false + newRange.headDelimiter = 0 + // only shorten range and leave map data; next head render will overwrite it + f.setRange(f.db, f.indexedView, newRange, false) } - return chainView.limitedView(0) } // reset un-initializes the FilterMaps structure and removes all related data from diff --git a/core/filtermaps/map_renderer.go b/core/filtermaps/map_renderer.go index 74baec6a1a5..5379cbb1579 100644 --- a/core/filtermaps/map_renderer.go +++ b/core/filtermaps/map_renderer.go @@ -468,15 +468,25 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error { r.f.filterMapCache.Remove(mapIndex) } } + var blockNumber uint64 + if r.finished.First() > 0 { + // in order to always ensure continuous block pointers, initialize + // blockNumber based on the last block of the previous map, then verify + // against the first block associated with each rendered map + lastBlock, _, err := r.f.getLastBlockOfMap(r.finished.First() - 1) + if err != nil { + return fmt.Errorf("failed to get last block of previous map %d: %v", r.finished.First()-1, err) + } + blockNumber = lastBlock + 1 + } // add or update block pointers - 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() if blockNumber != renderedMap.firstBlock() { - panic("non-continuous block numbers") + return fmt.Errorf("non-continuous block numbers in rendered map %d (next expected: %d first rendered: %d)", mapIndex, blockNumber, renderedMap.firstBlock()) } + r.f.storeLastBlockOfMap(batch, mapIndex, renderedMap.lastBlock, renderedMap.lastBlockId) + checkWriteCnt() for _, lvPtr := range renderedMap.blockLvPtrs { r.f.storeBlockLvPointer(batch, blockNumber, lvPtr) checkWriteCnt() From 2d86a54000be027286145f7aec36dd78fadcf070 Mon Sep 17 00:00:00 2001 From: Miro Date: Sat, 3 May 2025 22:16:33 -0400 Subject: [PATCH 142/658] core/txpool/legacypool: fix data race of pricedList access (#31758) --- core/txpool/legacypool/legacypool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index affe44cf060..0223b456e65 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -1934,7 +1934,7 @@ func (pool *LegacyPool) Clear() { pool.reserver.Release(addr) } pool.all.Clear() - pool.priced = newPricedList(pool.all) + pool.priced.Reheap() pool.pending = make(map[common.Address]*list) pool.queue = make(map[common.Address]*list) pool.pendingNonces = newNoncer(pool.currentState) From 516451dc3a514c7c122f28864ea76742a027b858 Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Sun, 4 May 2025 20:40:31 +0800 Subject: [PATCH 143/658] params: fix comment for `DefaultBlobSchedule` (#31760) `DefaultBlobSchedule` is actually used downstream to calculate blob fees (e.g., [src](https://github.com/ethereum-optimism/optimism/blob/601a380e47853c2922ea1f8944cda05f0eac16f4/op-service/eth/blob.go#L301)), this PR makes it explicit that these params are for `Ethereum prod` instead of `test chains`. --- params/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/params/config.go b/params/config.go index 67aa6b22251..2e825ffcd56 100644 --- a/params/config.go +++ b/params/config.go @@ -358,7 +358,7 @@ var ( Max: 9, UpdateFraction: 5007716, } - // DefaultBlobSchedule is the latest configured blob schedule for test chains. + // DefaultBlobSchedule is the latest configured blob schedule for Ethereum mainnet. DefaultBlobSchedule = &BlobScheduleConfig{ Cancun: DefaultCancunBlobConfig, Prague: DefaultPragueBlobConfig, From 615d29f7c2d5aed84cf8c7ec952d9f9a9f706e4b Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Mon, 5 May 2025 04:07:55 +0200 Subject: [PATCH 144/658] core: reduce load on txindexer from API (#31752) Fixes https://github.com/ethereum/go-ethereum/issues/31732. This logic was removed in the recent refactoring in the txindexer to handle history cutoff (#31393). It was first introduced in this PR: https://github.com/ethereum/go-ethereum/pull/28908. I have tested it and it works as an alternative to #31745. This PR packs 3 changes to the flow of fetching txs from the API: - It caches the indexer tail after each run is over to avoid hitting the db all the time as was done originally in #28908. - Changes `backend.GetTransaction`. It doesn't return an error anymore when tx indexer is in progress. It shifts the responsibility to the caller to check the progress. The reason is that in most cases we anyway check the txpool for the tx. If it was indeed a pending tx we can avoid the indexer progress check. --------- Co-authored-by: Gary Rong --- core/blockchain_reader.go | 54 ++++++++---------- core/txindexer.go | 72 ++++++++++++++---------- core/txindexer_test.go | 22 +++----- eth/api_backend.go | 28 +++++---- eth/tracers/api.go | 14 +++-- eth/tracers/api_test.go | 8 ++- ethstats/ethstats.go | 6 +- graphql/graphql.go | 6 +- internal/ethapi/api.go | 46 ++++++++------- internal/ethapi/api_test.go | 11 +++- internal/ethapi/backend.go | 5 +- internal/ethapi/transaction_args_test.go | 9 ++- 12 files changed, 150 insertions(+), 131 deletions(-) diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index b4ba5d9fd88..fefeb375424 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -270,42 +270,20 @@ func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, max // GetTransactionLookup retrieves the lookup along with the transaction // itself associate with the given transaction hash. // -// An error will be returned if the transaction is not found, and background -// indexing for transactions is still in progress. The transaction might be -// reachable shortly once it's indexed. -// -// A null will be returned in the transaction is not found and background -// transaction indexing is already finished. The transaction is not existent -// from the node's perspective. -func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) { +// A null will be returned if the transaction is not found. This can be due to +// the transaction indexer not being finished. The caller must explicitly check +// the indexer progress. +func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction) { bc.txLookupLock.RLock() defer bc.txLookupLock.RUnlock() // Short circuit if the txlookup already in the cache, retrieve otherwise if item, exist := bc.txLookupCache.Get(hash); exist { - return item.lookup, item.transaction, nil + return item.lookup, item.transaction } tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash) if tx == nil { - progress, err := bc.TxIndexProgress() - if err != nil { - // No error is returned if the transaction indexing progress is unreachable - // due to unexpected internal errors. In such cases, it is impossible to - // determine whether the transaction does not exist or has simply not been - // indexed yet without a progress marker. - // - // In such scenarios, the transaction is treated as unreachable, though - // this is clearly an unintended and unexpected situation. - return nil, nil, nil - } - // The transaction indexing is not finished yet, returning an - // error to explicitly indicate it. - if !progress.Done() { - return nil, nil, errors.New("transaction indexing still in progress") - } - // The transaction is already indexed, the transaction is either - // not existent or not in the range of index, returning null. - return nil, nil, nil + return nil, nil } lookup := &rawdb.LegacyTxLookupEntry{ BlockHash: blockHash, @@ -316,7 +294,23 @@ func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLoo lookup: lookup, transaction: tx, }) - return lookup, tx, nil + return lookup, tx +} + +// TxIndexDone returns true if the transaction indexer has finished indexing. +func (bc *BlockChain) TxIndexDone() bool { + progress, err := bc.TxIndexProgress() + if err != nil { + // No error is returned if the transaction indexing progress is unreachable + // due to unexpected internal errors. In such cases, it is impossible to + // determine whether the transaction does not exist or has simply not been + // indexed yet without a progress marker. + // + // In such scenarios, the transaction is treated as unreachable, though + // this is clearly an unintended and unexpected situation. + return true + } + return progress.Done() } // HasState checks if state trie is fully present in the database or not. @@ -412,7 +406,7 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) { if bc.txIndexer == nil { return TxIndexProgress{}, errors.New("tx indexer is not enabled") } - return bc.txIndexer.txIndexProgress() + return bc.txIndexer.txIndexProgress(), nil } // HistoryPruningCutoff returns the configured history pruning point. diff --git a/core/txindexer.go b/core/txindexer.go index 64a2e8c49f5..587118ed7f3 100644 --- a/core/txindexer.go +++ b/core/txindexer.go @@ -17,8 +17,8 @@ package core import ( - "errors" "fmt" + "sync/atomic" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" @@ -47,26 +47,38 @@ type txIndexer struct { // and all others shouldn't. limit uint64 + // The current head of blockchain for transaction indexing. This field + // is accessed by both the indexer and the indexing progress queries. + head atomic.Uint64 + + // The current tail of the indexed transactions, null indicates + // that no transactions have been indexed yet. + // + // This field is accessed by both the indexer and the indexing + // progress queries. + tail atomic.Pointer[uint64] + // cutoff denotes the block number before which the chain segment should // be pruned and not available locally. - cutoff uint64 - db ethdb.Database - progress chan chan TxIndexProgress - term chan chan struct{} - closed chan struct{} + cutoff uint64 + db ethdb.Database + term chan chan struct{} + closed chan struct{} } // newTxIndexer initializes the transaction indexer. func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer { cutoff, _ := chain.HistoryPruningCutoff() indexer := &txIndexer{ - limit: limit, - cutoff: cutoff, - db: chain.db, - progress: make(chan chan TxIndexProgress), - term: make(chan chan struct{}), - closed: make(chan struct{}), + limit: limit, + cutoff: cutoff, + db: chain.db, + term: make(chan chan struct{}), + closed: make(chan struct{}), } + indexer.head.Store(indexer.resolveHead()) + indexer.tail.Store(rawdb.ReadTxIndexTail(chain.db)) + go indexer.loop(chain) var msg string @@ -154,6 +166,7 @@ func (indexer *txIndexer) repair(head uint64) { // A crash may occur between the two delete operations, // potentially leaving dangling indexes in the database. // However, this is considered acceptable. + indexer.tail.Store(nil) rawdb.DeleteTxIndexTail(indexer.db) rawdb.DeleteAllTxLookupEntries(indexer.db, nil) log.Warn("Purge transaction indexes", "head", head, "tail", *tail) @@ -174,6 +187,7 @@ func (indexer *txIndexer) repair(head uint64) { // Traversing the database directly within the transaction // index namespace might be slow and expensive, but we // have no choice. + indexer.tail.Store(nil) rawdb.DeleteTxIndexTail(indexer.db) rawdb.DeleteAllTxLookupEntries(indexer.db, nil) log.Warn("Purge transaction indexes", "head", head, "cutoff", indexer.cutoff) @@ -187,6 +201,7 @@ func (indexer *txIndexer) repair(head uint64) { // A crash may occur between the two delete operations, // potentially leaving dangling indexes in the database. // However, this is considered acceptable. + indexer.tail.Store(&indexer.cutoff) rawdb.WriteTxIndexTail(indexer.db, indexer.cutoff) rawdb.DeleteAllTxLookupEntries(indexer.db, func(txhash common.Hash, blob []byte) bool { n := rawdb.DecodeTxLookupEntry(blob, indexer.db) @@ -216,16 +231,15 @@ func (indexer *txIndexer) loop(chain *BlockChain) { // Listening to chain events and manipulate the transaction indexes. var ( - stop chan struct{} // Non-nil if background routine is active - done chan struct{} // Non-nil if background routine is active - head = indexer.resolveHead() // The latest announced chain head - + stop chan struct{} // Non-nil if background routine is active + done chan struct{} // Non-nil if background routine is active headCh = make(chan ChainHeadEvent) sub = chain.SubscribeChainHeadEvent(headCh) ) defer sub.Unsubscribe() // Validate the transaction indexes and repair if necessary + head := indexer.head.Load() indexer.repair(head) // Launch the initial processing if chain is not empty (head != genesis). @@ -238,17 +252,18 @@ func (indexer *txIndexer) loop(chain *BlockChain) { for { select { case h := <-headCh: + indexer.head.Store(h.Header.Number.Uint64()) if done == nil { stop = make(chan struct{}) done = make(chan struct{}) go indexer.run(h.Header.Number.Uint64(), stop, done) } - head = h.Header.Number.Uint64() + case <-done: stop = nil done = nil - case ch := <-indexer.progress: - ch <- indexer.report(head) + indexer.tail.Store(rawdb.ReadTxIndexTail(indexer.db)) + case ch := <-indexer.term: if stop != nil { close(stop) @@ -264,7 +279,7 @@ func (indexer *txIndexer) loop(chain *BlockChain) { } // report returns the tx indexing progress. -func (indexer *txIndexer) report(head uint64) TxIndexProgress { +func (indexer *txIndexer) report(head uint64, tail *uint64) TxIndexProgress { // Special case if the head is even below the cutoff, // nothing to index. if head < indexer.cutoff { @@ -284,7 +299,6 @@ func (indexer *txIndexer) report(head uint64) TxIndexProgress { } // Compute how many blocks have been indexed var indexed uint64 - tail := rawdb.ReadTxIndexTail(indexer.db) if tail != nil { indexed = head - *tail + 1 } @@ -300,16 +314,12 @@ func (indexer *txIndexer) report(head uint64) TxIndexProgress { } } -// txIndexProgress retrieves the tx indexing progress, or an error if the -// background tx indexer is already stopped. -func (indexer *txIndexer) txIndexProgress() (TxIndexProgress, error) { - ch := make(chan TxIndexProgress, 1) - select { - case indexer.progress <- ch: - return <-ch, nil - case <-indexer.closed: - return TxIndexProgress{}, errors.New("indexer is closed") - } +// txIndexProgress retrieves the transaction indexing progress. The reported +// progress may slightly lag behind the actual indexing state, as the tail is +// only updated at the end of each indexing operation. However, this delay is +// considered acceptable. +func (indexer *txIndexer) txIndexProgress() TxIndexProgress { + return indexer.report(indexer.head.Load(), indexer.tail.Load()) } // close shutdown the indexer. Safe to be called for multiple times. diff --git a/core/txindexer_test.go b/core/txindexer_test.go index 7a5688241f0..6543ff429dc 100644 --- a/core/txindexer_test.go +++ b/core/txindexer_test.go @@ -121,9 +121,8 @@ func TestTxIndexer(t *testing.T) { // Index the initial blocks from ancient store indexer := &txIndexer{ - limit: 0, - db: db, - progress: make(chan chan TxIndexProgress), + limit: 0, + db: db, } for i, limit := range c.limits { indexer.limit = limit @@ -241,9 +240,8 @@ func TestTxIndexerRepair(t *testing.T) { // Index the initial blocks from ancient store indexer := &txIndexer{ - limit: c.limit, - db: db, - progress: make(chan chan TxIndexProgress), + limit: c.limit, + db: db, } indexer.run(chainHead, make(chan struct{}), make(chan struct{})) @@ -432,15 +430,11 @@ func TestTxIndexerReport(t *testing.T) { // Index the initial blocks from ancient store indexer := &txIndexer{ - limit: c.limit, - cutoff: c.cutoff, - db: db, - progress: make(chan chan TxIndexProgress), + limit: c.limit, + cutoff: c.cutoff, + db: db, } - if c.tail != nil { - rawdb.WriteTxIndexTail(db, *c.tail) - } - p := indexer.report(c.head) + p := indexer.report(c.head, c.tail) if p.Indexed != c.expIndexed { t.Fatalf("Unexpected indexed: %d, expected: %d", p.Indexed, c.expIndexed) } diff --git a/eth/api_backend.go b/eth/api_backend.go index 10f7ffcbce7..57f5a508374 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -349,22 +349,20 @@ func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction // GetTransaction retrieves the lookup along with the transaction itself associate // with the given transaction hash. // -// An error will be returned if the transaction is not found, and background -// indexing for transactions is still in progress. The error is used to indicate the -// scenario explicitly that the transaction might be reachable shortly. -// -// A null will be returned in the transaction is not found and background transaction -// indexing is already finished. The transaction is not existent from the perspective -// of node. -func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) { - lookup, tx, err := b.eth.blockchain.GetTransactionLookup(txHash) - if err != nil { - return false, nil, common.Hash{}, 0, 0, err - } +// A null will be returned if the transaction is not found. The transaction is not +// existent from the node's perspective. This can be due to the transaction indexer +// not being finished. The caller must explicitly check the indexer progress. +func (b *EthAPIBackend) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) { + lookup, tx := b.eth.blockchain.GetTransactionLookup(txHash) if lookup == nil || tx == nil { - return false, nil, common.Hash{}, 0, 0, nil + return false, nil, common.Hash{}, 0, 0 } - return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index, nil + return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index +} + +// TxIndexDone returns true if the transaction indexer has finished indexing. +func (b *EthAPIBackend) TxIndexDone() bool { + return b.eth.blockchain.TxIndexDone() } func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { @@ -391,7 +389,7 @@ func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.S return b.eth.txPool.SubscribeTransactions(ch, true) } -func (b *EthAPIBackend) SyncProgress() ethereum.SyncProgress { +func (b *EthAPIBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress { prog := b.eth.Downloader().Progress() if txProg, err := b.eth.blockchain.TxIndexProgress(); err == nil { prog.TxIndexFinishedBlocks = txProg.Indexed diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 3cb86c80e26..17a0ad687a8 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -82,7 +82,8 @@ type Backend interface { HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) - GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) + GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) + TxIndexDone() bool RPCGasCap() uint64 ChainConfig() *params.ChainConfig Engine() consensus.Engine @@ -858,12 +859,13 @@ func containsTx(block *types.Block, hash common.Hash) bool { // TraceTransaction returns the structured logs created during the execution of EVM // and returns them as a JSON object. func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { - found, _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) - if err != nil { - return nil, ethapi.NewTxIndexingError() - } - // Only mined txes are supported + found, _, blockHash, blockNumber, index := api.backend.GetTransaction(hash) if !found { + // Warn in case tx indexer is not done. + if !api.backend.TxIndexDone() { + return nil, ethapi.NewTxIndexingError() + } + // Only mined txes are supported return nil, errTxNotFound } // It shouldn't happen in practice. diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 529448e3972..fa391876942 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -116,9 +116,13 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) return b.chain.GetBlockByNumber(uint64(number)), nil } -func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) { +func (b *testBackend) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) { tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash) - return tx != nil, tx, hash, blockNumber, index, nil + return tx != nil, tx, hash, blockNumber, index +} + +func (b *testBackend) TxIndexDone() bool { + return true } func (b *testBackend) RPCGasCap() uint64 { diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index 0090a7d4c1d..b6191baa12b 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -66,7 +66,7 @@ type backend interface { CurrentHeader() *types.Header HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) Stats() (pending int, queued int) - SyncProgress() ethereum.SyncProgress + SyncProgress(ctx context.Context) ethereum.SyncProgress } // fullNodeBackend encompasses the functionality necessary for a full node @@ -766,7 +766,7 @@ func (s *Service) reportStats(conn *connWrapper) error { ) // check if backend is a full node if fullBackend, ok := s.backend.(fullNodeBackend); ok { - sync := fullBackend.SyncProgress() + sync := fullBackend.SyncProgress(context.Background()) syncing = !sync.Done() price, _ := fullBackend.SuggestGasTipCap(context.Background()) @@ -775,7 +775,7 @@ func (s *Service) reportStats(conn *connWrapper) error { gasprice += int(basefee.Uint64()) } } else { - sync := s.backend.SyncProgress() + sync := s.backend.SyncProgress(context.Background()) syncing = !sync.Done() } // Assemble the node stats and send it to the server diff --git a/graphql/graphql.go b/graphql/graphql.go index 7af1adbb4a8..e23e6fcb0e6 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -229,7 +229,7 @@ func (t *Transaction) resolve(ctx context.Context) (*types.Transaction, *Block) return t.tx, t.block } // Try to return an already finalized transaction - found, tx, blockHash, _, index, _ := t.r.backend.GetTransaction(ctx, t.hash) + found, tx, blockHash, _, index := t.r.backend.GetTransaction(t.hash) if found { t.tx = tx blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false) @@ -1530,8 +1530,8 @@ func (s *SyncState) TxIndexRemainingBlocks() hexutil.Uint64 { // - healingBytecode: number of bytecodes pending // - txIndexFinishedBlocks: number of blocks whose transactions are indexed // - txIndexRemainingBlocks: number of blocks whose transactions are not indexed yet -func (r *Resolver) Syncing() (*SyncState, error) { - progress := r.backend.SyncProgress() +func (r *Resolver) Syncing(ctx context.Context) (*SyncState, error) { + progress := r.backend.SyncProgress(ctx) // Return not syncing if the synchronisation already completed if progress.Done() { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 3b699748b89..8f736226c71 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -144,8 +144,8 @@ func (api *EthereumAPI) BlobBaseFee(ctx context.Context) *hexutil.Big { // - highestBlock: block number of the highest block header this node has received from peers // - pulledStates: number of state entries processed until now // - knownStates: number of known state entries that still need to be pulled -func (api *EthereumAPI) Syncing() (interface{}, error) { - progress := api.b.SyncProgress() +func (api *EthereumAPI) Syncing(ctx context.Context) (interface{}, error) { + progress := api.b.SyncProgress(ctx) // Return not syncing if the synchronisation already completed if progress.Done() { @@ -1333,16 +1333,18 @@ func (api *TransactionAPI) GetTransactionCount(ctx context.Context, address comm // GetTransactionByHash returns the transaction for the given hash func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) { // Try to return an already finalized transaction - found, tx, blockHash, blockNumber, index, err := api.b.GetTransaction(ctx, hash) + found, tx, blockHash, blockNumber, index := api.b.GetTransaction(hash) if !found { // No finalized transaction, try to retrieve it from the pool if tx := api.b.GetPoolTransaction(hash); tx != nil { return NewRPCPendingTransaction(tx, api.b.CurrentHeader(), api.b.ChainConfig()), nil } - if err == nil { - return nil, nil + // If also not in the pool there is a chance the tx indexer is still in progress. + if !api.b.TxIndexDone() { + return nil, NewTxIndexingError() } - return nil, NewTxIndexingError() + // If the transaction is not found in the pool and the indexer is done, return nil + return nil, nil } header, err := api.b.HeaderByHash(ctx, blockHash) if err != nil { @@ -1354,27 +1356,31 @@ func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common // GetRawTransactionByHash returns the bytes of the transaction for the given hash. func (api *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { // Retrieve a finalized transaction, or a pooled otherwise - found, tx, _, _, _, err := api.b.GetTransaction(ctx, hash) + found, tx, _, _, _ := api.b.GetTransaction(hash) if !found { if tx = api.b.GetPoolTransaction(hash); tx != nil { return tx.MarshalBinary() } - if err == nil { - return nil, nil + // If also not in the pool there is a chance the tx indexer is still in progress. + if !api.b.TxIndexDone() { + return nil, NewTxIndexingError() } - return nil, NewTxIndexingError() + // If the transaction is not found in the pool and the indexer is done, return nil + return nil, nil } return tx.MarshalBinary() } // GetTransactionReceipt returns the transaction receipt for the given transaction hash. func (api *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) { - found, tx, blockHash, blockNumber, index, err := api.b.GetTransaction(ctx, hash) - if err != nil { - return nil, NewTxIndexingError() // transaction is not fully indexed - } + found, tx, blockHash, blockNumber, index := api.b.GetTransaction(hash) if !found { - return nil, nil // transaction is not existent or reachable + // Make sure indexer is done. + if !api.b.TxIndexDone() { + return nil, NewTxIndexingError() + } + // No such tx. + return nil, nil } header, err := api.b.HeaderByHash(ctx, blockHash) if err != nil { @@ -1774,15 +1780,17 @@ func (api *DebugAPI) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.Block // GetRawTransaction returns the bytes of the transaction for the given hash. func (api *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { // Retrieve a finalized transaction, or a pooled otherwise - found, tx, _, _, _, err := api.b.GetTransaction(ctx, hash) + found, tx, _, _, _ := api.b.GetTransaction(hash) if !found { if tx = api.b.GetPoolTransaction(hash); tx != nil { return tx.MarshalBinary() } - if err == nil { - return nil, nil + // If also not in the pool there is a chance the tx indexer is still in progress. + if !api.b.TxIndexDone() { + return nil, NewTxIndexingError() } - return nil, NewTxIndexingError() + // Transaction is not found in the pool and the indexer is done. + return nil, nil } return tx.MarshalBinary() } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 5071e2412f0..ef799d99945 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -472,7 +472,9 @@ func (b *testBackend) setPendingBlock(block *types.Block) { b.pending = block } -func (b testBackend) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} } +func (b testBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress { + return ethereum.SyncProgress{} +} func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { return big.NewInt(0), nil } @@ -589,9 +591,12 @@ func (b testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) even func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { panic("implement me") } -func (b testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) { +func (b testBackend) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) { tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.db, txHash) - return true, tx, blockHash, blockNumber, index, nil + return true, tx, blockHash, blockNumber, index +} +func (b testBackend) TxIndexDone() bool { + return true } func (b testBackend) GetPoolTransactions() (types.Transactions, error) { panic("implement me") } func (b testBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { panic("implement me") } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index e28cb932962..49c3a37560f 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -41,7 +41,7 @@ import ( // both full and light clients) with access to necessary functions. type Backend interface { // General Ethereum API - SyncProgress() ethereum.SyncProgress + SyncProgress(ctx context.Context) ethereum.SyncProgress SuggestGasTipCap(ctx context.Context) (*big.Int, error) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) @@ -74,7 +74,8 @@ type Backend interface { // Transaction pool API SendTx(ctx context.Context, signedTx *types.Transaction) error - GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) + GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) + TxIndexDone() bool GetPoolTransactions() (types.Transactions, error) GetPoolTransaction(txHash common.Hash) *types.Transaction GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 9dd6a547290..9b86e452a5b 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -323,7 +323,9 @@ func (b *backendMock) CurrentHeader() *types.Header { return b.current } func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config } // Other methods needed to implement Backend interface. -func (b *backendMock) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} } +func (b *backendMock) SyncProgress(ctx context.Context) ethereum.SyncProgress { + return ethereum.SyncProgress{} +} func (b *backendMock) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) { return nil, nil, nil, nil, nil, nil, nil } @@ -378,9 +380,10 @@ func (b *backendMock) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) eve return nil } func (b *backendMock) SendTx(ctx context.Context, signedTx *types.Transaction) error { return nil } -func (b *backendMock) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) { - return false, nil, [32]byte{}, 0, 0, nil +func (b *backendMock) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) { + return false, nil, [32]byte{}, 0, 0 } +func (b *backendMock) TxIndexDone() bool { return true } func (b *backendMock) GetPoolTransactions() (types.Transactions, error) { return nil, nil } func (b *backendMock) GetPoolTransaction(txHash common.Hash) *types.Transaction { return nil } func (b *backendMock) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { From 1b18ba24235127e172a797f35dc0913d0330a1ba Mon Sep 17 00:00:00 2001 From: Marcel <153717436+MonkeyMarcel@users.noreply.github.com> Date: Mon, 5 May 2025 10:09:58 +0800 Subject: [PATCH 145/658] logs(indexer)Clean up log format in head index progress messages (#31761) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates the log entries in `core/filtermaps/indexer.go` to remove double quotes around keys like "first block" and "last block", changing them to `firstblock` and `lastblock`. This brings them in line with the general logging style used across the codebase, where log keys are unquoted single words. For example, the log: ` INFO [...] "first block"=..., "last block"=...` Is now rendered as: ` INFO [...] firstblock=..., lastblock=...` This change improves readability and maintains consistency with logs such as: ` INFO [...] number=2 sealhash=... uncles=0 txs=0 ...` No functional behavior is changed — this is purely a formatting cleanup for better developer experience. --- core/filtermaps/indexer.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/filtermaps/indexer.go b/core/filtermaps/indexer.go index 3ec49ca1166..02ae2b920dd 100644 --- a/core/filtermaps/indexer.go +++ b/core/filtermaps/indexer.go @@ -254,7 +254,7 @@ func (f *FilterMaps) tryIndexHead() error { ((!f.loggedHeadIndex && time.Since(f.startedHeadIndexAt) > headLogDelay) || time.Since(f.lastLogHeadIndex) > logFrequency) { log.Info("Log index head rendering in progress", - "first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(), + "firstblock", f.indexedRange.blocks.First(), "lastblock", 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))) @@ -266,7 +266,7 @@ func (f *FilterMaps) tryIndexHead() error { } if f.loggedHeadIndex && f.indexedRange.hasIndexedBlocks() { log.Info("Log index head rendering finished", - "first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(), + "firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(), "processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex, "elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt))) } @@ -323,7 +323,7 @@ func (f *FilterMaps) tryIndexTail() (bool, error) { 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.blocks.First(), "last block", f.indexedRange.blocks.Last(), + "firstblock", 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, @@ -346,7 +346,7 @@ func (f *FilterMaps) tryIndexTail() (bool, error) { } if f.loggedTailIndex && f.indexedRange.hasIndexedBlocks() { log.Info("Log index tail rendering finished", - "first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(), + "firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(), "processed", f.ptrTailIndex-f.indexedRange.blocks.First(), "elapsed", common.PrettyDuration(time.Since(f.startedTailIndexAt))) f.loggedTailIndex = false From fc2ba1fb2e61804ac5f572fd4af304c7bd94f8ee Mon Sep 17 00:00:00 2001 From: GarmashAlex Date: Mon, 5 May 2025 09:01:53 +0300 Subject: [PATCH 146/658] triedb: add test suite for preimage store (#31574) --- triedb/preimages_test.go | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 triedb/preimages_test.go diff --git a/triedb/preimages_test.go b/triedb/preimages_test.go new file mode 100644 index 00000000000..da2ec8dbe35 --- /dev/null +++ b/triedb/preimages_test.go @@ -0,0 +1,78 @@ +// Copyright 2023 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 triedb + +import ( + "bytes" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/triedb/hashdb" +) + +// TestDatabasePreimages tests the preimage functionality of the trie database. +func TestDatabasePreimages(t *testing.T) { + // Create a database with preimages enabled + memDB := rawdb.NewMemoryDatabase() + config := &Config{ + Preimages: true, + HashDB: hashdb.Defaults, + } + db := NewDatabase(memDB, config) + defer db.Close() + + // Test inserting and retrieving preimages + preimages := make(map[common.Hash][]byte) + for i := 0; i < 10; i++ { + data := []byte{byte(i), byte(i + 1), byte(i + 2)} + hash := common.BytesToHash(data) + preimages[hash] = data + } + + // Insert preimages into the database + db.InsertPreimage(preimages) + + // Verify all preimages are retrievable + for hash, data := range preimages { + retrieved := db.Preimage(hash) + if retrieved == nil { + t.Errorf("Preimage for %x not found", hash) + } + if !bytes.Equal(retrieved, data) { + t.Errorf("Preimage data mismatch: got %x want %x", retrieved, data) + } + } + + // Test non-existent preimage + nonExistentHash := common.HexToHash("deadbeef") + if data := db.Preimage(nonExistentHash); data != nil { + t.Errorf("Unexpected preimage data for non-existent hash: %x", data) + } + + // Force preimage commit and verify again + db.WritePreimages() + for hash, data := range preimages { + retrieved := db.Preimage(hash) + if retrieved == nil { + t.Errorf("Preimage for %x not found after forced commit", hash) + } + if !bytes.Equal(retrieved, data) { + t.Errorf("Preimage data mismatch after forced commit: got %x want %x", retrieved, data) + } + } +} From bca0646ede39d45303d8bd0b24ff5e7efa4f3e28 Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Mon, 5 May 2025 12:42:19 +0200 Subject: [PATCH 147/658] internal/ethapi: fix tx.from in eth_simulateV1 (#31480) Issue statement: when user requests eth_simulateV1 to return full transaction objects, these objects always had an empty `from` field. The reason is we lose the sender when translation the message into a types.Transaction which is then later on serialized. I did think of an alternative but opted to keep with this approach as it keeps complexity at the edge. The alternative would be to pass down a signer object to RPCMarshal* methods and define a custom signer which keeps the senders in its state and doesn't attempt the signature recovery. --- internal/ethapi/api_test.go | 71 +++++++++++++++++++++++++++++++++++++ internal/ethapi/simulate.go | 43 +++++++++++++++------- 2 files changed, 102 insertions(+), 12 deletions(-) diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index ef799d99945..0a157dce795 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -2488,6 +2488,77 @@ func TestSimulateV1ChainLinkage(t *testing.T) { require.Equal(t, block2.Hash().Bytes(), []byte(results[2].Calls[1].ReturnValue), "returned blockhash for block2 does not match") } +func TestSimulateV1TxSender(t *testing.T) { + var ( + sender = common.Address{0xaa, 0xaa} + sender2 = common.Address{0xaa, 0xab} + sender3 = common.Address{0xaa, 0xac} + recipient = common.Address{0xbb, 0xbb} + gspec = &core.Genesis{ + Config: params.MergedTestChainConfig, + Alloc: types.GenesisAlloc{ + sender: {Balance: big.NewInt(params.Ether)}, + sender2: {Balance: big.NewInt(params.Ether)}, + sender3: {Balance: big.NewInt(params.Ether)}, + }, + } + ctx = context.Background() + ) + backend := newTestBackend(t, 0, gspec, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {}) + stateDB, baseHeader, err := backend.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)) + if err != nil { + t.Fatalf("failed to get state and header: %v", err) + } + + sim := &simulator{ + b: backend, + state: stateDB, + base: baseHeader, + chainConfig: backend.ChainConfig(), + gp: new(core.GasPool).AddGas(math.MaxUint64), + traceTransfers: false, + validate: false, + fullTx: true, + } + + results, err := sim.execute(ctx, []simBlock{ + {Calls: []TransactionArgs{ + {From: &sender, To: &recipient, Value: (*hexutil.Big)(big.NewInt(1000))}, + {From: &sender2, To: &recipient, Value: (*hexutil.Big)(big.NewInt(2000))}, + {From: &sender3, To: &recipient, Value: (*hexutil.Big)(big.NewInt(3000))}, + }}, + {Calls: []TransactionArgs{ + {From: &sender2, To: &recipient, Value: (*hexutil.Big)(big.NewInt(4000))}, + }}, + }) + if err != nil { + t.Fatalf("simulation execution failed: %v", err) + } + require.Len(t, results, 2, "expected 2 simulated blocks") + require.Len(t, results[0].Block.Transactions(), 3, "expected 3 transaction in simulated block") + require.Len(t, results[1].Block.Transactions(), 1, "expected 1 transaction in 2nd simulated block") + enc, err := json.Marshal(results) + if err != nil { + t.Fatalf("failed to marshal results: %v", err) + } + type resultType struct { + Transactions []struct { + From common.Address `json:"from"` + } + } + var summary []resultType + if err := json.Unmarshal(enc, &summary); err != nil { + t.Fatalf("failed to unmarshal results: %v", err) + } + require.Len(t, summary, 2, "expected 2 simulated blocks") + require.Len(t, summary[0].Transactions, 3, "expected 3 transaction in simulated block") + require.Equal(t, sender, summary[0].Transactions[0].From, "sender address mismatch") + require.Equal(t, sender2, summary[0].Transactions[1].From, "sender address mismatch") + require.Equal(t, sender3, summary[0].Transactions[2].From, "sender address mismatch") + require.Len(t, summary[1].Transactions, 1, "expected 1 transaction in simulated block") + require.Equal(t, sender2, summary[1].Transactions[0].From, "sender address mismatch") +} + func TestSignTransaction(t *testing.T) { t.Parallel() // Initialize test accounts diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 9241b509da7..b997cf297b8 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -78,11 +78,25 @@ type simBlockResult struct { chainConfig *params.ChainConfig Block *types.Block Calls []simCallResult + // senders is a map of transaction hashes to their senders. + senders map[common.Hash]common.Address } func (r *simBlockResult) MarshalJSON() ([]byte, error) { blockData := RPCMarshalBlock(r.Block, true, r.fullTx, r.chainConfig) blockData["calls"] = r.Calls + // Set tx sender if user requested full tx objects. + if r.fullTx { + if raw, ok := blockData["transactions"].([]any); ok { + for _, tx := range raw { + if tx, ok := tx.(*RPCTransaction); ok { + tx.From = r.senders[tx.Hash] + } else { + return nil, errors.New("simulated transaction result has invalid type") + } + } + } + } return json.Marshal(blockData) } @@ -181,18 +195,18 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]*simBlo parent = sim.base ) for bi, block := range blocks { - result, callResults, err := sim.processBlock(ctx, &block, headers[bi], parent, headers[:bi], timeout) + result, callResults, senders, err := sim.processBlock(ctx, &block, headers[bi], parent, headers[:bi], timeout) if err != nil { return nil, err } headers[bi] = result.Header() - results[bi] = &simBlockResult{fullTx: sim.fullTx, chainConfig: sim.chainConfig, Block: result, Calls: callResults} + results[bi] = &simBlockResult{fullTx: sim.fullTx, chainConfig: sim.chainConfig, Block: result, Calls: callResults, senders: senders} parent = result.Header() } return results, nil } -func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, parent *types.Header, headers []*types.Header, timeout time.Duration) (*types.Block, []simCallResult, error) { +func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, parent *types.Header, headers []*types.Header, timeout time.Duration) (*types.Block, []simCallResult, map[common.Hash]common.Address, error) { // Set header fields that depend only on parent block. // Parent hash is needed for evm.GetHashFn to work. header.ParentHash = parent.Hash() @@ -222,7 +236,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, precompiles := sim.activePrecompiles(sim.base) // State overrides are applied prior to execution of a block if err := block.StateOverrides.Apply(sim.state, precompiles); err != nil { - return nil, nil, err + return nil, nil, nil, err } var ( gasUsed, blobGasUsed uint64 @@ -235,6 +249,10 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, NoBaseFee: !sim.validate, Tracer: tracer.Hooks(), } + // senders is a map of transaction hashes to their senders. + // Transaction objects contain only the signature, and we lose track + // of the sender when translating the arguments into a transaction object. + senders = make(map[common.Hash]common.Address) ) tracingStateDB := vm.StateDB(sim.state) if hooks := tracer.Hooks(); hooks != nil { @@ -255,16 +273,17 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, var allLogs []*types.Log for i, call := range block.Calls { if err := ctx.Err(); err != nil { - return nil, nil, err + return nil, nil, nil, err } if err := sim.sanitizeCall(&call, sim.state, header, blockContext, &gasUsed); err != nil { - return nil, nil, err + return nil, nil, nil, err } var ( tx = call.ToTransaction(types.DynamicFeeTxType) txHash = tx.Hash() ) txes[i] = tx + senders[txHash] = call.from() tracer.reset(txHash, uint(i)) sim.state.SetTxContext(txHash, i) // EoA check is always skipped, even in validation mode. @@ -272,7 +291,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) if err != nil { txErr := txValidationError(err) - return nil, nil, txErr + return nil, nil, nil, txErr } // Update the state with pending changes. var root []byte @@ -311,15 +330,15 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, requests = [][]byte{} // EIP-6110 if err := core.ParseDepositLogs(&requests, allLogs, sim.chainConfig); err != nil { - return nil, nil, err + return nil, nil, nil, err } // EIP-7002 if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil { - return nil, nil, err + return nil, nil, nil, err } // EIP-7251 if err := core.ProcessConsolidationQueue(&requests, evm); err != nil { - return nil, nil, err + return nil, nil, nil, err } } if requests != nil { @@ -330,10 +349,10 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, chainHeadReader := &simChainHeadReader{ctx, sim.b} b, err := sim.b.Engine().FinalizeAndAssemble(chainHeadReader, header, sim.state, blockBody, receipts) if err != nil { - return nil, nil, err + return nil, nil, nil, err } repairLogs(callResults, b.Hash()) - return b, callResults, nil + return b, callResults, senders, nil } // repairLogs updates the block hash in the logs present in the result of From b135da2eac9bd3beb043f4b11418c09034fb9cc3 Mon Sep 17 00:00:00 2001 From: Matus Kysel Date: Mon, 5 May 2025 14:43:47 +0200 Subject: [PATCH 148/658] rpc: add method name length limit (#31711) This change adds a limit for RPC method names to prevent potential abuse where large method names could lead to large response sizes. The limit is enforced in: - handleCall for regular RPC method calls - handleSubscribe for subscription method calls Added tests in websocket_test.go to verify the length limit functionality for both regular method calls and subscriptions. --------- Co-authored-by: Felix Lange --- rpc/handler.go | 9 ++++++ rpc/json.go | 1 + rpc/websocket_test.go | 74 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/rpc/handler.go b/rpc/handler.go index f23b544b586..45558d5821c 100644 --- a/rpc/handler.go +++ b/rpc/handler.go @@ -501,6 +501,10 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage if msg.isUnsubscribe() { callb = h.unsubscribeCb } else { + // Check method name length + if len(msg.Method) > maxMethodNameLength { + return msg.errorResponse(&invalidRequestError{fmt.Sprintf("method name too long: %d > %d", len(msg.Method), maxMethodNameLength)}) + } callb = h.reg.callback(msg.Method) } if callb == nil { @@ -536,6 +540,11 @@ func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMes return msg.errorResponse(ErrNotificationsUnsupported) } + // Check method name length + if len(msg.Method) > maxMethodNameLength { + return msg.errorResponse(&invalidRequestError{fmt.Sprintf("subscription name too long: %d > %d", len(msg.Method), maxMethodNameLength)}) + } + // Subscription method name is first argument. name, err := parseSubscriptionName(msg.Params) if err != nil { diff --git a/rpc/json.go b/rpc/json.go index e932389d17c..fcd801fc95d 100644 --- a/rpc/json.go +++ b/rpc/json.go @@ -35,6 +35,7 @@ const ( subscribeMethodSuffix = "_subscribe" unsubscribeMethodSuffix = "_unsubscribe" notificationMethodSuffix = "_subscription" + maxMethodNameLength = 2048 defaultWriteTimeout = 10 * time.Second // used if context has no deadline ) diff --git a/rpc/websocket_test.go b/rpc/websocket_test.go index 10a998b3512..a8d86249003 100644 --- a/rpc/websocket_test.go +++ b/rpc/websocket_test.go @@ -391,3 +391,77 @@ func wsPingTestHandler(t *testing.T, conn *websocket.Conn, shutdown, sendPing <- } } } + +func TestWebsocketMethodNameLengthLimit(t *testing.T) { + t.Parallel() + + var ( + srv = newTestServer() + httpsrv = httptest.NewServer(srv.WebsocketHandler([]string{"*"})) + wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") + ) + defer srv.Stop() + defer httpsrv.Close() + + client, err := DialWebsocket(context.Background(), wsURL, "") + if err != nil { + t.Fatalf("can't dial: %v", err) + } + defer client.Close() + + // Test cases + tests := []struct { + name string + method string + params []interface{} + expectedError string + isSubscription bool + }{ + { + name: "valid method name", + method: "test_echo", + params: []interface{}{"test", 1}, + expectedError: "", + isSubscription: false, + }, + { + name: "method name too long", + method: "test_" + string(make([]byte, maxMethodNameLength+1)), + params: []interface{}{"test", 1}, + expectedError: "method name too long", + isSubscription: false, + }, + { + name: "valid subscription", + method: "nftest_subscribe", + params: []interface{}{"someSubscription", 1, 2}, + expectedError: "", + isSubscription: true, + }, + { + name: "subscription name too long", + method: string(make([]byte, maxMethodNameLength+1)) + "_subscribe", + params: []interface{}{"newHeads"}, + expectedError: "subscription name too long", + isSubscription: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var result interface{} + err := client.Call(&result, tt.method, tt.params...) + if tt.expectedError == "" { + if err != nil { + t.Errorf("unexpected error: %v", err) + } + } else { + if err == nil { + t.Error("expected error, got nil") + } else if !strings.Contains(err.Error(), tt.expectedError) { + t.Errorf("expected error containing %q, got %q", tt.expectedError, err.Error()) + } + } + }) + } +} From 7705d13ed492a6291b2d7aa7f7c15b70749e9a65 Mon Sep 17 00:00:00 2001 From: jwasinger Date: Mon, 5 May 2025 22:15:59 +0800 Subject: [PATCH 149/658] eth/tracers: fix `standardTraceBlockToFile` (#31763) Fixes methods debug_standardTraceBlockToFile and debug_standardTraceBadBlockToFile which were outputting empty files. --------- Co-authored-by: maskpp Co-authored-by: Sina Mahmoodi --- eth/tracers/api.go | 64 +++++++++++----------- eth/tracers/api_test.go | 116 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 30 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 17a0ad687a8..fe72924828e 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -778,6 +778,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Note: This copies the config, to not screw up the main config chainConfig, canon = overrideConfig(chainConfig, config.Overrides) } + evm := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{}) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { core.ProcessBeaconBlockRoot(*beaconRoot, evm) @@ -787,42 +788,45 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block } for i, tx := range block.Transactions() { // Prepare the transaction for un-traced execution - var ( - msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee()) - vmConf vm.Config - dump *os.File - writer *bufio.Writer - err error - ) - // If the transaction needs tracing, swap out the configs - if tx.Hash() == txHash || txHash == (common.Hash{}) { - // Generate a unique temporary file to dump it into - prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4]) - if !canon { - prefix = fmt.Sprintf("%valt-", prefix) - } - dump, err = os.CreateTemp(os.TempDir(), prefix) + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + if txHash != (common.Hash{}) && tx.Hash() != txHash { + // Process the tx to update state, but don't trace it. + _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) if err != nil { - return nil, err - } - dumps = append(dumps, dump.Name()) - - // Swap out the noop logger to the standard tracer - writer = bufio.NewWriter(dump) - vmConf = vm.Config{ - Tracer: logger.NewJSONLogger(&logConfig, writer), - EnablePreimageRecording: true, + return dumps, err } + // Finalize the state so any modifications are written to the trie + // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect + statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number())) + continue } + // The transaction should be traced. + // Generate a unique temporary file to dump it into. + prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4]) + if !canon { + prefix = fmt.Sprintf("%valt-", prefix) + } + var dump *os.File + dump, err := os.CreateTemp(os.TempDir(), prefix) + if err != nil { + return nil, err + } + dumps = append(dumps, dump.Name()) + // Set up the tracer and EVM for the transaction. + var ( + writer = bufio.NewWriter(dump) + tracer = logger.NewJSONLogger(&logConfig, writer) + evm = vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{ + Tracer: tracer, + NoBaseFee: true, + }) + ) // Execute the transaction and flush any traces to disk statedb.SetTxContext(tx.Hash(), i) - if vmConf.Tracer.OnTxStart != nil { - vmConf.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) - } - vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) - if vmConf.Tracer.OnTxEnd != nil { - vmConf.Tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err) + if tracer.OnTxStart != nil { + tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) } + _, err = core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) if writer != nil { writer.Flush() } diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index fa391876942..d20d5eaff65 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "math/big" + "os" "reflect" "slices" "sync/atomic" @@ -1218,3 +1219,118 @@ func TestTraceBlockWithBasefee(t *testing.T) { } } } + +func TestStandardTraceBlockToFile(t *testing.T) { + var ( + // A sender who makes transactions, has some funds + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000000000) + + // first contract the sender transacts with + aa = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43") + aaCode = []byte{byte(vm.PUSH1), 0x00, byte(vm.POP)} + + // second contract the sender transacts with + bb = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f44") + bbCode = []byte{byte(vm.PUSH2), 0x00, 0x01, byte(vm.POP)} + ) + + genesis := &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{ + address: {Balance: funds}, + aa: { + Code: aaCode, + Nonce: 1, + Balance: big.NewInt(0), + }, + bb: { + Code: bbCode, + Nonce: 1, + Balance: big.NewInt(0), + }, + }, + } + txHashs := make([]common.Hash, 0, 2) + backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { + b.SetCoinbase(common.Address{1}) + // first tx to aa + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: 0, + To: &aa, + Value: big.NewInt(0), + Gas: 50000, + GasPrice: b.BaseFee(), + Data: nil, + }), types.HomesteadSigner{}, key) + b.AddTx(tx) + txHashs = append(txHashs, tx.Hash()) + // second tx to bb + tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: 1, + To: &bb, + Value: big.NewInt(1), + Gas: 100000, + GasPrice: b.BaseFee(), + Data: nil, + }), types.HomesteadSigner{}, key) + b.AddTx(tx) + txHashs = append(txHashs, tx.Hash()) + }) + defer backend.chain.Stop() + + var testSuite = []struct { + blockNumber rpc.BlockNumber + config *StdTraceConfig + want []string + }{ + { + // test that all traces in the block were outputted if no trace config is specified + blockNumber: rpc.LatestBlockNumber, + config: nil, + want: []string{ + `{"pc":0,"op":96,"gas":"0x7148","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":80,"gas":"0x7145","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"POP"} +{"pc":3,"op":0,"gas":"0x7143","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"} +{"output":"","gasUsed":"0x5"} +`, + `{"pc":0,"op":97,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":3,"op":80,"gas":"0x13495","gasCost":"0x2","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"POP"} +{"pc":4,"op":0,"gas":"0x13493","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"} +{"output":"","gasUsed":"0x5"} +`, + }, + }, + { + // test that only a specific tx is traced if specified + blockNumber: rpc.LatestBlockNumber, + config: &StdTraceConfig{TxHash: txHashs[1]}, + want: []string{ + `{"pc":0,"op":97,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":3,"op":80,"gas":"0x13495","gasCost":"0x2","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"POP"} +{"pc":4,"op":0,"gas":"0x13493","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"} +{"output":"","gasUsed":"0x5"} +`, + }, + }, + } + + api := NewAPI(backend) + for i, tc := range testSuite { + block, _ := api.blockByNumber(context.Background(), tc.blockNumber) + txTraces, err := api.StandardTraceBlockToFile(context.Background(), block.Hash(), tc.config) + if err != nil { + t.Fatalf("test index %d received error %v", i, err) + } + for j, traceFileName := range txTraces { + traceReceived, err := os.ReadFile(traceFileName) + if err != nil { + t.Fatalf("could not read trace file: %v", err) + } + if tc.want[j] != string(traceReceived) { + t.Fatalf("unexpected trace result. expected\n'%s'\n\nreceived\n'%s'\n", tc.want[j], string(traceReceived)) + } + } + } +} From 36b2371c59cd91a9b1da062b3e382f05a6d8687e Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 5 May 2025 16:19:58 +0200 Subject: [PATCH 150/658] version: release go-ethereum v1.15.11 stable --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 6b9a3b2ca47..4b0a7ebb81e 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 11 // Patch version component of the current release - Meta = "unstable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 11 // Patch version component of the current release + Meta = "stable" // Version metadata to append to the version string ) From d6655cb4506bf2e838eed5199b84bfc63bf4515a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 5 May 2025 16:20:38 +0200 Subject: [PATCH 151/658] version: begin v1.15.12 release cycle --- version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/version/version.go b/version/version.go index 4b0a7ebb81e..51cf410cda7 100644 --- a/version/version.go +++ b/version/version.go @@ -17,8 +17,8 @@ package version const ( - Major = 1 // Major version component of the current release - Minor = 15 // Minor version component of the current release - Patch = 11 // Patch version component of the current release - Meta = "stable" // Version metadata to append to the version string + Major = 1 // Major version component of the current release + Minor = 15 // Minor version component of the current release + Patch = 12 // Patch version component of the current release + Meta = "unstable" // Version metadata to append to the version string ) From 79e8870e34ff946fa372c3bfb11b3d6de84dce9b Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Tue, 6 May 2025 13:30:19 +1000 Subject: [PATCH 152/658] go.mod: update pebble to v1.1.5 to reduce clutter in go.sum (#31541) ``` go get github.com/cockroachdb/pebble@v1.1.5 go mod tidy ``` Co-authored-by: lightclient --- go.mod | 12 +- go.sum | 403 ++------------------------------------------------------- 2 files changed, 18 insertions(+), 397 deletions(-) diff --git a/go.mod b/go.mod index 96826859379..924f0d26420 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/route53 v1.30.2 github.com/cespare/cp v0.1.0 github.com/cloudflare/cloudflare-go v0.114.0 - github.com/cockroachdb/pebble v1.1.2 + github.com/cockroachdb/pebble v1.1.5 github.com/consensys/gnark-crypto v0.16.0 github.com/crate-crypto/go-eth-kzg v1.3.0 github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a @@ -120,7 +120,7 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/minio/sha256-simd v1.0.0 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/mitchellh/pointerstructure v1.2.0 // indirect @@ -133,10 +133,10 @@ require ( github.com/pion/transport/v3 v3.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.12.0 // indirect - github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a // indirect - github.com/prometheus/common v0.32.1 // indirect - github.com/prometheus/procfs v0.7.3 // indirect + github.com/prometheus/client_golang v1.15.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 1ac65bc01d3..5d35a7a0e15 100644 --- a/go.sum +++ b/go.sum @@ -1,36 +1,3 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0 h1:8q4SaHjFsClSvuVne0ID/5Ka8u3fcIHyqkLjcFpNRHQ= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= @@ -43,8 +10,6 @@ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 h1:gggzg0SUMs6SQbEw+ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0/go.mod h1:+6KLcKIVgxoBDMqMO/Nvy7bZ9a0nbU3I1DtFQK3YvB4= github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY= github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= @@ -53,11 +18,6 @@ github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDO github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= @@ -86,30 +46,20 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 h1:0BkLfgeDjfZnZ+MhB3ONb01u9pwF github.com/aws/aws-sdk-go-v2/service/sts v1.23.2/go.mod h1:Eows6e1uQEsc4ZaHANmsPRzAKcVDrcmjjWiih2+HUUQ= github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8= github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.114.0 h1:ucoti4/7Exo0XQ+rzpn1H+IfVVe++zgiM+tyKtf0HUA= github.com/cloudflare/cloudflare-go v0.114.0/go.mod h1:O7fYfFfA6wKqKFn2QIR9lhj7FDw6VQCGOY6hd2TBtd0= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= @@ -118,8 +68,8 @@ github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/e github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= -github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw= +github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= @@ -162,10 +112,6 @@ github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 h1:+3HCtB74++ClLy8GgjU github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w= github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E= github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= @@ -191,15 +137,6 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= @@ -207,86 +144,44 @@ github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -294,8 +189,6 @@ github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY4 github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= @@ -306,7 +199,6 @@ github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXei github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k= github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= @@ -322,15 +214,6 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52 h1:msKODTL1m0wigztaqILOtla9HeW1ciscYG4xjLtvk5I= github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52/go.mod h1:qk1sX/IBgppQNcGCRoj90u6EGC056EBoIc1oEjCWla8= github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4= @@ -342,9 +225,6 @@ github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -378,9 +258,8 @@ github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= @@ -390,13 +269,6 @@ github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8oh github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0= @@ -431,7 +303,6 @@ github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -439,29 +310,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg= -github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a h1:CmF68hwI0XsOQ5UwlBopMi2Ow4Pbg32akc4KIVCOm+Y= -github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= +github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/protolambda/bls12-381-util v0.1.0 h1:05DU2wJN7DTU7z28+Q+zejXkIsA/MF8JZQGhtBZZiWk= github.com/protolambda/bls12-381-util v0.1.0/go.mod h1:cdkysJTRpeFeuUVx/TXGDQNMTiRAalk1vQw3TYTHcE4= github.com/protolambda/zrnt v0.34.1 h1:qW55rnhZJDnOb3TwFiFRJZi3yTXFrJdGOFQM7vCwYGg= @@ -472,7 +328,6 @@ github.com/prysmaticlabs/gohashtree v0.0.1-alpha.0.20220714111606-acbb2962fb48 h github.com/prysmaticlabs/gohashtree v0.0.1-alpha.0.20220714111606-acbb2962fb48/go.mod h1:4pWaT30XoEx1j8KNJf3TV+E3mQkaufn7mf+jRNb/Fuk= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -483,16 +338,11 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -518,24 +368,14 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME= go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -545,77 +385,24 @@ golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= @@ -623,78 +410,37 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -716,9 +462,7 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -730,54 +474,14 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= @@ -788,87 +492,16 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -877,10 +510,8 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -888,15 +519,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= From 51b34efebcf36c4fd083b13b78ec49eb081623b9 Mon Sep 17 00:00:00 2001 From: jwasinger Date: Tue, 6 May 2025 12:40:03 +0800 Subject: [PATCH 153/658] cmd/utils: don't allow network ID override if a preset network is specified (#31630) --- cmd/geth/consolecmd_test.go | 3 +-- cmd/utils/flags.go | 24 +++++++----------------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/cmd/geth/consolecmd_test.go b/cmd/geth/consolecmd_test.go index b8c2c498a64..ca8efb5f98a 100644 --- a/cmd/geth/consolecmd_test.go +++ b/cmd/geth/consolecmd_test.go @@ -39,9 +39,8 @@ const ( // child g gets a temporary data directory. func runMinimalGeth(t *testing.T, args ...string) *testgeth { // --holesky to make the 'writing genesis to disk' faster (no accounts) - // --networkid=1337 to avoid cache bump // --syncmode=full to avoid allocating fast sync bloom - allArgs := []string{"--holesky", "--networkid", "1337", "--authrpc.port", "0", "--syncmode=full", "--port", "0", + allArgs := []string{"--holesky", "--authrpc.port", "0", "--syncmode=full", "--port", "0", "--nat", "none", "--nodiscover", "--maxpeers", "0", "--cache", "64", "--datadir.minfreedisk", "0"} return runGeth(t, append(allArgs, args...)...) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index b26f43b3767..44363d13f46 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1571,8 +1571,8 @@ func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) { // SetEthConfig applies eth-related command line flags to the config. func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { - // Avoid conflicting network flags - flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag, HoodiFlag) + // Avoid conflicting network flags, don't allow network id override on preset networks + flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag, HoodiFlag, NetworkIdFlag) flags.CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer // Set configurations from CLI flags @@ -1743,33 +1743,23 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // Override any default configs for hard coded networks. switch { case ctx.Bool(MainnetFlag.Name): - if !ctx.IsSet(NetworkIdFlag.Name) { - cfg.NetworkId = 1 - } + cfg.NetworkId = 1 cfg.Genesis = core.DefaultGenesisBlock() SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash) case ctx.Bool(HoleskyFlag.Name): - if !ctx.IsSet(NetworkIdFlag.Name) { - cfg.NetworkId = 17000 - } + cfg.NetworkId = 17000 cfg.Genesis = core.DefaultHoleskyGenesisBlock() SetDNSDiscoveryDefaults(cfg, params.HoleskyGenesisHash) case ctx.Bool(SepoliaFlag.Name): - if !ctx.IsSet(NetworkIdFlag.Name) { - cfg.NetworkId = 11155111 - } + cfg.NetworkId = 11155111 cfg.Genesis = core.DefaultSepoliaGenesisBlock() SetDNSDiscoveryDefaults(cfg, params.SepoliaGenesisHash) case ctx.Bool(HoodiFlag.Name): - if !ctx.IsSet(NetworkIdFlag.Name) { - cfg.NetworkId = 560048 - } + cfg.NetworkId = 560048 cfg.Genesis = core.DefaultHoodiGenesisBlock() SetDNSDiscoveryDefaults(cfg, params.HoodiGenesisHash) case ctx.Bool(DeveloperFlag.Name): - if !ctx.IsSet(NetworkIdFlag.Name) { - cfg.NetworkId = 1337 - } + cfg.NetworkId = 1337 cfg.SyncMode = ethconfig.FullSync // Create new developer account or reuse existing one var ( From 3e356d69efcde2ce5f287863be5eb4f71177f607 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 7 May 2025 12:53:45 +0200 Subject: [PATCH 154/658] beacon/blsync: fix requests encoding in engine_newPayloadV4 (#31775) This fixes an issue where blocks containing CL requests triggered an error in the engine API. The encoding of requests used base64 instead of hex. --- beacon/blsync/engineclient.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/beacon/blsync/engineclient.go b/beacon/blsync/engineclient.go index 77bee83f5bc..f9821fc6f36 100644 --- a/beacon/blsync/engineclient.go +++ b/beacon/blsync/engineclient.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/beacon/params" "github.com/ethereum/go-ethereum/beacon/types" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" ctypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" @@ -104,7 +105,11 @@ func (ec *engineClient) callNewPayload(fork string, event types.ChainHeadEvent) method = "engine_newPayloadV4" parentBeaconRoot := event.BeaconHead.ParentRoot blobHashes := collectBlobHashes(event.Block) - params = append(params, blobHashes, parentBeaconRoot, event.ExecRequests) + hexRequests := make([]hexutil.Bytes, len(event.ExecRequests)) + for i := range event.ExecRequests { + hexRequests[i] = hexutil.Bytes(event.ExecRequests[i]) + } + params = append(params, blobHashes, parentBeaconRoot, hexRequests) case "deneb": method = "engine_newPayloadV3" parentBeaconRoot := event.BeaconHead.ParentRoot From 0d5de826da144cef795542a3489f899f49dee1e8 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 7 May 2025 15:34:52 +0200 Subject: [PATCH 155/658] p2p: add metrics for inbound connection errors (#31652) Add metics detailing reasons we reject inbound connections for, and reasons these connections fail during the handshake. --- p2p/metrics.go | 42 ++++++++++++++++++++++++++++++++++++++++++ p2p/server.go | 2 ++ 2 files changed, 44 insertions(+) diff --git a/p2p/metrics.go b/p2p/metrics.go index 29c2acb0cba..c44a878aa4a 100644 --- a/p2p/metrics.go +++ b/p2p/metrics.go @@ -66,6 +66,18 @@ var ( // capture the rest of errors that are not handled by the above meters dialOtherError = metrics.NewRegisteredMeter("p2p/dials/error/other", nil) + + // handshake error meters for inbound connections + serveTooManyPeers = metrics.NewRegisteredMeter("p2p/serves/error/saturated", nil) + serveAlreadyConnected = metrics.NewRegisteredMeter("p2p/serves/error/known", nil) + serveSelf = metrics.NewRegisteredMeter("p2p/serves/error/self", nil) + serveUselessPeer = metrics.NewRegisteredMeter("p2p/serves/error/useless", nil) + serveUnexpectedIdentity = metrics.NewRegisteredMeter("p2p/serves/error/id/unexpected", nil) + serveEncHandshakeError = metrics.NewRegisteredMeter("p2p/serves/error/rlpx/enc", nil) //EOF; connection reset during handshake; (message too big?) + serveProtoHandshakeError = metrics.NewRegisteredMeter("p2p/serves/error/rlpx/proto", nil) + + // capture the rest of errors that are not handled by the above meters + serveOtherError = metrics.NewRegisteredMeter("p2p/serves/error/other", nil) ) // markDialError matches errors that occur while setting up a dial connection to the @@ -99,6 +111,36 @@ func markDialError(err error) { } } +// markServeError matches errors that occur while serving an inbound connection +// to the corresponding meter. +func markServeError(err error) { + if !metrics.Enabled() { + return + } + + var reason DiscReason + var handshakeErr *protoHandshakeError + d := errors.As(err, &reason) + switch { + case d && reason == DiscTooManyPeers: + serveTooManyPeers.Mark(1) + case d && reason == DiscAlreadyConnected: + serveAlreadyConnected.Mark(1) + case d && reason == DiscSelf: + serveSelf.Mark(1) + case d && reason == DiscUselessPeer: + serveUselessPeer.Mark(1) + case d && reason == DiscUnexpectedIdentity: + serveUnexpectedIdentity.Mark(1) + case errors.As(err, &handshakeErr): + serveProtoHandshakeError.Mark(1) + case errors.Is(err, errEncHandshakeError): + serveEncHandshakeError.Mark(1) + default: + serveOtherError.Mark(1) + } +} + // meteredConn is a wrapper around a net.Conn that meters both the // inbound and outbound network traffic. type meteredConn struct { diff --git a/p2p/server.go b/p2p/server.go index d9105976dd2..f3a58bba299 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -864,6 +864,8 @@ func (srv *Server) SetupConn(fd net.Conn, flags connFlag, dialDest *enode.Node) if err != nil { if !c.is(inboundConn) { markDialError(err) + } else { + markServeError(err) } c.close(err) } From 07d073bc5a711ddf40f25c56b54f88badf3c3694 Mon Sep 17 00:00:00 2001 From: JukLee0ira Date: Thu, 8 May 2025 14:57:17 +0800 Subject: [PATCH 156/658] internal/web3ext: remove the legacy backtraceAt method (#31783) The function `BacktraceAt` has been removed in #28187 . But the API end-point `debug_backtraceAt` is not removed from the file `internal/web3ext/web3ext.go`. --- internal/web3ext/web3ext.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index aed6bbbdb97..a6d93fc1c53 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -247,11 +247,6 @@ web3._extend({ call: 'debug_vmodule', params: 1 }), - new web3._extend.Method({ - name: 'backtraceAt', - call: 'debug_backtraceAt', - params: 1, - }), new web3._extend.Method({ name: 'stacks', call: 'debug_stacks', From 10519768a2f7259e55cc34e51ff6ec73e6a703e9 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 8 May 2025 19:10:26 +0800 Subject: [PATCH 157/658] core, ethdb: introduce database sync function (#31703) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This pull request introduces a SyncKeyValue function to the ethdb.KeyValueStore interface, providing the ability to forcibly flush all previous writes to disk. This functionality is critical for go-ethereum, which internally uses two independent database engines: a key-value store (such as Pebble, LevelDB, or memoryDB for testing) and a flat-file–based freezer. To ensure write-order consistency between these engines, the key-value store must be explicitly synced before writing to the freezer and vice versa. Fixes - https://github.com/ethereum/go-ethereum/issues/31405 - https://github.com/ethereum/go-ethereum/issues/29819 --- core/bench_test.go | 8 +++---- core/blockchain.go | 24 ++++++++++---------- core/blockchain_repair_test.go | 8 +++---- core/blockchain_sethead_test.go | 2 +- core/blockchain_snapshot_test.go | 4 ++-- core/blockchain_test.go | 2 +- core/headerchain.go | 35 +++++++++++++++++++++++++++- core/rawdb/chain_freezer.go | 2 +- core/rawdb/database.go | 4 ++-- core/rawdb/freezer.go | 4 ++-- core/rawdb/freezer_memory.go | 4 ++-- core/rawdb/freezer_resettable.go | 6 ++--- core/rawdb/freezer_test.go | 2 +- core/rawdb/table.go | 12 +++++++--- ethdb/database.go | 14 +++++++++--- ethdb/leveldb/leveldb.go | 16 +++++++++++++ ethdb/memorydb/memorydb.go | 6 +++++ ethdb/pebble/pebble.go | 39 ++++++++++++++++++++++++++++---- ethdb/pebble/pebble_test.go | 24 ++++++++++++++++++++ ethdb/remotedb/remotedb.go | 6 ++++- node/database.go | 13 ++++------- trie/trie_test.go | 1 + triedb/pathdb/buffer.go | 9 +++++--- triedb/pathdb/database.go | 9 ++++++++ 24 files changed, 194 insertions(+), 60 deletions(-) diff --git a/core/bench_test.go b/core/bench_test.go index 00f924076ad..155fa6c3b54 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -183,7 +183,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { if !disk { db = rawdb.NewMemoryDatabase() } else { - pdb, err := pebble.New(b.TempDir(), 128, 128, "", false, true) + pdb, err := pebble.New(b.TempDir(), 128, 128, "", false) if err != nil { b.Fatalf("cannot create temporary database: %v", err) } @@ -303,7 +303,7 @@ func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uin func benchWriteChain(b *testing.B, full bool, count uint64) { genesis := &Genesis{Config: params.AllEthashProtocolChanges} for i := 0; i < b.N; i++ { - pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false, true) + pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false) if err != nil { b.Fatalf("error opening database: %v", err) } @@ -316,7 +316,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) { func benchReadChain(b *testing.B, full bool, count uint64) { dir := b.TempDir() - pdb, err := pebble.New(dir, 1024, 128, "", false, true) + pdb, err := pebble.New(dir, 1024, 128, "", false) if err != nil { b.Fatalf("error opening database: %v", err) } @@ -332,7 +332,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) { b.ResetTimer() for i := 0; i < b.N; i++ { - pdb, err = pebble.New(dir, 1024, 128, "", false, true) + pdb, err = pebble.New(dir, 1024, 128, "", false) if err != nil { b.Fatalf("error opening database: %v", err) } diff --git a/core/blockchain.go b/core/blockchain.go index 6667f649110..b0c1b119fc5 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -979,17 +979,16 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha // Ignore the error here since light client won't hit this path frozen, _ := bc.db.Ancients() if num+1 <= frozen { - // Truncate all relative data(header, total difficulty, body, receipt - // and canonical hash) from ancient store. - if _, err := bc.db.TruncateHead(num); err != nil { - log.Crit("Failed to truncate ancient data", "number", num, "err", err) - } - // Remove the hash <-> number mapping from the active store. - rawdb.DeleteHeaderNumber(db, hash) + // The chain segment, such as the block header, canonical hash, + // body, and receipt, will be removed from the ancient store + // in one go. + // + // The hash-to-number mapping in the key-value store will be + // removed by the hc.SetHead function. } else { - // Remove relative body and receipts from the active store. - // The header, total difficulty and canonical hash will be - // removed in the hc.SetHead function. + // Remove the associated body and receipts from the key-value store. + // The header, hash-to-number mapping, and canonical hash will be + // removed by the hc.SetHead function. rawdb.DeleteBody(db, hash, num) rawdb.DeleteReceipts(db, hash, num) } @@ -1361,7 +1360,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ size += writeSize // Sync the ancient store explicitly to ensure all data has been flushed to disk. - if err := bc.db.Sync(); err != nil { + if err := bc.db.SyncAncient(); err != nil { return 0, err } // Write hash to number mappings @@ -2627,7 +2626,8 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e if err != nil { return 0, err } - if err := bc.db.Sync(); err != nil { + // Sync the ancient store explicitly to ensure all data has been flushed to disk. + if err := bc.db.SyncAncient(); err != nil { return 0, err } // Write hash to number mappings diff --git a/core/blockchain_repair_test.go b/core/blockchain_repair_test.go index 3ff1d77fc83..6c52d057adf 100644 --- a/core/blockchain_repair_test.go +++ b/core/blockchain_repair_test.go @@ -1765,7 +1765,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s datadir := t.TempDir() ancient := filepath.Join(datadir, "ancient") - pdb, err := pebble.New(datadir, 0, 0, "", false, true) + pdb, err := pebble.New(datadir, 0, 0, "", false) if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } @@ -1850,7 +1850,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s chain.stopWithoutSaving() // Start a new blockchain back up and see where the repair leads us - pdb, err = pebble.New(datadir, 0, 0, "", false, true) + pdb, err = pebble.New(datadir, 0, 0, "", false) if err != nil { t.Fatalf("Failed to reopen persistent key-value database: %v", err) } @@ -1915,7 +1915,7 @@ func testIssue23496(t *testing.T, scheme string) { datadir := t.TempDir() ancient := filepath.Join(datadir, "ancient") - pdb, err := pebble.New(datadir, 0, 0, "", false, true) + pdb, err := pebble.New(datadir, 0, 0, "", false) if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } @@ -1973,7 +1973,7 @@ func testIssue23496(t *testing.T, scheme string) { chain.stopWithoutSaving() // Start a new blockchain back up and see where the repair leads us - pdb, err = pebble.New(datadir, 0, 0, "", false, true) + pdb, err = pebble.New(datadir, 0, 0, "", false) if err != nil { t.Fatalf("Failed to reopen persistent key-value database: %v", err) } diff --git a/core/blockchain_sethead_test.go b/core/blockchain_sethead_test.go index e998b510df9..424854b2bf8 100644 --- a/core/blockchain_sethead_test.go +++ b/core/blockchain_sethead_test.go @@ -1969,7 +1969,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme datadir := t.TempDir() ancient := filepath.Join(datadir, "ancient") - pdb, err := pebble.New(datadir, 0, 0, "", false, true) + pdb, err := pebble.New(datadir, 0, 0, "", false) if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } diff --git a/core/blockchain_snapshot_test.go b/core/blockchain_snapshot_test.go index 23effea15e2..1a6fe38af6d 100644 --- a/core/blockchain_snapshot_test.go +++ b/core/blockchain_snapshot_test.go @@ -66,7 +66,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo datadir := t.TempDir() ancient := filepath.Join(datadir, "ancient") - pdb, err := pebble.New(datadir, 0, 0, "", false, true) + pdb, err := pebble.New(datadir, 0, 0, "", false) if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } @@ -257,7 +257,7 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) { chain.triedb.Close() // Start a new blockchain back up and see where the repair leads us - pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false, true) + pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false) if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 134deee237d..b981c33f21e 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -2492,7 +2492,7 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) { datadir := t.TempDir() ancient := path.Join(datadir, "ancient") - pdb, err := pebble.New(datadir, 0, 0, "", false, true) + pdb, err := pebble.New(datadir, 0, 0, "", false) if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } diff --git a/core/headerchain.go b/core/headerchain.go index f7acc49bef4..6e70dfa8659 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -591,17 +591,50 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat hashes = append(hashes, hdr.Hash()) } for _, hash := range hashes { + // Remove the associated block body and receipts if required. + // + // If the block is in the chain freezer, then this delete operation + // is actually ineffective. if delFn != nil { delFn(batch, hash, num) } + // Remove the hash->number mapping along with the header itself rawdb.DeleteHeader(batch, hash, num) } + // Remove the number->hash mapping rawdb.DeleteCanonicalHash(batch, num) } } // Flush all accumulated deletions. if err := batch.Write(); err != nil { - log.Crit("Failed to rewind block", "error", err) + log.Crit("Failed to commit batch in setHead", "err", err) + } + // Explicitly flush the pending writes in the key-value store to disk, ensuring + // data durability of the previous deletions. + if err := hc.chainDb.SyncKeyValue(); err != nil { + log.Crit("Failed to sync the key-value store in setHead", "err", err) + } + // Truncate the excessive chain segments in the ancient store. + // These are actually deferred deletions from the loop above. + // + // This step must be performed after synchronizing the key-value store; + // otherwise, in the event of a panic, it's theoretically possible to + // lose recent key-value store writes while the ancient store deletions + // remain, leading to data inconsistency, e.g., the gap between the key + // value store and ancient can be created due to unclean shutdown. + if delFn != nil { + // Ignore the error here since light client won't hit this path + frozen, _ := hc.chainDb.Ancients() + header := hc.CurrentHeader() + + // Truncate the excessive chain segment above the current chain head + // in the ancient store. + if header.Number.Uint64()+1 < frozen { + _, err := hc.chainDb.TruncateHead(header.Number.Uint64() + 1) + if err != nil { + log.Crit("Failed to truncate head block", "err", err) + } + } } // Clear out any stale content from the caches hc.headerCache.Purge() diff --git a/core/rawdb/chain_freezer.go b/core/rawdb/chain_freezer.go index f3c671f45a8..cc7a62df329 100644 --- a/core/rawdb/chain_freezer.go +++ b/core/rawdb/chain_freezer.go @@ -205,7 +205,7 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) { continue } // Batch of blocks have been frozen, flush them before wiping from key-value store - if err := f.Sync(); err != nil { + if err := f.SyncAncient(); err != nil { log.Crit("Failed to flush frozen tables", "err", err) } // Wipe out all data from the active database diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 2a50e3f9eef..a03dbafb1f4 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -131,8 +131,8 @@ func (db *nofreezedb) TruncateTail(items uint64) (uint64, error) { return 0, errNotSupported } -// Sync returns an error as we don't have a backing chain freezer. -func (db *nofreezedb) Sync() error { +// SyncAncient returns an error as we don't have a backing chain freezer. +func (db *nofreezedb) SyncAncient() error { return errNotSupported } diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go index 105d3af9344..1e5b98c7fe0 100644 --- a/core/rawdb/freezer.go +++ b/core/rawdb/freezer.go @@ -325,8 +325,8 @@ func (f *Freezer) TruncateTail(tail uint64) (uint64, error) { return old, nil } -// Sync flushes all data tables to disk. -func (f *Freezer) Sync() error { +// SyncAncient flushes all data tables to disk. +func (f *Freezer) SyncAncient() error { var errs []error for _, table := range f.tables { if err := table.Sync(); err != nil { diff --git a/core/rawdb/freezer_memory.go b/core/rawdb/freezer_memory.go index 4274546de51..bd286f45f57 100644 --- a/core/rawdb/freezer_memory.go +++ b/core/rawdb/freezer_memory.go @@ -395,8 +395,8 @@ func (f *MemoryFreezer) TruncateTail(tail uint64) (uint64, error) { return old, nil } -// Sync flushes all data tables to disk. -func (f *MemoryFreezer) Sync() error { +// SyncAncient flushes all data tables to disk. +func (f *MemoryFreezer) SyncAncient() error { return nil } diff --git a/core/rawdb/freezer_resettable.go b/core/rawdb/freezer_resettable.go index 2e64e6074c5..01df2877d91 100644 --- a/core/rawdb/freezer_resettable.go +++ b/core/rawdb/freezer_resettable.go @@ -194,12 +194,12 @@ func (f *resettableFreezer) TruncateTail(tail uint64) (uint64, error) { return f.freezer.TruncateTail(tail) } -// Sync flushes all data tables to disk. -func (f *resettableFreezer) Sync() error { +// SyncAncient flushes all data tables to disk. +func (f *resettableFreezer) SyncAncient() error { f.lock.RLock() defer f.lock.RUnlock() - return f.freezer.Sync() + return f.freezer.SyncAncient() } // AncientDatadir returns the path of the ancient store. diff --git a/core/rawdb/freezer_test.go b/core/rawdb/freezer_test.go index 150734d3ac9..a7a3559ec43 100644 --- a/core/rawdb/freezer_test.go +++ b/core/rawdb/freezer_test.go @@ -392,7 +392,7 @@ func TestFreezerCloseSync(t *testing.T) { if err := f.Close(); err != nil { t.Fatal(err) } - if err := f.Sync(); err == nil { + if err := f.SyncAncient(); err == nil { t.Fatalf("want error, have nil") } else if have, want := err.Error(), "[closed closed]"; have != want { t.Fatalf("want %v, have %v", have, want) diff --git a/core/rawdb/table.go b/core/rawdb/table.go index 1a9060b6365..9a342a8217b 100644 --- a/core/rawdb/table.go +++ b/core/rawdb/table.go @@ -107,10 +107,10 @@ func (t *table) TruncateTail(items uint64) (uint64, error) { return t.db.TruncateTail(items) } -// Sync is a noop passthrough that just forwards the request to the underlying +// SyncAncient is a noop passthrough that just forwards the request to the underlying // database. -func (t *table) Sync() error { - return t.db.Sync() +func (t *table) SyncAncient() error { + return t.db.SyncAncient() } // AncientDatadir returns the ancient datadir of the underlying database. @@ -188,6 +188,12 @@ func (t *table) Compact(start []byte, limit []byte) error { return t.db.Compact(start, limit) } +// SyncKeyValue ensures that all pending writes are flushed to disk, +// guaranteeing data durability up to the point. +func (t *table) SyncKeyValue() error { + return t.db.SyncKeyValue() +} + // NewBatch creates a write-only database that buffers changes to its host db // until a final write is called, each operation prefixing all keys with the // pre-configured string. diff --git a/ethdb/database.go b/ethdb/database.go index f2d458b85f3..7f421752c47 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -57,6 +57,13 @@ type KeyValueStater interface { Stat() (string, error) } +// KeyValueSyncer wraps the SyncKeyValue method of a backing data store. +type KeyValueSyncer interface { + // SyncKeyValue ensures that all pending writes are flushed to disk, + // guaranteeing data durability up to the point. + SyncKeyValue() error +} + // Compacter wraps the Compact method of a backing data store. type Compacter interface { // Compact flattens the underlying data store for the given key range. In essence, @@ -75,6 +82,7 @@ type KeyValueStore interface { KeyValueReader KeyValueWriter KeyValueStater + KeyValueSyncer KeyValueRangeDeleter Batcher Iteratee @@ -126,6 +134,9 @@ type AncientWriter interface { // The integer return value is the total size of the written data. ModifyAncients(func(AncientWriteOp) error) (int64, error) + // SyncAncient flushes all in-memory ancient store data to disk. + SyncAncient() error + // TruncateHead discards all but the first n ancient data from the ancient store. // After the truncation, the latest item can be accessed it item_n-1(start from 0). TruncateHead(n uint64) (uint64, error) @@ -138,9 +149,6 @@ type AncientWriter interface { // // Note that data marked as non-prunable will still be retained and remain accessible. TruncateTail(n uint64) (uint64, error) - - // Sync flushes all in-memory ancient store data to disk. - Sync() error } // AncientWriteOp is given to the function argument of ModifyAncients. diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go index ef02e91822e..223d01aff6e 100644 --- a/ethdb/leveldb/leveldb.go +++ b/ethdb/leveldb/leveldb.go @@ -324,6 +324,22 @@ func (db *Database) Path() string { return db.fn } +// SyncKeyValue flushes all pending writes in the write-ahead-log to disk, +// ensuring data durability up to that point. +func (db *Database) SyncKeyValue() error { + // In theory, the WAL (Write-Ahead Log) can be explicitly synchronized using + // a write operation with SYNC=true. However, there is no dedicated key reserved + // for this purpose, and even a nil key (key=nil) is considered a valid + // database entry. + // + // In LevelDB, writes are blocked until the data is written to the WAL, meaning + // recent writes won't be lost unless a power failure or system crash occurs. + // Additionally, LevelDB is no longer the default database engine and is likely + // only used by hash-mode archive nodes. Given this, the durability guarantees + // without explicit sync are acceptable in the context of LevelDB. + return nil +} + // meter periodically retrieves internal leveldb counters and reports them to // the metrics subsystem. func (db *Database) meter(refresh time.Duration, namespace string) { diff --git a/ethdb/memorydb/memorydb.go b/ethdb/memorydb/memorydb.go index a797275e92a..f56727cf4a6 100644 --- a/ethdb/memorydb/memorydb.go +++ b/ethdb/memorydb/memorydb.go @@ -199,6 +199,12 @@ func (db *Database) Compact(start []byte, limit []byte) error { return nil } +// SyncKeyValue ensures that all pending writes are flushed to disk, +// guaranteeing data durability up to the point. +func (db *Database) SyncKeyValue() error { + return nil +} + // Len returns the number of entries currently present in the memory database. // // Note, this method is only used for testing (i.e. not public in general) and diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go index 969e67af5a3..9ece9956552 100644 --- a/ethdb/pebble/pebble.go +++ b/ethdb/pebble/pebble.go @@ -144,7 +144,7 @@ func (l panicLogger) Fatalf(format string, args ...interface{}) { // New returns a wrapped pebble DB object. The namespace is the prefix that the // metrics reporting should use for surfacing internal stats. -func New(file string, cache int, handles int, namespace string, readonly bool, ephemeral bool) (*Database, error) { +func New(file string, cache int, handles int, namespace string, readonly bool) (*Database, error) { // Ensure we have some minimal caching and file guarantees if cache < minCache { cache = minCache @@ -182,10 +182,18 @@ func New(file string, cache int, handles int, namespace string, readonly bool, e memTableSize = maxMemTableSize - 1 } db := &Database{ - fn: file, - log: logger, - quitChan: make(chan chan error), - writeOptions: &pebble.WriteOptions{Sync: !ephemeral}, + fn: file, + log: logger, + quitChan: make(chan chan error), + + // Use asynchronous write mode by default. Otherwise, the overhead of frequent fsync + // operations can be significant, especially on platforms with slow fsync performance + // (e.g., macOS) or less capable SSDs. + // + // Note that enabling async writes means recent data may be lost in the event of an + // application-level panic (writes will also be lost on a machine-level failure, + // of course). Geth is expected to handle recovery from an unclean shutdown. + writeOptions: pebble.NoSync, } opt := &pebble.Options{ // Pebble has a single combined cache area and the write @@ -228,6 +236,15 @@ func New(file string, cache int, handles int, namespace string, readonly bool, e WriteStallEnd: db.onWriteStallEnd, }, Logger: panicLogger{}, // TODO(karalabe): Delete when this is upstreamed in Pebble + + // Pebble is configured to use asynchronous write mode, meaning write operations + // return as soon as the data is cached in memory, without waiting for the WAL + // to be written. This mode offers better write performance but risks losing + // recent writes if the application crashes or a power failure/system crash occurs. + // + // By setting the WALBytesPerSync, the cached WAL writes will be periodically + // flushed at the background if the accumulated size exceeds this threshold. + WALBytesPerSync: 5 * ethdb.IdealBatchSize, } // Disable seek compaction explicitly. Check https://github.com/ethereum/go-ethereum/pull/20130 // for more details. @@ -414,6 +431,18 @@ func (d *Database) Path() string { return d.fn } +// SyncKeyValue flushes all pending writes in the write-ahead-log to disk, +// ensuring data durability up to that point. +func (d *Database) SyncKeyValue() error { + // The entry (value=nil) is not written to the database; it is only + // added to the WAL. Writing this special log entry in sync mode + // automatically flushes all previous writes, ensuring database + // durability up to this point. + b := d.db.NewBatch() + b.LogData(nil, nil) + return d.db.Apply(b, pebble.Sync) +} + // meter periodically retrieves internal pebble counters and reports them to // the metrics subsystem. func (d *Database) meter(refresh time.Duration, namespace string) { diff --git a/ethdb/pebble/pebble_test.go b/ethdb/pebble/pebble_test.go index 3265491d4a3..e703a8d0ced 100644 --- a/ethdb/pebble/pebble_test.go +++ b/ethdb/pebble/pebble_test.go @@ -17,6 +17,7 @@ package pebble import ( + "errors" "testing" "github.com/cockroachdb/pebble" @@ -54,3 +55,26 @@ func BenchmarkPebbleDB(b *testing.B) { } }) } + +func TestPebbleLogData(t *testing.T) { + db, err := pebble.Open("", &pebble.Options{ + FS: vfs.NewMem(), + }) + if err != nil { + t.Fatal(err) + } + + _, _, err = db.Get(nil) + if !errors.Is(err, pebble.ErrNotFound) { + t.Fatal("Unknown database entry") + } + + b := db.NewBatch() + b.LogData(nil, nil) + db.Apply(b, pebble.Sync) + + _, _, err = db.Get(nil) + if !errors.Is(err, pebble.ErrNotFound) { + t.Fatal("Unknown database entry") + } +} diff --git a/ethdb/remotedb/remotedb.go b/ethdb/remotedb/remotedb.go index 8a91fdbcf26..a417a25854f 100644 --- a/ethdb/remotedb/remotedb.go +++ b/ethdb/remotedb/remotedb.go @@ -110,7 +110,7 @@ func (db *Database) TruncateTail(n uint64) (uint64, error) { panic("not supported") } -func (db *Database) Sync() error { +func (db *Database) SyncAncient() error { return nil } @@ -138,6 +138,10 @@ func (db *Database) Compact(start []byte, limit []byte) error { return nil } +func (db *Database) SyncKeyValue() error { + return nil +} + func (db *Database) Close() error { db.remote.Close() return nil diff --git a/node/database.go b/node/database.go index b7d0d856cbd..e3ccb910667 100644 --- a/node/database.go +++ b/node/database.go @@ -36,11 +36,6 @@ type openOptions struct { Cache int // the capacity(in megabytes) of the data caching Handles int // number of files to be open simultaneously ReadOnly bool - - // Ephemeral means that filesystem sync operations should be avoided: - // data integrity in the face of a crash is not important. This option - // should typically be used in tests. - Ephemeral bool } // openDatabase opens both a disk-based key-value database such as leveldb or pebble, but also @@ -83,7 +78,7 @@ func openKeyValueDatabase(o openOptions) (ethdb.Database, error) { } if o.Type == rawdb.DBPebble || existingDb == rawdb.DBPebble { log.Info("Using pebble as the backing database") - return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral) + return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly) } if o.Type == rawdb.DBLeveldb || existingDb == rawdb.DBLeveldb { log.Info("Using leveldb as the backing database") @@ -91,7 +86,7 @@ func openKeyValueDatabase(o openOptions) (ethdb.Database, error) { } // No pre-existing database, no user-requested one either. Default to Pebble. log.Info("Defaulting to pebble as the backing database") - return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral) + return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly) } // newLevelDBDatabase creates a persistent key-value database without a freezer @@ -107,8 +102,8 @@ func newLevelDBDatabase(file string, cache int, handles int, namespace string, r // newPebbleDBDatabase creates a persistent key-value database without a freezer // moving immutable chain segments into cold storage. -func newPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly bool, ephemeral bool) (ethdb.Database, error) { - db, err := pebble.New(file, cache, handles, namespace, readonly, ephemeral) +func newPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) { + db, err := pebble.New(file, cache, handles, namespace, readonly) if err != nil { return nil, err } diff --git a/trie/trie_test.go b/trie/trie_test.go index 54d1b083d8e..91fde6dbf26 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -830,6 +830,7 @@ func (s *spongeDb) NewBatch() ethdb.Batch { return &spongeBat func (s *spongeDb) NewBatchWithSize(size int) ethdb.Batch { return &spongeBatch{s} } func (s *spongeDb) Stat() (string, error) { panic("implement me") } func (s *spongeDb) Compact(start []byte, limit []byte) error { panic("implement me") } +func (s *spongeDb) SyncKeyValue() error { return nil } func (s *spongeDb) Close() error { return nil } func (s *spongeDb) Put(key []byte, value []byte) error { var ( diff --git a/triedb/pathdb/buffer.go b/triedb/pathdb/buffer.go index dea8875bda5..c4e081b9737 100644 --- a/triedb/pathdb/buffer.go +++ b/triedb/pathdb/buffer.go @@ -135,10 +135,13 @@ func (b *buffer) flush(db ethdb.KeyValueStore, freezer ethdb.AncientWriter, node start = time.Now() batch = db.NewBatchWithSize(b.nodes.dbsize() * 11 / 10) // extra 10% for potential pebble internal stuff ) - // Explicitly sync the state freezer, ensuring that all written - // data is transferred to disk before updating the key-value store. + // Explicitly sync the state freezer to ensure all written data is persisted to disk + // before updating the key-value store. + // + // This step is crucial to guarantee that the corresponding state history remains + // available for state rollback. if freezer != nil { - if err := freezer.Sync(); err != nil { + if err := freezer.SyncAncient(); err != nil { return err } } diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index d48850c102f..155e28543db 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -454,6 +454,15 @@ func (db *Database) Recover(root common.Hash) error { db.tree.reset(dl) } rawdb.DeleteTrieJournal(db.diskdb) + + // Explicitly sync the key-value store to ensure all recent writes are + // flushed to disk. This step is crucial to prevent a scenario where + // recent key-value writes are lost due to an application panic, while + // the associated state histories have already been removed, resulting + // in the inability to perform a state rollback. + if err := db.diskdb.SyncKeyValue(); err != nil { + return err + } _, err := truncateFromHead(db.diskdb, db.freezer, dl.stateID()) if err != nil { return err From 181dd2e66025ee6e5cd1c304f4c5f62f911d6272 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 8 May 2025 21:15:36 +0800 Subject: [PATCH 158/658] cmd/geth, internal: fix flaky console tests (#31784) This pull request bumps the timeout for flaky console tests on appveyor. --- cmd/geth/consolecmd_test.go | 6 +++--- eth/api_backend_test.go | 18 +++++++++++------- internal/cmdtest/test_cmd.go | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/cmd/geth/consolecmd_test.go b/cmd/geth/consolecmd_test.go index ca8efb5f98a..4e1f6340a00 100644 --- a/cmd/geth/consolecmd_test.go +++ b/cmd/geth/consolecmd_test.go @@ -102,17 +102,17 @@ func TestAttachWelcome(t *testing.T) { "--http", "--http.port", httpPort, "--ws", "--ws.port", wsPort) t.Run("ipc", func(t *testing.T) { - waitForEndpoint(t, ipc, 4*time.Second) + waitForEndpoint(t, ipc, 2*time.Minute) testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs) }) t.Run("http", func(t *testing.T) { endpoint := "http://127.0.0.1:" + httpPort - waitForEndpoint(t, endpoint, 4*time.Second) + waitForEndpoint(t, endpoint, 2*time.Minute) testAttachWelcome(t, geth, endpoint, httpAPIs) }) t.Run("ws", func(t *testing.T) { endpoint := "ws://127.0.0.1:" + wsPort - waitForEndpoint(t, endpoint, 4*time.Second) + waitForEndpoint(t, endpoint, 2*time.Minute) testAttachWelcome(t, geth, endpoint, httpAPIs) }) geth.Kill() diff --git a/eth/api_backend_test.go b/eth/api_backend_test.go index 049f68d8273..dfca24aba78 100644 --- a/eth/api_backend_test.go +++ b/eth/api_backend_test.go @@ -134,13 +134,17 @@ func TestSendTx(t *testing.T) { func testSendTx(t *testing.T, withLocal bool) { b := initBackend(withLocal) - txA := pricedSetCodeTx(0, 250000, uint256.NewInt(params.GWei), uint256.NewInt(params.GWei), key, []unsignedAuth{ - { - nonce: 0, - key: key, - }, - }) - b.SendTx(context.Background(), txA) + txA := pricedSetCodeTx(0, 250000, uint256.NewInt(params.GWei), uint256.NewInt(params.GWei), key, []unsignedAuth{{nonce: 0, key: key}}) + if err := b.SendTx(context.Background(), txA); err != nil { + t.Fatalf("Failed to submit tx: %v", err) + } + for { + pending, _ := b.TxPool().ContentFrom(address) + if len(pending) == 1 { + break + } + time.Sleep(100 * time.Millisecond) + } txB := makeTx(1, nil, nil, key) err := b.SendTx(context.Background(), txB) diff --git a/internal/cmdtest/test_cmd.go b/internal/cmdtest/test_cmd.go index 4890d0b7c61..f6f0425598a 100644 --- a/internal/cmdtest/test_cmd.go +++ b/internal/cmdtest/test_cmd.go @@ -237,7 +237,7 @@ func (tt *TestCmd) Kill() { } func (tt *TestCmd) withKillTimeout(fn func()) { - timeout := time.AfterFunc(30*time.Second, func() { + timeout := time.AfterFunc(2*time.Minute, func() { tt.Log("killing the child process (timeout)") tt.Kill() }) From 6bc57579d14063c0f2a9c7da5022d25b3a850cf3 Mon Sep 17 00:00:00 2001 From: maskpp Date: Thu, 8 May 2025 21:21:48 +0800 Subject: [PATCH 159/658] core/types: delete unused test variable (#31776) Delete the unused `Account.PrivateKey` variable. --- core/types/account.go | 12 ++++-------- core/types/gen_account.go | 22 ++++++++-------------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/core/types/account.go b/core/types/account.go index 52ce184cda5..bcfb83418c8 100644 --- a/core/types/account.go +++ b/core/types/account.go @@ -38,17 +38,13 @@ type Account struct { Storage map[common.Hash]common.Hash `json:"storage,omitempty"` Balance *big.Int `json:"balance" gencodec:"required"` Nonce uint64 `json:"nonce,omitempty"` - - // used in tests - PrivateKey []byte `json:"secretKey,omitempty"` } type accountMarshaling struct { - Code hexutil.Bytes - Balance *math.HexOrDecimal256 - Nonce math.HexOrDecimal64 - Storage map[storageJSON]storageJSON - PrivateKey hexutil.Bytes + Code hexutil.Bytes + Balance *math.HexOrDecimal256 + Nonce math.HexOrDecimal64 + Storage map[storageJSON]storageJSON } // storageJSON represents a 256 bit byte array, but allows less than 256 bits when diff --git a/core/types/gen_account.go b/core/types/gen_account.go index 4e475896a73..89165ee3ad8 100644 --- a/core/types/gen_account.go +++ b/core/types/gen_account.go @@ -17,11 +17,10 @@ var _ = (*accountMarshaling)(nil) // MarshalJSON marshals as JSON. func (a Account) MarshalJSON() ([]byte, error) { type Account struct { - Code hexutil.Bytes `json:"code,omitempty"` - Storage map[storageJSON]storageJSON `json:"storage,omitempty"` - Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"` - Nonce math.HexOrDecimal64 `json:"nonce,omitempty"` - PrivateKey hexutil.Bytes `json:"secretKey,omitempty"` + Code hexutil.Bytes `json:"code,omitempty"` + Storage map[storageJSON]storageJSON `json:"storage,omitempty"` + Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"` + Nonce math.HexOrDecimal64 `json:"nonce,omitempty"` } var enc Account enc.Code = a.Code @@ -33,18 +32,16 @@ func (a Account) MarshalJSON() ([]byte, error) { } enc.Balance = (*math.HexOrDecimal256)(a.Balance) enc.Nonce = math.HexOrDecimal64(a.Nonce) - enc.PrivateKey = a.PrivateKey return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. func (a *Account) UnmarshalJSON(input []byte) error { type Account struct { - Code *hexutil.Bytes `json:"code,omitempty"` - Storage map[storageJSON]storageJSON `json:"storage,omitempty"` - Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"` - Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"` - PrivateKey *hexutil.Bytes `json:"secretKey,omitempty"` + Code *hexutil.Bytes `json:"code,omitempty"` + Storage map[storageJSON]storageJSON `json:"storage,omitempty"` + Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"` + Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"` } var dec Account if err := json.Unmarshal(input, &dec); err != nil { @@ -66,8 +63,5 @@ func (a *Account) UnmarshalJSON(input []byte) error { if dec.Nonce != nil { a.Nonce = uint64(*dec.Nonce) } - if dec.PrivateKey != nil { - a.PrivateKey = *dec.PrivateKey - } return nil } From 0f48cbf017d6ee721ec2b74fced95b05c155f72b Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 8 May 2025 22:27:01 +0800 Subject: [PATCH 160/658] core, triedb/pathdb: bail out error if write state history fails (#31781) This PR fixes an issue that could lead to data corruption. Writing the state history may fail due to insufficient disk space or other potential errors. With this change, the entire state insertion will be aborted instead of silently ignoring the error. Without this fix, state transitions would continue while the associated state history is lost. After a restart, the resulting gap would be detected, making recovery impossible. --- core/rawdb/accessors_state.go | 24 ++++++++++++++++-------- triedb/pathdb/disklayer.go | 2 ++ triedb/pathdb/history.go | 5 +++-- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/core/rawdb/accessors_state.go b/core/rawdb/accessors_state.go index adc77fae835..41e15debe94 100644 --- a/core/rawdb/accessors_state.go +++ b/core/rawdb/accessors_state.go @@ -258,13 +258,21 @@ func ReadStateHistory(db ethdb.AncientReaderOp, id uint64) ([]byte, []byte, []by // WriteStateHistory writes the provided state history to database. Compute the // position of state history in freezer by minus one since the id of first state // history starts from one(zero for initial state). -func WriteStateHistory(db ethdb.AncientWriter, id uint64, meta []byte, accountIndex []byte, storageIndex []byte, accounts []byte, storages []byte) { - db.ModifyAncients(func(op ethdb.AncientWriteOp) error { - op.AppendRaw(stateHistoryMeta, id-1, meta) - op.AppendRaw(stateHistoryAccountIndex, id-1, accountIndex) - op.AppendRaw(stateHistoryStorageIndex, id-1, storageIndex) - op.AppendRaw(stateHistoryAccountData, id-1, accounts) - op.AppendRaw(stateHistoryStorageData, id-1, storages) - return nil +func WriteStateHistory(db ethdb.AncientWriter, id uint64, meta []byte, accountIndex []byte, storageIndex []byte, accounts []byte, storages []byte) error { + _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + if err := op.AppendRaw(stateHistoryMeta, id-1, meta); err != nil { + return err + } + if err := op.AppendRaw(stateHistoryAccountIndex, id-1, accountIndex); err != nil { + return err + } + if err := op.AppendRaw(stateHistoryStorageIndex, id-1, storageIndex); err != nil { + return err + } + if err := op.AppendRaw(stateHistoryAccountData, id-1, accounts); err != nil { + return err + } + return op.AppendRaw(stateHistoryStorageData, id-1, storages) }) + return err } diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go index 184f6430a22..f3a60a507d0 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -231,6 +231,8 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) { oldest uint64 ) if dl.db.freezer != nil { + // Bail out with an error if writing the state history fails. + // This can happen, for example, if the device is full. err := writeHistory(dl.db.freezer, bottom) if err != nil { return nil, err diff --git a/triedb/pathdb/history.go b/triedb/pathdb/history.go index c063e453711..aed0296da5b 100644 --- a/triedb/pathdb/history.go +++ b/triedb/pathdb/history.go @@ -542,8 +542,9 @@ func writeHistory(writer ethdb.AncientWriter, dl *diffLayer) error { indexSize := common.StorageSize(len(accountIndex) + len(storageIndex)) // Write history data into five freezer table respectively. - rawdb.WriteStateHistory(writer, dl.stateID(), history.meta.encode(), accountIndex, storageIndex, accountData, storageData) - + if err := rawdb.WriteStateHistory(writer, dl.stateID(), history.meta.encode(), accountIndex, storageIndex, accountData, storageData); err != nil { + return err + } historyDataBytesMeter.Mark(int64(dataSize)) historyIndexBytesMeter.Mark(int64(indexSize)) historyBuildTimeMeter.UpdateSince(start) From 485ff4bbff83abbf27a82a5660545e713c992c3f Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 8 May 2025 22:28:16 +0800 Subject: [PATCH 161/658] core: implement in-block prefetcher (#31557) This pull request enhances the block prefetcher by executing transactions in parallel to warm the cache alongside the main block processor. Unlike the original prefetcher, which only executes the next block and is limited to chain syncing, the new implementation can be applied to any block. This makes it useful not only during chain sync but also for regular block insertion after the initial sync. --------- Co-authored-by: Marius van der Wijden --- core/blockchain.go | 145 ++++++++++++++++-------------- core/blockchain_insert.go | 1 + core/state/database.go | 13 ++- core/state/reader.go | 180 ++++++++++++++++++++++++++++++++++---- core/state/statedb.go | 19 +++- core/state_prefetcher.go | 111 ++++++++++++++++------- core/state_transition.go | 4 +- triedb/pathdb/metrics.go | 4 +- 8 files changed, 351 insertions(+), 126 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index b0c1b119fc5..3c691600eb7 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -92,8 +92,10 @@ var ( blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil) blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil) - blockPrefetchExecuteTimer = metrics.NewRegisteredTimer("chain/prefetch/executes", nil) - blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil) + blockPrefetchExecuteTimer = metrics.NewRegisteredResettingTimer("chain/prefetch/executes", nil) + blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil) + blockPrefetchTxsInvalidMeter = metrics.NewRegisteredMeter("chain/prefetch/txs/invalid", nil) + blockPrefetchTxsValidMeter = metrics.NewRegisteredMeter("chain/prefetch/txs/valid", nil) errInsertionInterrupted = errors.New("insertion is interrupted") errChainStopped = errors.New("blockchain is stopped") @@ -1758,18 +1760,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bc.reportBlock(block, nil, err) return nil, it.index, err } - // No validation errors for the first block (or chain prefix skipped) - var activeState *state.StateDB - defer func() { - // The chain importer is starting and stopping trie prefetchers. If a bad - // block or other error is hit however, an early return may not properly - // terminate the background threads. This defer ensures that we clean up - // and dangling prefetcher, without deferring each and holding on live refs. - if activeState != nil { - activeState.StopPrefetcher() - } - }() - // Track the singleton witness from this chain insertion (if any) var witness *stateless.Witness @@ -1825,63 +1815,20 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness continue } // Retrieve the parent block and it's state to execute on top - start := time.Now() parent := it.previous() if parent == nil { parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1) } - statedb, err := state.New(parent.Root, bc.statedb) - if err != nil { - return nil, it.index, err - } - - // If we are past Byzantium, enable prefetching to pull in trie node paths - // while processing transactions. Before Byzantium the prefetcher is mostly - // useless due to the intermediate root hashing after each transaction. - if bc.chainConfig.IsByzantium(block.Number()) { - // Generate witnesses either if we're self-testing, or if it's the - // only block being inserted. A bit crude, but witnesses are huge, - // so we refuse to make an entire chain of them. - if bc.vmConfig.StatelessSelfValidation || (makeWitness && len(chain) == 1) { - witness, err = stateless.NewWitness(block.Header(), bc) - if err != nil { - return nil, it.index, err - } - } - statedb.StartPrefetcher("chain", witness) - } - activeState = statedb - - // If we have a followup block, run that against the current state to pre-cache - // transactions and probabilistically some of the account/storage trie nodes. - var followupInterrupt atomic.Bool - if !bc.cacheConfig.TrieCleanNoPrefetch { - if followup, err := it.peek(); followup != nil && err == nil { - throwaway, _ := state.New(parent.Root, bc.statedb) - - go func(start time.Time, followup *types.Block, throwaway *state.StateDB) { - // Disable tracing for prefetcher executions. - vmCfg := bc.vmConfig - vmCfg.Tracer = nil - bc.prefetcher.Prefetch(followup, throwaway, vmCfg, &followupInterrupt) - - blockPrefetchExecuteTimer.Update(time.Since(start)) - if followupInterrupt.Load() { - blockPrefetchInterruptMeter.Mark(1) - } - }(time.Now(), followup, throwaway) - } - } - // The traced section of block import. - res, err := bc.processBlock(block, statedb, start, setHead) - followupInterrupt.Store(true) + start := time.Now() + res, err := bc.processBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1) if err != nil { return nil, it.index, err } // Report the import stats before returning the various results stats.processed++ stats.usedGas += res.usedGas + witness = res.witness var snapDiffItems, snapBufItems common.StorageSize if bc.snaps != nil { @@ -1937,11 +1884,74 @@ type blockProcessingResult struct { usedGas uint64 procTime time.Duration status WriteStatus + witness *stateless.Witness } // processBlock executes and validates the given block. If there was no error // it writes the block and associated state to database. -func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) { +func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) { + var ( + err error + startTime = time.Now() + statedb *state.StateDB + interrupt atomic.Bool + ) + defer interrupt.Store(true) // terminate the prefetch at the end + + if bc.cacheConfig.TrieCleanNoPrefetch { + statedb, err = state.New(parentRoot, bc.statedb) + if err != nil { + return nil, err + } + } else { + // If prefetching is enabled, run that against the current state to pre-cache + // transactions and probabilistically some of the account/storage trie nodes. + // + // Note: the main processor and prefetcher share the same reader with a local + // cache for mitigating the overhead of state access. + reader, err := bc.statedb.ReaderWithCache(parentRoot) + if err != nil { + return nil, err + } + throwaway, err := state.NewWithReader(parentRoot, bc.statedb, reader) + if err != nil { + return nil, err + } + statedb, err = state.NewWithReader(parentRoot, bc.statedb, reader) + if err != nil { + return nil, err + } + go func(start time.Time, throwaway *state.StateDB, block *types.Block) { + // Disable tracing for prefetcher executions. + vmCfg := bc.vmConfig + vmCfg.Tracer = nil + bc.prefetcher.Prefetch(block, throwaway, vmCfg, &interrupt) + + blockPrefetchExecuteTimer.Update(time.Since(start)) + if interrupt.Load() { + blockPrefetchInterruptMeter.Mark(1) + } + }(time.Now(), throwaway, block) + } + + // If we are past Byzantium, enable prefetching to pull in trie node paths + // while processing transactions. Before Byzantium the prefetcher is mostly + // useless due to the intermediate root hashing after each transaction. + var witness *stateless.Witness + if bc.chainConfig.IsByzantium(block.Number()) { + // Generate witnesses either if we're self-testing, or if it's the + // only block being inserted. A bit crude, but witnesses are huge, + // so we refuse to make an entire chain of them. + if bc.vmConfig.StatelessSelfValidation || makeWitness { + witness, err = stateless.NewWitness(block.Header(), bc) + if err != nil { + return nil, err + } + } + statedb.StartPrefetcher("chain", witness) + defer statedb.StopPrefetcher() + } + if bc.logger != nil && bc.logger.OnBlockStart != nil { bc.logger.OnBlockStart(tracing.BlockEvent{ Block: block, @@ -2000,7 +2010,7 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s } } xvtime := time.Since(xvstart) - proctime := time.Since(start) // processing + validation + cross validation + proctime := time.Since(startTime) // processing + validation + cross validation // Update the metrics touched during block processing and validation accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing) @@ -2041,9 +2051,14 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits) - blockInsertTimer.UpdateSince(start) - - return &blockProcessingResult{usedGas: res.GasUsed, procTime: proctime, status: status}, nil + blockInsertTimer.UpdateSince(startTime) + + return &blockProcessingResult{ + usedGas: res.GasUsed, + procTime: proctime, + status: status, + witness: witness, + }, nil } // insertSideChain is called when an import batch hits upon a pruned ancestor diff --git a/core/blockchain_insert.go b/core/blockchain_insert.go index ec3f771818f..b4bd4446061 100644 --- a/core/blockchain_insert.go +++ b/core/blockchain_insert.go @@ -138,6 +138,7 @@ func (it *insertIterator) next() (*types.Block, error) { // // Both header and body validation errors (nil too) is cached into the iterator // to avoid duplicating work on the following next() call. +// nolint:unused func (it *insertIterator) peek() (*types.Block, error) { // If we reached the end of the chain, abort if it.index+1 >= len(it.chain) { diff --git a/core/state/database.go b/core/state/database.go index faf4954650b..cef59cccfb8 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -34,10 +34,10 @@ import ( const ( // Number of codehash->size associations to keep. - codeSizeCacheSize = 100000 + codeSizeCacheSize = 1_000_000 // 4 megabytes in total // Cache size granted for caching clean code. - codeCacheSize = 64 * 1024 * 1024 + codeCacheSize = 256 * 1024 * 1024 // Number of address->curve point associations to keep. pointCacheSize = 4096 @@ -208,6 +208,15 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), combined), nil } +// ReaderWithCache creates a state reader with internal local cache. +func (db *CachingDB) ReaderWithCache(stateRoot common.Hash) (Reader, error) { + reader, err := db.Reader(stateRoot) + if err != nil { + return nil, err + } + return newReaderWithCache(reader), nil +} + // OpenTrie opens the main account trie at a specific root hash. func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) { if db.triedb.IsVerkle() { diff --git a/core/state/reader.go b/core/state/reader.go index a0f15dfcc8b..5ad0385e9e7 100644 --- a/core/state/reader.go +++ b/core/state/reader.go @@ -18,6 +18,7 @@ package state import ( "errors" + "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/lru" @@ -32,6 +33,24 @@ import ( "github.com/ethereum/go-ethereum/triedb/database" ) +// bufferPool holds the buffers for keccak calculation. +var bufferPool = sync.Pool{ + New: func() interface{} { + return crypto.NewKeccakState() + }, +} + +// allocBuff allocates the keccak buffer from the pool +func allocBuff() crypto.KeccakState { + return bufferPool.Get().(crypto.KeccakState) +} + +// releaseBuff returns the provided keccak buffer to the pool. It's unnecessary +// to clear the buffer, as it will be cleared before the calculation. +func releaseBuff(buff crypto.KeccakState) { + bufferPool.Put(buff) +} + // ContractCodeReader defines the interface for accessing contract code. type ContractCodeReader interface { // Code retrieves a particular contract's code. @@ -51,6 +70,9 @@ type ContractCodeReader interface { // StateReader defines the interface for accessing accounts and storage slots // associated with a specific state. +// +// StateReader is assumed to be thread-safe and implementation must take care +// of the concurrency issue by themselves. type StateReader interface { // Account retrieves the account associated with a particular address. // @@ -70,6 +92,9 @@ type StateReader interface { // Reader defines the interface for accessing accounts, storage slots and contract // code associated with a specific state. +// +// Reader is assumed to be thread-safe and implementation must take care of the +// concurrency issue by themselves. type Reader interface { ContractCodeReader StateReader @@ -77,6 +102,8 @@ type Reader interface { // cachingCodeReader implements ContractCodeReader, accessing contract code either in // local key-value store or the shared code cache. +// +// cachingCodeReader is safe for concurrent access. type cachingCodeReader struct { db ethdb.KeyValueReader @@ -123,18 +150,14 @@ func (r *cachingCodeReader) CodeSize(addr common.Address, codeHash common.Hash) return len(code), nil } -// flatReader wraps a database state reader. +// flatReader wraps a database state reader and is safe for concurrent access. type flatReader struct { reader database.StateReader - buff crypto.KeccakState } // newFlatReader constructs a state reader with on the given state root. func newFlatReader(reader database.StateReader) *flatReader { - return &flatReader{ - reader: reader, - buff: crypto.NewKeccakState(), - } + return &flatReader{reader: reader} } // Account implements StateReader, retrieving the account specified by the address. @@ -144,7 +167,10 @@ func newFlatReader(reader database.StateReader) *flatReader { // // The returned account might be nil if it's not existent. func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) { - account, err := r.reader.Account(crypto.HashData(r.buff, addr.Bytes())) + buff := allocBuff() + defer releaseBuff(buff) + + account, err := r.reader.Account(crypto.HashData(buff, addr.Bytes())) if err != nil { return nil, err } @@ -174,8 +200,11 @@ func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) { // // The returned storage slot might be empty if it's not existent. func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) { - addrHash := crypto.HashData(r.buff, addr.Bytes()) - slotHash := crypto.HashData(r.buff, key.Bytes()) + buff := allocBuff() + defer releaseBuff(buff) + + addrHash := crypto.HashData(buff, addr.Bytes()) + slotHash := crypto.HashData(buff, key.Bytes()) ret, err := r.reader.Storage(addrHash, slotHash) if err != nil { return common.Hash{}, err @@ -196,13 +225,20 @@ func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash, // trieReader implements the StateReader interface, providing functions to access // state from the referenced trie. +// +// trieReader is safe for concurrent read. type trieReader struct { - root common.Hash // State root which uniquely represent a state - db *triedb.Database // Database for loading trie - buff crypto.KeccakState // Buffer for keccak256 hashing - mainTrie Trie // Main trie, resolved in constructor + root common.Hash // State root which uniquely represent a state + db *triedb.Database // Database for loading trie + buff crypto.KeccakState // Buffer for keccak256 hashing + + // Main trie, resolved in constructor. Note either the Merkle-Patricia-tree + // or Verkle-tree is not safe for concurrent read. + mainTrie Trie + subRoots map[common.Address]common.Hash // Set of storage roots, cached when the account is resolved subTries map[common.Address]Trie // Group of storage tries, cached when it's resolved + lock sync.Mutex // Lock for protecting concurrent read } // trieReader constructs a trie reader of the specific state. An error will be @@ -230,11 +266,8 @@ func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCach }, nil } -// Account implements StateReader, retrieving the account specified by the address. -// -// An error will be returned if the trie state is corrupted. An nil account -// will be returned if it's not existent in the trie. -func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) { +// account is the inner version of Account and assumes the r.lock is already held. +func (r *trieReader) account(addr common.Address) (*types.StateAccount, error) { account, err := r.mainTrie.GetAccount(addr) if err != nil { return nil, err @@ -247,12 +280,26 @@ func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) { return account, nil } +// Account implements StateReader, retrieving the account specified by the address. +// +// An error will be returned if the trie state is corrupted. An nil account +// will be returned if it's not existent in the trie. +func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) { + r.lock.Lock() + defer r.lock.Unlock() + + return r.account(addr) +} + // Storage implements StateReader, retrieving the storage slot specified by the // address and slot key. // // An error will be returned if the trie state is corrupted. An empty storage // slot will be returned if it's not existent in the trie. func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) { + r.lock.Lock() + defer r.lock.Unlock() + var ( tr Trie found bool @@ -268,7 +315,7 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, // The storage slot is accessed without account caching. It's unexpected // behavior but try to resolve the account first anyway. if !ok { - _, err := r.Account(addr) + _, err := r.account(addr) if err != nil { return common.Hash{}, err } @@ -293,6 +340,9 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, // multiStateReader is the aggregation of a list of StateReader interface, // providing state access by leveraging all readers. The checking priority // is determined by the position in the reader list. +// +// multiStateReader is safe for concurrent read and assumes all underlying +// readers are thread-safe as well. type multiStateReader struct { readers []StateReader // List of state readers, sorted by checking priority } @@ -358,3 +408,95 @@ func newReader(codeReader ContractCodeReader, stateReader StateReader) *reader { StateReader: stateReader, } } + +// readerWithCache is a wrapper around Reader that maintains additional state caches +// to support concurrent state access. +type readerWithCache struct { + Reader // safe for concurrent read + + // Previously resolved state entries. + accounts map[common.Address]*types.StateAccount + accountLock sync.RWMutex + + // List of storage buckets, each of which is thread-safe. + // This reader is typically used in scenarios requiring concurrent + // access to storage. Using multiple buckets helps mitigate + // the overhead caused by locking. + storageBuckets [16]struct { + lock sync.RWMutex + storages map[common.Address]map[common.Hash]common.Hash + } +} + +// newReaderWithCache constructs the reader with local cache. +func newReaderWithCache(reader Reader) *readerWithCache { + r := &readerWithCache{ + Reader: reader, + accounts: make(map[common.Address]*types.StateAccount), + } + for i := range r.storageBuckets { + r.storageBuckets[i].storages = make(map[common.Address]map[common.Hash]common.Hash) + } + return r +} + +// Account implements StateReader, retrieving the account specified by the address. +// The returned account might be nil if it's not existent. +// +// An error will be returned if the state is corrupted in the underlying reader. +func (r *readerWithCache) Account(addr common.Address) (*types.StateAccount, error) { + // Try to resolve the requested account in the local cache + r.accountLock.RLock() + acct, ok := r.accounts[addr] + r.accountLock.RUnlock() + if ok { + return acct, nil + } + // Try to resolve the requested account from the underlying reader + acct, err := r.Reader.Account(addr) + if err != nil { + return nil, err + } + r.accountLock.Lock() + r.accounts[addr] = acct + r.accountLock.Unlock() + return acct, nil +} + +// Storage implements StateReader, retrieving the storage slot specified by the +// address and slot key. The returned storage slot might be empty if it's not +// existent. +// +// An error will be returned if the state is corrupted in the underlying reader. +func (r *readerWithCache) Storage(addr common.Address, slot common.Hash) (common.Hash, error) { + var ( + value common.Hash + ok bool + bucket = &r.storageBuckets[addr[0]&0x0f] + ) + // Try to resolve the requested storage slot in the local cache + bucket.lock.RLock() + slots, ok := bucket.storages[addr] + if ok { + value, ok = slots[slot] + } + bucket.lock.RUnlock() + if ok { + return value, nil + } + // Try to resolve the requested storage slot from the underlying reader + value, err := r.Reader.Storage(addr, slot) + if err != nil { + return common.Hash{}, err + } + bucket.lock.Lock() + slots, ok = bucket.storages[addr] + if !ok { + slots = make(map[common.Hash]common.Hash) + bucket.storages[addr] = slots + } + slots[slot] = value + bucket.lock.Unlock() + + return value, nil +} diff --git a/core/state/statedb.go b/core/state/statedb.go index e3f5b9e1a0a..9378cae7de3 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -159,11 +159,17 @@ type StateDB struct { // New creates a new state from a given trie. func New(root common.Hash, db Database) (*StateDB, error) { - tr, err := db.OpenTrie(root) + reader, err := db.Reader(root) if err != nil { return nil, err } - reader, err := db.Reader(root) + return NewWithReader(root, db, reader) +} + +// NewWithReader creates a new state for the specified state root. Unlike New, +// this function accepts an additional Reader which is bound to the given root. +func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, error) { + tr, err := db.OpenTrie(root) if err != nil { return nil, err } @@ -392,6 +398,12 @@ func (s *StateDB) Database() Database { return s.db } +// Reader retrieves the low level database reader supporting the +// lower level operations. +func (s *StateDB) Reader() Reader { + return s.reader +} + func (s *StateDB) HasSelfDestructed(addr common.Address) bool { stateObject := s.getStateObject(addr) if stateObject != nil { @@ -650,11 +662,10 @@ func (s *StateDB) CreateContract(addr common.Address) { // Snapshots of the copied state cannot be applied to the copy. func (s *StateDB) Copy() *StateDB { // Copy all the basic fields, initialize the memory ones - reader, _ := s.db.Reader(s.originalRoot) // impossible to fail state := &StateDB{ db: s.db, trie: mustCopyTrie(s.trie), - reader: reader, + reader: s.reader, originalRoot: s.originalRoot, stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)), stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)), diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 805df5ef622..f3129f57cdb 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -17,17 +17,22 @@ package core import ( + "bytes" + "runtime" "sync/atomic" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" + "golang.org/x/sync/errgroup" ) -// statePrefetcher is a basic Prefetcher, which blindly executes a block on top -// of an arbitrary state with the goal of prefetching potentially useful state -// data from disk before the main block processor start executing. +// statePrefetcher is a basic Prefetcher that executes transactions from a block +// on top of the parent state, aiming to prefetch potentially useful state data +// from disk. Transactions are executed in parallel to fully leverage the +// SSD's read performance. type statePrefetcher struct { config *params.ChainConfig // Chain configuration options chain *HeaderChain // Canonical block chain @@ -43,41 +48,81 @@ func newStatePrefetcher(config *params.ChainConfig, chain *HeaderChain) *statePr // Prefetch processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb, but any changes are discarded. The -// only goal is to pre-cache transaction signatures and state trie nodes. +// only goal is to warm the state caches. func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *atomic.Bool) { var ( - header = block.Header() - gaspool = new(GasPool).AddGas(block.GasLimit()) - blockContext = NewEVMBlockContext(header, p.chain, nil) - evm = vm.NewEVM(blockContext, statedb, p.config, cfg) - signer = types.MakeSigner(p.config, header.Number, header.Time) + fails atomic.Int64 + header = block.Header() + signer = types.MakeSigner(p.config, header.Number, header.Time) + workers errgroup.Group + reader = statedb.Reader() ) + workers.SetLimit(runtime.NumCPU() / 2) + // Iterate over and process the individual transactions - byzantium := p.config.IsByzantium(block.Number()) for i, tx := range block.Transactions() { - // If block precaching was interrupted, abort - if interrupt != nil && interrupt.Load() { - return - } - // Convert the transaction into an executable message and pre-cache its sender - msg, err := TransactionToMessage(tx, signer, header.BaseFee) - if err != nil { - return // Also invalid block, bail out - } - statedb.SetTxContext(tx.Hash(), i) + stateCpy := statedb.Copy() // closure + workers.Go(func() error { + // If block precaching was interrupted, abort + if interrupt != nil && interrupt.Load() { + return nil + } + // Preload the touched accounts and storage slots in advance + sender, err := types.Sender(signer, tx) + if err != nil { + fails.Add(1) + return nil + } + reader.Account(sender) - // We attempt to apply a transaction. The goal is not to execute - // the transaction successfully, rather to warm up touched data slots. - if _, err := ApplyMessage(evm, msg, gaspool); err != nil { - return // Ugh, something went horribly wrong, bail out - } - // If we're pre-byzantium, pre-load trie nodes for the intermediate root - if !byzantium { - statedb.IntermediateRoot(true) - } - } - // If were post-byzantium, pre-load trie nodes for the final root hash - if byzantium { - statedb.IntermediateRoot(true) + if tx.To() != nil { + account, _ := reader.Account(*tx.To()) + + // Preload the contract code if the destination has non-empty code + if account != nil && !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) { + reader.Code(*tx.To(), common.BytesToHash(account.CodeHash)) + } + } + for _, list := range tx.AccessList() { + reader.Account(list.Address) + if len(list.StorageKeys) > 0 { + for _, slot := range list.StorageKeys { + reader.Storage(list.Address, slot) + } + } + } + // Execute the message to preload the implicit touched states + evm := vm.NewEVM(NewEVMBlockContext(header, p.chain, nil), stateCpy, p.config, cfg) + + // Convert the transaction into an executable message and pre-cache its sender + msg, err := TransactionToMessage(tx, signer, header.BaseFee) + if err != nil { + fails.Add(1) + return nil // Also invalid block, bail out + } + // Disable the nonce check + msg.SkipNonceChecks = true + + stateCpy.SetTxContext(tx.Hash(), i) + + // We attempt to apply a transaction. The goal is not to execute + // the transaction successfully, rather to warm up touched data slots. + if _, err := ApplyMessage(evm, msg, new(GasPool).AddGas(block.GasLimit())); err != nil { + fails.Add(1) + return nil // Ugh, something went horribly wrong, bail out + } + // Pre-load trie nodes for the intermediate root. + // + // This operation incurs significant memory allocations due to + // trie hashing and node decoding. TODO(rjl493456442): investigate + // ways to mitigate this overhead. + stateCpy.IntermediateRoot(true) + return nil + }) } + workers.Wait() + + blockPrefetchTxsValidMeter.Mark(int64(len(block.Transactions())) - fails.Load()) + blockPrefetchTxsInvalidMeter.Mark(fails.Load()) + return } diff --git a/core/state_transition.go b/core/state_transition.go index f9c9a2ab5a5..ff2051ddd2e 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -159,7 +159,9 @@ type Message struct { // When SkipNonceChecks is true, the message nonce is not checked against the // account nonce in state. - // This field will be set to true for operations like RPC eth_call. + // + // This field will be set to true for operations like RPC eth_call + // or the state prefetching. SkipNonceChecks bool // When SkipFromEOACheck is true, the message sender is not checked to be an EOA. diff --git a/triedb/pathdb/metrics.go b/triedb/pathdb/metrics.go index 45dad6f1ae9..abe2dfe1f64 100644 --- a/triedb/pathdb/metrics.go +++ b/triedb/pathdb/metrics.go @@ -46,7 +46,7 @@ var ( nodeDiskFalseMeter = metrics.NewRegisteredMeter("pathdb/disk/false", nil) nodeDiffFalseMeter = metrics.NewRegisteredMeter("pathdb/diff/false", nil) - commitTimeTimer = metrics.NewRegisteredTimer("pathdb/commit/time", nil) + commitTimeTimer = metrics.NewRegisteredResettingTimer("pathdb/commit/time", nil) commitNodesMeter = metrics.NewRegisteredMeter("pathdb/commit/nodes", nil) commitBytesMeter = metrics.NewRegisteredMeter("pathdb/commit/bytes", nil) @@ -57,7 +57,7 @@ var ( gcStorageMeter = metrics.NewRegisteredMeter("pathdb/gc/storage/count", nil) gcStorageBytesMeter = metrics.NewRegisteredMeter("pathdb/gc/storage/bytes", nil) - historyBuildTimeMeter = metrics.NewRegisteredTimer("pathdb/history/time", nil) + historyBuildTimeMeter = metrics.NewRegisteredResettingTimer("pathdb/history/time", nil) historyDataBytesMeter = metrics.NewRegisteredMeter("pathdb/history/bytes/data", nil) historyIndexBytesMeter = metrics.NewRegisteredMeter("pathdb/history/bytes/index", nil) ) From 0eb2eeea908d654b971249142fcbb735ba2c6923 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 9 May 2025 07:52:40 +0200 Subject: [PATCH 162/658] all: create global hasher pool (#31769) This PR creates a global hasher pool that can be used by all packages. It also removes a bunch of the package local pools. It also updates a few locations to use available hashers or the global hashing pool to reduce allocations all over the codebase. This change should reduce global allocation count by ~1% --------- Co-authored-by: Gary Rong --- core/rawdb/accessors_trie.go | 28 ++-------------------------- core/state/reader.go | 30 +++--------------------------- core/types/hashing.go | 3 +-- core/vm/evm.go | 3 ++- core/vm/instructions.go | 7 +------ core/vm/interpreter.go | 2 +- crypto/crypto.go | 15 +++++++++++++-- trie/hasher.go | 2 +- trie/sync.go | 24 +----------------------- triedb/pathdb/disklayer.go | 26 ++------------------------ triedb/pathdb/execute.go | 15 +++++++-------- 11 files changed, 34 insertions(+), 121 deletions(-) diff --git a/core/rawdb/accessors_trie.go b/core/rawdb/accessors_trie.go index 8bd6b71eeec..e154ab527b8 100644 --- a/core/rawdb/accessors_trie.go +++ b/core/rawdb/accessors_trie.go @@ -18,7 +18,6 @@ package rawdb import ( "fmt" - "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -45,25 +44,6 @@ const HashScheme = "hash" // on extra state diffs to survive deep reorg. const PathScheme = "path" -// hasher is used to compute the sha256 hash of the provided data. -type hasher struct{ sha crypto.KeccakState } - -var hasherPool = sync.Pool{ - New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} }, -} - -func newHasher() *hasher { - return hasherPool.Get().(*hasher) -} - -func (h *hasher) hash(data []byte) common.Hash { - return crypto.HashData(h.sha, data) -} - -func (h *hasher) release() { - hasherPool.Put(h) -} - // ReadAccountTrieNode retrieves the account trie node with the specified node path. func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte { data, _ := db.Get(accountTrieNodeKey(path)) @@ -170,9 +150,7 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c if len(blob) == 0 { return false } - h := newHasher() - defer h.release() - return h.hash(blob) == hash // exists but not match + return crypto.Keccak256Hash(blob) == hash // exists but not match default: panic(fmt.Sprintf("Unknown scheme %v", scheme)) } @@ -194,9 +172,7 @@ func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash if len(blob) == 0 { return nil } - h := newHasher() - defer h.release() - if h.hash(blob) != hash { + if crypto.Keccak256Hash(blob) != hash { return nil // exists but not match } return blob diff --git a/core/state/reader.go b/core/state/reader.go index 5ad0385e9e7..09edf6ab8db 100644 --- a/core/state/reader.go +++ b/core/state/reader.go @@ -33,24 +33,6 @@ import ( "github.com/ethereum/go-ethereum/triedb/database" ) -// bufferPool holds the buffers for keccak calculation. -var bufferPool = sync.Pool{ - New: func() interface{} { - return crypto.NewKeccakState() - }, -} - -// allocBuff allocates the keccak buffer from the pool -func allocBuff() crypto.KeccakState { - return bufferPool.Get().(crypto.KeccakState) -} - -// releaseBuff returns the provided keccak buffer to the pool. It's unnecessary -// to clear the buffer, as it will be cleared before the calculation. -func releaseBuff(buff crypto.KeccakState) { - bufferPool.Put(buff) -} - // ContractCodeReader defines the interface for accessing contract code. type ContractCodeReader interface { // Code retrieves a particular contract's code. @@ -167,10 +149,7 @@ func newFlatReader(reader database.StateReader) *flatReader { // // The returned account might be nil if it's not existent. func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) { - buff := allocBuff() - defer releaseBuff(buff) - - account, err := r.reader.Account(crypto.HashData(buff, addr.Bytes())) + account, err := r.reader.Account(crypto.Keccak256Hash(addr.Bytes())) if err != nil { return nil, err } @@ -200,11 +179,8 @@ func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) { // // The returned storage slot might be empty if it's not existent. func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) { - buff := allocBuff() - defer releaseBuff(buff) - - addrHash := crypto.HashData(buff, addr.Bytes()) - slotHash := crypto.HashData(buff, key.Bytes()) + addrHash := crypto.Keccak256Hash(addr.Bytes()) + slotHash := crypto.Keccak256Hash(key.Bytes()) ret, err := r.reader.Storage(addrHash, slotHash) if err != nil { return common.Hash{}, err diff --git a/core/types/hashing.go b/core/types/hashing.go index 224d7a87eaf..3cc22d50d10 100644 --- a/core/types/hashing.go +++ b/core/types/hashing.go @@ -25,12 +25,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/crypto/sha3" ) // hasherPool holds LegacyKeccak256 hashers for rlpHash. var hasherPool = sync.Pool{ - New: func() interface{} { return sha3.NewLegacyKeccak256() }, + New: func() interface{} { return crypto.NewKeccakState() }, } // encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding. diff --git a/core/vm/evm.go b/core/vm/evm.go index c28dcb25543..ecb0f118ecc 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -555,7 +555,8 @@ func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *ui // The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:] // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { - contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), crypto.Keccak256(code)) + inithash := crypto.HashData(evm.interpreter.hasher, code) + contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:]) return evm.create(caller, code, gas, endowment, contractAddr, CREATE2) } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 0b3b1d15690..63bb6d2d51c 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -22,7 +22,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -234,11 +233,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( offset, size := scope.Stack.pop(), scope.Stack.peek() data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) - if interpreter.hasher == nil { - interpreter.hasher = crypto.NewKeccakState() - } else { - interpreter.hasher.Reset() - } + interpreter.hasher.Reset() interpreter.hasher.Write(data) interpreter.hasher.Read(interpreter.hasherBuf[:]) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index a0038d1aa83..a62c3c843d9 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -150,7 +150,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { } } evm.Config.ExtraEips = extraEips - return &EVMInterpreter{evm: evm, table: table} + return &EVMInterpreter{evm: evm, table: table, hasher: crypto.NewKeccakState()} } // Run loops and evaluates the contract's code with the given input data and returns diff --git a/crypto/crypto.go b/crypto/crypto.go index 13e9b134f06..09596c05ce8 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -28,6 +28,7 @@ import ( "io" "math/big" "os" + "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" @@ -73,6 +74,12 @@ func NewKeccakState() KeccakState { return sha3.NewLegacyKeccak256().(KeccakState) } +var hasherPool = sync.Pool{ + New: func() any { + return sha3.NewLegacyKeccak256().(KeccakState) + }, +} + // HashData hashes the provided data using the KeccakState and returns a 32 byte hash func HashData(kh KeccakState, data []byte) (h common.Hash) { kh.Reset() @@ -84,22 +91,26 @@ func HashData(kh KeccakState, data []byte) (h common.Hash) { // Keccak256 calculates and returns the Keccak256 hash of the input data. func Keccak256(data ...[]byte) []byte { b := make([]byte, 32) - d := NewKeccakState() + d := hasherPool.Get().(KeccakState) + d.Reset() for _, b := range data { d.Write(b) } d.Read(b) + hasherPool.Put(d) return b } // Keccak256Hash calculates and returns the Keccak256 hash of the input data, // converting it to an internal Hash data structure. func Keccak256Hash(data ...[]byte) (h common.Hash) { - d := NewKeccakState() + d := hasherPool.Get().(KeccakState) + d.Reset() for _, b := range data { d.Write(b) } d.Read(h[:]) + hasherPool.Put(d) return h } diff --git a/trie/hasher.go b/trie/hasher.go index 614640ae3a9..393cb0bd4d9 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -34,7 +34,7 @@ type hasher struct { // hasherPool holds pureHashers var hasherPool = sync.Pool{ - New: func() interface{} { + New: func() any { return &hasher{ tmp: make([]byte, 0, 550), // cap is as large as a full fullNode. sha: crypto.NewKeccakState(), diff --git a/trie/sync.go b/trie/sync.go index 3b7caae5b10..8d0ce6901c8 100644 --- a/trie/sync.go +++ b/trie/sync.go @@ -729,9 +729,7 @@ func (s *Sync) hasNode(owner common.Hash, path []byte, hash common.Hash) (exists } else { blob = rawdb.ReadStorageTrieNode(s.database, owner, path) } - h := newBlobHasher() - defer h.release() - exists = hash == h.hash(blob) + exists = hash == crypto.Keccak256Hash(blob) inconsistent = !exists && len(blob) != 0 return exists, inconsistent } @@ -746,23 +744,3 @@ func ResolvePath(path []byte) (common.Hash, []byte) { } return owner, path } - -// blobHasher is used to compute the sha256 hash of the provided data. -type blobHasher struct{ state crypto.KeccakState } - -// blobHasherPool is the pool for reusing pre-allocated hash state. -var blobHasherPool = sync.Pool{ - New: func() interface{} { return &blobHasher{state: crypto.NewKeccakState()} }, -} - -func newBlobHasher() *blobHasher { - return blobHasherPool.Get().(*blobHasher) -} - -func (h *blobHasher) hash(data []byte) common.Hash { - return crypto.HashData(h.state, data) -} - -func (h *blobHasher) release() { - blobHasherPool.Put(h) -} diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go index f3a60a507d0..b8869888d97 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -115,15 +115,12 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co dirtyNodeMissMeter.Mark(1) // Try to retrieve the trie node from the clean memory cache - h := newHasher() - defer h.release() - key := nodeCacheKey(owner, path) if dl.nodes != nil { if blob := dl.nodes.Get(nil, key); len(blob) > 0 { cleanNodeHitMeter.Mark(1) cleanNodeReadMeter.Mark(int64(len(blob))) - return blob, h.hash(blob), &nodeLoc{loc: locCleanCache, depth: depth}, nil + return blob, crypto.Keccak256Hash(blob), &nodeLoc{loc: locCleanCache, depth: depth}, nil } cleanNodeMissMeter.Mark(1) } @@ -138,7 +135,7 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co dl.nodes.Set(key, blob) cleanNodeWriteMeter.Mark(int64(len(blob))) } - return blob, h.hash(blob), &nodeLoc{loc: locDiskLayer, depth: depth}, nil + return blob, crypto.Keccak256Hash(blob), &nodeLoc{loc: locDiskLayer, depth: depth}, nil } // account directly retrieves the account RLP associated with a particular @@ -359,22 +356,3 @@ func (dl *diskLayer) resetCache() { dl.nodes.Reset() } } - -// hasher is used to compute the sha256 hash of the provided data. -type hasher struct{ sha crypto.KeccakState } - -var hasherPool = sync.Pool{ - New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} }, -} - -func newHasher() *hasher { - return hasherPool.Get().(*hasher) -} - -func (h *hasher) hash(data []byte) common.Hash { - return crypto.HashData(h.sha, data) -} - -func (h *hasher) release() { - hasherPool.Put(h) -} diff --git a/triedb/pathdb/execute.go b/triedb/pathdb/execute.go index 2400f280a33..db1e6792779 100644 --- a/triedb/pathdb/execute.go +++ b/triedb/pathdb/execute.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" @@ -85,10 +86,9 @@ func apply(db database.NodeDatabase, prevRoot common.Hash, postRoot common.Hash, func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address) error { // The account was present in prev-state, decode it from the // 'slim-rlp' format bytes. - h := newHasher() - defer h.release() + h := crypto.NewKeccakState() - addrHash := h.hash(addr.Bytes()) + addrHash := crypto.HashData(h, addr.Bytes()) prev, err := types.FullAccount(ctx.accounts[addr]) if err != nil { return err @@ -113,7 +113,7 @@ func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address) for key, val := range ctx.storages[addr] { tkey := key if ctx.rawStorageKey { - tkey = h.hash(key.Bytes()) + tkey = crypto.HashData(h, key.Bytes()) } var err error if len(val) == 0 { @@ -149,10 +149,9 @@ func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address) // account and storage is wiped out correctly. func deleteAccount(ctx *context, db database.NodeDatabase, addr common.Address) error { // The account must be existent in post-state, load the account. - h := newHasher() - defer h.release() + h := crypto.NewKeccakState() - addrHash := h.hash(addr.Bytes()) + addrHash := crypto.HashData(h, addr.Bytes()) blob, err := ctx.accountTrie.Get(addrHash.Bytes()) if err != nil { return err @@ -174,7 +173,7 @@ func deleteAccount(ctx *context, db database.NodeDatabase, addr common.Address) } tkey := key if ctx.rawStorageKey { - tkey = h.hash(key.Bytes()) + tkey = crypto.HashData(h, key.Bytes()) } if err := st.Delete(tkey.Bytes()); err != nil { return err From 0db99f4e409b05c109cffac26c15dd641757a3ab Mon Sep 17 00:00:00 2001 From: Satoshi Is Here <39875249+SatoshiIsHere@users.noreply.github.com> Date: Fri, 9 May 2025 19:56:00 +0900 Subject: [PATCH 163/658] core/types: reduce allocations in tx.EffectiveGasTip (#31598) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR introduces an allocation-free version of the Transaction.EffectiveGasTip method to improve performance by reducing memory allocations. ## Changes - Added a new `EffectiveGasTipInto` method that accepts a destination parameter to avoid memory allocations - Refactored the existing `EffectiveGasTip` method to use the new allocation-free implementation - Updated related methods (`EffectiveGasTipValue`, `EffectiveGasTipCmp`, `EffectiveGasTipIntCmp`) to use the allocation-free approach - Added tests and benchmarks to verify correctness and measure performance improvements ## Motivation In high-transaction-volume environments, the `EffectiveGasTip` method is called frequently. Reducing memory allocations in this method decreases garbage collection pressure and improves overall system performance. ## Benchmark Results As-Is BenchmarkEffectiveGasTip/Original-10 42089140 27.45 ns/op 8 B/op 1 allocs/op To-Be BenchmarkEffectiveGasTip/IntoMethod-10 72353263 16.73 ns/op 0 B/op 0 allocs/op ## Summary of Improvements - **Performance**: ~39% faster execution (27.45 ns/op → 16.73 ns/op) - **Memory**: Eliminated all allocations (8 B/op → 0 B/op) - **Allocation count**: Reduced from 1 to 0 allocations per operation This optimization follows the same pattern successfully applied to other methods in the codebase, maintaining API compatibility while improving performance. ## Safety & Compatibility This optimization has no side effects or adverse impacts because: - It maintains functional equivalence as confirmed by comprehensive tests - It preserves API compatibility with existing callers - It follows clear memory ownership patterns with the destination parameter - It maintains thread safety by only modifying the caller-provided destination parameter This optimization follows the same pattern successfully applied to other methods in the codebase, providing better performance without compromising stability or correctness. --------- Co-authored-by: lightclient --- core/types/transaction.go | 41 ++++++++------ core/types/transaction_test.go | 99 ++++++++++++++++++++++++++++++++++ eth/tracers/js/goja.go | 3 +- 3 files changed, 126 insertions(+), 17 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index a2f41046351..934feb73531 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -355,28 +355,31 @@ func (tx *Transaction) GasTipCapIntCmp(other *big.Int) int { // Note: if the effective gasTipCap is negative, this method returns both error // the actual negative value, _and_ ErrGasFeeCapTooLow func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) { + dst := new(big.Int) + err := tx.calcEffectiveGasTip(dst, baseFee) + return dst, err +} + +// calcEffectiveGasTip calculates the effective gas tip of the transaction and +// saves the result to dst. +func (tx *Transaction) calcEffectiveGasTip(dst *big.Int, baseFee *big.Int) error { if baseFee == nil { - return tx.GasTipCap(), nil + dst.Set(tx.inner.gasTipCap()) + return nil } + var err error - gasFeeCap := tx.GasFeeCap() + gasFeeCap := tx.inner.gasFeeCap() if gasFeeCap.Cmp(baseFee) < 0 { err = ErrGasFeeCapTooLow } - gasFeeCap = gasFeeCap.Sub(gasFeeCap, baseFee) - gasTipCap := tx.GasTipCap() - if gasTipCap.Cmp(gasFeeCap) < 0 { - return gasTipCap, err + dst.Sub(gasFeeCap, baseFee) + gasTipCap := tx.inner.gasTipCap() + if gasTipCap.Cmp(dst) < 0 { + dst.Set(gasTipCap) } - return gasFeeCap, err -} - -// EffectiveGasTipValue is identical to EffectiveGasTip, but does not return an -// error in case the effective gasTipCap is negative -func (tx *Transaction) EffectiveGasTipValue(baseFee *big.Int) *big.Int { - effectiveTip, _ := tx.EffectiveGasTip(baseFee) - return effectiveTip + return err } // EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee. @@ -384,7 +387,11 @@ func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *big.Int) if baseFee == nil { return tx.GasTipCapCmp(other) } - return tx.EffectiveGasTipValue(baseFee).Cmp(other.EffectiveGasTipValue(baseFee)) + // Use more efficient internal method. + txTip, otherTip := new(big.Int), new(big.Int) + tx.calcEffectiveGasTip(txTip, baseFee) + other.calcEffectiveGasTip(otherTip, baseFee) + return txTip.Cmp(otherTip) } // EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap. @@ -392,7 +399,9 @@ func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) i if baseFee == nil { return tx.GasTipCapIntCmp(other) } - return tx.EffectiveGasTipValue(baseFee).Cmp(other) + txTip := new(big.Int) + tx.calcEffectiveGasTip(txTip, baseFee) + return txTip.Cmp(other) } // BlobGas returns the blob gas limit of the transaction for blob transactions, 0 otherwise. diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index 8922448d97d..7d5e2f058af 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -593,3 +594,101 @@ func BenchmarkHash(b *testing.B) { signer.Hash(tx) } } + +func BenchmarkEffectiveGasTip(b *testing.B) { + signer := LatestSigner(params.TestChainConfig) + key, _ := crypto.GenerateKey() + txdata := &DynamicFeeTx{ + ChainID: big.NewInt(1), + Nonce: 0, + GasTipCap: big.NewInt(2000000000), + GasFeeCap: big.NewInt(3000000000), + Gas: 21000, + To: &common.Address{}, + Value: big.NewInt(0), + Data: nil, + } + tx, _ := SignNewTx(key, signer, txdata) + baseFee := big.NewInt(1000000000) // 1 gwei + + b.Run("Original", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, err := tx.EffectiveGasTip(baseFee) + if err != nil { + b.Fatal(err) + } + } + }) + + b.Run("IntoMethod", func(b *testing.B) { + b.ReportAllocs() + dst := new(big.Int) + for i := 0; i < b.N; i++ { + err := tx.calcEffectiveGasTip(dst, baseFee) + if err != nil { + b.Fatal(err) + } + } + }) +} + +func TestEffectiveGasTipInto(t *testing.T) { + signer := LatestSigner(params.TestChainConfig) + key, _ := crypto.GenerateKey() + + testCases := []struct { + tipCap int64 + feeCap int64 + baseFee *int64 + }{ + {tipCap: 1, feeCap: 100, baseFee: intPtr(50)}, + {tipCap: 10, feeCap: 100, baseFee: intPtr(50)}, + {tipCap: 50, feeCap: 100, baseFee: intPtr(50)}, + {tipCap: 100, feeCap: 100, baseFee: intPtr(50)}, + {tipCap: 1, feeCap: 50, baseFee: intPtr(50)}, + {tipCap: 1, feeCap: 20, baseFee: intPtr(50)}, // Base fee higher than fee cap + {tipCap: 50, feeCap: 100, baseFee: intPtr(0)}, + {tipCap: 50, feeCap: 100, baseFee: nil}, // nil base fee + } + + for i, tc := range testCases { + txdata := &DynamicFeeTx{ + ChainID: big.NewInt(1), + Nonce: 0, + GasTipCap: big.NewInt(tc.tipCap), + GasFeeCap: big.NewInt(tc.feeCap), + Gas: 21000, + To: &common.Address{}, + Value: big.NewInt(0), + Data: nil, + } + tx, _ := SignNewTx(key, signer, txdata) + + var baseFee *big.Int + if tc.baseFee != nil { + baseFee = big.NewInt(*tc.baseFee) + } + + // Get result from original method + orig, origErr := tx.EffectiveGasTip(baseFee) + + // Get result from new method + dst := new(big.Int) + newErr := tx.calcEffectiveGasTip(dst, baseFee) + + // Compare results + if (origErr != nil) != (newErr != nil) { + t.Fatalf("case %d: error mismatch: orig %v, new %v", i, origErr, newErr) + } + + if orig.Cmp(dst) != 0 { + t.Fatalf("case %d: result mismatch: orig %v, new %v", i, orig, dst) + } + } +} + +// Helper function to create integer pointer +func intPtr(i int64) *int64 { + return &i +} diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 227ea57226d..d1e65bf7f44 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -260,7 +260,8 @@ func (t *jsTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from t.activePrecompiles = vm.ActivePrecompiles(rules) t.ctx["block"] = t.vm.ToValue(t.env.BlockNumber.Uint64()) t.ctx["gas"] = t.vm.ToValue(tx.Gas()) - gasPriceBig, err := t.toBig(t.vm, tx.EffectiveGasTipValue(env.BaseFee).String()) + gasTip, _ := tx.EffectiveGasTip(env.BaseFee) + gasPriceBig, err := t.toBig(t.vm, gasTip.String()) if err != nil { t.err = err return From fa86416ce914d36464f1af31fae0e6c8a8c65f69 Mon Sep 17 00:00:00 2001 From: Marcel <153717436+MonkeyMarcel@users.noreply.github.com> Date: Sat, 10 May 2025 08:27:27 +0800 Subject: [PATCH 164/658] metrics: add chain/mgasps to track gas usage rate (#31753) This adds a metric called `chain/mgasps`, which records how many million gas per second are being used during block insertion. The value is calculated as `usedGas * 1000 / elapsed`, and it's updated in the `insertStats.report` method. Also cleaned up the log output to reuse the same value instead of recalculating it. Useful for monitoring block processing throughput. --------- Co-authored-by: Gary Rong --- core/blockchain.go | 3 ++- core/blockchain_insert.go | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 3c691600eb7..302ab14cf07 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -65,7 +65,8 @@ var ( headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil) headSafeBlockGauge = metrics.NewRegisteredGauge("chain/head/safe", nil) - chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil) + chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil) + chainMgaspsGauge = metrics.NewRegisteredGauge("chain/mgasps", nil) accountReadTimer = metrics.NewRegisteredResettingTimer("chain/account/reads", nil) accountHashTimer = metrics.NewRegisteredResettingTimer("chain/account/hashes", nil) diff --git a/core/blockchain_insert.go b/core/blockchain_insert.go index b4bd4446061..695aa6679b4 100644 --- a/core/blockchain_insert.go +++ b/core/blockchain_insert.go @@ -43,8 +43,12 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn // Fetch the timings for the batch var ( now = mclock.Now() - elapsed = now.Sub(st.startTime) + elapsed = now.Sub(st.startTime) + 1 // prevent zero division + mgasps = float64(st.usedGas) * 1000 / float64(elapsed) ) + // Update the Mgas per second gauge + chainMgaspsGauge.Update(int64(mgasps)) + // If we're at the last block of the batch or report period reached, log if index == len(chain)-1 || elapsed >= statsReportLimit { // Count the number of transactions in this segment @@ -58,7 +62,7 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn context := []interface{}{ "number", end.Number(), "hash", end.Hash(), "blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000, - "elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed), + "elapsed", common.PrettyDuration(elapsed), "mgasps", mgasps, } if timestamp := time.Unix(int64(end.Time()), 0); time.Since(timestamp) > time.Minute { context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...) From 098cc7e8782178f5f34b9101550ec2f43831188d Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 12 May 2025 14:37:01 +0800 Subject: [PATCH 165/658] ethdb/pebble: expose stall counter of pebble (#31782) This pull request adds a more Pebble metrics, tracking the amount of write stalls with specific reasons --- ethdb/pebble/pebble.go | 91 +++++++++++++++++++++++++++++++++--------- 1 file changed, 73 insertions(+), 18 deletions(-) diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go index 9ece9956552..5c851af9101 100644 --- a/ethdb/pebble/pebble.go +++ b/ethdb/pebble/pebble.go @@ -21,6 +21,7 @@ import ( "bytes" "fmt" "runtime" + "strings" "sync" "sync/atomic" "time" @@ -55,24 +56,35 @@ const ( // Apart from basic data storage functionality it also supports batch writes and // iterating over the keyspace in binary-alphabetical order. type Database struct { - fn string // filename for reporting - db *pebble.DB // Underlying pebble storage engine - - compTimeMeter *metrics.Meter // Meter for measuring the total time spent in database compaction - compReadMeter *metrics.Meter // Meter for measuring the data read during compaction - compWriteMeter *metrics.Meter // Meter for measuring the data written during compaction - writeDelayNMeter *metrics.Meter // Meter for measuring the write delay number due to database compaction - writeDelayMeter *metrics.Meter // Meter for measuring the write delay duration due to database compaction - diskSizeGauge *metrics.Gauge // Gauge for tracking the size of all the levels in the database - diskReadMeter *metrics.Meter // Meter for measuring the effective amount of data read - diskWriteMeter *metrics.Meter // Meter for measuring the effective amount of data written - memCompGauge *metrics.Gauge // Gauge for tracking the number of memory compaction - level0CompGauge *metrics.Gauge // Gauge for tracking the number of table compaction in level0 - nonlevel0CompGauge *metrics.Gauge // Gauge for tracking the number of table compaction in non0 level - seekCompGauge *metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt - manualMemAllocGauge *metrics.Gauge // Gauge for tracking amount of non-managed memory currently allocated - - levelsGauge []*metrics.Gauge // Gauge for tracking the number of tables in levels + fn string // filename for reporting + db *pebble.DB // Underlying pebble storage engine + namespace string // Namespace for metrics + + compTimeMeter *metrics.Meter // Meter for measuring the total time spent in database compaction + compReadMeter *metrics.Meter // Meter for measuring the data read during compaction + compWriteMeter *metrics.Meter // Meter for measuring the data written during compaction + writeDelayNMeter *metrics.Meter // Meter for measuring the write delay number due to database compaction + writeDelayMeter *metrics.Meter // Meter for measuring the write delay duration due to database compaction + diskSizeGauge *metrics.Gauge // Gauge for tracking the size of all the levels in the database + diskReadMeter *metrics.Meter // Meter for measuring the effective amount of data read + diskWriteMeter *metrics.Meter // Meter for measuring the effective amount of data written + memCompGauge *metrics.Gauge // Gauge for tracking the number of memory compaction + level0CompGauge *metrics.Gauge // Gauge for tracking the number of table compaction in level0 + nonlevel0CompGauge *metrics.Gauge // Gauge for tracking the number of table compaction in non0 level + seekCompGauge *metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt + manualMemAllocGauge *metrics.Gauge // Gauge for tracking amount of non-managed memory currently allocated + liveMemTablesGauge *metrics.Gauge // Gauge for tracking the number of live memory tables + zombieMemTablesGauge *metrics.Gauge // Gauge for tracking the number of zombie memory tables + blockCacheHitGauge *metrics.Gauge // Gauge for tracking the number of total hit in the block cache + blockCacheMissGauge *metrics.Gauge // Gauge for tracking the number of total miss in the block cache + tableCacheHitGauge *metrics.Gauge // Gauge for tracking the number of total hit in the table cache + tableCacheMissGauge *metrics.Gauge // Gauge for tracking the number of total miss in the table cache + filterHitGauge *metrics.Gauge // Gauge for tracking the number of total hit in bloom filter + filterMissGauge *metrics.Gauge // Gauge for tracking the number of total miss in bloom filter + estimatedCompDebtGauge *metrics.Gauge // Gauge for tracking the number of bytes that need to be compacted + liveCompGauge *metrics.Gauge // Gauge for tracking the number of in-progress compactions + liveCompSizeGauge *metrics.Gauge // Gauge for tracking the size of in-progress compactions + levelsGauge []*metrics.Gauge // Gauge for tracking the number of tables in levels quitLock sync.RWMutex // Mutex protecting the quit channel and the closed flag quitChan chan chan error // Quit channel to stop the metrics collection before closing the database @@ -88,6 +100,7 @@ type Database struct { writeStalled atomic.Bool // Flag whether the write is stalled writeDelayStartTime time.Time // The start time of the latest write stall + writeDelayReason string // The reason of the latest write stall writeDelayCount atomic.Int64 // Total number of write stall counts writeDelayTime atomic.Int64 // Total time spent in write stalls @@ -120,11 +133,30 @@ func (d *Database) onWriteStallBegin(b pebble.WriteStallBeginInfo) { d.writeDelayStartTime = time.Now() d.writeDelayCount.Add(1) d.writeStalled.Store(true) + + // Take just the first word of the reason. These are two potential + // reasons for the write stall: + // - memtable count limit reached + // - L0 file count limit exceeded + reason := b.Reason + if i := strings.IndexByte(reason, ' '); i != -1 { + reason = reason[:i] + } + if reason == "L0" || reason == "memtable" { + d.writeDelayReason = reason + metrics.GetOrRegisterGauge(d.namespace+"stall/count/"+reason, nil).Inc(1) + } } func (d *Database) onWriteStallEnd() { d.writeDelayTime.Add(int64(time.Since(d.writeDelayStartTime))) d.writeStalled.Store(false) + + if d.writeDelayReason != "" { + metrics.GetOrRegisterResettingTimer(d.namespace+"stall/time/"+d.writeDelayReason, nil).UpdateSince(d.writeDelayStartTime) + d.writeDelayReason = "" + } + d.writeDelayStartTime = time.Time{} } // panicLogger is just a noop logger to disable Pebble's internal logger. @@ -270,6 +302,17 @@ func New(file string, cache int, handles int, namespace string, readonly bool) ( db.nonlevel0CompGauge = metrics.GetOrRegisterGauge(namespace+"compact/nonlevel0", nil) db.seekCompGauge = metrics.GetOrRegisterGauge(namespace+"compact/seek", nil) db.manualMemAllocGauge = metrics.GetOrRegisterGauge(namespace+"memory/manualalloc", nil) + db.liveMemTablesGauge = metrics.GetOrRegisterGauge(namespace+"table/live", nil) + db.zombieMemTablesGauge = metrics.GetOrRegisterGauge(namespace+"table/zombie", nil) + db.blockCacheHitGauge = metrics.GetOrRegisterGauge(namespace+"cache/block/hit", nil) + db.blockCacheMissGauge = metrics.GetOrRegisterGauge(namespace+"cache/block/miss", nil) + db.tableCacheHitGauge = metrics.GetOrRegisterGauge(namespace+"cache/table/hit", nil) + db.tableCacheMissGauge = metrics.GetOrRegisterGauge(namespace+"cache/table/miss", nil) + db.filterHitGauge = metrics.GetOrRegisterGauge(namespace+"filter/hit", nil) + db.filterMissGauge = metrics.GetOrRegisterGauge(namespace+"filter/miss", nil) + db.estimatedCompDebtGauge = metrics.GetOrRegisterGauge(namespace+"compact/estimateDebt", nil) + db.liveCompGauge = metrics.GetOrRegisterGauge(namespace+"compact/live/count", nil) + db.liveCompSizeGauge = metrics.GetOrRegisterGauge(namespace+"compact/live/size", nil) // Start up the metrics gathering and return go db.meter(metricsGatheringInterval, namespace) @@ -517,6 +560,18 @@ func (d *Database) meter(refresh time.Duration, namespace string) { d.nonlevel0CompGauge.Update(nonLevel0CompCount) d.level0CompGauge.Update(level0CompCount) d.seekCompGauge.Update(stats.Compact.ReadCount) + d.liveCompGauge.Update(stats.Compact.NumInProgress) + d.liveCompSizeGauge.Update(stats.Compact.InProgressBytes) + + d.liveMemTablesGauge.Update(stats.MemTable.Count) + d.zombieMemTablesGauge.Update(stats.MemTable.ZombieCount) + d.estimatedCompDebtGauge.Update(int64(stats.Compact.EstimatedDebt)) + d.tableCacheHitGauge.Update(stats.TableCache.Hits) + d.tableCacheMissGauge.Update(stats.TableCache.Misses) + d.blockCacheHitGauge.Update(stats.BlockCache.Hits) + d.blockCacheMissGauge.Update(stats.BlockCache.Misses) + d.filterHitGauge.Update(stats.Filter.Hits) + d.filterMissGauge.Update(stats.Filter.Misses) for i, level := range stats.Levels { // Append metrics for additional layers From d121c27acefb96b875f7d3047dd26e0b83862d59 Mon Sep 17 00:00:00 2001 From: Klimov Sergey Date: Mon, 12 May 2025 15:41:24 +0800 Subject: [PATCH 166/658] core/state: fix incorrect description of function behavior (#31809) --- core/state/statedb.go | 2 +- core/vm/interface.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 9378cae7de3..2453d67f3ee 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -298,7 +298,7 @@ func (s *StateDB) SubRefund(gas uint64) { } // Exist reports whether the given account address exists in the state. -// Notably this also returns true for self-destructed accounts. +// Notably this also returns true for self-destructed accounts within the current transaction. func (s *StateDB) Exist(addr common.Address) bool { return s.getStateObject(addr) != nil } diff --git a/core/vm/interface.go b/core/vm/interface.go index 57f35cb2492..86e8c56ab0e 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -69,7 +69,7 @@ type StateDB interface { SelfDestruct6780(common.Address) (uint256.Int, bool) // Exist reports whether the given account exists in state. - // Notably this should also return true for self-destructed accounts. + // Notably this also returns true for self-destructed accounts within the current transaction. Exist(common.Address) bool // Empty returns whether the given account is empty. Empty // is defined according to EIP161 (balance = nonce = code = 0). From c7bdf6b2fe4eae1db035147431147ed54f84fae7 Mon Sep 17 00:00:00 2001 From: SHADOW Date: Mon, 12 May 2025 15:01:48 +0530 Subject: [PATCH 167/658] accounts/abi/abigen: remove unnecessary test files (#31804) fix #31793: resolve conflict markers in structs-abi.go.txt --- .../abi/abigen/testdata/v2/structs-abi.go.txt | 116 ------------------ 1 file changed, 116 deletions(-) delete mode 100644 accounts/abi/abigen/testdata/v2/structs-abi.go.txt diff --git a/accounts/abi/abigen/testdata/v2/structs-abi.go.txt b/accounts/abi/abigen/testdata/v2/structs-abi.go.txt deleted file mode 100644 index aab62427074..00000000000 --- a/accounts/abi/abigen/testdata/v2/structs-abi.go.txt +++ /dev/null @@ -1,116 +0,0 @@ -// Code generated via abigen V2 - DO NOT EDIT. -// This file is a generated binding and any manual changes will be lost. - -package v1bindtests - -import ( - "bytes" - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" -) - -// Reference imports to suppress errors if they are not otherwise used. -var ( - _ = bytes.Equal - _ = errors.New - _ = big.NewInt - _ = common.Big1 - _ = types.BloomLookup - _ = abi.ConvertType -) - -// Struct0 is an auto generated low-level Go binding around an user-defined struct. -type Struct0 struct { - B [32]byte -} - -// StructsMetaData contains all meta data concerning the Structs contract. -var StructsMetaData = bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"F\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"B\",\"type\":\"bytes32\"}],\"internalType\":\"structStructs.A[]\",\"name\":\"a\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[]\",\"name\":\"c\",\"type\":\"uint256[]\"},{\"internalType\":\"bool[]\",\"name\":\"d\",\"type\":\"bool[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"G\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"B\",\"type\":\"bytes32\"}],\"internalType\":\"structStructs.A[]\",\"name\":\"a\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - ID: "Structs", -} - -// Structs is an auto generated Go binding around an Ethereum contract. -type Structs struct { - abi abi.ABI -} - -// NewStructs creates a new instance of Structs. -func NewStructs() *Structs { - parsed, err := StructsMetaData.ParseABI() - if err != nil { - panic(errors.New("invalid ABI: " + err.Error())) - } - return &Structs{abi: *parsed} -} - -// Instance creates a wrapper for a deployed contract instance at the given address. -<<<<<<< HEAD -// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc. -func (c *Structs) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract { - return bind.NewBoundContract(addr, c.abi, backend, backend, backend) -======= -// Use this to create the instance object passed to abigen v2 library functions Call, -// Transact, etc. -func (c *Structs) Instance(backend bind.ContractBackend, addr common.Address) bind.BoundContract { - return bind.NewBoundContract(backend, addr, c.abi) ->>>>>>> 854c25e086 (accounts/abi/abigen: improve v2 template) -} - -// F is the Go binding used to pack the parameters required for calling -// the contract method 0x28811f59. -// -// Solidity: function F() view returns((bytes32)[] a, uint256[] c, bool[] d) -func (structs *Structs) PackF() ([]byte, error) { - return structs.abi.Pack("F") -} - -// FOutput serves as a container for the return parameters of contract -// method F. -type FOutput struct { - A []Struct0 - C []*big.Int - D []bool -} - -// UnpackF is the Go binding that unpacks the parameters returned -// from invoking the contract method with ID 0x28811f59. -// -// Solidity: function F() view returns((bytes32)[] a, uint256[] c, bool[] d) -func (structs *Structs) UnpackF(data []byte) (*FOutput, error) { - out, err := structs.abi.Unpack("F", data) - if err != nil { - return nil, err - } - ret := new(FOutput) - ret.A = *abi.ConvertType(out[0], new([]Struct0)).(*[]Struct0) - ret.C = *abi.ConvertType(out[1], new([]*big.Int)).(*[]*big.Int) - ret.D = *abi.ConvertType(out[2], new([]bool)).(*[]bool) - return ret, nil -} - -// G is the Go binding used to pack the parameters required for calling -// the contract method 0x6fecb623. -// -// Solidity: function G() view returns((bytes32)[] a) -func (structs *Structs) PackG() ([]byte, error) { - return structs.abi.Pack("G") -} - -// UnpackG is the Go binding that unpacks the parameters returned -// from invoking the contract method with ID 0x6fecb623. -// -// Solidity: function G() view returns((bytes32)[] a) -func (structs *Structs) UnpackG(data []byte) (*[]Struct0, error) { - out, err := structs.abi.Unpack("G", data) - if err != nil { - return nil, err - } - out0 := *abi.ConvertType(out[0], new([]Struct0)).(*[]Struct0) - return &out0, nil -} From 2cd5f22037f1c148a536b0d9b766365519c9cf60 Mon Sep 17 00:00:00 2001 From: wellna Date: Tue, 13 May 2025 13:11:27 +0100 Subject: [PATCH 168/658] crypto/kzg4844: use package github.com/crate-crypto/go-eth-kzg (#31806) I saw in https://github.com/ethereum/go-ethereum/pull/31378 introduced github.com/crate-crypto/go-eth-kzg to calculate the kzg hash, and github.com/crate-crypto/go-kzg-4844 was only used in the test files, so propose to drop it with go-eth-kzg instead --- crypto/kzg4844/kzg4844_test.go | 2 +- go.mod | 1 - go.sum | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/crypto/kzg4844/kzg4844_test.go b/crypto/kzg4844/kzg4844_test.go index a6782d4768a..7fa261e523e 100644 --- a/crypto/kzg4844/kzg4844_test.go +++ b/crypto/kzg4844/kzg4844_test.go @@ -21,7 +21,7 @@ import ( "testing" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" - gokzg4844 "github.com/crate-crypto/go-kzg-4844" + gokzg4844 "github.com/crate-crypto/go-eth-kzg" ) func randFieldElement() [32]byte { diff --git a/go.mod b/go.mod index 924f0d26420..d27af647ece 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,6 @@ require ( github.com/consensys/gnark-crypto v0.16.0 github.com/crate-crypto/go-eth-kzg v1.3.0 github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a - github.com/crate-crypto/go-kzg-4844 v1.1.0 github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set/v2 v2.6.0 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 diff --git a/go.sum b/go.sum index 5d35a7a0e15..200b3725eaa 100644 --- a/go.sum +++ b/go.sum @@ -84,8 +84,6 @@ github.com/crate-crypto/go-eth-kzg v1.3.0 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= -github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4= -github.com/crate-crypto/go-kzg-4844 v1.1.0/go.mod h1:JolLjpSff1tCCJKaJx4psrlEdlXuJEC996PL3tTAFks= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From c53db5e1f6383606d6de2fc4577ed7dbc6f8def4 Mon Sep 17 00:00:00 2001 From: maskpp Date: Tue, 13 May 2025 21:28:16 +0800 Subject: [PATCH 169/658] eth: fix returned blockContext for empty blocks in stateAtTransaction (#31768) Co-authored-by: lightclient --- eth/state_accessor.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 99ed28d96af..3c3e79a5841 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -217,7 +217,13 @@ func (eth *Ethereum) stateAtBlock(ctx context.Context, block *types.Block, reexe return eth.pathState(block) } -// stateAtTransaction returns the execution environment of a certain transaction. +// stateAtTransaction returns the execution environment of a certain +// transaction. +// +// Note: when a block is empty and the state for tx index 0 is requested, this +// function will return the state of block after the pre-block operations have +// been completed (e.g. updating system contracts), but before post-block +// operations are completed (e.g. processing withdrawals). func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) { // Short circuit if it's genesis block. if block.NumberU64() == 0 { @@ -245,7 +251,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, core.ProcessParentBlockHash(block.ParentHash(), evm) } if txIndex == 0 && len(block.Transactions()) == 0 { - return nil, vm.BlockContext{}, statedb, release, nil + return nil, context, statedb, release, nil } // Recompute transactions up to the target index. signer := types.MakeSigner(eth.blockchain.Config(), block.Number(), block.Time()) From 88a7ef233abcddca1da79c88ab78a1243e23837d Mon Sep 17 00:00:00 2001 From: Delweng Date: Wed, 14 May 2025 09:26:08 +0800 Subject: [PATCH 170/658] core: use unix time to check fork readiness (#31800) --- core/blockchain.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 302ab14cf07..320b90dcbef 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2543,14 +2543,22 @@ func (bc *BlockChain) reportBlock(block *types.Block, res *ProcessResult, err er // logForkReadiness will write a log when a future fork is scheduled, but not // active. This is useful so operators know their client is ready for the fork. func (bc *BlockChain) logForkReadiness(block *types.Block) { - c := bc.Config() - current, last := c.LatestFork(block.Time()), c.LatestFork(math.MaxUint64) - t := c.Timestamp(last) - if t == nil { + config := bc.Config() + current, last := config.LatestFork(block.Time()), config.LatestFork(math.MaxUint64) + + // Short circuit if the timestamp of the last fork is undefined, + // or if the network has already passed the last configured fork. + t := config.Timestamp(last) + if t == nil || current >= last { return } at := time.Unix(int64(*t), 0) - if current < last && time.Now().After(bc.lastForkReadyAlert.Add(forkReadyInterval)) { + + // Only log if: + // - Current time is before the fork activation time + // - Enough time has passed since last alert + now := time.Now() + if now.Before(at) && now.After(bc.lastForkReadyAlert.Add(forkReadyInterval)) { log.Info("Ready for fork activation", "fork", last, "date", at.Format(time.RFC822), "remaining", time.Until(at).Round(time.Second), "timestamp", at.Unix()) bc.lastForkReadyAlert = time.Now() From 16b0d9e982f7f0469930b59ba62d0c9e43281b78 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 14 May 2025 16:50:02 +0200 Subject: [PATCH 171/658] eth/catalyst: refactor engine api checks (#31182) This PR contains three refactors: - refactor the latest fork check that we use quite extensively - refactor the nil checks in NewPayloads --------- Co-authored-by: lightclient --- eth/catalyst/api.go | 449 +++++++++++++++++++++----------------------- 1 file changed, 217 insertions(+), 232 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 1e6981a76af..be25712c970 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -38,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/forks" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" @@ -196,11 +197,11 @@ func newConsensusAPIWithoutHeartbeat(eth *eth.Ethereum) *ConsensusAPI { // and return its payloadID. func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { if payloadAttributes != nil { - if payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil { - return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("withdrawals and beacon root not supported in V1")) - } - if api.eth.BlockChain().Config().IsShanghai(api.eth.BlockChain().Config().LondonBlock, payloadAttributes.Timestamp) { - return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("forkChoiceUpdateV1 called post-shanghai")) + switch { + case payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil: + return engine.STATUS_INVALID, paramsErr("withdrawals and beacon root not supported in V1") + case !api.checkFork(payloadAttributes.Timestamp, forks.Paris, forks.Shanghai): + return engine.STATUS_INVALID, paramsErr("fcuV1 called post-shanghai") } } return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, false) @@ -210,20 +211,15 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, pa // attributes. It supports both PayloadAttributesV1 and PayloadAttributesV2. func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { if params != nil { - if params.BeaconRoot != nil { - return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("unexpected beacon root")) - } - switch api.eth.BlockChain().Config().LatestFork(params.Timestamp) { - case forks.Paris: - if params.Withdrawals != nil { - return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("withdrawals before shanghai")) - } - case forks.Shanghai: - if params.Withdrawals == nil { - return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals")) - } - default: - return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV2 must only be called with paris and shanghai payloads")) + switch { + case params.BeaconRoot != nil: + return engine.STATUS_INVALID, attributesErr("unexpected beacon root") + case api.checkFork(params.Timestamp, forks.Paris) && params.Withdrawals != nil: + return engine.STATUS_INVALID, attributesErr("withdrawals before shanghai") + case api.checkFork(params.Timestamp, forks.Shanghai) && params.Withdrawals == nil: + return engine.STATUS_INVALID, attributesErr("missing withdrawals") + case !api.checkFork(params.Timestamp, forks.Paris, forks.Shanghai): + return engine.STATUS_INVALID, unsupportedForkErr("fcuV2 must only be called with paris or shanghai payloads") } } return api.forkchoiceUpdated(update, params, engine.PayloadV2, false) @@ -233,14 +229,13 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, pa // in the payload attributes. It supports only PayloadAttributesV3. func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { if params != nil { - if params.Withdrawals == nil { - return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals")) - } - if params.BeaconRoot == nil { - return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing beacon root")) - } - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun && api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { - return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV3 must only be called for cancun payloads")) + switch { + case params.Withdrawals == nil: + return engine.STATUS_INVALID, attributesErr("missing withdrawals") + case params.BeaconRoot == nil: + return engine.STATUS_INVALID, attributesErr("missing beacon root") + case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague): + return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads") } } // TODO(matt): the spec requires that fcu is applied when called on a valid @@ -254,11 +249,11 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, pa // generates an execution witness too if block building was requested. func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { if payloadAttributes != nil { - if payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil { - return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("withdrawals and beacon root not supported in V1")) - } - if api.eth.BlockChain().Config().IsShanghai(api.eth.BlockChain().Config().LondonBlock, payloadAttributes.Timestamp) { - return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("forkChoiceUpdateV1 called post-shanghai")) + switch { + case payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil: + return engine.STATUS_INVALID, paramsErr("withdrawals and beacon root not supported in V1") + case !api.checkFork(payloadAttributes.Timestamp, forks.Paris, forks.Shanghai): + return engine.STATUS_INVALID, paramsErr("fcuV1 called post-shanghai") } } return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, true) @@ -268,20 +263,15 @@ func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(update engine.Forkchoice // generates an execution witness too if block building was requested. func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { if params != nil { - if params.BeaconRoot != nil { - return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("unexpected beacon root")) - } - switch api.eth.BlockChain().Config().LatestFork(params.Timestamp) { - case forks.Paris: - if params.Withdrawals != nil { - return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("withdrawals before shanghai")) - } - case forks.Shanghai: - if params.Withdrawals == nil { - return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals")) - } - default: - return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV2 must only be called with paris and shanghai payloads")) + switch { + case params.BeaconRoot != nil: + return engine.STATUS_INVALID, attributesErr("unexpected beacon root") + case api.checkFork(params.Timestamp, forks.Paris) && params.Withdrawals != nil: + return engine.STATUS_INVALID, attributesErr("withdrawals before shanghai") + case api.checkFork(params.Timestamp, forks.Shanghai) && params.Withdrawals == nil: + return engine.STATUS_INVALID, attributesErr("missing withdrawals") + case !api.checkFork(params.Timestamp, forks.Paris, forks.Shanghai): + return engine.STATUS_INVALID, unsupportedForkErr("fcuV2 must only be called with paris or shanghai payloads") } } return api.forkchoiceUpdated(update, params, engine.PayloadV2, true) @@ -291,14 +281,13 @@ func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(update engine.Forkchoice // generates an execution witness too if block building was requested. func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { if params != nil { - if params.Withdrawals == nil { - return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals")) - } - if params.BeaconRoot == nil { - return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing beacon root")) - } - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun && api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { - return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV3 must only be called for cancun payloads")) + switch { + case params.Withdrawals == nil: + return engine.STATUS_INVALID, attributesErr("missing withdrawals") + case params.BeaconRoot == nil: + return engine.STATUS_INVALID, attributesErr("missing beacon root") + case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague): + return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads") } } // TODO(matt): the spec requires that fcu is applied when called on a valid @@ -468,7 +457,7 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.Transit api.lastTransitionUpdate = time.Now() api.lastTransitionLock.Unlock() - ttd := api.eth.BlockChain().Config().TerminalTotalDifficulty + ttd := api.config().TerminalTotalDifficulty if ttd == nil || ttd.Cmp(config.TerminalTotalDifficulty.ToInt()) != 0 { log.Warn("Invalid TTD configured", "geth", ttd, "beacon", config.TerminalTotalDifficulty) return nil, fmt.Errorf("invalid ttd: execution %v consensus %v", ttd, config.TerminalTotalDifficulty) @@ -550,86 +539,74 @@ func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProo return res, nil } +// Helper for NewPayload* methods. +var invalidStatus = engine.PayloadStatusV1{Status: engine.INVALID} + // NewPayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.PayloadStatusV1, error) { if params.Withdrawals != nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1")) + return invalidStatus, paramsErr("withdrawals not supported in V1") } return api.newPayload(params, nil, nil, nil, false) } // NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) { - if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use newPayloadV2 post-cancun")) - } - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai { - if params.Withdrawals == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) - } - } else { - if params.Withdrawals != nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai")) - } - } - if params.ExcessBlobGas != nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun")) - } - if params.BlobGasUsed != nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun")) + var ( + cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp) + shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp) + ) + switch { + case cancun: + return invalidStatus, paramsErr("can't use newPayloadV2 post-cancun") + case shanghai && params.Withdrawals == nil: + return invalidStatus, paramsErr("nil withdrawals post-shanghai") + case !shanghai && params.Withdrawals != nil: + return invalidStatus, paramsErr("non-nil withdrawals pre-shanghai") + case params.ExcessBlobGas != nil: + return invalidStatus, paramsErr("non-nil excessBlobGas pre-cancun") + case params.BlobGasUsed != nil: + return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun") } return api.newPayload(params, nil, nil, nil, false) } // NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) { - if params.Withdrawals == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) - } - if params.ExcessBlobGas == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun")) - } - if params.BlobGasUsed == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun")) - } - - if versionedHashes == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun")) - } - if beaconRoot == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) - } - - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV3 must only be called for cancun payloads")) + switch { + case params.Withdrawals == nil: + return invalidStatus, paramsErr("nil withdrawals post-shanghai") + case params.ExcessBlobGas == nil: + return invalidStatus, paramsErr("nil excessBlobGas post-cancun") + case params.BlobGasUsed == nil: + return invalidStatus, paramsErr("nil blobGasUsed post-cancun") + case versionedHashes == nil: + return invalidStatus, paramsErr("nil versionedHashes post-cancun") + case beaconRoot == nil: + return invalidStatus, paramsErr("nil beaconRoot post-cancun") + case !api.checkFork(params.Timestamp, forks.Cancun): + return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") } return api.newPayload(params, versionedHashes, beaconRoot, nil, false) } // NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) { - if params.Withdrawals == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) - } - if params.ExcessBlobGas == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun")) - } - if params.BlobGasUsed == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun")) - } - - if versionedHashes == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun")) - } - if beaconRoot == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) - } - if executionRequests == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) - } - - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV4 must only be called for prague payloads")) + switch { + case params.Withdrawals == nil: + return invalidStatus, paramsErr("nil withdrawals post-shanghai") + case params.ExcessBlobGas == nil: + return invalidStatus, paramsErr("nil excessBlobGas post-cancun") + case params.BlobGasUsed == nil: + return invalidStatus, paramsErr("nil blobGasUsed post-cancun") + case versionedHashes == nil: + return invalidStatus, paramsErr("nil versionedHashes post-cancun") + case beaconRoot == nil: + return invalidStatus, paramsErr("nil beaconRoot post-cancun") + case executionRequests == nil: + return invalidStatus, paramsErr("nil executionRequests post-prague") + case !api.checkFork(params.Timestamp, forks.Prague): + return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") } requests := convertRequests(executionRequests) if err := validateRequests(requests); err != nil { @@ -650,23 +627,21 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV1(params engine.ExecutableData) ( // NewPayloadWithWitnessV2 is analogous to NewPayloadV2, only it also generates // and returns a stateless witness after running the payload. func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) { - if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use newPayloadV2 post-cancun")) - } - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai { - if params.Withdrawals == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) - } - } else { - if params.Withdrawals != nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai")) - } - } - if params.ExcessBlobGas != nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun")) - } - if params.BlobGasUsed != nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun")) + var ( + cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp) + shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp) + ) + switch { + case cancun: + return invalidStatus, paramsErr("can't use newPayloadV2 post-cancun") + case shanghai && params.Withdrawals == nil: + return invalidStatus, paramsErr("nil withdrawals post-shanghai") + case !shanghai && params.Withdrawals != nil: + return invalidStatus, paramsErr("non-nil withdrawals pre-shanghai") + case params.ExcessBlobGas != nil: + return invalidStatus, paramsErr("non-nil excessBlobGas pre-cancun") + case params.BlobGasUsed != nil: + return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun") } return api.newPayload(params, nil, nil, nil, true) } @@ -674,25 +649,19 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) ( // NewPayloadWithWitnessV3 is analogous to NewPayloadV3, only it also generates // and returns a stateless witness after running the payload. func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) { - if params.Withdrawals == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) - } - if params.ExcessBlobGas == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun")) - } - if params.BlobGasUsed == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun")) - } - - if versionedHashes == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun")) - } - if beaconRoot == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) - } - - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadWithWitnessV3 must only be called for cancun payloads")) + switch { + case params.Withdrawals == nil: + return invalidStatus, paramsErr("nil withdrawals post-shanghai") + case params.ExcessBlobGas == nil: + return invalidStatus, paramsErr("nil excessBlobGas post-cancun") + case params.BlobGasUsed == nil: + return invalidStatus, paramsErr("nil blobGasUsed post-cancun") + case versionedHashes == nil: + return invalidStatus, paramsErr("nil versionedHashes post-cancun") + case beaconRoot == nil: + return invalidStatus, paramsErr("nil beaconRoot post-cancun") + case !api.checkFork(params.Timestamp, forks.Cancun): + return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") } return api.newPayload(params, versionedHashes, beaconRoot, nil, true) } @@ -700,28 +669,21 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, v // NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates // and returns a stateless witness after running the payload. func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) { - if params.Withdrawals == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) - } - if params.ExcessBlobGas == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun")) - } - if params.BlobGasUsed == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun")) - } - - if versionedHashes == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun")) - } - if beaconRoot == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) - } - if executionRequests == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) - } - - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadWithWitnessV4 must only be called for prague payloads")) + switch { + case params.Withdrawals == nil: + return invalidStatus, paramsErr("nil withdrawals post-shanghai") + case params.ExcessBlobGas == nil: + return invalidStatus, paramsErr("nil excessBlobGas post-cancun") + case params.BlobGasUsed == nil: + return invalidStatus, paramsErr("nil blobGasUsed post-cancun") + case versionedHashes == nil: + return invalidStatus, paramsErr("nil versionedHashes post-cancun") + case beaconRoot == nil: + return invalidStatus, paramsErr("nil beaconRoot post-cancun") + case executionRequests == nil: + return invalidStatus, paramsErr("nil executionRequests post-prague") + case !api.checkFork(params.Timestamp, forks.Prague): + return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") } requests := convertRequests(executionRequests) if err := validateRequests(requests); err != nil { @@ -742,23 +704,21 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV1(params engine.ExecutableData, // ExecuteStatelessPayloadV2 is analogous to NewPayloadV2, only it operates in // a stateless mode on top of a provided witness instead of the local database. func (api *ConsensusAPI) ExecuteStatelessPayloadV2(params engine.ExecutableData, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { - if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use newPayloadV2 post-cancun")) - } - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai { - if params.Withdrawals == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) - } - } else { - if params.Withdrawals != nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai")) - } - } - if params.ExcessBlobGas != nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun")) - } - if params.BlobGasUsed != nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun")) + var ( + cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp) + shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp) + ) + switch { + case cancun: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("can't use newPayloadV2 post-cancun") + case shanghai && params.Withdrawals == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai") + case !shanghai && params.Withdrawals != nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil withdrawals pre-shanghai") + case params.ExcessBlobGas != nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil excessBlobGas pre-cancun") + case params.BlobGasUsed != nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil blobGasUsed pre-cancun") } return api.executeStatelessPayload(params, nil, nil, nil, opaqueWitness) } @@ -766,25 +726,19 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV2(params engine.ExecutableData, // ExecuteStatelessPayloadV3 is analogous to NewPayloadV3, only it operates in // a stateless mode on top of a provided witness instead of the local database. func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { - if params.Withdrawals == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) - } - if params.ExcessBlobGas == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun")) - } - if params.BlobGasUsed == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun")) - } - - if versionedHashes == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun")) - } - if beaconRoot == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) - } - - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("executeStatelessPayloadV3 must only be called for cancun payloads")) + switch { + case params.Withdrawals == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai") + case params.ExcessBlobGas == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil excessBlobGas post-cancun") + case params.BlobGasUsed == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil blobGasUsed post-cancun") + case versionedHashes == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil versionedHashes post-cancun") + case beaconRoot == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun") + case !api.checkFork(params.Timestamp, forks.Cancun): + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") } return api.executeStatelessPayload(params, versionedHashes, beaconRoot, nil, opaqueWitness) } @@ -792,30 +746,26 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, // ExecuteStatelessPayloadV4 is analogous to NewPayloadV4, only it operates in // a stateless mode on top of a provided witness instead of the local database. func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { - if params.Withdrawals == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) - } - if params.ExcessBlobGas == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun")) - } - if params.BlobGasUsed == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun")) - } - - if versionedHashes == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun")) - } - if beaconRoot == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) - } - if executionRequests == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) - } - - if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("executeStatelessPayloadV4 must only be called for prague payloads")) + switch { + case params.Withdrawals == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai") + case params.ExcessBlobGas == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil excessBlobGas post-cancun") + case params.BlobGasUsed == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil blobGasUsed post-cancun") + case versionedHashes == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil versionedHashes post-cancun") + case beaconRoot == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun") + case executionRequests == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil executionRequests post-prague") + case !api.checkFork(params.Timestamp, forks.Prague): + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") } requests := convertRequests(executionRequests) + if err := validateRequests(requests); err != nil { + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(err) + } return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requests, opaqueWitness) } @@ -981,7 +931,7 @@ func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, v api.lastNewPayloadLock.Unlock() log.Trace("Executing block statelessly", "number", block.Number(), "hash", params.BlockHash) - stateRoot, receiptRoot, err := core.ExecuteStateless(api.eth.BlockChain().Config(), vm.Config{}, block, witness) + stateRoot, receiptRoot, err := core.ExecuteStateless(api.config(), vm.Config{}, block, witness) if err != nil { log.Warn("ExecuteStatelessPayload: execution failed", "err", err) errorMsg := err.Error() @@ -1119,7 +1069,7 @@ func (api *ConsensusAPI) heartbeat() { time.Sleep(beaconUpdateStartupTimeout) // If the network is not yet merged/merging, don't bother continuing. - if api.eth.BlockChain().Config().TerminalTotalDifficulty == nil { + if api.config().TerminalTotalDifficulty == nil { return } @@ -1163,6 +1113,23 @@ func (api *ConsensusAPI) heartbeat() { } } +// config retrieves the chain's fork configuration. +func (api *ConsensusAPI) config() *params.ChainConfig { + return api.eth.BlockChain().Config() +} + +// checkFork returns true if the latest fork at the given timestamp +// is one of the forks provided. +func (api *ConsensusAPI) checkFork(timestamp uint64, forks ...forks.Fork) bool { + latest := api.config().LatestFork(timestamp) + for _, fork := range forks { + if latest == fork { + return true + } + } + return false +} + // ExchangeCapabilities returns the current methods provided by this node. func (api *ConsensusAPI) ExchangeCapabilities([]string) []string { return caps @@ -1288,3 +1255,21 @@ func validateRequests(requests [][]byte) error { } return nil } + +// paramsErr is a helper function for creating an InvalidPayloadAttributes +// Engine API error. +func paramsErr(msg string) error { + return engine.InvalidParams.With(errors.New(msg)) +} + +// attributesErr is a helper function for creating an InvalidPayloadAttributes +// Engine API error. +func attributesErr(msg string) error { + return engine.InvalidPayloadAttributes.With(errors.New(msg)) +} + +// unsupportedForkErr is a helper function for creating an UnsupportedFork +// Engine API error. +func unsupportedForkErr(msg string) error { + return engine.UnsupportedFork.With(errors.New(msg)) +} From 52dbd206bb9ea9b4a1f0f7feaefc5f7828dd4c67 Mon Sep 17 00:00:00 2001 From: maskpp Date: Thu, 15 May 2025 15:32:20 +0800 Subject: [PATCH 172/658] cmd/utils: always record preimages in dev mode (#31821) Fix a todo: force-enable this in --dev mode --------- Co-authored-by: jwasinger --- cmd/utils/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 44363d13f46..a100ee15485 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1712,7 +1712,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } } if ctx.IsSet(VMEnableDebugFlag.Name) { - // TODO(fjl): force-enable this in --dev mode cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name) } @@ -1761,6 +1760,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { case ctx.Bool(DeveloperFlag.Name): cfg.NetworkId = 1337 cfg.SyncMode = ethconfig.FullSync + cfg.EnablePreimageRecording = true // Create new developer account or reuse existing one var ( developer accounts.Account From 228803c1a29acf93c8cd53a29e477d8801fc60ad Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 15 May 2025 14:17:58 +0200 Subject: [PATCH 173/658] p2p/enode: add support for naming iterator sources (#31779) This adds support for naming the source iterators of FairMix, like so: mix.AddSource(enode.WithSourceName("mySource", iter)) The source that produced the latest node is returned by the new NodeSource method. --- p2p/enode/iter.go | 79 ++++++++++++++++++++++++++++++++---------- p2p/enode/iter_test.go | 48 +++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 18 deletions(-) diff --git a/p2p/enode/iter.go b/p2p/enode/iter.go index b8ab4a758ae..4b7e28929ee 100644 --- a/p2p/enode/iter.go +++ b/p2p/enode/iter.go @@ -30,6 +30,35 @@ type Iterator interface { Close() // ends the iterator } +// SourceIterator represents a sequence of nodes like [Iterator] +// Each node also has a named 'source'. +type SourceIterator interface { + Iterator + NodeSource() string // source of current node +} + +// WithSource attaches a 'source name' to an iterator. +func WithSourceName(name string, it Iterator) SourceIterator { + return sourceIter{it, name} +} + +func ensureSourceIter(it Iterator) SourceIterator { + if si, ok := it.(SourceIterator); ok { + return si + } + return WithSourceName("", it) +} + +type sourceIter struct { + Iterator + name string +} + +// NodeSource implements IteratorSource. +func (it sourceIter) NodeSource() string { + return it.name +} + // ReadNodes reads at most n nodes from the given iterator. The return value contains no // duplicates and no nil values. To prevent looping indefinitely for small repeating node // sequences, this function calls Next at most n times. @@ -106,16 +135,16 @@ func (it *sliceIter) Close() { // Filter wraps an iterator such that Next only returns nodes for which // the 'check' function returns true. func Filter(it Iterator, check func(*Node) bool) Iterator { - return &filterIter{it, check} + return &filterIter{ensureSourceIter(it), check} } type filterIter struct { - Iterator + SourceIterator check func(*Node) bool } func (f *filterIter) Next() bool { - for f.Iterator.Next() { + for f.SourceIterator.Next() { if f.check(f.Node()) { return true } @@ -135,9 +164,9 @@ func (f *filterIter) Next() bool { // It's safe to call AddSource and Close concurrently with Next. type FairMix struct { wg sync.WaitGroup - fromAny chan *Node + fromAny chan mixItem timeout time.Duration - cur *Node + cur mixItem mu sync.Mutex closed chan struct{} @@ -146,11 +175,16 @@ type FairMix struct { } type mixSource struct { - it Iterator - next chan *Node + it SourceIterator + next chan mixItem timeout time.Duration } +type mixItem struct { + n *Node + source string +} + // NewFairMix creates a mixer. // // The timeout specifies how long the mixer will wait for the next fairly-chosen source @@ -159,7 +193,7 @@ type mixSource struct { // timeout makes the mixer completely fair. func NewFairMix(timeout time.Duration) *FairMix { m := &FairMix{ - fromAny: make(chan *Node), + fromAny: make(chan mixItem), closed: make(chan struct{}), timeout: timeout, } @@ -175,7 +209,11 @@ func (m *FairMix) AddSource(it Iterator) { return } m.wg.Add(1) - source := &mixSource{it, make(chan *Node), m.timeout} + source := &mixSource{ + it: ensureSourceIter(it), + next: make(chan mixItem), + timeout: m.timeout, + } m.sources = append(m.sources, source) go m.runSource(m.closed, source) } @@ -201,7 +239,7 @@ func (m *FairMix) Close() { // Next returns a node from a random source. func (m *FairMix) Next() bool { - m.cur = nil + m.cur = mixItem{} for { source := m.pickSource() @@ -217,12 +255,12 @@ func (m *FairMix) Next() bool { } select { - case n, ok := <-source.next: + case item, ok := <-source.next: if ok { // Here, the timeout is reset to the configured value // because the source delivered a node. source.timeout = m.timeout - m.cur = n + m.cur = item return true } // This source has ended. @@ -239,15 +277,20 @@ func (m *FairMix) Next() bool { // Node returns the current node. func (m *FairMix) Node() *Node { - return m.cur + return m.cur.n +} + +// NodeSource returns the current node's source name. +func (m *FairMix) NodeSource() string { + return m.cur.source } // nextFromAny is used when there are no sources or when the 'fair' choice // doesn't turn up a node quickly enough. func (m *FairMix) nextFromAny() bool { - n, ok := <-m.fromAny + item, ok := <-m.fromAny if ok { - m.cur = n + m.cur = item } return ok } @@ -284,10 +327,10 @@ func (m *FairMix) runSource(closed chan struct{}, s *mixSource) { defer m.wg.Done() defer close(s.next) for s.it.Next() { - n := s.it.Node() + item := mixItem{s.it.Node(), s.it.NodeSource()} select { - case s.next <- n: - case m.fromAny <- n: + case s.next <- item: + case m.fromAny <- item: case <-closed: return } diff --git a/p2p/enode/iter_test.go b/p2p/enode/iter_test.go index b736ed450ad..577f9c28256 100644 --- a/p2p/enode/iter_test.go +++ b/p2p/enode/iter_test.go @@ -19,6 +19,7 @@ package enode import ( "encoding/binary" "runtime" + "slices" "sync/atomic" "testing" "time" @@ -183,6 +184,53 @@ func TestFairMixRemoveSource(t *testing.T) { } } +// This checks that FairMix correctly returns the name of the source that produced the node. +func TestFairMixSourceName(t *testing.T) { + nodes := make([]*Node, 6) + for i := range nodes { + nodes[i] = testNode(uint64(i), uint64(i)) + } + mix := NewFairMix(-1) + mix.AddSource(WithSourceName("s1", IterNodes(nodes[0:2]))) + mix.AddSource(WithSourceName("s2", IterNodes(nodes[2:4]))) + mix.AddSource(WithSourceName("s3", IterNodes(nodes[4:6]))) + + var names []string + for range nodes { + mix.Next() + names = append(names, mix.NodeSource()) + } + want := []string{"s2", "s3", "s1", "s2", "s3", "s1"} + if !slices.Equal(names, want) { + t.Fatalf("wrong names: %v", names) + } +} + +// This checks that FairMix returns the name of the source that produced the node, +// even when FairMix instances are nested. +func TestFairMixNestedSourceName(t *testing.T) { + nodes := make([]*Node, 6) + for i := range nodes { + nodes[i] = testNode(uint64(i), uint64(i)) + } + mix := NewFairMix(-1) + mix.AddSource(WithSourceName("s1", IterNodes(nodes[0:2]))) + submix := NewFairMix(-1) + submix.AddSource(WithSourceName("s2", IterNodes(nodes[2:4]))) + submix.AddSource(WithSourceName("s3", IterNodes(nodes[4:6]))) + mix.AddSource(submix) + + var names []string + for range nodes { + mix.Next() + names = append(names, mix.NodeSource()) + } + want := []string{"s3", "s1", "s2", "s1", "s3", "s2"} + if !slices.Equal(names, want) { + t.Fatalf("wrong names: %v", names) + } +} + type blockingIter chan struct{} func (it blockingIter) Next() bool { From af9a3a1a03d5155494a54b7b450333d70641e38a Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 15 May 2025 14:43:52 +0200 Subject: [PATCH 174/658] core/state, core/vm: update stateless gas costs to follow the verkle-gen-7 testnet (#31014) Adding values to the witness introduces a new class of issues for computing gas: if there is not enough gas to cover adding an item to the witness, then the item should not be added to the witness. The problem happens when several items are added together, and that process runs out of gas. The witness gas computation needs a way to signal that not enough gas was provided. These values can not be hardcoded, however, as they are context dependent, i.e. two calls to the same function with the same parameters can give two different results. The approach is to return both the gas that was actually consumed, and the gas that was necessary. If the values don't match, then a witness update OOG'd. The caller should then charge the `consumed` value (remaining gas will be 0) and error out. Why not return a boolean instead of the wanted value? Because when several items are touched, we want to distinguish which item lacked gas. --------- Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --- core/state/access_events.go | 187 +++++++++++++++++++------------ core/state/access_events_test.go | 33 +++--- core/state_transition.go | 4 +- core/vm/contracts.go | 2 +- core/vm/eips.go | 22 ++-- core/vm/evm.go | 24 ++-- core/vm/gas_table.go | 19 +--- core/vm/interpreter.go | 6 +- core/vm/jump_table.go | 2 +- core/vm/operations_verkle.go | 178 +++++++++++++++++------------ 10 files changed, 275 insertions(+), 202 deletions(-) diff --git a/core/state/access_events.go b/core/state/access_events.go index b745c383b15..0575c9898ae 100644 --- a/core/state/access_events.go +++ b/core/state/access_events.go @@ -18,6 +18,7 @@ package state import ( "maps" + gomath "math" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" @@ -92,97 +93,94 @@ func (ae *AccessEvents) Copy() *AccessEvents { // AddAccount returns the gas to be charged for each of the currently cold // member fields of an account. -func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 { - var gas uint64 - gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite) - gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite) +func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool, availableGas uint64) uint64 { + var gas uint64 // accumulate the consumed gas + consumed, expected := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, availableGas) + if consumed < expected { + return expected + } + gas += consumed + consumed, expected = ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, availableGas-consumed) + if consumed < expected { + return expected + gas + } + gas += expected return gas } // MessageCallGas returns the gas to be charged for each of the currently // cold member fields of an account, that need to be touched when making a message // call to that account. -func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 { - var gas uint64 - gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false) - return gas +func (ae *AccessEvents) MessageCallGas(destination common.Address, availableGas uint64) uint64 { + _, expected := ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false, availableGas) + if expected == 0 { + expected = params.WarmStorageReadCostEIP2929 + } + return expected } // ValueTransferGas returns the gas to be charged for each of the currently // cold balance member fields of the caller and the callee accounts. -func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 { - var gas uint64 - gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true) - gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true) - return gas +func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address, availableGas uint64) uint64 { + _, expected1 := ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas) + if expected1 > availableGas { + return expected1 + } + _, expected2 := ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas-expected1) + if expected1+expected2 == 0 { + return params.WarmStorageReadCostEIP2929 + } + return expected1 + expected2 } // ContractCreatePreCheckGas charges access costs before // a contract creation is initiated. It is just reads, because the // address collision is done before the transfer, and so no write // are guaranteed to happen at this point. -func (ae *AccessEvents) ContractCreatePreCheckGas(addr common.Address) uint64 { - var gas uint64 - gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false) - gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false) - return gas +func (ae *AccessEvents) ContractCreatePreCheckGas(addr common.Address, availableGas uint64) uint64 { + consumed, expected1 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, availableGas) + _, expected2 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false, availableGas-consumed) + return expected1 + expected2 } // ContractCreateInitGas returns the access gas costs for the initialization of // a contract creation. -func (ae *AccessEvents) ContractCreateInitGas(addr common.Address) uint64 { +func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, availableGas uint64) (uint64, uint64) { var gas uint64 - gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true) - gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true) - return gas + consumed, expected1 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas) + gas += consumed + consumed, expected2 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true, availableGas-consumed) + gas += consumed + return gas, expected1 + expected2 } // AddTxOrigin adds the member fields of the sender account to the access event list, // so that cold accesses are not charged, since they are covered by the 21000 gas. func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) { - ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true) - ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false) + ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, gomath.MaxUint64) + ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false, gomath.MaxUint64) } // AddTxDestination adds the member fields of the sender account to the access event list, // so that cold accesses are not charged, since they are covered by the 21000 gas. -func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) { - ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue) - ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false) +func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue, doesntExist bool) { + ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, gomath.MaxUint64) + ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, doesntExist, gomath.MaxUint64) } // SlotGas returns the amount of gas to be charged for a cold storage access. -func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite bool) uint64 { +func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 { treeIndex, subIndex := utils.StorageIndex(slot.Bytes()) - return ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite) -} - -// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the cold -// access cost to be charged, if need be. -func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 { - stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := ae.touchAddress(addr, treeIndex, subIndex, isWrite) - - var gas uint64 - if stemRead { - gas += params.WitnessBranchReadCost - } - if selectorRead { - gas += params.WitnessChunkReadCost - } - if stemWrite { - gas += params.WitnessBranchWriteCost - } - if selectorWrite { - gas += params.WitnessChunkWriteCost + _, expected := ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite, availableGas) + if expected == 0 && chargeWarmCosts { + expected = params.WarmStorageReadCostEIP2929 } - if selectorFill { - gas += params.WitnessChunkFillCost - } - return gas + return expected } -// touchAddress adds any missing access event to the access event list. -func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) (bool, bool, bool, bool, bool) { +// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the +// consumed and required gas. +func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool, availableGas uint64) (uint64, uint64) { branchKey := newBranchAccessKey(addr, treeIndex) chunkKey := newChunkAccessKey(branchKey, subIndex) @@ -190,11 +188,9 @@ func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, var branchRead, chunkRead bool if _, hasStem := ae.branches[branchKey]; !hasStem { branchRead = true - ae.branches[branchKey] = AccessWitnessReadFlag } if _, hasSelector := ae.chunks[chunkKey]; !hasSelector { chunkRead = true - ae.chunks[chunkKey] = AccessWitnessReadFlag } // Write access. @@ -202,17 +198,51 @@ func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, if isWrite { if (ae.branches[branchKey] & AccessWitnessWriteFlag) == 0 { branchWrite = true - ae.branches[branchKey] |= AccessWitnessWriteFlag } chunkValue := ae.chunks[chunkKey] if (chunkValue & AccessWitnessWriteFlag) == 0 { chunkWrite = true - ae.chunks[chunkKey] |= AccessWitnessWriteFlag } - // TODO: charge chunk filling costs if the leaf was previously empty in the state } - return branchRead, chunkRead, branchWrite, chunkWrite, chunkFill + + var gas uint64 + if branchRead { + gas += params.WitnessBranchReadCost + } + if chunkRead { + gas += params.WitnessChunkReadCost + } + if branchWrite { + gas += params.WitnessBranchWriteCost + } + if chunkWrite { + gas += params.WitnessChunkWriteCost + } + if chunkFill { + gas += params.WitnessChunkFillCost + } + + if availableGas < gas { + // consumed != expected + return availableGas, gas + } + + if branchRead { + ae.branches[branchKey] = AccessWitnessReadFlag + } + if branchWrite { + ae.branches[branchKey] |= AccessWitnessWriteFlag + } + if chunkRead { + ae.chunks[chunkKey] = AccessWitnessReadFlag + } + if chunkWrite { + ae.chunks[chunkKey] |= AccessWitnessWriteFlag + } + + // consumed == expected + return gas, gas } type branchAccessKey struct { @@ -240,7 +270,7 @@ func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey { } // CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs -func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite bool) uint64 { +func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite bool, availableGas uint64) (uint64, uint64) { // note that in the case where the copied code is outside the range of the // contract code but touches the last leaf with contract code in it, // we don't include the last leaf of code in the AccessWitness. The @@ -248,7 +278,7 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, // is already in the AccessWitness so a stateless verifier can see that // the code from the last leaf is not needed. if (codeLen == 0 && size == 0) || startPC > codeLen { - return 0 + return 0, 0 } endPC := startPC + size @@ -263,22 +293,34 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ { treeIndex := *uint256.NewInt((chunkNumber + 128) / 256) subIndex := byte((chunkNumber + 128) % 256) - gas := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite) + consumed, expected := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite, availableGas) + // did we OOG ? + if expected > consumed { + return statelessGasCharged + consumed, statelessGasCharged + expected + } var overflow bool - statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas) + statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, consumed) if overflow { panic("overflow when adding gas") } + availableGas -= consumed } - return statelessGasCharged + return statelessGasCharged, statelessGasCharged } // BasicDataGas adds the account's basic data to the accessed data, and returns the // amount of gas that it costs. // Note that an access in write mode implies an access in read mode, whereas an // access in read mode does not imply an access in write mode. -func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 { - return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite) +func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 { + _, expected := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, availableGas) + if expected == 0 && chargeWarmCosts { + if availableGas < params.WarmStorageReadCostEIP2929 { + return availableGas + } + expected = params.WarmStorageReadCostEIP2929 + } + return expected } // CodeHashGas adds the account's code hash to the accessed data, and returns the @@ -286,6 +328,13 @@ func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 { // in write mode. If false, the charged gas corresponds to an access in read mode. // Note that an access in write mode implies an access in read mode, whereas an access in // read mode does not imply an access in write mode. -func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 { - return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite) +func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 { + _, expected := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, availableGas) + if expected == 0 && chargeWarmCosts { + if availableGas < params.WarmStorageReadCostEIP2929 { + return availableGas + } + expected = params.WarmStorageReadCostEIP2929 + } + return expected } diff --git a/core/state/access_events_test.go b/core/state/access_events_test.go index 10630b3181b..5e1fee767c5 100644 --- a/core/state/access_events_test.go +++ b/core/state/access_events_test.go @@ -17,6 +17,7 @@ package state import ( + "math" "testing" "github.com/ethereum/go-ethereum/common" @@ -40,50 +41,50 @@ func TestAccountHeaderGas(t *testing.T) { ae := NewAccessEvents(utils.NewPointCache(1024)) // Check cold read cost - gas := ae.BasicDataGas(testAddr, false) + gas := ae.BasicDataGas(testAddr, false, math.MaxUint64, false) if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want { t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) } // Check warm read cost - gas = ae.BasicDataGas(testAddr, false) + gas = ae.BasicDataGas(testAddr, false, math.MaxUint64, false) if gas != 0 { t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) } // Check cold read costs in the same group no longer incur the branch read cost - gas = ae.CodeHashGas(testAddr, false) + gas = ae.CodeHashGas(testAddr, false, math.MaxUint64, false) if gas != params.WitnessChunkReadCost { t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost) } // Check cold write cost - gas = ae.BasicDataGas(testAddr, true) + gas = ae.BasicDataGas(testAddr, true, math.MaxUint64, false) if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want { t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) } // Check warm write cost - gas = ae.BasicDataGas(testAddr, true) + gas = ae.BasicDataGas(testAddr, true, math.MaxUint64, false) if gas != 0 { t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) } // Check a write without a read charges both read and write costs - gas = ae.BasicDataGas(testAddr2, true) + gas = ae.BasicDataGas(testAddr2, true, math.MaxUint64, false) if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want { t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) } // Check that a write followed by a read charges nothing - gas = ae.BasicDataGas(testAddr2, false) + gas = ae.BasicDataGas(testAddr2, false, math.MaxUint64, false) if gas != 0 { t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) } // Check that reading a slot from the account header only charges the // chunk read cost. - gas = ae.SlotGas(testAddr, common.Hash{}, false) + gas = ae.SlotGas(testAddr, common.Hash{}, false, math.MaxUint64, false) if gas != params.WitnessChunkReadCost { t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost) } @@ -100,13 +101,13 @@ func TestContractCreateInitGas(t *testing.T) { } // Check cold read cost, without a value - gas := ae.ContractCreateInitGas(testAddr) + gas, _ := ae.ContractCreateInitGas(testAddr, math.MaxUint64) if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + 2*params.WitnessChunkWriteCost + 2*params.WitnessChunkReadCost; gas != want { t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) } // Check warm read cost - gas = ae.ContractCreateInitGas(testAddr) + gas, _ = ae.ContractCreateInitGas(testAddr, math.MaxUint64) if gas != 0 { t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) } @@ -118,24 +119,24 @@ func TestMessageCallGas(t *testing.T) { ae := NewAccessEvents(utils.NewPointCache(1024)) // Check cold read cost, without a value - gas := ae.MessageCallGas(testAddr) + gas := ae.MessageCallGas(testAddr, math.MaxUint64) if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want { t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) } // Check that reading the basic data and code hash of the same account does not incur the branch read cost - gas = ae.BasicDataGas(testAddr, false) + gas = ae.BasicDataGas(testAddr, false, math.MaxUint64, false) if gas != 0 { t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) } - gas = ae.CodeHashGas(testAddr, false) + gas = ae.CodeHashGas(testAddr, false, math.MaxUint64, false) if gas != params.WitnessChunkReadCost { t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) } // Check warm read cost - gas = ae.MessageCallGas(testAddr) - if gas != 0 { - t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) + gas = ae.MessageCallGas(testAddr, math.MaxUint64) + if gas != params.WarmStorageReadCostEIP2929 { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WarmStorageReadCostEIP2929) } } diff --git a/core/state_transition.go b/core/state_transition.go index ff2051ddd2e..4f172682d07 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -457,7 +457,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { st.evm.AccessEvents.AddTxOrigin(msg.From) if targetAddr := msg.To; targetAddr != nil { - st.evm.AccessEvents.AddTxDestination(*targetAddr, msg.Value.Sign() != 0) + st.evm.AccessEvents.AddTxDestination(*targetAddr, msg.Value.Sign() != 0, !st.state.Exist(*targetAddr)) } } @@ -552,7 +552,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { // add the coinbase to the witness iff the fee is greater than 0 if rules.IsEIP4762 && fee.Sign() != 0 { - st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true) + st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true, math.MaxUint64) } } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 06849e65ade..784b4108026 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -139,7 +139,7 @@ var PrecompiledContractsPrague = PrecompiledContracts{ var PrecompiledContractsBLS = PrecompiledContractsPrague -var PrecompiledContractsVerkle = PrecompiledContractsPrague +var PrecompiledContractsVerkle = PrecompiledContractsBerlin var ( PrecompiledAddressesPrague []common.Address diff --git a/core/vm/eips.go b/core/vm/eips.go index 6159eade7e2..d95fa512843 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -339,12 +339,10 @@ func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC addr := common.Address(a.Bytes20()) code := interpreter.evm.StateDB.GetCode(addr) paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) - if !scope.Contract.IsSystemCall { - statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false) - if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { - scope.Contract.Gas = 0 - return nil, ErrOutOfGas - } + consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas) + scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) + if consumed < wanted { + return nil, ErrOutOfGas } scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy) @@ -367,9 +365,9 @@ func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext // touch next chunk if PUSH1 is at the boundary. if so, *pc has // advanced past this boundary. contractAddr := scope.Contract.Address() - statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false) - if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { - scope.Contract.Gas = 0 + consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas) + scope.Contract.UseGas(wanted, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) + if consumed < wanted { return nil, ErrOutOfGas } } @@ -395,9 +393,9 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc { if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall { contractAddr := scope.Contract.Address() - statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false) - if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { - scope.Contract.Gas = 0 + consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas) + scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) + if consumed < wanted { return nil, ErrOutOfGas } } diff --git a/core/vm/evm.go b/core/vm/evm.go index ecb0f118ecc..b45a4345453 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -206,8 +206,14 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g if !evm.StateDB.Exist(addr) { if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) { - // add proof of absence to witness - wgas := evm.AccessEvents.AddAccount(addr, false) + // Add proof of absence to witness + // At this point, the read costs have already been charged, either because this + // is a direct tx call, in which case it's covered by the intrinsic gas, or because + // of a CALL instruction, in which case BASIC_DATA has been added to the access + // list in write mode. If there is enough gas paying for the addition of the code + // hash leaf to the access list, then account creation will proceed unimpaired. + // Thus, only pay for the creation of the code hash leaf here. + wgas := evm.AccessEvents.CodeHashGas(addr, true, gas, false) if gas < wgas { evm.StateDB.RevertToSnapshot(snapshot) return nil, 0, ErrOutOfGas @@ -433,7 +439,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui // Charge the contract creation init gas in verkle mode if evm.chainRules.IsEIP4762 { - statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address) + statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas) if statelessGas > gas { return nil, common.Address{}, 0, ErrOutOfGas } @@ -481,14 +487,14 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui } // Charge the contract creation init gas in verkle mode if evm.chainRules.IsEIP4762 { - statelessGas := evm.AccessEvents.ContractCreateInitGas(address) - if statelessGas > gas { + consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas) + if consumed < wanted { return nil, common.Address{}, 0, ErrOutOfGas } if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { - evm.Config.Tracer.OnGasChange(gas, gas-statelessGas, tracing.GasChangeWitnessContractInit) + evm.Config.Tracer.OnGasChange(gas, gas-consumed, tracing.GasChangeWitnessContractInit) } - gas = gas - statelessGas + gas = gas - consumed } evm.Context.Transfer(evm.StateDB, caller, address, value) @@ -535,7 +541,9 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b return ret, ErrCodeStoreOutOfGas } } else { - if len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) { + consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas) + contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) + if len(ret) > 0 && (consumed < wanted) { return ret, ErrCodeStoreOutOfGas } } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 55855727b52..f711aa4a187 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -394,14 +394,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { return 0, ErrGasUintOverflow } - if evm.chainRules.IsEIP4762 && !contract.IsSystemCall { - if transfersValue { - gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address)) - if overflow { - return 0, ErrGasUintOverflow - } - } - } + evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err @@ -428,16 +421,6 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { return 0, ErrGasUintOverflow } - if evm.chainRules.IsEIP4762 && !contract.IsSystemCall { - address := common.Address(stack.Back(1).Bytes20()) - transfersValue := !stack.Back(2).IsZero() - if transfersValue { - gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address)) - if overflow { - return 0, ErrGasUintOverflow - } - } - } evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index a62c3c843d9..d0e5967e6e7 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -237,7 +237,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // if the PC ends up in a new "chunk" of verkleized code, charge the // associated costs. contractAddr := contract.Address() - contract.Gas -= in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false) + consumed, wanted := in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas) + contract.UseGas(consumed, in.evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) + if consumed < wanted { + return nil, ErrOutOfGas + } } // Get the operation from the jump table and validate the stack to ensure there are diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index ee811b447e8..26b9473fe84 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -87,7 +87,7 @@ func validate(jt JumpTable) JumpTable { } func newVerkleInstructionSet() JumpTable { - instructionSet := newCancunInstructionSet() + instructionSet := newShanghaiInstructionSet() enable4762(&instructionSet) return validate(instructionSet) } diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go index 751761a9115..30f99577754 100644 --- a/core/vm/operations_verkle.go +++ b/core/vm/operations_verkle.go @@ -25,31 +25,16 @@ import ( ) func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true) - if gas == 0 { - gas = params.WarmStorageReadCostEIP2929 - } - return gas, nil + return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas, true), nil } func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false) - if gas == 0 { - gas = params.WarmStorageReadCostEIP2929 - } - return gas, nil + return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas, true), nil } func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - if contract.IsSystemCall { - return 0, nil - } address := stack.peek().Bytes20() - gas := evm.AccessEvents.BasicDataGas(address, false) - if gas == 0 { - gas = params.WarmStorageReadCostEIP2929 - } - return gas, nil + return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil } func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { @@ -57,56 +42,69 @@ func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, if _, isPrecompile := evm.precompile(address); isPrecompile { return 0, nil } - if contract.IsSystemCall { - return 0, nil - } - gas := evm.AccessEvents.BasicDataGas(address, false) - if gas == 0 { - gas = params.WarmStorageReadCostEIP2929 - } - return gas, nil + return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil } func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - if contract.IsSystemCall { - return 0, nil - } address := stack.peek().Bytes20() if _, isPrecompile := evm.precompile(address); isPrecompile { return 0, nil } - gas := evm.AccessEvents.CodeHashGas(address, false) - if gas == 0 { - gas = params.WarmStorageReadCostEIP2929 - } - return gas, nil + return evm.AccessEvents.CodeHashGas(address, false, contract.Gas, true), nil } -func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc { +func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) gasFunc { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := oldCalculator(evm, contract, stack, mem, memorySize) - if err != nil { - return 0, err - } - if contract.IsSystemCall { - return gas, nil - } - if _, isPrecompile := evm.precompile(contract.Address()); isPrecompile { - return gas, nil - } - witnessGas := evm.AccessEvents.MessageCallGas(contract.Address()) - if witnessGas == 0 { + var ( + target = common.Address(stack.Back(1).Bytes20()) + witnessGas uint64 + _, isPrecompile = evm.precompile(target) + isSystemContract = target == params.HistoryStorageAddress + ) + + // If value is transferred, it is charged before 1/64th + // is subtracted from the available gas pool. + if withTransferCosts && !stack.Back(2).IsZero() { + wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas) + if wantedValueTransferWitnessGas > contract.Gas { + return wantedValueTransferWitnessGas, nil + } + witnessGas = wantedValueTransferWitnessGas + } else if isPrecompile || isSystemContract { witnessGas = params.WarmStorageReadCostEIP2929 + } else { + // The charging for the value transfer is done BEFORE subtracting + // the 1/64th gas, as this is considered part of the CALL instruction. + // (so before we get to this point) + // But the message call is part of the subcall, for which only 63/64th + // of the gas should be available. + wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas-witnessGas) + var overflow bool + if witnessGas, overflow = math.SafeAdd(witnessGas, wantedMessageCallWitnessGas); overflow { + return 0, ErrGasUintOverflow + } + if witnessGas > contract.Gas { + return witnessGas, nil + } + } + + contract.Gas -= witnessGas + // if the operation fails, adds witness gas to the gas before returning the error + gas, err := oldCalculator(evm, contract, stack, mem, memorySize) + contract.Gas += witnessGas // restore witness gas so that it can be charged at the callsite + var overflow bool + if gas, overflow = math.SafeAdd(gas, witnessGas); overflow { + return 0, ErrGasUintOverflow } - return witnessGas + gas, nil + return gas, err } } var ( - gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall) - gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode) - gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall) - gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall) + gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall, true) + gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode, false) + gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall, false) + gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall, false) ) func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { @@ -118,15 +116,44 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem return 0, nil } contractAddr := contract.Address() - statelessGas := evm.AccessEvents.BasicDataGas(contractAddr, false) + wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas, false) + if wanted > contract.Gas { + return wanted, nil + } + statelessGas := wanted + balanceIsZero := evm.StateDB.GetBalance(contractAddr).Sign() == 0 + _, isPrecompile := evm.precompile(beneficiaryAddr) + isSystemContract := beneficiaryAddr == params.HistoryStorageAddress + + if (isPrecompile || isSystemContract) && balanceIsZero { + return statelessGas, nil + } + if contractAddr != beneficiaryAddr { - statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, false) + wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas-statelessGas, false) + if wanted > contract.Gas-statelessGas { + return statelessGas + wanted, nil + } + statelessGas += wanted } // Charge write costs if it transfers value - if evm.StateDB.GetBalance(contractAddr).Sign() != 0 { - statelessGas += evm.AccessEvents.BasicDataGas(contractAddr, true) + if !balanceIsZero { + wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas-statelessGas, false) + if wanted > contract.Gas-statelessGas { + return statelessGas + wanted, nil + } + statelessGas += wanted + if contractAddr != beneficiaryAddr { - statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, true) + if evm.StateDB.Exist(beneficiaryAddr) { + wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas-statelessGas, false) + } else { + wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas-statelessGas) + } + if wanted > contract.Gas-statelessGas { + return statelessGas + wanted, nil + } + statelessGas += wanted } } return statelessGas, nil @@ -137,17 +164,19 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, if err != nil { return 0, err } - var ( - codeOffset = stack.Back(1) - length = stack.Back(2) - ) - uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() - if overflow { - uint64CodeOffset = gomath.MaxUint64 - } - _, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64()) if !contract.IsDeployment && !contract.IsSystemCall { - gas += evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false) + var ( + codeOffset = stack.Back(1) + length = stack.Back(2) + ) + uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() + if overflow { + uint64CodeOffset = gomath.MaxUint64 + } + + _, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64()) + _, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas-gas) + gas += wanted } return gas, nil } @@ -158,16 +187,17 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo if err != nil { return 0, err } - if contract.IsSystemCall { - return gas, nil - } addr := common.Address(stack.peek().Bytes20()) - wgas := evm.AccessEvents.BasicDataGas(addr, false) - if wgas == 0 { - wgas = params.WarmStorageReadCostEIP2929 + _, isPrecompile := evm.precompile(addr) + if isPrecompile || addr == params.HistoryStorageAddress { + var overflow bool + if gas, overflow = math.SafeAdd(gas, params.WarmStorageReadCostEIP2929); overflow { + return 0, ErrGasUintOverflow + } + return gas, nil } + wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas-gas, true) var overflow bool - // We charge (cold-warm), since 'warm' is already charged as constantGas if gas, overflow = math.SafeAdd(gas, wgas); overflow { return 0, ErrGasUintOverflow } From 85b26f3d6841fb1bc90a77ada7e9cf62988c8cc0 Mon Sep 17 00:00:00 2001 From: Charlotte <69423184+tqpcharlie@users.noreply.github.com> Date: Thu, 15 May 2025 15:55:38 -0400 Subject: [PATCH 175/658] AUTHORS, .mailmap: update name and email attribution (#31624) --- .mailmap | 7 ++++--- AUTHORS | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.mailmap b/.mailmap index 92a9e077489..597da62dc35 100644 --- a/.mailmap +++ b/.mailmap @@ -50,6 +50,10 @@ Boqin Qin Casey Detrio +Charlotte +Charlotte +Charlotte + Cheng Li Chris Ziogas @@ -301,9 +305,6 @@ Yohann Léon yzb <335357057@qq.com> yzb <335357057@qq.com> -Zachinquarantine -Zachinquarantine - Ziyuan Zhong Zsolt Felföldi diff --git a/AUTHORS b/AUTHORS index 1ec240aeb6a..da482717c63 100644 --- a/AUTHORS +++ b/AUTHORS @@ -123,6 +123,7 @@ Ceyhun Onur chabashilah changhong Charles Cooper +Charlotte Chase Wright Chawin Aiemvaravutigul Chen Quan @@ -839,7 +840,6 @@ ywzqwwt <39263032+ywzqwwt@users.noreply.github.com> yzb <335357057@qq.com> zaccoding Zach -Zachinquarantine zah Zahoor Mohamed Zak Cole From 3ceec0ea9bd66346071512587cb2bdbd7160e48f Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 15 May 2025 22:53:26 +0200 Subject: [PATCH 176/658] cmd/geth, internal/era/eradl: add era1 downloader tool (#31823) This adds a geth subcommand for downloading era1 files and placing them into the correct location. The tool can be used even while geth is already running on the datadir. Downloads are checked against a hard-coded list of checksums for mainnet and sepolia. ``` ./geth download-era --server $SERVER --block 333333 ./geth download-era --server $SERVER --block 333333-444444 ./geth download-era --server $SERVER --epoch 0-10 ./geth download-era --server $SERVER --all ``` The implementation reuses the file downloader we already had for fetching build tools. I've done some refactoring on it to make sure it can support the new use case, and there are some changes to the build here as well. --- build/ci.go | 55 +- cmd/geth/chaincmd.go | 122 +- cmd/geth/main.go | 3 +- internal/build/download.go | 151 -- internal/build/gotool.go | 54 +- internal/download/download.go | 298 ++++ internal/era/eradl/checksums_mainnet.txt | 1897 ++++++++++++++++++++++ internal/era/eradl/checksums_sepolia.txt | 183 +++ internal/era/eradl/eradl.go | 115 ++ 9 files changed, 2642 insertions(+), 236 deletions(-) delete mode 100644 internal/build/download.go create mode 100644 internal/download/download.go create mode 100644 internal/era/eradl/checksums_mainnet.txt create mode 100644 internal/era/eradl/checksums_sepolia.txt create mode 100644 internal/era/eradl/eradl.go diff --git a/build/ci.go b/build/ci.go index b2ff829d3f8..88e4e82282a 100644 --- a/build/ci.go +++ b/build/ci.go @@ -59,6 +59,7 @@ import ( "github.com/cespare/cp" "github.com/ethereum/go-ethereum/crypto/signify" "github.com/ethereum/go-ethereum/internal/build" + "github.com/ethereum/go-ethereum/internal/download" "github.com/ethereum/go-ethereum/internal/version" ) @@ -190,7 +191,7 @@ func doInstall(cmdline []string) { // Configure the toolchain. tc := build.GoToolchain{GOARCH: *arch, CC: *cc} if *dlgo { - csdb := build.MustLoadChecksums("build/checksums.txt") + csdb := download.MustLoadChecksums("build/checksums.txt") tc.Root = build.DownloadGo(csdb) } // Disable CLI markdown doc generation in release builds. @@ -285,7 +286,7 @@ func doTest(cmdline []string) { flag.CommandLine.Parse(cmdline) // Get test fixtures. - csdb := build.MustLoadChecksums("build/checksums.txt") + csdb := download.MustLoadChecksums("build/checksums.txt") downloadSpecTestFixtures(csdb, *cachedir) // Configure the toolchain. @@ -329,16 +330,11 @@ func doTest(cmdline []string) { } // downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures. -func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string { - executionSpecTestsVersion, err := build.Version(csdb, "spec-tests") - if err != nil { - log.Fatal(err) - } +func downloadSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string { ext := ".tar.gz" base := "fixtures_pectra-devnet-6" // TODO(s1na) rename once the version becomes part of the filename - url := fmt.Sprintf("https://github.com/ethereum/execution-spec-tests/releases/download/%s/%s%s", executionSpecTestsVersion, base, ext) archivePath := filepath.Join(cachedir, base+ext) - if err := csdb.DownloadFile(url, archivePath); err != nil { + if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil { log.Fatal(err) } if err := build.ExtractArchive(archivePath, executionSpecTestsDir); err != nil { @@ -444,14 +440,13 @@ func doLint(cmdline []string) { // downloadLinter downloads and unpacks golangci-lint. func downloadLinter(cachedir string) string { - csdb := build.MustLoadChecksums("build/checksums.txt") - version, err := build.Version(csdb, "golangci") + csdb := download.MustLoadChecksums("build/checksums.txt") + version, err := csdb.FindVersion("golangci") if err != nil { log.Fatal(err) } arch := runtime.GOARCH ext := ".tar.gz" - if runtime.GOOS == "windows" { ext = ".zip" } @@ -459,9 +454,8 @@ func downloadLinter(cachedir string) string { arch += "v" + os.Getenv("GOARM") } base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, arch) - url := fmt.Sprintf("https://github.com/golangci/golangci-lint/releases/download/v%s/%s%s", version, base, ext) archivePath := filepath.Join(cachedir, base+ext) - if err := csdb.DownloadFile(url, archivePath); err != nil { + if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil { log.Fatal(err) } if err := build.ExtractArchive(archivePath, cachedir); err != nil { @@ -497,8 +491,8 @@ func protocArchiveBaseName() (string, error) { // in the generate command. It returns the full path of the directory // containing the 'protoc-gen-go' executable. func downloadProtocGenGo(cachedir string) string { - csdb := build.MustLoadChecksums("build/checksums.txt") - version, err := build.Version(csdb, "protoc-gen-go") + csdb := download.MustLoadChecksums("build/checksums.txt") + version, err := csdb.FindVersion("protoc-gen-go") if err != nil { log.Fatal(err) } @@ -510,10 +504,8 @@ func downloadProtocGenGo(cachedir string) string { archiveName += ".tar.gz" } - url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf-go/releases/download/v%s/%s", version, archiveName) - archivePath := path.Join(cachedir, archiveName) - if err := csdb.DownloadFile(url, archivePath); err != nil { + if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil { log.Fatal(err) } extractDest := filepath.Join(cachedir, baseName) @@ -531,8 +523,8 @@ func downloadProtocGenGo(cachedir string) string { // files as a CI step. It returns the full path to the directory containing // the protoc executable. func downloadProtoc(cachedir string) string { - csdb := build.MustLoadChecksums("build/checksums.txt") - version, err := build.Version(csdb, "protoc") + csdb := download.MustLoadChecksums("build/checksums.txt") + version, err := csdb.FindVersion("protoc") if err != nil { log.Fatal(err) } @@ -543,10 +535,8 @@ func downloadProtoc(cachedir string) string { fileName := fmt.Sprintf("protoc-%s-%s", version, baseName) archiveFileName := fileName + ".zip" - url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf/releases/download/v%s/%s", version, archiveFileName) archivePath := filepath.Join(cachedir, archiveFileName) - - if err := csdb.DownloadFile(url, archivePath); err != nil { + if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil { log.Fatal(err) } extractDest := filepath.Join(cachedir, fileName) @@ -826,18 +816,17 @@ func doDebianSource(cmdline []string) { // downloadGoBootstrapSources downloads the Go source tarball(s) that will be used // to bootstrap the builder Go. func downloadGoBootstrapSources(cachedir string) []string { - csdb := build.MustLoadChecksums("build/checksums.txt") + csdb := download.MustLoadChecksums("build/checksums.txt") var bundles []string for _, booter := range []string{"ppa-builder-1.19", "ppa-builder-1.21", "ppa-builder-1.23"} { - gobootVersion, err := build.Version(csdb, booter) + gobootVersion, err := csdb.FindVersion(booter) if err != nil { log.Fatal(err) } file := fmt.Sprintf("go%s.src.tar.gz", gobootVersion) - url := "https://dl.google.com/go/" + file dst := filepath.Join(cachedir, file) - if err := csdb.DownloadFile(url, dst); err != nil { + if err := csdb.DownloadFileFromKnownURL(dst); err != nil { log.Fatal(err) } bundles = append(bundles, dst) @@ -847,15 +836,14 @@ func downloadGoBootstrapSources(cachedir string) []string { // downloadGoSources downloads the Go source tarball. func downloadGoSources(cachedir string) string { - csdb := build.MustLoadChecksums("build/checksums.txt") - dlgoVersion, err := build.Version(csdb, "golang") + csdb := download.MustLoadChecksums("build/checksums.txt") + dlgoVersion, err := csdb.FindVersion("golang") if err != nil { log.Fatal(err) } file := fmt.Sprintf("go%s.src.tar.gz", dlgoVersion) - url := "https://dl.google.com/go/" + file dst := filepath.Join(cachedir, file) - if err := csdb.DownloadFile(url, dst); err != nil { + if err := csdb.DownloadFileFromKnownURL(dst); err != nil { log.Fatal(err) } return dst @@ -1181,5 +1169,6 @@ func doPurge(cmdline []string) { } func doSanityCheck() { - build.DownloadAndVerifyChecksums(build.MustLoadChecksums("build/checksums.txt")) + csdb := download.MustLoadChecksums("build/checksums.txt") + csdb.DownloadAndVerifyAll() } diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index a947f35f2f6..dc071725c11 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -21,9 +21,12 @@ import ( "errors" "fmt" "os" + "path/filepath" + "regexp" "runtime" "slices" "strconv" + "strings" "sync/atomic" "time" @@ -39,6 +42,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/era" + "github.com/ethereum/go-ethereum/internal/era/eradl" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -190,7 +194,7 @@ This command dumps out the state for a given block (or latest, if none provided) `, } - pruneCommand = &cli.Command{ + pruneHistoryCommand = &cli.Command{ Action: pruneHistory, Name: "prune-history", Usage: "Prune blockchain history (block bodies and receipts) up to the merge block", @@ -201,6 +205,42 @@ The prune-history command removes historical block bodies and receipts from the blockchain database up to the merge block, while preserving block headers. This helps reduce storage requirements for nodes that don't need full historical data.`, } + + downloadEraCommand = &cli.Command{ + Action: downloadEra, + Name: "download-era", + Usage: "Fetches era1 files (pre-merge history) from an HTTP endpoint", + ArgsUsage: "", + Flags: slices.Concat( + utils.DatabaseFlags, + utils.NetworkFlags, + []cli.Flag{ + eraBlockFlag, + eraEpochFlag, + eraAllFlag, + eraServerFlag, + }, + ), + } +) + +var ( + eraBlockFlag = &cli.StringFlag{ + Name: "block", + Usage: "Block number to fetch. (can also be a range -)", + } + eraEpochFlag = &cli.StringFlag{ + Name: "epoch", + Usage: "Epoch number to fetch (can also be a range -)", + } + eraAllFlag = &cli.BoolFlag{ + Name: "all", + Usage: "Download all available era1 files", + } + eraServerFlag = &cli.StringFlag{ + Name: "server", + Usage: "era1 server URL", + } ) // initGenesis will initialise the given JSON format genesis file and writes it as @@ -665,3 +705,83 @@ func pruneHistory(ctx *cli.Context) error { return nil } + +// downladEra is the era1 file downloader tool. +func downloadEra(ctx *cli.Context) error { + flags.CheckExclusive(ctx, eraBlockFlag, eraEpochFlag, eraAllFlag) + + // Resolve the network. + var network = "mainnet" + if utils.IsNetworkPreset(ctx) { + switch { + case ctx.IsSet(utils.MainnetFlag.Name): + case ctx.IsSet(utils.SepoliaFlag.Name): + network = "sepolia" + default: + return fmt.Errorf("unsupported network, no known era1 checksums") + } + } + + // Resolve the destination directory. + stack, _ := makeConfigNode(ctx) + defer stack.Close() + ancients := stack.ResolveAncient("chaindata", "") + dir := filepath.Join(ancients, "era") + + baseURL := ctx.String(eraServerFlag.Name) + if baseURL == "" { + return fmt.Errorf("need --%s flag to download", eraServerFlag.Name) + } + + l, err := eradl.New(baseURL, network) + if err != nil { + return err + } + switch { + case ctx.IsSet(eraAllFlag.Name): + return l.DownloadAll(dir) + + case ctx.IsSet(eraBlockFlag.Name): + s := ctx.String(eraBlockFlag.Name) + start, end, ok := parseRange(s) + if !ok { + return fmt.Errorf("invalid block range: %q", s) + } + return l.DownloadBlockRange(start, end, dir) + + case ctx.IsSet(eraEpochFlag.Name): + s := ctx.String(eraEpochFlag.Name) + start, end, ok := parseRange(s) + if !ok { + return fmt.Errorf("invalid epoch range: %q", s) + } + return l.DownloadEpochRange(start, end, dir) + + default: + return fmt.Errorf("specify one of --%s, --%s, or --%s to download", eraAllFlag.Name, eraBlockFlag.Name, eraEpochFlag.Name) + } +} + +func parseRange(s string) (start uint64, end uint64, ok bool) { + if m, _ := regexp.MatchString("[0-9]+", s); m { + start, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return 0, 0, false + } + end = start + return start, end, true + } + if m, _ := regexp.MatchString("[0-9]+-[0-9]+", s); m { + s1, s2, _ := strings.Cut(s, "-") + start, err := strconv.ParseUint(s1, 10, 64) + if err != nil { + return 0, 0, false + } + end, err = strconv.ParseUint(s2, 10, 64) + if err != nil { + return 0, 0, false + } + return start, end, true + } + return 0, 0, false +} diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 289030ae659..bdff96e3694 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -225,7 +225,8 @@ func init() { removedbCommand, dumpCommand, dumpGenesisCommand, - pruneCommand, + pruneHistoryCommand, + downloadEraCommand, // See accountcmd.go: accountCommand, walletCommand, diff --git a/internal/build/download.go b/internal/build/download.go deleted file mode 100644 index 50268227a50..00000000000 --- a/internal/build/download.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2019 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 build - -import ( - "bufio" - "crypto/sha256" - "encoding/hex" - "fmt" - "io" - "log" - "net/http" - "os" - "path/filepath" - "strings" -) - -// ChecksumDB keeps file checksums. -type ChecksumDB struct { - allChecksums []string -} - -// MustLoadChecksums loads a file containing checksums. -func MustLoadChecksums(file string) *ChecksumDB { - content, err := os.ReadFile(file) - if err != nil { - log.Fatal("can't load checksum file: " + err.Error()) - } - return &ChecksumDB{strings.Split(strings.ReplaceAll(string(content), "\r\n", "\n"), "\n")} -} - -// Verify checks whether the given file is valid according to the checksum database. -func (db *ChecksumDB) Verify(path string) error { - fd, err := os.Open(path) - if err != nil { - return err - } - defer fd.Close() - - h := sha256.New() - if _, err := io.Copy(h, bufio.NewReader(fd)); err != nil { - return err - } - fileHash := hex.EncodeToString(h.Sum(nil)) - if !db.findHash(filepath.Base(path), fileHash) { - return fmt.Errorf("invalid file hash: %s %s", fileHash, filepath.Base(path)) - } - return nil -} - -func (db *ChecksumDB) findHash(basename, hash string) bool { - want := hash + " " + basename - for _, line := range db.allChecksums { - if strings.TrimSpace(line) == want { - return true - } - } - return false -} - -// DownloadFile downloads a file and verifies its checksum. -func (db *ChecksumDB) DownloadFile(url, dstPath string) error { - if err := db.Verify(dstPath); err == nil { - fmt.Printf("%s is up-to-date\n", dstPath) - return nil - } - fmt.Printf("%s is stale\n", dstPath) - fmt.Printf("downloading from %s\n", url) - - resp, err := http.Get(url) - if err != nil { - return fmt.Errorf("download error: %v", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("download error: status %d", resp.StatusCode) - } - if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil { - return err - } - fd, err := os.OpenFile(dstPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) - if err != nil { - return err - } - dst := newDownloadWriter(fd, resp.ContentLength) - _, err = io.Copy(dst, resp.Body) - dst.Close() - if err != nil { - return err - } - return db.Verify(dstPath) -} - -type downloadWriter struct { - file *os.File - dstBuf *bufio.Writer - size int64 - written int64 - lastpct int64 -} - -func newDownloadWriter(dst *os.File, size int64) *downloadWriter { - return &downloadWriter{ - file: dst, - dstBuf: bufio.NewWriter(dst), - size: size, - } -} - -func (w *downloadWriter) Write(buf []byte) (int, error) { - n, err := w.dstBuf.Write(buf) - - // Report progress. - w.written += int64(n) - pct := w.written * 10 / w.size * 10 - if pct != w.lastpct { - if w.lastpct != 0 { - fmt.Print("...") - } - fmt.Print(pct, "%") - w.lastpct = pct - } - return n, err -} - -func (w *downloadWriter) Close() error { - if w.lastpct > 0 { - fmt.Println() // Finish the progress line. - } - flushErr := w.dstBuf.Flush() - closeErr := w.file.Close() - if flushErr != nil { - return flushErr - } - return closeErr -} diff --git a/internal/build/gotool.go b/internal/build/gotool.go index 2a474604183..172fa134647 100644 --- a/internal/build/gotool.go +++ b/internal/build/gotool.go @@ -24,6 +24,8 @@ import ( "path/filepath" "runtime" "strings" + + "github.com/ethereum/go-ethereum/internal/download" ) type GoToolchain struct { @@ -84,8 +86,8 @@ func (g *GoToolchain) goTool(command string, args ...string) *exec.Cmd { // DownloadGo downloads the Go binary distribution and unpacks it into a temporary // directory. It returns the GOROOT of the unpacked toolchain. -func DownloadGo(csdb *ChecksumDB) string { - version, err := Version(csdb, "golang") +func DownloadGo(csdb *download.ChecksumDB) string { + version, err := csdb.FindVersion("golang") if err != nil { log.Fatal(err) } @@ -130,51 +132,3 @@ func DownloadGo(csdb *ChecksumDB) string { } return goroot } - -// Version returns the versions defined in the checksumdb. -func Version(csdb *ChecksumDB, version string) (string, error) { - for _, l := range csdb.allChecksums { - if !strings.HasPrefix(l, "# version:") { - continue - } - v := strings.Split(l, ":")[1] - parts := strings.Split(v, " ") - if len(parts) != 2 { - log.Print("Erroneous version-string", "v", l) - continue - } - if parts[0] == version { - return parts[1], nil - } - } - return "", fmt.Errorf("no version found for '%v'", version) -} - -// DownloadAndVerifyChecksums downloads all files and checks that they match -// the checksum given in checksums.txt. -// This task can be used to sanity-check new checksums. -func DownloadAndVerifyChecksums(csdb *ChecksumDB) { - var ( - base = "" - ucache = os.TempDir() - ) - for _, l := range csdb.allChecksums { - if strings.HasPrefix(l, "# https://") { - base = l[2:] - continue - } - if strings.HasPrefix(l, "#") { - continue - } - hashFile := strings.Split(l, " ") - if len(hashFile) != 2 { - continue - } - file := hashFile[1] - url := base + file - dst := filepath.Join(ucache, file) - if err := csdb.DownloadFile(url, dst); err != nil { - log.Print(err) - } - } -} diff --git a/internal/download/download.go b/internal/download/download.go new file mode 100644 index 00000000000..26c7795ce59 --- /dev/null +++ b/internal/download/download.go @@ -0,0 +1,298 @@ +// Copyright 2019 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 download implements checksum-verified file downloads. +package download + +import ( + "bufio" + "bytes" + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "iter" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" +) + +// ChecksumDB keeps file checksums and tool versions. +type ChecksumDB struct { + hashes []hashEntry + versions []versionEntry +} + +type versionEntry struct { + name string + version string +} + +type hashEntry struct { + hash string + file string + url *url.URL +} + +// MustLoadChecksums loads a file containing checksums. +func MustLoadChecksums(file string) *ChecksumDB { + content, err := os.ReadFile(file) + if err != nil { + panic("can't load checksum file: " + err.Error()) + } + db, err := ParseChecksums(content) + if err != nil { + panic(fmt.Sprintf("invalid checksums in %s: %v", file, err)) + } + return db +} + +// ParseChecksums parses a checksum database. +func ParseChecksums(input []byte) (*ChecksumDB, error) { + var ( + csdb = new(ChecksumDB) + rd = bytes.NewBuffer(input) + lastURL *url.URL + ) + for lineNum := 1; ; lineNum++ { + line, err := rd.ReadString('\n') + if err == io.EOF { + break + } + line = strings.TrimSpace(line) + switch { + case line == "": + // Blank lines are allowed, and they reset the current urlEntry. + lastURL = nil + + case strings.HasPrefix(line, "#"): + // It's a comment. Some comments have special meaning. + content := strings.TrimLeft(line, "# ") + switch { + case strings.HasPrefix(content, "version:"): + // Version comments define the version of a tool. + v := strings.Split(content, ":")[1] + parts := strings.Split(v, " ") + if len(parts) != 2 { + return nil, fmt.Errorf("line %d: invalid version string: %q", lineNum, v) + } + csdb.versions = append(csdb.versions, versionEntry{parts[0], parts[1]}) + + case strings.HasPrefix(content, "https://") || strings.HasPrefix(content, "http://"): + // URL comments define the URL where the following files are found. Here + // we keep track of the last found urlEntry and attach it to each file later. + u, err := url.Parse(content) + if err != nil { + return nil, fmt.Errorf("line %d: invalid URL: %v", lineNum, err) + } + lastURL = u + } + + default: + // It's a file hash entry. + fields := strings.Fields(line) + if len(fields) != 2 { + return nil, fmt.Errorf("line %d: invalid number of space-separated fields (%d)", lineNum, len(fields)) + } + csdb.hashes = append(csdb.hashes, hashEntry{fields[0], fields[1], lastURL}) + } + } + return csdb, nil +} + +// Files returns an iterator over all file names. +func (db *ChecksumDB) Files() iter.Seq[string] { + return func(yield func(string) bool) { + for _, e := range db.hashes { + if !yield(e.file) { + return + } + } + } +} + +// DownloadAndVerifyAll downloads all files and checks that they match the checksum given in +// the database. This task can be used to sanity-check new checksums. +func (db *ChecksumDB) DownloadAndVerifyAll() { + var tmp = os.TempDir() + for _, e := range db.hashes { + if e.url == nil { + fmt.Printf("Skipping verification of %s: no URL defined in checksum database", e.file) + continue + } + url := e.url.JoinPath(e.file).String() + dst := filepath.Join(tmp, e.file) + if err := db.DownloadFile(url, dst); err != nil { + fmt.Println("error:", err) + } + } +} + +// verifyHash checks that the file at 'path' has the expected hash. +func verifyHash(path, expectedHash string) error { + fd, err := os.Open(path) + if err != nil { + return err + } + defer fd.Close() + + h := sha256.New() + if _, err := io.Copy(h, bufio.NewReader(fd)); err != nil { + return err + } + fileHash := hex.EncodeToString(h.Sum(nil)) + if fileHash != expectedHash { + return fmt.Errorf("invalid file hash: %s %s", fileHash, filepath.Base(path)) + } + return nil +} + +// DownloadFileFromKnownURL downloads a file from the URL defined in the checksum database. +func (db *ChecksumDB) DownloadFileFromKnownURL(dstPath string) error { + base := filepath.Base(dstPath) + url, err := db.FindURL(base) + if err != nil { + return err + } + return db.DownloadFile(url, dstPath) +} + +// DownloadFile downloads a file and verifies its checksum. +func (db *ChecksumDB) DownloadFile(url, dstPath string) error { + basename := filepath.Base(dstPath) + hash := db.findHash(basename) + if hash == "" { + return fmt.Errorf("no known hash for file %q", basename) + } + // Shortcut if already downloaded. + if verifyHash(dstPath, hash) == nil { + fmt.Printf("%s is up-to-date\n", dstPath) + return nil + } + + fmt.Printf("%s is stale\n", dstPath) + fmt.Printf("downloading from %s\n", url) + resp, err := http.Get(url) + if err != nil { + return fmt.Errorf("download error: %v", err) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("download error: status %d", resp.StatusCode) + } + if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil { + return err + } + + // Download to a temporary file. + tmpfile := dstPath + ".tmp" + fd, err := os.OpenFile(tmpfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) + if err != nil { + return err + } + dst := newDownloadWriter(fd, resp.ContentLength) + _, err = io.Copy(dst, resp.Body) + dst.Close() + if err != nil { + os.Remove(tmpfile) + return err + } + if err := verifyHash(tmpfile, hash); err != nil { + os.Remove(tmpfile) + return err + } + // It's valid, rename to dstPath to complete the download. + return os.Rename(tmpfile, dstPath) +} + +// findHash returns the known hash of a file. +func (db *ChecksumDB) findHash(basename string) string { + for _, e := range db.hashes { + if e.file == basename { + return e.hash + } + } + return "" +} + +// FindVersion returns the current known version of a tool, if it is defined in the file. +func (db *ChecksumDB) FindVersion(tool string) (string, error) { + for _, e := range db.versions { + if e.name == tool { + return e.version, nil + } + } + return "", fmt.Errorf("tool version %q not defined in checksum database", tool) +} + +// FindURL gets the URL for a file. +func (db *ChecksumDB) FindURL(basename string) (string, error) { + for _, e := range db.hashes { + if e.file == basename { + if e.url == nil { + return "", fmt.Errorf("file %q has no URL defined", e.file) + } + return e.url.JoinPath(e.file).String(), nil + } + } + return "", fmt.Errorf("file %q does not exist in checksum database", basename) +} + +type downloadWriter struct { + file *os.File + dstBuf *bufio.Writer + size int64 + written int64 + lastpct int64 +} + +func newDownloadWriter(dst *os.File, size int64) *downloadWriter { + return &downloadWriter{ + file: dst, + dstBuf: bufio.NewWriter(dst), + size: size, + } +} + +func (w *downloadWriter) Write(buf []byte) (int, error) { + n, err := w.dstBuf.Write(buf) + + // Report progress. + w.written += int64(n) + pct := w.written * 10 / w.size * 10 + if pct != w.lastpct { + if w.lastpct != 0 { + fmt.Print("...") + } + fmt.Print(pct, "%") + w.lastpct = pct + } + return n, err +} + +func (w *downloadWriter) Close() error { + if w.lastpct > 0 { + fmt.Println() // Finish the progress line. + } + flushErr := w.dstBuf.Flush() + closeErr := w.file.Close() + if flushErr != nil { + return flushErr + } + return closeErr +} diff --git a/internal/era/eradl/checksums_mainnet.txt b/internal/era/eradl/checksums_mainnet.txt new file mode 100644 index 00000000000..76915fc03c1 --- /dev/null +++ b/internal/era/eradl/checksums_mainnet.txt @@ -0,0 +1,1897 @@ +9c3f42e0247d5503533f437ada2d44e7e9661170421c1b7844687c8dcfc0eb9b mainnet-00000-5ec1ffb8.era1 +8a8337dbb190b27dd547827db62aed42fe9ef0ab323b6895191d48bc7c6a7127 mainnet-00001-a5364e9a.era1 +7da7be2b4b2f6d8ab8543b709928b71d5f3dfac080f82a94b7d07ee7f2adef43 mainnet-00002-98cbd8a9.era1 +8ca41709b2306a978f00e608f601741011b543c9d8426493c8f4d3d0939a14cc mainnet-00003-d8b8a40b.era1 +b73ad13560e80b13457497a2267e060c02a5ec4ddd92c8d3fa81954b250f7aef mainnet-00004-6e3baba7.era1 +11207a3fc5c0b392f296dfd6450813e9e994e71fbfe3076a7e050016fc617ef0 mainnet-00005-5cff5a4b.era1 +d0281e7b848e69f53333f1cdff1841f101d846605fa82df6e849a3b2ddeb8354 mainnet-00006-678fb793.era1 +8f38a986ac5bde04fba3f9cb1ab2b6b4053368e9051a8a19592f8785feefa01d mainnet-00007-d9bc682b.era1 +0ce07c95e68c29c3b92bba2025df6304bbe021d010e94fd47c5a8d67b519e235 mainnet-00008-12c9605f.era1 +3b0bebbf6e316dc61ee8eb61d36abb5b09a954267ed3fa86be4e8ea91585bc49 mainnet-00009-f9e4e890.era1 +9409cebf51c92bbdd4069908d419c30798efb61d88632d45dfac9509622e39b2 mainnet-00010-5f5d4516.era1 +6914841419be9ed0e35c2dc733d43d199d6ed592ffddc7f07c6b471bc8941e75 mainnet-00011-30f04eb9.era1 +3efbba460c6164593325d8d66b09b537f3682d93cf5a6591b34f1dad09c995e9 mainnet-00012-5ecb9bf9.era1 +469791afa1fbbc7e7e60948fa151908269c16584f3ff5bd046d94efba48b85ce mainnet-00013-d0175c1e.era1 +ccf29e712848407724b8a9b3df855bf9e8c0f44989bfce045261cdab82322917 mainnet-00014-4f92d781.era1 +866c17d2d4d590502f48b8e35a6335b56a4c6bfce8a4df2d18b0e8c6950522eb mainnet-00015-a47cb8eb.era1 +f340e23bb565d6e42d9c5c345b654dd281d696556dcdf2de36803c12bde59e48 mainnet-00016-9344d8b7.era1 +399adffc4bc8e72883018475d05c4f1ded482c1940ac5fdd57fe285a3c09c1c7 mainnet-00017-43963724.era1 +0acaa325a1115fb6cce6a96a47c5e9aa082ae2447962f2c371329aaaf577edd9 mainnet-00018-efce27b4.era1 +ae332ca6f2b9c6725280dba003445c3b7f0e7dd49a6e2e9588787457ba621b14 mainnet-00019-f5434352.era1 +ccb29a81758de7da262c4b5d42c902989cb00f27df3515dae247b831d2ebbc3b mainnet-00020-0c405203.era1 +0aec5d0f99f567ca4b1ca370bf02142b5552ff4845d8ac3a502308f49facac04 mainnet-00021-20d8f1af.era1 +e7f0f1567c1ac9c600753f24df65372150bc3cb4773bd673a69db7b0a2fa2ec5 mainnet-00022-b694d895.era1 +361c0b9d23d7befd5d1924c166f0ec0b40072c57c9b02cb4f764a4815473df6e mainnet-00023-11beacba.era1 +eb484d168d8d60b5249c9ec8fd270049bfe55d0775fdd1d9176dff1e0c062211 mainnet-00024-f216a28a.era1 +dcf2c651a68085006ea26f8b06f9038c6c4b0b8dc69683aea4fd72fdd32155ee mainnet-00025-987cb620.era1 +dfdcc53b9a99aae72aaa023b0a633f0b3d88fe5ed2beb445ba24e21a60c2c448 mainnet-00026-3afd50ff.era1 +9a2004c5d9c4040bcc5009e04a3ebfacc764773aad2ad3e009c67ede76c916cc mainnet-00027-28083285.era1 +821a1978eb350a7529d4ff62e81c2b5fc87c65b93bf9bf9640503d4d3a4e7abe mainnet-00028-362fc97c.era1 +11537dbc309b94b2c4b18c2ed6b9d0aeb79cb7f2bf792ea10e766dd3f7986bda mainnet-00029-a0cb99e2.era1 +c86c2efb18098961533aa58ca13ee39af4d4204767efe5cc593285e33b011c45 mainnet-00030-78fc5e8e.era1 +f6947b30494defb10b72e014b0192dc1dfadfdbc6cf740c59525fd76c8447b3d mainnet-00031-52306cf9.era1 +0dcecd1909ba393e6fb0905707fc1922d776d891d4bdc978a3cd1f9eb1818703 mainnet-00032-cb4d0c3a.era1 +fd1194994e0b3ed22d7c1809a2bd5233fee92b9f01684bca2f134b0a1c27e47a mainnet-00033-0c3781bb.era1 +afcfdca6fb1e7691ce9fd5d8b3f09c5105da96882ea9ba1c5e188964d40243c8 mainnet-00034-fac9315a.era1 +e46841bca92299fba6aa326f000a4384537ba481667149ca0846118ae6436c21 mainnet-00035-737e0757.era1 +bd6913818cface9f9bcffb0869a5edb290b9f760fc9931f0ac67eb7fdbc4da4f mainnet-00036-84c7c1e7.era1 +a2b34158951ecde2c770d5d488e3d1264d40b9090b2fe7a77feb5b79d65fe7d8 mainnet-00037-34d06765.era1 +98b8f1dab9c615c1b98f7b39f48da29c1352b4df8bba36d03f306b11899b1edd mainnet-00038-38aaf94c.era1 +8dff95e9cbd3df32923de8ba6c30efd467c18c4eb84c133974ae06d8890356e7 mainnet-00039-4ad4940c.era1 +20e59943d0d807fadf05f755be3f11c296dff160e72e714de7c09a5d1cb24c1d mainnet-00040-4707f60d.era1 +8d2826f8cb0da37875b245898dac35a69d6cca7c59763058f4a3efb02943791b mainnet-00041-a6a87a9e.era1 +95480ed8ca2d8669f0c359fbc4095b3e1181b7335f2232d4e19b3be21241d6c9 mainnet-00042-5c8dca3c.era1 +6b7450b614e063acc97aaf35ed92a8a89f14cb2d1abec4489be2106122dfb89f mainnet-00043-cb513d91.era1 +5f97400722b684df94affe98e34822a63c4b1469482be6f013810ce1fee9778a mainnet-00044-1c72a390.era1 +077c7a17334e560638d42a8eb3b985312f20a2130a344567af98798c498b27b5 mainnet-00045-a87afdc1.era1 +e7acc9e2f16b438ec8d0f4f897e597557613793c3184bab75456aac00adf401d mainnet-00046-22b3f78d.era1 +41731bb360547c1f7a0f51058f0597f8ed47a80daadcc87dc1b7fe3e51529934 mainnet-00047-92d84372.era1 +9cd84bd46d099a9c752f7f9eea3b40cbedd0429f60427b393467b9670b1a31d0 mainnet-00048-78ae53ed.era1 +97f459559429fd0fe8e2a53c069e419c48168ecfc3ca28defc2df4b3008e3735 mainnet-00049-3934e960.era1 +cf85c4454b9c01575a6e32c1b8892777fda66dce0c88276a0b6b6d57d5e953e1 mainnet-00050-71698ebf.era1 +8d868be1bfd9be49bbb4ee934ba6fcd4061d36ce288f52c2841868e98f8e7299 mainnet-00051-2c1c7778.era1 +1f2580df1e484cf4c7de668f36db08c57d5b59ac68757bdebec689a9aa3ff314 mainnet-00052-3a047d9a.era1 +09c39d3ed18b7222ebae1dd5744b2c197a0b8bfb6fecc2f885dddd44d3568c51 mainnet-00053-161ee1b5.era1 +3b6eb0744531dc25bd9423793f119aff7a4e9abb156e44a7959f761f6cd0b52b mainnet-00054-14074ce7.era1 +5e0f959ec3cf8437bef4a9143871350a44028597626d8089342dbc17189c2d2f mainnet-00055-ae639ad8.era1 +752eb0640af043828bcf854b85d05d158e7ca3687c87c1fbbe11545f5da0f47f mainnet-00056-d92c394c.era1 +1af48939e98749aee96a9fbf91b0f33df71f885e36b04bff3b188510cddb880d mainnet-00057-4bbe776e.era1 +b3a272aca377aa55f80c1dbffb612663ab396edc96515aea426650a04b9829d7 mainnet-00058-9ac60ed8.era1 +d9060a7daa173b9fc2e15cb6aff5c09f18ddf817d7ed85e1430de23a997858fe mainnet-00059-246c1b33.era1 +38b4cf15925460bfb4f26a6615ecf44eb8196178e43906c051531a34c18503ff mainnet-00060-4533d0c5.era1 +ee2f48f124bdee13de49f655e939bea610d8567ab1ed4db955e7bd0e64abaa37 mainnet-00061-122db1c2.era1 +ef4649a54302ab73f8d0db0a0f02d7adfb329da1e4b1149a136532411fbb2fc1 mainnet-00062-65505079.era1 +240f3248e2cdd82ab945709755536f245c915c0006cdad5a1ee0ccec7ea34f25 mainnet-00063-39b21911.era1 +36635126cfe521a3f3d1b3ac845ae3ad9fb492df8698a542c9d308d70f4a90ae mainnet-00064-db26a83c.era1 +acbd243e23502b01ff099d707d94b9d0056e7269265c63d19fb927f875be303b mainnet-00065-1df3a40f.era1 +d11eb990957f6c356a90ad5f2ca7bc476b4e623af20acdacbd0957f9e0ebf23e mainnet-00066-07bde22d.era1 +f66d3833b3724f6c15822ab00c60424e301f523693034c637898187daff42f76 mainnet-00067-751bac83.era1 +dc9359481ac2399643c1cfb337016ab1dd14acbe8e69138f29147eded6088a4e mainnet-00068-546a10c1.era1 +842bd952c402efeffdbeaeb592addd80dbac69e409be25f2f0de673beb574c3a mainnet-00069-55c236ed.era1 +69358acd86be8eb1bc8fc5f8d2d09a8ba95bfd44240b63999b342cab63b54bc9 mainnet-00070-0ac3ebb1.era1 +661cc154b17b16fe01c99bb533bc5480c548fdf5d739504ec1470615ec2577ce mainnet-00071-2fb03713.era1 +9c9377c654f4e8c88da31b31247e04e179b06915594b6d7cb24d14aa943885ed mainnet-00072-2f9a4a75.era1 +ee3a628de9e60f3183c61261e42e1723430d42a06b07ef8101a8726b1980ddf9 mainnet-00073-32871a43.era1 +e3bd37549c917ab6d82b6300be91eaed3d11ed8b0dbd0be9be260b4b48f23392 mainnet-00074-8aacdeee.era1 +44db8bddfff6cd79e3566e77bd383995218c7587f43c76263a315918a69e1d02 mainnet-00075-368ce2b1.era1 +03c6d37a2b6992dec4e0a028e51c1ab000ec97dd9899e857d63660d61b3f3f8c mainnet-00076-290a4131.era1 +9c696fe696282bcb09dcf0960b50e40c3438250ef074d1240d62a99864e61fe5 mainnet-00077-5d736b0a.era1 +8bbfe812605d612ad6c318836b8dc18046c6259db4324c7b0df1dd21ffb0044a mainnet-00078-98ac3e9d.era1 +97d6735bd7e1d332392be25adee20abae497cfd2b73be7dbd54c5536db076ca6 mainnet-00079-95e0734e.era1 +942b46166b52acf202c4220815b7076daec2c9a6f90921004dd09052dcbaf204 mainnet-00080-d778ae86.era1 +ecfd5c75eb61dd7612edceec5a7b54294484bc956596fd2154ad6005927b3874 mainnet-00081-2089ffc8.era1 +687012f0accdc5525d2522bd818d8780c5f13d845a6732de746b621b8398212f mainnet-00082-382ac3bc.era1 +3ff51981d8da4d2635d17324614b390628fbaecb300bfaf86f1b3d819c9964aa mainnet-00083-5ab1cca2.era1 +2e6094b9c31b6aeca3c4eddee9da7dd939398acb1259ed27bd55a042b2d30f2a mainnet-00084-9ec0497d.era1 +d3834c0f0ececed210279e0ba14e678ecfb0763504b7fc7aaf6652d059de6267 mainnet-00085-f847bef9.era1 +e378cea66dd39c845c13a33a5afe57d527a8c4fea40d09388fd99df831b28287 mainnet-00086-0dd5eb92.era1 +b5424d22adfc070d70b983d6a59bc208d3b69b84d5260672650bc6fca19be18e mainnet-00087-c0612d68.era1 +efe3c59bae8e561bcd27eb34fd5158fc447cbbccf030966b342c3e905ff6d0b9 mainnet-00088-3f433e63.era1 +bce0705620a992e73525cfcbadbbc637e9d5a6df0f4d1c11d6ad5c6600512ab6 mainnet-00089-0432ee13.era1 +d4e01aaa61e640bb49b8d0a7a54618fccec213a02e10e606495f8a59178dd338 mainnet-00090-cf23b0c8.era1 +86975356bf8e070f6a9e72d74a5bbb238ca01e91387646ce7d8f9fdb8239dbf7 mainnet-00091-9fbb0197.era1 +fe5bfb77adb766ad96c270897d1998a41b32ea747aeec107aeaf7963d08b2d0f mainnet-00092-f88ab15f.era1 +2bbd7abb11077c01143c952f183043ca7e791d0cf19beb848acccb02ffdffb78 mainnet-00093-b7499b8c.era1 +cf6d59ff853c33848c6d944a3e409d12b5750d4e675a21f13fa909e8ff322baf mainnet-00094-5c45bbb1.era1 +f30e125dbbed27f0595800330c2fe1ec396baf86439d0e9a9056ced6bbe9a989 mainnet-00095-a3dea11d.era1 +8ed14ae5ea4801411e4969ef98cc7281bbb96ce0d600227251b5e2262d04b334 mainnet-00096-91ff33f7.era1 +ec0f5896e06ee4dd845c5c8f4afd21f089e213270693c619b7f7418f396d62c6 mainnet-00097-8e4ffd2b.era1 +0d5c0acc86d6ac250db521e4e549d375a04eb3782407547eb1998d1a1a14941b mainnet-00098-4c5709af.era1 +1874a696a018e92d1231a988349899ab713c5fb363ff66d809e639819b951651 mainnet-00099-d55b92c1.era1 +7975244ba01f2c47fc484b54c379a88a2bb000d63142209bf42b89cec7cce1fc mainnet-00100-bb39d00a.era1 +2cd5c2b365bc770ab681ae9fb239434dca9dc9e9313608d7248c2b789705749b mainnet-00101-e6df1942.era1 +473cca4df9b620773192be4e474ae7aadb2246b0236d80bf7d9cebd8987e8617 mainnet-00102-2aac9e21.era1 +b5b81df30c8e682b446eb08090701b978e9dd15e8a121854b01233f321fb85f1 mainnet-00103-3fe56c2a.era1 +28e6861bd1ce937c7808b5d98807b644459e9529a785279ab8288e4fe041e9bf mainnet-00104-f3269b30.era1 +71bb327b8b61e9a7146dd5c947cc24a3e1f05e6aa0fd444663fd22583bc1c29d mainnet-00105-6f2f86e8.era1 +db9b1e7caa3135a81aabf36d5bb18ebbb57ad7fba5e1b0933c77fe5bf840db8e mainnet-00106-621a2527.era1 +a5e6534639f5a61d2379c44ee0ca47067a8301e0ac66533825abb4b1b356766f mainnet-00107-c3e8f7cd.era1 +e13b2b0fa20c89fd295db7589860d05857364952acae4b0ae5205ca585f6234d mainnet-00108-be1a1635.era1 +b262e31da0fe5c9b4d93cd8d685ba2e93c9677744eac361907cf6ce9645c52d8 mainnet-00109-49424c3e.era1 +380595c5f65a25f8d807fb325d58a6f00b8b3126d9de7be76d83a6c03a4fe71e mainnet-00110-af29e604.era1 +33a817abf2e9ca55472f02a290c4fc943f022addb2ea06b5571488257e05c8d7 mainnet-00111-1738246f.era1 +51ac6063c5076cb5176fd5e53473b73cda93b5b04bc8df917a9fc47478fd2a33 mainnet-00112-0074a32e.era1 +124cae748ad7963bbcfab0425450bf4ba224b61537324083e4f75880cac8c5a2 mainnet-00113-56e19130.era1 +cb12db25f4efdfaab5d7eb589edf41a144572f53e7884ab8955c0d31e69ae8fc mainnet-00114-10c2a569.era1 +f15a30ef45c74b6739f224f74e8d6f991cdeb692053a12e2882089beeac0f884 mainnet-00115-26efb6b7.era1 +6dc15f7f578b5c2ad9ae38952113cb236e2d56ee1c5efeab93ea00ddb4a869f9 mainnet-00116-bdf3bc6e.era1 +fcf289c4b3126aeb3187167aa2210f8feb1daf018c623955524edc2164f939cf mainnet-00117-e68e5334.era1 +10907ad560d2f6355aafff4b32cbf197971126c8e0aa0e96be7c8c97b241aace mainnet-00118-1c9a2437.era1 +f335c97001169b8d2068fc70067306bfe92740eb101f15b6700c55eb09ef2304 mainnet-00119-5fc6b689.era1 +37e6754278740f158e8a5220e649000c499cbe5ad579900e598bfe0eeca1785d mainnet-00120-cfefc65f.era1 +fc7d276a531ad1ab2cdd14909204a97db745345fe1dc66801304d9d6263b76df mainnet-00121-0e29b6cd.era1 +e003880ddbe6c14ec8993a141b7d03bd7a94c535f16bdcbac2dfd221d20c17ad mainnet-00122-cddbda3f.era1 +b2c7bc85c76937b4d5e8f03553ff02ca12d76e8631346bda57042bf2652c217f mainnet-00123-7717d395.era1 +020aed308ebec19fb1e688f4708c177ab5c36ea1795db2a8ba77afca97e1fe3e mainnet-00124-2e66a66a.era1 +4d4c12d854a2f5e364f5636fa5dcf69e8b0aad416cb4f61e8fb63bfbac076e7f mainnet-00125-82fdaab1.era1 +8fc7016bd372baa22614741a4d6fe2417e9b807931597c67e256e8586ffed7cb mainnet-00126-427637ff.era1 +9133009a71e9a524744106fda96e8a092bbe8488a52bbeb108ead8dd45f621f5 mainnet-00127-65e302f1.era1 +2d1e305a6693b93a47f8d014cf0ecc78a1eb9246aa4a22af689d75b7f855e9fc mainnet-00128-6fdc1fed.era1 +7b283541ad76e10929e2a2396174a776707234ec70edbdfc1a5129764c6615bb mainnet-00129-c8705b6e.era1 +5a96e61a62fc166db0e838b619f31b21edbd5c097726fb72659a2dc0a05e60a3 mainnet-00130-650f1d51.era1 +7a60d78f969174b20c7f00907965f9237fefd44bb8167c7d42a6b8647b67239c mainnet-00131-7b4435ca.era1 +cf3b648a50a3ecb1514819701a6ddd345f456d58fecb449d60d4c8439bf5b9ee mainnet-00132-23bd7603.era1 +2e2bcebed27256595cacb454f47274301d71e25086e992e09b740b5b7fff7bf2 mainnet-00133-c0faccfc.era1 +7be5c210f2dbf15457ca97b836c252ed4113ce2f8b794fcb1857ffa5b6475d86 mainnet-00134-ec9cf3ec.era1 +1379ae2244ddcb1a026bb9b04f26abc1687a79d82a71d8c196da1015b63f904b mainnet-00135-66f5486b.era1 +c072dcc6a7c5b27929921034fc0cb96d609e528f614efb97daf59d9b848cb322 mainnet-00136-dc4ebb94.era1 +a8570851f92ffd33cb8c44b4aea130a0a24cc8d2418c9cb32cfb05d0c7865ee3 mainnet-00137-02298200.era1 +50faed8730fcf592ffbe1ffe8bbdc1b3c8643704bd419a8c9bc33c4edfef9a63 mainnet-00138-3d231400.era1 +939ac8db4dc0b0c27635e986257a2fbfb8ca8aa6a45f2aa22ebb39591a57a3fa mainnet-00139-5f316611.era1 +7e84c2ad1da194e20c4533be1c297a2dd442cf829aad1c99bd9cfdf8b265f576 mainnet-00140-f13c9fc0.era1 +6830c25223962969088b4d55b1cc06a879dec452695d582e45b4bf74387e141a mainnet-00141-17dbb0e3.era1 +3665c541116f9563aec1cb9454a4c61535b681717d34ecf9c8de70d53053a38d mainnet-00142-e42539c8.era1 +64cd17cc860e99b3efa89afa7943ccd894227ff2dc31687936e64b86c4f88f7a mainnet-00143-5594a813.era1 +4eca366cc00e4ff4d5cb240b91fa84c54fe5a82632b6cfe66c776139bc2f0e0e mainnet-00144-5ac2296f.era1 +e30191fbb8fee15c6b844a4b806271330259c7c8ebafa9125690c17b400a53f3 mainnet-00145-9244d418.era1 +84fcc41d9252fabebab8254ce6c518a93ee3b50188adcb6b8974c9607323d4c3 mainnet-00146-af9e7a53.era1 +e0dbea3796018c40c3a2297d804d1944b32284eb8050f2c16004db94ef0c9c43 mainnet-00147-1b6fa332.era1 +1f1d4e7a4f95bc6ea9b5f606851fab7ce9d688012212c18ff4042760bfdf10c2 mainnet-00148-39f0b057.era1 +0742f38779b0fdba8f687d428e4a1ec19ade71c079259c29606ebc6afb858947 mainnet-00149-586c4a91.era1 +56157f687d849c562eb1e704e7d2265e774a5701a704b6cacf3de140d15f96e3 mainnet-00150-3ff89a4a.era1 +18d55523deb34917db5212e0b025a9249e416b8a6120841e87507e0eed3f6435 mainnet-00151-e322efe1.era1 +e120aa74fda9cb3673811d00e7c5fdb64155845f9979171a3e4261b3769c84f7 mainnet-00152-4d9d0d1c.era1 +d1d327c715f83afcb574518c842c93f7781a3e18192d8c0a052868c743540f4a mainnet-00153-6b5702b3.era1 +38bf622cb23f04c237b3b780cf5375ec519a06e2270648a9d61da3af009b2567 mainnet-00154-13608c12.era1 +d00300907afe184f2df7c6176478db881a1ef20d8fe4f7cb976f63c83edb28c9 mainnet-00155-d0bf6ce2.era1 +776f7f6d327c12164ceba5dfd7900edc73cc3610c42d162d6cab06d960bc44d4 mainnet-00156-b8c3629c.era1 +d423c18e2ccc90db5684073b77f4b6530b17b4d0568d2d4e2aea6e60f7b5315a mainnet-00157-97351a9d.era1 +660cda8c979fa567c1be2535a64fe1f1d2b2ef221ebba4a2c8fc697f9a1707ac mainnet-00158-5cee8f8c.era1 +e9db5574fd2a08826615813364d816b77988b601afcd3634bc9cd542dee2fad9 mainnet-00159-b92f84e9.era1 +d460f1142d881241a5ac03e07e55611bed45b979a85a84271448b39bdcfbd797 mainnet-00160-3e41d9f5.era1 +e198428126e8432e99a3821079a701ab8400b64919f6d6c936bc404e1846c198 mainnet-00161-0736b99a.era1 +5566e30ef4b51515d26b9e3cc6d7167f5f3ed4fab0b463ffeb8e49b2c859e15e mainnet-00162-0a1530da.era1 +90d71da92883bfdb64166a3b06b2e6afc1afa93241a63b3b19205e806fd11217 mainnet-00163-43212d3b.era1 +6dd77d7250267af09559b1bc759d48225604a3eb5dec2f0ce4a183e42c0ee8ef mainnet-00164-3744a01c.era1 +a976c82d9667b66d86b3963537270ecb412dceb859d34e6334977c7411752e81 mainnet-00165-3d14fc84.era1 +8b42ae6ddcdeced9a4a12fdb321ef07ec07cced26724b21e5b5fbc7be50e0393 mainnet-00166-97c25925.era1 +7d64711e3c5fcaf098bb3110ed93487038cce4cd4ec002d90461fc6cee995f77 mainnet-00167-11485002.era1 +c76e19eeadcd672c5896d98d55c5599bf53bce667d09395ba3b013551581d0df mainnet-00168-88a365fa.era1 +76c17d3eb82334c5d208d0c6a995a0776f71009bf9797ed5c3c73e7052077fb1 mainnet-00169-f18242e7.era1 +61b9bce113cb3d426ec6891927430859f97f8efd994b45399f40235bad94dac7 mainnet-00170-f03ea8ee.era1 +4a0d46d3efcdcc98c4606f371017b8dbd9ed0d418e70d80bcbbd926353f3a5d4 mainnet-00171-bec8ccd9.era1 +7c906d4fd5bd1df403a3a340f52df6b9a31d56ba098105217f69154a7fe106fc mainnet-00172-34a4c5d2.era1 +2a7d767dd8f37b8de9b52d6593a4e322e7993bbb18f383a8ff1fff0214bb77db mainnet-00173-e1ae6c81.era1 +806eec85a2df1d7253dc9282a1d430d3f2a898e8fae74cfdf25872c8b57d4718 mainnet-00174-727d10b8.era1 +554840bd4982cf792b3439227c38492f601b08b40e1175c347ffdb52e1894865 mainnet-00175-1bf21dcc.era1 +ca8d89eec998fa58a128a64770137273ed7d11e7d47454f83577aad42b9b57b7 mainnet-00176-ecc872aa.era1 +5034d99d013c2b173c1c98794b5e8519657d6643ac23a0484763bce3a9aae210 mainnet-00177-966bdbf2.era1 +5100933799b61590847d3279f3e8d52ae00c6a78a5bb0f701ee9d582d270a01b mainnet-00178-b412f22c.era1 +8a28818dac4aaf5f8bb8d7edafcfa2b9672a06c36206a3110737cf53bfce4fcf mainnet-00179-09e40d3b.era1 +bfd6db13cda398fe7c80b41cbd770931dae78a0292d2b06ef572bb89d0718615 mainnet-00180-8ce22357.era1 +2e207838904ce105993d9528173ee48771ffcade3c28f497c3eeeac772af6f58 mainnet-00181-ac1cf418.era1 +66a64cfc1220caef2f49667cfb6a4c7b408e470da598caf87c50abb2217d133f mainnet-00182-f770e12a.era1 +2b1235b5dc35872f2178caa60a2bd4bdc72913509955d79796a455f8a22e6ddc mainnet-00183-798224ae.era1 +7ce2dfe40051314987fc3b1204da6d9e721b92c2f9df0a12facfcec00435f6e8 mainnet-00184-876fdbe5.era1 +4647b15e4799dfc87cdf931cc5025c9ff92b43cdf57e25f9b36c61ed1688981b mainnet-00185-7b447a76.era1 +5040f826c205e038873b66deb1fb562b60a9c8cd6d7d264697663fab3d70fb70 mainnet-00186-f1e11ac5.era1 +3c1a5e911eb86bd1813363194cf963c9b7d67c6ceeb15cf86ed2d7172eac5ef2 mainnet-00187-ce8b009f.era1 +cd03d04eb762ec7a86ff476d54985a5b47561d1cfbbae14c6bbf8462ce79a528 mainnet-00188-c7bd816d.era1 +e302ab30c052d1daf7783a021af19c1d394049ba4eed3e3bc33c559dcaa4b622 mainnet-00189-144cc97d.era1 +d2dff17507620e705c50040cb45507d9fe83f488945dc10e9e0d4f508647e71a mainnet-00190-cbbcca51.era1 +626db8c8972a26c290b5fa6441245db68f2eb6de7e41b90d6ab214ef44909359 mainnet-00191-3a7f6484.era1 +3ca721781718361e94fa2d011b37cb89d8477348d4ab7d99ffb32af79093d7c6 mainnet-00192-8a4639c3.era1 +8350e06f7bc0831e6ac2184cbdb8603177fb3184b092f4320e470d521579a78d mainnet-00193-52629553.era1 +395802fc385c14d30e2a3da26eda066ce93e64228a3564c85aa309fd0c1f9727 mainnet-00194-c4828a4b.era1 +32b1ce49f1ec65ec06bc5dacf8e5a3ae3df4ac6ab46a0e0b74a79440f652e0af mainnet-00195-f50a165e.era1 +0ebe874ce10e7d9459b55f9e9743f72634b89b58edf4171389299599654d348e mainnet-00196-5c31919b.era1 +0a1eed2cf5a71cbfd0c969bf11834cf637dd977ea81198f046f7762c7227ee46 mainnet-00197-9e2a709b.era1 +14b10d6bec29dc751efc999b3663f546e6c05d46b48ad666786ee03e81706fb9 mainnet-00198-c3e781bf.era1 +f93ff7cfcb2a47f5e3798db2f74446635342b92b21594f1c91bd800346ca69d5 mainnet-00199-20c05ee3.era1 +7fdad50e2fcc986cbe27fd5aedef822fe1c5bc43d8bb0a733d86915172c21746 mainnet-00200-dc265ad9.era1 +0c1a1efcae99cf65a0533ce10085cddbd081ae80c9ad6eceb3e16301d23a5810 mainnet-00201-1356f2da.era1 +e737494f1c4188a3aff61ea510d3791bc14c5ceb9ec2bc81bcaf560f86de3b48 mainnet-00202-84e86114.era1 +b7e1d6cb3fa829ab59879425bd0801dcd5134ef4d45419a62623e5b74db8809b mainnet-00203-f2da7b50.era1 +b05ff70490032070dd3d02ec0170b9e16881f25a466909a738a8f58f6c3ae51c mainnet-00204-7f42b43f.era1 +bf5046c68782b52fec94aafc863a592bf9a7c44c347ab0f5c78fef14b61d91fa mainnet-00205-afd08f8e.era1 +0f8ea93ef10ceac74ea516129006b69651a2abbcb129366d947fc49dca8523e9 mainnet-00206-cf4a2c65.era1 +dda172ad37c9db5e43fbeaaca0dec05d3673c55ed4c5f9b593d8c4862f7f1027 mainnet-00207-59a0e883.era1 +e157c8d46379205b6bec2f49189ebc1f9bbe0e2b361c1068da3ce32d2eaab797 mainnet-00208-3d0bbaf2.era1 +22ee433ea64f0e857171e10c904f0d190c6d9c43d037e5313bd49ea0cd1d05b2 mainnet-00209-c1041285.era1 +26c3bc51f0ad7320b02448db82705201257274f129f899b4b15e2c4238dbc2c9 mainnet-00210-1eb673ab.era1 +467213b7bb251a7b51236a357cf019ca1691c86d65116ada6b2783867bd2e9d9 mainnet-00211-01e5a4d0.era1 +431217d3ab4dc0c5566e3e98df2a38a4b34f05934757090b72362bc3035909da mainnet-00212-83c3bb0a.era1 +66346ececfbf29fc572d85d6019f0e7eb76cbfd8d126d2a5f7aea7b2547147bd mainnet-00213-9a09fe68.era1 +e060bc6dda403f94b5029601d9e698ff1e73c6f8b9978f235f02dcaf3ab722da mainnet-00214-9e78dc12.era1 +319c9a5a2bfa41e06885002611873b156828815c7d2761270e7f072271cea80c mainnet-00215-b02bf96a.era1 +746442aea25eaa0f243fa13bf7bdb37b4b3e294e65a0c36310c17765e5a9ec74 mainnet-00216-209c8b32.era1 +399c317a2f40ae90905d2809e9e5eb4dc0ced9f19cf2fbfb3fa5e2d780e525ba mainnet-00217-6c84f49a.era1 +c05d0562e2c800f77a14dd29201acdc17ccdb06c87b88f5027135f648270ff47 mainnet-00218-acb60f14.era1 +fe740d7ec3f43c228bd39d4bb8bae8526c7cdc747062bab8954adb6da628a1ed mainnet-00219-0a773645.era1 +5409e1bdf10518d1cb5d9f58e70d07bc5313f969f7dde1e617cc0af49a2ca834 mainnet-00220-76ed2324.era1 +2880b0f5c23c49e79e498751975137e2a9e5ce89044560636ef16060e241ea6f mainnet-00221-76ddd2d8.era1 +5f03fc4b387d079e32f314b4a352212376fc6551dad8cf210e40a36486130b65 mainnet-00222-71e986b2.era1 +1de0c6bb2abc98fd83fe626070d64a7759bd1561196587ef462d357751f4dffc mainnet-00223-ca2b5c28.era1 +4fe9e7d3fbdac24e845b4945c9b0ed046325c67b325cbdb4a5222ecb9f71de96 mainnet-00224-ff571e13.era1 +69fccefa8e7b4cb53e1aeee876a2bf6f644a685a18a2bdc5ccd1f807769a9cb3 mainnet-00225-1cfe3239.era1 +1d551889715dff2276ef1cb3d28b94ab84debf4577fd14509fdd433d20016413 mainnet-00226-b0eb8f2f.era1 +4d9f29c9b439494a0f377979c76d6f47377171f3de0e503d1732b12e31a188fe mainnet-00227-83556e9f.era1 +70d7125b225f523943b79616af7cfe6edf5cd20621f321e7ca894d73a2cc77f8 mainnet-00228-7651e7fb.era1 +be32f18cfe34a4e6187f87fcd4e6e7baa10778971049f8a34f40c77407d88588 mainnet-00229-238ed788.era1 +d901af617491c36bc121e25b0734a3c7a37496e6a3bcd03d8d09cf717eb57e6e mainnet-00230-3826affa.era1 +4ef61acb4b95445cc7a8ab5105aed016fb35f6ba9a2154d382eed203dba33279 mainnet-00231-b81f93c6.era1 +d506f9275e552aab49cde4e09ad5499bea5d8da6fe6483c83af627612f6218a1 mainnet-00232-0cda2a75.era1 +8c0386c53e59d8363e4e73d68c1a9fe124c61eea8c72486716600649d59927e6 mainnet-00233-b183167b.era1 +1e4702b597a06209d7821ead4405160cde6ad37602df2f68eef9167f6a5ecdbd mainnet-00234-4a88300d.era1 +34acd755492f89d62ed5ebe484aafac11f8577e7d10f71ee114f46cb84bf21f3 mainnet-00235-05ef6143.era1 +fa336dadad7c7c12a720641ee21dcf712fe6faae02819944772084a74bad29b1 mainnet-00236-4164fdf4.era1 +293b613f1b39810a965f334d4b68cb5930758117659ad5e8ebde82bc8cf05887 mainnet-00237-d5c9eed4.era1 +725bb2261ff58b71cfffc9863e1871f74c07249fe542515560f4e5cb58a35b7e mainnet-00238-db0d90c5.era1 +4db568aeec1e45cd5bf9dba0c284a40b303a50d1d6fce44f44f41491dd3e56b2 mainnet-00239-a0c972ed.era1 +c1f65805f6bdbc48c3059859e4d32796778cd4184f451f035baea8e216f6b597 mainnet-00240-9cc1a86a.era1 +e24ea67de4ff052b3dec75ce1f868e53a40187949411f3995cc2ab167fe51347 mainnet-00241-8ab5ad43.era1 +5c3f0b5e946c42fe19348ec73bfe7fd4c9de5030f02539b235aa37056bcb0a93 mainnet-00242-491f232e.era1 +0bfe4e75b28f738ba47f5d3ca868b44f57d66173bad19b375eebe9cd93816d7a mainnet-00243-f66eb348.era1 +536be6c4e4b85b18244b4ae7831faccd98918e60f8cf371846f5278f5adfddcc mainnet-00244-7b141f13.era1 +395e73f999f92c6934129b14b10093fbb556d30eebf794f858d55cd4bf72a821 mainnet-00245-ef5b96a5.era1 +ec7ed2022fed7a83624a665612a4ad0bb69d8a22ac9b851a6f198b42c495ca5c mainnet-00246-32d63aa1.era1 +dc5a26bca0d024416229caf8f3e21afdeae726e117fa858e23c6db533b28f583 mainnet-00247-7b033a6c.era1 +f325b28a7f1015865b5bbf20ba2db40c13109af7c1a96faab8c7e91591a57703 mainnet-00248-50916052.era1 +157805c7380f776514b786262f29295a798cfda5669d2feaeeb7c99d36175763 mainnet-00249-75e0db34.era1 +a9bcada73a1902235625eb43b828c533648ae862999711691f8c6b67b08bbd14 mainnet-00250-4781ac7f.era1 +657b73cdcb86e43603efbc2625b449c526c501fcad055b5072e845d96d6f4286 mainnet-00251-c0f8b33b.era1 +18fdff48e3ff474f39a595eff14317d5bda72dfe6e113f880ee5444719e7168e mainnet-00252-afa72ca2.era1 +cccf5306905b1e2141db3d5dbdf994e063e9d931e607bbc2f0193f232e076461 mainnet-00253-96c6ccb3.era1 +06a86243f76c00c63bc9296cf01cc35dc6e6c798a34c76e5f1d8c95e814e5090 mainnet-00254-7b32050e.era1 +5ffcfc2b084ab71d603f8f07ef9ac9b48945e310b46009378f97859a7f14aa17 mainnet-00255-16317cf9.era1 +5e8b0f5d502a41b4a7220cd2f8b9257446799e56a20c3654dc3cf67e9e528839 mainnet-00256-b4ccb50b.era1 +5f4aab770a331de70119302d26cdbb6b1bdcb593738ee436b00c22cf57df740b mainnet-00257-98808d16.era1 +0738e28ea91aec4c979ba9ba8b5381513c3cc01a01306c4a33410c55a4cbdb0f mainnet-00258-3cf61b1c.era1 +fa877be3381780ef3b4defe1e4a98c89089cc78b4c4910fc574e2b7708b86699 mainnet-00259-051764d2.era1 +09aff4f954451ba6e19705e27fe2af7a9d2108310184a5ff60d83b05b35b9351 mainnet-00260-3b0545e8.era1 +258032efc35138e75bfd888ee5665d419e4ab1f89548da1cd0e27066bb95e693 mainnet-00261-a6f4bfca.era1 +eab73c427116a2759ac62acb104d127f2c4672f53118752caf08e4a540ae9ee5 mainnet-00262-1a75662f.era1 +4c16ead662a9731e092d0f1ce5be5df0e37b422d6c1844e6f853af2ec709a219 mainnet-00263-21bf7eee.era1 +0be7714626e9531630df7740b159be925eef22cd9a8243d745594d854ba49207 mainnet-00264-d41eb83d.era1 +ea30083769d810c1cfb254c52b8d691e810921bb61731d9372f093fcee0c50d0 mainnet-00265-e7a19561.era1 +934ab681e87604eafbc3535dea53b1a0d98480236d09fe022febebd53a67e407 mainnet-00266-4f172aab.era1 +5831b78b87a3cd549f3dcc891b40af7b88b983a5d50431d971ed25984a8f963a mainnet-00267-7c0e1bcf.era1 +2b562646da50134fba39460cbb0544bcf91b1784162a1a837749228fdc0ccbe6 mainnet-00268-c839e6d4.era1 +5878d26fdcd2c89bb5ffcabdc1fb73648a608aed48a644fc3bf093c8ed88d871 mainnet-00269-9ec52dfb.era1 +57cce61c6d0571b2fefe353bea1f7b07fd0cc6150f9c64418c54347fa3cf3842 mainnet-00270-4e8bc727.era1 +864afce33474b26425e22a753d92b4e903ef354c225d4c4b8e4368df5a456b44 mainnet-00271-5294c75d.era1 +e288f65b7037d78d48c7050c9796f285c215c17f5774624bf55334e2c609ee51 mainnet-00272-02a11db2.era1 +031dc92f73528d0245e3c71a145f04515bf797d63288c11c6e8138ea13505d80 mainnet-00273-d81a2c41.era1 +03a56b01a6de3a402af96dddf91a491f759237c1da99e42bd200bf43c356b525 mainnet-00274-9c4bd87d.era1 +74c043d4703bb1b795b7f7c3d5f280a30af2d35e9517071b045603f31bc4e6cc mainnet-00275-85b9c67d.era1 +809bbf2e67ec7872c95391dd90a4afe4bc769f0027927be382f3365e31620a5c mainnet-00276-38cc1236.era1 +677387a3f7f0bf084e41b0d7e1fabf82a56ddedf40b18edd5d9856e9000eb5ac mainnet-00277-40c70f95.era1 +0a838d463deec9e5c5b85e18d8725f183c2170540dd9f54e75d69b06c57ca23d mainnet-00278-d9e0d738.era1 +ef9c97d47691c727f714970dd4a9947d8143e09e5e25eb872360cc16e8ea9364 mainnet-00279-d20a7b7f.era1 +a622740738e04a5e673ed21192fa94aa81ac2e81dfd348edb8f36cbeec1edc58 mainnet-00280-54a85faa.era1 +6b98f2e6d1a839411148692305b86ab30240faa1343d1bb8f27bafa37cd9f634 mainnet-00281-f08b9749.era1 +6abbc1880218b1ba0c4684c4c0769c98b26545c41bebb0c4c388410fe7b26a2c mainnet-00282-a5912776.era1 +78dea83c37faa03f9d03bbb6b23101bd072dc512a8976a19b2f032c43e0a0d8a mainnet-00283-8ee7ec35.era1 +85407f624d764cedca0eb875a5d18f0cfa87aa060c36841c94537b02f3602932 mainnet-00284-374996f2.era1 +b88c6c1c9bccdb5b8f0d0fdb5d1a2a24b253f5f8e465106d1c6a8e6abc70684a mainnet-00285-94942585.era1 +8940a7e96a2c2c7a40a18f18a93118d9a5c41dc0cb7ad11f64392f344e028356 mainnet-00286-6d47a234.era1 +df3d4819985088b642837429800af088389b479843e1c99bc74e2bad652dd717 mainnet-00287-f9804151.era1 +cc8bfc4ee1ce288ef3c6579391179890995660ef78b36328b8c989f4da8d7ab8 mainnet-00288-3724a8c9.era1 +bbb4f0963dbf87bbe6c0ae49398ea9713bb8ebbc6103922bca89ee4c7251767e mainnet-00289-60d72bd9.era1 +4a9f3162be5b9e8e12186454dc4d64c34d17a78d8a7e51b9845bff538032e229 mainnet-00290-64cca80b.era1 +667cd9a0e715eb9476b4b0750bce1d729579e1040833c55dd45f172224ad336b mainnet-00291-0dfa92f2.era1 +fbbbaf9d092c289e437648ce030aef4d0bfc48bd01ac88cffdf4288a190f7c5d mainnet-00292-94905988.era1 +b4db0d6ffe70930e6c4a83fb787c3662fa7bf1782bd2775f791190c2eab9558a mainnet-00293-0d6c5812.era1 +ee58661e70728f41d969f9bbc2f3813d8524a8ae5814d2d6c9bc4098330c92a7 mainnet-00294-f6c5c94a.era1 +e9b226a29d2dbbfad666c14988ab99f1b70dde5e98366a667d5951188248fd58 mainnet-00295-4efa78d0.era1 +651d8832de7b0aeb119ce650914db9960bd5c18c49fe3db8d19e289787f749f8 mainnet-00296-81c1446a.era1 +1e65186f5a9d78ce2e9a87c49b734aac341c81d33ff638ccdd8cfb68e354d0ca mainnet-00297-08d13a31.era1 +803bb2f55f8c75b7916d0ca02b4e41c0a78a7f196036315ec940f554fc6112f7 mainnet-00298-3d1d6d89.era1 +5b88575c8949f5919df6f84f5240e4615ee3523063e149491e817005eb88fe99 mainnet-00299-23728d43.era1 +a69c3443a1142f5550e19f97c5b002fd6eed389fc3a788df1d598cd5d1985db9 mainnet-00300-de033253.era1 +6900304acacb549a91cd1a8134b20f996af9743da1efc554e40d6422a934f21a mainnet-00301-15a24df9.era1 +f6b8bb0f46957fa47e2dc210605e0631891f36e90061a1919607f01f6105efce mainnet-00302-ba653536.era1 +be87a729cae74d6e5493425f1363ca3ebad2d5b62f75f9e2be58638408ee7a82 mainnet-00303-35fde006.era1 +d5991b5894638d6062dacd21d764ff0c25989b2638544099d1af8c043f94da28 mainnet-00304-377bf395.era1 +f7b46b0af17d873ff5612a8eca4d7f933a08b39201a934a77724f86d86eaefa8 mainnet-00305-340a0b81.era1 +11c7324dc37122afb9409f009374c0b4eb62e6eaba1cc9d25d64ba523429152d mainnet-00306-848e3d92.era1 +e27c325de831974697c9ba15564c9df699cad55868a3536569e04bd37b71282b mainnet-00307-a3ff7916.era1 +ede1004132d35682082626bdcdcadf7df17182b94ff13923ecaa5a4f685dcbb0 mainnet-00308-48c7160c.era1 +6a338b7a5c69f50556da5495e9d26b74f7c3d77aa28200058f21d2b85827431a mainnet-00309-e7948131.era1 +890963652b631abb1981f98de7dec943f9e6dbb70973c71f01ccf5aa4382f2f0 mainnet-00310-db22eaaf.era1 +94c3bf7e8b977816736f06d6f8204a3fe1ed17ffd29d4d311be5859d9a7a8f2f mainnet-00311-f9e4fff9.era1 +53ea643472d4e5faff5137a793d106b285235a24d6e3cd6ff4bb53e72ab3afe2 mainnet-00312-d95903d0.era1 +72004650bd296128b8d3efefeecaac923a16b86989304110adebaffc3910aedb mainnet-00313-73f91876.era1 +281fb5fd464478f53069670afa53f68922d311bcbae6c660e738dd5755820fff mainnet-00314-8e339794.era1 +a607be2c022534077226b3a670db8d295344a1e57fcb831c89d2ba92a178893f mainnet-00315-e83123f0.era1 +e6df2e86fb6cc688529e4371ba198c1f250faead7f1b64b5afc885f5b8805d0c mainnet-00316-e49c7af7.era1 +87af67240d12f602175519be1ec4e39d5e238b5db8e4be17c8e5f939bbd02cf2 mainnet-00317-f3ae6a62.era1 +8c06c1bd3ffe05cb3b70a1a3884d3a9913eb9e0d80acce9ee2afe8b231ab8926 mainnet-00318-4bf5c84a.era1 +bed2444997d93ec16706cd80684c77ebfaf53b539280f2d2e3059100fafbdabb mainnet-00319-391837c4.era1 +db0346a2558c7181fd86d92dd4b55188c6b6e117fe3c422080a8a2bc0a850bfe mainnet-00320-bfd4677e.era1 +5ad52159859033d593d70524cebbd2c6781ba33276acbd710aed2caa93db2a4b mainnet-00321-7189f496.era1 +e0a05a81ce6f08de0398677ba3c57762e6cf03b68f0bb3a9c5738d2a1db8a8fe mainnet-00322-344663aa.era1 +f2a0ed15703130b139fc81bd88f561ff67b44c2a95e5af376fcbfd0b6673e1b9 mainnet-00323-e111bfc2.era1 +b75164f946d2a1aba85c9bb1fceb68740c19b3d86324c98ce93d2294a8653f09 mainnet-00324-be2b4b22.era1 +48b8e615992d0bb139cb3ee2776783032a2ed1a04e72acd85a785fd837d09c82 mainnet-00325-441fdc44.era1 +6b3575d54eb17c3b631e64d737b5e58787b2239e514eb84e15f8457cce72e13c mainnet-00326-42979360.era1 +f4dc8f39fd7f2172169c8e6f4766b4a09c7904db085fae98324b6329face3cb2 mainnet-00327-820afe74.era1 +abb852aac204091bdf380e6f20f1577d5307f0c9f70d3d885df4771e497d2201 mainnet-00328-08665862.era1 +d1396c5f8acaa5469f394522e8408ed3e7ae816a6ea8e7d68fa6ce60d9ce78f2 mainnet-00329-cc85d0d9.era1 +2be3f4490eaf2fbf8631b643bc34da2d2b978f2b24557e6e34e42096a1269c89 mainnet-00330-8ffabe96.era1 +b23f5f857c96c35e5d651131841eb91bd10d96d2c1d56328b81eefd51752201b mainnet-00331-57c9aa44.era1 +b441204d73f9484d12e5bb1b81d2068fe2a6e4564654e9f8827b258e0212b5dc mainnet-00332-3135b734.era1 +ce17b8b868abfb2b3caa4be81253c778fc6f89e672b0453dec2e68a930568edd mainnet-00333-ef368f00.era1 +579f357ea1096016184e8de024d8ac499d4317f027680f06d2ec4af955d5bd80 mainnet-00334-1c3457cf.era1 +972a808e6f0242894c9f2677e8c13fa78f3b9dc4b0402a1350f3fa22385b48f4 mainnet-00335-b191a95e.era1 +4b15db1107f822a04b34cb9fe6ad6fe5ffc7b203b11a4582d3edb8a905883883 mainnet-00336-b5318d5c.era1 +42f60efa2d09ef0c09429abc092a6f68ee43ed34f00c131f2793e61ec06ac0da mainnet-00337-a84d51fe.era1 +6e8ecac55c2621f7ec7f985c61b2ecd820b6500d3bc55db1b73d9d61c7011450 mainnet-00338-f0b5744f.era1 +669dd0598d4f0b1d845c56e186d3b6775e3a3a68616dc943f3542e6d6bda00fd mainnet-00339-5a637c4c.era1 +b7c2a9105a97e7c45c2d8f6832beebc7ca09d072ff606298072558eb14fab8c1 mainnet-00340-6bd16b95.era1 +510f6645c303a5bef35e3a42bb67561788f45537311938b6994e3ce21d2622f3 mainnet-00341-e0d9d5cb.era1 +3ecdadfdb461bbe51b78adc390fbe1a0a62af515fda9f673fda32efb436227f3 mainnet-00342-203bc599.era1 +3192d942c5fc52cb417fa533bce24cb75b2b3ee7642b0917d68c834aa8bd6df9 mainnet-00343-a668f92e.era1 +f30fa7c0be0490cfeb52a219e34190ea0c4794d63b7ffbf52a9b0e3a78d5cbc9 mainnet-00344-9cfd6013.era1 +75ebd51403dca99b2b4af773e9e38b705d137daea79cb8bbe4aebdc736d081c3 mainnet-00345-54595ee7.era1 +31d39ee38b82e05c35b3f98d973153e905130dc0746fba15e8db5880fce296c7 mainnet-00346-2297e35e.era1 +29d62be96827f03a2e6ce994be348df7b81fb6eea599bdae9e9d8b3322a2ed5b mainnet-00347-db8912b3.era1 +2a09d0ce3566e1ff1e4f1913ab021eacf7edf57244bc22ef9a3d485e02ae2c68 mainnet-00348-1dde5ab9.era1 +7e9d44abc0c85b06c138781dc9694f4b5c902def74f6f6b90d9bca4f902bdce1 mainnet-00349-7e25d7fe.era1 +7b286530125adcc21fbd3d08ad29b27d9005b51048d3be73a17d7606492fce11 mainnet-00350-2bce5715.era1 +d837111205b43025f5d6dc3524203fb901091491b5b1e3c998366b75ac5aa213 mainnet-00351-112b61e1.era1 +d9541f10b1a86c867ee32c39e38a9d7cc74d2865c6f111bcbed5e019dff93092 mainnet-00352-2ffd764d.era1 +49eb0817515d01c1913055ec9280ee5c1998fe5709da7687f9e26aa5c588d95a mainnet-00353-8d5ed81a.era1 +b2323b55726ec90ee2c8eb62fcf4c8a13504f9af80e6c2951b0df13f8b02526e mainnet-00354-78c57cc6.era1 +ee66a1433fb112d363c195f15ac63ac3c5115bfd1a4acdf942198068f0221067 mainnet-00355-55973475.era1 +6f4476ee25d30a33bad20eb1b46a3af49748c858033c14d0a315aca6387bbbc4 mainnet-00356-c7493a6e.era1 +7dcfcf636800c8396a9ebc28227c0b6891ddafe4635989c37a54bcb5b12f25f8 mainnet-00357-e5dd9171.era1 +35148d904c5dfa5d2a40ec89a7f7dc020f4fec1edd685272270a0fe9c4b31864 mainnet-00358-60ed8451.era1 +6c8f45131f9e0b7b0e7d74e9ee836590c484a52d1000e0882a7d83c4f3d2055b mainnet-00359-eeb9573d.era1 +37873b1e0fcb60f7b7de68b84cb08db41b32b61dc64163f39ccf5948ff08efcb mainnet-00360-277ba2fe.era1 +c66cf63b180e2569f2d6c72d780af8c01cb8b7d6948515435e095b2172daefdb mainnet-00361-fc97c47f.era1 +48f612db69c52e246491e750e5f94df47b80dba15592b26b318ed0e613178830 mainnet-00362-77f810e7.era1 +1b2179d93b57c2a17875b444a0c3d1fc4097ff084ba8e6bb937bc092e04d3edc mainnet-00363-056ef66f.era1 +7ac13fcbf596e3e8c23040778198077c0aebfa3f0d03c39a70ecc000218f62f4 mainnet-00364-374faafa.era1 +a4c07f6a1c9f6d8325e59ab32efa67242a7a57045bea8d55eb532493ecee9f8a mainnet-00365-a959566e.era1 +e9388ce5ba91752066804f0686d836887ea7b1103933e291668f70ce8c5de325 mainnet-00366-905c4a52.era1 +b475829a1bc10c1c74748cb87a5c0e2f839bd5e6b69f986ee2d05d6cc2bed562 mainnet-00367-ed4e7dab.era1 +1eddab130d93a1f6d4b87fe5d2bf03f19de10953c1ba49d23b2cd73ae3a5c362 mainnet-00368-b4a84335.era1 +e49f3b85041bc16ddf43279060c2e3c123ab2f441b309dc582f8b5ca3d46e89b mainnet-00369-1f3be833.era1 +a84eff31f8c984e5ffe0b0d0fd3c71f21e3337d8590778f72aed4cd00a337fac mainnet-00370-ccf6672e.era1 +5543f4f45161c97173eb33149431fb42cd60351d3a47cde9d9df3af0c55cff36 mainnet-00371-1aa762ac.era1 +9c4bd2e345c961a78171424a04fa289b2ba87cf9f96c46f388820cd970934dc0 mainnet-00372-05d7f23c.era1 +c469096ccbac56b9d2430d9c701bdefb3f3637bcf007cf769e89320454d04314 mainnet-00373-9a832205.era1 +5aebe7c74581d12fec8d08db79d8691c80b55642bdf2a20aecc68b1f9620dfc2 mainnet-00374-dab73b8e.era1 +61a2bd7564b57b1c09df53f21cc290935f5408fb17e9738b7c50748588b9ce50 mainnet-00375-e97d6f9f.era1 +f93bb38730e0585997daf5e0f200955d19d7ad8e377348bf82e9f33c38c93701 mainnet-00376-6af73957.era1 +086d1ab1ad0bae35f365a752c9b65edfb6de8aaf73f4f44e764c8b8d502eb07d mainnet-00377-fa0bd020.era1 +2fa64acb1e994584a32f066784e8a1372a4bda26c2a9bd666f0c170211e31d39 mainnet-00378-85b101eb.era1 +ee8d899c60918d323b752102b7e27087c3474d95378d1bf57a833717e7ab0c51 mainnet-00379-fbe01c0b.era1 +46cc8266511d62f79cc181e862b0c5c4924e81ff1b33caeb67d3434e20551941 mainnet-00380-3e90265a.era1 +1e98509dfb62e17c5f06fb0388a1eb44a586e7ba636567d2947652e7e5e0e53f mainnet-00381-0b316492.era1 +b973f4831df7a43b82d1006cf2be4826ec3160336b78a9b12ffe5cffff8f8e96 mainnet-00382-a02e585d.era1 +e68d474a4b46edaeb641c973d00bad2814c7e590e7171ed009015c6abebaf914 mainnet-00383-2198573b.era1 +17f9393733c93d004aaf59fcf832c09f013f47ffcae3aa7a81a2e19e923ecb01 mainnet-00384-0c367f63.era1 +2fc3cfcd0f1188247724e5c2e2e7379a7202651c59b919a421cf2928254dba15 mainnet-00385-8893c8da.era1 +c2017d7d6f0e564557eae95368462b2b9bdc4a7319f9377eb3ed17ad97c462f1 mainnet-00386-30637c5e.era1 +a5a18058a5fabc9ee99d16fb2a6e42de640e941233dbfacd19d3702d838c9ff4 mainnet-00387-069b5e28.era1 +5d898b95222612813fa0f52b81c0298a1c37f768cf4dc00a059f155ee8e7e0d0 mainnet-00388-82890633.era1 +c7645b3b2019799b9ff38355724030216fd0a48a398263edcd21ab7c73fbff78 mainnet-00389-def916eb.era1 +79ef309a1a41a7af16ca5a125a446a9628f47c9b4176afb50e3687c1ba2381de mainnet-00390-00f64677.era1 +ca8373c3fbb5e219405ec04cbbffa68879b4ded6dd07544af0532dca5a9e7535 mainnet-00391-60554823.era1 +c4c2d24fb97cd258c8184188a7fe997b9d349c62dfcf912aa389156c4933f64c mainnet-00392-01eb08ec.era1 +48fa1fb93ea20714a26c5fec36c2ee0f15d198c215ce0bda2d2562f62fbd174c mainnet-00393-a5b5b2ed.era1 +8fad6ac037743de46833ea43a6ffd100a00b2cb9f3ad0407d54d0b03b28ed6fb mainnet-00394-c96f2c65.era1 +f8371e073362e9d543b36782ab258cd42eb87e2d6c683155b4e04624d495a088 mainnet-00395-c7c79169.era1 +9402bb75c8892c815493febc8ae569d52b6cd2bfabc75401522534093a436ae6 mainnet-00396-98f01c73.era1 +bc11672da09ff239535c8880d1083d41f982d147b737c143c149af770024bb3e mainnet-00397-ece428f5.era1 +f68cc56919983c2fdec4b8c8854e539abaaad4e9d4f3849cd378e2d41939efb2 mainnet-00398-15d52476.era1 +4088ac92ce31b46020624856191212f6d1b2f0489cc5c99d95fee4f770fece7f mainnet-00399-07f40278.era1 +24ebb117e879bdbe930c41e479cdac2ad4152ecaf2bc788358d53c6d6cfe044c mainnet-00400-837a78da.era1 +2d4314ad1f2ea766461a5bf829ba5bbac2d190847febc7b89a44aa3aa0e70ba7 mainnet-00401-f319bb85.era1 +34d577cae57e3e8fb2000127d0fc9c814248e6ca2af94d9ade2546c85ec311dc mainnet-00402-28918ded.era1 +d1b7d976d494fdd7290e9c4b910350a4ecc850520d072d3bf556b7051387792d mainnet-00403-8d3e10d5.era1 +bf34ec30154f19b6d6af35495519c0596f291c02a35e22f1cdb36dc37636a3b9 mainnet-00404-729d063e.era1 +37ef95e629c825838cd3aff096265ff3101c736a1dfabf789c30f0e8ee4bd955 mainnet-00405-3857def7.era1 +2f83546b46b79fafa526ed628b95b7ded32e5ebcc549e35cbef3454d9ef4d0da mainnet-00406-7be7d5cd.era1 +fb77db101bcde3e613657bef4451b71fd4dc123ea7f8909a5b89a716dc03a082 mainnet-00407-0a5da5b2.era1 +5415aa49b05054b0d5ff111cb47c56aaa56e9390565dd1187338a8d2f08cb9d6 mainnet-00408-4ef48eb4.era1 +2964715cd90a4f9c867a19fedb27e7e0000b7db7f83a452c8e1f43ff93ccd4b1 mainnet-00409-e4148ff7.era1 +5cf7d4cae5e6646d48c546f19a3704615dd40446d6f509a82a33c82894323a09 mainnet-00410-b195d1d0.era1 +54d28a8f851216be0cabea18dae1197194906b1586e767dc73b7388dea6153a7 mainnet-00411-ab2766df.era1 +db229733b6c4cb5f3870f5ddc0d810b73e33af7a75ccdb81d6b8cd3aa988531b mainnet-00412-4fcf3d8c.era1 +ad7e77249977c6d6d642a23dcca97c2f6053505567739a1f3dc01477c94f5a06 mainnet-00413-371970b0.era1 +90f77c70dcb0d58fe7ab36669fa8a5acbf6406c952738352cc84a99f723a59d8 mainnet-00414-7525f2ed.era1 +eb22fc6619ae6d605715d55ea954902aa31cb2f015ef4134af497a39e7cd7804 mainnet-00415-4820498a.era1 +a6fe2ff5ee3e391ab69c97d342aefe88ed2e2146870eab6569dd4516b2677866 mainnet-00416-55dcafa7.era1 +5b99127fbc5f6c24d4eac783462e2379bb7f4fc70909c43e829119e1d498fdb2 mainnet-00417-9a836cea.era1 +cd41a4ee69f5082e38f6307d6f2b94fd51dba629f8f9b41b49e068408a148e55 mainnet-00418-c5248447.era1 +037c937e58c4ce3959641aa241af4ccedf4db7c55e179c73e2da0ac45ba30fdd mainnet-00419-aa00e844.era1 +15d65319cdf5dc1e76c7aaf4e7109b048cdc2e4929d42dc6131259cdcd316c67 mainnet-00420-1a718263.era1 +18505aeee0cb67e5b08106145b4a17efbb9340d896450b789c9581629b1e859c mainnet-00421-b7a77195.era1 +edb813dbdce2343f9b75698a75b743e51c3a8414cd4488a3b78225802e8d8788 mainnet-00422-05e01b82.era1 +c18ae35aba240dbd82651d7387558b06cedb558f47598d0d369b3335174a76cc mainnet-00423-0c5c2035.era1 +547302236112ceaaae8a85ca0176d81dd79193b932174dffd6123267602ce9c1 mainnet-00424-7d31cd1a.era1 +12fb9063597f62079d3b445e4a927414f261875d793f326eb091d310a9d15436 mainnet-00425-737fc200.era1 +281d9c52930f84dcb7d3972d55eed330227c454f00348edfbf34aa505253c5f2 mainnet-00426-b042cd22.era1 +eb20f6e387d6c977608b08d9a3cbed74eed31ddd276b42986df8aecf851f535d mainnet-00427-a5f8fadb.era1 +529b620623c4d04588ef718211c3798085f63affa234d09863ebe201956911be mainnet-00428-2247adf4.era1 +149459f90e22f39ecd0b3747e0cccabfc1a7ff60ff3d3765a8a6e88d3ed3f95f mainnet-00429-1cae28d5.era1 +3a5a78a76eabf340bd230c91eeb74ca2188bb5a9a893c8c3c25d1958bf07c1bf mainnet-00430-ab59cee6.era1 +a0f6b0c0ee9e935cb32fe509023b99b74a914ed6b740929c766845bcdcea109d mainnet-00431-ed24090f.era1 +610414b9f4d5c298c54a145519e2d98f88f2ca17514430c1f30471e5d880db50 mainnet-00432-ecfb2f47.era1 +3ad76fa4ae6a1ed8d510673ce7d2875b6f956ff0c8cc1af3c4af647880f7a07e mainnet-00433-ea10cb3f.era1 +4fe2fd1182a72ae4e73f9b80480e02319d0a9e10e1ce939080fe3cb367ef907a mainnet-00434-ed8823c8.era1 +637b8cac56d8d9d2c13d1201cccecb24504ede7381ac3b8bab3500a8b7b3ef12 mainnet-00435-1acee0de.era1 +a6d101e7ae0f2f0ee20e7c5991acee4baa08a2480e0b6b61514868562cf2040b mainnet-00436-c3510bf4.era1 +5758607436d4abec5629c29e4de09018668c6f4e3372ade527d220e604c272aa mainnet-00437-f2dcc620.era1 +98a4c4a1624eeada42e5ab55e37de3c9303e692f2b29261637be5c23153245bd mainnet-00438-00f5e21d.era1 +18c0623aa3283174932fcedccb6be4a29ad6735cd2527091a7f5bd4a97240128 mainnet-00439-5149508d.era1 +7c3a144dda6e614f7897166a93374b12ecabf08eb001b5c91455c058c3d5c1bd mainnet-00440-9feb9189.era1 +5c8be22c7adf0f45b608fbd84f86a21289696d14b55988ba997fb11bd174f3e1 mainnet-00441-3f1832ca.era1 +e9c5db7bb9d23f65b7cc7189f2f562e910ecf45714ba134d5078e835d44c481f mainnet-00442-1d30de4a.era1 +a328f5ed715bb41631f1db499dd3cf5d5810492e65878e1313830209203cf3cc mainnet-00443-ea71b6f9.era1 +964f815a7fc2152daf9918cdfdc24742b564ed5a7c9053c7d57b2f55dbf756aa mainnet-00444-c56da958.era1 +b281e2c5e56f1eafa14af3e8b1c648512f15050d8aedb7d76a2ec04ff2e15f50 mainnet-00445-02cff3d7.era1 +8d1ac05b1efedef5c382bb6de1bf6bdbc8e1e048280e03c4ec177450d8665aa3 mainnet-00446-2280f1cf.era1 +4317a705b8a6ad0111850b6185c22cb3785fe58841cf13607bef0c208c0333b1 mainnet-00447-3cff32e3.era1 +5c691ceb14e8cb47a482a5420cabe8a7ec859136a989f47edd12cc451f3faeb9 mainnet-00448-7dd2c4f1.era1 +0abe1d8b735750b2004a10b0a1e6927afe8665a13539697982f2e8c21b6c0cfe mainnet-00449-6d1d274b.era1 +5bcdce9f1193f5da2c6e4be6263139486e95a35f0cf3ffce6d44afddf2726331 mainnet-00450-2b5e1149.era1 +a1a501000dc22c9b8ae03b9f7e4a93222393e170a7ae2827eb97224bc18cf76d mainnet-00451-4abe0e07.era1 +c9fba4cf9aa72ea9f74d9bdd08c694b807aeb480ffbadf04f939992fb2a3cea9 mainnet-00452-42606107.era1 +d2aa4df81dab672f50d3f79c98fec32ee7fcaa54cc1041be58a1834d37f347ea mainnet-00453-23cdebe0.era1 +231aaf7ca5c7ce0107aa1e8520273c18a82224b81f5daa2c98e58ebe5217b846 mainnet-00454-b2b5e5f9.era1 +eca2fb672e6c4fd0e90abb107e535a7bff18ce5c66cb8fce13b6f7dc3cdef58d mainnet-00455-54943c8b.era1 +f2030b4728d9dbb95e6c4a1796ef569c4a7fbbbedefa186f13bd13de303ca0a4 mainnet-00456-cab07908.era1 +a53b67150bd02d6ed966b436fc40e15a76c1185ffece264ccdbdbb068f264e2b mainnet-00457-82d06ed1.era1 +a32713a0a07a3c376d9507c1bff380a082096693dd38deebcb87ec71540124f2 mainnet-00458-2bff3cd7.era1 +451fda4aa67cde3221f2deb7f94e28562ea1d388d488bb77f925b645d8f474c6 mainnet-00459-29e03017.era1 +263844132e6617fbfe71c5aeb5de309aa811aaa8db3022cdd01963719a34dd01 mainnet-00460-6f2ddb12.era1 +5cea732f6df87faa2fcfa2e5abab525acc144d1135fca90c04777159b6a102b8 mainnet-00461-86535bae.era1 +ed2a2f0d305dcef66490a1258e1918f49b99e26ad74352bcdb54b41f38c8a011 mainnet-00462-3658342e.era1 +56ca521c6c281c5175ea66527e0fdc7280f4319c7d4afe9aafb0d4b36e9eec75 mainnet-00463-4f586d74.era1 +c3eb5333f6c002989114af2bd844c0e8e28e6f6e4407fea00e548138f577d720 mainnet-00464-2da57d6c.era1 +7f4c5ed20fbc6656ae5a8310037f16f15e9ca3d4d62d98cf7655c27c5d2240d3 mainnet-00465-afdd52d6.era1 +cca9cf85c9152b6c4eb1d0795ab121c4961553ad6e6613722be52a1a94c4a91d mainnet-00466-21cf05fb.era1 +bc875cadf0d713ccda036f1e2656be47d9f244a715e95990201bf6b47f4d92cd mainnet-00467-27685b9d.era1 +a903dfb595e8ea9d49ffb0e5b120f9cfebdc43b66ab797d406244f40aa61467f mainnet-00468-610466b6.era1 +28b570e840b71cf51b2cfe868a3cb1f0ed9588e0df5f4de7e2e6a5f8182170c2 mainnet-00469-8c5751e2.era1 +f655e24d6972690175ea4dc133e9d5e9b3f7837f3e17d2befbc7b9ba9c35dcea mainnet-00470-2e445b03.era1 +fb4c985a4bb83e274daa136dac66ffddbed69b6189b4ac219d40819e6625be74 mainnet-00471-7f375623.era1 +9df7017dc925a606786d780e02df664b12999b6dce1844a4a2dd2e59e9ff9a42 mainnet-00472-5f279db6.era1 +164d29232a4ef13a37b85d5149f6088b4472f2f43bc46b9e12ef6e9e02a8ed33 mainnet-00473-5ae48eae.era1 +414314c0e27330485dbcd2c689d3db8adf740672bef0cc19083cf3bba54905d3 mainnet-00474-8b70d9c8.era1 +68d098339c7a7d1dd941a6488fec8e969a08d6cea26b93c0cfb82fc1662f1d4e mainnet-00475-a8e53747.era1 +b1795e08edd0ec1a200504de3cb6d02c7dd796ac3c2a3836e7179724c06ea38f mainnet-00476-dd34a7c1.era1 +ded59ffaeabb0ee900fdee509f91296869fbdf48f534618cca664da1860769cc mainnet-00477-488d852f.era1 +982b4a076083a00ea755ecaa2e8e83772b451dad624d58a2abd65236fd7880bd mainnet-00478-232a4fc9.era1 +7cd6e8a704e5871f17fb1d5f3fe5988f90a6fb85b5930066239769d66aee0649 mainnet-00479-4b082f24.era1 +f5455ff560d2ad13510f307730c86674246a48a9417315c6afa02007a7b6ab6f mainnet-00480-b54b802e.era1 +0495187bc1dd5002de2c0eea40b7e8afa409881794ea806620638ae14a15aae4 mainnet-00481-213ae81b.era1 +14f00d8d7612f3dd621378a5733a5b1381a50aa8feb7f5f37e2451b57b1a78c6 mainnet-00482-5d09d6ee.era1 +9cf6cf9a7502fb5cb39c17261fddd3c9c75ed137d165df63e59e8d4e64e4e7bb mainnet-00483-f3c11c6e.era1 +12177883a5f8ad1203ebcea311e22fc6152f42162b14de0e1580c9c2a35b8d62 mainnet-00484-cdc41b84.era1 +3b402d38b246d3ce16eb13767165ee833d32d16f40f74585fedbbeb47f5d2e41 mainnet-00485-b26f2e53.era1 +2f9849026ef56ac144cf56e61b1ed5ece5ba0b513a66175fd7c16e364bf46802 mainnet-00486-82b8c438.era1 +513ff1109a8567db088e57f1eed1236b0bafa122539fe14f85ba2a3f70a08376 mainnet-00487-d834f157.era1 +51993f2f055177c99910a8cfd0af62a09e195d12aedadc8994f9b4b5e4c8acf1 mainnet-00488-fa3989a5.era1 +588a68e84b5483f3903528f998b4e294e0542eb025fea3d91716879156d60259 mainnet-00489-4db390c5.era1 +20a241cdb9315952d266f45aed67141fc5b0eed9610f609a563aed184bc450cf mainnet-00490-b9ad2e4d.era1 +3745f7942b2c1e0c254fe9eb051d59983e9ecdf74cf17c5acd11a1e35391965e mainnet-00491-6aa98f9f.era1 +5708622aac0ea34edc5ea2c6c40d323eb838101def39febbca203cabb7de3444 mainnet-00492-ebdcd70d.era1 +49beb4efcc4f515ce1c5f497114bf1c166d65c16d5b9c47ff2523934a86ada05 mainnet-00493-3079f625.era1 +87c21e393d2396c3a6d42ef7b1dc139b0c95b69c52edcc0c17d4a7aea83a9694 mainnet-00494-33f97b59.era1 +b64261ac3156a8cc499da652dfd909338399a957cbc8bdd2f0c8b792ed61357c mainnet-00495-ee3904ae.era1 +1dc0fdd218d106d7559d046e77b30992ca0926cd7307b95e4f8d0bfd3534ad17 mainnet-00496-cdf13c18.era1 +49801e34f60ed5986c876b6cc4ec010423a25e029eacc8c9b903dde8e6c58da7 mainnet-00497-27f7ad95.era1 +86b7545b74e9b6ff8d4f2272d2307c725043074d362c8ec669ddecfdb8e7f2fd mainnet-00498-fed48beb.era1 +44b03694c9c02c3038d22b134c2ec1caeb2c8802e9b350e76b1ffbb7e1806f24 mainnet-00499-d9c9a733.era1 +19af4c2cba365b97f2e8e04dbac106c8ae4880d434aefa7f373d143bc1e6fcfb mainnet-00500-b11653db.era1 +83be8e12e664ac4c6cb455f832c36a8537093a4b94ea9d4c45e96ac444f5a522 mainnet-00501-b202cc73.era1 +580ef812ed778ff77930be56d0a3b15ed6e1610aa9877a2a4656fd0e82819644 mainnet-00502-19ef7cc0.era1 +a52cf41b1d14b02ec3b1c70a8904a416370a15a1f014d1e63b5ee53d59ea1e8d mainnet-00503-45f20620.era1 +9655fa5d25014d2d47e21c6006cbd66ab29bfd3022edd282ac745e27a7161717 mainnet-00504-21e3a8f2.era1 +15385a304034611f7665e75df8c7efdef6b39de3af271cbbb23fb23ee9882695 mainnet-00505-6dd1ba56.era1 +f4feb4ae45164687d99f194a2377359f90a53c92dc5fdec01e386b6d82ab85f5 mainnet-00506-c0414076.era1 +b7108796a98dbca3865eb23d040f2f2bb7b81e5613363601b05e38007599d5d1 mainnet-00507-a7340f2e.era1 +3835eb369cf4a7c1d7f422a3533026d4c7f39e824c8db0fc5b06decc76f35f93 mainnet-00508-dde4c38d.era1 +58328611f7492611aba568c1b1bf881f0c4712047cda5e474dacc20a6f54b800 mainnet-00509-fa5dc42a.era1 +de13c1e15738b569e0f2e39d2ec188182dd1b607e3dd62806b98ed29f36e83d5 mainnet-00510-ea2b8d47.era1 +1f0af6eaf3908eac69ac0c7daacb17d012f8410cf500e178232af0d9b17d699e mainnet-00511-d5d2416f.era1 +85497cab605da0915dcd15f81784b6dea663f5823162ffe17ba9e51d5219ac64 mainnet-00512-e2918e50.era1 +f815f4014da4b936f60b4a597c4c73f381272b465a0c8ddb954cd20018983edb mainnet-00513-d39eefad.era1 +029afaabac6f5aa8249f971cefd3843166239f9cf087cda5b8ab7ee78774cea0 mainnet-00514-8bd8f6bd.era1 +9ecfaf77c345db1c7194277b1b7b52a3f7368df97003d7161f23554e39a962d8 mainnet-00515-66d4642e.era1 +01cd75539ae70d3ce141f22490a225c602f705df3a7e57db12428ff68768d342 mainnet-00516-ed3b1187.era1 +68ebb5b3d0d41e8c255d16a0a100b41272b74ac23a40c3f61c4ff30a4d53d2b4 mainnet-00517-5ef487b2.era1 +d5d7f46100a8ac2ccc7312c7f56e951555c14bcd9aef4faf4540c2fac3005487 mainnet-00518-71027029.era1 +773e464730bc33535b0795c24c47eae3f75dad5661f7cb9e3a723fa7737d2afa mainnet-00519-218c3b62.era1 +73c2ec4fd8b5ab8fd994866d5ea905e02f589f0bfd17c62b60586b5e90faaa3e mainnet-00520-062e1719.era1 +c4b190b473cbeedbf892d2d3c081360df71b038997e0466820fbf39796fb2f13 mainnet-00521-5b55367f.era1 +433aea03dfe9a761ea95d8fb326d545596a6b6ae24af3bee9b9c7e5abae43367 mainnet-00522-dbca1ed3.era1 +3c279449419da666df1e2c8edc9adbfa3f6c2481921b0f0541e03e19db77dccb mainnet-00523-297cf9d5.era1 +3a1a62e5fc02eca3e2061ca7ec0ed5561dfa031bdb104cde4419d6841ee59e43 mainnet-00524-f389f3b6.era1 +1cd54284de4fbbcd0b9d60eeb837b50f91f92b167034e4c44941ec5a6556cd1b mainnet-00525-d851e822.era1 +bf17ca4be7cb2c307fb5f13f295a20b448d819928ea5c588072f6da9ff2e3400 mainnet-00526-c69972a1.era1 +05e4db40af70a7e78ee362faaf381b877a7add46e3ce1f51ae7bbf1e9fa26ea0 mainnet-00527-2c103256.era1 +3a6ee2131b8b4283d1a30f2b1137106c461d0c26e3d018c8dab506b06d87daaa mainnet-00528-32a674ae.era1 +ce09d7816ef22ce8e0431f1a482febbba259a4e28fe7cfc486441bb41d70446c mainnet-00529-1bf711d1.era1 +7e9d28e51f686b6a1012942281ab8bc0b410784d2bed7f7c987a35bcaeebd904 mainnet-00530-aab9d4e3.era1 +26104ebb607588e04ce2323c4b3891f9cab06244fefb505609acc622cc40f749 mainnet-00531-0f51dbca.era1 +120499bd51f65e76117aca0674c8cff513589f3274ef70b183d170ca4f806728 mainnet-00532-b4c6703d.era1 +eaee56370a0bdb1309d4c2ff52fe804e7d2164b5469c8b72c4957ce8171586a7 mainnet-00533-bb2932ed.era1 +2e6cd452bc720c247941118e8a0047a3c7ebe561b87bc975bf87b195d7a64bd9 mainnet-00534-c65d109d.era1 +f0a6b2bfff8e75bc5e804bcf9c0363ffd19ef9e8ee45cc132fae638b77e23bd3 mainnet-00535-bb367122.era1 +30cdaf9922f008643dbcde8528730c0dc30799d34a43e2df58eaa53210e2434e mainnet-00536-faadd066.era1 +f874f4faeca9a98e5601a53898d2102617e9c0c57738ff691766c78139611c8e mainnet-00537-576b374c.era1 +f0783e7c936d1b71d19bb4abe24e8a1e7e70f06b65d2ec7f7e8a00bdf0d0f1ac mainnet-00538-d8eab6c1.era1 +7e5262ba5514aaf42edd137db7587cde57279d90b43eba05b9b4cb6a866989f2 mainnet-00539-2c32d06d.era1 +058a5cb9e04e96861a4821b42d087c6f876dcb65119f8e40df0d79e06018a013 mainnet-00540-f3078a9b.era1 +21636685ef41978eb7a796969d98ff8c4582021b47f204b2e21f93652f1f410f mainnet-00541-3063ad60.era1 +501ddb0a72f282a9c0c04252eb7aff752b3b7d1336f832713a11a0fb17bce918 mainnet-00542-90a242f8.era1 +121e55143583932220273c895e9eedce10620c044f0f5431550cdf5571d9928d mainnet-00543-be9dd353.era1 +f3e298a94342fea1557770766082e47347e48a1b219e91791534e56dd7d88a26 mainnet-00544-1c5c7615.era1 +4285a6021c374cc02e0b7317e1a069e8a3ec1dde760b901a7c542e92d97e7636 mainnet-00545-e6369239.era1 +1d7616d952fb561b06d1fa898c8c3849cd66351755bf56d7d3ebf2cbc4f7f058 mainnet-00546-21351413.era1 +91a8417bbde8372258e4ea7403e9e711d0df6fd722be8b2e73494b0359eb90e1 mainnet-00547-0fd9e031.era1 +84cd049065fe0f7f39f23d3c96d81f59a54a94170ef4632429326082a8ca3a6d mainnet-00548-e318be79.era1 +4d5332a037b993314883f3e274e139314a7b8d2c916cedaaf535cf7ca04e0b70 mainnet-00549-2dde72e5.era1 +73dd18877d5cb50629c1043ac51fe95b9cff600494a4cedbf5aafc45a09b5abd mainnet-00550-d89b717d.era1 +8cacc8487d8342e2aa6c4642b0e14585797448d0a29a377d00719f768bf17cb4 mainnet-00551-a8dfd860.era1 +1ea0ddf1d4a156cc3c5d055fe0e90516669f80b29265222815d54bcbb1257d7b mainnet-00552-d6a3521a.era1 +afb3063c25115627793da7620d1f118548789a2d34d8efb175d8aa51aafcea70 mainnet-00553-f702584d.era1 +746d867c504c5b0cbe642f9566dfb007fb4a3184fed3811a49c38b05dfe34006 mainnet-00554-700d58da.era1 +1186a9337ad0b4c9c2335817e5e1f6b7359093ad3da2a40cd108d1e6537c856f mainnet-00555-41db6d14.era1 +63aef838d5c3f9acff8e3b678dd9248b6e2464d6e5085cba0a2dbe37a5623922 mainnet-00556-3503dd53.era1 +6b779cede943e8d4cd55da81fb9ae43faed525d0bc25e9bd2f2b94ca6d478121 mainnet-00557-02391085.era1 +9ce299b548477737341d6591775f8967ae4f09b5bf1f23baf8c477838eb9c364 mainnet-00558-ed670fa9.era1 +e7cba63e763a2fa24ab9d76c54104d1f2deb017ee5083595c10278e7281e0c45 mainnet-00559-63277435.era1 +3361e01e4e11b8ea2f6867edefa813195b9e3c67ee01d0883788932b85285c09 mainnet-00560-e89160d7.era1 +3fb208b1a30c3cb39954a8f7403c4ffaa5d4bcdfa967ae6d0d83d807a885831f mainnet-00561-f95c755f.era1 +076eec9208a1a7c1f2f3575c2eb52cfcc98ceef632aef8cea724c1be4e1fabeb mainnet-00562-97a6fdba.era1 +864299fa4f4daf4d0c48230f831a70592fd2b854ebf0b7bafeb1a1b5634df17c mainnet-00563-8aa6ac0e.era1 +2c32656b7c27705a4807c827bc3545d11f2fbcf7146c29518937fb89f3ef8968 mainnet-00564-ba8486b8.era1 +7c7fd609e45de5491e46be7ea7ccd097b208fc2c88e16980c4426dd7205a8031 mainnet-00565-7cba894e.era1 +2f42a73fa8eaf0c1c0dc53f8727c5fa579ea4d4b4eb838bfe9cc61d7c0f92fea mainnet-00566-54b3e34b.era1 +e15a38d740ab95ddab6d31f2a4d1419ec17a463a2e6e77fca9efdd78c29c260d mainnet-00567-40280a85.era1 +0da2dc7c686f59ef96b2db151f763480d3a881645c8bf431f4dcdb996d0818b1 mainnet-00568-ad863b1e.era1 +de95cf0a8e2d4ba416296a179d17e1c3206e2fc537075460193979f67cf888b2 mainnet-00569-dcb283ea.era1 +3fa3cedaab307b161d1a8a6317ba34f15a1239e3cc30749192004d7daa1653b6 mainnet-00570-fb912362.era1 +ff4d8628fb67ca2337f55f92f9d1c23602e5e4db51d6df8e209e8900087a9263 mainnet-00571-b65f3342.era1 +1ad917e8e7a61f0833399945c9e687a65e7802309edd9ecb37421d3e8d1bdc4a mainnet-00572-dc59179d.era1 +24471e846f24c62c93f126d7501cae75867f791a8129dd97a8a1a7680635e386 mainnet-00573-21aec308.era1 +d9b8550a87770cc0afdba48e976d6e122ea136a7071177c6e4f13fd393e794c3 mainnet-00574-55e72fc9.era1 +f04623ddb0eb35b6be08f15e0e8b44ffaab1d8c3da87f7bd30bf1355f4471752 mainnet-00575-f6564eb5.era1 +f651f045ea114e31eb027b82d7531167fe65c4004286ee8555e8f03c4f8ef161 mainnet-00576-923096d4.era1 +fd857476eeb1af8743993fbd0608cae7aa1c14c757db07e4c9198378a77d13e2 mainnet-00577-5914605d.era1 +b061083da7a861c1bf7d58da0913ce7e28d09412d0b10e43e1ef600607066a2d mainnet-00578-57d591a8.era1 +06e5104850d19d3369d8499ccc590b98374f563d2ea77a10c6292cfecd20f4c0 mainnet-00579-705b8e04.era1 +4ee6f9ce5e5cda1381faebefb06c6696cb02ab2c6cc9a8c1692b398f0c4f0f36 mainnet-00580-5b210184.era1 +aa6ea1063f36beae91cb1dc14f1e758c945e1b1435837c4c974fa183ecd5b766 mainnet-00581-d3dc3f99.era1 +f0da6705ad1a4ec97a2e877ef9e4faa3f7592d04fd06e813bbad77c55f670b7b mainnet-00582-d6b2c7ef.era1 +d15b9640646d0a3f7ca5c96ff12f2439b0a0b25488c8e82eb381adb7b1e21925 mainnet-00583-43400160.era1 +6a111a0d8631dc36365127b5b86267c413fd7a6836e56d5eca1026495f4bd143 mainnet-00584-21b2682b.era1 +4f5ce93c8872a6ee42dcea100b0543d6f37c4af82a7a87ca7b0cb289c8a7cfd1 mainnet-00585-02f0b151.era1 +d3ebdaeca270a0fd7ce27195d6f227993de02e775b11d7ff3ade7d6ec3ac8032 mainnet-00586-60d868cb.era1 +ecad45bc22fa043a181d656b0270ac4f45c232f2eac5a54fa12242db64bfb0eb mainnet-00587-59cc91f7.era1 +4a6a589355b5cc0fdfea3adf27751370f32a9a1c911ce760d83c587c20f25207 mainnet-00588-efefa87c.era1 +9463605b6b5f6cf4c7b0a888d9e43409f6e97f7733165349fb01773fee6ddb5e mainnet-00589-85c8dc20.era1 +0f1526ef098bd10ff59ea57c283dbfd2195824748b83d80c6fda5686b7c76851 mainnet-00590-2bd79cac.era1 +146f1df8637311b2f1fbf58cfdc3e8c6d3c82e9064ddf55cd7623f2f27dda77c mainnet-00591-d9b21bd9.era1 +c92cf6a221783f9acd08c79e6130f4362ab25b871127705d9424b7bea0246020 mainnet-00592-9dd2dc6a.era1 +847870a9fb4096ea2dd1f297a2151187d68100cb6f07f777e3220ba40b41ffa9 mainnet-00593-0a9de411.era1 +6f31046df50e4ebceac9e592b488ac7bfd49952661cfd3664888b5c0760da0f1 mainnet-00594-54bb5026.era1 +c3dd517701c421643251fc581192ef1b111b1ecf45c0fa3dc568f53488c94bf8 mainnet-00595-08fa2659.era1 +38d4e79e4e3524b054563e35b497aee9931b88ed0aa642114ec8a0cb385c30bb mainnet-00596-5e2423f9.era1 +b0b62ee0c0830fbb9aaf7f500cf2d7d636532aaa4200fa7ac5095a681454712d mainnet-00597-49b11d14.era1 +491ec13b3ef1ad873607254933d7f894f9257cfde114ea633074acc71f67f675 mainnet-00598-050d0b97.era1 +b705e64cb2580dbe83c38e4220cd1a0420b29a99e94d888c3436d6dfe8763211 mainnet-00599-a19b1a20.era1 +5867f93ea1a0462fcfed3038757464c0c932a713c1ab9ef5c5639aeba80f3da9 mainnet-00600-a81ae85f.era1 +eb9f78165da14363c498cb339eddbb2c6766c00b4c033f7b99bd593670ef7b72 mainnet-00601-9e26205a.era1 +3c95474d17a48074ed8545c42927f086b6adb1d59d0fb6bedaf189309c9608be mainnet-00602-28713614.era1 +61b4b1139697c6282a6328a9d146307e98c52c2c7db51723e7046d3fb0588713 mainnet-00603-9a115bef.era1 +05a250272371db23f2c5a391d11bc2856b803855121163e326d2de556644149e mainnet-00604-ba62e002.era1 +033a4bf1b53f3d04f903df7cbcdb883a0eb407c65fbd274e554858909f0feec8 mainnet-00605-0d936f16.era1 +9a5db5eab59797b36203aa0276246d973e663d45850c9f0c2b56d1cb6bbad1b3 mainnet-00606-ae859b84.era1 +1a5d3e0a9cccc60d553a6ba1646f18a07c280e918159e72654ecd347277d42c1 mainnet-00607-62d75fce.era1 +1977fd6f4e3fe0c278c8ca04e46b79314ec07d02e885827ff5d72e3891556187 mainnet-00608-d449c48d.era1 +266f2d868f41878223d57b167b555447610fdcaea375bd1c40bbcac087c10470 mainnet-00609-7e60445e.era1 +0cd85b03694239516f90edd8a59d6ec9385aadbfd28d2ba3cb49a21a0aaaaaf2 mainnet-00610-2f230cba.era1 +b128c7ac9d8f20541803ccce5cfa6b7a49804b1d115ccc0b44ca023a6749aa4c mainnet-00611-290b8c2b.era1 +958cddccb94723db4013a8e54259897fa91df9c3c56d0dcbbba3e753b2e73290 mainnet-00612-b363f647.era1 +b0f6977d0c4dc7b8c288a5dbdc30b314443c6b2ca1aa6de9cd065097dec96c41 mainnet-00613-80a4144c.era1 +6eacd8cc703e153abd9d63b128c4f442fd90935b5f6344169755252369a79b2d mainnet-00614-20913bde.era1 +027d09aa99250322cae4c2f6a4033252d3b787dc7cfebc6f51077b554ce3ae76 mainnet-00615-8b6e7a1e.era1 +e8e5a7f53abb982b9f658cfe7def2aba43caee175d5e053ea748656fc6a330aa mainnet-00616-bd35481a.era1 +e661d9a123138573e618f8251c46675c9094a76c3533852c5d54206c74a47d15 mainnet-00617-624ad401.era1 +3e10672b6ffb5a6bcc59420b2d5c155a87066b033883f9b8e668942298439d5e mainnet-00618-c70c6f7e.era1 +dabd9d6332a1c1f429e5b581fc73ab595d6c667ecc4598c4f1c250ebff4c1c27 mainnet-00619-1b807ced.era1 +0c9d1874174ea831bdbf74616811eae0794a0c881e8b08078dd6851733de7773 mainnet-00620-85065023.era1 +17e8e3a04b8bd7bd414b5bc25c4c9f66ae524337a4354a612f49bb4f74734945 mainnet-00621-385fbb21.era1 +d99a5e65a0f80a12fdb3b0b3c745ddec6e5312c3364ef46485c06d9748af97ad mainnet-00622-92fb5e57.era1 +3026bd1407e02493df937cf5d57e67421b94284780874ab475962071712a4bdb mainnet-00623-642081dd.era1 +31e47742ac21fcb1b121c86130c4f044aacc13abb7df3c67dac7a442a1adb3c2 mainnet-00624-1d72d627.era1 +941fe056f09b29ff179be410567e07fcee1ca191d0599ceaf869c336891d5174 mainnet-00625-f7ca7cb8.era1 +d31fe29f030c01f7e49ae70472def2ecb1872608a23549ccfe521c02b4b282ae mainnet-00626-4f92dd26.era1 +9574994c5d43c9b2aef288b599c29a52ae4f942bfb6d05e5b75b351fc84f2798 mainnet-00627-06e70723.era1 +c8815a923482ecbc634ee658595e5333bcca9c53aba119d288090c4437013b8a mainnet-00628-313cbe43.era1 +5f7da2352308eb1274e73800cddd672f8ffeb1cfddd3574d780dc31b1d5e69f8 mainnet-00629-e939c151.era1 +207244939f8f19921eff5064ca9f109debe695a697464dc5c7779b587c60f8be mainnet-00630-1fbd3e9a.era1 +80128ec9240a841bae7d883ca736a923014907b5529c71ccca0204285b4cb7f7 mainnet-00631-333c0583.era1 +8c558409dd45ebf8633fe459a03180f3a141ef56ee355e99ab615c6ab6feeed1 mainnet-00632-bd6027f3.era1 +88db31cb6128b4de50ebc719bf28ef010a103214427e5c37e568cd42a493d946 mainnet-00633-2fc5d74d.era1 +32c8e4076799588f1cd35559ef2830058d7f776da6874231b83b46eaab84282e mainnet-00634-5b3c9d59.era1 +72fbcafc97d51ef4cd15dc5a2cb7a954cfe497554f1486ba69d9748a2ca21ea8 mainnet-00635-95e4dede.era1 +932eb65ac3753538380a3b2f1ae3be2a56fb93db259771c3ba24c7fe5d978de9 mainnet-00636-f8124b1e.era1 +5adbe1f6170a9cc09f96267601bc4076e350ad36760e207c69ada235c157a09a mainnet-00637-12daf758.era1 +599e56da4c0cfe52eac27a988679cec886cbc64038bc1a9f3989c61e660f758a mainnet-00638-75957ec2.era1 +6cac0942b944eba26ef6f87fd9529f35bd340127744ec1fe58414e823ce548ae mainnet-00639-7c4f218a.era1 +de762fc2f9932b012470f51da960775a5323924b62e9055a05ec6792a16647bd mainnet-00640-3cdc6132.era1 +afb93a2efb314c5df655789296901dbc0ef0411534d44d0b22ac355118f546c6 mainnet-00641-471f9e80.era1 +349f4285db586f9cd6e3b0e86ca25e0b500e9802c9523405c453e8753850f92a mainnet-00642-551764bd.era1 +1e8cd0ea9699d6bd1a3cf0a4a41f6f20857c72f78d5774c3e0835fb96e8c2820 mainnet-00643-368d4f6f.era1 +de6b4dfbca85c05979a61d538283bfd63068471487561a826bd6549208e36b89 mainnet-00644-81a7c3da.era1 +afc34e2bcbc18f2b3baf86aad08cb934b08322168564f3a57db8cfaee4bfc0b3 mainnet-00645-2582b644.era1 +7e58250e2a5bbd7e4737dbd0f028fa972519d6a4c16474b7a2987ba93f6c6f79 mainnet-00646-7fd3548a.era1 +de98a8249f13257740e2a39381933eb4f0050452889037c6ca4219e874ef7193 mainnet-00647-07e93c81.era1 +cabee428d74add0d820ce6fcf617f36dde5408a25c627d15c1c76a64b57dc94d mainnet-00648-01a981a6.era1 +257deed1d444ca8c838fb953c08846dbe6ea5de22b5d26ae9ea243c6a8c3f0ef mainnet-00649-05bf160d.era1 +57eb9ff0d06f758af8739d12aed01a8d750d5f19ff18fb41355f78a6da428703 mainnet-00650-119bef0c.era1 +cd625575d5f2a3b546b1801cb31de9854d592790d50527bd43a433525335005b mainnet-00651-4d3f0b82.era1 +87202ccda9b11225a8d097e495b1f62681206cce56e81600f22e4057858361b8 mainnet-00652-730975c7.era1 +3a1052f205840ca993bbc3225667b593aa5479f7d80cc3b27bfba4b07fa8de07 mainnet-00653-2eb89f05.era1 +95ab2a49aa68595de236f97733f6b99c22a8b7fe43f512e3ecdd896f2d84b9c7 mainnet-00654-51b0bde3.era1 +8d4c7eb3aaee1522398a5e232ef4febf4027f3c236502885faa891e9a00ebbf3 mainnet-00655-06381406.era1 +aa49d7868cb14727d5bbed6e8d48290af874c9feec7abc6524c967fd649fddcf mainnet-00656-0c072423.era1 +f52ebbc585811c9c26d15986ec927122c88eb1b840746a3ebc831cee403bf02a mainnet-00657-4b0dfe65.era1 +4d28f6ff21fb6fc3448d7bbf0cd7e7c92a2df8a339df0259400b4cdde6f3ffa1 mainnet-00658-eafc91b6.era1 +3fe97ada56a3ecd29920cc28f088817fc384e4de1028991566f4ca46b7cb9fb0 mainnet-00659-49e7399f.era1 +12abdfae11202056ebe3a89d1fbe165aef5ed2b4bb83608e249674f5323eb211 mainnet-00660-376add4f.era1 +2157bf342557f8400a1dd5fe4ba08105d8e97788469c25a69fa7df9d1bb147a5 mainnet-00661-a8a70e6e.era1 +4cceef1dfd6ca6fcf875461813ca98582b5cc6bc3fdb178c8003f6b88776e637 mainnet-00662-dffecb3c.era1 +defe253696a6266c6fab5c8ebc6f5539bab56bbbca80e6897b603704c20d5da7 mainnet-00663-6c4ef261.era1 +e292d90ffad574c26f88140a0fb3adeb6e0feeb42926fb2185ec13ded5dde951 mainnet-00664-3eb2993d.era1 +f6d998485da74f49e00218f4fdb9783c3a7d51192958c020a6f49aaf2edc6dc4 mainnet-00665-8f406786.era1 +e582415262cbc5638f27219f51c9540fcf6cb8e30ab8d629f205f965c58bfdc7 mainnet-00666-4ed7ee7f.era1 +327cb6bb9586ef1cd5776c856e0b838044fcb12beaa4b986066585f9b20a1ff5 mainnet-00667-a6b898d4.era1 +1cad75fb6ad6e754db1b3853171fd4eddd48479b92769162a0602014b090c094 mainnet-00668-c4a9d166.era1 +e22ac2e3c703357e2c18b04b19ea413697538ffc1acf84d87c386d1d9af08836 mainnet-00669-d8d97842.era1 +372ad46d1309e2c31bca31da93d1993bdd42b3ec5cfb16cea5a4cb7e3253658e mainnet-00670-be3afc5c.era1 +2c2682a6baa86d8bd465e124cc488c94b0d151d6fe942fcf6e8bf073ac445cec mainnet-00671-bf138a26.era1 +43124aaaa3f46a7505a988731de60b0f095c3a119184ec8463b93a13d8f8fb8f mainnet-00672-bf106779.era1 +38b7dd5161b825fcf13b8c84e6f885576dc748e73cb9cc3417419ff738591bc8 mainnet-00673-defe7756.era1 +a48ded2af1b573fe6574124093f7f070bc374ee339e9b68a2f512455e26fc77c mainnet-00674-c6dc6388.era1 +4491cf363abfbb3c9f7c4c58013facc62788946fd9bf36b0717d28d03d5b8798 mainnet-00675-1e729aa1.era1 +c4c629a38e80616f50c9c79457ffb31169e1db56798c9aa6467ef47db2be6698 mainnet-00676-a5855d98.era1 +aed81eb7e8722c02febe53bc640645d76a03176a96ca2716828504c0b35c20cc mainnet-00677-f3e17cb0.era1 +b7193ec008a5f6ad77cb2d38e25382491e519652068c2bd8b8424a99ff672eed mainnet-00678-42e73724.era1 +9703fd0c87a1b44fb78073d64f25b680bbb1231436be09153898a1562ce54a0f mainnet-00679-f0fc97a5.era1 +df514a67472e6534e222756e1fa51f300e90b1a939b33401df6e72d5056612b0 mainnet-00680-a6045d36.era1 +c09ca9581d1b07bc6e6a2fb949dd21ff1d985c35c4e453cbaf4e108209cfb117 mainnet-00681-247222f0.era1 +5e7b3b9e90efc074faafa91a1d9a2d59f5260bb27b9a42259e6d5e4722a0047d mainnet-00682-a6b3aedb.era1 +db451ff45d68afeabf0b0230635f246e901c1cedf3280207ac615ff62561fcbc mainnet-00683-19fcceed.era1 +67e85a3ffd3881dd4f3c8bf608e78e8dd3003fb0e52d61815aaa6f0385df9911 mainnet-00684-388b0bdc.era1 +7e8bc25827cdd3ba2e5388fa49d4b3f0fc07d1c02b6e4231a8a9348150d0ac02 mainnet-00685-a2bcc3dd.era1 +c97ac912960f05cd5f6fe586a23d3b0822a5acf5073de8d87af1e892270d3c1e mainnet-00686-df515337.era1 +8a61444d3779778eda326b26ab0d38f92bd5854c60e8287ffe51d90f4310e4ae mainnet-00687-89774425.era1 +188794b31490a304267a019775cf6ee50619885d4a637aefab85c2e589051793 mainnet-00688-a979df6f.era1 +f4c53896dfebcb9fea054ac87fad4ebe6edeb0626ecbdf968e71a94ac03c037f mainnet-00689-33e5fa30.era1 +0f975a96a671fbe2cb3da544eb92c0531120ab966661568470b8d7996bcc071b mainnet-00690-7c95061f.era1 +91c003334447afce06ed484e69166c2d8a80692cb99ec9e223328825177233a3 mainnet-00691-062b4177.era1 +1a0679af4a788eac614047b0c7f7d5286d285ef23d2729518cf05e1e62086622 mainnet-00692-945645ca.era1 +ed2e41823a39f3913774790dd3f7cca12a862bb327243ab5d3d327f64aeee0eb mainnet-00693-8c6754fd.era1 +6a9f6d5784fc19aa2795e0f7169824ce3be94f328a8e08c991c7527030152649 mainnet-00694-222e02bc.era1 +11f48d72bc9f0ee8fd0ed8ecc58604da4c2acd1474e77956111c93b01d19a8f0 mainnet-00695-cebc5a0e.era1 +169be7b45abddc01bbf49a7dcd3a91bf7de2272e80cf08dcdbbb636c23e43046 mainnet-00696-f73fca1b.era1 +f01589a9225cc431608eaee63a9b5eb2af6ff1d2268d50e9abf3aeffef99938d mainnet-00697-cc3b1831.era1 +9959539b491fefc82aa849676da42da17c25bbf7c004ff2889fde60659304d77 mainnet-00698-062461df.era1 +aaa7490ab9125230ab58d21840f5a645460e58c94a50264aacdc41406258edb3 mainnet-00699-7dcae2e8.era1 +b18c1e7c0569f12199080c43ac67f26f7129db745fa82767e9979a6128014448 mainnet-00700-64f029a9.era1 +529130a29dba8fd6fc9423ddcd3cbd2350f3038e75d00ed2932728ddc79b34e9 mainnet-00701-c0ae757b.era1 +1d79be36536d3a481a961ea9d31228a07734a04454258968944ab85a70ff8b15 mainnet-00702-abe351f7.era1 +0649eb04c5f994176702911403dce6e76d9ab9cb3646b0cc9900fd1da5456319 mainnet-00703-ddee734c.era1 +647f0d18ab11581d96ba1ec39a8fc39463b97fe456ffb39b9a38588d8ff0bfbb mainnet-00704-2ea464e9.era1 +d79091de53ddcd1f1428615058f9ece31916858ce935dc0b9faf833128efd8b3 mainnet-00705-9f0bc9d6.era1 +759196c48a51d500c1059d1050d7d11c4019f9a01b465c43c6c4f97d06f2a116 mainnet-00706-58568eed.era1 +fcdb249290108031e8552ccefd4e85d227a1dccfbd760551bde1bb1b6bb07588 mainnet-00707-6aca1899.era1 +b4e811a52bfceacd836304f5e6b2b63d8ca05e417d095d0b81a79d7f07289a3b mainnet-00708-2478502c.era1 +4c24a09dfe7fdfb5d2dd6b7b8043b85ae1691a708f96079d31163427a2f78bb4 mainnet-00709-07426af0.era1 +b222ec65ca7e78c42b025a003bbb7ba4b82a7ef665a8df6f2b2b19ebcd171087 mainnet-00710-a70b753c.era1 +9c9476146babdbf7cfbfcc85755e97c9a1dcfc41cd93488adca9d230e8c1d35c mainnet-00711-1146e2f9.era1 +facfe2ab8f519ecf4622a2a25af984cb7fc8b7300cc18c6d34f6d721bed02dd9 mainnet-00712-560a8119.era1 +20eb96472a2f5a26e5cf3c48cccbbfeba111fa563bad6fc62db37752c3ef12e4 mainnet-00713-54d9396e.era1 +4fa11aa49d6bdd34abcff3b98475b59fb6ff04309a26142b5a7ebe5ff7dd20a4 mainnet-00714-8979cc56.era1 +efcf6d2d45ac3fa094630cf95c03a841956002005fc61a8e282ac6295054c1e0 mainnet-00715-1a583ca6.era1 +f0735f3cfe7e989cc47f4fe90bfbce7f8825fa0283239b0678a0da00b7003595 mainnet-00716-4850905c.era1 +43477645f9faaf6d860e4db56b65f8b96c67133e6833bd7914f6b5efb09210aa mainnet-00717-f510edaa.era1 +cba1ea26f8f8bc454bbe630aadaa7a1cdce945a86ef441627dae86049c836807 mainnet-00718-511c0a35.era1 +11b10200113c145e9c396eb86feb639d753f16c8ac27c5bb568e06d6895b2597 mainnet-00719-14409e14.era1 +4e7715a873ab9019ba4b3ea66cff451602b4764c151a94df5556a70b77b96930 mainnet-00720-cf91e8a7.era1 +5e6036eb5134080ede07fb7e6a731eec2e5e88f220b54e0caffa8fbea6442f77 mainnet-00721-aa869587.era1 +c06d8a0d579be2c487591b9e7e068bfe3b2348ee6e38ea94eb096202b4f79747 mainnet-00722-96e85fdb.era1 +f6f2737160322c926e85fb2dddb66ee382bfdbcd36696b864756b2c69829f23f mainnet-00723-78bf01d3.era1 +45080dca125923a0b07c3539e13dcb431851e1cf19ae911916fecdd8331cc0cc mainnet-00724-d7c27830.era1 +1a072571138c6b6dfb3fc4c89169e4b240a53c3d516fb12158b50d90e1d51af8 mainnet-00725-8859c97f.era1 +47c32c2917e15ed6775575b9ee53139abb572a04ce3c3b502659f5cfeca8a506 mainnet-00726-a705da9b.era1 +aa884f208d403cacee04f18685198ba1b049e951ed9d4c3226c02286e98f0ed8 mainnet-00727-58d83126.era1 +0baeafe4a53271e53c5d745cce806aba8d169839df6f40224a1bb0b924012c32 mainnet-00728-2daec931.era1 +ac3cd8a41130765fa97b54e59f1b61c8f7890c0b6de0ff7bc63e5b960d329888 mainnet-00729-fe141ac7.era1 +403acdd0eee9567602d404dc27aa0aa43dbbe5813e9d07c33c6dc04542fefaa4 mainnet-00730-4ab51ad5.era1 +81f2a1dcce251e910a6b2616235ac1c9718c4d4dee628430c76d4eb67e1e9c2c mainnet-00731-11b0f603.era1 +39d7e4c8d1f909dc9926481ae7b41210fc34fa9c1d3eb8c7d21b972e7f16ecf8 mainnet-00732-3e648126.era1 +a4ca4269db030f9983932fb80c52d86cbee645cf55585f8e96c8310fbdf2e767 mainnet-00733-87195980.era1 +a05161b11cbbd92e96b8734514dae7e41fb32ac53f760a5656d48e500b3eabc2 mainnet-00734-1a924a1b.era1 +757b374c13193263d38ee688ce99be309cb138aefa1382d3ee9c46fe34f140d1 mainnet-00735-34d9d6a7.era1 +e0b1341fd91ae23aaa8fe6422cca6b4136c2ae01f9d5eaad479ed6cbf7dda434 mainnet-00736-1569f88b.era1 +5278b2c6a98464ee7cf44ef2783ea83a7b797278a5fe0dd37c29de437d674b68 mainnet-00737-288181ee.era1 +5d106d493c40c900f9dd36f10f7cbaf382722bf259c1e597ab55bad31d979439 mainnet-00738-28dbb0e5.era1 +ef9dc3b4e683d35dfad3ada4e18074d1aa6eda4b7d5852e932014d2a6e9d262c mainnet-00739-4bd79b88.era1 +234b110d1c89edcfb76b887e7d38b1697a4e6b477521e2dfb6d23cfff7f509ec mainnet-00740-c84ee780.era1 +454006b0104087dd6f2a855b831284641b8ed3a581fd5d22f893ad647799a447 mainnet-00741-2aacc622.era1 +36f9e4340000b17fee5a60aaa8b027f6b6b0868729044fdd348fa956b402ca5a mainnet-00742-165aa7d2.era1 +f2fd1d7559677afe1687761b169792823d1711db640bc8f4c7142b5de856fd61 mainnet-00743-4505bedc.era1 +d6259ca729eb889b9d4a877364fabccb6b6598f996f92ed999108c6e0a8f4b0d mainnet-00744-08785ac9.era1 +951d9d6d312df5215e226ff147e65ef4b9c478357b34cad24fb092c49366ff83 mainnet-00745-6ea3cb82.era1 +00cdc00c7b037f9fec4025bb005dc4fdb10d844abf664095b5624cb121d3d253 mainnet-00746-3ec58c85.era1 +aeed399dfddb49eb56ed6c4fc985de8e1239a0156e304225a7fbf1e42b53152e mainnet-00747-aaaebe5b.era1 +f0127615e787afdf46e2f0e9f521852c4925d02067dc2b53817c2e787a5bc208 mainnet-00748-5ab3b222.era1 +e618e9d6c36cd81075adfd4720f08decd11d117dc729b486e374ba675a1f8fb4 mainnet-00749-27aebfb9.era1 +69ae574192e47b15950c6bfef28c3a35bd96c77c6d461f32e6648cbec47a5994 mainnet-00750-cc08abf8.era1 +5184e0347b27b7c2846a7cc9d8d56f58d1c868c0ca8c750f65d5abf7a92a7db0 mainnet-00751-032ebacd.era1 +73dfcd6412f043002eeede1e22b59fd8e7c2d38375105fe48e2f60f8d2b17aac mainnet-00752-f09c12bf.era1 +af231b7d2be6ca761ce05e0839242505d5ecd15e363b81bdcde0cd2c99326c19 mainnet-00753-876733dd.era1 +edb3b762bf7f179c9415c0b4d9d8b849cd01df99fff8d72350e3aa530c726649 mainnet-00754-f50f5eb2.era1 +c96ad2aabd39d9cb790f32a4c2e9aa24ba8877ce3489abbed79cbd85d261bee3 mainnet-00755-4d52bb12.era1 +5bb0d284e04c58b366933c7ad93b816b60ec6774afd18155f19ec148af677b87 mainnet-00756-0af4e42b.era1 +8cf3c848bb9ba0f512bcf211bd89752193725c03ce60c077d983f82d72da83cf mainnet-00757-d1988f79.era1 +0aa584f390cd3e83d11245e62c0e1f3edbaf89a7b4166f929598f932170be0ef mainnet-00758-c66fcb07.era1 +f4ad0b6868a576f6e670960aa670883d0691a656545c6dd5756dea054684ae66 mainnet-00759-a6242a1c.era1 +faa95dcaa6cf362bff7639586a79001b601b0d2c49e548c2b77eed7f04918a63 mainnet-00760-824af748.era1 +b8776c5583333a6bf3063c06c132b39c363099b3351c7bb44bbb82914cd15175 mainnet-00761-76b41584.era1 +c29deb290cd84e84e59bd86974ae049bd69aa283cb94da57e385c71fd8b895ce mainnet-00762-b3ed5869.era1 +0e0cd6eeb32a3320122073f1ba597f9600d0f3abe0444e7e48a64e92824697da mainnet-00763-713aaaf6.era1 +d85121d80c07bd88b93ee21d95136006971523c36dfb46925254f14775e50354 mainnet-00764-3a6de7d9.era1 +72ef66a9c979eca04f650c54cd1f334bf94a34be6f0c287235960baa10726a8a mainnet-00765-18b5d602.era1 +20cc4439cdb54ece9650382d4360834c7c2b406224573f5a09f1683d6c77bbb0 mainnet-00766-6b5ade6c.era1 +e9a38db7edfe16fbca16a7b44e304bfbb7c7f6fbf230a77de534a380a37cd20a mainnet-00767-a0084763.era1 +73f882b64246e774201d47b5e1f884df1f0a0f8ef92916289292a546b74534ff mainnet-00768-8dc563b6.era1 +4fb15c76d90da3a64468adc30ca86c984fc77f5cc872193d4837bb58033c0b7f mainnet-00769-efd6696f.era1 +7cd9b83f4a1e70031835ae489fba16fb9b117cd00368a6587788b84715d2697f mainnet-00770-d9db47f4.era1 +e74a69adbaf2fcc03ef8effbc27ee37bc6377b2900a8aac1e689ff7634163e55 mainnet-00771-2aa70a70.era1 +25e786ec9a3ec2697b013da805d8e9f4099ceca0cff3c3bf54c4a8dffd242ec4 mainnet-00772-44dcb9ab.era1 +987a384d69b0c8f45108efa12a0a69641d419f50bd5ef58b4115eac0ad4f7a0b mainnet-00773-e4dcc8e8.era1 +8cbd5932732dd2189913c32d567a0c3c3bc7cdbf905f021c19de90e7d747ae33 mainnet-00774-63f82331.era1 +fad610eacb2c1fda9951b16da32c499067c2dbbffa9089affacad6cef75eb1a7 mainnet-00775-f24f282e.era1 +655a4fec41c94fe11f86f7354456de04806fdd2e6c101164617d8bc4dc833580 mainnet-00776-382525f1.era1 +76c4992a3df40f20c6db7dca0107b76805c5f823c26655e74ea4d62776c68f1c mainnet-00777-2d3a5e7a.era1 +d404fdbafa7f116cdd0dbd659eb13a4ae3c495ba31a2301034e0369178b5cb7d mainnet-00778-9a24e966.era1 +23cbfc6d257ac6111ce234a1dc24621e63f3a64d2f9122a69c366bb320bef26d mainnet-00779-0be7242c.era1 +2d3ca21e39e058e9e0fbdef9c8d6f2ede1c729e435bea0171607928b6b5ea459 mainnet-00780-0b8c7a94.era1 +1e5cd634954cb5af5c2026c8168cef07831ee5cc1a354b84c8b528e3a27ced7c mainnet-00781-6e9ae080.era1 +5b39d1a2fdfaac1445d5527ba604911c5dfca84e56ba21547c3d82f3d71ddae4 mainnet-00782-e888e6c2.era1 +eba1cf5edf3a613187567a095dbed18573b7693d84064ee8233697f46cd288a0 mainnet-00783-03027295.era1 +54f8cc69cc158450a31f5e10945863651b92506b6c9ee0388efb3bde946fee1a mainnet-00784-b6efb516.era1 +266195977db2fe1a4e990f73b85b91a15201b6d4d34259dd8398c0d9acf28f3d mainnet-00785-79728b75.era1 +7d14175e688c263b1f3fea0c78b1d753ea8edbefee30fa088e2b3db5a34ac0fc mainnet-00786-0586eef4.era1 +5c848a25b0e382189d63b672a3047f457c0da1f29d7398279042e9ab07828daa mainnet-00787-a0d4c8c2.era1 +1bbc2702d2afe8fe0196739912657171694143266120dcab6a8b6f4eae4a9b80 mainnet-00788-a55feb48.era1 +2c6677eac63b14228a79de2b470c0e87554078fd65ce3b128bc2d3a93e497a9b mainnet-00789-9b95d3aa.era1 +23c2b521567f7faf280ad5c396fe6e300d966cc70c82a2f33495444b55bb06f3 mainnet-00790-4a62280e.era1 +26a010a5d04321387a16aff3b20a38d85e42d40a327610ec7fc5eff8ecb45d23 mainnet-00791-064776da.era1 +ef387e14a6e19a36cb30e3057df93d90680296e6db33f4e6f1d2ca5179d847eb mainnet-00792-e9a29542.era1 +06d9b61a90665ca14338bd691f130cbec6fcbf17076d531c4afaa08eb1dc1899 mainnet-00793-93ac12ba.era1 +6f9a483d559732565981e64d9f30696e9ac41a2af3a60398fcda559fa1b88c67 mainnet-00794-b199a309.era1 +3b7a9aedd26d7c5edb27afb1db0d876cb00ab7f954e369ce6293ffa4fb464fcd mainnet-00795-284915ec.era1 +f352e9f87838e6e16e1aea4133c7dafd5f1bbca965dfc10ffe844a7af8083e8c mainnet-00796-80b8a598.era1 +55f664a64de8eb2f391f3347ad306dcb35650144072053c39711443956c19c9b mainnet-00797-40e902aa.era1 +44650700eb2ed69c8ea01a50ea3d55b88982011ef2ca161bb3ef9209fb0a05a4 mainnet-00798-25bb39c7.era1 +a2527eeb080e317933c7ae3d15b5617bb51eb2ab60444b264d16787678f5a084 mainnet-00799-565a1bf8.era1 +c27e810664eeccdf7611104508fdd016255ada12d8c9e2f1b0c275f684f6bf1c mainnet-00800-6723d6d9.era1 +576eb0468f152ea74036e597cbd655c4656e177cfc9d4c21b731d381e54eec7a mainnet-00801-2c9057ba.era1 +1f0182c783567efd7b08b8eb00955e2aac20a00047b8de80a7aa8518e993b422 mainnet-00802-75e347ad.era1 +3ec2b5d0bcb227dbfb350665400b7668bf31b990307766f572d303620acabf68 mainnet-00803-afb8c20f.era1 +516955f0f2a8cc35bc4bf0485bbe7cabcb9325f93abbed372d9d48d601032cb5 mainnet-00804-472b0432.era1 +c193068ff829f0ce35841c777a390750ce40f069e9cf948bc8aea96f6036810b mainnet-00805-a00ad773.era1 +8f11cc6871e56c446cb3fb797808f2908de4ac225eee56ec069af8e858202fa5 mainnet-00806-4fabbfd5.era1 +1c5b683781c57d0b4117b92c8204e8adc1080cd53e59b7aa16629b5b987fcdc5 mainnet-00807-ebadaa84.era1 +755a72dd1700d49a4190e5fb5d4fac7cb39c7a06531ba16738e8408865ed4515 mainnet-00808-71a5a039.era1 +1e880154f791ae3fa004c93f10f460a67055e98eb8993ccfbed1616596ed5b63 mainnet-00809-2a78afe8.era1 +2d0af55bbb0fe44298351b997063d7f225822141b70d56fb7835219f90e4a805 mainnet-00810-ddc91137.era1 +64737d4dd5ca8dd7b678d5c5e6a5d6afc21afa5403810e8d9ed9068142aa3e2e mainnet-00811-47305f69.era1 +b1eb8f4c86160122ead51c906f295d231ad0966f56f54f078ab5a53925af1d2a mainnet-00812-6492e812.era1 +b3c222d370e3ee3d8aa0246b763a4105ba639c99506f2cb465244e384cdad618 mainnet-00813-9ccf4c43.era1 +dba81e8ae00885ba63625f28e5ad23cc4de38fd8cf70bf7d39c300eb4896079c mainnet-00814-02f878d6.era1 +de85a65021d240a9babaf9119dcb8733f8856b69630ca513f6d01be42b906883 mainnet-00815-e97a4d00.era1 +aec0e36c9eb751b619607bed0dbe90c1ecae0baed021108bdb8875ca16687969 mainnet-00816-13c60a90.era1 +c43fdf019869fd29803afb9b594cf31c287abe5df29a5729d91a8d19b7ad463d mainnet-00817-6f8e5552.era1 +156b02cea4489b1a74986a6a96207c49e769cb9fec3f3623254cf66d15d9bdc2 mainnet-00818-8143cae1.era1 +6f7d16fd1fe7ba3287373b882673d7f8de1b3caa83a979449cb8ad0ca220c7ac mainnet-00819-3894e28c.era1 +2ef10197e0ec1f076007e76cd0044f6012e9478bce455e1c6ab27af5b28ecedd mainnet-00820-97b69aec.era1 +2b571624e519694ac946a8963cc35a80843d2a22fda91476fc430e6323fd36db mainnet-00821-22732c18.era1 +274fe58e0bf337c9af2e66e61e7866937fe5290e5c05580fa0443bb4ba342765 mainnet-00822-6977fa0d.era1 +d10336ec40c300b2967191042dea4613c7ce16d114962dc4245f70391d2cdc2c mainnet-00823-0d164934.era1 +0e7487d0cd7a9273c6e4adf98b6627672f97acbc540f4f752b1f7a6ed5389706 mainnet-00824-0de9eb6c.era1 +9668d2f0fede353ebe778a5916b48dd22521e38c8e424805a0020e73a09318b0 mainnet-00825-14eaae63.era1 +541df6a997cd34fcfea892d47007778a079ec92516b0a53c54b7a17dfc2c87be mainnet-00826-e0848f10.era1 +67176e0ace780fdc4c751faca06aa35b8b897de4fec7ff120cfc2cc623e23482 mainnet-00827-6846201d.era1 +ddca6498e8f12dc4d15a2f0592a7dcb78681f6d4d6ba2db1508bb021c9d76dea mainnet-00828-e89c0e11.era1 +0b1678360a7b7a4d269123cb054c9dcab19eb9e21f1fc7ec4d684076b243556e mainnet-00829-3096af76.era1 +be8f8693a8f699e6b3d3de90bdb0fd907bd9ebadd424e5b7ee2a0e525ce8aa07 mainnet-00830-55e8b4c2.era1 +5d90ca3043f67c4ec0c16aaf656dd7675aafce8146c9a119c7d2ea7262b7ebe5 mainnet-00831-c501837e.era1 +ff9c8780356bc7b44cec3ec9a07e07c1e5c102e0408e3a03cd52ab8203e59c68 mainnet-00832-f7000808.era1 +777a905c9c451f682e8902689ae81d244b955b5ccd7166fee687ac65299e57c4 mainnet-00833-5d73b97a.era1 +0d09a043f487b768767080a7cb9a2999e0afb6f766f1eb17017bd1c2e735129b mainnet-00834-f240decc.era1 +5f7413b54fe8b908d238383ad8aa53557f31d244b3e09835305e89acdcdd700d mainnet-00835-fbd0a368.era1 +f9604ae67043945c772d35f5b354da6027810809745e869af295d1b1d9797136 mainnet-00836-fbd2c4fe.era1 +7e658a4924682a76f2e9b66a2b7cf38ba4e7189d06f8633380c28921787b874a mainnet-00837-8bdc3304.era1 +0c4b2d40b6f42990ab482febb32d7c118f4a884079860696ceed02b5061caa78 mainnet-00838-2a2047ce.era1 +b56d1805a5a2f536c44269940db3fe5e7a75bc5594fd5f2a704ff68db3e60535 mainnet-00839-84c88273.era1 +8efe7fa27a8065153fa40f6363c99104ef10b34f5319c9f480c0f167ce6f3021 mainnet-00840-b96ae20a.era1 +1c3abd6c87a8d771905b6faf2c536241996da762d7499b9b47c04f59baad580c mainnet-00841-5cfe6abd.era1 +9fbe25349e52af63cc08d8d0aa091ae401e935fcb18cdd1f2fb1a4e3b0f444a8 mainnet-00842-b7d8f3a6.era1 +349bb1550ac853bf6fb334c73d6096d94c501caf0f5ffb3b997d0c2a690c7bb1 mainnet-00843-c14d7f51.era1 +825bdb0e4dc314a975be7ec16975bbec2a494c56a8dacb3a3f8adb38a2d72702 mainnet-00844-c5eea731.era1 +621fba64543fa22a6f7d50092f0da5f182eff044cc993b5319c6be4c5f5aee4c mainnet-00845-aea57615.era1 +dacb6a1bb588a2b093473df1061457fb052a703f97dbe77365cd3b5cb1e8bfe4 mainnet-00846-be228f4a.era1 +99c926962e17407596982c55cd9034d5aa32cdecdd9fa24ad9bfccfe2c01ff4a mainnet-00847-5721c5a9.era1 +d1bf985348255c72ee123835be2523aef37106ec34e9d3555f821f647d2c812e mainnet-00848-4f17a7ba.era1 +3dfbee47d98740194164a1da35f778c5e2de41249eb5716fd8b1959a0bfdd258 mainnet-00849-81731fd5.era1 +cb89b3cc922fc3ca0a3634c88ae899f14769fb555af4e93df2bcb6c08a313970 mainnet-00850-c8596aa5.era1 +a5374105029f37804a25ff1fa5cd6fbe17e1c7c8c230e5e2da3dc9926c329395 mainnet-00851-d9d76d29.era1 +69d5fceb5bdb30f0bd0c02e8c7c01a9a06e3603f496add26cb41f02f81d72bee mainnet-00852-3db9cab9.era1 +1318265fc0e49c9549289db1583ad004e718411e8f959d590c3269ab9d58ac60 mainnet-00853-05eacb14.era1 +32ed53a95c655e7ddf5cba1b83f8b06a3ea0f16d9598f747371d580f2d087e53 mainnet-00854-78744453.era1 +3b424157848486554e6bad453e338dd7a770c825464b65e7fdba8c74d41346a6 mainnet-00855-b042cdda.era1 +027cbd4ae8b99476b0c4ebe2a6c164c3055856b7b2c69d0ad1afa8b73ee0e988 mainnet-00856-c3dd963b.era1 +72a2f7309c80206e90129356a0700eea83359cfafea4eb724c1650c139b9777a mainnet-00857-1caf90ed.era1 +cedde5c9900e8bac7d6855b70aebd43094c1ea39d36f0769936622e0996445cf mainnet-00858-7d89c8e7.era1 +e528af9ce07553334ec61b00251670229b142d151e3a5ed87ff8071ff1efc30d mainnet-00859-ec9f0685.era1 +8718ccac4340a9427ed167d0330ff341ea8b5489b4a0d65b6194c50eeb001401 mainnet-00860-608f5138.era1 +7154dc222f0ea9ea1bd075bdc565eafd2a80b2e8d0f74c2550c0068ff04947ca mainnet-00861-bc713924.era1 +129b6ac7dab14ab7ed58ea9055d4e457d2089b356870552cc45f92fd0e384e8a mainnet-00862-f5d2654f.era1 +c4892358ce1ddc2d27c6575534201cce78bbde93bb4477656991b5ff6bb9cbac mainnet-00863-29f0d1bd.era1 +dfe0378cbc98fd5e150e9bd4d9b483902b86393d6de48c87cfb7a6f4478e6e1c mainnet-00864-ba09ed24.era1 +d8e572774bb74383b0b3b67c2390a7752a1291fe53bc503b8cd4dcff72eb0e2e mainnet-00865-ef98f25e.era1 +85fdb6469bd9861605ede2bda3da71688ec2c7c9794a85957845b4a0541fc44e mainnet-00866-62ec875a.era1 +4aa0daf8db04b5f98ea162fbbe21e5b9ebbc4201ec48f14ffeddf9796bf05d32 mainnet-00867-6e38f92d.era1 +f822fa286ce4f488fe2fef1dbd825ef938c375e7a730fd2386d735d4f64f5f96 mainnet-00868-ada339e0.era1 +475a67c9e88fee4153749b7b508ff6e04956786b5808c579597d3ba307a59d25 mainnet-00869-687f70ac.era1 +7eeb7e0e77ccf7e4338c71ed5161ae89ac45c16f6a81c2c6ec7f40d56b5cbc9b mainnet-00870-69305b66.era1 +b87586f0529bd9ac75cadf5872d01e97a128bc0468ade8177975a5f75c9c6146 mainnet-00871-dfb48357.era1 +e1ff274cee47442d0f6a3196f981ebd336279c9ec557a7534c29fe3dcc970635 mainnet-00872-1fe0bd68.era1 +118ab69632ee10241e5713c0f6d8f2093ec87925913718e004d3d9b8ad4efbcb mainnet-00873-6754774c.era1 +84c7fbc68de4a30337119a8352fa366afef32c63b6e3e6ca6860c904c82552be mainnet-00874-89ed1e98.era1 +390950d9c6a9583c14170f406278c32300e62564c8638b4c4933a35c444e407d mainnet-00875-918e70e4.era1 +bcf535b017d33ed06f70ace1300fca8eda94b078ad584f8e23ef90b2980a9966 mainnet-00876-b174fe36.era1 +c672c2865f7edcb16a11c7799af76a3ce654810e0fddb936f429a3959f582b63 mainnet-00877-1ee06c60.era1 +9d280918ca66cb1b75b6ff39abc1953cd866d40e708be09ddc29c8c5508f6c11 mainnet-00878-acf82dea.era1 +4025b2ff2764c1093b7b556337238b4dcb79ee20330e884918b9d0d159fd4a2b mainnet-00879-194ae199.era1 +e28cc9f4907f77f63c1e2098e3100d3bb5a9585a553adc860e31fed266e826a1 mainnet-00880-88dc77d7.era1 +48c57783de2299b511ad9b4b3b1cfaf24d5b0e864f82ab0dd620952dd2000ec9 mainnet-00881-291ee430.era1 +75132edf2ac9f031bdb28dfe8bef1fc628262135b2d57cd70cc28c1257fae015 mainnet-00882-e0bd01b9.era1 +4c576b4ef7eb6ee53792c977fe7e73d584efeeedb968ed005ced2356f2babb3a mainnet-00883-24ee5653.era1 +9a3ba18e1794c2d088c6314b9e7829358d8a6a7ad54cb04a6d0c7b7665efa57f mainnet-00884-f4649173.era1 +3164f52873eead3c508a80e297071ace74b7471209770d0eeed4b5c2dc60fcbe mainnet-00885-18be5807.era1 +fa98b03cf7ced8a5e106f6cca26e0f91a1eba337f55241952a056d47d00c65a4 mainnet-00886-7b8e7f87.era1 +de1d232a777550f9efcd6d42d03aea8cd8a8dab7234989a5a5f8f0e6f3df3baf mainnet-00887-ebfbdc02.era1 +dcbc95e80ab179d461aa8fab2fcd1a47f3d9160be1934c7f1dad4e67e8497015 mainnet-00888-2911ec46.era1 +ebc966ee429a98abc65a1acc8bda0cf89846e908fad41229d133fee188e8f0c7 mainnet-00889-7bc66f0a.era1 +e1d6125e7cf2b1058e3b2897b467be1d4597855f52732bca87c0b8e67b386de5 mainnet-00890-de4cc0f1.era1 +137ef3280a18c42f45a0e5b7a75ea5613ac9e0408a7ff89451dbd9e089d27a1f mainnet-00891-1aa1a5b9.era1 +efd3e89f05487d7577a02ec113d6d783ffaee002823eba325a3b9221f3e2bfb6 mainnet-00892-b5354846.era1 +6fc953a47233f82b6448c9d62de998aaf4de47bc86baea1111e3673630474181 mainnet-00893-75d76eee.era1 +c4d4b4ae18dfdba3c50ab34896ec4314853526019d7a40af437c96c9276e0e77 mainnet-00894-81aa556e.era1 +fec02056e20385d7a07b8d138d6060c4e034f38e42e1b70eb2b0f482daf35a07 mainnet-00895-47043527.era1 +a21ab7e602030f9f66efe79072a3cc41eb08df50b3fe780f2a3975195e4e1a99 mainnet-00896-1ab5749f.era1 +bb6e1cd24d035f6ed54c378f1f45d5b8266980309ea97e97b7992f2d4be7ebc7 mainnet-00897-b81f63bc.era1 +7bad241161c5205162bd908cfb3a4a70fcd45d14422cd218676b153d9765b121 mainnet-00898-ac96c902.era1 +fd2a6b0df6b2af60e1b85fae56526460285c589b008044144e0d1a2be09850fc mainnet-00899-922b1cf4.era1 +d24f52be2d507c65a11179fa7f8792e892ee2f0d50bdf798b03a190cbd807195 mainnet-00900-0f8ce285.era1 +4d66f4166981e486be794971c42d27251b873681f53c1be0342cb2b2219fe51f mainnet-00901-f6f06a90.era1 +46291a80c3c36d692c14fd2ad98324a0c1799fb186b7d7a46f17a330ebe0c5f2 mainnet-00902-65bd2e95.era1 +ab933ea7198ffbbcfb8eecdeb5a4a79f38bfb2175446f7f0e11ff5e46a86fdbd mainnet-00903-3b916edf.era1 +bf50a0862e08e65b515bb34cf467ec2f1877011f91788d5da85af375c5928125 mainnet-00904-2ebf7c0d.era1 +9fe3d97ed06c05719278c081a0ce81fc9b90b60f4364517cef837e02bd1aa871 mainnet-00905-9e7868aa.era1 +ada9399b7d3cb60516a05e8e2f8e82f53d67e50191834c3a07e29bfabf32bca9 mainnet-00906-6fe8a5a9.era1 +59e1bb4ff7461c2613f75d9b8f4ced46df1090fb83073b4ecce9200aab342347 mainnet-00907-fc681d3f.era1 +767a171cc735352d2a21d48c0acfc59094617f850ed0a8814627cc0863e8cdd6 mainnet-00908-65f73397.era1 +121d6b676ecd1d763e18f8569049ac47b615d6ace0d5beab85ccc9d5b34c07e7 mainnet-00909-38ce4f9f.era1 +8e0c3de6b2ba02de171f9fc08ffcfedbce4c5fe31115fff19d7c4088706887b8 mainnet-00910-d44a929f.era1 +c27791d691c1fffe75344b160b441986e09261dad084f892663c2594b1316161 mainnet-00911-fc91f464.era1 +1b7a3b002df8b6181573d4ea657abb3262c62bb3db209b96132a0252e5e38861 mainnet-00912-9acf8a7e.era1 +eda9aa0912b50a7f9cbcf16e7f452bbca9460dfccec5609f38ff854316e48374 mainnet-00913-07907337.era1 +571a05a8c5c25983a338bd9cd3498cfa4c60a8c671bf3817cf1c693cbc80621a mainnet-00914-822dfb1a.era1 +5cb2b9593e4677323ec2d75bb76c3d9f1a9675e1ec61872f4300e890e646170c mainnet-00915-62602cad.era1 +63e4fcefebebca610aea69c2210aca0f58882e0eca79f8c6cfbe5e29436e0295 mainnet-00916-8b6dd223.era1 +f6c8b5fb2a4e3a963bed34909902bf4584f85c3447bf545e24b0e6ef8e73aa57 mainnet-00917-b309c469.era1 +a95f81ebc9e973ab484bfc2d3fca9585641ed9e1e334a94f3c3286b426175441 mainnet-00918-8974583a.era1 +192024c65b05a4d2bd1042fad07b4e61839e672a1e81a52773fe974c365be55b mainnet-00919-d139d7b4.era1 +926ad369eebebbb0d7a40aa54f15d4c2cda4c9c1fb3360504233d2e8245d8747 mainnet-00920-5b83da49.era1 +efc26906d93119cc50eb15775fa8978ae12d6466ca140fd1e2f62c0e051c8ec1 mainnet-00921-7a62f3b8.era1 +0b88d2be1ec743ffd1c2ffb962c6523e4d5fef009a201280e6a9fa5890085dc1 mainnet-00922-30f2071b.era1 +e5fc818ff8a23885d56a380cd6e9d7e2728b7126ee1362bbe5b6633ccf8e1115 mainnet-00923-9f05e151.era1 +9b1f50c79fc88f347b5a974810ed75cc9ee256686135cd57dbe943aaaf89dd5d mainnet-00924-8d0da501.era1 +76b98352808b1a455c23b302047b15fca7fbcb9a551914472302850966e4113c mainnet-00925-9de1930e.era1 +7ca90c759ebf83ef3c983b061032488895a0b134f62920f55d16a311ce087dd8 mainnet-00926-ac17e41e.era1 +e4d9a82a541fca599acf86184fe54b6e949ee6c9157f94576f3cc097f86c2dee mainnet-00927-ad6865ca.era1 +b0cc10fa6661cd4dd7970ca05c098358d671b74767cef13b46c6a3c7b6a23785 mainnet-00928-3feef311.era1 +5f161190ca57d017f5a901d1fc17b510fe2cb06a9463bd1c53a38977ad2f7c29 mainnet-00929-6e2d1f4b.era1 +df1fa99bf21a8e99fece3e1d5be69f1d87e6bdfc6f4f2ae164b616a692c672fc mainnet-00930-228684d7.era1 +8dc902f268d0879ff3dd2ab5311b6770677af4059c9dcd7ac065633b7a619311 mainnet-00931-2485471e.era1 +22b6ac2db24272183afe4cb1c082765b5fb6d612632fba7f5eaf1abdf5b1a325 mainnet-00932-f2f69e07.era1 +91ff864c90169f8b32be834d74026cf6e661d65b4b8f4833a3d06d27876e46ba mainnet-00933-44eff5ae.era1 +b870a45ce77aceb77f85f8409f2d9fbb5253852d2b00e4832957b94680d1e4b2 mainnet-00934-ac8cf5be.era1 +ae5fe657129b44a16a75fb44b9d677c0b0491a6eadb26f14cef08fa8e1c5f710 mainnet-00935-c052cdd4.era1 +160287eece33d1233317b47d943692a55c3f1a1f1be7f74a30a9330cd1de4cec mainnet-00936-0d1ce1ba.era1 +254785beb70cf70428f860f264c6c0e6acc3a1dec54ea55abdc5446675d6ab86 mainnet-00937-20d08362.era1 +f413c0b34ea5ffe18cda24b6c47dfc331464946ec2d823fa69fc2ad930acc265 mainnet-00938-fb915a95.era1 +b4420ab136b9747fbbe43d40f6877b95b630576a0486efbb4bc6c39c0570a999 mainnet-00939-2d5f2cb9.era1 +9674ae27b0f8066f265ea8b4a3eb72bceffdd519a85f5c46657889cf01966c6d mainnet-00940-31a360b2.era1 +a913eb68f537131b434eb6dde18efb3ac7f7cf1cb1f6d6c734124e13a81aedf3 mainnet-00941-6247e765.era1 +a12a0a421cebf730486a4fd64e229cd7db1973bb9333ae001c12cc08dff96430 mainnet-00942-c352aa92.era1 +0ba8f136af56f8191059c1ed11d4746bb3cd0089324d6e17631c5e51d5e36ade mainnet-00943-b8b31db9.era1 +44fdd29605fa3cce277d80ded2197aa897d8f6ebe1d3710458ff3636a02d5599 mainnet-00944-2a8be083.era1 +369f0ba252ad601864d9892fbdbe4bb6566849c213fcf8b87c690d14f61288a1 mainnet-00945-80961bb9.era1 +87d56afcd024694fd0595fed8a6c736ef1bfd9cae4a439698f5713adff6e9458 mainnet-00946-caefe64b.era1 +aabec04e13481a949bcc0e702dc865d4ffc3059886aa31bf8b6853479fa8ddbc mainnet-00947-ccd88aff.era1 +fc8707c984ec97d7cf1dc937c8ac1d4afc655aecf05ad523479f4e65d187995b mainnet-00948-11d91c25.era1 +38d4814a35cbe059eef3e6d7e66c6f05b56406a1f66c344b04fd67c5b0d6bbcc mainnet-00949-209f845f.era1 +2ba49a308d695c35548b8935ac0c224bdc656db3021cdd4722d554a53fa733a5 mainnet-00950-4a9dede7.era1 +26face4ba8260c69ffa7ec6efce893455a0fddda9ec81e702c821f1ba11fa1a1 mainnet-00951-6493fcf8.era1 +1e78004872cc834c2d92b4b9daeab61277c85a8e0bd135b15e2b12268e72f628 mainnet-00952-611a2c46.era1 +d43cdff38cbe428e0f177bee43dea616c407df0ffc16fd31f32d6c1b962fc20b mainnet-00953-81e7d017.era1 +7cc671fa8a9cd067a420eec0251690d5773937ef01d6a10428ea0fc30ed978ed mainnet-00954-19895bb2.era1 +cd24f28447005cb8c014e30d429ebf005e05f31a6b64c4071f81695d05ad160f mainnet-00955-443f9153.era1 +64e0fca3590d4c5beaac462033a48514345f7292fd1ebcfb690c79a13c0aef83 mainnet-00956-3dcb543b.era1 +12d96b92e2b1b331583f5b520d38b1b2e65fdb24cc4cbd586d5e50eb95bb8b4c mainnet-00957-a2d2aa39.era1 +e6092e6ba5a45be6db818b86cf03d63b13e7718ad02b8815862f59263435338a mainnet-00958-24f3f044.era1 +4113dd29011e08d6ea70e08712d69ca98c99cd77788a45839b6739b0f67e0ad9 mainnet-00959-8fc647c4.era1 +a90611c78f0fe1a3f460c8f11ef47c5357677cc02381441d231ee5ac07430b6e mainnet-00960-680241a4.era1 +66727a8881ca4c719999119cbeb6324177ef95cb8063b92b4ec59917ae37dccf mainnet-00961-759690ad.era1 +cb3c393d014ad5b817113f3a6ea5e6ded53df7db2e3830405ecd52463c028bcc mainnet-00962-12b37dfb.era1 +91f6d92eb6656777199e960af9318ab4c5c994fe582d38df8cdd40e6270eaf66 mainnet-00963-2b4168b7.era1 +731983296d9b8ef3bfb2b5f47b709146c22f1564b9057135a85dbca51c76c52a mainnet-00964-a7136656.era1 +b93b2f350ed9e3e1fd42dba9aa328fe3abe6122c1d421c3393ded798c6e609ca mainnet-00965-8a62b405.era1 +29d762425352d04e8b3ed89a9d0de9994524af5ca224cb1a574feb575371c6fc mainnet-00966-73e45b2c.era1 +227426426b25320eb31e6fc556914e27f88bab1c0cf93c3c7d3450660f1a7e2d mainnet-00967-39942459.era1 +f5c723924ca804105153c00c2aa9d5e38a67499246b81c6daafaa8424006b12b mainnet-00968-dce8aff6.era1 +08f52f2cad8b332ce802a7b43ecd0667770044e327224e877dad3ec9593d98b9 mainnet-00969-49be4756.era1 +c773e4c1eafecf30ada6c33610525efbcd8df74696b9d169794648620adfcf01 mainnet-00970-68c57c6a.era1 +9723cd64a4d5a894b2dc2952d1b264cb6dd96996d4e1104b68b0a904a2c6310e mainnet-00971-c4f6b046.era1 +85a606fe8e462595bee1085c3ab7f587e075c7f8b1b19a0f4bf4c9320a43968c mainnet-00972-13a7fe3f.era1 +7bc56fdc61c327d7d4e057b802290dd5f10c7d76d31cac193513c78f65da9ab7 mainnet-00973-135d2ae1.era1 +4bb6a1e85bc267f1b72d91f79ac7f66fe4df1e53205ad22132d3187763ade96d mainnet-00974-922bb3c2.era1 +30d549c01c8a292b7321796d4f3266f2f7e142e5410c19eab04b6a444be3f0ae mainnet-00975-27b6441b.era1 +17f8b5f4a153431f82836afec6e67bbd18eed207911aedd74862041feadaef34 mainnet-00976-63eafa02.era1 +55c1230e48a49eb9f50536f5e7b7c0d3ab1806066c76df8af2cd75ce0fc7d8ef mainnet-00977-05bdc732.era1 +9f4bee6308808e08e34188ee93a77fb167221aba4c9793f4bff87fd0162b2694 mainnet-00978-95d7750a.era1 +99f0e262ba3ec8ea549cdf0e15f39a1b36e69593b3ae2c70b98a584ec54333f8 mainnet-00979-8cf0d624.era1 +5a03ca9953ce0c35e51a25bf84f3ad95b9b97d736db2af7a4a2550791cd73891 mainnet-00980-a8768f5c.era1 +63b7809c76fa5f72ae92dcd42eb2f5f9cd7a24a2fc7951ac4f1c51e840360fc2 mainnet-00981-c4e8ddcf.era1 +8f4bb2e28111a26adeb4e9ee0e76a85b46357821c44597a12342f23303eac05a mainnet-00982-d4ecd7c3.era1 +c434b12269ffe378fb5bdae5943d4e34eacce459d934ad3c03490d1fc320ba81 mainnet-00983-d39b640f.era1 +386a66ce7a9c92fd26fd45ab3d25642c15ae9054d20cf82f499026350d13563f mainnet-00984-22592d52.era1 +9dd47c3592dada95175b944878e6b671d21046c677ec334e7e8dac02b08ca2bb mainnet-00985-e4e144f9.era1 +e5d7bef7c30a51b441967ea6df0df649c6f4d449c5a6bd9f07bf37366e2daa37 mainnet-00986-b5e7db59.era1 +e47c38f443c8a73c304cc6e04b73e846ddf2bdfb6f41218b9f0fa8e94a4fa26f mainnet-00987-9316c767.era1 +5fd0592e9cdc55d79992c274174b0f85b7ee5c584fe536398cc47aa344104b61 mainnet-00988-871302ef.era1 +4d42c4a88e1af0974887663d9227e30b9d5be4a8d988d70170e0a619ea2a6e2b mainnet-00989-1c3a7616.era1 +e6b3797997eb717ea44a6a9fec06ed1728cbaa97f824b4027bab6f2524d61d95 mainnet-00990-a666b389.era1 +61692c49a9f2a1df764b1935198f2284842ab57b921b765166e45e0110bc1ba6 mainnet-00991-3154c955.era1 +7dbb7e563b116d322c725eec6bd232c57e6350c39325ca287caf9903c1b3112b mainnet-00992-d078def2.era1 +d42a759bed529201dc3b5246c6bacf33db8347573c3c60bf869c26295e36d3dc mainnet-00993-6b2615f1.era1 +aeeaab06a62c01aa287cc2604052f5779303d850191c69ad445d706c7f5fa676 mainnet-00994-6ed3f90f.era1 +f8c136533ecf07678c9f0394cfea628e8cda6e4e17758b488cf953691c2f3d9f mainnet-00995-d3223ce7.era1 +3c13ab210843321c30caf84af68b628ff4345a1fc0595b1f01b32417842c4f07 mainnet-00996-016c769b.era1 +158517c5ff036852a86cbb659cd18ce59e01c46267a55b4bbb16329ee4703493 mainnet-00997-e19a4c9d.era1 +b7950092d83905626099cdb64c81bbf224ca0728dcb4dfbcc0ccbedc7ac4209f mainnet-00998-36c9ff39.era1 +4d96eaf7f23db43fc8260e00a20f89bc8d0c9dda56c0c88dc32dc127cb617a81 mainnet-00999-83c72fa2.era1 +2030ba76226df88d4e9b0141702bb29702d92ff80f39af34caae4d58feabf070 mainnet-01000-ddcc6036.era1 +ca961a71ecc2f608ec95c2d3cabfe722ba69a242c8830236121110ae8fde87cf mainnet-01001-2d0fc419.era1 +e8e5c0142ae6d8e73fcdb9c108aaafc0cc99283d7476d9f21aea1607adafe76f mainnet-01002-f69f7b39.era1 +c5e7d9785ca37ca68adc460ae649854c1d529ef8f90263d00c55386125f880d8 mainnet-01003-17632710.era1 +6791a5fb4f3636e9a7f503fe8cdebfa7fddb4835f587d54db7ea7f3fe26fd8b3 mainnet-01004-fbfc0216.era1 +9f059f87ff05fbcec84a85233e0b6a576f75a62f34f98005bf58c858fae09e7d mainnet-01005-584687a4.era1 +6f98e878cb0186b684f67d18432cf4ffc504b8cffadaea66b4b886865c764208 mainnet-01006-df8970e8.era1 +6389a787c67015adaf7b3e0aa495192a9dee8e795f73339aa8b5c48c69f04252 mainnet-01007-2fbbff92.era1 +c06ed975ced7957c3763954b5e79f86fa3ae2333439feadc0ba26b97b1613c11 mainnet-01008-235c9a91.era1 +96cad5272aece888e7390b8ac170e14556c1b27d021d5b7d17c02a75a3087783 mainnet-01009-fa09b4f1.era1 +064d7a29cac16e87348ea22c86c07b099b86f806d37ad5a8640e8f4420913d97 mainnet-01010-3cacc95c.era1 +99093e195efe2f0ebb023ca172a20bc3cfecf75d4666352f14bbfa4f693c1925 mainnet-01011-dbcb3c64.era1 +168f113b3368eb2df264ded3a88e928f8eaed9aed6219988e579e18b59f274c0 mainnet-01012-6ac002dd.era1 +eb4a448536a1d180da7a26077224ea0c70c56e78cfd3671d8aed75cc984110b4 mainnet-01013-dd1763be.era1 +f6284ecbb3db7c4579a8cf2ce0e37377f9a291f378722af69d8d854517f02469 mainnet-01014-c4e0e059.era1 +532f119847749a5b068777293af5d094f047f75b7fb30c7b0dcbb2e12625e69a mainnet-01015-d2d13e5e.era1 +acea5aa287f1cdb1261436bf28f4d29b22dfd2ba3f038a8b6ff298fec39455f7 mainnet-01016-a93277ff.era1 +39a66b30e7588c8f5558863042d90ecf0238f278870ec95cc6f92a7a705d0175 mainnet-01017-019e6db0.era1 +cfe2e6ecea6a40468ae53f0cc8486981ad3eac40509b2bdc101f8b0d69f67b80 mainnet-01018-763ea921.era1 +4e1641b989a482e9b6a70f6615a457ddd434498f7fb4389efa17b5a13b692509 mainnet-01019-45d27951.era1 +821ebeb97e8ecd4cd2e79fc64cf0a8082b38990710e39861071fbab1dc631ae6 mainnet-01020-37e8f37e.era1 +f0bec9191390d1aa416dca456adbfe372d3da587c0405893eeb3cc7fdae859e3 mainnet-01021-6654a92d.era1 +7759d1bd02558eac871d5a783d5ffd69b6f3b5635fbd20923f38ebc7c67e96da mainnet-01022-b81a65c1.era1 +1162121637cac8aca3977dedcfe29083b7807f921f74fcef01911eed7fb6c549 mainnet-01023-b72e8700.era1 +f6a87889a36ab17422c8dd9262036027ae00a30ab41b319598b85bf8b0ce5242 mainnet-01024-75f6d852.era1 +56033649f9045994d5e5a6b23aa4705cc907882b4385915e2b7d9f4820e10af1 mainnet-01025-cd13479c.era1 +f84df05b13cb4f3603f50b952e7da992e35fe229771e8831f7237006bf1d025c mainnet-01026-1ff6a9b9.era1 +0347df0ba24d97c325e6a5100c6ec1a8dd2b62a8dedba16dfedb6cdc77b1a237 mainnet-01027-6c76c020.era1 +217b9ecd8653cd56735f82ec13f9f9b80f87571fc4b25aa0073f08ec99848156 mainnet-01028-78f99056.era1 +a4938dd48c02ac77af4f70bfa02a805fa1ee24ad0148d7ff3b209cb6f2c56799 mainnet-01029-3dcb5e2a.era1 +c3ae2e4ca70f6eaa23f00ea142d0316e149ba6ad72e4685ccb78ac9dd2fa9059 mainnet-01030-373e75f1.era1 +d68a4f4659203db89c939d227357c2f875973464abae8a02a918afd296ab5a4c mainnet-01031-f06e62b5.era1 +e61a71d2aa334de5122cdcdc4921fa597fe516c78637637c29b54e14b5980e28 mainnet-01032-3f19793e.era1 +beda23a07218acf026ab4de706e9c8716b8a3ba052d867f24eb151a7de34b174 mainnet-01033-a9765c1c.era1 +05f6f06eb10562f992c10268235df7caa2b5bce994fbc9744de35d4c56fd333d mainnet-01034-1dcfe017.era1 +65f03b88c05b89fe7793fbc0ea3a1d7b403937eab092b0b63583d98c41354ad8 mainnet-01035-946b550f.era1 +74031d7bc115ea6c526773670dbf0c40cd638354e174dd53a993a591239e5cd2 mainnet-01036-b953b4b7.era1 +75279c9f4d8e6be5db40f6193afab02ef695d7aec87deeabb57aa3405785a57d mainnet-01037-8356dd44.era1 +a1e60f79dc9971780b6b71ab9ffb988dbd9fdcca2137feeb746615c1fbed6ad6 mainnet-01038-3cefbeb9.era1 +7f3629ad6d60d3bcadbc87b2d8f89ae1997772c8360f545914e5069442c8a963 mainnet-01039-4765ee42.era1 +a598e383070990db48c876eddcb5901d3d9618a3f2d86d67a169ca05c9eab1cb mainnet-01040-c559659c.era1 +8bd6ced70e2b8d7b666975a6af26916921fa567e85cce68808153c75f510f08d mainnet-01041-1b1f9767.era1 +736c0536e6a85b59921dafb87d0e6d9f1378dbffb080c2faa51d37abab2275a5 mainnet-01042-ed5a8fb5.era1 +700a4bbc14266da52241307e5024adc526db1385a0a51b930968ecfb18854f62 mainnet-01043-a396fcbd.era1 +65c75391e62218324342a8b79a1e862c61b9a19e80aff082a8d67d2ffaeef4b9 mainnet-01044-33bd34bd.era1 +aad19a6f122726160f36e5445a697163554c75602e40b705228441b044865811 mainnet-01045-e62bdcaa.era1 +52a3b2e210d69846a3e6c32520107b5ed7afac182fb097cf687d83e45ab3edbb mainnet-01046-37db60cd.era1 +860b139156ce093f74218560c8babbcf6894cda0b13dab72433eda787d79a5fb mainnet-01047-d02ff344.era1 +cc50c84bac158219ef04eef7190fdf4bcc4ed9a467e213adad06c08b59f1a6a0 mainnet-01048-27c44206.era1 +494dd65f9f69d7dc86b98db90f12db587d62e5a33cc2e07e3d0cbf7c3c24f89e mainnet-01049-d74dcd43.era1 +97059495bf3a8efdaededcda73ed9f17e212c9094b54a229431b5943c6ca372a mainnet-01050-583482c4.era1 +db4a2bd13f3d13d13ec7f8f4b08cb95b3a0ae8d3e96eeb3363596d4038dd5d0c mainnet-01051-b1375fb5.era1 +d484e0bb8feb94aaab82fe78ef2ec3a78ce97f547db8b1b706a3d26b303f411e mainnet-01052-fc68a5db.era1 +56bfa4277e67fcde394721e9fa3c002376389a35a25785d9ba681d71b3cd5b4f mainnet-01053-038d0dc5.era1 +337026169261de6cec5708dfebfdce9191d66220fe0c83f59e5bb44164411ef2 mainnet-01054-fc630d10.era1 +6a1ef87c494b74d34f2064c3fbf5bf9815b59d717b326c61c248dec6a10cdf83 mainnet-01055-dc564afc.era1 +0c8e44d6b50903a639b67f87011dbd486b5cb19eec9c1172784173df352992e5 mainnet-01056-75fba5d5.era1 +0fd44ab1a99b65b3a23c89482f3a3bd4930ed1cc063aa718bbbedfcc38c38ae2 mainnet-01057-f2a347f3.era1 +9117c72dd287b56a50613fe0c71569792966eecaabe526e030418e0ab493b91f mainnet-01058-7e00f70a.era1 +ae5c3353aea30d7418921434a638454bf9e17a8dedfe8fc5a7eec0c761a30113 mainnet-01059-7c557845.era1 +44235f35b9dc76e823e7d2ff15bfff8bc004f3cf30615486a62777977ff30553 mainnet-01060-d6b98869.era1 +9d4003643418a6c2f941064f20595e970077cb78d0a8ba97ef84cf1bef2f7cec mainnet-01061-7edcc464.era1 +280bbe45db8a424df21a6ea4242fb2ecd9b63c0211977bd66d5eb56961c3336f mainnet-01062-84fae3df.era1 +eabb0d3253f23668972605d4ded167862f1868133197d11e668e7fbd98ee4b81 mainnet-01063-97a50adf.era1 +643d1a50e895224b545d57a0e3667e694fa17d822946b3d235686081c669bf46 mainnet-01064-25e68a39.era1 +741ae08f901ae74a5e507906f79ed3ee1e0bd6204469736a7dc8a769a1fcde6a mainnet-01065-88ea91bd.era1 +f28e396a734f6766ecd32efaf640ad83f1ba858c1c101d9e50b665f444de42fb mainnet-01066-77a58ff5.era1 +fd291b0c15156b885cf9af594af329c8a2901ef2d5a498e711c1d51bb03f178a mainnet-01067-95926dd9.era1 +88d01100df01cc5680576f3bc06c6774f5fb20f9f4f176c35a4be96631297119 mainnet-01068-9e4b22f0.era1 +2a5d4e98f1adfb8da0f123cc6d7aa30cadc0088f5d88e4f4ede083997a447880 mainnet-01069-5451d56c.era1 +6e8fc5c611fbc0b367b0f24057c13d190cfa20b601dcb76ddeccf90ef05182a2 mainnet-01070-dd746cbf.era1 +8e5078c90f147cc6b2fb870b74a2f8a7e86ae2a9bd718a418e43c9017c94de18 mainnet-01071-e7162b85.era1 +1a76ae6d39cf9b7bd23355cbe2bd0918adf70280b73f425d750627890d442da7 mainnet-01072-a0266c31.era1 +ed7229b26279376d6776d4eee63b3ec7b0df51480a07b87305a847d2e320a2fc mainnet-01073-fb390415.era1 +709d9e99da9db956efc547a8da53b1259cf0aadd1fb20fb4fa15cbc38529aa19 mainnet-01074-7d668fd5.era1 +89951ca75c4c605dcf7dd333ef9b260492cda7b34a8fea430f9344103298bae7 mainnet-01075-38bef7ff.era1 +3d68e0261715155af12b191814d8c6587e2e54f8f6d7cde62b3c8d6502fe65e1 mainnet-01076-55f5a07a.era1 +57aaa772d4054832b9849f5b22b685ffe28819b413cf54060d878834ef60bdf4 mainnet-01077-61a13d92.era1 +caf6c5aa2d02ff166a0b00116e8edb60230a6a874155673e02cd2dbd0a54b236 mainnet-01078-227e7127.era1 +e261e53961745ba2d5ab256dc1576b03fbeeed614fbd4f9bcc44344aa5aca997 mainnet-01079-f1a39e59.era1 +645cbda4a151a25566424e85d6c47d42f2579e2af705ef35e491716346e5dbb9 mainnet-01080-1b7d0990.era1 +6a1383d5b385a1814b7bb725924ff0dcbe5b951a3648cf2041ecbd960cb373dc mainnet-01081-679b4359.era1 +931dfdd43061fd1c92112416ed48b640b35fef0aa5a16f497f65e594f8ab3252 mainnet-01082-5e7dea25.era1 +3c123d39b3ca6bb4b94345b08756345c6e87904d5a91cf1d45ca0112c4ef757e mainnet-01083-cdcb8e56.era1 +efe233952150d5d9384586b5aa291aa810fd28d34beabb3a14dc3fbca8f695c4 mainnet-01084-5f2ca307.era1 +2d785c18ebbb9c32fc274f34c9728aacb027efd4e8b38df7e610ec90fce056f1 mainnet-01085-12230dff.era1 +04337221c9309450bd5e32ff4ca248046ca14534426e91b92e592ba5f45f596b mainnet-01086-6eb5d461.era1 +81c6aa9d6ebe238ddda95def065ed86b54f02826b964a9149c1cad8671570fc8 mainnet-01087-45541bfe.era1 +9711c653391102114ef0a91cd9aa3b1ecbdd06efe48b0f6eb9bd4103f8bd280d mainnet-01088-09f2f43e.era1 +0ae82b02b1fdc71b22fef74ae5f15f3ca52227ce49d6488c470ab5a968f7563b mainnet-01089-4519917d.era1 +4929d107163c10d2399239a9652b31046175b9b3a56b4e4cca7a50829cbb9725 mainnet-01090-506362e6.era1 +1adb8982013063f3f75c5a9658353c1386ada4d77251fffef4d1c24f8625220f mainnet-01091-408e7c24.era1 +8755aa4dbcc94903cd22788a7701cdcaf1acc7b60e7e33d1f090b2a3cd56ccba mainnet-01092-4f1216d3.era1 +fe012d19b6f24004b9256f21faccbebffcee4d3162bf3a3be59ceeade8d5612e mainnet-01093-f7943fda.era1 +67d801f711c55e287c0b864fec6e60eadcffe4de27621240e1d54b8152f0f5d0 mainnet-01094-a2458aba.era1 +9629286e67ab8c6b315ae09f84d249523dc97eff653ca95bd29f98649fd07e70 mainnet-01095-c3174a59.era1 +8760cd2f788250b8fa559ea7d701749edac84afc929c658a4b91b87f58d4ae19 mainnet-01096-5475a30e.era1 +17ecbaab8c991bebbeb4c0b57ea5cafebcb4053958d72efa03885ce44015946d mainnet-01097-d2ce1e42.era1 +8192603d05ff32023df5a9b79059fb456c8f846c5095d166468d6554a8eb2910 mainnet-01098-5fb12ec4.era1 +f8fbfba63a81a2bc092261b01ede77af006099d11d11bb463d620908daab8b95 mainnet-01099-1cd92052.era1 +5906280eb0adb9b57fcc3b9a30aac43785816aea99176b98ead4aa2cd790613f mainnet-01100-22b095d4.era1 +2961b929b05ea871114f1992ea903702830f62e6690b64b563472ffe0701a14f mainnet-01101-844b63b3.era1 +e265b235b9bc18f193d6b93444757aea34fb0b65eb17124e0a7018b4244e0322 mainnet-01102-bcce4dd3.era1 +2e998dde589e88f2b5a444f47f445a2881fbd9ac44243579c78ab16086534cfc mainnet-01103-c9711792.era1 +39079368520f97896290263edcca5ce219733357ceb079f59a8e7c41689c4318 mainnet-01104-5035027c.era1 +98e3fd0235e76beee860b0f7cc698cfc4f6049a95ddd38b2b6ec7ee52d15b8ed mainnet-01105-9331be3b.era1 +b9ebaab58e96a52061462335bf23ed93cc0be2a0b69276b47922b7f6e0c2e4bd mainnet-01106-901078cb.era1 +c7bc6484c0adca1f6725408dfba43a2beb541c89254ffb757ece514f41b1bbee mainnet-01107-f8edc0d4.era1 +2436cfccee3bdc946b849043cc3823347f64dd251b8647c59ad40d5cb3ec7bef mainnet-01108-42362545.era1 +76d159a9b7ba724d20b5a6de7947e8e5a48ce9fc8e8e05dcb80dfe032411d14f mainnet-01109-bc521cea.era1 +02a940756d49d9be6a45272ab800d67856817f6bc8a098b2d2ddaf7fefbe61c2 mainnet-01110-199eeafe.era1 +07661f820a67426b63d1f8c90c0e72301f64371337f6d04baad2fc548466fc14 mainnet-01111-d3446282.era1 +aa3d74f50754f2454d9fa4b7ce5830128a7e578bb3e3bf602410ff213079de88 mainnet-01112-1c93ba5b.era1 +c951c5d212149de8e017db8ce6a147100a2fbe907343d1fd75c6b85231004e77 mainnet-01113-beafcef8.era1 +693b9883a3b1b94f51dd9b1abead7b47a9caf1f50b7d5327678a0f55a79aff23 mainnet-01114-71ec790b.era1 +257d13b78b096ae907f2d8c37da31af8a0aaf2a6c55c6e4b651b3caeb48be7a1 mainnet-01115-6f6094bf.era1 +33e76f63eeefc3b82a6f651acc73c2514a5bf3139491cadfd9caf31fd6367120 mainnet-01116-053afb71.era1 +1052384909bdd8a400603abf7e0bfccb83952d1312b35a84bd8242ea8ee22c3c mainnet-01117-98396ff4.era1 +8f0c0ee760bdd5b34d1ee4f031b18511ee0bf397ca1cc138bac1ed9ffced9768 mainnet-01118-57479712.era1 +53647ada8269cd41e65fd17e59d90e0a7909d9cadce79abf28d7bb31cd754acc mainnet-01119-1554c473.era1 +492ea63b5092e9eba1525546a1bf2a6709cfe87c2a4eec8bedc8436c26745ad8 mainnet-01120-5db300fa.era1 +44ae269d3e8291e1fd98177bb419205a7bba05c2960ae3a773ed85e99bf0e7e5 mainnet-01121-b9aeb88a.era1 +5fd7882a18010543684a44d2246d4c35c7b676fcbc41bc8082341a9b7db42622 mainnet-01122-a8849157.era1 +4bacacad8b1f09c93144dc85414a483d4e6380d7655ad33345cb42287735c7a8 mainnet-01123-0b7bcf33.era1 +486948076131219d1ae8408cc6c6e13bbe707577cba8ba88d2e8a3b62aaacf12 mainnet-01124-224f4405.era1 +5df25de47e78d727b3493afe919f0c59371f8c58aa08e85bfd15240a0c6c48bc mainnet-01125-0b6064b3.era1 +0d478eda34abf9738783fc5e3c19759515454d20e9523a6d6b983d8576ff651f mainnet-01126-c7344cf2.era1 +61d872eaff6fc36378b47c78ef3301a9cdc4612e29b8f55f7627b65cfb3c99d0 mainnet-01127-ad12ba38.era1 +8d8dea3e3f3ef33921c2dd3f76bb1df1afe174915835d57a934762877259f675 mainnet-01128-3a386e6d.era1 +5a45d158d34d4b91f76bff6e0f224f7b3f713a3d64a0bb85e6ebc3ad06cee638 mainnet-01129-c77dcea6.era1 +a196a55b1a930962768945133597054e8ab8420c8b2347049726ce0e8638701e mainnet-01130-a25a8fc1.era1 +840364bb4c929197ad5a9d39a106d4900c7dc1c335bbd05b7b59df034208c67b mainnet-01131-0225209e.era1 +72d73c29f6df76128478453c2832e15c4f41294889a8f6f5414c2ca175ab6ec7 mainnet-01132-1ba9f77c.era1 +bf4159c5b85f495dc486553cdad3eb4f6ebc8423161ff01e7e1018c28d39f425 mainnet-01133-8e1c1d63.era1 +f22ac40f22a3e3bd8621d1d5ee974936063c95c4b3cae1e168dbf9fe654040f8 mainnet-01134-3197a1d6.era1 +a0bba5215642e11b803b79d74fdb99ab882f1faee1fd39cc5a9cb2dbe06796ab mainnet-01135-d28f2c9f.era1 +f9d70ccb72a014ffc73597a2ca0f64a2cdd7d0020a15349716a662cd42c40fcf mainnet-01136-6cf8d26d.era1 +7f07dba1ecb6618772d4800eb19165e6fbbc1b8375422250c4f13dedaf448ec2 mainnet-01137-62215680.era1 +87be3f04548dd4161a991e1a0093a666a072971d2514cfc367a5807bb32a03f6 mainnet-01138-44e4142c.era1 +c15b673920e6c2c56879b2cd87eeb9024851898011090c6dccecdac8516cf1c3 mainnet-01139-1a70666e.era1 +381abbba5cc8cf7c0b45a96fe730ce0406161cb825735c0cdc4231f78ea16008 mainnet-01140-9bef85d3.era1 +42f9a2999d097cc2148ea7f93c6a1c89a46cd2068f37ca2f15494fda2830d9eb mainnet-01141-48066cf2.era1 +bf8b07de2ec39c355da7b30e06e95e0f39c1f30f9f1d9f4c85a2495c2fb5fbe0 mainnet-01142-112745d1.era1 +8750348515c1d43ad51d0cfb245f15a7b637f970de34f6f763c81625f296d65a mainnet-01143-c07f5625.era1 +32f9ac799b52f528daed2780d8b79bc9acee50bcacea386a2f2f7d221050063c mainnet-01144-ff3489f3.era1 +af3e756ebc415d0bafdf3b082fd50d3abf37525dbb442ed5243e5f4b3ea08a15 mainnet-01145-aa9a507e.era1 +f1863c4de8ffee8ab1cf9ed366fda800276f407ada526a4c6e2a318bac60a93f mainnet-01146-ae05fa5d.era1 +21bd47773e374770c725b4809088c46ad97ac1f63e9c6cf9eefd23927686d65b mainnet-01147-0dca795f.era1 +4f95ed27a854b4dd9dfdc4bf607261e6c6916773f572f2e4b0de04d0c9df8b1f mainnet-01148-b970027b.era1 +1814c3b3773adc356d8b5aa16201d97c16865552d2338b66d5dd7ea42fa94fbe mainnet-01149-b2f6f517.era1 +06e7013d7e74c6a3bff223bb343160748c5cff8d23e4ec62196692940caae233 mainnet-01150-ae9dea31.era1 +04db87c8a1ddf007769a332ba44dd3d885d2fad4c7e35447493192cc4ec34f82 mainnet-01151-1cb2b8d4.era1 +8505c37ef3bff814772e094799ff748c9bf5befbdbfd5535a257ea9fb75c513e mainnet-01152-28b41e38.era1 +4b53ddd657e94ed3e798c36b681b2f81d1f532add195a5dbd1b5b2fe8597decf mainnet-01153-9cc1f6f0.era1 +2ede6bcdc623794d5ba9c13f2f8c65b3c6cc3bde0a6a1bdabf4102db4e25f7f3 mainnet-01154-1d470ae1.era1 +4fb4abc0c2727aea473e2d3216ae48525f8240423d18811f44b61dcc87716c6f mainnet-01155-b516307f.era1 +10c01bb633db2fde70660d215c7dd7d7e5708b1da1cedb1342b58b2f93fd4009 mainnet-01156-8c4c1119.era1 +fbede5bf3794645fcf6d9e6ef8180d9474658d8dd984cdfd58a4c4e5d81f5044 mainnet-01157-d7bb8fbd.era1 +6d1372e9f0213ad9a6a4d3096375e9681b9bbde4c424b778c278e6f737988e12 mainnet-01158-12ec0bec.era1 +81d2253bd67ae0a17b73541bd6d07bc96cc3059be380784bbf1e0fbd016544d3 mainnet-01159-4fafaf52.era1 +3606259ac7c812b03f91a002773d2b83f9d8dfc3f9011f6448ec0f5b8c25d725 mainnet-01160-d0e1b363.era1 +4ec53f617ae6e948775633026b11900ee53c363590f6052eb7623a100f037e48 mainnet-01161-caff5303.era1 +d0a093057a4a9d018ddaf3716fcefa29a9d76320456838bc7187b4f58e682b84 mainnet-01162-070dea60.era1 +016b9a8185d7aa34ea226975f8ccc37e168a863d0d21cf31d1d5912e9ea3cc55 mainnet-01163-d285b66e.era1 +b82af4019e231a7960f204262524921df9a9d249528f19face594c80b6311330 mainnet-01164-2eb4c50f.era1 +a4f27f235b35f59146c2e945abc1ea89c5e49df5702423c098138c4ff18bcd9f mainnet-01165-ff4e71c2.era1 +39c3973459e3dc04e5a9e3e363498ce1f5e67da950f7464faf5fb091022e7750 mainnet-01166-4116850a.era1 +cf6cd7d4d48a6a33b81a99a6f165848b8b8feafa3a123d79c5a5a6f1d8222e80 mainnet-01167-5b698645.era1 +c70360eab450f746e535e0cb8eb236af779ac4f17aa110ada2bf1ac65a0bfe52 mainnet-01168-a23912b4.era1 +d57c0d867db78864af6e6ad62d9022b7ccf48002172a1aa41bc112eba378ec96 mainnet-01169-0737b4e5.era1 +595775629d10dbf6dbfc752293ecbdabfdbf1c3184d40647b3c0ce5ba4364fec mainnet-01170-df35982e.era1 +475d800ecbeda476068354341f9e30efd0d3a9d2e4c2d1bbc3d2637460d6c958 mainnet-01171-cc7466dd.era1 +e170b5a5d077f5fa1071220a290e99d4ec0e6cd0f08c3f489cc7e3e8f5a5fc93 mainnet-01172-9abdb4ae.era1 +c7bcd71886900eeacb2cfa148731845d27ea730b76734bfe3912c3d804861915 mainnet-01173-cdbdfbc6.era1 +61e83598aa81b1ed90dbabe6ae4e065acda4a00bca83120c96f0487091c0a52b mainnet-01174-d79c75b8.era1 +6530e6d619c22c585b2d5278fbc722fa6d5c4e50db60b13de50f41cff7f80989 mainnet-01175-0d2dbfd7.era1 +4a06ded80069ad524f38a81f7ca340b28e424dd7441be0ce07658a7345740e3e mainnet-01176-94adbb4c.era1 +c2f256165eb05f8819c212499ba030f8cf8b350c3f37f756ff33f188106ba8ef mainnet-01177-d5e0dc5a.era1 +0c2fd41d9533fe0058a63fe235bf38fad34e904f683bb634fbca5cd8c9eb970f mainnet-01178-5c34962d.era1 +f4ea75ca9d75c6d4114c94d6ce904d6775e668775d17e84f6cde3cd3fe0fa8ea mainnet-01179-1de75523.era1 +9131b648784028a47f51ad723bfd375cfe5362bdbdb3338da8676edb008035d1 mainnet-01180-aac82868.era1 +7a487ba57da5bb3fea8ea01ee909d2a5498e5af9a80a887ae69a67209f4d2fb4 mainnet-01181-58996635.era1 +03deb922a1b8f3b5a6cd0ef5cff55a8a9c8b4ce3c9d77ea785e20f3cf0a1d43b mainnet-01182-74b8aa44.era1 +1cbf90260ba688290b8ebcebb54f325b5c6c2ca310491fe13ddff593c0f7bacb mainnet-01183-38d85671.era1 +0d29d24eee891361ad82df1862ae5b1a2eb2773d1c1879c0e01b2f2702169468 mainnet-01184-2a82d902.era1 +5a49dba599ff573884652f69cebdc81b5cab86982cded90b0b9092b412d9edad mainnet-01185-528d123a.era1 +4b0a6ebfb2a328cc7f6c7c3bebce5862f31387e8606bf9d728d07ad6158c026b mainnet-01186-dcefe6dd.era1 +6445c0340ead092dc5865fba846ad190950ab0ee68ce57a8655bb894ff5940df mainnet-01187-25b5519c.era1 +13977d383ba3945eeaf510ddc8aa731ec973e8ec208659c69b845db89478e4ce mainnet-01188-a8eec328.era1 +a24aff2c666efa07e0ec015b1b64a05041252b886a496295d849b49fd3cf29d4 mainnet-01189-09125340.era1 +c814651f4c00d1053e3810139062efa6203e323e06d859d9391d6c4ee6230f94 mainnet-01190-13085b0b.era1 +07f7ec8d3cdff76faff689a7ec5bda388df986a9742cf2da26e619964cadefa9 mainnet-01191-e8d4138f.era1 +345256471129f0b8bdfd544e6aa6bc7b4da7d8a5c9a0ec93ffdb1950d4455013 mainnet-01192-7a4ee217.era1 +a7ad1020b5aa38fa4ff4d291b31a0228e854dbdd3f1f115956ab463613602bf3 mainnet-01193-7c6dae3a.era1 +83ebac2b6337b01ccb71a1402e1839fc8d03b5adf952e7ef8d123c55c0376466 mainnet-01194-7d063ead.era1 +b828f0888a3c135d1af5e335e79f84928434fff164788f06bb489e2b738483eb mainnet-01195-68a0b792.era1 +59d92764901edf298f803a8b48cda4ea0b79e0b2df52e44b944f28b5d5ad7abb mainnet-01196-94c96976.era1 +9fc33c270dd919d77d97cff2fdd85aeccf3b52e9b13ff72a1a246b6aa605cfe1 mainnet-01197-a6c39440.era1 +4816e2a372f205e92d1b34b7be644441f6cd051d9e9ff9da33712eb69737eb4f mainnet-01198-f7a5ab53.era1 +47bc5ca9bb98735c1a97634dd5d02adcbde5945b3b5964fd673b5e9384b92659 mainnet-01199-84b9e12a.era1 +3b50781f290fa2ede67e95940889e44bcc370e85ca0c84d07964e92b311b8a54 mainnet-01200-a6b6a963.era1 +0de9940cdae7cb587d710cb901d0c07c823a9dd6219364d6f7918840ab055aff mainnet-01201-4a0c7f03.era1 +01481f0c79e7e0ea2af40b80d4b5a38f7726da1a868756f0d8a9422da84f732a mainnet-01202-5486c645.era1 +9e8947ebe59f2eae8147317f22626e6ef424c35050bb292c4f9b74d3f3861c27 mainnet-01203-08a5313b.era1 +13bb7f536ddd6d9f9f9fd21d684f2ad2118b0464c1f733ee22ad1dbcda66abed mainnet-01204-97510371.era1 +86790f4e75276ee19dd2ef21c8c9a95038bdb802f95336486756bb5799e173a6 mainnet-01205-7bbd5580.era1 +564fbc2ad3ca12d50b9d1d892ac91d701bc441540e41562329a10c14724d3df0 mainnet-01206-61e11a8c.era1 +41c7147e448add71a9b21c94d4bc1e49fdd6375cd04163d71361b0c0298be39e mainnet-01207-37459490.era1 +6f5772cbf97c2f3ed2cab5990242a8678f2bad69a7cae1a45224ee3ae5a8b5d6 mainnet-01208-4d0cc348.era1 +192ea288f03a1f59bf08d0e06ee519f72320192bef37f680b62d5d15a02e7f20 mainnet-01209-2008dcc1.era1 +5c5c25280dbc637ed83fac1e4295e06aa5bc462d403d7e3a3b38badc43463fec mainnet-01210-842d4195.era1 +a3281a2c120c19e73bbebda715847cc20caf2028bb9d647ea2089077c86c84a2 mainnet-01211-a9c13697.era1 +a6a026943d1ae7274de83838b877b47984f777b7df1f3013bac62ef560107ba8 mainnet-01212-a6435d49.era1 +e015c22e00634dae4505144d499554ad363523371a3e513d97658b8dafe15a4c mainnet-01213-71e5659c.era1 +240f1b91e57a77b5832fa055e91b67f5f97a42fc54764bc7ca488453423e2f61 mainnet-01214-1791e683.era1 +332dc15434f7af625143c6c3f5989addbad7f16d3b07538c06cd7d3703ce1fa5 mainnet-01215-b250520d.era1 +e80c9baca725dfa9959794b280d70b75b9f67d740b88f06ccf69184a24e9ffa9 mainnet-01216-a13eb7c2.era1 +6a2f77b4f9d7067ffa99d1f67025534f533b14a06327b63a4ee94eecfb57b6e8 mainnet-01217-5c83a392.era1 +c684f238f79e63b0deac70898a9f27576b1430517b3ae23b50ee25069592d1ad mainnet-01218-c13220ea.era1 +88d1d4adc93b5680856088abe8d31161fc8360188b3d040aca98d7e126751c46 mainnet-01219-5f56b030.era1 +d9e73a19eb37d4987a28e07079107e3cdf3018052b6a9bc348960f1eb7a5cfa2 mainnet-01220-512900cb.era1 +657c125ac26f2c42bf31158a411d908f2d2d73fdbcf628844583cdae9d62cb9d mainnet-01221-90c06f46.era1 +e4615ab4867d13f8c216508f6a4f99ec9e953d5fb352e06eb620c8dc38526fba mainnet-01222-096acd86.era1 +d8ea23ac75bdbf10b3e5cd9a695a6d112fd9fe4d129e1922d68de12238622266 mainnet-01223-1a1baa66.era1 +1c0ba11faba7076a9026e4b509a8d3e55f2dda54d7cca819e71c10d65420fc6b mainnet-01224-34f529a2.era1 +146ba8c0070f24c129c8e055bd1c45acfa87a4fa07d2aa0171f3f87b142d6e39 mainnet-01225-ad7198e0.era1 +9f04e9b228eaf0f6c843c109f93e13b3bb78a76efb6e21febfe2b74f3bf9390b mainnet-01226-2d858029.era1 +83210bd4c642258e31e29a69fffdd44dca7f81d6076b06fc6107ff3c577c7a8d mainnet-01227-bb9ed6f3.era1 +40da5526ff0164147c683769be2b053ea2b18a3e43e760bc7939a2b0b1dffb49 mainnet-01228-024d8b09.era1 +9d9ede24c506913cb27a328c56f60b5b8668f83b4c96f6f1851cdf4b4fec75b9 mainnet-01229-4fbd23d8.era1 +da688e247e00489555c25570cdfb9d6dce177456af52916974220ff0fbe3aa7b mainnet-01230-b70e3603.era1 +f41306c021272c4f2bb438f77ab3f2e2e734aac564d0de4890e96c9b435445b3 mainnet-01231-76e468cb.era1 +a056a64ab8bb0f5aff4c387c824e4bb321e9a791871179b7334c734875763a4a mainnet-01232-7cd7b651.era1 +787d4bd2d9a7732648d4a82a4742e1613c7ec44cd9b05279a3e436c391f598a3 mainnet-01233-d077ca78.era1 +276bd1a16a4caa8496fd6546afffbea6bf77b95fd27c4a4a93f774c0f9c8a620 mainnet-01234-ccf5a0f7.era1 +b876aa33ef886b8fda3e0b6c662e414379255ef4637910a1d2b382cc93bfa675 mainnet-01235-d7666847.era1 +e7306edbea916db1689f079213a57c7ac8d85862fd8f7bef3c5787a8f060fc04 mainnet-01236-3d10b175.era1 +78915d2267090010736974de75be869bdab6a1c6eba2db6fac98793511da43ec mainnet-01237-cfc709d6.era1 +1453318be63fa843b9093a8854d6637975c7c101fde14290fc48edbd45bdfdd0 mainnet-01238-f6f1e083.era1 +653839c244591ffdfe2c587bbb525bbea199dec20c2b76eae634d1498db2d642 mainnet-01239-cbeef9b0.era1 +7f02d914a1ac094137822197e7937690b94938297c3a5528611b7a7e31a1f000 mainnet-01240-02688fe8.era1 +e2e49c79ad5c755d8563f633231257c72df94c5d09024cbee3e11b2b7cf1afdd mainnet-01241-20ebd652.era1 +ff4ce149bb2465076266a0267034440bed5535c2074eeda82909684b472c579f mainnet-01242-ceb89cc1.era1 +35d472dc040cb1c04a39c68bbf638485bde139c63ba0dd89c04309801bc5276e mainnet-01243-6ca6a14a.era1 +f6d2f31e47435a9bda50eec2e5bc2a92abf11399f021e1eb90aa202560e35473 mainnet-01244-40769f6c.era1 +58f234bc312045351066024a1a438a4de4607371fdc9f721f994f1fef1efb76d mainnet-01245-f2f56ea6.era1 +e9e84173f9cb74d9ea02f965c0af1bd634e80a98cbf90ecc9b3ce62ac6e11ebf mainnet-01246-c0e897c0.era1 +c5c99e8a744476544b5ee1f63c1f884d9c11fd24fe47c2e8565215c5c09f77a9 mainnet-01247-58d2fe58.era1 +8995a517ecc43dd474ee81d84db7acade36d6edd2db327e086ac1789295a5f42 mainnet-01248-e2cdcc7c.era1 +0230977840319fa93322f591a7b920a54dd91dcaeed3ca4d724d4954313d6598 mainnet-01249-379da93b.era1 +36069d8fa590faf508b0b64684e967bea94ef01b22a87c0d7ffc1aed3be390dc mainnet-01250-afede008.era1 +cdc6b15be72dc4095678d8064eb2734c576c2880e546d301b0f90139a345056a mainnet-01251-eeede50f.era1 +9a170f1ddba4379c4e8e7e9a9ac8f32bfb2e43aa05c3e6399a18cf48c941f6f3 mainnet-01252-c5b81847.era1 +a49221586db21315e377b927628313f9dcce3ef9b2b644a0b38d5498a78af290 mainnet-01253-96748f67.era1 +4f5c59d345bbfe445f576c69e84b93f6039044dacd90be7dfabbfde6cf16e7cf mainnet-01254-5c3badc9.era1 +54e2236adc2317eeb9f8a8b658c6dc7c35cd945809a6f27d2bd63fca722c0170 mainnet-01255-2599ae05.era1 +2726f17aa58942e5b5c827b3c8b928c285e87f873487687525a30a5a1a111476 mainnet-01256-2b44aee4.era1 +723b298028bf04446ac6e05b2619130ddb65adf23f6f5829e27d6fc92273af79 mainnet-01257-4952c512.era1 +596689f86e347d0b40880a5d550ce7947b3d175b810f7082e54a48df86174bda mainnet-01258-713aba14.era1 +c0dd89598b20a411b933c13f08d7c6fb19a16029b74f99c95d089e4915bfd95d mainnet-01259-1ae7b5ef.era1 +0094c7609647801d3b73fc03c9b8dcfa2b8c7edcfbfcae359851714a7b87da4a mainnet-01260-6f90c321.era1 +e4022bc4e10f4f03ae45dece6074679a850dca4ecf4c4e26463163f8229812d6 mainnet-01261-61d416a8.era1 +50aec2a2f05df43c4b112afa8fac437d25b5cadeb45a53848e064fc1b9fa5579 mainnet-01262-aee21385.era1 +9a2e557765a00817c1ce2dc32e2187ea9c0e97d81284725e6d1cd639dcf63f99 mainnet-01263-9fa06b72.era1 +720e8ee102ac6a4efcd44555926085355c238752625dafee6afcf826f7e9b113 mainnet-01264-53a8cc3b.era1 +19a6fa6f6a05862416f0bbd72549f316e21921a8625d9e3f020b2d9dca6f2844 mainnet-01265-1366aabf.era1 +60ef14179f622a314e3e63abffb42ee6e4741aed4eb6983c8ef5868677f12f89 mainnet-01266-2631621e.era1 +1004d4829ac69cfebfcf177b73d7d3ed7248ac12b9713a6fb9db3946cfea9f5b mainnet-01267-3dd94158.era1 +1c772e74363742e058f003f19f65ef1ff87646defb9946e2bcea1f9c060adbf3 mainnet-01268-ed99cb91.era1 +d1cb3fb6577e706d164d668977f2c7f1ab4fb82d9a6dd192a14c309ccee89d4e mainnet-01269-3e65584f.era1 +bfeb65c5ec20f46232dcb9869fd6f3a016bacf4aa679b01cc5c0594f522f7177 mainnet-01270-69ccf142.era1 +96a1923cbf40ed50749fe02ab6ba42c8fa3dfd6eb9e1f0a94a35b70f9b78f6a0 mainnet-01271-66fbd4f1.era1 +59de1f5a8fba96663a39a5c2b1b8df45c8e86a6833ba4457d93a86cdca9abdb6 mainnet-01272-7f448c5e.era1 +988b890503c163ee6ffd22acb6c5576cf4f6614f8f55f4db55ecfad02947f36e mainnet-01273-3db6b5ee.era1 +cc04d78356f4762e7753cbf6fee1f5372486feeabf83b5e71b1761626262a30b mainnet-01274-63adf378.era1 +10354faf8a0bc80af4ecff9d5300ae12aad11f44dffe0d03faa1aba62c2376fc mainnet-01275-0c89bb3e.era1 +90fc0dae0ef111e66c11355fde4394e575f54ccf114ac328339877393701165c mainnet-01276-399bff64.era1 +a85a26858ca53bfadbf0ce9e3e3f6d9cf0339d4776b1426594ebb9a59ec031c5 mainnet-01277-1b14df37.era1 +6cc09625423c09edd772c178537799b9574d71548dbea2d979e7072992e1e1de mainnet-01278-7bb5b295.era1 +39368cfc2ced10c4e0abe27cdea8351111d17948a3fe560a7cd866286facfe29 mainnet-01279-4ba003f3.era1 +38ddddc4c9ec93c6f4357d8dbc1033d16efc0c2bddf56f7ade6c474163d56572 mainnet-01280-2ab6865f.era1 +dbc86ae92832c804bd6c9bc6cd2023a4577a7fb2ae5e5965173ed0b67b2e7c49 mainnet-01281-a5598247.era1 +16412dc3d507e2f5c978903f485fa3cabdc393406c3ac256441178242b11e45a mainnet-01282-8357bfd9.era1 +af6e6c5a9064469827ecdc95afc44b765a5f0711fa90d422e021ef12e760f7fe mainnet-01283-b502eea2.era1 +42c1ddacf27e325b14e8cbac5620fee7b90c0394a7e218365d4d899184f0b410 mainnet-01284-f9cc2497.era1 +c4ee83b0840b9e1491699b6b4f4405c020401d9a810c5380559c2108eb9b0c0d mainnet-01285-5ad4fdc8.era1 +6e30672f57f37ac89737f32765fed41b2fec36249b32ad7dc97eec4bfa7fd71c mainnet-01286-4de64a70.era1 +4a454402f31522bef8aa28fe8852ecc3307dd3d918fcb67d19dddbce3885c87e mainnet-01287-1cd8e301.era1 +c25655e481cdc4fc991b299ae067a6909c359d62dea887f1cf35e954f6971ad4 mainnet-01288-461721aa.era1 +58163222d18ed04c31db06ee1e77e60a83c5461459113ae29f0b961608df2337 mainnet-01289-d776cb85.era1 +7e28f52f1c043b809b58d97dfb880206494fb0075723e44c27c021a94fa6b7b2 mainnet-01290-873eb969.era1 +017542bedfb0b360f109d76bb52ffabfcd19eb20ec45498c57f869254fd96efd mainnet-01291-3466a370.era1 +7029158d2d683f09729cee8e39314828ee0565277dc2757759c9cefa8f8de1ba mainnet-01292-f7c7ef3a.era1 +8b861921ae93420118284ab2a290c0dad9d500c6c7b81db7726f4174be0cf1c4 mainnet-01293-eab36fbd.era1 +09bf6ae22a2291d050a1a996c42f015abb24301150ff29429fc2ca5b59cfbec7 mainnet-01294-d38b96b7.era1 +f08965eeb9fdc69f3142876a0941078b2c7f9701cb389785651f5ba6d2425dda mainnet-01295-9a8e547a.era1 +3099ba185db6d2f9db70ef88b49344d867ded95a09234dc642797c73c07fc8f9 mainnet-01296-044263f3.era1 +8235ab9b1d14e907833ae85ad4de8822a40df73a7157476b35b24cd6bf266dcc mainnet-01297-2a6fa840.era1 +c83e6abc2c078e0fb54d55370366a23c06d7e9f4ecb66fb7c2deb663b68ae98e mainnet-01298-25e8cecf.era1 +e7968bca5bea8361d9777c057d0cff1f759a2df90cf1f42543363ecc1ff5718f mainnet-01299-4783a5b4.era1 +a8785b8a11f423ef761d28e7a3a8940b436675890437aabef419817407eb902b mainnet-01300-05128f56.era1 +9f0dde66478065a5173ce5bff435a39ed2dc1c30826cc1fea205f65ac79495e0 mainnet-01301-f8ad3107.era1 +a9d03ede167e577bfd7bca060df5cf72436b9a1ddea598e4fec170b221b83032 mainnet-01302-564f335c.era1 +280bb64da624126213fa779695c7ee32bfe187c2fa4a9e31c6af92949e1bb498 mainnet-01303-0dd93fff.era1 +3e1b58db61947c5a1b45ada5c9c64d9686ce421174dcbbab5c5a65134044f32f mainnet-01304-20eaa74f.era1 +4875455d7119b917fb82f49b255dd769169d66c8f5b7e40646820e0cc1cf1b84 mainnet-01305-d00fc022.era1 +b29fa8400d04e6b24f89a39b9b5e8c343d36f44d8308a739e786087edbb6ed03 mainnet-01306-1f67a41c.era1 +a060656d775c6284c53f1a36d79b1ca604e2e8bab852b3957fd43d3a0f605c21 mainnet-01307-2f77e7c8.era1 +81b2c72440a57f9d3b04ad238c9a7133de2f1efd020f62bc5fb9394eaada69ef mainnet-01308-8eaf029a.era1 +256a812c87b6737aa725ca16b854cc48bbee781c23e1d209c93fc270b1b6b1ef mainnet-01309-0e9134c9.era1 +920e4b3be271752b4fe53a3a07ff32d06caf07b1044c0dc817ff4962ddc080ee mainnet-01310-36d0712a.era1 +daf44c55b9ee3b0d0237ac386ffd61f8594842b00fe895f0e8074019a0a3eea9 mainnet-01311-b196f52d.era1 +81a4a2f671007b73025cad89c68330c3138ba0c547fe18af2aa3955b1a8feedd mainnet-01312-052a3539.era1 +e2c81f6dcb0ca87dfbdbc2752449a52b5cdc480851305d64ba155500fe47745b mainnet-01313-ed3d98e8.era1 +19094842e9d0e78c761dcc6661f6d1763e58964470451f2a2710ec93b00f699e mainnet-01314-6711d975.era1 +ace576a9c744c7c38f272e111c62da33b4fe65a8f5edc619a3388dc4711003fc mainnet-01315-c90257cc.era1 +7abfa2f8199e55bd438d77f5da0f5bad1127d8a74bc99a9050516a4f2c383dbe mainnet-01316-d23ae50b.era1 +18bfea667e282bae9f4f0e67221452492e49a8cd53e91115869091036c4b91b8 mainnet-01317-b2c5b1b5.era1 +f68104365e1e4abba936d17bc53868818601f152a441f6900177d2dc56b4014e mainnet-01318-803d224b.era1 +a2720d85bd277b218f7f68d7b1d53557d73479267f40da994c5d9135dadfd7b4 mainnet-01319-f7082c22.era1 +7a4c8f4df8f7bd62dade2adfa030ac0742e2a55d6da79ec4382a592d70b0547f mainnet-01320-66c7f004.era1 +ca7665fcac6d8f77db741b44e976596a0a0c785bf199df49df99553091f63ed0 mainnet-01321-04af06fa.era1 +7b31771617b1dc5b3284a0e2ab301fba9ec12b621dad9ccb5571bdb0375aa648 mainnet-01322-44c4c046.era1 +56fae2bc1641b01ce37537b761530da0ab4cb9dd8e166b805f32f199c4e75fd6 mainnet-01323-570d0caa.era1 +f638ff307a6390b3ff8dcbc49383567831ee5a9063f4ce8ef4033ac4e54ef585 mainnet-01324-d4cb7a0d.era1 +21ca9ba2766202aeceb4aa0ff0f4a3046d3901ed8f71786a34a39742ac1c4967 mainnet-01325-b10b22ec.era1 +2b26f4d078ff4f04d8b8eb96de5c024a3b97583d7e891623a8532663181de6f9 mainnet-01326-ea52129a.era1 +1bedace32560f94d1b02d419b4b5f190776c846110b5033dc31fb38230c397f6 mainnet-01327-4f88b085.era1 +0169f104bba10ebe1349fd82bca9bc54db392ea75c105550897e0a63da217cf5 mainnet-01328-83f1204e.era1 +91a7dde4a0edf5667fb04c6c78fe7d38ec2ebde946fb34e4951f39090b432012 mainnet-01329-572dcecf.era1 +331f8506237bb24cf7d3803452474285316dcc8ea98ef15bf4eef4cc6064b60e mainnet-01330-45db98fb.era1 +04f81f58920bbbf42ad4a2061cf59ebe30596f1fce26dbf6453d6d1bdcf51f84 mainnet-01331-79ac113e.era1 +1027d282aa646d5c8fa685e69a82474a46394d523a26dee346297971155e24fa mainnet-01332-2e02ff93.era1 +7120f994b83f2319c62825655b1cfeb828e30b71a5c700f6e15d823dfeb4d87b mainnet-01333-1c8da535.era1 +090fa69dd9c403e5f94eff043afae39a7e91b3b05071d7e2578cce777a2ab34f mainnet-01334-9983596d.era1 +358aa0ccd7de8410c5d056cf5b288f5842104804244d58d7008618d65c856382 mainnet-01335-4143f16c.era1 +071a5e58f4e5da61d3fdfec62e7eb78f9984c9e691d26a600cacb01ad33ca6e1 mainnet-01336-2c80acab.era1 +5f98ae78a7ed528bbdf91b91eb8300e4be58cf3e0b326b2ab0ef3b9f4288ca8b mainnet-01337-70afebf0.era1 +7251206639c8e37fc1e068c0fae077b0af83ef8d63b5bb2fa08602a145c92530 mainnet-01338-0d957f43.era1 +b9a2ca5ee55eb6dd44d249fb88097cb9e298448ec956dc22069d9a8e1ba3c725 mainnet-01339-a954d4bb.era1 +f5804a500f5c96fc3e628bf4d22743dc3ffd4524d57eef9cc3e11767044611ca mainnet-01340-d259e3db.era1 +4c2c2933f924dbbeb9a85ef9aeed72cf3129e7ac9c4c0649506baf653de670a2 mainnet-01341-a393c46b.era1 +f8b45dc8bc01486a5489e1efde75625201840dcf7fb89a340e81fa1b188a5ab4 mainnet-01342-febdbdcd.era1 +e42589d618af2b21dc4bb69d6f6b120cba058928db76b678974cdc36f21bd1a9 mainnet-01343-c205cffc.era1 +fa369dcce9af813ce9f731510589770e7238f09358c8efde7de2dc3f3176a904 mainnet-01344-e55d1bc5.era1 +a60e9d83f28290d2482f0fbd238e61735274bca66daaa6fa7a59349a489166d8 mainnet-01345-18129517.era1 +751c869193eeab784ee29ab2fdf4598371b9b569e3a31b4af797faa78d98faac mainnet-01346-62600923.era1 +62737b0cb00535cf635bc72b8fad4a1a775545f28415b424b2497d37761a5e10 mainnet-01347-b7bc048b.era1 +1356d26c5b96a9075a842ec37ba5d2a575c28531edf8fd2436780f2a85c6946b mainnet-01348-63f08fbe.era1 +e2783ba2d36fd2ffd8e04c2e347b9251a2772ec33f4bb985a3b56b745b1931cc mainnet-01349-d8f90a76.era1 +19bd461fb3bbf4beb58d475b988cc702624d69b490e5113b690837283589574f mainnet-01350-3b7ca4ef.era1 +d7544dff8e768f2cbd51ab40a3983d707d0ab2934ee29d1289b2eb56c38f50e4 mainnet-01351-a5aeba3b.era1 +01a87d40fc0deb2ec80255fa71092a4e18c76b98fab9eb864100b8474a4ccae0 mainnet-01352-75faba09.era1 +b976cd09d412c661c69d0b1f9b86d301fd2a1348ee8fece91233cee1e14bec26 mainnet-01353-53743d70.era1 +97ef2623262c43289dc1b097d55fbd1e36d0ffd5ec5b82315c1f73e2267eb400 mainnet-01354-dcc29cd5.era1 +29ce252a595b7ce7d16b3620b1078c65266eefb8d392a3869a20f53dc1ab063d mainnet-01355-73301488.era1 +d73ce56c6f635c1eab39a3c8a6471c30d242f44555be00633541d0deaa5301a3 mainnet-01356-c5358154.era1 +4b457254df3087505297c300b6d18d4623f77cae0e68d8bf08fce557abc35880 mainnet-01357-6ae35c6d.era1 +3f4c5e1c267f0f715a25e5bbc0c34eb1974371bf6441fd1d67068135f44dcd53 mainnet-01358-40401985.era1 +0bc31b98df3ca2c46c9f1e4672d6b00d03c5ded22afc569fb9a2ea13ad9636d4 mainnet-01359-4f6b2658.era1 +59a35576c168008295fa658fb87f4ec18aab0d8d46297d911238e6cbc81863c2 mainnet-01360-d0b1559c.era1 +ebb0970c37869805182bb0f8e41049ffcef7e003493e64664ecc61850079475c mainnet-01361-ef5b459d.era1 +f830f6c6695673742e55da926b1927df8ac3b57457971655c6fdc93b8779c9af mainnet-01362-08dfe9e3.era1 +5b1c0b75458116106ac511ea780fb0bdb441db0458b8d531645768d363050592 mainnet-01363-80f7dfd0.era1 +ce4738d883c80ce1874fe3755dc0ed784cefafd7c23009f2fb98f41a50e1e492 mainnet-01364-4b47710e.era1 +1b5a17c77982f23f514e1c6be2d88db5c0faa788fd87e774f4c5060e42148ced mainnet-01365-cc79e6c6.era1 +0fba63789d2fb5ea620767e7519783329f805cd16d1c358797a879fc6d81f640 mainnet-01366-d76553f6.era1 +133d860d3af93f94ac7fb78d709b16b39d50302978e7ab26a3f62c00cbe7dbf0 mainnet-01367-d7efc68f.era1 +a0b2820cbf08ace921eaa12a316be9ebd6adb81ce25b67ae3c4f9febe9862b1a mainnet-01368-17ea70af.era1 +53da9926f9aa968cf1d2c047ea37c7cdd922e650d01544f3c90401ac7775a91d mainnet-01369-f4a498df.era1 +b13022fb1a11ca7c6b0ea1e6e588092faa2593e57dfc5cf3f611ef45f61f4dcf mainnet-01370-027e2a2d.era1 +e9ddc8dcdd6b9082f522e2186571e929fb967d06473227f2c3bb69dec33f0a99 mainnet-01371-b90ef17e.era1 +49e5c55886a224573ae183904879fea6e59afdab572f78e4bd764ab8f1d12d65 mainnet-01372-b4719483.era1 +d8c7e3333558f050ba0594202e01e757fd8380626ff315e79f9c8ddb33e14202 mainnet-01373-b999269e.era1 +711aecf03b8bd204e770345c60ae0f3879e0a5077784a1da0c9ea4c12d65a3de mainnet-01374-662f1591.era1 +bca1ff381f460cd89fb9fa87c0f015c97a1eb22ca36158587ed4e96a96776abd mainnet-01375-108ccbef.era1 +5b5e841c9e725a1027a6c8938982f37a2604cdc308a8b77a9f24f1467b1f8fff mainnet-01376-54d29629.era1 +6cd2cc7e83c3ca0d08bf49a40f0161da41af1d6d68f341be4373a19b2abafa32 mainnet-01377-6d7bcae5.era1 +7786fd64d694cdeb834964cc70b7595d7c76ed236c8e0e72593169843d2a5188 mainnet-01378-fd477f0b.era1 +5e6428929b7cb5aa3eff3c4e0249306957382e9e304734c7ecada18dfbf2f509 mainnet-01379-57b72b9a.era1 +43addf5c34b75fe9482f8328072645bc17ef9aea096ee4f7c8b8512ce0f40228 mainnet-01380-beacd779.era1 +8900a0de91e023699743f61ce33457c05098974403b10fba0efb7a2ef004770d mainnet-01381-4b6c0027.era1 +46b507c7a377844c294d2232275f05e5a59c6d0fde73d1c00fcf11e32299bf64 mainnet-01382-485b0ed3.era1 +b1e23b51dba1636103604950c469b01028f6e641c3c5cba3a62c8f6d745e09d5 mainnet-01383-402cc664.era1 +064ee6442baf410bb426782b90948d3fd0582906c475d3729040b1dbd7ae143f mainnet-01384-547da0f4.era1 +4899fcc76e8069936a49bd0707a11ec90ecae225e238f42a8bf611b9f6604de7 mainnet-01385-058c55b0.era1 +3ed4deff02898719e040db928881e6f21edca03304f0b615e9174d27e2af5c3e mainnet-01386-951ddf6c.era1 +c6022db6e9081c7c30f451b3266e2aa639d22f95635109c31b073d500ee056b7 mainnet-01387-85a7797d.era1 +30030a87c2356bfb803d8a6638af8946bebf986b5a421833a019bf83cff0069a mainnet-01388-64376e67.era1 +b9eae82bd40f8701ac7970f24fd84333d41f960b7b0f7ea7de66ac56050bc41e mainnet-01389-58445b6b.era1 +43a3608276f808e23937ce0121922459293a605d989850496c11ee138b9fa988 mainnet-01390-6a2d6ce4.era1 +3862c2b6a685a4296890825c26ff10f133ae8b2a0ba526923780f2fbfd0921cc mainnet-01391-330e88ac.era1 +153b03e32abb8cac2acf2cb8596fd0d7bae740c7ee7984bbf705648008492ace mainnet-01392-3bf42cf0.era1 +151e349aa5c132e943c9c61f0d8dcdf470332968b1ac8f64b813f4f48ee1f23e mainnet-01393-239fa3d0.era1 +f0e0d1ff10a5c4538b3e480234b397dcfb5ad2ace98003135b3f400f672c03c8 mainnet-01394-76707dbf.era1 +e8ae97e74835238f588fea6ac5ddd546f0aa27ad4a62855690005a4da464f975 mainnet-01395-4bcaea6c.era1 +c8c7fbff86d136da6763cf2b990f54f4ad8c9f0e37a6c730dac52ff7e7bdec5f mainnet-01396-cc5b5c9a.era1 +59ee08c1ff222371627faaba48a1fcc6eb78dff5747b93908265ce86769226f9 mainnet-01397-ce3fa54e.era1 +24f8b067471cea0bd15e36bff586b4ae5715c0dc77275886a1da2d3ca28a303b mainnet-01398-d06c9a04.era1 +2971dfd0ab5cfc0585a4c75803637377efc89aa9279877b74dd97408b5f609a6 mainnet-01399-e131599e.era1 +e039e2e9f725c32a61574919b26232839a73a7844b44ae60a7c8963214f03354 mainnet-01400-1a628757.era1 +550c715ab4f6128d0f5fa9ffe1c2eb779c3c8e6c70562a66c80218da9698310f mainnet-01401-e7d4a880.era1 +68288f605024621e3e14cf90ec1610c42cbbce0b69c6af67ae2bd7358c3471ba mainnet-01402-85d3f303.era1 +4bf2e03672a4dbee47b8f5af34d296a30026320c9c4c23947e0ffe8435888219 mainnet-01403-e1e41aa6.era1 +26a0d4a885854d083fcd9e37adefa805bea73412145dfd3df665baf96a3200b5 mainnet-01404-eba27a23.era1 +c0b5759280ebafb0e867da46e4d5bbf5db7a8d6063b54dd6a0e1bfeb7743c676 mainnet-01405-b0ddf49d.era1 +23253a01baddd96f8dbdfffe9d1e078426b4677d3d12e5c2e7d5cd004ea60faa mainnet-01406-651ac24a.era1 +f8579d34d6bbe198a7a8827883b216f49b1342dedca2598fdf6476d868d282e6 mainnet-01407-a76ac2e0.era1 +50ad5742fac9ceaba1c8f4b470b4abfc7822d7ac2a33de684ef6914f748c9ee0 mainnet-01408-209624ba.era1 +e11fd2f4b0ceabb5b87c78bb98c1648e89af91a4e874e5f87c1b1ca3eac4e262 mainnet-01409-af729a25.era1 +cdda1247b3e40b95c52348edd0dc7adf00295c49e5e690abd7b5ef6974280529 mainnet-01410-a53b25ca.era1 +ec46ec3277de6aec68923500bdcf2ece80317e51bd8fe23062c60b9a93e5f364 mainnet-01411-00cba9ae.era1 +cb214c5b3d56da00ff519257e9b9c3753d2cb3293b8d3e8f727b74f1d4912ddb mainnet-01412-da3d50bf.era1 +94cbf93ced0fc4aa95ef7bb9cdbd9e22771e876e630e6fde313851559eed8f60 mainnet-01413-fe77f727.era1 +47d326864b404343670f3d4017c74fdb808218909f9c5fe5298668e1cdd403b8 mainnet-01414-1053d23c.era1 +2bdb9cef6e02b7435c7cdafd0f39973d45b3e36df274209287eb311d291947bf mainnet-01415-1f9ee408.era1 +449271b516378b806099799baa60f6f6d18994f2b2f228e4a2895691ff244855 mainnet-01416-162769df.era1 +d5e996beb771a4bf530244ca6d7cbf3e56f08ac58c5a359cab963c66e26514c1 mainnet-01417-6904f505.era1 +7886d7c1e992bce0026ca17feea1bd2e4ba79a044c4ae90c04565a82a1825e60 mainnet-01418-66837954.era1 +b39df5fdcfbf4b5dc24eb93b64b9842405bd341a2a24a3144859874812918e1c mainnet-01419-55bb8c9a.era1 +54f228ff43b89b77285bc0e0b5455db5f827f54a19cab17fe1ec8ed10578be71 mainnet-01420-37cb2c92.era1 +c649dfcd02b17c2db486d4b615d85bb21c1166a18d1c33d305c356c15f763c63 mainnet-01421-05b6cf1b.era1 +fcc2ebd17639a49670c82e7a54da022a48245d63f8044d088facb416d79d49d7 mainnet-01422-cf249616.era1 +2620809bbec0a5182223dd203fd74b1dce08122eb8c616cc4ce9443560b63657 mainnet-01423-8500cec0.era1 +0f95398ea8a61b7d617990214131cce800ac947722bed77fbb28bc8f3e482a09 mainnet-01424-f9b23ca9.era1 +4635f7300b5365a9ff84e6277235b580d679464518869c52fe0c071eb201534a mainnet-01425-2e9e77d2.era1 +fe37f157d5aa804adca28571f2071703a2498b6f5fd80486391ccecd0d0e908b mainnet-01426-8c071005.era1 +663f0ed02b6b32aadb3c2a703e62ae0d2f878207f6b41a95f86cc7a467279f9f mainnet-01427-53b8caf6.era1 +5248d97d29eb6998b52c2a1cefab09b16c7c031f673176c570b62341653ae031 mainnet-01428-68be6c9f.era1 +7811c5e4bc35527005852c8435520d59cd902cd41eee2e0cbda7d4f049cf11af mainnet-01429-12a34f39.era1 +1ec5eda1110cfaeec844abc4794042836c8b4dfe9c16ef0a79f6c7342f76d062 mainnet-01430-ff0a7be9.era1 +1ac28630745631a76edfcdb5f2e09fc1c089577c427e28a743db0335f5e48eb1 mainnet-01431-64d4fc5b.era1 +06087f72cea1fd49e3aafa8033adfc4e098ee49bc20642a474c85081c8a70ef7 mainnet-01432-2205cdfc.era1 +8a0fc7e9fc2ef3b49590c1a17f93b7795d13dfe863f609e4ad6ea997b1f0b785 mainnet-01433-e4ccf743.era1 +d58b8c466c548174751d212b73e15a8455adee4f71644080acba1155461bb046 mainnet-01434-77202058.era1 +e3945c73830db6b9a6f7b5fcd17ac292131a9838bca96df6f7e5cb8d173e1c82 mainnet-01435-467addf0.era1 +b6eea12a891a32bb6e8cae0be78406cd260bcb13148ee22ec498d359e9284036 mainnet-01436-8bd916a8.era1 +258708f393377fec046013eb172c2667c6707b9b647698c65d7eb44737c56617 mainnet-01437-332b343d.era1 +0f3939607d194332c2eb9e653b430bbc86e0e2befb2a9e14eeb1e161b32f1374 mainnet-01438-595d9cbe.era1 +7bd68818b8ccad9090066f14dc52845be8668f19ea461d35d850154a18774e41 mainnet-01439-aed3cc6a.era1 +30ea77ccdc53fd2b7034000a4f4741ce53d3580f9625db147a6a63ed42ae2c55 mainnet-01440-41f0520f.era1 +4f7eb2c90307cfac84978570170284598db309420e89e9846d28dd22f6671383 mainnet-01441-e2a38fbc.era1 +72040094c53ccc955ea8e57cf5af40413d1c07e24f8d9aa17a933fe31a39869a mainnet-01442-ef37dd9b.era1 +23acd9d6182cee70eaf6ca004b371827712a13f4110f71ffec7b71e117ba812b mainnet-01443-145a661e.era1 +f020fc7faa8f6dfc515f738dd978dfd6ae9115a65622a861c3ce22c7ec14585b mainnet-01444-9821beb8.era1 +d39a4606cfce240af8de5edd4949ce9687fe7bb2c1111a1121df44f8a5a67cd6 mainnet-01445-728597bd.era1 +d1285ce2512ddb1689343ca9d261047c75b789068b574769b353726deac9cbcf mainnet-01446-5ad80991.era1 +e2a7cffb91e452a16be34901d97aecbaaab166ab10deffe5643bc7063e94472d mainnet-01447-6b884cac.era1 +947fc8b6c496bfe9295cb43245a33d669171433e03d8c99e5eacdaf8c66a9cb3 mainnet-01448-869cb3c7.era1 +c0de9d0a34ac81edd5dd2fad777baa87a0f02631c4d5d9c67893bb2dece5bc66 mainnet-01449-5cac1bcb.era1 +9e94a360b729c2b7f5528cff27e651e0210c85e6c15e2ce329f2b97d5c938f74 mainnet-01450-b9278411.era1 +0b46c80caca37c2f6e81c71fe22bf7e8dcd7eb9820945170c4db1cc604642a29 mainnet-01451-32f5e0e5.era1 +029949a8d6a72ad330a3ef4de2a0fa0f9dcd19fa0ef9acd77567a76f0e66d4cd mainnet-01452-b43138d2.era1 +655e1d40f4460af26df21fbf85fd19f369d6ceaa04b94b662dbc99647179ec72 mainnet-01453-34bcded8.era1 +c0d73fb7410233d63eb9530dfd029d37b626b96dcae5ee67d53005613c2cd162 mainnet-01454-11f80fd3.era1 +3c7acdc8951c4da16a59c3413ddc92242e7939f4c694e7afdf392e610d42622d mainnet-01455-18ea77d0.era1 +8a0daf91f9c12c1d42d974d081e192dffcb4c69bb392041e2733d96f0c482d52 mainnet-01456-f1aadb7c.era1 +f4c13615dba720a8f75fdf351a3f508bd976c4ae83d67adb3cc1d2af8ec9c152 mainnet-01457-d00e9e6f.era1 +875f458bc26979c34e221a0e9f4c6d9951a69081e88a1a6658466ab490b179fc mainnet-01458-7ffab11d.era1 +e48729e17d5606eff2088dfe1f918d780fed77f54cff97d00373e6ecf9cc1027 mainnet-01459-6039b280.era1 +1a272e704e29a3a720c8d494b4e85429e8ad8efba1e86d03d244e42c305d607a mainnet-01460-8e889e14.era1 +57219d1e9b7331b75cdb5680b4a220cb29250d3a946036bd1760e488bd503c56 mainnet-01461-773c0e11.era1 +d644c1708407495e82dd2007d3daed7893c5436c8163b8d7ea12ddb11c626882 mainnet-01462-df1babfe.era1 +328526d39e8a4827a45ec6f6aa3207f20c75d4a8f234b3479ddc53507ee973c2 mainnet-01463-6d328022.era1 +0d763982c38b56ad61733141aad9fe28219639d3afc1b4add07419f8fefdbdd0 mainnet-01464-1c7eb43c.era1 +bddecd820bdd0408dc8e89728fded6317386494ce38b163846f2e69d5388e9f1 mainnet-01465-10ffef9e.era1 +d9595c500b8d4b83015030e238e9accca337b461b8ea05a4ebae657dbc4b2847 mainnet-01466-7785bbd4.era1 +c3a1ffeeb26017bcbc60c02e78490ae606d464f96c1415e42c65a59d5388e823 mainnet-01467-b09a9b8e.era1 +7f1fef22110077eaf5299fbe2b68dd2bc8e32283ec71baf34166f8472e9a87c9 mainnet-01468-1919baf8.era1 +8294a9de1df2f713da58ad385fe5f998bb7f357cc474132515551fd07884ac37 mainnet-01469-6338bf7f.era1 +4b74fb9a3a8edd99ac5635c148618a0852c669f63df0c99659ec95ff3b88e82f mainnet-01470-46fb7a9b.era1 +24f0d5f8cbaff1b381ff0c856ab75bf59e8aa56d374e5848fb95ba6fde700f24 mainnet-01471-18fb7c8f.era1 +b7818f256e53486839ba6d6768f0600079597b9f20522265192ac8030c745e49 mainnet-01472-47a32b9d.era1 +7b1cf4d233aa7ea6fa0d870d1a7e1ab30670ed65a145acfa1f1b80969a749df8 mainnet-01473-333b7e86.era1 +8cd04cb58abc7da45f99e366b10bb73ccb7dbbdff63e9707a49cb7f537875c2e mainnet-01474-fe24e3c1.era1 +cebec68718bfb963d022aac9913c3b8dc753bd91c89c922e6175091bea150f58 mainnet-01475-81c78370.era1 +0ad4e0b20d58b882e9d6aa2c7b175ae71b68a378f8fb2db7ae5ce757d47067eb mainnet-01476-857187a1.era1 +16edcbdd31ef409559db026df862893ec3d55def3f6fe3b89df08f11ad198fa4 mainnet-01477-ad00e0b4.era1 +219f03a7b1d6b28beae01894b62223d9b14a31b5170e160911bc198d2fee19f7 mainnet-01478-8994eaa1.era1 +11e4ecdc331d0682687fb25c9b0074509721401a1b3805d7e4277c5b9735002a mainnet-01479-cf4abf0e.era1 +1004791f7fd598673fb3f1de73099581696dee5d58df7e3ff3f92735085f44c0 mainnet-01480-ae5f5367.era1 +aaf7a7f356371cedd630168afa7b5c5c66ea4b0f8f4f30b9f8f770d0be2cf5bd mainnet-01481-a121c10e.era1 +4359f1ae454034430f427926f775c92837066e8b816b8e77b48e315e5d3859cd mainnet-01482-2110d171.era1 +d85dabca73e870a41464dfb9d0d621e9302c7169d397b0d266291af6ad74c007 mainnet-01483-56ec2844.era1 +44ea248447182e4fcd1e02ea0029a9eb29271d4e4be6963113994ff772d4206d mainnet-01484-ba8ad4f1.era1 +159518bed983ac81d742d28e0d9c36aec9da2609bb7831a3ad3b692ac7045835 mainnet-01485-c56c3ef6.era1 +96e628e5f4548dcf3d691be556d909893a786d4e743d0c3a7b6fae6e536f113a mainnet-01486-57bc4104.era1 +631a3635b6102e3025d7a81a4f3d203a5e47907e37aba13ea8127123c7b30c55 mainnet-01487-122d8e0f.era1 +5f08d89cf44c555c7ae594a113688e8ef660982db091988e223d86e46837b324 mainnet-01488-42b25fd3.era1 +e9ba8cce1fec966b0237eac6d47873cf198b0bfcd9426ee4484c3dac7fd94d5e mainnet-01489-b1acea4e.era1 +a232396cc9d4986fd24e55121f721c85d3c7138853c32c63687b90ae37804951 mainnet-01490-34280f11.era1 +4fbb2af1cc0bc00e2dd5140da8b178431bf2c6144a54dedb045f98517b2e61dc mainnet-01491-503bfdfd.era1 +b31814c8d0016b4655779c448cab692824246c916b3c7940bcb5e31f64644ae6 mainnet-01492-1b74997d.era1 +1cb371fa71fc71d0a4a33c17c3d200d8b575601b9cb02a66353091109bd22e66 mainnet-01493-edfb26fb.era1 +189a4097e29f4e1cace72fcb032b6e89ed1601fc56c57a22fb54f072a93f0d20 mainnet-01494-da405d13.era1 +da0d7984339278660e96c34e34f747574c6ad88f06711b2124c778ae809a17aa mainnet-01495-3efe0cda.era1 +ee1b345b735da46f9d6f5d3dc3bd5a29ac626fe846ff013d940ed3835a619217 mainnet-01496-809b00b0.era1 +4d95a40cc150dd87d65db07f2226b41a258709d1afe44ced5a3b8661e2406d66 mainnet-01497-c6923521.era1 +eaa06eef1c79a08df66c3c880c944b8d4b73764d105545ee657d2011cbc48571 mainnet-01498-2ef9dbc9.era1 +0db058c7e692dee965d6c89c3ad3ec2289e157bc7f21e0a48631c13e616169c0 mainnet-01499-23a65ef9.era1 +737b67182d604632e495d3419afb72551b56cd2b6efedaefab26106258013ee8 mainnet-01500-17e97c49.era1 +3402ec4cb8df4a0e73da4105fad3765b8a581eb93fb0099f326cd363dc877e47 mainnet-01501-38815f39.era1 +1dbddc6f53480075be9e42447712038bd58ef70c87535f1d54fe2c0b5554f2d1 mainnet-01502-c31ef6bf.era1 +a27a320d801023f199b6151e982305d28bd09b339a11c1c88bfeebe9da0ffd7e mainnet-01503-c5393ba2.era1 +c7d1660bb9b1970884ec8fd0bc889bf7ef2c164962813c524136126e82933e83 mainnet-01504-b478f53b.era1 +d2ad6311cbdf2dd49bc6e407790547a53d918229ceec26fb9d6b72e8e51e3eb1 mainnet-01505-b8013f87.era1 +2a3b61fd2b53680cf3411a8436c1c81c2edc790b9f314b847c13ad0925f8c331 mainnet-01506-f23ee995.era1 +dab1a1ece8dc43fb51c020ea4043f07bdc3eefd049de662b4845c8632d1a8bfd mainnet-01507-21b534cf.era1 +5954cdecc5f4781057bd6d0d979ca9433fb6829dce045aaa1425da4bd3299d91 mainnet-01508-9fbff7e7.era1 +05d9c685cef8f8d2f140023521426a6405db9d954df79285f15065e96ceda595 mainnet-01509-e5141a21.era1 +f44d5da91e8975ac61a11091f7c1f4df06062bba7bc921e26740c4ffffc5da4d mainnet-01510-87aec91b.era1 +e9f8f6b2f4c433f171c843d7f877547f5e2eae6595e3831c3fa8cd7db25c3a2a mainnet-01511-b041bb19.era1 +2141bfc440a25c3709c21bbec11af208605fc6acbea3758b85ee8558933f4c35 mainnet-01512-e41b5ac2.era1 +66d286405e536c2cc59f5cecb90e963aabd69142247234e54fa8cdb5850b9448 mainnet-01513-56a66926.era1 +155660bd87dba182ebcd1bf5c60d3472063e9db7d26a586595e9ab6006f8bfc1 mainnet-01514-898d4f37.era1 +0cfa6d8521e4895b371e130b2f44004901585592d8c2365ea872c2cd979763c2 mainnet-01515-935b5d7a.era1 +491efbee84d36f57e582e39895f98d9e774051d8decf6d0ca165260702cd282e mainnet-01516-4ad5c3e3.era1 +cc6ca31096fc3ef2574e85b786f3db1f681b4707ecd5be234a1d7dd85bb6e257 mainnet-01517-be94d5d0.era1 +4882f5168a45010401d472f7742e91a5c0e9057cb1de13aa1673711bf4027e36 mainnet-01518-40fa62d3.era1 +71933f720ac13c6dfa662eb622549110d94a84881834d0eec8cf58d33ac4efc5 mainnet-01519-110e8af3.era1 +ab1b918ba41b10c0fb7b2ac74a7e0016c4a1013a9ceca3aa6e7872f4800ecc25 mainnet-01520-6e412e20.era1 +f3ea5d0379e73f0cb04f0525751dea63547e952863f9321375c7e5a985b9c35c mainnet-01521-8ffe28ad.era1 +dbe4c506e0acf2a76a28ad5a04967d6336b3d58ddfdaff5f714445e0220b693a mainnet-01522-e93cd0a0.era1 +185d042fb5e6b4c86cb2108ad197e1cb179c896a6c593bf476da4de1bcbe1a74 mainnet-01523-77f19add.era1 +73aafe37a41815c3accbe83ae518d302339c3a6ba9e8a8c2283a53eeb2068879 mainnet-01524-f583c727.era1 +6d7423924d47b47aef930b909b1f9f08730f47f2d7210be165a144c1165859cb mainnet-01525-326bed46.era1 +06e4b72e68ad8f8471827d1b5622b7aa5eb96b507c23a4160496e1f8e1cdd203 mainnet-01526-558f5bae.era1 +4305fcf21693115296a43a96f9bb46dc59fa89911147d1f711e4b64e8bd16aa6 mainnet-01527-17af0f4e.era1 +2a31614752754ae8046ddc39451171b291adbd0b5d9f7e8f5276dd5943129206 mainnet-01528-b3d28e9b.era1 +991bf22dab16b2108ad2b449102520b64c5580f23e2554f35da579a8e756ad6a mainnet-01529-6ce3df5c.era1 +6273cc58c1618d4dfcc71767a2ff4d17731a8eaa7fd64dcd7b285b30aea2422b mainnet-01530-70b295e4.era1 +b61fa4992aaa5215fea67d4b81f9f71ae273ebacbd98dc051d82775e834572d6 mainnet-01531-5694754d.era1 +3aeadb98bea934112453d371028a815ad5720782b834e15f7a551b8e29188975 mainnet-01532-b7d62fb8.era1 +89c0868bea5d3c6d08b3040af4bcc913020fe3209f578e0f0bd6cfd59c6c5828 mainnet-01533-1c4ea22b.era1 +84447512ea413fe1aa379f54b0e626668ce35d66641d6011fac103cfbc2d2982 mainnet-01534-3f50bd7f.era1 +18d5bc1bddf4f4f2cc36750a1fabda5bd5d101e55ec599e8e7fc819100e2d8f7 mainnet-01535-56e96bf3.era1 +d967e1886bb929afd0895eabea57fe8466cd91a38627a9610c9462c2f118cc54 mainnet-01536-4c0b5a25.era1 +dd6f936d078566b520cc8cf96b05a93f971270ac653b2dfe72fb6fa44029f8b1 mainnet-01537-b84376f6.era1 +52181bcf117efe1a469a9918c2b83212dff8bb394c413161a6e5f768d28ac54a mainnet-01538-a859e797.era1 +77aaa8c1d10420f6e6978fa46d00a727290439d7d7020a31f85a9523a3bdc8d3 mainnet-01539-b8f73328.era1 +fff50cc1d5230d7193097a94b57f6cb616dbe1c1c5d519a7cca6c10cc8972543 mainnet-01540-5075b4c4.era1 +6ecc4dd87a259c2621f7d3f7e4bd23d64c7b5333555634d0fa2f9e7797089ece mainnet-01541-d9a68897.era1 +3de40d9e684347861180fb1f164cc58fb42481a365f80062f24decbd3ab32cac mainnet-01542-4c44c323.era1 +5fcdd91fff82c163ed7cdc09302e77201c64b6bedf9d15a3957dcdade56e1a08 mainnet-01543-ba0d8406.era1 +a01e414694eef9f1f2984e7ae2c32d11d31900e39ad48db494fd63860c96cff8 mainnet-01544-b730d9fb.era1 +bef24f31d20a90421c90e8523a643c051141807306342c6e67303f6cbb03f4fa mainnet-01545-2a36dd0a.era1 +93ef8d3faffa706ea8dfc12345a2c7b1ace54c91d41ddaf039815cf1f428a40c mainnet-01546-cb6390d2.era1 +bb7cccfe47eb34649f0593e3dda7f82916661cc2262d119a1a7a21f3d860762d mainnet-01547-7be29447.era1 +63ff4c203e7bbf73f44d58fee4d9f05c4c1b890dd799fa27ec398b07da574e62 mainnet-01548-d42b2e59.era1 +39e8e2ba83464387f5997a9e46188defada3d96dc957c0e93d525a1c20e95c54 mainnet-01549-3259acc6.era1 +4bbcc6756e221e0c2206ae5c858da6f731b50c7717cfb2c73eae57ad4fe0915f mainnet-01550-15c4efe6.era1 +b3a8f035e443ff1e42baeecaa9af0df015a6c9b2ba9329384c6694b3186aa358 mainnet-01551-2d99a1dc.era1 +010d875ab6d7f9ad167aa7552c6d2a36a692cbceb7f8a1ee141828277dd0e2a4 mainnet-01552-ccd34c1e.era1 +908e58301e84862a7606650611347559345c9b0941b53de0e117267e3a28ff1f mainnet-01553-0f3371da.era1 +5d82854cd46149806494fd65362a6546adda300639525e5fccfb86c5a8b4668f mainnet-01554-6acbe79c.era1 +d0248d1e7cbc9a9d1a2118a45928b1979e191a91b391be76160cc962c976ed39 mainnet-01555-975db585.era1 +8a6a1d1e844167fe7579ddb3767db47f330d35259f2bc041a553a62000ae9609 mainnet-01556-f8d968d9.era1 +6293f17d2943184a9c6c44390bb65fc800212a2cca1de7ca320745955c0ebca4 mainnet-01557-5250d82f.era1 +55c059c3ee3e2284f89a1fb98a8cd4e657ce8ba60ce077c68410ffdd4b54e9a5 mainnet-01558-8817dc1d.era1 +3f873a8be5b8addbe307dc61ef53a110f2ce55a2530259c94e9a230d1f9e8815 mainnet-01559-16cbfdac.era1 +861b1d574ba05a80af960a65738802957208441616cfab1ca8b84828551cf5e6 mainnet-01560-cbaf55ad.era1 +cfa188c57de44897d2c9aeb8953692f89508b4e8486c0a5b9a775d345efcbfcb mainnet-01561-a12fc593.era1 +06afd26026b4031cc3e8de4e36b46c62573f851a38b9fb90436177a79d3f4427 mainnet-01562-310c6496.era1 +885a2881bde1a7c23629f2becccf2ceafc70d4b899db66ef1b8b8cee7f9491a7 mainnet-01563-5345e3d4.era1 +16c23370eced457e061e57abc676d0742b883c7fa31cbb9c3bfd6d4b20a4cce3 mainnet-01564-2280e4c1.era1 +787159001781cc1bd4633faa7b695d4ca247fcd53105ed63f5e6b0f9efd02e2c mainnet-01565-893942f1.era1 +c6d6439e53843b65f0f045af335e62ee9369a6c32e8bbcff4d1e40dbb4262357 mainnet-01566-237ef759.era1 +b91bd8aa86e20d442f88e8c08281413b1852231380639fd17012b385e8848abb mainnet-01567-1aec3b6e.era1 +aa96f4dcb3c01646352bab9f2b971a68eaf282d39a8c7ea31a11cfe434d4222f mainnet-01568-fc92fcf6.era1 +06e6119905bac7ee9daa9ed5916e0deb49c443afb55642fe1507c4d785d5cbb7 mainnet-01569-eacd9d2b.era1 +b879842b5db2608f1cedea127444ca7822082b76cefeee710501f9109c9ac78d mainnet-01570-a22bf288.era1 +18cb3d19703093a8ae31888c221b5569f92cd7c8813450cf5a12e83818114e68 mainnet-01571-1a439d07.era1 +e6c86e5e1360184283df511875918831368ec561c45b0f29aa04523ff6efc061 mainnet-01572-947ddced.era1 +9e346d0294064ba836d65ed33fb1c7b76d795754f2d7f08619c4d7f642e1ce03 mainnet-01573-3a831bed.era1 +b88fcfc397b8e40f1b5972027e0d4d06f8651fc73e5c71d70a5a62aac371b450 mainnet-01574-9028d18b.era1 +2252964fecedc20208c159243232d8a7d7057aaa330f748b9be887681468efdf mainnet-01575-598f030e.era1 +a20354aa4e90169203f04f32060da81e7d9e546ea4bb1eaf04a18a63a70ceb4b mainnet-01576-f9043297.era1 +42116706a2db87cdd04b52d956a4c47379f03233bf226c8d275eb45b1f5984e3 mainnet-01577-8a587dfc.era1 +bcabb605925eab14d7a9d8fc2a4b7cec4921a7f5d0533abaf129f8f4b8f86f75 mainnet-01578-43c1dcc3.era1 +2627f3b6b3007a17aabede2bfb9231d36e44e8eec38703e2f3ef7e7c984c4e0c mainnet-01579-92a983f2.era1 +0bf8f2d6b46e4418738db01923d56b76e250f53645bcfa0cf530bb68f3a9b635 mainnet-01580-1c243107.era1 +75bd2ec19aca5a74c590784cfe97922313edfeeedab25355ab285d3ee5ca70e5 mainnet-01581-1f943824.era1 +94d703236e86ebd23536a8af4d8f7b060519523ef1afbd65f342a5a191a7f100 mainnet-01582-de1cad89.era1 +ab1d6916d63d63e9daa34d90e36febfbe50642259b33c4fb9ed012e104746e8f mainnet-01583-2d72b6e4.era1 +a771a91cd9bd8419a46a5fa09153141acab84a0817a506a7c957f7a5082e28cc mainnet-01584-393498cd.era1 +adc1c64e9e02427ef348a135592f4c4bbcc903436387ace59e5bf28f6635fc92 mainnet-01585-fa098e6d.era1 +ef6aba72154d336723d100cba0948084f9e2a7ea1541f72c5138fdb6be2c13d6 mainnet-01586-379cb7fe.era1 +d0a6465c5fdac882bc85105dbdb28099830dfe60db68bf288d80911be215e795 mainnet-01587-62641130.era1 +ce7424f4a11798631796a5385aa390cf970d14d14ab9fc0e9a79919fc8c808aa mainnet-01588-63224c34.era1 +6be4cd261a1ec3c2f122e9c35d74fee83185021d89d1d42efe7adae8dde85665 mainnet-01589-a972b2e4.era1 +efd280edffc259d29d57cd36b89e34430f1fb956d4b90d37127f641b1a006063 mainnet-01590-1370376c.era1 +96bc3d9168aa98775bab57cc1de3de2765f233d633876acaf0e650cdf5ab8187 mainnet-01591-3c5940a5.era1 +4eb2803b800798fbf132a46d6a322a5e1d3c6f604c69c96b224368f88f1fbeaf mainnet-01592-fdcf0f05.era1 +902ac965eef864615dce6eccb5acde91b0da565512b646d1a4a53a450f41feff mainnet-01593-7779354b.era1 +df62bd03b138c1f024b3565cd5d5d9c9ffb86217b6279b6c5d39cfc1e16ad0db mainnet-01594-fc12f95b.era1 +b0a9fd005f6825f874b44b5da480046610142772cedb58c4be7fda04ff1bf2b1 mainnet-01595-2118c3d0.era1 +010782b7a091404af6232bc8768ac499423dfddb08ee1f62f9d47174b41aff5e mainnet-01596-f94bdd17.era1 +e8d40720e7be1738f3ef18c4af35e0f49e88ee61a55dc9d0e95ff024a90237a9 mainnet-01597-aeb7c436.era1 +31dc95aebdd1a2747077e52ad793d2e62a7cb69d77ebd19828074a15989b5d38 mainnet-01598-f56b793b.era1 +79f502abb89bb0eec42f3ab80847577e1e272f35f2203eac65b57729c77d27e0 mainnet-01599-6cf982a2.era1 +03fafb175c2a6a24c19f82ab9f94e06938bfb0f9cef9fcc94022ba23320f82f5 mainnet-01600-c6a9ee35.era1 +2e272107d43417600bca39785659ff66168182e38b2ed763becc5de3673fa22e mainnet-01601-0497bfd9.era1 +ebbec792fe3fdc063a260131febe6668a477ea7abccdfdb96ed8695832dbaf44 mainnet-01602-9a50fff5.era1 +a7282f54e8595b1d4c2abcbc64d378f418abdb1daced7a64b27e382168eb89b3 mainnet-01603-a1d88a26.era1 +dbf9264d64b6a8ed7b7c956f4f6d6588fabcc9dc447247b215d8c8cf16d36c85 mainnet-01604-00e0c17d.era1 +1100f28385ae46291c8f573b4e8c994eeee0609d230820089258fefa3ec104ca mainnet-01605-8fec155e.era1 +c99b73fdf1034c93bc879254190b3ec81f5d756481b2383998005865c1001625 mainnet-01606-7642a7d3.era1 +57248072c17b75b5ebb7ef087b9809cc08fc6e7e4d87a1bfe26789dcb4b2351b mainnet-01607-0a337fdb.era1 +89169750db27f32763c38701c7cd4ad2fbeaff06ceed6dec1dd69f8c71527a65 mainnet-01608-2b8f0227.era1 +b3d5edeb90bda42709bc5ff1793774c964c114d747d10d2e30d38875461f98f5 mainnet-01609-ceafb201.era1 +a0f60c0036177ba34aefd82bdd0c6d3d483279aa61a6b531cc62c275da00afed mainnet-01610-99fdde4b.era1 +6cb64bd1346e0a19fc61a73a100cd32389a54751147379d2e67936c7dc75d6b1 mainnet-01611-0d642ed6.era1 +3b6adbc75240ac148b57991e0c603bf1102f38021b48c8252760c2dfea463639 mainnet-01612-01a109f9.era1 +b0d5791e2c0e618beb451c897bcd2c8310e7d95e6db533a62ad720480e4003cd mainnet-01613-409c216e.era1 +cef35f80ebd7396e051e5c475c6aeea8721db6fa8f96b5e2d7c7aa0e52fe0b41 mainnet-01614-608abfc5.era1 +cc9e5f97cf1baf05026b9f366523a7257b762498d873189df8cceca0b48fa764 mainnet-01615-546296f9.era1 +9b9bd6c05c18a663aac7ef90f0b078d2278766710f13b720eb3ce0010d8991d9 mainnet-01616-210f4a03.era1 +3947b044d7275b95ce0d5c30c7522020d7e6ef63ca4477291ad1ee4ae0f2aab0 mainnet-01617-f300016d.era1 +2dd5cc29c9e516373bfbf690f37d6541f83d44530d5f1adbeb46f0d5f1ea4a8b mainnet-01618-d126355b.era1 +7e327c4c515a42c2c67a67ee85c40fe1a5fa55e28f9c066da63b262f62b4e740 mainnet-01619-fccf39ee.era1 +947f7d0ffde5c24358b6c924c54354bb35982e0324d81dad142b213a097bcbc6 mainnet-01620-62719ac2.era1 +7e82249cb9924bd9c7df57dc63917afa4247ae696b0e36b8548d5cc826425ddc mainnet-01621-d410e2d7.era1 +4a60acb269f942c6d6fc5edb66d09b8f40cf6003fb8ed2dd980ca6e9d4c78ee9 mainnet-01622-3a2643d1.era1 +83cb30fb9f530a63729e6632381adf1d1ea3d9790c1a8a3885493f14538b7a6d mainnet-01623-b86b68f1.era1 +f33d2dc5910930251ec6fa6663d608e14eaad79902a25b9e0e1c5923060cb451 mainnet-01624-79abcea6.era1 +4d978612c4d21afae90bca1876bad2a15e11d2529c0eb5519b7b8cdc24deef8a mainnet-01625-9d2445ad.era1 +6e73bf434de732e0aed33793de14dff42e6436685ec3ba9a012888877e812a01 mainnet-01626-a0719f02.era1 +0de06257af6217f6b1e48a4ecd834d2f0eb38e7dc3f11350bda7403820956482 mainnet-01627-3825eb62.era1 +5750b01adf5e5db444ec333ec9de9d65419010007df44a362f58b59dad668fcc mainnet-01628-49d1a71d.era1 +206bf4ddd8c95816e9116daef0c06a547d208a43d1dd42041b799aa37881785e mainnet-01629-1ec93093.era1 +1d8eb98edaca0ff1ec5ce89dc6e9dc46454e9f90fef71f1d0df94b3016d95fa5 mainnet-01630-d52d9f3e.era1 +56c5864a3e6f2fbbb66b38eb35243d00522222e47cbc17c798e861d4323818b9 mainnet-01631-438a5c23.era1 +09b7d3e9e045731a6889a463e2cbafbd4bd459eb0e1dc19dc188c0396f2093fc mainnet-01632-b811a1ab.era1 +0b9a9ae0174a8cd5d0ea944001d43dd2c919ff62039662ef4ba3397f1791abc1 mainnet-01633-2dba253d.era1 +55c727428d8d8d2b2a4233b3ff9cb5fea6c66c23090350f49aa2fbc53b79636b mainnet-01634-c0975217.era1 +83a255aff9d3c7defd78d7c17850ce4df4ad2c10f114a0a5d40a75f099baaa4b mainnet-01635-926f0429.era1 +edcd2e9493d70b3498a113159ea7548075d5f6ba03ec7dc441447688ed207d65 mainnet-01636-88bc22af.era1 +68d93f5aa6c13075badba3c6fae3ae8bc0fa357a80b9bd1539cb249d9c414879 mainnet-01637-d10229ae.era1 +d18c50eb79436b9e960c8fd12fdff6925d901dd8e2c26f4e08dd4178b8e3e1a1 mainnet-01638-f60a479f.era1 +91baa2e3f7ebed254f3753afdb5c8e9666553e77c7cd651aacefcc97c453bf65 mainnet-01639-45574d29.era1 +6d2f5a87fec391b024b77ded2cee7f2c9647dea36d48a5d7876ad59180e3cdce mainnet-01640-f4d925c6.era1 +1cffb9c6dd88abf72b7746754950a27d8e215715d44c402de5b61838aa7186f4 mainnet-01641-05561645.era1 +438300a7f95a8ae17aa27b64d6500c70e6b2ffb908f968879772f6321b17660a mainnet-01642-bffe5d04.era1 +fde6789e767f290c4734b9bb96704029923f2c6094ce2458e8642af7227669ac mainnet-01643-db04ce69.era1 +20b99f2db27430968a5f22db2148d37abb048e5733349336241a62782b33d35d mainnet-01644-836dd3c2.era1 +78d93ddb94df9a2595706c2b7a622d90b8f06c407af9d5a895b09bb625732c09 mainnet-01645-ccdd4d37.era1 +e855b060e1cee6387a7ce6b2681f1c3af5ea6803a362efd422f3313a32046dc8 mainnet-01646-df78d1aa.era1 +28e21deae06a082bd6ad582c893960e0cb5559de8c6f003a627dffb4a4ea302f mainnet-01647-bc1a2596.era1 +e4ed9e14f0d72f721289c4e806a6d07e6edf81a01b05b9ef7953ae8e155c40e6 mainnet-01648-ba444550.era1 +b005a1085fae3f049796cca6076dee3cdc246d798b1449b6ac696646ece3c1bc mainnet-01649-c772985e.era1 +6499d2ce493545fd5ac85e0aae30ce766c08ba6aaca6b7a94fa61089834ff422 mainnet-01650-0cd44f84.era1 +d2e6f7286e6380b83118d83d27b9bcb6d05b4664af7c79a0ba930c0d19e74db5 mainnet-01651-108b8139.era1 +06b4cee7d62b17bae99db042addddec5fbc97bd7b882c2ccebc2bfbe09d9971b mainnet-01652-92dc53a4.era1 +1a72131ec11e08bf5e482c006804e56e2ba7b1586f6c8cfb30144fe66a1eb2ff mainnet-01653-ac1c8ccb.era1 +01c2b842a7eaf97c1244ad6ab451aaa6384c58dd2311a429242603fef836246d mainnet-01654-2f466ff6.era1 +35a9484fd611163daa74558d7be708e0e262eeb17270235fe61da80b6e5baae1 mainnet-01655-4dc5edd2.era1 +96837fa185b1e407429e2eae00e0cc40af64b3a9b51dbdb9fc83002e1e62730f mainnet-01656-72d321b5.era1 +e4bfb4fee82e660922baa7a7a0b4073739f863d5a82d447f1b2e2c14d96ee4f6 mainnet-01657-d0684723.era1 +38274fd658e3a2225814dab185807334c4a2377fe00952935f41103453bbe7df mainnet-01658-651f1c70.era1 +4352318a3c8a480a115d081afe9e37711e997f96904722efdcc23cf16401a4d8 mainnet-01659-0013c08b.era1 +e101cedb4dc4dc0427182376ad6cbf02a4b5ccac9fa079425819481b526080e6 mainnet-01660-7825d5b2.era1 +a06e6331b045cba8ca1a31edc83f30e4de2e6abbdd310f11e9995b2515f7990d mainnet-01661-84e21383.era1 +cf84a100eec66094997768ba35ee2d39d670c98ccfcc50eecf04692441af6fe8 mainnet-01662-a5681587.era1 +a837dcd4cdd8917d21032e9b45f2a7688d527810c4321dda9bb8530f52ef9d10 mainnet-01663-c84283fa.era1 +4dd4b08e0577e72febcb904f542ecdd239b2fdc6c37aa9900e937319f2e9e6d0 mainnet-01664-ab3af7a0.era1 +18428df15b49400723296f714c964a6c0a7f553cf70643a0c77c13ec9338dd53 mainnet-01665-d0adeec0.era1 +4391c6ff4a21e044e07acd9b2a34277fa903b26c52ddb4563be7bafd611cedc5 mainnet-01666-827de27e.era1 +e46ab921179e9f1ce080f2d9e9cdfd5fbc1fe86c324b7daa04a3be0a75f7070e mainnet-01667-558e2125.era1 +aea3af1e83809aca973445cb92d437fdd6c3277ec37ba5530dc68d1c1186a82c mainnet-01668-8d75bc10.era1 +0d1b9b762a6acc59cfe20ce1444ceba7b28aac87a367d8ad867c75acae1242f2 mainnet-01669-84ccda7e.era1 +4d273107762c163b629e1f1c93be2a90788db4534c1ee47caf3d201a1a3bb39a mainnet-01670-3490d679.era1 +e974b0c2a4119dc0b933dcc3a57bdb5d0c9b1058090a98fedcfbfbb775bad598 mainnet-01671-4533f7b3.era1 +9b9fe120f05946ba3755cb8a1a407c1473f12ecb48caac7d082983a7f23de120 mainnet-01672-abf10629.era1 +b35de36af93d752a2d0a3c2661f75945418f069f0cb4d3bfbf43834593274e9b mainnet-01673-d67018bd.era1 +29962e598e4aa1445f1cdcfc71228ecff08a9dc716ed22bb33bcf504586ed060 mainnet-01674-16b9e877.era1 +b4210ce62e93c251b47b9baf4a35ca2c512064f4461d10af2e681901ec888b74 mainnet-01675-35b0af7b.era1 +ac0a3ede1bad8761005d2f4ba0e29b47d46899039c30e6e65116daf06302c713 mainnet-01676-2753d9cd.era1 +913ed37b7280c0488f0b2de53bf8186c872b861bd1661e81a99834d4129aeffe mainnet-01677-e40b2ea4.era1 +2bc0af84a6edc0a2af8bdf9ac47d80a68b05241a9caa4404ea55a9822e48302b mainnet-01678-fcbdf0e3.era1 +63f5cee3827ce44ec03a277bb6af7e806ba240a59541fc9b36362b4089a8025d mainnet-01679-e5f342e2.era1 +df44e2d0aa327e3fd223579886f0da34b5c760bd4062202d2a565215b81ea528 mainnet-01680-af511202.era1 +a6c893c0bf0cfb25566d230ce2e9e701702826c670b58757a0122bb2c80c3f68 mainnet-01681-d0e6d3d3.era1 +d36179575d40b3c0d77d1baf8bb54d9db4029da6c917a157ffd42186b7c8ff00 mainnet-01682-66ddcfa8.era1 +977c03ba488a3aa991bd54b335b103df2b19039358736e77c2bb62c9bd61e6e8 mainnet-01683-8887a5b6.era1 +350e7911f40b1691f883cf1a45e5f1c5af895b4e6c69741b8b8630edef1342c0 mainnet-01684-bb0ab38c.era1 +42005763913f34a9f40a30bf34cfa29d33e4afbc8e37bdd5b59037211609e59c mainnet-01685-64bf045d.era1 +efaf03b1a2ce0c90ee6820aecafa4dc3c5a842938eea67b8682a7dee5a54d301 mainnet-01686-7a8fba5b.era1 +c110709475eab96bf1199fc05f12b1e93934bbe913578eee747c8a6c2dad38ee mainnet-01687-26481dad.era1 +e17e1ada1db485936003d7ac7e39adbb61ee1603e5ae612eab56434836eb98fc mainnet-01688-05f0a174.era1 +0a7ba360649c251244495f591840e2d6fd5573a78e3c722cd893955c2a28ad7d mainnet-01689-ac53e2ec.era1 +e08c6a38b0388a831182bc1ce40138fed2a801ad1c2630af40ac65c099414ad9 mainnet-01690-9dbcd976.era1 +a9c29e89fc7ce6ad5be4fd1d4549299d627c03bdb238372d73c3d9be25f12675 mainnet-01691-bb315f50.era1 +2ad967654e2f974e258e44b6754a77f56cba0afe8ccaa15fc439fbc12798c693 mainnet-01692-b877548b.era1 +106ef010fd0d5aaee7a0403d87e0f23ecfa645393b8c2bdc9e015afbd11497ba mainnet-01693-3d2c2aad.era1 +e35d0448682fb19310974dd10afe35e15e3ff72cdcc274e522a1d39ac48b1893 mainnet-01694-d42cdf89.era1 +3660dc81c12ae17c90ad39c60d53d66ae434b7501e2ef471241736a392e7f16b mainnet-01695-8d97b217.era1 +8ad51c98a5383b4f1b8cd4daa76e0cb06efb757442b2aa60051e30cfbf4936e9 mainnet-01696-49e0b594.era1 +31804fc49af12294cae633abefbb2deeb3edd68a8d88025bce280bf73e564e7f mainnet-01697-01b23d08.era1 +1e9a9b43703a4204766e4a7c5417239e70fada858f1d6736048c5098f4138c62 mainnet-01698-9c316b67.era1 +cb7065153c0805f9067abc909fa30e2710bc8ac5daf1e2b8a91f21ef58aa1d82 mainnet-01699-cea5c210.era1 +3ad35b036db0a06fd8f5643920e932a18c934a3a48211d0d29821fc5be1c62d4 mainnet-01700-3d9833e9.era1 +8f53349a041f87bbdabc195e8a5a8b5a6a298dea93a793bb06e8875bb720b673 mainnet-01701-073ad3dc.era1 +dfa4e8dfd877609aaa5cf823d6c6451521ce52b6ace31b9dd4709c1660d3380d mainnet-01702-9c454cbc.era1 +20e5075f81c16f0646712c90db5adf2bc7d700cb85eaeae6fd20ca25eeaf1c69 mainnet-01703-0d424bdb.era1 +d3c36455190c49fed972a9cca857b8003de46f05d60c6e274b3476987ce5b806 mainnet-01704-c8426624.era1 +a9c26e0771f6434853d481b5989dace7ab3bdac64b09236127fb8a776d11a908 mainnet-01705-850425a7.era1 +e1280b5098b064e034d683dde04136fde1501092956a2d1326e4332ac8050aa1 mainnet-01706-5f7468d2.era1 +c4797f0d3596853c7f8467a34216cae9c96577cd89e7a417cd11aa9faddeb8f5 mainnet-01707-68199dcd.era1 +001a05c2058846729183727e9e2f5c88c6a2ed60effa7fcaad1a065fee12e296 mainnet-01708-1bc39088.era1 +c5659c0e1114fcbd5d3e6fa7df0af74bef56a6e16b6ce39e161d710b02318644 mainnet-01709-ac6bdf4c.era1 +cdb9ec5a2786d0f8ec381e7196b8f29ec52dc115090f48262a06cfd5688ac477 mainnet-01710-616a4213.era1 +aff691d78a906fb94516900a6e00971fd804eeea5266fbb447a0fc02183652ee mainnet-01711-675a2efc.era1 +142053135f5bf9c9a08b04039c68a4172330bf4703867603f4d8e2882da1d602 mainnet-01712-b3466ce1.era1 +2351634486ff7748d00b61b4120b8bdaa5521098b5d2da3d0d59c0d13b92f054 mainnet-01713-efe87a15.era1 +d8a5922f7d26e436321ba56ffc2923fc0be6299d632f1f17befd6f99e4a63966 mainnet-01714-8a93b06d.era1 +63e092eec59ff940754f0f05dc96a053d6929a1da224a264dd2e9a0bf3ebfd25 mainnet-01715-c53f6fe8.era1 +54ef87fde748440e0347ff7c3e339691f75c664c93de1658b6f9111cc0403e89 mainnet-01716-beebbc85.era1 +b5d541673d8f32aa5041c5d46459b3423896fecc9568b1fc9c2b8468cc7892aa mainnet-01717-2daba19a.era1 +6ed9fee8c53ab7a9c16ed490aeee51f2d5a78393d670f288dfd77f36d4967d81 mainnet-01718-69ddf701.era1 +831c00c3a6325a2c7ef613d965e0b4bbfd67c64822e634844c27088beb059c21 mainnet-01719-ac2ebcf4.era1 +054fdc70c1d020bd1a910c6ec22351b95067001a52767921f460f2993ff4522e mainnet-01720-c428eb52.era1 +6b3ea18780edf83f361fe2d3a17c53303868c5c4c46a8139ba003f332fc28676 mainnet-01721-940116ce.era1 +fd954f67fbaa219ea51fb592c34c1ffec066002b824bf18e6614c673eee598bf mainnet-01722-1aff873b.era1 +2511305a5ce78ad72aa9dbdad9817e8469a295dcbb5984a957aece48930a0195 mainnet-01723-f19dc7c1.era1 +4c36e22cee8ddbcaf18d09e596cec9a92a3f0a45f3fe65b57aa0706af2f5fb23 mainnet-01724-50ba9e24.era1 +d1aa0d2d92ab9eb9bd387b15c77b9f0221be2a767f86f1b7b9309de24fbb76b8 mainnet-01725-6244c9ba.era1 +f31545c2b115d0c0eebf6d80b1bcfd78598ae06f1986df5efdcfc3d4f37b7ed1 mainnet-01726-d9ca0531.era1 +78004f7204f2e7ac123860e3d5ca0776498e8253d01ace192c0f3ebda61a5ab8 mainnet-01727-019f04b7.era1 +15a6bd053f3a88335e91d6c6442d72eaaccb0d57c7ff2dfc9f17ff3c07fa865e mainnet-01728-0bd9139d.era1 +d5b5ba3f562642fb45208da6733142293472cbda4b99fc6e984955595efd1fc9 mainnet-01729-a85a5ebe.era1 +6e49421a232ca1b641ddbe56383bc74982b163b86d41aca834473134c4610cd1 mainnet-01730-e07d8fc1.era1 +477eac763c868cf1ac6259e07c633774ea0f08c64d3ca04687074bb18a419e1a mainnet-01731-cdeae685.era1 +ca68cdf760dbad25ad5ef8bdaf85b0058de563632136f7fe9d2d95a04bfe339f mainnet-01732-d4b05748.era1 +06b1c9f0c06d35ce43c35df229c1d6faf8c79a2b402bbed4f130065531ee7835 mainnet-01733-2b75c3c9.era1 +4a954844ebcc4b0f0c94ca52547190210f649f72bcdb9f80d1d3d4e71f74ce1b mainnet-01734-0b51aa4b.era1 +330fb8fdd3c5aabae38c66750176f804e9c07bbc4bde6455d2c43afbb6db8800 mainnet-01735-5b7da9fd.era1 +d2add601504ca241728d9b8432aac9a6d369d207c54cca01c7d49bbf7c48c0ea mainnet-01736-2d581b43.era1 +7346d0e283a184e99980cec04b06e311719f4f29c18b2012fe37128c2a19711b mainnet-01737-053468cb.era1 +259d8dfb172a6b0dec07c12c1fab578d7a7518902cb12e42423732d81203d37a mainnet-01738-c4ea7355.era1 +cea5a2851704064b8fd19552ecaf48de00beafdc4ae82c37f4a2082e11b43015 mainnet-01739-390296d2.era1 +9a57cb258cc2dd09ce7b55e09f2f260bb73c7c71aaca242048fa60ed02354acf mainnet-01740-0bc99a43.era1 +caa75f9f4de04adfc7e12807d84de63a0d25d5f9ad526af13c748ba0cbe12469 mainnet-01741-831ca140.era1 +814ea7efaee9fd9f538c120e23c5da3f179cdf7d657335e34940506e24b466e0 mainnet-01742-6ea854bc.era1 +4a80a24474e75c813ece60c103e053f33d5d0e56b40ce212361ee914232cb078 mainnet-01743-cbe74e16.era1 +444335c8e046badc93ef4cfd8da27ffcbbde62a739874c9a133d6a1aee184436 mainnet-01744-afde3d1a.era1 +4f49ec87006142e56afa1208efe10c3e57b671a7995bddb5d2e52d8814f30115 mainnet-01745-265425af.era1 +98484f5a5cd6307bd21db41c280cc8e26e343c648db6b83fc3dc76770ab97b99 mainnet-01746-ffa83edd.era1 +86e82209a82218f8d477c84622831fb7a985f8995fe7bc2c9a3f1dddd2f509cc mainnet-01747-2d7f0f5a.era1 +147366fe1cf8a15b86df6e2057b6197efd6291e5a1c02130697b395f9579a084 mainnet-01748-7b69dcfe.era1 +bb8718154e5cfbdc3851e1bd92d25d0c7c1f6caa408b247fa23aeff123e70bb4 mainnet-01749-c08c9611.era1 +ea12b0e1908f4992cf34a041e1ddb828a28a2d896917e3cdc1accb7be1bacb57 mainnet-01750-6067e2da.era1 +d457cc2a7f6dc98768f427366b7062b5c18394e60b39873a11001f0eecde22b5 mainnet-01751-50262a09.era1 +44cb9be229a4a0f73f0ffbb979adfafea36a0a2b04cf45bf06f8cdc73d47a770 mainnet-01752-0488926e.era1 +29642c796b4ac6fe40e42ba781c3f1cd973eed1b333bd18d8fea12fac869f04d mainnet-01753-7477c0a1.era1 +c72f3c3be651e63836a45aac5a2ebfaeeddc84c5623fe55c9040725503580398 mainnet-01754-646def35.era1 +390aab178f3fa64c3f7c9d49a168cfd4864406d98479a1262fe4639877f932a1 mainnet-01755-84f4fc01.era1 +00809821d0ac264ceeca9cde077b30a7d7bbc090680b72bcce0b7a3e352dc675 mainnet-01756-308e0580.era1 +ab7d0b5eba4e9362a3beae38f4c9cfedf1eb7e953ef541019c0d3779a92d2ff1 mainnet-01757-8b98854f.era1 +b5d44da8b08efd4d49f08a069a329877344304d0db8f031d3748090e73ef1c9d mainnet-01758-7c7bd992.era1 +039f94e36edc9664ba94554e943f5e1e3832fc3c2367b915cab73702a2ddfabb mainnet-01759-ef9dd1ae.era1 +3b00ec14cfec68038f9b09d0e471472a543b7e81c5b195aa0d49be1064b733c3 mainnet-01760-c908c0cb.era1 +7d7ba43a3b5d3d07083c5c3764f3062f19adabc13ea0e91a71bc164ba058e8a0 mainnet-01761-4c223079.era1 +690a0ac75a9478e85925efa16fc3c773fc108ea10d993ca64a7fcf1ead7ed4e7 mainnet-01762-58e9482f.era1 +a206d2ee6859a7941e7bf3adcdd01b954a6f1e99258f05cc6c1d1badeb94e1b1 mainnet-01763-e9b64e43.era1 +61221de714d558e1d478b269e4ac07246d2ee4d563f0121297e5cda10eca8b71 mainnet-01764-69d58dfd.era1 +a2c885b3dd2e7bb137983721941c5330d50134568c96bea6dd84335ba29b0a64 mainnet-01765-56508960.era1 +4ec349b6cfa62117f3e22f5d6876bad0cd33b7dec29517e429e5a8f986368129 mainnet-01766-e47ad77c.era1 +54cfd1f1b777197a0baf6b8856a898b37dc9395a449f605ef344b1cf0f21365d mainnet-01767-c92526a4.era1 +56f8a3ca394e71021db7502256d1725de65913549bf948f9b9309e13034b50e9 mainnet-01768-0f318bcd.era1 +a6e19802b345ad2eefc4643b2f2cfff050d95ecb2f571fb83b3347c2e387e19d mainnet-01769-cc56b7c7.era1 +92b9eab7566de2d1bd81bb87737b5695d37a10913daeacf8ee8626c48e6a4dab mainnet-01770-8a0d9bcd.era1 +9837b0e62989fa89b8365c58de124b132aaa0a02db4f2f9925d609d5cbf7cd70 mainnet-01771-8c0c7922.era1 +f1f5657d4d74fe786d08b9b84f4aeb2e8e1229ed79284b95bcf3978506aa9aeb mainnet-01772-490072e1.era1 +5c9e6d7f79d6b66d869184db4f737facefc8a77656500f7cf70d1e24cb781ca3 mainnet-01773-441a3c8c.era1 +11bf26b1e892ecb5fb1bc2fa901d76fb3b80a5d2f1244a9bfa97734a7fbf3d20 mainnet-01774-9d3c41ed.era1 +abdffb40e251c5519e279f29e3457ff35e1940587353f6af98e85ed6ee0ef2b1 mainnet-01775-6023b5ad.era1 +8207baec682cafc0bd49f8374a27dc42e8438c91250f6fad4984386cdb938bad mainnet-01776-848902b0.era1 +ad7cc2dac5c009b4478f2e7af83f7742e1b118e01c66d5b301563a0dcc9bca34 mainnet-01777-56f9c62b.era1 +10242c34bf2105d577e40e835c49eba8ce00e330f4027875a7a2dd31c175bb87 mainnet-01778-01a7fc06.era1 +def53a3865dd9824c40a2a2ac918f1d68f6fe44be12fb32d70a3c6f0dd79ad35 mainnet-01779-9ff0b054.era1 +cff7bb320ad2df3c95e50b28f36f4cd08080036f6e3487f763a14a8a3c736a6c mainnet-01780-0601be62.era1 +908ba078f115308795bb316a04eb26fbf2c6ad6ee70c8013b222f9b88d1fc25c mainnet-01781-c73b916a.era1 +d54d51b9943469b3f98d41fc032966326a0284a196758d9a97ec25d9b503be0a mainnet-01782-4ff9deb8.era1 +e5ef6720d4344b8172b8c5e3e65314137c72a2cf8d8df3ac67ba1ff348c081eb mainnet-01783-6dcf0704.era1 +ff9d6f57135bafa3744ffd3562495937dc479b5e466fecd128bffffb354d4bde mainnet-01784-37725e0b.era1 +1626ff1b25cbd8d365ef179d760206d2f3a7e34283ad89e0c3de76682a4a8c9b mainnet-01785-bb264cc3.era1 +e8139e73f730e001fcd2c95f4d100401841d13fefc7abdd3d0da7b32cffb1d44 mainnet-01786-8f3dccda.era1 +ca3bfe0711d28fb7a43d9a37a6802cbb53a23a03855de31e45b3a2282f4dca00 mainnet-01787-8cae5bdc.era1 +2c05e0edf4cafea94575d5ddc29d9db3e9af79ffca259a1e1fc34ae1d8efdf8f mainnet-01788-7e1c1704.era1 +1ff17cc6dbb42da4be0c4c312a55cce95ed32829a332e888b8e999807327ac18 mainnet-01789-db34d4b8.era1 +e7df0305b5fd13fafb69b72ecf65715086ca9dfd9b5b0a60949cb4fb93e8a513 mainnet-01790-0e2fc599.era1 +972f33b700efda3b51dd8e7ba4285bbfd735c0caac70669c26c663cd5f428b23 mainnet-01791-0dabc8f4.era1 +2c203a477905059a0ddf15f23cdb0a6af453706542fc23547347ccc6f35e61b7 mainnet-01792-648d0dd2.era1 +c140566f729c7234daa27aca935c23b94264750db2e5c31ce51328531933656a mainnet-01793-77485ef3.era1 +5919ad8de00445b2dc6889583151806527b3da2933098324442036d19559f9ea mainnet-01794-e291673e.era1 +b8be8c99dff125973899bf7aa1afe6781416df937f5226e4d4cb3215953f2093 mainnet-01795-d1d496b1.era1 +b86336614b33a78c996f4d336d4228384b202ee1b7252eac6077d4e8393f6754 mainnet-01796-33a5546b.era1 +08bba1af8d0185a153b55ee9f0cdb03294c6521523d4bae4d9ef2e91553af78c mainnet-01797-34123297.era1 +9db3986e4738134b2f924332b8e3b9fff78735be682326ce0a60425df0f8abc3 mainnet-01798-3ca0aead.era1 +e71d18586ae068704b0a70c226559e49334ad35b90adeb21e0e6a6497ef71280 mainnet-01799-e949b50c.era1 +169ef20cc0417506badbe99059319fb2e075d41348b589f831041b43f93e4601 mainnet-01800-07cb6fc5.era1 +3b6ac2d372e4f7d086f414aede4fcd85ab99eecd5b8813de28374dd4395a9474 mainnet-01801-8edf5827.era1 +373d59a8877cbd8b04a7fdcde7b622255add49295b6bb0e6798f96742f14aec4 mainnet-01802-f95306bb.era1 +ffb419fdb5ef8b1751582d9bffe0d415a8f3575fcf3637c6e3f318ae339146f7 mainnet-01803-4e1d05e2.era1 +817da39985c59490e5e5c9d552800210bb63ac4bcbe69e5660d2197c7645520d mainnet-01804-a24b5a4a.era1 +96e1f8b663ee8858688c96c680a7b50fc5b4ab6e417de66804cc37ebab61d83b mainnet-01805-81d9a952.era1 +7cd6588b9de8c36552aa5720f1eef8befb529348a28e2b12bc24dc62dfdce32b mainnet-01806-66534ad9.era1 +28733727ad69c62d6cb996c7ba56ce877228cc13b49404c58480f5744e1b2f42 mainnet-01807-37ac1801.era1 +11d4f6cdde3fab436ee0860524869d550a296973a16123564cc378fd6b8fb76b mainnet-01808-3f1a883d.era1 +f134e7b5a1dab190a560976c49d44af0bf4b29ecdf90b5956e68ef1fc5873b07 mainnet-01809-45d0f53f.era1 +7d896a5ee60a4538daddfd3224ee1733ffe03ce2d9205fbb8bcdd5556880cd18 mainnet-01810-6ccaab76.era1 +97270e0c74daf2b6dce276f7e57da1675686898f5fe6e71b41372a5ed71b2861 mainnet-01811-9ab98ad9.era1 +128b576e331eb6dd13669fd41ad838a55234ba8fd551b540334aaa5977a25839 mainnet-01812-80d86e96.era1 +94b8405c2ca5e927fe04c53abad00195af8f5ddcaabd5bcb380ebacb974d0f0b mainnet-01813-1b4ae400.era1 +bcbfc951eb5accdfc901c2d4e09bed8c2dd8527523ddf5168bde7f6cafb65665 mainnet-01814-e0064cce.era1 +9cae44c640a4eedfd0203544557b9859fad78525315fea016f0760fee9621c68 mainnet-01815-a2d04240.era1 +bd0d76e3aacd16bca11fdf97f3c4d59d6c94d2c6632336b409a17362e18a7149 mainnet-01816-9a493dd7.era1 +e1e52b715b706fb6a51fd475c90ffe844c05af4682212dc735b70eb6d15b8c7b mainnet-01817-f534c5f3.era1 +47d4d64d864b2899d7de2579c51313938e0ff0fa9138b2243a5ddb52f9dfdf7f mainnet-01818-c34b86c6.era1 +b3e560f0cf07d7eef8e791bc06dd9641c7c7735d4236dcb2a36b21eec1a197c0 mainnet-01819-918b86fa.era1 +9f731bd11450d62258465e82551e0880cb069ce742ae13bde68dea2601721748 mainnet-01820-17e7111c.era1 +d6f23c1501e8f32f0684225a3c127c72315a8fc456bcbf92cdc217eed2a84d33 mainnet-01821-e118ce72.era1 +5f3e345b1e4de32db962e04b1a634bf7a5015fe1cc37e19fd65cc29a78d49ec9 mainnet-01822-d7b7b15d.era1 +6d2414fb6da3b2cb1b6ed24a68cef0b3e57f4d6a8b2f0c5a90c03c4cd02cedcf mainnet-01823-3fe7a945.era1 +0ea96886a709d1e7ea834c4a524b07b75dc235cf34d6819812929067511301a3 mainnet-01824-6ad87d35.era1 +c612eab2bb1cee468887caff7032f5fd6a09a297ede51e25c2100cc46f82fa1f mainnet-01825-7f981244.era1 +0fb454e3e2d97d1d136ad2f026926b9340cf0a338e2bce065274b5b699fdd99a mainnet-01826-c17dc5a3.era1 +f01b822341df294990d8f41a1d9ddc3a99f77614fdbb06882a3efbde0470ca61 mainnet-01827-fa5fbccb.era1 +dc618a87935f5b1c5a7dafeb662c8672a9548c387d1bf856f0c9ab47ad430e23 mainnet-01828-540009e5.era1 +7097ea8556d18ada8179df1a19b9979230dd60b101448242fe0f715efe087593 mainnet-01829-41ed6fe7.era1 +dd6a8b10bf7d6a29d83be0ca90812865496bb76501e6f42d4abf3b8df3cbc380 mainnet-01830-2afebfec.era1 +5dedcfe545ca0511c90ef476e003381a11aa60164579d750b19ebf38a4114159 mainnet-01831-7289db8d.era1 +600a782467d1fb81a455d097a0a5e877b2fcef41d39a2eabc50c8079c768615c mainnet-01832-b1198d24.era1 +ffe32e137d93df3c83d498a8af890369979255326fd8955e93849772304732fe mainnet-01833-0bef1261.era1 +e8d69bf28daf5c338649528feb91b91635d686144caa6b302d4f2ccbf63887c8 mainnet-01834-fdca337f.era1 +4f97a53b046804e68bede066b1648d83ef18c90f0849e15963e3ac70a9441c26 mainnet-01835-a45d06ec.era1 +17cbe71911e869e1180fa3f4e9f428f62a2305c6c86f7b9e3eeaeabc7e4cb4e3 mainnet-01836-87fe297b.era1 +2d02765b6d417d1800dd62d9057e933c21eb7440c075cc8de5d8a8e2e0b8e13b mainnet-01837-e26e40bd.era1 +133f804d8f0fcb26fdb49cc1d10ddf51dbe1214e1e7ffd622b72d96fe8170029 mainnet-01838-ec6b0c03.era1 +351b5e8110203c9095b32a38090e5643da57b48038d4dd84480816cd4550ff7c mainnet-01839-07950048.era1 +161f66a9d95eede26f873efa400763d90b638521268f89c9316462356161a2b8 mainnet-01840-e287972b.era1 +17ff39336044d08693519e82319b3aebcbc78e179068520d5feddca7407ea7bd mainnet-01841-2a6039e5.era1 +c882be49dedfa2ddfabf14b1d7ac3ea64e62fae051d5eb93f0caea9b1b8fbc17 mainnet-01842-5567d80f.era1 +feb4455e45319b835135110b67da829745bdaba876f5e8ad518a77189e0bec4e mainnet-01843-3153e10a.era1 +1a205dddfdd1fb0341bc260b8f2a6fe210d274f18bd1d7f650a2c475f3f9a71a mainnet-01844-03b0963d.era1 +622530320567648c3bf30a111c3e8f0c38a236772313d29f280179c2d02197ea mainnet-01845-03b93b15.era1 +eb797fcc91cd6154d60230772ca77fff747a7054a2867cf7b8d8393abdb6d577 mainnet-01846-3e05673e.era1 +82cd487fafe6d8604d9c9725bbe9452f95e026e86d76b3239b6d3e4b7146bb50 mainnet-01847-2de5f285.era1 +21045bf78b5fa1f63d746908c8677e4bcfe33ed33fe3c5388d3b46dedd945fae mainnet-01848-113a0599.era1 +7a1f6b831f2f06761fac5f630b22c38ca8cd6ffb95e14d4b23ec7229dc27945c mainnet-01849-71675e40.era1 +6dc1341e050c93d17c63b041c0135c97d5b8ed492cc386aa12b9805d71abf2bd mainnet-01850-eb0e8dce.era1 +774e428b959db0a51682351a5a68349794eb61d5f5899a2aa642c63d85b55f46 mainnet-01851-27b488ec.era1 +fb66711c88759a426d60bd0793589799c46c0275aff4957a9b46eb1b0315c940 mainnet-01852-a405f5f3.era1 +74ffea8a7f9694760b7ff2a368f5111b816813591182d44008551d5ce7a5c3f6 mainnet-01853-de7294f5.era1 +315a725314b5b8c1a00ee93fc0bb7e17166885e2daa8ef1d4abf72fdc0c02099 mainnet-01854-1b3a9f72.era1 +977941463e01f6a0a06cb7c30e6799755ef1b98b0cf8e47fb2957b4a2360345d mainnet-01855-bb986915.era1 +842ac3df8762f07afb910ab82cb89101418636f13c54d31d21e978c15fa8b246 mainnet-01856-10e8b0ee.era1 +0ed37c5b1df966e68716fa4b2c0328c2738262721fb0da19fc87ec9fd3441fb9 mainnet-01857-e0a70c38.era1 +1d419c191e93d5915e7e1a39ac7a4d1a6b5e5111f7ce772bc9c9d2bf09f0bef9 mainnet-01858-96299d52.era1 +fef06b6aae88746a73d84aae756c7073f3f7378a89c80b5629d4cbc7ba004fed mainnet-01859-056f7f9b.era1 +3d5329a9404c468f64482bd8c76ad05fd3b769cf3e1ada43910c367eec4f3f09 mainnet-01860-be17dd25.era1 +0b421c693e9d5c747b8348ce9d236e70592f5817c7f7f8ab2b579fd5d8461f67 mainnet-01861-31641a08.era1 +16c9d7c2c498f72d1336c86f477ac4ce4c1cd65fedee0cbbc196a15cfc47d4a0 mainnet-01862-deb651e1.era1 +abee7f8f73830a9a8a0eeccd6ea82abf6eb9fdaa1c58c79b95832c98c273586e mainnet-01863-500103a0.era1 +6af0acfc1ea37b531aa8cfbfaee8c99277b10ead6ceb3b19e6cdaa3bd01825a0 mainnet-01864-168be6df.era1 +7494fabb5f9f94f5afafc82ac7ef40739f8b4f74003bbabd286a8f51716920ee mainnet-01865-d69771c5.era1 +29334e8e24b5b98bfddb7f25b2c1dd31ad51d325c9613eb6210112281f1eb1ce mainnet-01866-3cf6a306.era1 +dffd7716b669261690556826cd8b21da2197ef3e98ad21cedbe9623e4f41d998 mainnet-01867-69891a16.era1 +34bda1f8771b070b95ef1ea62b49dccbd3b987f10531cf65ac18bf95ce20e415 mainnet-01868-fb7b596a.era1 +48b4ce8201c4c0e6d3dc7e079f3e87745fd6260d70cf89d210b17cf30d079b85 mainnet-01869-d864488d.era1 +8aa2f4333d0f21601874d7525ee983489b861d3198124db45ac9b6b922e609e6 mainnet-01870-3dc73a6d.era1 +5284fd43ca889845d07bac332cddc9c183e1a9a90c4281c18e1cfc00734c3a27 mainnet-01871-ca53dc21.era1 +63a6f4f1e593247cadb590ea238ca9d613c0fff308331fb7a85239cd8c4eec66 mainnet-01872-d571c79a.era1 +ca798432a63ae6b761cf9e90a98f94add78b4e292d8bd8287e3425a7b7a34c5e mainnet-01873-c71b3cef.era1 +c2134b7fa84e04548facd1d22d7f6193805a03785258bb9f2240b67afe6ebacd mainnet-01874-b43aeeec.era1 +52fd98bfa5b8a99416e1d27968215180adb0be009b6efdb22a4d3ead0349134d mainnet-01875-24fb7782.era1 +103b8e5d17b21ded74f53be236713cb8a9be4fe935ffc246a746417272d1102a mainnet-01876-41ac34f8.era1 +8b0e3abe1b964e612fae21238ebe7cb4a9070c3bda35f3aae8b31dafa907fab2 mainnet-01877-54983d68.era1 +6a16105b29fbe23cf69d902dc2cffa79f3beaf359d5b0a5d2b58789f34cc4f0a mainnet-01878-56db2145.era1 +7f73eb9fcdfb4cfc7dc9bb2cc8f0ea0498298a65eed71686102e0a695ea1a23d mainnet-01879-793c08df.era1 +41c749a4adbcb134050ff91a5d2ec6a28d33682700e294e949fd68b1dd88e4b3 mainnet-01880-0412a89e.era1 +177c549d6b99ea7ab2607343e4ba70d2a203773c43776876d1f337c5c635680f mainnet-01881-8d991285.era1 +163b67fa36d84a677985407c40f5197d7263f3d24fc3a0954bf6a8158f80dfc0 mainnet-01882-44df0aab.era1 +94b4d2fc661d91d6ef54366fccf40927dd99073564cf8b4a43127c9162dca41a mainnet-01883-bd172681.era1 +3a29dffbe1fdf5104ce49066c83e6f141c07e3d8d40ddf2d724223deb5b94c5e mainnet-01884-5732d988.era1 +c44e1614da6b9354732294c4e6615af19ca03bd4a6f967dcdffb36877e9be079 mainnet-01885-5480f074.era1 +6cb8e8d675993a2d48451afce0af65c0941fa3ccb10680bcbc52baa422c5f662 mainnet-01886-b5e8b2b0.era1 +c48f190c2b26d1cf9b8bf678cab4340faaf974c390eb4c4546399cc77fa242a4 mainnet-01887-dededef3.era1 +0c0bdea9087424b4a1fb4a6efbb5049fa814b1394f5ecffebc950d7ed08fc015 mainnet-01888-cdbce5e3.era1 +9a4d17719f76b51d6bd4be3a755be4bef91f43123a5e57982befc0aef35d5dde mainnet-01889-4a09fe43.era1 +426860d94b6b23be3d8ef680404107e9b55da28433bd514c93d5d4d41a5f6288 mainnet-01890-ff7a1b11.era1 +6c088ba4841eacd5c09a98b96631b48b655a79560cf81cfc7334af146e7e8d2a mainnet-01891-0f7bffdd.era1 +ee819214286025c3dd6f375a734a5f5b7f75567cc39f5dfda6eb3fd355a72e4b mainnet-01892-9671b1ed.era1 +f9fb6a102e845e4f7c3aab4ab72cac6720dc1a463cb541fdd24904430dcc320f mainnet-01893-1b07973b.era1 +ae91309834074d0b79557c499203bcfd03b6ddbad330874c68cb25780ca678cb mainnet-01894-80400894.era1 +30cad67a29a70185f0529bef58005ffc121fca1bfc02f95e4d7c00a4dd8aebba mainnet-01895-3f81607c.era1 +6f7cc262142969b3b06952cae4e0a2ff75710217beffd259a6128b60f2b8b23c mainnet-01896-e6ebe562.era1 diff --git a/internal/era/eradl/checksums_sepolia.txt b/internal/era/eradl/checksums_sepolia.txt new file mode 100644 index 00000000000..e8d6203999c --- /dev/null +++ b/internal/era/eradl/checksums_sepolia.txt @@ -0,0 +1,183 @@ +ab7f6d4f4eba0267406f783b75accc7a93dece520242d04fed27b0af51d79242 sepolia-00000-643a00f7.era1 +9cae627459d13ed3d05f6812eefa93242555fbefd27aa0927b40b62d44feb2e9 sepolia-00001-f3ea493e.era1 +a6f691585bc74fae6c445a8985f0df342e983ba1ef569018befb4b21b30891e2 sepolia-00002-dcb01f4d.era1 +1add5a98a9e6c15a667d6a7bbdaea115893019f3033664c54c2dcab70829268d sepolia-00003-18b32584.era1 +5e5ce2ca04b0f1aef6f026214cb64d16f76606fb5b5baf8a462f4a851dda0513 sepolia-00004-e93b3f11.era1 +db6f7687e9826a4e4dbc1361d666a8f2aa735eae309f63d9cadf6b27a899277b sepolia-00005-90918472.era1 +f61118a4e1cb718bdb71fde1daf84e59c3524b1e241ea1a5e2112d8c53acc625 sepolia-00006-a4a583b1.era1 +fd0c6fc443a4c30617ce88a48e7438fecaf4cf4772034511fa51433c7aae181a sepolia-00007-ef1e0a86.era1 +95bc416c6393e0a579f9e4af42421cf861e43354d49273d369bcf9b38e070246 sepolia-00008-dfe1e6dd.era1 +8262e7182f9a8abb9f5fc05d745a3cd2061eacda5c87cc2533af2407bd20672c sepolia-00009-dba60c04.era1 +d7c123c8e06713df23db0046df7c45d873e153ff076357b6d0c5bc9b0081a246 sepolia-00010-c99c706d.era1 +7fd359358ebf710c5c61a6a65551a4cb91c2160dbd7b59ed355a4d7e10f29328 sepolia-00011-04513853.era1 +9d6ea741d1b91e1c2c37ac1659ef3516d4968b6e7c164d347e534c4bebe644fd sepolia-00012-6e07302d.era1 +ce3e580fd3e5cb9e0ba9ebffae948406fdbe59f96fa3a05a30a381bc7d1b583e sepolia-00013-25709be2.era1 +15df46dc8c2cfd402f302f2925d1c50ac12538d394e8687920f53346429d6743 sepolia-00014-7c90e5a5.era1 +1985aaeb96c81279b978beab1145b3baf460b12bff86b8ca05c5067e1b0ca25b sepolia-00015-fadc08ab.era1 +2a53126cd443e9310fa15180c5bbbdb76f257d7fee67159500d8efa0ed13bef4 sepolia-00016-d44b2499.era1 +e783cff42ba7a53461c7fcdaf59a5387e7091a1230aa6761af05e7daf59b4805 sepolia-00017-02451eb3.era1 +92848afbeea81397fc9790e7fba7c627c01c17cbc5b8961a48ca60329bbfa3f3 sepolia-00018-cdb53b11.era1 +d15ac188a4dfac75818dfeb04e72c2f269d9f985100a457ebdc17661e36f0d83 sepolia-00019-fa770019.era1 +e8ef40780f55a0b640d1b68e1a89d1d438e8105bb1388bffc8a743b88e357671 sepolia-00020-15fc80e7.era1 +8041d790a0c50044e331a385eb98b17f5050ab25637b6a4f2c3e8e99f3f81e6e sepolia-00021-b8814b14.era1 +f17adda073b66cc97cb58acc7b776296e135943718dab9fc7cfecbac748419b7 sepolia-00022-87944c43.era1 +159a5ec4fa1e79be6d13fe4753eaa8994f547e31783069082a39d51e3e00b178 sepolia-00023-516a30f5.era1 +5b75090c9f26899aa6fe504963b7f1a43ab13a8fd7547da42f9ca9cc980511de sepolia-00024-b922dde6.era1 +d5c39668b309b43eec720b2185ac23ef819cc7fa92e35f2e6c9a4ce9ee1c249d sepolia-00025-a081e175.era1 +188b5119e110feec067dc06aefde7decba2a87bde642cc1735391a1782a99b03 sepolia-00026-d3931a12.era1 +3114068f4eb9cf8277d4417da371e820f0bf1a654600f8dea149f9854523d1f2 sepolia-00027-43f5e0ac.era1 +b18597daf66f27976638a84a4cac5622f7d53fca8264dac3665f84f28435cc26 sepolia-00028-dae08170.era1 +d0672ee04c5a84e881c4acc3757e16f963c57b5b69da81a674498c1b06159f3e sepolia-00029-17818143.era1 +122dff18aaac0bae0a006da088794f638ee95ddaa1e0a347ca9a8eca816471c1 sepolia-00030-cc7fc4eb.era1 +42c2206fda093c7aa6b31fad1addf2dd14e862b4686b0e17050bd284dc479cfd sepolia-00031-3171eede.era1 +42b023d07a02ad739b61ec8297c43501ddac200d1866fa3203df466d2442c4b0 sepolia-00032-9e2ff5ca.era1 +2461fb005a9a62cf6ab52a0b3505abde9828c1f36def4fa12a547ebb5e8c89c0 sepolia-00033-bc921023.era1 +2ca97708fba029693d7754a85424d29f57ee296a48fcbde02597603e7e90525d sepolia-00034-b2496634.era1 +d2944e1cbd7a3276ca5ef9d4b4473557a65e58bd512029b2fb00d41f618ac551 sepolia-00035-f3add4f1.era1 +c7f63d7230dd7ae9c57c33014d56bd4c921f5db95f3872bf6745d03bc64b5cc0 sepolia-00036-0db12924.era1 +dc027011c543418fcfdd7897cea3356de16122f676da2459d78da5369cdc2b8f sepolia-00037-ae8bdd13.era1 +9be2262ef4e1e1b93cee3316ad473895bd7a7f3bff15ac8c18c1a157292ab7f6 sepolia-00038-81f80b03.era1 +56c03c86028c95d431030fa2728493ca8001cc86eec3f5d7a558224e550b42ef sepolia-00039-03e5d6f1.era1 +d64e65ecfcf480c2edbdebac350ac899b25874a522c226695a46fab17de5b15c sepolia-00040-296fe287.era1 +18226ba389f150581b36a234d877fad9c08c0fa691a366ba11f808ec9110cf67 sepolia-00041-5cd87470.era1 +0d2960f23933deadcdfe545fc8aba626e79c0c010fad0ac01b577ca5b99840e0 sepolia-00042-de86936c.era1 +b14f66c2fc74d0f772eb75c548a86da4bf160cf1067091c65c05a90812e7af1f sepolia-00043-ede4e682.era1 +15d704883fadfcbe4ce3520bc9ed5fcff98a8a419b6fa9f832394008ae1941aa sepolia-00044-4d49a81a.era1 +5343d8015eb0a6b8728b571fd0e7a2bb92728ec593469b0a9d8d2082a5afc36d sepolia-00045-7071c19b.era1 +63efe606b8c687aeeff4d2ff5596a72da862d024f9dbb1ff5bf67ca4f2921f77 sepolia-00046-4f4fe79f.era1 +9eb6dd3772ebfe63c37a22423310a5cb36c0d09defd66d7aa02a5b4c1283ca80 sepolia-00047-76b58fb3.era1 +82afedad4e56f86d836446f0c71616f8d88fdec11b93a6360dacc111cdb12db2 sepolia-00048-0fa9d93c.era1 +8a86584323e83a11499265f33ddabe0de1517b51c8e73985fa6eed15cfd63d0b sepolia-00049-d193ff47.era1 +b66621a148fb7acce05efe6aab89be5a965463a4c4c95a3350e64f3e8a684417 sepolia-00050-736b969f.era1 +49d47547284339de6f92e6495347dce0c45c5294f07c1b96fcfa510c43cfc206 sepolia-00051-466eb482.era1 +b075750d6efdde0cd755c90cd893b2917c56ab8674022fb36bb7a1305dc9bfed sepolia-00052-9752212c.era1 +36eb486d7dab70e759c72198b9f2505ac3fcb7f6a0ae2cca82fd9ade3d65e86d sepolia-00053-b2897233.era1 +9f024ea4e135fe6939f2fedb6a6821d61202139fa35378616fcf0274bdeac172 sepolia-00054-aedad6a8.era1 +2bc46d10add9c9903d554523f6ed0c8f5a632521c659d9f3f4af4c444221d08c sepolia-00055-19af5091.era1 +21900526ff5f0f31257bdabaae3fffa700eccd3cf3a6ba346b4bdf4347c97324 sepolia-00056-9ac921a1.era1 +c9a47d7db6af1b984e6ad8de2ea0eb0e73e442281d4fa497ba64c02caaf0b7d0 sepolia-00057-240f011a.era1 +10d38d3b6b66367a1c05922040073d6a62a2cbb843ea821dbd02f4d9f037c740 sepolia-00058-06d606d6.era1 +d391f44dfa9bab3ced35676cd964daeffe0f4e4028c3bd44fe41010b761c5229 sepolia-00059-8ea69a55.era1 +39a1bdf4d7c1dd8365f635c8da568b34d1c2e59aa5d7fe8745d6b7db2cf2c6ed sepolia-00060-d22079b0.era1 +cf41ae65b6391d8c44bb0128e66591a96d42fea0a7d35c30c8eca5bbe0790578 sepolia-00061-834d00e3.era1 +9bcff9fa47c2addc140857b23d6453583ca526c6cdf11144583db24acf33b1b2 sepolia-00062-d1230cc2.era1 +984c5662f6f36ece4e0ca9efc47dcb3e50e0aa581a874a680736f2f558cdd889 sepolia-00063-e1e9fa40.era1 +7582532b67144ea9499c28d935e7ac4bbf8f465c382a8a97ce04e56539b8c9af sepolia-00064-7f827781.era1 +945e3da724660df934484d68868d54fbfaa5feaacdd635f6185b56e88858ad8d sepolia-00065-9d8993d6.era1 +77fdff04d6d6233c4bab9811ad559a328fdb0447070b96e2f38ea7450639a00e sepolia-00066-8d516260.era1 +3486301cf48b1f727b46c0223c4a018e03d0857efedc2f616ea850b539139f93 sepolia-00067-7466141b.era1 +2954560d2b1c6ff48242312b7f5fe902c53dc93a3d1c7d9abf41633926ef72ac sepolia-00068-1bd11e8a.era1 +8f27de663991d7e4e8a7005dee7b94f991cfefad743507362eca5ad83f53e449 sepolia-00069-536ecd2c.era1 +2d0fa324482aab3db61718abfb2a5c084a55db5f39c4bce125255eec9a56a01a sepolia-00070-b27f7c5c.era1 +472b1c9ec83d9b6564a45d4363c856604d21b53d8c89a7b5810f2596c14f710a sepolia-00071-d2ef5349.era1 +3a0e6051e4c5f1a263b51f7a827d2276c7b25cb9d426f24f39fca33a7e76d623 sepolia-00072-b23578a9.era1 +734b46f0bb7150e1d705bff53a0cdedd3ed57ca1461f06deb517c8b74c4b25c9 sepolia-00073-8bacb416.era1 +28091f1a78dc398e2d47212087f652c8d35f07cda5457ee691b4191c3a6698c8 sepolia-00074-0e81003c.era1 +b743e3e1721e593562d9a144f49a927bbe2db70be9e4884521cae9500b0004cb sepolia-00075-09177034.era1 +6850f49599b95dcf1db54a3c5d126654ca21de4df27b7a24bc35c4d10fe6cdb7 sepolia-00076-0f2898a3.era1 +da57f20ff5a3944bd1fef22e7c914f2eda3e79dd7346b0e7c0a01e69cf538d7e sepolia-00077-61de3538.era1 +fc20551b69bacce502fa42ba5b0a223c9fb558d5f495948e38c060328f1ceb7e sepolia-00078-be407b86.era1 +ab72961502e1d8055886d744c077dbf60db8acba64c7879109b3ed573ce4cd02 sepolia-00079-d5c957be.era1 +216b65dba90adf1288d40fc689f6087ff2435bd379df482b130cd464cbd1d490 sepolia-00080-ae6ce310.era1 +e824e04c1cf15f142bdbb40161b64953767abd3ec7c40a726ce69d4ce646df08 sepolia-00081-daac26d6.era1 +d70d059609dda26e25af96d32b646c364e2ae44fe7b1cf9e2e8589d731e9f381 sepolia-00082-ecb8f0c2.era1 +a7dc282ca7fd7aa8d42693363aec655d6cfea330220233c7aad3f7955284d650 sepolia-00083-5b017cd4.era1 +2f1006528caa6d47729c4ef0a699c441cb5d2c201afb6563ab51184429294020 sepolia-00084-8c1f92cd.era1 +374494754cfd728b1b4e4a00db810012346ba8c4dc9cf2ad1d80187999ab868b sepolia-00085-9180667c.era1 +7ff9b360a0656ddbace4032d249f5cf06068ee3f8be526d2a4ae0ed6eaef046f sepolia-00086-1aca7add.era1 +de592f4bde8de973d8c82a7d89dbe21cf75bb0b28204567ea4f5012b8be1f28c sepolia-00087-0e100944.era1 +fb6eb1e1e1ce97e03b39fe2d53e3595d759701a65cc861d0568c04ee7e2ec03c sepolia-00088-fcdd7aa5.era1 +4d178ce50f5436f5ba2347d87dc79e9a2e18c17d3056eb77c7c8dbb9b03bb986 sepolia-00089-b23b2368.era1 +21e919f0d32137e7924d54ddcf1e697bb5b57bbb7cc12a169475df993c4c1d9f sepolia-00090-b78edeb7.era1 +cb7ae025ca17c5f52ef7910147a47a0d3680c26f9d2d109d6af330f93f95b735 sepolia-00091-4c81cdca.era1 +1e0249554f150b63cb7dd57c53c624c197b415401c0a1f3986c2f06e8aaa795c sepolia-00092-ddbf3a4c.era1 +8d950d8c13a4e002514a47e6d6ebcad4bf7e5808398641a5c04863d9c90f0f87 sepolia-00093-a52a45ec.era1 +b8720324f04dca4e0046b12ec3b8a2b862158f3156c4d1fc2947340b1fc1a9cb sepolia-00094-839ea4e5.era1 +8c4134bbb21d36fbd7ba608eb1e485e76cb0fd56c4aa8032be1b6848757856d9 sepolia-00095-bccc0958.era1 +e3ad213e760ea2320ff52caf5de32ced37d6b7ad0f99384785ca42a7e58d1cd9 sepolia-00096-429b2f39.era1 +9c324e109f77ebc1750dd2a0941904ff820047def91f8b832e61c01b31297f38 sepolia-00097-6f74184d.era1 +26efc4469736b283fd80d9105fc85a70dbec209fbb0e1e40a905a9406e1bb3cc sepolia-00098-40286059.era1 +e14134b3aad0533366f9aeb19cf2561cf1e81b989a0b83f12b05493948dd71ea sepolia-00099-dd1ef4f4.era1 +7283a437244545e784d51186c0d7d4809808dd472850ca3cd2d7a40f4883b50d sepolia-00100-bd3d6bf4.era1 +b67270e94377703aac798d8323c0a91b451d36806775013a2b8b12db04797851 sepolia-00101-41676e0d.era1 +dd6ea805242a67c6255867f64fc5fbc941a98318b5ea1d5e2d42d42c6910493c sepolia-00102-4f3c1fe1.era1 +d6f6da94b16a201b304936cb5f798b6fedba76ead056cd2c4dc860c21d82b8dd sepolia-00103-e13a95f0.era1 +e2309561f7a86eddfe182dc86fb38e5454c5a272cc77b3d0d7c6ce987ddc874c sepolia-00104-4e49b068.era1 +deff905084758be35bf0b8cb640b1b283232abbc6153388a4275c767ea0bdf0e sepolia-00105-4ab7d9ea.era1 +964f9679a39fe06192a7025f14404cb1f6542071665f1c2f739f62fdb55e9274 sepolia-00106-5ec678af.era1 +8fd75370992f97e77a2cd3663bc1fb9b2bcec9b64a0439de7846d7c6321e6d93 sepolia-00107-44721c55.era1 +7e8219aa13d6a7b32c3a69888a2c5f446703eada3fdcde841ba26edfe3cb4453 sepolia-00108-3b4ad768.era1 +24e8d2f8d01abf63d97365bafa914dd5203f4e807bc6655dc0673b9f87215ebd sepolia-00109-16e54758.era1 +edadb0ebec6c4b7b54237e5802cc59fd962e23cf690ff10ab52c10258d7b7827 sepolia-00110-b5da103b.era1 +4faebfbdc009188cc8cf0ffe4aeca9c93ac5b3abe763cfb165d8bed37b12b51c sepolia-00111-15c53aba.era1 +40210479bd14ca3f8db76b288c2ed4e8f3d5b5ce8116ba8d8fe3133b0cc765ad sepolia-00112-8e2144e3.era1 +92ed406d564ed1504bc7ff91298a419fe72a710585256b1830040ba3d1adbde8 sepolia-00113-1e1aac0a.era1 +7e771bba1aa9c87be155a9a7d62d3479240f21934a3d763cafa5f3cd4ce49f8a sepolia-00114-0d7a4b23.era1 +51d71ed3674f5d70f719b3251e29a0825f2573b1bfd1a92940b013806aaf4fd3 sepolia-00115-8e983a59.era1 +6abea02d82f7a30b67d6de4516bbc711855eea3d891ea42fa0124c101f981f86 sepolia-00116-1708ba6e.era1 +88e7222e35b1275483b6eec11bf8d4417602fdb16b611d6c154abd8d0ec0b49a sepolia-00117-86962046.era1 +6ddeae324cc17337fa23c71b242ab1675b72f43fb393af7e971edec20451f864 sepolia-00118-934e9f5f.era1 +914c35142f7244d07b670b1f990c8bb5997119db451e8dcd8a67c8d71fcc6af2 sepolia-00119-4d88db3e.era1 +eedec437c182f330f2f5f3c530cce3bc59f0c99c9e6cd36288a588adba7679bb sepolia-00120-1a3274d5.era1 +4914f6a8cf63cb806c5736431f6402d9a99c2de953da05a17069fa15cf8c695b sepolia-00121-62b79757.era1 +b9a0138c77dcbb2b4784813204ec8bc30407cd8daf3501707719985acff4a1cb sepolia-00122-41d14652.era1 +57453e0eaa2bf46a13c94d9e96ee98943a46012c2bdd9da5d7c77064e803fbfd sepolia-00123-67f86708.era1 +1e927fdb13cebda45a963c8cec921b16c795fb3020cad9054fa1fb7f4cb528cd sepolia-00124-488cb226.era1 +8015846c8c3663005a8a6d59347fb672a602e6bbff028989355a01f5f6d2b183 sepolia-00125-8a21f7da.era1 +8807f6abe63b76f31d99cf14404fee8bb5eba3a814e2d08b9d85540ef9742550 sepolia-00126-6569478a.era1 +28dc46c8120426b395cf9b5ff781962a3212766b24511b2dc3d2172568b10021 sepolia-00127-1312ab23.era1 +ed66119ec9dae6c61952cbf40b9dc5770cd80e659a83dd18e1a5f2ffa947a04b sepolia-00128-1c95bdc6.era1 +a4a68dd54b77f21939ddb1ec9ef9ae0a3551d6d53b8f9578b01ad8e32e5c8d8e sepolia-00129-f32fdefa.era1 +e54d6d4b95a985f37391750b3bf3bc0f9f4e2f790805797ee198d77287433ca2 sepolia-00130-8f31c590.era1 +577fe68c5c33e47af8823ac573709ed9d63df0d7f9aec02a7907e3e6e5922d67 sepolia-00131-5aaad354.era1 +c3beada9620c55b6d0dbdab8f66b735da0b3d62d5c5eb2c3b5369619549fb3e0 sepolia-00132-2147d970.era1 +12843e128e76d5636f857c8c6801e433757fc4f8aba159bc2d4e6c41f2c94abd sepolia-00133-690cc16f.era1 +e5e4904ec83ebcebccc1aae1de89622a50bf6f735d985234da88fa1ced1787c0 sepolia-00134-fe276e6b.era1 +9c618458f6d76a09f16c86baf526dc7f2310ce92d862f73392144987c04b39ed sepolia-00135-ce40efd0.era1 +1d032d1279c3841c65385f85463226df04a98a3ccc5d7bf461f042492230615c sepolia-00136-adc6e1bb.era1 +a3aa401ca2bc8cb8b104667c6f68e28d00e37813a1a993d8abd1ec5c3555f71d sepolia-00137-805a333a.era1 +105c1e50e73715bf58d42f6bbe49bff36dc0051bfb477efc77ebf14b5ba98974 sepolia-00138-65ff170f.era1 +ad53fc0e4d2718ce105fa3a7ce5574dbb945f49b2fa7470a406179c45738fc46 sepolia-00139-1c86d86a.era1 +c27ae427ca0052686a3ac8a3e568d1dae30449f9edba465f218c147074d61ccc sepolia-00140-e7f36280.era1 +143d09ffb26cd2a1bb334958d92b77a92a8f9ac99c01059d22bee4e4d8bc3212 sepolia-00141-5d3d0d5a.era1 +378a77f0b0c4072297de4c382cece46be521a6c6935db8eb499dd99857d566aa sepolia-00142-e45aa418.era1 +35855d5bd75dff01248dd5b2080babf6127d87c157a987124f7a258cfb9a5ecb sepolia-00143-70379f59.era1 +58f07f98d8e9486acd01ec212518b1c0d7764e68263f9b1e9c51cf5e597a0b6b sepolia-00144-c345e75c.era1 +17caa603e2d1f9c01c9117754f0bb4c8ce42c58da806f758f99468d9f8c1c47b sepolia-00145-9f4bb4c5.era1 +d05a92b8fe1306397b98a6202b18f66c9bc259bd41c25b3d8211c09813e3a9ac sepolia-00146-3336d26b.era1 +b7120a656d06d4010a06a2abadd809ee7f26707cf313e9de5cb1bd172745ad23 sepolia-00147-6b007873.era1 +aa45cc5747e9c21ccfc44c87f98717702551a0511f423b2624a34af58ba798fc sepolia-00148-35f8a5b4.era1 +952843d531ea4d9ee9c456e50e77c50f75a559bff13326dd593271f90cd55129 sepolia-00149-01b6e9ca.era1 +2d93d1d84e0f4013d6daab61d66907488223a2f1b50e4a22d3ba1da24a9c6a84 sepolia-00150-0c372e80.era1 +6b516e3f31720c54453c5ce9bbac68300615f455a7cf4b1dc6592a5d5432ee89 sepolia-00151-c15a5955.era1 +82023cab017453e8fcf49ace9befbaef4af167e0e6c6976456bf251f803b5d51 sepolia-00152-ef7ac893.era1 +6ace677a17380127349ba0710fd02ff38ba185526cc2d9bbb6f05375d6f163f6 sepolia-00153-0e4073c2.era1 +66db5e0786cb9963ed66a0e31773b30d3405621a7636c62ffbb0b84ff41705bd sepolia-00154-298c3549.era1 +cf797283087a079773770c1bebfecb113a4155e0d77d5713c702478e2c24f64a sepolia-00155-a99309bb.era1 +b143cc994d0b544cd08b2a1f66308718ea49f3f68adef8e62300e91f8048130a sepolia-00156-8c33148f.era1 +cbc20f9f767324438a237c3114a1c4e65dda74d4a655d55a7cc3e63e62f373f8 sepolia-00157-ab02fc10.era1 +c8368d1752e66bcd7cabc653477f9aec0071027f91112a8cf71a945db23404e7 sepolia-00158-17992856.era1 +03a1a66eab3a7cbc63b29cfbe2e70285940e64db4af6d23ab70592c820f8d3ea sepolia-00159-b3448cf6.era1 +bf9dcd8400e56c58cd3280f0a3d7aab23a7eaf594e67db5387f40101ad8e8c92 sepolia-00160-acad7054.era1 +03a3b975b2a78a4b28afd9ee089440465ceefd32bd1d82b74ae69caa80b6fb6c sepolia-00161-36e611bd.era1 +ddf201b59ebc77eccdec97b0a5a7d7a89dda02928ff7c5cca8868b5aba84f533 sepolia-00162-3557cc58.era1 +041be469daf6f7abaa0cfe66c4d68b7d9d223383553c16f078edb3a1fa3d0d92 sepolia-00163-638b9afe.era1 +68d151048412318cbd0af98efa27d7ee16e2fc58830d93b4d76b66cb632ebfc3 sepolia-00164-fbc67b64.era1 +3025771380736a5de790ead991c0512a4c73106fd1d2f169863d395964238ac1 sepolia-00165-442b83a1.era1 +6d6b1273a6fc353c6999b4c9be301660a404b78cced911e230991305da990e3c sepolia-00166-018915fc.era1 +01052c4f779e4ec276498d4192b8ad11ad6d2d3992224bb62ea882a80deda935 sepolia-00167-0df2b4c5.era1 +2f8f3034eb7e41c5add0e639e21b52c0794f36bb5918274849402c5fe0d247e6 sepolia-00168-7cab66f5.era1 +504b0203d995e5ef6aee679b048c276f45b05dc68a6fd319a0f4f0133b230a77 sepolia-00169-75850131.era1 +b277df79fe5a83189f9619b275a9aaab63db8506539c56997be61fd5a3552bb9 sepolia-00170-c2044b78.era1 +1af0a1778fc5d2a53aaca06dbda6671b91ae3f56da73a6c6708bc729e5fb9efd sepolia-00171-af44ff2e.era1 +a977654c0ed243ccce7b5514914ec94d70a3a742ff014ac52b0654e21940f2da sepolia-00172-bd76a8d9.era1 +146672b2e63c320c9474ecf41d7f32f31f5298512cc8f911074c88789849d935 sepolia-00173-b6924da5.era1 +82af1c5bc51f9e9ae1be5d0e0827ea755d104ed373b374c08ef401115491671f sepolia-00174-2a61fac5.era1 +627ee6cf9282191ff32d291fc0437b812bad01f158b5ca42ae717b7bbb125f8c sepolia-00175-1cc76405.era1 +6ee38f117646a47f6ab2f666fa6460bda770c194ae4a29744534e9bfd9e7ba3b sepolia-00176-20daa2c6.era1 +28e7e49e71dd8581cc484d8be89fb083a79577b06f26c1e3bfd4a57bdf24c5e7 sepolia-00177-379df7e4.era1 +055c365cfeda3495a5e2b334803dfffe7af2a584745acabc42b5ad146b250534 sepolia-00178-88c9d2aa.era1 +bca9a920232983636b7cf20fc8319f891df94ba0860f51b5a052290b58d9e3ae sepolia-00179-8329b80c.era1 +e894a6f3ce0ede2825aaafa64122a5f458cd3b2ecaeae20a44a59fd668971bb0 sepolia-00180-22de0418.era1 +83bfe0d7c49c3a108a4ed044119a3549f2f4b0f8e74c971150b6977b7d46a250 sepolia-00181-b3fbe6f2.era1 +3b806177536ac3615f376440c0589696ea746eea0215bb3b175df8f4f1e73894 sepolia-00182-a4f0a8a1.era1 diff --git a/internal/era/eradl/eradl.go b/internal/era/eradl/eradl.go new file mode 100644 index 00000000000..30bd2bc0d54 --- /dev/null +++ b/internal/era/eradl/eradl.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 eradl implements downloading of era1 files. +package eradl + +import ( + _ "embed" + "fmt" + "net/url" + "path/filepath" + "regexp" + "strconv" + + "github.com/ethereum/go-ethereum/internal/download" + "github.com/ethereum/go-ethereum/internal/era" +) + +//go:embed checksums_mainnet.txt +var mainnetDB []byte + +//go:embed checksums_sepolia.txt +var sepoliaDB []byte + +type Loader struct { + csdb *download.ChecksumDB + network string + baseURL *url.URL +} + +// New creates an era1 loader for the given server URL and network name. +func New(baseURL string, network string) (*Loader, error) { + var checksums []byte + switch network { + case "mainnet": + checksums = mainnetDB + case "sepolia": + checksums = sepoliaDB + default: + return nil, fmt.Errorf("missing era1 checksum definitions for network %q", network) + } + + csdb, err := download.ParseChecksums(checksums) + if err != nil { + return nil, fmt.Errorf("invalid checksums: %v", err) + } + + base, err := url.Parse(baseURL) + if err != nil { + return nil, fmt.Errorf("invalid base URL %q: %v", baseURL, err) + } + if base.Scheme != "http" && base.Scheme != "https" { + return nil, fmt.Errorf("invalid base URL scheme, expected http(s): %q", baseURL) + } + + l := &Loader{ + network: network, + csdb: csdb, + baseURL: base, + } + return l, nil +} + +// DownloadAll downloads all known era1 files to the given directory. +func (l *Loader) DownloadAll(destDir string) error { + for file := range l.csdb.Files() { + if err := l.download(file, destDir); err != nil { + return err + } + } + return nil +} + +// DownloadBlockRange fetches the era1 files for the given block range. +func (l *Loader) DownloadBlockRange(start, end uint64, destDir string) error { + startEpoch := start / uint64(era.MaxEra1Size) + endEpoch := end / uint64(era.MaxEra1Size) + return l.DownloadEpochRange(startEpoch, endEpoch, destDir) +} + +// DownloadEpochRange fetches the era1 files in the given epoch range. +func (l *Loader) DownloadEpochRange(start, end uint64, destDir string) error { + pat := regexp.MustCompile(regexp.QuoteMeta(l.network) + "-([0-9]+)-[0-9a-f]+\\.era1") + for file := range l.csdb.Files() { + m := pat.FindStringSubmatch(file) + if len(m) == 2 { + fileEpoch, _ := strconv.Atoi(m[1]) + if uint64(fileEpoch) >= start && uint64(fileEpoch) <= end { + if err := l.download(file, destDir); err != nil { + return err + } + } + } + } + return nil +} + +func (l *Loader) download(file, destDir string) error { + url := l.baseURL.JoinPath(file).String() + dest := filepath.Join(destDir, file) + return l.csdb.DownloadFile(url, dest) +} From 1f175347c47810564c7d6fab3bbd855eb449468d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Kj=C3=A6rstad?= Date: Thu, 15 May 2025 22:54:18 +0200 Subject: [PATCH 177/658] build: upgrade -dlgo version to Go 1.24.3 (#31774) New security fix: https://groups.google.com/g/golang-announce/c/UZoIkUT367A --- build/checksums.txt | 94 ++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/build/checksums.txt b/build/checksums.txt index a914d798600..c819429969f 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -5,54 +5,54 @@ # https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-6%40v1.0.0/ b69211752a3029083c020dc635fe12156ca1a6725a08559da540a0337586a77e fixtures_pectra-devnet-6.tar.gz -# version:golang 1.24.2 +# version:golang 1.24.3 # https://go.dev/dl/ -9dc77ffadc16d837a1bf32d99c624cb4df0647cee7b119edd9e7b1bcc05f2e00 go1.24.2.src.tar.gz -427b373540d8fd51dbcc46bdecd340af109cd41514443c000d3dcde72b2c65a3 go1.24.2.aix-ppc64.tar.gz -238d9c065d09ff6af229d2e3b8b5e85e688318d69f4006fb85a96e41c216ea83 go1.24.2.darwin-amd64.tar.gz -535ed9ff283fee39575a7fb9b6d8b1901b6dc640d06dc71fd7d3faeefdaf8030 go1.24.2.darwin-amd64.pkg -b70f8b3c5b4ccb0ad4ffa5ee91cd38075df20fdbd953a1daedd47f50fbcff47a go1.24.2.darwin-arm64.tar.gz -4732f607a47ce4d898c0af01ff68f07e0820a6b50603aef5d5c777d1102505e2 go1.24.2.darwin-arm64.pkg -c17686b5fd61a663fbfafccfa177961be59386cf294e935ce35866b9dcb8e78a go1.24.2.dragonfly-amd64.tar.gz -026f1dd906189acff714c7625686bbc4ed91042618ba010d45b671461acc9e63 go1.24.2.freebsd-386.tar.gz -49399ba759b570a8f87d12179133403da6c2dd296d63a8830dee309161b9c40c go1.24.2.freebsd-amd64.tar.gz -1f48f47183794d97c29736004247ab541177cf984ac6322c78bc43828daa1172 go1.24.2.freebsd-arm.tar.gz -ef856428b60a8c0bd9a2cba596e83024be6f1c2d5574e89cb1ff2262b08df8b9 go1.24.2.freebsd-arm64.tar.gz -ec2088823e16df00600a6d0f72e9a7dc6d2f80c9c140c2043c0cf20e1404d1a9 go1.24.2.freebsd-riscv64.tar.gz -e030e7cedbb8688f1d75cb80f3de6ee2e6617a67d34051e794e5992b53462147 go1.24.2.illumos-amd64.tar.gz -4c382776d52313266f3026236297a224a6688751256a2dffa3f524d8d6f6c0ba go1.24.2.linux-386.tar.gz -68097bd680839cbc9d464a0edce4f7c333975e27a90246890e9f1078c7e702ad go1.24.2.linux-amd64.tar.gz -756274ea4b68fa5535eb9fe2559889287d725a8da63c6aae4d5f23778c229f4b go1.24.2.linux-arm64.tar.gz -438d5d3d7dcb239b58d893a715672eabe670b9730b1fd1c8fc858a46722a598a go1.24.2.linux-armv6l.tar.gz -6aefd3bf59c3c5592eda4fb287322207f119c2210f3795afa9be48d3ccb73e1b go1.24.2.linux-loong64.tar.gz -93e49bb4692783b0e9a2deab9558c6e8d2867f35592aeff285adda60924167f3 go1.24.2.linux-mips.tar.gz -6e86e703675016f3faf6604b8f68f20dc1bba75849136e6dd4f43f69c8a4a9d9 go1.24.2.linux-mips64.tar.gz -f233d237538ca1559a7d7cf519a29f0147923a951377bc4e467af4c059e68851 go1.24.2.linux-mips64le.tar.gz -545e1b9a7939f923fd53bde98334b987ef42eb353ee3e0bfede8aa06079d6b24 go1.24.2.linux-mipsle.tar.gz -6eab31481f2f46187bc1b6c887662eef06fc9d7271a8390854072cdb387c8d74 go1.24.2.linux-ppc64.tar.gz -5fff857791d541c71d8ea0171c73f6f99770d15ff7e2ad979104856d01f36563 go1.24.2.linux-ppc64le.tar.gz -91bda1558fcbd1c92769ad86c8f5cf796f8c67b0d9d9c19f76eecfc75ce71527 go1.24.2.linux-riscv64.tar.gz -1cb3448166d6abb515a85a3ee5afbdf932081fb58ad7143a8fb666fbc06146d9 go1.24.2.linux-s390x.tar.gz -a9a2c0db2e826f20f00b02bee01dfdaeb49591c2f6ffacb78dc64a950894f7ff go1.24.2.netbsd-386.tar.gz -cd1a35b76ed9c7b6c0c1616741bd319699a77867ade0be9924f32496c0a87a3f go1.24.2.netbsd-amd64.tar.gz -8c666388d066e479155cc5116950eeb435df28087ef277c18f1dc7479f836e60 go1.24.2.netbsd-arm.tar.gz -5d42f0be04f58da5be788a1e260f8747c316b8ce182bf0b273c2e4c691feaa1a go1.24.2.netbsd-arm64.tar.gz -688effa23ea3973cc8b0fdf4246712cbeef55ff20c45f3a9e28b0c2db04246cf go1.24.2.openbsd-386.tar.gz -e5daf95f1048d8026b1366450a3f8044d668b0639db6422ad9a83755c6745cf7 go1.24.2.openbsd-amd64.tar.gz -aeadaf74bd544d1a12ba9b14c0e7cdb1964de3ba9a52acb4619e91dbae7def7b go1.24.2.openbsd-arm.tar.gz -9e222d9adb0ce836a5b3c8d5aadbd167c8869c030b113f4a81aa88e9a200f279 go1.24.2.openbsd-arm64.tar.gz -192fffa34536adc3cd1bb7c1ee785b8bc156ae7afd10bbf5db99ec8f2e93066e go1.24.2.openbsd-ppc64.tar.gz -a23e90b451a390549042c2a7efbec6f29ed98b2d5618c8d2a35704e21be96e09 go1.24.2.openbsd-riscv64.tar.gz -5cdcafe455d859b02779611a5a1e1d63e498b922e05818fb3debe410a5959e9e go1.24.2.plan9-386.tar.gz -81351659804fa505c1b3ec6fdf9599f7f88df08614307eeb96071bf5e2e74beb go1.24.2.plan9-amd64.tar.gz -6e337d5def14ed0123423c1c32e2e6d8b19161e5d5ffaa7356dad48ee0fd80b4 go1.24.2.plan9-arm.tar.gz -07e6926ebc476c044d7d5b17706abfc52be52bccc2073d1734174efe63c6b35e go1.24.2.solaris-amd64.tar.gz -13d86cb818bba331da75fcd18246ab31a1067b44fb4a243b6dfd93097eda7f37 go1.24.2.windows-386.zip -8a702d9f7104a15bd935f4191c58c24c0b6389e066b9d5661b93915114a2bef0 go1.24.2.windows-386.msi -29c553aabee0743e2ffa3e9fa0cda00ef3b3cc4ff0bc92007f31f80fd69892e1 go1.24.2.windows-amd64.zip -acefb191e72fea0bdb1a3f5f8f6f5ab18b42b3bbce0c7183f189f25953aff275 go1.24.2.windows-amd64.msi -ab267f7f9a3366d48d7664be9e627ce3e63273231430cce5f7783fb910f14148 go1.24.2.windows-arm64.zip -d187bfe539356c39573d2f46766d1d08122b4f33da00fd14d12485fa9e241ff5 go1.24.2.windows-arm64.msi +229c08b600b1446798109fae1f569228102c8473caba8104b6418cb5bc032878 go1.24.3.src.tar.gz +6f6901497547db3b77c14f7f953fbcef9fa5fb84199ee2ee14a5686e66bed5a6 go1.24.3.aix-ppc64.tar.gz +a05fa7e4043a4fec66897135219e3b8ab2202b5ef351c60c2fbb531dfb8f2900 go1.24.3.darwin-amd64.pkg +13e6fe3fcf65689d77d40e633de1e31c6febbdbcb846eb05fc2434ed2213e92b go1.24.3.darwin-amd64.tar.gz +97055ff4214043b39dc32e043fdd5c565df7c0a4e2fc0174e779a134c347ae0e go1.24.3.darwin-arm64.pkg +64a3fa22142f627e78fac3018ce3d4aeace68b743eff0afda8aae0411df5e4fb go1.24.3.darwin-arm64.tar.gz +32de3fd44d5055973978436a7f1f0ffbaae85c1b603ec6105e5c38d8a674c721 go1.24.3.dragonfly-amd64.tar.gz +9fe6101b3797919bd7337ee5ce591954f85d59db7ae88983904db29fd64c3dd1 go1.24.3.freebsd-386.tar.gz +6ccf4cca287e90cc28cd7954b6172f5d177a17e20b072b65f7f39636c325e2fb go1.24.3.freebsd-amd64.tar.gz +ce45ebf389066f82a7b056b66dd650efb51fde6f8bf92a2a3ab6990f02788ebf go1.24.3.freebsd-arm.tar.gz +8f6494a12a874d0ea57c67987829359e016960ce3ba0673273609d6ac2af589a go1.24.3.freebsd-arm64.tar.gz +f9db392560cf0851f0bc8f2190e1978e01b4603038c27fecfc8658a695b71616 go1.24.3.freebsd-riscv64.tar.gz +01717fff64c5d98457272002fa825d0a15e307bf6e189f2b0c23817fa033b61c go1.24.3.illumos-amd64.tar.gz +41b1051063e68cbd2b919bf12326764fe33937cf1d32b5c529dd1a4f43dce578 go1.24.3.linux-386.tar.gz +3333f6ea53afa971e9078895eaa4ac7204a8c6b5c68c10e6bc9a33e8e391bdd8 go1.24.3.linux-amd64.tar.gz +a463cb59382bd7ae7d8f4c68846e73c4d589f223c589ac76871b66811ded7836 go1.24.3.linux-arm64.tar.gz +17a392d7e826625dd12a32099df0b00b85c32d8132ed86fe917183ee5c3f88ed go1.24.3.linux-armv6l.tar.gz +e4b003c04c902edc140153d279b42167f1ad7c229f48f1f729bbef5e65e88d1f go1.24.3.linux-loong64.tar.gz +1c79d89edf835edf9d4336ccea7cb89bc5c0ca82b12b36b218d599a5400d60fe go1.24.3.linux-mips.tar.gz +0b64fe147d69f4d681d8e8a035c760477531432f83d831f18d37cb9bf3652488 go1.24.3.linux-mips64.tar.gz +396b784c255b64512dc00c302c053e43a3cbfc77518664c6ac5569aafad4d1e6 go1.24.3.linux-mips64le.tar.gz +93898313887f14e8efbe9d7386d5da4792b2d6c492bee562993fd4c9daa75c6d go1.24.3.linux-mipsle.tar.gz +873ae3a6a6655a7b6f820e095d9965507e8dfd3cf76bc92d75c564ecbca385f6 go1.24.3.linux-ppc64.tar.gz +341a749d168f47b1d4dad25e32cae70849b7ceed7c290823b853c9e6b0df0856 go1.24.3.linux-ppc64le.tar.gz +fa482f53ccb4ba280316b8c5751ea67291507280d9166f2a38fe4d9b5d5fb64b go1.24.3.linux-riscv64.tar.gz +a87b0c2a079a0bece1620fb29a00e02b4dba17507850f837e754af7d57cda282 go1.24.3.linux-s390x.tar.gz +63155382308db1306200aff7821aa26bf2a2dda23537dd637a9704b485b6ddf0 go1.24.3.netbsd-386.tar.gz +fe2c5c79482958b867c08a4fc2a10a998de9c0206b08d5b3ebcb2232e8d2777c go1.24.3.netbsd-amd64.tar.gz +e8ff77aef21521b5dd94e44282a3243309b80717414cf12f72835a45886a049f go1.24.3.netbsd-arm.tar.gz +b337fbaf82822685940ffaa76fbcf4be5d2f0258bc819cd80bc408b491f45c04 go1.24.3.netbsd-arm64.tar.gz +c1bb9dd8418480aa7f65452b08de3759da3bf89702be71b5a9fc084836b24ad5 go1.24.3.openbsd-386.tar.gz +531218de748b0caaf6d1ad18921206fc12baaa89bf483a0a5e60a571c206fe6f go1.24.3.openbsd-amd64.tar.gz +bcd0dc959986fc346969b5d4111c3c8031882d8bf8d87a2c2ecf1328962a91f2 go1.24.3.openbsd-arm.tar.gz +00ee6f8f1c41fd2e28ad386bd7e39acce7cab84af6de835855b29d1c597335c4 go1.24.3.openbsd-arm64.tar.gz +9f4ec0a9203ed3c54ce1a2a390ad3d45838cdb7efd85baeff857e37dfde04edd go1.24.3.openbsd-ppc64.tar.gz +da4d6f80e2373250d8c31c32dcd1e08775c327c0d610923604660cc0e07e8cba go1.24.3.openbsd-riscv64.tar.gz +f5d02149132eedda6c2d46b360d7da462b8a5f9e3f8567db100c2d7bff0ddcd7 go1.24.3.plan9-386.tar.gz +175f3d79f4762a3c545d2c6393bf6b8bac24e838026869dafab06b930735c94f go1.24.3.plan9-amd64.tar.gz +d1e4ac15095da1611659261c2228c2058756cf87d61d9fad262f76755ef26849 go1.24.3.plan9-arm.tar.gz +e644220a6ced3c07a7acc1364193cb709a97737dd8b6792a07a8ec6d9996713e go1.24.3.solaris-amd64.tar.gz +0d7e7dc0a31ba0cdd487415709d03b02fc9490ef111e8dfd22788a6d63316f37 go1.24.3.windows-386.msi +c27c463a61ab849266baa0c17a6c5c4256a574ab642f609ba25c96ec965dc184 go1.24.3.windows-386.zip +d5b7637e7e138be877d96a4501709d480e050d86a8f402bc950e72112b5aedc5 go1.24.3.windows-amd64.msi +be9787cb08998b1860fe3513e48a5fe5b96302d358a321b58e651184fa9638b3 go1.24.3.windows-amd64.zip +7efde2e5e8468e9caf2c7fc94f4da78a726a5031a1ed63acff7899527cdddff6 go1.24.3.windows-arm64.msi +eec9fa736056b54dd88ecb669db2bfad39b0c48f6f9080f036dfa1ca42dc4bb5 go1.24.3.windows-arm64.zip # version:golangci 2.0.2 # https://github.com/golangci/golangci-lint/releases/ From 57e985ecabf567f9a752e1e3ed56533f63787d0e Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Fri, 16 May 2025 10:20:36 +0200 Subject: [PATCH 178/658] cmd/utils: remove deprecated LES flags (#31838) They were not widely used in users setups. --- cmd/geth/main.go | 6 ------ cmd/utils/flags.go | 23 ----------------------- cmd/utils/flags_legacy.go | 37 ------------------------------------- 3 files changed, 66 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index bdff96e3694..733c9cf2302 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -91,13 +91,7 @@ var ( utils.LogNoHistoryFlag, utils.LogExportCheckpointsFlag, utils.StateHistoryFlag, - utils.LightServeFlag, // deprecated - utils.LightIngressFlag, // deprecated - utils.LightEgressFlag, // deprecated - utils.LightMaxPeersFlag, // deprecated - utils.LightNoPruneFlag, // deprecated utils.LightKDFFlag, - utils.LightNoSyncServeFlag, // deprecated utils.EthRequiredBlocksFlag, utils.LegacyWhitelistFlag, // deprecated utils.CacheFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index a100ee15485..169f5ed49eb 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1245,28 +1245,6 @@ func setIPC(ctx *cli.Context, cfg *node.Config) { } } -// setLes shows the deprecation warnings for LES flags. -func setLes(ctx *cli.Context, cfg *ethconfig.Config) { - if ctx.IsSet(LightServeFlag.Name) { - log.Warn("The light server has been deprecated, please remove this flag", "flag", LightServeFlag.Name) - } - if ctx.IsSet(LightIngressFlag.Name) { - log.Warn("The light server has been deprecated, please remove this flag", "flag", LightIngressFlag.Name) - } - if ctx.IsSet(LightEgressFlag.Name) { - log.Warn("The light server has been deprecated, please remove this flag", "flag", LightEgressFlag.Name) - } - if ctx.IsSet(LightMaxPeersFlag.Name) { - log.Warn("The light server has been deprecated, please remove this flag", "flag", LightMaxPeersFlag.Name) - } - if ctx.IsSet(LightNoPruneFlag.Name) { - log.Warn("The light server has been deprecated, please remove this flag", "flag", LightNoPruneFlag.Name) - } - if ctx.IsSet(LightNoSyncServeFlag.Name) { - log.Warn("The light server has been deprecated, please remove this flag", "flag", LightNoSyncServeFlag.Name) - } -} - // MakeDatabaseHandles raises out the number of allowed file handles per process // for Geth and returns half of the allowance to assign to the database. func MakeDatabaseHandles(max int) int { @@ -1582,7 +1560,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { setBlobPool(ctx, &cfg.BlobPool) setMiner(ctx, &cfg.Miner) setRequiredBlocks(ctx, cfg) - setLes(ctx, cfg) // Cap the cache allowance and tune the garbage collector mem, err := gopsutil.VirtualMemory() diff --git a/cmd/utils/flags_legacy.go b/cmd/utils/flags_legacy.go index ff63dd5685b..dc41a75d119 100644 --- a/cmd/utils/flags_legacy.go +++ b/cmd/utils/flags_legacy.go @@ -39,12 +39,6 @@ var DeprecatedFlags = []cli.Flag{ CacheTrieRejournalFlag, LegacyDiscoveryV5Flag, TxLookupLimitFlag, - LightServeFlag, - LightIngressFlag, - LightEgressFlag, - LightMaxPeersFlag, - LightNoPruneFlag, - LightNoSyncServeFlag, LogBacktraceAtFlag, LogDebugFlag, MinerNewPayloadTimeoutFlag, @@ -88,37 +82,6 @@ var ( Value: ethconfig.Defaults.TransactionHistory, Category: flags.DeprecatedCategory, } - // Light server and client settings, Deprecated November 2023 - LightServeFlag = &cli.IntFlag{ - Name: "light.serve", - Usage: "Maximum percentage of time allowed for serving LES requests (deprecated)", - Category: flags.DeprecatedCategory, - } - LightIngressFlag = &cli.IntFlag{ - Name: "light.ingress", - Usage: "Incoming bandwidth limit for serving light clients (deprecated)", - Category: flags.DeprecatedCategory, - } - LightEgressFlag = &cli.IntFlag{ - Name: "light.egress", - Usage: "Outgoing bandwidth limit for serving light clients (deprecated)", - Category: flags.DeprecatedCategory, - } - LightMaxPeersFlag = &cli.IntFlag{ - Name: "light.maxpeers", - Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated)", - Category: flags.DeprecatedCategory, - } - LightNoPruneFlag = &cli.BoolFlag{ - Name: "light.nopruning", - Usage: "Disable ancient light chain data pruning (deprecated)", - Category: flags.DeprecatedCategory, - } - LightNoSyncServeFlag = &cli.BoolFlag{ - Name: "light.nosyncserve", - Usage: "Enables serving light clients before syncing (deprecated)", - Category: flags.DeprecatedCategory, - } // Deprecated November 2023 LogBacktraceAtFlag = &cli.StringFlag{ Name: "log.backtrace", From 892a661ee2e7bcafcfd4eb981abf0ba697d57a5c Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 16 May 2025 18:29:38 +0800 Subject: [PATCH 179/658] core, triedb/pathdb: final integration (snapshot integration pt 5) (#30661) In this pull request, snapshot generation in pathdb has been ported from the legacy state snapshot implementation. Additionally, when running in path mode, legacy state snapshot data is now managed by the pathdb based snapshot logic. Note: Existing snapshot data will be re-generated, regardless of whether it was previously fully constructed. --- core/blockchain.go | 62 +- core/blockchain_repair_test.go | 34 +- core/blockchain_sethead_test.go | 2 +- core/blockchain_snapshot_test.go | 22 +- core/state/database.go | 25 +- core/state/snapshot/generate_test.go | 4 +- core/state/statedb_test.go | 3 +- eth/handler.go | 3 +- eth/protocols/snap/handler.go | 44 +- eth/protocols/snap/sync_test.go | 2 +- tests/block_test_util.go | 6 +- trie/database_test.go | 6 +- triedb/database.go | 20 + triedb/pathdb/buffer.go | 10 +- triedb/pathdb/context.go | 246 ++++++++ triedb/pathdb/database.go | 138 ++++- triedb/pathdb/database_test.go | 3 +- triedb/pathdb/disklayer.go | 205 ++++++- triedb/pathdb/errors.go | 9 + triedb/pathdb/flush.go | 68 +++ triedb/pathdb/generate.go | 856 +++++++++++++++++++++++++++ triedb/pathdb/generate_test.go | 766 ++++++++++++++++++++++++ triedb/pathdb/iterator.go | 18 +- triedb/pathdb/iterator_binary.go | 36 +- triedb/pathdb/iterator_fast.go | 25 +- triedb/pathdb/iterator_test.go | 217 +++---- triedb/pathdb/journal.go | 59 +- triedb/pathdb/metrics.go | 51 +- triedb/pathdb/states.go | 9 +- 29 files changed, 2701 insertions(+), 248 deletions(-) create mode 100644 triedb/pathdb/context.go create mode 100644 triedb/pathdb/generate.go create mode 100644 triedb/pathdb/generate_test.go diff --git a/core/blockchain.go b/core/blockchain.go index 320b90dcbef..f93cddfe44b 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -183,8 +183,13 @@ func (c *CacheConfig) triedbConfig(isVerkle bool) *triedb.Config { } if c.StateScheme == rawdb.PathScheme { config.PathDB = &pathdb.Config{ - StateHistory: c.StateHistory, - CleanCacheSize: c.TrieCleanLimit * 1024 * 1024, + StateHistory: c.StateHistory, + TrieCleanSize: c.TrieCleanLimit * 1024 * 1024, + StateCleanSize: c.SnapshotLimit * 1024 * 1024, + + // TODO(rjl493456442): The write buffer represents the memory limit used + // for flushing both trie data and state data to disk. The config name + // should be updated to eliminate the confusion. WriteBufferSize: c.TrieDirtyLimit * 1024 * 1024, } } @@ -380,11 +385,14 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis // Do nothing here until the state syncer picks it up. log.Info("Genesis state is missing, wait state sync") } else { - // Head state is missing, before the state recovery, find out the - // disk layer point of snapshot(if it's enabled). Make sure the - // rewound point is lower than disk layer. + // Head state is missing, before the state recovery, find out the disk + // layer point of snapshot(if it's enabled). Make sure the rewound point + // is lower than disk layer. + // + // Note it's unnecessary in path mode which always keep trie data and + // state data consistent. var diskRoot common.Hash - if bc.cacheConfig.SnapshotLimit > 0 { + if bc.cacheConfig.SnapshotLimit > 0 && bc.cacheConfig.StateScheme == rawdb.HashScheme { diskRoot = rawdb.ReadSnapshotRoot(bc.db) } if diskRoot != (common.Hash{}) { @@ -457,7 +465,32 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis bc.logger.OnGenesisBlock(bc.genesisBlock, alloc) } } + bc.setupSnapshot() + + // Rewind the chain in case of an incompatible config upgrade. + if compatErr != nil { + log.Warn("Rewinding chain to upgrade configuration", "err", compatErr) + if compatErr.RewindToTime > 0 { + bc.SetHeadWithTimestamp(compatErr.RewindToTime) + } else { + bc.SetHead(compatErr.RewindToBlock) + } + rawdb.WriteChainConfig(db, genesisHash, chainConfig) + } + + // Start tx indexer if it's enabled. + if txLookupLimit != nil { + bc.txIndexer = newTxIndexer(*txLookupLimit, bc) + } + return bc, nil +} +func (bc *BlockChain) setupSnapshot() { + // Short circuit if the chain is established with path scheme, as the + // state snapshot has been integrated into path database natively. + if bc.cacheConfig.StateScheme == rawdb.PathScheme { + return + } // Load any existing snapshot, regenerating it if loading failed if bc.cacheConfig.SnapshotLimit > 0 { // If the chain was rewound past the snapshot persistent layer (causing @@ -465,7 +498,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis // in recovery mode and in that case, don't invalidate the snapshot on a // head mismatch. var recover bool - head := bc.CurrentBlock() if layer := rawdb.ReadSnapshotRecoveryNumber(bc.db); layer != nil && *layer >= head.Number.Uint64() { log.Warn("Enabling snapshot recovery", "chainhead", head.Number, "diskbase", *layer) @@ -482,22 +514,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis // Re-initialize the state database with snapshot bc.statedb = state.NewDatabase(bc.triedb, bc.snaps) } - - // Rewind the chain in case of an incompatible config upgrade. - if compatErr != nil { - log.Warn("Rewinding chain to upgrade configuration", "err", compatErr) - if compatErr.RewindToTime > 0 { - bc.SetHeadWithTimestamp(compatErr.RewindToTime) - } else { - bc.SetHead(compatErr.RewindToBlock) - } - rawdb.WriteChainConfig(db, genesisHash, chainConfig) - } - // Start tx indexer if it's enabled. - if txLookupLimit != nil { - bc.txIndexer = newTxIndexer(*txLookupLimit, bc) - } - return bc, nil } // empty returns an indicator whether the blockchain is empty. diff --git a/core/blockchain_repair_test.go b/core/blockchain_repair_test.go index 6c52d057adf..edc8854892f 100644 --- a/core/blockchain_repair_test.go +++ b/core/blockchain_repair_test.go @@ -1791,7 +1791,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s } ) defer engine.Close() - if snapshots { + if snapshots && scheme == rawdb.HashScheme { config.SnapshotLimit = 256 config.SnapshotWait = true } @@ -1820,7 +1820,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s if err := chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false); err != nil { t.Fatalf("Failed to flush trie state: %v", err) } - if snapshots { + if snapshots && scheme == rawdb.HashScheme { if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil { t.Fatalf("Failed to flatten snapshots: %v", err) } @@ -1952,8 +1952,10 @@ func testIssue23496(t *testing.T, scheme string) { if _, err := chain.InsertChain(blocks[1:2]); err != nil { t.Fatalf("Failed to import canonical chain start: %v", err) } - if err := chain.snaps.Cap(blocks[1].Root(), 0); err != nil { - t.Fatalf("Failed to flatten snapshots: %v", err) + if scheme == rawdb.HashScheme { + if err := chain.snaps.Cap(blocks[1].Root(), 0); err != nil { + t.Fatalf("Failed to flatten snapshots: %v", err) + } } // Insert block B3 and commit the state into disk @@ -1997,15 +1999,23 @@ func testIssue23496(t *testing.T, scheme string) { } expHead := uint64(1) if scheme == rawdb.PathScheme { - expHead = uint64(2) + // The pathdb database makes sure that snapshot and trie are consistent, + // so only the last block is reverted in case of a crash. + expHead = uint64(3) } if head := chain.CurrentBlock(); head.Number.Uint64() != expHead { t.Errorf("Head block mismatch: have %d, want %d", head.Number, expHead) } - - // Reinsert B2-B4 - if _, err := chain.InsertChain(blocks[1:]); err != nil { - t.Fatalf("Failed to import canonical chain tail: %v", err) + if scheme == rawdb.PathScheme { + // Reinsert B4 + if _, err := chain.InsertChain(blocks[3:]); err != nil { + t.Fatalf("Failed to import canonical chain tail: %v", err) + } + } else { + // Reinsert B2-B4 + if _, err := chain.InsertChain(blocks[1:]); err != nil { + t.Fatalf("Failed to import canonical chain tail: %v", err) + } } if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) { t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4) @@ -2016,7 +2026,9 @@ func testIssue23496(t *testing.T, scheme string) { if head := chain.CurrentBlock(); head.Number.Uint64() != uint64(4) { t.Errorf("Head block mismatch: have %d, want %d", head.Number, uint64(4)) } - if layer := chain.Snapshots().Snapshot(blocks[2].Root()); layer == nil { - t.Error("Failed to regenerate the snapshot of known state") + if scheme == rawdb.HashScheme { + if layer := chain.Snapshots().Snapshot(blocks[2].Root()); layer == nil { + t.Error("Failed to regenerate the snapshot of known state") + } } } diff --git a/core/blockchain_sethead_test.go b/core/blockchain_sethead_test.go index 424854b2bf8..51e2a5275fe 100644 --- a/core/blockchain_sethead_test.go +++ b/core/blockchain_sethead_test.go @@ -2023,7 +2023,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme } if tt.commitBlock > 0 { chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false) - if snapshots { + if snapshots && scheme == rawdb.HashScheme { if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil { t.Fatalf("Failed to flatten snapshots: %v", err) } diff --git a/core/blockchain_snapshot_test.go b/core/blockchain_snapshot_test.go index 1a6fe38af6d..23640fe8433 100644 --- a/core/blockchain_snapshot_test.go +++ b/core/blockchain_snapshot_test.go @@ -105,7 +105,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo if basic.commitBlock > 0 && basic.commitBlock == point { chain.TrieDB().Commit(blocks[point-1].Root(), false) } - if basic.snapshotBlock > 0 && basic.snapshotBlock == point { + if basic.snapshotBlock > 0 && basic.snapshotBlock == point && basic.scheme == rawdb.HashScheme { // Flushing the entire snap tree into the disk, the // relevant (a) snapshot root and (b) snapshot generator // will be persisted atomically. @@ -149,13 +149,17 @@ func (basic *snapshotTestBasic) verify(t *testing.T, chain *BlockChain, blocks [ block := chain.GetBlockByNumber(basic.expSnapshotBottom) if block == nil { t.Errorf("The corresponding block[%d] of snapshot disk layer is missing", basic.expSnapshotBottom) - } else if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) { - t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot()) + } else if basic.scheme == rawdb.HashScheme { + if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) { + t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot()) + } } // Check the snapshot, ensure it's integrated - if err := chain.snaps.Verify(block.Root()); err != nil { - t.Errorf("The disk layer is not integrated %v", err) + if basic.scheme == rawdb.HashScheme { + if err := chain.snaps.Verify(block.Root()); err != nil { + t.Errorf("The disk layer is not integrated %v", err) + } } } @@ -565,12 +569,14 @@ func TestHighCommitCrashWithNewSnapshot(t *testing.T) { // // Expected head header : C8 // Expected head fast block: C8 - // Expected head block : G - // Expected snapshot disk : C4 + // Expected head block : G (Hash mode), C6 (Hash mode) + // Expected snapshot disk : C4 (Hash mode) for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { expHead := uint64(0) if scheme == rawdb.PathScheme { - expHead = uint64(4) + // The pathdb database makes sure that snapshot and trie are consistent, + // so only the last two blocks are reverted in case of a crash. + expHead = uint64(6) } test := &crashSnapshotTest{ snapshotTestBasic{ diff --git a/core/state/database.go b/core/state/database.go index cef59cccfb8..aec841f59b5 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -175,26 +175,27 @@ func NewDatabaseForTesting() *CachingDB { func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { var readers []StateReader - // Set up the state snapshot reader if available. This feature - // is optional and may be partially useful if it's not fully - // generated. - if db.snap != nil { - // If standalone state snapshot is available (hash scheme), - // then construct the legacy snap reader. + // Configure the state reader using the standalone snapshot in hash mode. + // This reader offers improved performance but is optional and only + // partially useful if the snapshot is not fully generated. + if db.TrieDB().Scheme() == rawdb.HashScheme && db.snap != nil { snap := db.snap.Snapshot(stateRoot) if snap != nil { readers = append(readers, newFlatReader(snap)) } - } else { - // If standalone state snapshot is not available, try to construct - // the state reader with database. + } + // Configure the state reader using the path database in path mode. + // This reader offers improved performance but is optional and only + // partially useful if the snapshot data in path database is not + // fully generated. + if db.TrieDB().Scheme() == rawdb.PathScheme { reader, err := db.triedb.StateReader(stateRoot) if err == nil { - readers = append(readers, newFlatReader(reader)) // state reader is optional + readers = append(readers, newFlatReader(reader)) } } - // Set up the trie reader, which is expected to always be available - // as the gatekeeper unless the state is corrupted. + // Configure the trie reader, which is expected to be available as the + // gatekeeper unless the state is corrupted. tr, err := newTrieReader(stateRoot, db.triedb, db.pointCache) if err != nil { return nil, err diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go index 3de7735ef86..3f83cb1a00b 100644 --- a/core/state/snapshot/generate_test.go +++ b/core/state/snapshot/generate_test.go @@ -166,7 +166,9 @@ func newHelper(scheme string) *testHelper { diskdb := rawdb.NewMemoryDatabase() config := &triedb.Config{} if scheme == rawdb.PathScheme { - config.PathDB = &pathdb.Config{} // disable caching + config.PathDB = &pathdb.Config{ + SnapshotNoBuild: true, + } // disable caching } else { config.HashDB = &hashdb.Config{} // disable caching } diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index e740c64faa3..709b5ed510b 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -979,7 +979,8 @@ func testMissingTrieNodes(t *testing.T, scheme string) { ) if scheme == rawdb.PathScheme { tdb = triedb.NewDatabase(memDb, &triedb.Config{PathDB: &pathdb.Config{ - CleanCacheSize: 0, + TrieCleanSize: 0, + StateCleanSize: 0, WriteBufferSize: 0, }}) // disable caching } else { diff --git a/eth/handler.go b/eth/handler.go index 8283d7d02fe..f563b47138e 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/forkid" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -183,7 +184,7 @@ func newHandler(config *handlerConfig) (*handler, error) { } } // If snap sync is requested but snapshots are disabled, fail loudly - if h.snapSync.Load() && config.Chain.Snapshots() == nil { + if h.snapSync.Load() && (config.Chain.Snapshots() == nil && config.Chain.TrieDB().Scheme() == rawdb.HashScheme) { return nil, errors.New("snap sync not supported with snapshots disabled") } // Construct the downloader (long sync) diff --git a/eth/protocols/snap/handler.go b/eth/protocols/snap/handler.go index 924aff7ac9a..3249720f901 100644 --- a/eth/protocols/snap/handler.go +++ b/eth/protocols/snap/handler.go @@ -23,6 +23,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" @@ -31,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" + "github.com/ethereum/go-ethereum/triedb/database" ) const ( @@ -279,7 +282,16 @@ func ServiceGetAccountRangeQuery(chain *core.BlockChain, req *GetAccountRangePac if err != nil { return nil, nil } - it, err := chain.Snapshots().AccountIterator(req.Root, req.Origin) + // Temporary solution: using the snapshot interface for both cases. + // This can be removed once the hash scheme is deprecated. + var it snapshot.AccountIterator + if chain.TrieDB().Scheme() == rawdb.HashScheme { + // The snapshot is assumed to be available in hash mode if + // the SNAP protocol is enabled. + it, err = chain.Snapshots().AccountIterator(req.Root, req.Origin) + } else { + it, err = chain.TrieDB().AccountIterator(req.Root, req.Origin) + } if err != nil { return nil, nil } @@ -359,7 +371,19 @@ func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesP limit, req.Limit = common.BytesToHash(req.Limit), nil } // Retrieve the requested state and bail out if non existent - it, err := chain.Snapshots().StorageIterator(req.Root, account, origin) + var ( + err error + it snapshot.StorageIterator + ) + // Temporary solution: using the snapshot interface for both cases. + // This can be removed once the hash scheme is deprecated. + if chain.TrieDB().Scheme() == rawdb.HashScheme { + // The snapshot is assumed to be available in hash mode if + // the SNAP protocol is enabled. + it, err = chain.Snapshots().StorageIterator(req.Root, account, origin) + } else { + it, err = chain.TrieDB().StorageIterator(req.Root, account, origin) + } if err != nil { return nil, nil } @@ -479,8 +503,15 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket, s // We don't have the requested state available, bail out return nil, nil } - // The 'snap' might be nil, in which case we cannot serve storage slots. - snap := chain.Snapshots().Snapshot(req.Root) + // The 'reader' might be nil, in which case we cannot serve storage slots + // via snapshot. + var reader database.StateReader + if chain.Snapshots() != nil { + reader = chain.Snapshots().Snapshot(req.Root) + } + if reader == nil { + reader, _ = triedb.StateReader(req.Root) + } // Retrieve trie nodes until the packet size limit is reached var ( nodes [][]byte @@ -505,8 +536,9 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket, s default: var stRoot common.Hash + // Storage slots requested, open the storage trie and retrieve from there - if snap == nil { + if reader == nil { // We don't have the requested state snapshotted yet (or it is stale), // but can look up the account via the trie instead. account, err := accTrie.GetAccountByHash(common.BytesToHash(pathset[0])) @@ -516,7 +548,7 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket, s } stRoot = account.Root } else { - account, err := snap.Account(common.BytesToHash(pathset[0])) + account, err := reader.Account(common.BytesToHash(pathset[0])) loads++ // always account database reads, even for failures if err != nil || account == nil { break diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go index d318077d99a..d599e7ecc32 100644 --- a/eth/protocols/snap/sync_test.go +++ b/eth/protocols/snap/sync_test.go @@ -1962,5 +1962,5 @@ func newDbConfig(scheme string) *triedb.Config { if scheme == rawdb.HashScheme { return &triedb.Config{} } - return &triedb.Config{PathDB: pathdb.Defaults} + return &triedb.Config{PathDB: &pathdb.Config{SnapshotNoBuild: true}} } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 77bf945e40e..f029fa7f414 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -187,8 +187,10 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t } // Cross-check the snapshot-to-hash against the trie hash if snapshotter { - if err := chain.Snapshots().Verify(chain.CurrentBlock().Root); err != nil { - return err + if chain.Snapshots() != nil { + if err := chain.Snapshots().Verify(chain.CurrentBlock().Root); err != nil { + return err + } } } return t.validateImportedHeaders(chain, validBlocks) diff --git a/trie/database_test.go b/trie/database_test.go index 729d9f699be..535f0d61b20 100644 --- a/trie/database_test.go +++ b/trie/database_test.go @@ -25,7 +25,7 @@ import ( "github.com/ethereum/go-ethereum/triedb/database" ) -// testReader implements database.Reader interface, providing function to +// testReader implements database.NodeReader interface, providing function to // access trie nodes. type testReader struct { db ethdb.Database @@ -33,7 +33,7 @@ type testReader struct { nodes []*trienode.MergedNodeSet // sorted from new to old } -// Node implements database.Reader interface, retrieving trie node with +// Node implements database.NodeReader interface, retrieving trie node with // all available cached layers. func (r *testReader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) { // Check the node presence with the cached layer, from latest to oldest. @@ -54,7 +54,7 @@ func (r *testReader) Node(owner common.Hash, path []byte, hash common.Hash) ([]b return rawdb.ReadTrieNode(r.db, owner, path, hash, r.scheme), nil } -// testDb implements database.Database interface, using for testing purpose. +// testDb implements database.NodeDatabase interface, using for testing purpose. type testDb struct { disk ethdb.Database root common.Hash diff --git a/triedb/database.go b/triedb/database.go index f8ccc5ad339..18e24cd176d 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -312,6 +312,26 @@ func (db *Database) Journal(root common.Hash) error { return pdb.Journal(root) } +// AccountIterator creates a new account iterator for the specified root hash and +// seeks to a starting account hash. +func (db *Database) AccountIterator(root common.Hash, seek common.Hash) (pathdb.AccountIterator, error) { + pdb, ok := db.backend.(*pathdb.Database) + if !ok { + return nil, errors.New("not supported") + } + return pdb.AccountIterator(root, seek) +} + +// StorageIterator creates a new storage iterator for the specified root hash and +// account. The iterator will be move to the specific start position. +func (db *Database) StorageIterator(root common.Hash, account common.Hash, seek common.Hash) (pathdb.StorageIterator, error) { + pdb, ok := db.backend.(*pathdb.Database) + if !ok { + return nil, errors.New("not supported") + } + return pdb.StorageIterator(root, account, seek) +} + // IsVerkle returns the indicator if the database is holding a verkle tree. func (db *Database) IsVerkle() bool { return db.config.IsVerkle diff --git a/triedb/pathdb/buffer.go b/triedb/pathdb/buffer.go index c4e081b9737..e639a43835d 100644 --- a/triedb/pathdb/buffer.go +++ b/triedb/pathdb/buffer.go @@ -124,7 +124,7 @@ func (b *buffer) size() uint64 { // flush persists the in-memory dirty trie node into the disk if the configured // memory threshold is reached. Note, all data must be written atomically. -func (b *buffer) flush(db ethdb.KeyValueStore, freezer ethdb.AncientWriter, nodesCache *fastcache.Cache, id uint64) error { +func (b *buffer) flush(root common.Hash, db ethdb.KeyValueStore, freezer ethdb.AncientWriter, progress []byte, nodesCache, statesCache *fastcache.Cache, id uint64) error { // Ensure the target state id is aligned with the internal counter. head := rawdb.ReadPersistentStateID(db) if head+b.layers != id { @@ -133,7 +133,7 @@ func (b *buffer) flush(db ethdb.KeyValueStore, freezer ethdb.AncientWriter, node // Terminate the state snapshot generation if it's active var ( start = time.Now() - batch = db.NewBatchWithSize(b.nodes.dbsize() * 11 / 10) // extra 10% for potential pebble internal stuff + batch = db.NewBatchWithSize((b.nodes.dbsize() + b.states.dbsize()) * 11 / 10) // extra 10% for potential pebble internal stuff ) // Explicitly sync the state freezer to ensure all written data is persisted to disk // before updating the key-value store. @@ -146,7 +146,9 @@ func (b *buffer) flush(db ethdb.KeyValueStore, freezer ethdb.AncientWriter, node } } nodes := b.nodes.write(batch, nodesCache) + accounts, slots := b.states.write(batch, progress, statesCache) rawdb.WritePersistentStateID(batch, id) + rawdb.WriteSnapshotRoot(batch, root) // Flush all mutations in a single batch size := batch.ValueSize() @@ -155,8 +157,10 @@ func (b *buffer) flush(db ethdb.KeyValueStore, freezer ethdb.AncientWriter, node } commitBytesMeter.Mark(int64(size)) commitNodesMeter.Mark(int64(nodes)) + commitAccountsMeter.Mark(int64(accounts)) + commitStoragesMeter.Mark(int64(slots)) commitTimeTimer.UpdateSince(start) b.reset() - log.Debug("Persisted buffer content", "nodes", nodes, "bytes", common.StorageSize(size), "elapsed", common.PrettyDuration(time.Since(start))) + log.Debug("Persisted buffer content", "nodes", nodes, "accounts", accounts, "slots", slots, "bytes", common.StorageSize(size), "elapsed", common.PrettyDuration(time.Since(start))) return nil } diff --git a/triedb/pathdb/context.go b/triedb/pathdb/context.go new file mode 100644 index 00000000000..a5704de81aa --- /dev/null +++ b/triedb/pathdb/context.go @@ -0,0 +1,246 @@ +// Copyright 2024 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 pathdb + +import ( + "bytes" + "encoding/binary" + "errors" + "math" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethdb/memorydb" + "github.com/ethereum/go-ethereum/log" +) + +const ( + snapAccount = "account" // Identifier of account snapshot generation + snapStorage = "storage" // Identifier of storage snapshot generation +) + +// generatorStats is a collection of statistics gathered by the snapshot generator +// for logging purposes. This data structure is used throughout the entire +// lifecycle of the snapshot generation process and is shared across multiple +// generation cycles. +type generatorStats struct { + origin uint64 // Origin prefix where generation started + start time.Time // Timestamp when generation started + accounts uint64 // Number of accounts indexed(generated or recovered) + slots uint64 // Number of storage slots indexed(generated or recovered) + dangling uint64 // Number of dangling storage slots + storage common.StorageSize // Total account and storage slot size(generation or recovery) +} + +// log creates a contextual log with the given message and the context pulled +// from the internally maintained statistics. +func (gs *generatorStats) log(msg string, root common.Hash, marker []byte) { + var ctx []interface{} + if root != (common.Hash{}) { + ctx = append(ctx, []interface{}{"root", root}...) + } + // Figure out whether we're after or within an account + switch len(marker) { + case common.HashLength: + ctx = append(ctx, []interface{}{"at", common.BytesToHash(marker)}...) + case 2 * common.HashLength: + ctx = append(ctx, []interface{}{ + "in", common.BytesToHash(marker[:common.HashLength]), + "at", common.BytesToHash(marker[common.HashLength:]), + }...) + } + // Add the usual measurements + ctx = append(ctx, []interface{}{ + "accounts", gs.accounts, + "slots", gs.slots, + "storage", gs.storage, + "dangling", gs.dangling, + "elapsed", common.PrettyDuration(time.Since(gs.start)), + }...) + // Calculate the estimated indexing time based on current stats + if len(marker) > 0 { + if done := binary.BigEndian.Uint64(marker[:8]) - gs.origin; done > 0 { + left := math.MaxUint64 - binary.BigEndian.Uint64(marker[:8]) + + speed := done/uint64(time.Since(gs.start)/time.Millisecond+1) + 1 // +1s to avoid division by zero + ctx = append(ctx, []interface{}{ + "eta", common.PrettyDuration(time.Duration(left/speed) * time.Millisecond), + }...) + } + } + log.Info(msg, ctx...) +} + +// generatorContext holds several global fields that are used throughout the +// current generation cycle. It must be recreated if the generation cycle is +// restarted. +type generatorContext struct { + root common.Hash // State root of the generation target + account *holdableIterator // Iterator of account snapshot data + storage *holdableIterator // Iterator of storage snapshot data + db ethdb.KeyValueStore // Key-value store containing the snapshot data + batch ethdb.Batch // Database batch for writing data atomically + logged time.Time // The timestamp when last generation progress was displayed +} + +// newGeneratorContext initializes the context for generation. +func newGeneratorContext(root common.Hash, marker []byte, db ethdb.KeyValueStore) *generatorContext { + ctx := &generatorContext{ + root: root, + db: db, + batch: db.NewBatch(), + logged: time.Now(), + } + accMarker, storageMarker := splitMarker(marker) + ctx.openIterator(snapAccount, accMarker) + ctx.openIterator(snapStorage, storageMarker) + return ctx +} + +// openIterator constructs global account and storage snapshot iterators +// at the interrupted position. These iterators should be reopened from time +// to time to avoid blocking leveldb compaction for a long time. +func (ctx *generatorContext) openIterator(kind string, start []byte) { + if kind == snapAccount { + iter := ctx.db.NewIterator(rawdb.SnapshotAccountPrefix, start) + ctx.account = newHoldableIterator(rawdb.NewKeyLengthIterator(iter, 1+common.HashLength)) + return + } + iter := ctx.db.NewIterator(rawdb.SnapshotStoragePrefix, start) + ctx.storage = newHoldableIterator(rawdb.NewKeyLengthIterator(iter, 1+2*common.HashLength)) +} + +// reopenIterator releases the specified snapshot iterator and re-open it +// in the next position. It's aimed for not blocking leveldb compaction. +func (ctx *generatorContext) reopenIterator(kind string) { + // Shift iterator one more step, so that we can reopen + // the iterator at the right position. + var iter = ctx.account + if kind == snapStorage { + iter = ctx.storage + } + hasNext := iter.Next() + if !hasNext { + // Iterator exhausted, release forever and create an already exhausted virtual iterator + iter.Release() + if kind == snapAccount { + ctx.account = newHoldableIterator(memorydb.New().NewIterator(nil, nil)) + return + } + ctx.storage = newHoldableIterator(memorydb.New().NewIterator(nil, nil)) + return + } + next := iter.Key() + iter.Release() + ctx.openIterator(kind, next[1:]) +} + +// close releases all the held resources. +func (ctx *generatorContext) close() { + ctx.account.Release() + ctx.storage.Release() +} + +// iterator returns the corresponding iterator specified by the kind. +func (ctx *generatorContext) iterator(kind string) *holdableIterator { + if kind == snapAccount { + return ctx.account + } + return ctx.storage +} + +// removeStorageBefore deletes all storage entries which are located before +// the specified account. When the iterator touches the storage entry which +// is located in or outside the given account, it stops and holds the current +// iterated element locally. +func (ctx *generatorContext) removeStorageBefore(account common.Hash) uint64 { + var ( + count uint64 + start = time.Now() + iter = ctx.storage + ) + for iter.Next() { + key := iter.Key() + if bytes.Compare(key[1:1+common.HashLength], account.Bytes()) >= 0 { + iter.Hold() + break + } + count++ + ctx.batch.Delete(key) + if ctx.batch.ValueSize() > ethdb.IdealBatchSize { + ctx.batch.Write() + ctx.batch.Reset() + } + } + storageCleanCounter.Inc(time.Since(start).Nanoseconds()) + return count +} + +// removeStorageAt deletes all storage entries which are located in the specified +// account. When the iterator touches the storage entry which is outside the given +// account, it stops and holds the current iterated element locally. An error will +// be returned if the initial position of iterator is not in the given account. +func (ctx *generatorContext) removeStorageAt(account common.Hash) error { + var ( + count int64 + start = time.Now() + iter = ctx.storage + ) + for iter.Next() { + key := iter.Key() + cmp := bytes.Compare(key[1:1+common.HashLength], account.Bytes()) + if cmp < 0 { + return errors.New("invalid iterator position") + } + if cmp > 0 { + iter.Hold() + break + } + count++ + ctx.batch.Delete(key) + if ctx.batch.ValueSize() > ethdb.IdealBatchSize { + ctx.batch.Write() + ctx.batch.Reset() + } + } + wipedStorageMeter.Mark(count) + storageCleanCounter.Inc(time.Since(start).Nanoseconds()) + return nil +} + +// removeRemainingStorage deletes all storage entries which are located after +// the current iterator position. +func (ctx *generatorContext) removeRemainingStorage() uint64 { + var ( + count uint64 + start = time.Now() + iter = ctx.storage + ) + for iter.Next() { + count++ + ctx.batch.Delete(iter.Key()) + if ctx.batch.ValueSize() > ethdb.IdealBatchSize { + ctx.batch.Write() + ctx.batch.Reset() + } + } + danglingStorageMeter.Mark(int64(count)) + storageCleanCounter.Inc(time.Since(start).Nanoseconds()) + return count +} diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 155e28543db..c2064603159 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -17,6 +17,7 @@ package pathdb import ( + "encoding/binary" "errors" "fmt" "io" @@ -35,8 +36,11 @@ import ( ) const ( - // defaultCleanSize is the default memory allowance of clean cache. - defaultCleanSize = 16 * 1024 * 1024 + // defaultTrieCleanSize is the default memory allowance of clean trie cache. + defaultTrieCleanSize = 16 * 1024 * 1024 + + // defaultStateCleanSize is the default memory allowance of clean state cache. + defaultStateCleanSize = 16 * 1024 * 1024 // maxBufferSize is the maximum memory allowance of node buffer. // Too large buffer will cause the system to pause for a long @@ -111,9 +115,11 @@ type layer interface { // Config contains the settings for database. type Config struct { StateHistory uint64 // Number of recent blocks to maintain state history for - CleanCacheSize int // Maximum memory allowance (in bytes) for caching clean nodes + TrieCleanSize int // Maximum memory allowance (in bytes) for caching clean trie nodes + StateCleanSize int // Maximum memory allowance (in bytes) for caching clean state data WriteBufferSize int // Maximum memory allowance (in bytes) for write buffer - ReadOnly bool // Flag whether the database is opened in read only mode. + ReadOnly bool // Flag whether the database is opened in read only mode + SnapshotNoBuild bool // Flag Whether the background generation is allowed } // sanitize checks the provided user configurations and changes anything that's @@ -133,7 +139,11 @@ func (c *Config) fields() []interface{} { if c.ReadOnly { list = append(list, "readonly", true) } - list = append(list, "cache", common.StorageSize(c.CleanCacheSize)) + if c.SnapshotNoBuild { + list = append(list, "snapshot", false) + } + list = append(list, "triecache", common.StorageSize(c.TrieCleanSize)) + list = append(list, "statecache", common.StorageSize(c.StateCleanSize)) list = append(list, "buffer", common.StorageSize(c.WriteBufferSize)) list = append(list, "history", c.StateHistory) return list @@ -142,7 +152,8 @@ func (c *Config) fields() []interface{} { // Defaults contains default settings for Ethereum mainnet. var Defaults = &Config{ StateHistory: params.FullImmutabilityThreshold, - CleanCacheSize: defaultCleanSize, + TrieCleanSize: defaultTrieCleanSize, + StateCleanSize: defaultStateCleanSize, WriteBufferSize: defaultBufferSize, } @@ -240,6 +251,13 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database { log.Crit("Failed to disable database", "err", err) // impossible to happen } } + // Resolving the state snapshot generation progress from the database is + // mandatory. This ensures that uncovered flat states are not accessed, + // even if background generation is not allowed. If permitted, the generation + // might be scheduled. + if err := db.setStateGenerator(); err != nil { + log.Crit("Failed to setup the generator", "err", err) + } fields := config.fields() if db.isVerkle { fields = append(fields, "verkle", true) @@ -297,6 +315,60 @@ func (db *Database) repairHistory() error { return nil } +// setStateGenerator loads the state generation progress marker and potentially +// resume the state generation if it's permitted. +func (db *Database) setStateGenerator() error { + // Load the state snapshot generation progress marker to prevent access + // to uncovered states. + generator, root, err := loadGenerator(db.diskdb, db.hasher) + if err != nil { + return err + } + if generator == nil { + // Initialize an empty generator to rebuild the state snapshot from scratch + generator = &journalGenerator{ + Marker: []byte{}, + } + } + // Short circuit if the whole state snapshot has already been fully generated. + // The generator will be left as nil in disk layer for representing the whole + // state snapshot is available for accessing. + if generator.Done { + return nil + } + var origin uint64 + if len(generator.Marker) >= 8 { + origin = binary.BigEndian.Uint64(generator.Marker) + } + stats := &generatorStats{ + origin: origin, + start: time.Now(), + accounts: generator.Accounts, + slots: generator.Slots, + storage: common.StorageSize(generator.Storage), + } + dl := db.tree.bottom() + + // Disable the background snapshot building in these circumstances: + // - the database is opened in read only mode + // - the snapshot build is explicitly disabled + // - the database is opened in verkle tree mode + noBuild := db.readOnly || db.config.SnapshotNoBuild || db.isVerkle + + // Construct the generator and link it to the disk layer, ensuring that the + // generation progress is resolved to prevent accessing uncovered states + // regardless of whether background state snapshot generation is allowed. + dl.setGenerator(newGenerator(db.diskdb, noBuild, generator.Marker, stats)) + + // Short circuit if the background generation is not permitted + if noBuild || db.waitSync { + return nil + } + stats.log("Starting snapshot generation", root, generator.Marker) + dl.generator.run(root) + return nil +} + // Update adds a new layer into the tree, if that can be linked to an existing // old parent. It is disallowed to insert a disk layer (the origin of all). Apart // from that this function will flatten the extra diff layers at bottom into disk @@ -359,8 +431,13 @@ func (db *Database) Disable() error { } db.waitSync = true - // Mark the disk layer as stale to prevent access to persistent state. - db.tree.bottom().markStale() + // Terminate the state generator if it's active and mark the disk layer + // as stale to prevent access to persistent state. + disk := db.tree.bottom() + if disk.generator != nil { + disk.generator.stop() + } + disk.markStale() // Write the initial sync flag to persist it across restarts. rawdb.WriteSnapSyncStatusFlag(db.diskdb, rawdb.StateSyncRunning) @@ -390,6 +467,7 @@ func (db *Database) Enable(root common.Hash) error { // reset the persistent state id back to zero. batch := db.diskdb.NewBatch() rawdb.DeleteTrieJournal(batch) + rawdb.DeleteSnapshotRoot(batch) rawdb.WritePersistentStateID(batch, 0) if err := batch.Write(); err != nil { return err @@ -403,13 +481,13 @@ func (db *Database) Enable(root common.Hash) error { return err } } - // Re-construct a new disk layer backed by persistent state - // with **empty clean cache and node buffer**. - db.tree.reset(newDiskLayer(root, 0, db, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0))) - // Re-enable the database as the final step. db.waitSync = false rawdb.WriteSnapSyncStatusFlag(db.diskdb, rawdb.StateSyncFinished) + + // Re-construct a new disk layer backed by persistent state + // and schedule the state snapshot generation if it's permitted. + db.tree.reset(generateSnapshot(db, root, db.isVerkle || db.config.SnapshotNoBuild)) log.Info("Rebuilt trie database", "root", root) return nil } @@ -514,8 +592,12 @@ func (db *Database) Close() error { // following mutations. db.readOnly = true - // Release the memory held by clean cache. - db.tree.bottom().resetCache() + // Terminate the background generation if it's active + disk := db.tree.bottom() + if disk.generator != nil { + disk.generator.stop() + } + disk.resetCache() // release the memory held by clean cache // Close the attached state history freezer. if db.freezer == nil { @@ -580,14 +662,42 @@ func (db *Database) HistoryRange() (uint64, uint64, error) { return historyRange(db.freezer) } +// waitGeneration waits until the background generation is finished. It assumes +// that the generation is permitted; otherwise, it will block indefinitely. +func (db *Database) waitGeneration() { + gen := db.tree.bottom().generator + if gen == nil || gen.completed() { + return + } + <-gen.done +} + // AccountIterator creates a new account iterator for the specified root hash and // seeks to a starting account hash. func (db *Database) AccountIterator(root common.Hash, seek common.Hash) (AccountIterator, error) { + db.lock.RLock() + wait := db.waitSync + db.lock.RUnlock() + if wait { + return nil, errDatabaseWaitSync + } + if gen := db.tree.bottom().generator; gen != nil && !gen.completed() { + return nil, errNotConstructed + } return newFastAccountIterator(db, root, seek) } // StorageIterator creates a new storage iterator for the specified root hash and // account. The iterator will be moved to the specific start position. func (db *Database) StorageIterator(root common.Hash, account common.Hash, seek common.Hash) (StorageIterator, error) { + db.lock.RLock() + wait := db.waitSync + db.lock.RUnlock() + if wait { + return nil, errDatabaseWaitSync + } + if gen := db.tree.bottom().generator; gen != nil && !gen.completed() { + return nil, errNotConstructed + } return newFastStorageIterator(db, root, account, seek) } diff --git a/triedb/pathdb/database_test.go b/triedb/pathdb/database_test.go index c85d7832ee1..3b780c975da 100644 --- a/triedb/pathdb/database_test.go +++ b/triedb/pathdb/database_test.go @@ -126,7 +126,8 @@ func newTester(t *testing.T, historyLimit uint64, isVerkle bool, layers int) *te disk, _ = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false) db = New(disk, &Config{ StateHistory: historyLimit, - CleanCacheSize: 256 * 1024, + TrieCleanSize: 256 * 1024, + StateCleanSize: 256 * 1024, WriteBufferSize: 256 * 1024, }, isVerkle) diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go index b8869888d97..bee4cd1e8a4 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -17,9 +17,10 @@ package pathdb import ( - "errors" + "bytes" "fmt" "sync" + "time" "github.com/VictoriaMetrics/fastcache" "github.com/ethereum/go-ethereum/common" @@ -30,28 +31,42 @@ import ( // diskLayer is a low level persistent layer built on top of a key-value store. type diskLayer struct { - root common.Hash // Immutable, root hash to which this layer was made for - id uint64 // Immutable, corresponding state id - db *Database // Path-based trie database + root common.Hash // Immutable, root hash to which this layer was made for + id uint64 // Immutable, corresponding state id + db *Database // Path-based trie database + + // These two caches must be maintained separately, because the key + // for the root node of the storage trie (accountHash) is identical + // to the key for the account data. nodes *fastcache.Cache // GC friendly memory cache of clean nodes - buffer *buffer // Dirty buffer to aggregate writes of nodes and states - stale bool // Signals that the layer became stale (state progressed) - lock sync.RWMutex // Lock used to protect stale flag + states *fastcache.Cache // GC friendly memory cache of clean states + + buffer *buffer // Dirty buffer to aggregate writes of nodes and states + stale bool // Signals that the layer became stale (state progressed) + lock sync.RWMutex // Lock used to protect stale flag and genMarker + + // The generator is set if the state snapshot was not fully completed, + // regardless of whether the background generation is running or not. + generator *generator } // newDiskLayer creates a new disk layer based on the passing arguments. -func newDiskLayer(root common.Hash, id uint64, db *Database, nodes *fastcache.Cache, buffer *buffer) *diskLayer { - // Initialize a clean cache if the memory allowance is not zero - // or reuse the provided cache if it is not nil (inherited from +func newDiskLayer(root common.Hash, id uint64, db *Database, nodes *fastcache.Cache, states *fastcache.Cache, buffer *buffer) *diskLayer { + // Initialize the clean caches if the memory allowance is not zero + // or reuse the provided caches if they are not nil (inherited from // the original disk layer). - if nodes == nil && db.config.CleanCacheSize != 0 { - nodes = fastcache.New(db.config.CleanCacheSize) + if nodes == nil && db.config.TrieCleanSize != 0 { + nodes = fastcache.New(db.config.TrieCleanSize) + } + if states == nil && db.config.StateCleanSize != 0 { + states = fastcache.New(db.config.StateCleanSize) } return &diskLayer{ root: root, id: id, db: db, nodes: nodes, + states: states, buffer: buffer, } } @@ -72,6 +87,13 @@ func (dl *diskLayer) parentLayer() layer { return nil } +// setGenerator links the given generator to disk layer, representing the +// associated state snapshot is not fully completed yet and the generation +// is potentially running in the background. +func (dl *diskLayer) setGenerator(generator *generator) { + dl.generator = generator +} + // isStale return whether this layer has become stale (was flattened across) or if // it's still live. func (dl *diskLayer) isStale() bool { @@ -168,8 +190,41 @@ func (dl *diskLayer) account(hash common.Hash, depth int) ([]byte, error) { } dirtyStateMissMeter.Mark(1) - // TODO(rjl493456442) support persistent state retrieval - return nil, errors.New("not supported") + // If the layer is being generated, ensure the requested account has + // already been covered by the generator. + marker := dl.genMarker() + if marker != nil && bytes.Compare(hash.Bytes(), marker) > 0 { + return nil, errNotCoveredYet + } + // Try to retrieve the account from the memory cache + if dl.states != nil { + if blob, found := dl.states.HasGet(nil, hash[:]); found { + cleanStateHitMeter.Mark(1) + cleanStateReadMeter.Mark(int64(len(blob))) + + if len(blob) == 0 { + stateAccountInexMeter.Mark(1) + } else { + stateAccountExistMeter.Mark(1) + } + return blob, nil + } + cleanStateMissMeter.Mark(1) + } + // Try to retrieve the account from the disk. + blob = rawdb.ReadAccountSnapshot(dl.db.diskdb, hash) + if dl.states != nil { + dl.states.Set(hash[:], blob) + cleanStateWriteMeter.Mark(int64(len(blob))) + } + if len(blob) == 0 { + stateAccountInexMeter.Mark(1) + stateAccountInexDiskMeter.Mark(1) + } else { + stateAccountExistMeter.Mark(1) + stateAccountExistDiskMeter.Mark(1) + } + return blob, nil } // storage directly retrieves the storage data associated with a particular hash, @@ -203,8 +258,42 @@ func (dl *diskLayer) storage(accountHash, storageHash common.Hash, depth int) ([ } dirtyStateMissMeter.Mark(1) - // TODO(rjl493456442) support persistent state retrieval - return nil, errors.New("not supported") + // If the layer is being generated, ensure the requested storage slot + // has already been covered by the generator. + key := append(accountHash[:], storageHash[:]...) + marker := dl.genMarker() + if marker != nil && bytes.Compare(key, marker) > 0 { + return nil, errNotCoveredYet + } + // Try to retrieve the storage slot from the memory cache + if dl.states != nil { + if blob, found := dl.states.HasGet(nil, key); found { + cleanStateHitMeter.Mark(1) + cleanStateReadMeter.Mark(int64(len(blob))) + + if len(blob) == 0 { + stateStorageInexMeter.Mark(1) + } else { + stateStorageExistMeter.Mark(1) + } + return blob, nil + } + cleanStateMissMeter.Mark(1) + } + // Try to retrieve the account from the disk + blob := rawdb.ReadStorageSnapshot(dl.db.diskdb, accountHash, storageHash) + if dl.states != nil { + dl.states.Set(key, blob) + cleanStateWriteMeter.Mark(int64(len(blob))) + } + if len(blob) == 0 { + stateStorageInexMeter.Mark(1) + stateStorageInexDiskMeter.Mark(1) + } else { + stateStorageExistMeter.Mark(1) + stateStorageExistDiskMeter.Mark(1) + } + return blob, nil } // update implements the layer interface, returning a new diff layer on top @@ -267,13 +356,39 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) { // Merge the trie nodes and flat states of the bottom-most diff layer into the // buffer as the combined layer. combined := dl.buffer.commit(bottom.nodes, bottom.states.stateSet) + + // Terminate the background state snapshot generation before mutating the + // persistent state. if combined.full() || force { - if err := combined.flush(dl.db.diskdb, dl.db.freezer, dl.nodes, bottom.stateID()); err != nil { + // Terminate the background state snapshot generator before flushing + // to prevent data race. + var progress []byte + if dl.generator != nil { + dl.generator.stop() + progress = dl.generator.progressMarker() + + // If the snapshot has been fully generated, unset the generator + if progress == nil { + dl.setGenerator(nil) + } else { + log.Info("Paused snapshot generation") + } + } + // Flush the content in combined buffer. Any state data after the progress + // marker will be ignored, as the generator will pick it up later. + if err := combined.flush(bottom.root, dl.db.diskdb, dl.db.freezer, progress, dl.nodes, dl.states, bottom.stateID()); err != nil { return nil, err } + // Resume the background generation if it's not completed yet + if progress != nil { + dl.generator.run(bottom.root) + } + } + // Link the generator if snapshot is not yet completed + ndl := newDiskLayer(bottom.root, bottom.stateID(), dl.db, dl.nodes, dl.states, combined) + if dl.generator != nil { + ndl.setGenerator(dl.generator) } - ndl := newDiskLayer(bottom.root, bottom.stateID(), dl.db, dl.nodes, combined) - // To remove outdated history objects from the end, we set the 'tail' parameter // to 'oldest-1' due to the offset between the freezer index and the history ID. if overflow { @@ -288,6 +403,7 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) { // revert applies the given state history and return a reverted disk layer. func (dl *diskLayer) revert(h *history) (*diskLayer, error) { + start := time.Now() if h.meta.root != dl.rootHash() { return nil, errUnexpectedHistory } @@ -321,15 +437,40 @@ func (dl *diskLayer) revert(h *history) (*diskLayer, error) { if err != nil { return nil, err } - } else { - batch := dl.db.diskdb.NewBatch() - writeNodes(batch, nodes, dl.nodes) - rawdb.WritePersistentStateID(batch, dl.id-1) - if err := batch.Write(); err != nil { - log.Crit("Failed to write states", "err", err) + ndl := newDiskLayer(h.meta.parent, dl.id-1, dl.db, dl.nodes, dl.states, dl.buffer) + + // Link the generator if it exists + if dl.generator != nil { + ndl.setGenerator(dl.generator) } + log.Debug("Reverted data in write buffer", "oldroot", h.meta.root, "newroot", h.meta.parent, "elapsed", common.PrettyDuration(time.Since(start))) + return ndl, nil } - return newDiskLayer(h.meta.parent, dl.id-1, dl.db, dl.nodes, dl.buffer), nil + // Terminate the generation before writing any data into database + var progress []byte + if dl.generator != nil { + dl.generator.stop() + progress = dl.generator.progressMarker() + } + batch := dl.db.diskdb.NewBatch() + writeNodes(batch, nodes, dl.nodes) + + // Provide the original values of modified accounts and storages for revert + writeStates(batch, progress, accounts, storages, dl.states) + rawdb.WritePersistentStateID(batch, dl.id-1) + rawdb.WriteSnapshotRoot(batch, h.meta.parent) + if err := batch.Write(); err != nil { + log.Crit("Failed to write states", "err", err) + } + // Link the generator and resume generation if the snapshot is not yet + // fully completed. + ndl := newDiskLayer(h.meta.parent, dl.id-1, dl.db, dl.nodes, dl.states, dl.buffer) + if dl.generator != nil && !dl.generator.completed() { + ndl.generator = dl.generator + ndl.generator.run(h.meta.parent) + } + log.Debug("Reverted data in persistent state", "oldroot", h.meta.root, "newroot", h.meta.parent, "elapsed", common.PrettyDuration(time.Since(start))) + return ndl, nil } // size returns the approximate size of cached nodes in the disk layer. @@ -355,4 +496,16 @@ func (dl *diskLayer) resetCache() { if dl.nodes != nil { dl.nodes.Reset() } + if dl.states != nil { + dl.states.Reset() + } +} + +// genMarker returns the current state snapshot generation progress marker. If +// the state snapshot has already been fully generated, nil is returned. +func (dl *diskLayer) genMarker() []byte { + if dl.generator == nil { + return nil + } + return dl.generator.progressMarker() } diff --git a/triedb/pathdb/errors.go b/triedb/pathdb/errors.go index 49e9c3ca64b..5d953b2183d 100644 --- a/triedb/pathdb/errors.go +++ b/triedb/pathdb/errors.go @@ -39,4 +39,13 @@ var ( // errStateUnrecoverable is returned if state is required to be reverted to // a destination without associated state history available. errStateUnrecoverable = errors.New("state is unrecoverable") + + // errNotCoveredYet is returned from data accessors if the underlying snapshot + // is being generated currently and the requested data item is not yet in the + // range of accounts covered. + errNotCoveredYet = errors.New("not covered yet") + + // errNotConstructed is returned if the callers want to iterate the snapshot + // while the generation is not finished yet. + errNotConstructed = errors.New("snapshot is not constructed") ) diff --git a/triedb/pathdb/flush.go b/triedb/pathdb/flush.go index baa0bfb292b..6563dbccff6 100644 --- a/triedb/pathdb/flush.go +++ b/triedb/pathdb/flush.go @@ -17,6 +17,8 @@ package pathdb import ( + "bytes" + "github.com/VictoriaMetrics/fastcache" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" @@ -63,3 +65,69 @@ func writeNodes(batch ethdb.Batch, nodes map[common.Hash]map[string]*trienode.No } return total } + +// writeStates flushes state mutations into the provided database batch as a whole. +// +// This function assumes the background generator is already terminated and states +// before the supplied marker has been correctly generated. +// +// TODO(rjl493456442) do we really need this generation marker? The state updates +// after the marker can also be written and will be fixed by generator later if +// it's outdated. +func writeStates(batch ethdb.Batch, genMarker []byte, accountData map[common.Hash][]byte, storageData map[common.Hash]map[common.Hash][]byte, clean *fastcache.Cache) (int, int) { + var ( + accounts int + slots int + ) + for addrHash, blob := range accountData { + // Skip any account not yet covered by the snapshot. The account + // at the generation marker position (addrHash == genMarker[:common.HashLength]) + // should still be updated, as it would be skipped in the next + // generation cycle. + if genMarker != nil && bytes.Compare(addrHash[:], genMarker) > 0 { + continue + } + accounts += 1 + if len(blob) == 0 { + rawdb.DeleteAccountSnapshot(batch, addrHash) + if clean != nil { + clean.Set(addrHash[:], nil) + } + } else { + rawdb.WriteAccountSnapshot(batch, addrHash, blob) + if clean != nil { + clean.Set(addrHash[:], blob) + } + } + } + for addrHash, storages := range storageData { + // Skip any account not covered yet by the snapshot + if genMarker != nil && bytes.Compare(addrHash[:], genMarker) > 0 { + continue + } + midAccount := genMarker != nil && bytes.Equal(addrHash[:], genMarker[:common.HashLength]) + + for storageHash, blob := range storages { + // Skip any storage slot not yet covered by the snapshot. The storage slot + // at the generation marker position (addrHash == genMarker[:common.HashLength] + // and storageHash == genMarker[common.HashLength:]) should still be updated, + // as it would be skipped in the next generation cycle. + if midAccount && bytes.Compare(storageHash[:], genMarker[common.HashLength:]) > 0 { + continue + } + slots += 1 + if len(blob) == 0 { + rawdb.DeleteStorageSnapshot(batch, addrHash, storageHash) + if clean != nil { + clean.Set(append(addrHash[:], storageHash[:]...), nil) + } + } else { + rawdb.WriteStorageSnapshot(batch, addrHash, storageHash, blob) + if clean != nil { + clean.Set(append(addrHash[:], storageHash[:]...), blob) + } + } + } + } + return accounts, slots +} diff --git a/triedb/pathdb/generate.go b/triedb/pathdb/generate.go new file mode 100644 index 00000000000..f4f98c9d19f --- /dev/null +++ b/triedb/pathdb/generate.go @@ -0,0 +1,856 @@ +// Copyright 2019 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 pathdb + +import ( + "bytes" + "errors" + "fmt" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" + "github.com/ethereum/go-ethereum/triedb/database" +) + +var ( + // accountCheckRange is the upper limit of the number of accounts involved in + // each range check. This is a value estimated based on experience. If this + // range is too large, the failure rate of range proof will increase. Otherwise, + // if the range is too small, the efficiency of the state recovery will decrease. + accountCheckRange = 128 + + // storageCheckRange is the upper limit of the number of storage slots involved + // in each range check. This is a value estimated based on experience. If this + // range is too large, the failure rate of range proof will increase. Otherwise, + // if the range is too small, the efficiency of the state recovery will decrease. + storageCheckRange = 1024 + + // errMissingTrie is returned if the target trie is missing while the generation + // is running. In this case the generation is aborted and wait the new signal. + errMissingTrie = errors.New("missing trie") +) + +// diskReader is a wrapper of key-value store and implements database.NodeReader, +// providing a function for accessing persistent trie nodes in the disk +type diskReader struct{ db ethdb.KeyValueStore } + +// Node retrieves the trie node blob with the provided trie identifier, +// node path and the corresponding node hash. No error will be returned +// if the node is not found. +func (r *diskReader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) { + if owner == (common.Hash{}) { + return rawdb.ReadAccountTrieNode(r.db, path), nil + } + return rawdb.ReadStorageTrieNode(r.db, owner, path), nil +} + +// diskStore is a wrapper of key-value store and implements database.NodeDatabase. +// It's meant to be used for generating state snapshot from the trie data. +type diskStore struct { + db ethdb.KeyValueStore +} + +// NodeReader returns a node reader associated with the specific state. +// An error will be returned if the specified state is not available. +func (s *diskStore) NodeReader(stateRoot common.Hash) (database.NodeReader, error) { + root := types.EmptyRootHash + if blob := rawdb.ReadAccountTrieNode(s.db, nil); len(blob) > 0 { + root = crypto.Keccak256Hash(blob) + } + if root != stateRoot { + return nil, fmt.Errorf("state %x is not available", stateRoot) + } + return &diskReader{s.db}, nil +} + +// Generator is the struct for initial state snapshot generation. It is not thread-safe; +// the caller must manage concurrency issues themselves. +type generator struct { + noBuild bool // Flag indicating whether snapshot generation is permitted + running bool // Flag indicating whether the background generation is running + + db ethdb.KeyValueStore // Key-value store containing the snapshot data + stats *generatorStats // Generation statistics used throughout the entire life cycle + abort chan chan struct{} // Notification channel to abort generating the snapshot in this layer + done chan struct{} // Notification channel when generation is done + + progress []byte // Progress marker of the state generation, nil means it's completed + lock sync.RWMutex // Lock which protects the progress, only generator can mutate the progress +} + +// newGenerator constructs the state snapshot generator. +// +// noBuild will be true if the background snapshot generation is not allowed, +// usually used in read-only mode. +// +// progress indicates the starting position for resuming snapshot generation. +// It must be provided even if generation is not allowed; otherwise, uncovered +// states may be exposed for serving. +func newGenerator(db ethdb.KeyValueStore, noBuild bool, progress []byte, stats *generatorStats) *generator { + if stats == nil { + stats = &generatorStats{start: time.Now()} + } + return &generator{ + noBuild: noBuild, + progress: progress, + db: db, + stats: stats, + abort: make(chan chan struct{}), + done: make(chan struct{}), + } +} + +// run starts the state snapshot generation in the background. +func (g *generator) run(root common.Hash) { + if g.noBuild { + log.Warn("Snapshot generation is not permitted") + return + } + if g.running { + g.stop() + log.Warn("Paused the leftover generation cycle") + } + g.running = true + go g.generate(newGeneratorContext(root, g.progress, g.db)) +} + +// stop terminates the background generation if it's actively running. +// The Recent generation progress being made will be saved before returning. +func (g *generator) stop() { + if !g.running { + log.Debug("Snapshot generation is not running") + return + } + ch := make(chan struct{}) + g.abort <- ch + <-ch + g.running = false +} + +// completed returns the flag indicating if the whole generation is done. +func (g *generator) completed() bool { + progress := g.progressMarker() + return progress == nil +} + +// progressMarker returns the current generation progress marker. It may slightly +// lag behind the actual generation position, as the progress field is only updated +// when checkAndFlush is called. The only effect is that some generated states +// may be refused for serving. +func (g *generator) progressMarker() []byte { + g.lock.RLock() + defer g.lock.RUnlock() + + return g.progress +} + +// splitMarker is an internal helper which splits the generation progress marker +// into two parts. +func splitMarker(marker []byte) ([]byte, []byte) { + var accMarker []byte + if len(marker) > 0 { + accMarker = marker[:common.HashLength] + } + return accMarker, marker +} + +// generateSnapshot regenerates a brand-new snapshot based on an existing state +// database and head block asynchronously. The snapshot is returned immediately +// and generation is continued in the background until done. +func generateSnapshot(triedb *Database, root common.Hash, noBuild bool) *diskLayer { + // Create a new disk layer with an initialized state marker at zero + var ( + stats = &generatorStats{start: time.Now()} + genMarker = []byte{} // Initialized but empty! + ) + dl := newDiskLayer(root, 0, triedb, nil, nil, newBuffer(triedb.config.WriteBufferSize, nil, nil, 0)) + dl.setGenerator(newGenerator(triedb.diskdb, noBuild, genMarker, stats)) + + if !noBuild { + dl.generator.run(root) + log.Info("Started snapshot generation", "root", root) + } + return dl +} + +// journalProgress persists the generator stats into the database to resume later. +func journalProgress(db ethdb.KeyValueWriter, marker []byte, stats *generatorStats) { + // Write out the generator marker. Note it's a standalone disk layer generator + // which is not mixed with journal. It's ok if the generator is persisted while + // journal is not. + entry := journalGenerator{ + Done: marker == nil, + Marker: marker, + } + if stats != nil { + entry.Accounts = stats.accounts + entry.Slots = stats.slots + entry.Storage = uint64(stats.storage) + } + blob, err := rlp.EncodeToBytes(entry) + if err != nil { + panic(err) // Cannot happen, here to catch dev errors + } + var logstr string + switch { + case marker == nil: + logstr = "done" + case bytes.Equal(marker, []byte{}): + logstr = "empty" + case len(marker) == common.HashLength: + logstr = fmt.Sprintf("%#x", marker) + default: + logstr = fmt.Sprintf("%#x:%#x", marker[:common.HashLength], marker[common.HashLength:]) + } + log.Debug("Journalled generator progress", "progress", logstr) + rawdb.WriteSnapshotGenerator(db, blob) +} + +// proofResult contains the output of range proving which can be used +// for further processing regardless if it is successful or not. +type proofResult struct { + keys [][]byte // The key set of all elements being iterated, even proving is failed + vals [][]byte // The val set of all elements being iterated, even proving is failed + diskMore bool // Set when the database has extra snapshot states since last iteration + trieMore bool // Set when the trie has extra snapshot states(only meaningful for successful proving) + proofErr error // Indicator whether the given state range is valid or not + tr *trie.Trie // The trie, in case the trie was resolved by the prover (may be nil) +} + +// valid returns the indicator that range proof is successful or not. +func (result *proofResult) valid() bool { + return result.proofErr == nil +} + +// last returns the last verified element key regardless of whether the range proof is +// successful or not. Nil is returned if nothing involved in the proving. +func (result *proofResult) last() []byte { + var last []byte + if len(result.keys) > 0 { + last = result.keys[len(result.keys)-1] + } + return last +} + +// forEach iterates all the visited elements and applies the given callback on them. +// The iteration is aborted if the callback returns non-nil error. +func (result *proofResult) forEach(callback func(key []byte, val []byte) error) error { + for i := 0; i < len(result.keys); i++ { + key, val := result.keys[i], result.vals[i] + if err := callback(key, val); err != nil { + return err + } + } + return nil +} + +// proveRange proves the snapshot segment with particular prefix is "valid". +// The iteration start point will be assigned if the iterator is restored from +// the last interruption. Max will be assigned in order to limit the maximum +// amount of data involved in each iteration. +// +// The proof result will be returned if the range proving is finished, otherwise +// the error will be returned to abort the entire procedure. +func (g *generator) proveRange(ctx *generatorContext, trieId *trie.ID, prefix []byte, kind string, origin []byte, max int, valueConvertFn func([]byte) ([]byte, error)) (*proofResult, error) { + var ( + keys [][]byte + vals [][]byte + proof = rawdb.NewMemoryDatabase() + diskMore = false + iter = ctx.iterator(kind) + start = time.Now() + min = append(prefix, origin...) + ) + for iter.Next() { + // Ensure the iterated item is always equal or larger than the given origin. + key := iter.Key() + if bytes.Compare(key, min) < 0 { + return nil, errors.New("invalid iteration position") + } + // Ensure the iterated item still fall in the specified prefix. If + // not which means the items in the specified area are all visited. + // Move the iterator a step back since we iterate one extra element + // out. + if !bytes.Equal(key[:len(prefix)], prefix) { + iter.Hold() + break + } + // Break if we've reached the max size, and signal that we're not + // done yet. Move the iterator a step back since we iterate one + // extra element out. + if len(keys) == max { + iter.Hold() + diskMore = true + break + } + keys = append(keys, common.CopyBytes(key[len(prefix):])) + + if valueConvertFn == nil { + vals = append(vals, common.CopyBytes(iter.Value())) + } else { + val, err := valueConvertFn(iter.Value()) + if err != nil { + // Special case, the state data is corrupted (invalid slim-format account), + // don't abort the entire procedure directly. Instead, let the fallback + // generation to heal the invalid data. + // + // Here append the original value to ensure that the number of key and + // value are aligned. + vals = append(vals, common.CopyBytes(iter.Value())) + log.Error("Failed to convert account state data", "err", err) + } else { + vals = append(vals, val) + } + } + } + // Update metrics for database iteration and merkle proving + if kind == snapStorage { + storageSnapReadCounter.Inc(time.Since(start).Nanoseconds()) + } else { + accountSnapReadCounter.Inc(time.Since(start).Nanoseconds()) + } + defer func(start time.Time) { + if kind == snapStorage { + storageProveCounter.Inc(time.Since(start).Nanoseconds()) + } else { + accountProveCounter.Inc(time.Since(start).Nanoseconds()) + } + }(time.Now()) + + // The snap state is exhausted, pass the entire key/val set for verification + root := trieId.Root + if origin == nil && !diskMore { + stackTr := trie.NewStackTrie(nil) + for i, key := range keys { + if err := stackTr.Update(key, vals[i]); err != nil { + return nil, err + } + } + if gotRoot := stackTr.Hash(); gotRoot != root { + return &proofResult{ + keys: keys, + vals: vals, + proofErr: fmt.Errorf("wrong root: have %#x want %#x", gotRoot, root), + }, nil + } + return &proofResult{keys: keys, vals: vals}, nil + } + // Snap state is chunked, generate edge proofs for verification. + tr, err := trie.New(trieId, &diskStore{db: g.db}) + if err != nil { + log.Info("Trie missing, snapshotting paused", "state", ctx.root, "kind", kind, "root", trieId.Root) + return nil, errMissingTrie + } + // Generate the Merkle proofs for the first and last element + if origin == nil { + origin = common.Hash{}.Bytes() + } + if err := tr.Prove(origin, proof); err != nil { + log.Debug("Failed to prove range", "kind", kind, "origin", origin, "err", err) + return &proofResult{ + keys: keys, + vals: vals, + diskMore: diskMore, + proofErr: err, + tr: tr, + }, nil + } + if len(keys) > 0 { + if err := tr.Prove(keys[len(keys)-1], proof); err != nil { + log.Debug("Failed to prove range", "kind", kind, "last", keys[len(keys)-1], "err", err) + return &proofResult{ + keys: keys, + vals: vals, + diskMore: diskMore, + proofErr: err, + tr: tr, + }, nil + } + } + // Verify the snapshot segment with range prover, ensure that all flat states + // in this range correspond to merkle trie. + cont, err := trie.VerifyRangeProof(root, origin, keys, vals, proof) + return &proofResult{ + keys: keys, + vals: vals, + diskMore: diskMore, + trieMore: cont, + proofErr: err, + tr: tr}, + nil +} + +// onStateCallback is a function that is called by generateRange, when processing a range of +// accounts or storage slots. For each element, the callback is invoked. +// +// - If 'delete' is true, then this element (and potential slots) needs to be deleted from the snapshot. +// - If 'write' is true, then this element needs to be updated with the 'val'. +// - If 'write' is false, then this element is already correct, and needs no update. +// The 'val' is the canonical encoding of the value (not the slim format for accounts) +// +// However, for accounts, the storage trie of the account needs to be checked. Also, +// dangling storages(storage exists but the corresponding account is missing) need to +// be cleaned up. +type onStateCallback func(key []byte, val []byte, write bool, delete bool) error + +// generateRange generates the state segment with particular prefix. Generation can +// either verify the correctness of existing state through range-proof and skip +// generation, or iterate trie to regenerate state on demand. +func (g *generator) generateRange(ctx *generatorContext, trieId *trie.ID, prefix []byte, kind string, origin []byte, max int, onState onStateCallback, valueConvertFn func([]byte) ([]byte, error)) (bool, []byte, error) { + // Use range prover to check the validity of the flat state in the range + result, err := g.proveRange(ctx, trieId, prefix, kind, origin, max, valueConvertFn) + if err != nil { + return false, nil, err + } + last := result.last() + + // Construct contextual logger + logCtx := []interface{}{"kind", kind, "prefix", hexutil.Encode(prefix)} + if len(origin) > 0 { + logCtx = append(logCtx, "origin", hexutil.Encode(origin)) + } + logger := log.New(logCtx...) + + // The range prover says the range is correct, skip trie iteration + if result.valid() { + successfulRangeProofMeter.Mark(1) + logger.Trace("Proved state range", "last", hexutil.Encode(last)) + + // The verification is passed, process each state with the given + // callback function. If this state represents a contract, the + // corresponding storage check will be performed in the callback + if err := result.forEach(func(key []byte, val []byte) error { return onState(key, val, false, false) }); err != nil { + return false, nil, err + } + // Only abort the iteration when both database and trie are exhausted + return !result.diskMore && !result.trieMore, last, nil + } + logger.Trace("Detected outdated state range", "last", hexutil.Encode(last), "err", result.proofErr) + failedRangeProofMeter.Mark(1) + + // Special case, the entire trie is missing. In the original trie scheme, + // all the duplicated subtries will be filtered out (only one copy of data + // will be stored). While in the snapshot model, all the storage tries + // belong to different contracts will be kept even they are duplicated. + // Track it to a certain extent remove the noise data used for statistics. + if origin == nil && last == nil { + meter := missallAccountMeter + if kind == snapStorage { + meter = missallStorageMeter + } + meter.Mark(1) + } + // We use the snap data to build up a cache which can be used by the + // main account trie as a primary lookup when resolving hashes + var resolver trie.NodeResolver + if len(result.keys) > 0 { + tr := trie.NewEmpty(nil) + for i, key := range result.keys { + tr.Update(key, result.vals[i]) + } + _, nodes := tr.Commit(false) + hashSet := nodes.HashSet() + resolver = func(owner common.Hash, path []byte, hash common.Hash) []byte { + return hashSet[hash] + } + } + // Construct the trie for state iteration, reuse the trie + // if it's already opened with some nodes resolved. + tr := result.tr + if tr == nil { + tr, err = trie.New(trieId, &diskStore{db: g.db}) + if err != nil { + log.Info("Trie missing, snapshotting paused", "state", ctx.root, "kind", kind, "root", trieId.Root) + return false, nil, errMissingTrie + } + } + var ( + trieMore bool + kvkeys, kvvals = result.keys, result.vals + + // counters + count = 0 // number of states delivered by iterator + created = 0 // states created from the trie + updated = 0 // states updated from the trie + deleted = 0 // states not in trie, but were in snapshot + untouched = 0 // states already correct + + // timers + start = time.Now() + internal time.Duration + ) + nodeIt, err := tr.NodeIterator(origin) + if err != nil { + return false, nil, err + } + nodeIt.AddResolver(resolver) + iter := trie.NewIterator(nodeIt) + + for iter.Next() { + if last != nil && bytes.Compare(iter.Key, last) > 0 { + trieMore = true + break + } + count++ + write := true + created++ + for len(kvkeys) > 0 { + if cmp := bytes.Compare(kvkeys[0], iter.Key); cmp < 0 { + // delete the key + istart := time.Now() + if err := onState(kvkeys[0], nil, false, true); err != nil { + return false, nil, err + } + kvkeys = kvkeys[1:] + kvvals = kvvals[1:] + deleted++ + internal += time.Since(istart) + continue + } else if cmp == 0 { + // the snapshot key can be overwritten + created-- + if write = !bytes.Equal(kvvals[0], iter.Value); write { + updated++ + } else { + untouched++ + } + kvkeys = kvkeys[1:] + kvvals = kvvals[1:] + } + break + } + istart := time.Now() + if err := onState(iter.Key, iter.Value, write, false); err != nil { + return false, nil, err + } + internal += time.Since(istart) + } + if iter.Err != nil { + // Trie errors should never happen. Still, in case of a bug, expose the + // error here, as the outer code will presume errors are interrupts, not + // some deeper issues. + log.Error("State snapshotter failed to iterate trie", "err", iter.Err) + return false, nil, iter.Err + } + // Delete all stale snapshot states remaining + istart := time.Now() + for _, key := range kvkeys { + if err := onState(key, nil, false, true); err != nil { + return false, nil, err + } + deleted += 1 + } + internal += time.Since(istart) + + // Update metrics for counting trie iteration + if kind == snapStorage { + storageTrieReadCounter.Inc((time.Since(start) - internal).Nanoseconds()) + } else { + accountTrieReadCounter.Inc((time.Since(start) - internal).Nanoseconds()) + } + logger.Trace("Regenerated state range", "root", trieId.Root, "last", hexutil.Encode(last), + "count", count, "created", created, "updated", updated, "untouched", untouched, "deleted", deleted) + + // If there are either more trie items, or there are more snap items + // (in the next segment), then we need to keep working + return !trieMore && !result.diskMore, last, nil +} + +// checkAndFlush checks if an interruption signal is received or the +// batch size has exceeded the allowance. +func (g *generator) checkAndFlush(ctx *generatorContext, current []byte) error { + var abort chan struct{} + select { + case abort = <-g.abort: + default: + } + if ctx.batch.ValueSize() > ethdb.IdealBatchSize || abort != nil { + if bytes.Compare(current, g.progress) < 0 { + log.Error("Snapshot generator went backwards", "current", fmt.Sprintf("%x", current), "genMarker", fmt.Sprintf("%x", g.progress)) + } + // Persist the progress marker regardless of whether the batch is empty or not. + // It may happen that all the flat states in the database are correct, so the + // generator indeed makes progress even if there is nothing to commit. + journalProgress(ctx.batch, current, g.stats) + + // Flush out the database writes atomically + if err := ctx.batch.Write(); err != nil { + return err + } + ctx.batch.Reset() + + // Update the generation progress marker + g.lock.Lock() + g.progress = current + g.lock.Unlock() + + // Abort the generation if it's required + if abort != nil { + g.stats.log("Aborting snapshot generation", ctx.root, g.progress) + return newAbortErr(abort) // bubble up an error for interruption + } + // Don't hold the iterators too long, release them to let compactor works + ctx.reopenIterator(snapAccount) + ctx.reopenIterator(snapStorage) + } + if time.Since(ctx.logged) > 8*time.Second { + g.stats.log("Generating snapshot", ctx.root, g.progress) + ctx.logged = time.Now() + } + return nil +} + +// generateStorages generates the missing storage slots of the specific contract. +// It's supposed to restart the generation from the given origin position. +func (g *generator) generateStorages(ctx *generatorContext, account common.Hash, storageRoot common.Hash, storeMarker []byte) error { + onStorage := func(key []byte, val []byte, write bool, delete bool) error { + defer func(start time.Time) { + storageWriteCounter.Inc(time.Since(start).Nanoseconds()) + }(time.Now()) + + if delete { + rawdb.DeleteStorageSnapshot(ctx.batch, account, common.BytesToHash(key)) + wipedStorageMeter.Mark(1) + return nil + } + if write { + rawdb.WriteStorageSnapshot(ctx.batch, account, common.BytesToHash(key), val) + generatedStorageMeter.Mark(1) + } else { + recoveredStorageMeter.Mark(1) + } + g.stats.storage += common.StorageSize(1 + 2*common.HashLength + len(val)) + g.stats.slots++ + + // If we've exceeded our batch allowance or termination was requested, flush to disk + if err := g.checkAndFlush(ctx, append(account[:], key...)); err != nil { + return err + } + return nil + } + // Loop for re-generating the missing storage slots. + var origin = common.CopyBytes(storeMarker) + for { + id := trie.StorageTrieID(ctx.root, account, storageRoot) + exhausted, last, err := g.generateRange(ctx, id, append(rawdb.SnapshotStoragePrefix, account.Bytes()...), snapStorage, origin, storageCheckRange, onStorage, nil) + if err != nil { + return err // The procedure it aborted, either by external signal or internal error. + } + // Abort the procedure if the entire contract storage is generated + if exhausted { + break + } + if origin = increaseKey(last); origin == nil { + break // special case, the last is 0xffffffff...fff + } + } + return nil +} + +// generateAccounts generates the missing snapshot accounts as well as their +// storage slots in the main trie. It's supposed to restart the generation +// from the given origin position. +func (g *generator) generateAccounts(ctx *generatorContext, accMarker []byte) error { + onAccount := func(key []byte, val []byte, write bool, delete bool) error { + // Make sure to clear all dangling storages before this account + account := common.BytesToHash(key) + g.stats.dangling += ctx.removeStorageBefore(account) + + start := time.Now() + if delete { + rawdb.DeleteAccountSnapshot(ctx.batch, account) + wipedAccountMeter.Mark(1) + accountWriteCounter.Inc(time.Since(start).Nanoseconds()) + + ctx.removeStorageAt(account) + return nil + } + // Retrieve the current account and flatten it into the internal format + var acc types.StateAccount + if err := rlp.DecodeBytes(val, &acc); err != nil { + log.Crit("Invalid account encountered during snapshot creation", "err", err) + } + // If the account is not yet in-progress, write it out + if accMarker == nil || !bytes.Equal(account[:], accMarker) { + dataLen := len(val) // Approximate size, saves us a round of RLP-encoding + if !write { + if bytes.Equal(acc.CodeHash, types.EmptyCodeHash[:]) { + dataLen -= 32 + } + if acc.Root == types.EmptyRootHash { + dataLen -= 32 + } + recoveredAccountMeter.Mark(1) + } else { + data := types.SlimAccountRLP(acc) + dataLen = len(data) + rawdb.WriteAccountSnapshot(ctx.batch, account, data) + generatedAccountMeter.Mark(1) + } + g.stats.storage += common.StorageSize(1 + common.HashLength + dataLen) + g.stats.accounts++ + } + // If the snap generation goes here after interrupted, genMarker may go backward + // when last genMarker is consisted of accountHash and storageHash + marker := account[:] + if accMarker != nil && bytes.Equal(marker, accMarker) && len(g.progress) > common.HashLength { + marker = g.progress + } + // If we've exceeded our batch allowance or termination was requested, flush to disk + if err := g.checkAndFlush(ctx, marker); err != nil { + return err + } + accountWriteCounter.Inc(time.Since(start).Nanoseconds()) // let's count flush time as well + + // If the iterated account is the contract, create a further loop to + // verify or regenerate the contract storage. + if acc.Root == types.EmptyRootHash { + ctx.removeStorageAt(account) + } else { + var storeMarker []byte + if accMarker != nil && bytes.Equal(account[:], accMarker) && len(g.progress) > common.HashLength { + storeMarker = g.progress[common.HashLength:] + } + if err := g.generateStorages(ctx, account, acc.Root, storeMarker); err != nil { + return err + } + } + // Some account processed, unmark the marker + accMarker = nil + return nil + } + origin := common.CopyBytes(accMarker) + for { + id := trie.StateTrieID(ctx.root) + exhausted, last, err := g.generateRange(ctx, id, rawdb.SnapshotAccountPrefix, snapAccount, origin, accountCheckRange, onAccount, types.FullAccountRLP) + if err != nil { + return err // The procedure it aborted, either by external signal or internal error. + } + origin = increaseKey(last) + + // Last step, cleanup the storages after the last account. + // All the left storages should be treated as dangling. + if origin == nil || exhausted { + g.stats.dangling += ctx.removeRemainingStorage() + break + } + } + return nil +} + +// generate is a background thread that iterates over the state and storage tries, +// constructing the state snapshot. All the arguments are purely for statistics +// gathering and logging, since the method surfs the blocks as they arrive, often +// being restarted. +func (g *generator) generate(ctx *generatorContext) { + g.stats.log("Resuming snapshot generation", ctx.root, g.progress) + defer ctx.close() + + // Persist the initial marker and state snapshot root if progress is none + if len(g.progress) == 0 { + batch := g.db.NewBatch() + rawdb.WriteSnapshotRoot(batch, ctx.root) + journalProgress(batch, g.progress, g.stats) + if err := batch.Write(); err != nil { + log.Crit("Failed to write initialized state marker", "err", err) + } + } + // Initialize the global generator context. The snapshot iterators are + // opened at the interrupted position because the assumption is held + // that all the snapshot data are generated correctly before the marker. + // Even if the snapshot data is updated during the interruption (before + // or at the marker), the assumption is still held. + // For the account or storage slot at the interruption, they will be + // processed twice by the generator(they are already processed in the + // last run) but it's fine. + var ( + accMarker, _ = splitMarker(g.progress) + abort chan struct{} + ) + if err := g.generateAccounts(ctx, accMarker); err != nil { + // Extract the received interruption signal if exists + var aerr *abortErr + if errors.As(err, &aerr) { + abort = aerr.abort + } + // Aborted by internal error, wait the signal + if abort == nil { + abort = <-g.abort + } + close(abort) + return + } + // Snapshot fully generated, set the marker to nil. + // Note even there is nothing to commit, persist the + // generator anyway to mark the snapshot is complete. + journalProgress(ctx.batch, nil, g.stats) + if err := ctx.batch.Write(); err != nil { + log.Error("Failed to flush batch", "err", err) + abort = <-g.abort + close(abort) + return + } + ctx.batch.Reset() + + log.Info("Generated snapshot", "accounts", g.stats.accounts, "slots", g.stats.slots, + "storage", g.stats.storage, "dangling", g.stats.dangling, "elapsed", common.PrettyDuration(time.Since(g.stats.start))) + + // Update the generation progress marker + g.lock.Lock() + g.progress = nil + g.lock.Unlock() + close(g.done) + + // Someone will be looking for us, wait it out + abort = <-g.abort + close(abort) +} + +// increaseKey increase the input key by one bit. Return nil if the entire +// addition operation overflows. +func increaseKey(key []byte) []byte { + for i := len(key) - 1; i >= 0; i-- { + key[i]++ + if key[i] != 0x0 { + return key + } + } + return nil +} + +// abortErr wraps an interruption signal received to represent the +// generation is aborted by external processes. +type abortErr struct { + abort chan struct{} +} + +func newAbortErr(abort chan struct{}) error { + return &abortErr{abort: abort} +} + +func (err *abortErr) Error() string { + return "aborted" +} diff --git a/triedb/pathdb/generate_test.go b/triedb/pathdb/generate_test.go new file mode 100644 index 00000000000..23efb0e3c55 --- /dev/null +++ b/triedb/pathdb/generate_test.go @@ -0,0 +1,766 @@ +// Copyright 2024 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 pathdb + +import ( + "fmt" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/internal/testrand" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" + "github.com/ethereum/go-ethereum/trie/trienode" + "github.com/holiman/uint256" +) + +func hashData(input []byte) common.Hash { + return crypto.Keccak256Hash(input) +} + +type genTester struct { + diskdb ethdb.Database + db *Database + acctTrie *trie.Trie + nodes *trienode.MergedNodeSet + states *StateSetWithOrigin +} + +func newGenTester() *genTester { + disk := rawdb.NewMemoryDatabase() + config := *Defaults + config.SnapshotNoBuild = true // no background generation + db := New(disk, &config, false) + tr, _ := trie.New(trie.StateTrieID(types.EmptyRootHash), db) + return &genTester{ + diskdb: disk, + db: db, + acctTrie: tr, + nodes: trienode.NewMergedNodeSet(), + states: NewStateSetWithOrigin(nil, nil, nil, nil, false), + } +} + +func (t *genTester) addTrieAccount(acckey string, acc *types.StateAccount) { + var ( + addr = common.BytesToAddress([]byte(acckey)) + key = hashData([]byte(acckey)) + val, _ = rlp.EncodeToBytes(acc) + ) + t.acctTrie.MustUpdate(key.Bytes(), val) + + t.states.accountData[key] = val + t.states.accountOrigin[addr] = nil +} + +func (t *genTester) addSnapAccount(acckey string, acc *types.StateAccount) { + key := hashData([]byte(acckey)) + rawdb.WriteAccountSnapshot(t.diskdb, key, types.SlimAccountRLP(*acc)) +} + +func (t *genTester) addAccount(acckey string, acc *types.StateAccount) { + t.addTrieAccount(acckey, acc) + t.addSnapAccount(acckey, acc) +} + +func (t *genTester) addSnapStorage(accKey string, keys []string, vals []string) { + accHash := hashData([]byte(accKey)) + for i, key := range keys { + rawdb.WriteStorageSnapshot(t.diskdb, accHash, hashData([]byte(key)), []byte(vals[i])) + } +} + +func (t *genTester) makeStorageTrie(accKey string, keys []string, vals []string, commit bool) common.Hash { + var ( + owner = hashData([]byte(accKey)) + addr = common.BytesToAddress([]byte(accKey)) + id = trie.StorageTrieID(types.EmptyRootHash, owner, types.EmptyRootHash) + tr, _ = trie.New(id, t.db) + + storages = make(map[common.Hash][]byte) + storageOrigins = make(map[common.Hash][]byte) + ) + for i, k := range keys { + key := hashData([]byte(k)) + tr.MustUpdate(key.Bytes(), []byte(vals[i])) + storages[key] = []byte(vals[i]) + storageOrigins[key] = nil + } + if !commit { + return tr.Hash() + } + root, nodes := tr.Commit(false) + if nodes != nil { + t.nodes.Merge(nodes) + } + t.states.storageData[owner] = storages + t.states.storageOrigin[addr] = storageOrigins + return root +} + +func (t *genTester) Commit() common.Hash { + root, nodes := t.acctTrie.Commit(true) + if nodes != nil { + t.nodes.Merge(nodes) + } + t.db.Update(root, types.EmptyRootHash, 0, t.nodes, t.states) + t.db.Commit(root, false) + return root +} + +func (t *genTester) CommitAndGenerate() (common.Hash, *diskLayer) { + root := t.Commit() + dl := generateSnapshot(t.db, root, false) + return root, dl +} + +// Tests that snapshot generation from an empty database. +func TestGeneration(t *testing.T) { + helper := newGenTester() + stRoot := helper.makeStorageTrie("", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, false) + + helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + + helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + + root, dl := helper.CommitAndGenerate() + if have, want := root, common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"); have != want { + t.Fatalf("have %#x want %#x", have, want) + } + select { + case <-dl.generator.done: + // Snapshot generation succeeded + case <-time.After(3 * time.Second): + t.Errorf("Snapshot generation failed") + } + // TODO(rjl493456442) enable the snapshot tests + // checkSnapRoot(t, snap, root) + + // Signal abortion to the generator and wait for it to tear down + dl.generator.stop() +} + +// Tests that snapshot generation with existent flat state, where the flat state +// contains some errors: +// - the contract with empty storage root but has storage entries in the disk +// - the contract with non empty storage root but empty storage slots +// - the contract(non-empty storage) misses some storage slots +// - miss in the beginning +// - miss in the middle +// - miss in the end +// +// - the contract(non-empty storage) has wrong storage slots +// - wrong slots in the beginning +// - wrong slots in the middle +// - wrong slots in the end +// +// - the contract(non-empty storage) has extra storage slots +// - extra slots in the beginning +// - extra slots in the middle +// - extra slots in the end +func TestGenerateExistentStateWithWrongStorage(t *testing.T) { + helper := newGenTester() + + // Account one, empty storage trie root but non-empty flat states + helper.addAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) + + // Account two, non-empty storage trie root but empty flat states + stRoot := helper.makeStorageTrie("acc-2", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + + // Miss slots + { + // Account three, non-empty root but misses slots in the beginning + helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapStorage("acc-3", []string{"key-2", "key-3"}, []string{"val-2", "val-3"}) + + // Account four, non-empty root but misses slots in the middle + helper.makeStorageTrie("acc-4", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addAccount("acc-4", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapStorage("acc-4", []string{"key-1", "key-3"}, []string{"val-1", "val-3"}) + + // Account five, non-empty root but misses slots in the end + helper.makeStorageTrie("acc-5", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addAccount("acc-5", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapStorage("acc-5", []string{"key-1", "key-2"}, []string{"val-1", "val-2"}) + } + + // Wrong storage slots + { + // Account six, non-empty root but wrong slots in the beginning + helper.makeStorageTrie("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addAccount("acc-6", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapStorage("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"badval-1", "val-2", "val-3"}) + + // Account seven, non-empty root but wrong slots in the middle + helper.makeStorageTrie("acc-7", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addAccount("acc-7", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapStorage("acc-7", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "badval-2", "val-3"}) + + // Account eight, non-empty root but wrong slots in the end + helper.makeStorageTrie("acc-8", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addAccount("acc-8", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapStorage("acc-8", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "badval-3"}) + + // Account 9, non-empty root but rotated slots + helper.makeStorageTrie("acc-9", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addAccount("acc-9", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapStorage("acc-9", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-3", "val-2"}) + } + + // Extra storage slots + { + // Account 10, non-empty root but extra slots in the beginning + helper.makeStorageTrie("acc-10", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addAccount("acc-10", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapStorage("acc-10", []string{"key-0", "key-1", "key-2", "key-3"}, []string{"val-0", "val-1", "val-2", "val-3"}) + + // Account 11, non-empty root but extra slots in the middle + helper.makeStorageTrie("acc-11", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addAccount("acc-11", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapStorage("acc-11", []string{"key-1", "key-2", "key-2-1", "key-3"}, []string{"val-1", "val-2", "val-2-1", "val-3"}) + + // Account 12, non-empty root but extra slots in the end + helper.makeStorageTrie("acc-12", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addAccount("acc-12", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapStorage("acc-12", []string{"key-1", "key-2", "key-3", "key-4"}, []string{"val-1", "val-2", "val-3", "val-4"}) + } + + root, dl := helper.CommitAndGenerate() + t.Logf("Root: %#x\n", root) // Root = 0x8746cce9fd9c658b2cfd639878ed6584b7a2b3e73bb40f607fcfa156002429a0 + + select { + case <-dl.generator.done: + // Snapshot generation succeeded + + case <-time.After(3 * time.Second): + t.Errorf("Snapshot generation failed") + } + // TODO(rjl493456442) enable the snapshot tests + // checkSnapRoot(t, snap, root) + + // Signal abortion to the generator and wait for it to tear down + dl.generator.stop() +} + +// Tests that snapshot generation with existent flat state, where the flat state +// contains some errors: +// - miss accounts +// - wrong accounts +// - extra accounts +func TestGenerateExistentStateWithWrongAccounts(t *testing.T) { + helper := newGenTester() + + // Trie accounts [acc-1, acc-2, acc-3, acc-4, acc-6] + helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-2", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-4", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + stRoot := helper.makeStorageTrie("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + + // Missing accounts, only in the trie + { + helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Beginning + helper.addTrieAccount("acc-4", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Middle + helper.addTrieAccount("acc-6", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // End + } + + // Wrong accounts + { + helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: common.Hex2Bytes("0x1234")}) + + helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addSnapAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) + } + + // Extra accounts, only in the snap + { + helper.addSnapAccount("acc-0", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // before the beginning + helper.addSnapAccount("acc-5", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: common.Hex2Bytes("0x1234")}) // Middle + helper.addSnapAccount("acc-7", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // after the end + } + + root, dl := helper.CommitAndGenerate() + t.Logf("Root: %#x\n", root) // Root = 0x825891472281463511e7ebcc7f109e4f9200c20fa384754e11fd605cd98464e8 + + select { + case <-dl.generator.done: + // Snapshot generation succeeded + + case <-time.After(3 * time.Second): + t.Errorf("Snapshot generation failed") + } + // TODO(rjl493456442) enable the snapshot tests + // checkSnapRoot(t, snap, root) + + // Signal abortion to the generator and wait for it to tear down + dl.generator.stop() +} + +func TestGenerateCorruptAccountTrie(t *testing.T) { + helper := newGenTester() + helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074 + helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 + helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4 + + root := helper.Commit() // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978 + + // Delete an account trie node and ensure the generator chokes + path := []byte{0xc} + if !rawdb.HasAccountTrieNode(helper.diskdb, path) { + t.Logf("Invalid node path to delete, %v", path) + } + rawdb.DeleteAccountTrieNode(helper.diskdb, path) + helper.db.tree.bottom().resetCache() + + dl := generateSnapshot(helper.db, root, false) + select { + case <-dl.generator.done: + // Snapshot generation succeeded + t.Errorf("Snapshot generated against corrupt account trie") + + case <-time.After(time.Second): + // Not generated fast enough, hopefully blocked inside on missing trie node fail + } + // Signal abortion to the generator and wait for it to tear down + dl.generator.stop() +} + +func TestGenerateMissingStorageTrie(t *testing.T) { + var ( + acc1 = hashData([]byte("acc-1")) + acc3 = hashData([]byte("acc-3")) + helper = newGenTester() + ) + stRoot := helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 + helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e + helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 + stRoot = helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 + + root := helper.Commit() + + // Delete storage trie root of account one and three. + rawdb.DeleteStorageTrieNode(helper.diskdb, acc1, nil) + rawdb.DeleteStorageTrieNode(helper.diskdb, acc3, nil) + helper.db.tree.bottom().resetCache() + + dl := generateSnapshot(helper.db, root, false) + select { + case <-dl.generator.done: + // Snapshot generation succeeded + t.Errorf("Snapshot generated against corrupt storage trie") + + case <-time.After(time.Second): + // Not generated fast enough, hopefully blocked inside on missing trie node fail + } + // Signal abortion to the generator and wait for it to tear down + dl.generator.stop() +} + +func TestGenerateCorruptStorageTrie(t *testing.T) { + helper := newGenTester() + + stRoot := helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 + helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e + helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 + stRoot = helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 + + root := helper.Commit() + + // Delete a node in the storage trie. + path := []byte{0x4} + if !rawdb.HasStorageTrieNode(helper.diskdb, hashData([]byte("acc-1")), path) { + t.Logf("Invalid node path to delete, %v", path) + } + rawdb.DeleteStorageTrieNode(helper.diskdb, hashData([]byte("acc-1")), []byte{0x4}) + + if !rawdb.HasStorageTrieNode(helper.diskdb, hashData([]byte("acc-3")), path) { + t.Logf("Invalid node path to delete, %v", path) + } + rawdb.DeleteStorageTrieNode(helper.diskdb, hashData([]byte("acc-3")), []byte{0x4}) + + helper.db.tree.bottom().resetCache() + + dl := generateSnapshot(helper.db, root, false) + select { + case <-dl.generator.done: + // Snapshot generation succeeded + t.Errorf("Snapshot generated against corrupt storage trie") + + case <-time.After(time.Second): + // Not generated fast enough, hopefully blocked inside on missing trie node fail + } + // Signal abortion to the generator and wait for it to tear down + dl.generator.stop() +} + +func TestGenerateWithExtraAccounts(t *testing.T) { + helper := newGenTester() + + // Account one in the trie + stRoot := helper.makeStorageTrie("acc-1", + []string{"key-1", "key-2", "key-3", "key-4", "key-5"}, + []string{"val-1", "val-2", "val-3", "val-4", "val-5"}, + true, + ) + acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()} + val, _ := rlp.EncodeToBytes(acc) + helper.acctTrie.MustUpdate(hashData([]byte("acc-1")).Bytes(), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e + + // Identical in the snap + key := hashData([]byte("acc-1")) + rawdb.WriteAccountSnapshot(helper.diskdb, key, val) + rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-1")), []byte("val-1")) + rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-2")), []byte("val-2")) + rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-3")), []byte("val-3")) + rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-4")), []byte("val-4")) + rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-5")), []byte("val-5")) + + // Account two exists only in the snapshot + stRoot = helper.makeStorageTrie("acc-2", + []string{"key-1", "key-2", "key-3", "key-4", "key-5"}, + []string{"val-1", "val-2", "val-3", "val-4", "val-5"}, + true, + ) + acc = &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()} + val, _ = rlp.EncodeToBytes(acc) + key = hashData([]byte("acc-2")) + rawdb.WriteAccountSnapshot(helper.diskdb, key, val) + rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("b-key-1")), []byte("b-val-1")) + rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("b-key-2")), []byte("b-val-2")) + rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("b-key-3")), []byte("b-val-3")) + + root := helper.Commit() + + // To verify the test: If we now inspect the snap db, there should exist extraneous storage items + if data := rawdb.ReadStorageSnapshot(helper.diskdb, hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data == nil { + t.Fatalf("expected snap storage to exist") + } + dl := generateSnapshot(helper.db, root, false) + select { + case <-dl.generator.done: + // Snapshot generation succeeded + + case <-time.After(3 * time.Second): + t.Errorf("Snapshot generation failed") + } + // TODO(rjl493456442) enable the snapshot tests + // checkSnapRoot(t, snap, root) + + // Signal abortion to the generator and wait for it to tear down + dl.generator.stop() + + // If we now inspect the snap db, there should exist no extraneous storage items + if data := rawdb.ReadStorageSnapshot(helper.diskdb, hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data != nil { + t.Fatalf("expected slot to be removed, got %v", string(data)) + } +} + +func TestGenerateWithManyExtraAccounts(t *testing.T) { + helper := newGenTester() + + // Account one in the trie + stRoot := helper.makeStorageTrie("acc-1", + []string{"key-1", "key-2", "key-3"}, + []string{"val-1", "val-2", "val-3"}, + true, + ) + acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()} + val, _ := rlp.EncodeToBytes(acc) + helper.acctTrie.MustUpdate(hashData([]byte("acc-1")).Bytes(), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e + + // Identical in the snap + key := hashData([]byte("acc-1")) + rawdb.WriteAccountSnapshot(helper.diskdb, key, val) + rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-1")), []byte("val-1")) + rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-2")), []byte("val-2")) + rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-3")), []byte("val-3")) + + // 100 accounts exist only in snapshot + for i := 0; i < 1000; i++ { + acc := &types.StateAccount{Balance: uint256.NewInt(uint64(i)), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()} + val, _ := rlp.EncodeToBytes(acc) + key := hashData([]byte(fmt.Sprintf("acc-%d", i))) + rawdb.WriteAccountSnapshot(helper.diskdb, key, val) + } + + _, dl := helper.CommitAndGenerate() + select { + case <-dl.generator.done: + // Snapshot generation succeeded + + case <-time.After(3 * time.Second): + t.Errorf("Snapshot generation failed") + } + // TODO(rjl493456442) enable the snapshot tests + // checkSnapRoot(t, snap, root) + + // Signal abortion to the generator and wait for it to tear down + dl.generator.stop() +} + +func TestGenerateWithExtraBeforeAndAfter(t *testing.T) { + helper := newGenTester() + + acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()} + val, _ := rlp.EncodeToBytes(acc) + + acctHashA := hashData([]byte("acc-1")) + acctHashB := hashData([]byte("acc-2")) + + helper.acctTrie.MustUpdate(acctHashA.Bytes(), val) + helper.acctTrie.MustUpdate(acctHashB.Bytes(), val) + + rawdb.WriteAccountSnapshot(helper.diskdb, acctHashA, val) + rawdb.WriteAccountSnapshot(helper.diskdb, acctHashB, val) + + for i := 0; i < 16; i++ { + rawdb.WriteAccountSnapshot(helper.diskdb, common.Hash{byte(i)}, val) + } + _, dl := helper.CommitAndGenerate() + select { + case <-dl.generator.done: + // Snapshot generation succeeded + + case <-time.After(3 * time.Second): + t.Errorf("Snapshot generation failed") + } + // TODO(rjl493456442) enable the snapshot tests + // checkSnapRoot(t, snap, root) + + // Signal abortion to the generator and wait for it to tear down + dl.generator.stop() +} + +func TestGenerateWithMalformedStateData(t *testing.T) { + helper := newGenTester() + + acctHash := hashData([]byte("acc")) + acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()} + val, _ := rlp.EncodeToBytes(acc) + helper.acctTrie.MustUpdate(acctHash.Bytes(), val) + + junk := make([]byte, 100) + copy(junk, []byte{0xde, 0xad}) + rawdb.WriteAccountSnapshot(helper.diskdb, acctHash, junk) + for i := 0; i < 16; i++ { + rawdb.WriteAccountSnapshot(helper.diskdb, common.Hash{byte(i)}, junk) + } + + _, dl := helper.CommitAndGenerate() + select { + case <-dl.generator.done: + // Snapshot generation succeeded + + case <-time.After(3 * time.Second): + t.Errorf("Snapshot generation failed") + } + // TODO(rjl493456442) enable the snapshot tests + // checkSnapRoot(t, snap, root) + + // Signal abortion to the generator and wait for it to tear down + dl.generator.stop() +} + +func TestGenerateFromEmptySnap(t *testing.T) { + helper := newGenTester() + + for i := 0; i < 400; i++ { + stRoot := helper.makeStorageTrie(fmt.Sprintf("acc-%d", i), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addTrieAccount(fmt.Sprintf("acc-%d", i), &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + } + root, snap := helper.CommitAndGenerate() + t.Logf("Root: %#x\n", root) // Root: 0x6f7af6d2e1a1bf2b84a3beb3f8b64388465fbc1e274ca5d5d3fc787ca78f59e4 + + select { + case <-snap.generator.done: + // Snapshot generation succeeded + + case <-time.After(3 * time.Second): + t.Errorf("Snapshot generation failed") + } + // TODO(rjl493456442) enable the snapshot tests + // checkSnapRoot(t, snap, root) + + // Signal abortion to the generator and wait for it to tear down + snap.generator.stop() +} + +func TestGenerateWithIncompleteStorage(t *testing.T) { + helper := newGenTester() + stKeys := []string{"1", "2", "3", "4", "5", "6", "7", "8"} + stVals := []string{"v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8"} + + // We add 8 accounts, each one is missing exactly one of the storage slots. This means + // we don't have to order the keys and figure out exactly which hash-key winds up + // on the sensitive spots at the boundaries + for i := 0; i < 8; i++ { + accKey := fmt.Sprintf("acc-%d", i) + stRoot := helper.makeStorageTrie(accKey, stKeys, stVals, true) + helper.addAccount(accKey, &types.StateAccount{Balance: uint256.NewInt(uint64(i)), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + var moddedKeys []string + var moddedVals []string + for ii := 0; ii < 8; ii++ { + if ii != i { + moddedKeys = append(moddedKeys, stKeys[ii]) + moddedVals = append(moddedVals, stVals[ii]) + } + } + helper.addSnapStorage(accKey, moddedKeys, moddedVals) + } + root, dl := helper.CommitAndGenerate() + t.Logf("Root: %#x\n", root) // Root: 0xca73f6f05ba4ca3024ef340ef3dfca8fdabc1b677ff13f5a9571fd49c16e67ff + + select { + case <-dl.generator.done: + // Snapshot generation succeeded + + case <-time.After(3 * time.Second): + t.Errorf("Snapshot generation failed") + } + // TODO(rjl493456442) enable the snapshot tests + // checkSnapRoot(t, snap, root) + + // Signal abortion to the generator and wait for it to tear down + dl.generator.stop() +} + +func incKey(key []byte) []byte { + for i := len(key) - 1; i >= 0; i-- { + key[i]++ + if key[i] != 0x0 { + break + } + } + return key +} + +func decKey(key []byte) []byte { + for i := len(key) - 1; i >= 0; i-- { + key[i]-- + if key[i] != 0xff { + break + } + } + return key +} + +func populateDangling(disk ethdb.KeyValueStore) { + populate := func(accountHash common.Hash, keys []string, vals []string) { + for i, key := range keys { + rawdb.WriteStorageSnapshot(disk, accountHash, hashData([]byte(key)), []byte(vals[i])) + } + } + // Dangling storages of the "first" account + populate(common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) + + // Dangling storages of the "last" account + populate(common.HexToHash("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) + + // Dangling storages around the account 1 + hash := decKey(hashData([]byte("acc-1")).Bytes()) + populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) + hash = incKey(hashData([]byte("acc-1")).Bytes()) + populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) + + // Dangling storages around the account 2 + hash = decKey(hashData([]byte("acc-2")).Bytes()) + populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) + hash = incKey(hashData([]byte("acc-2")).Bytes()) + populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) + + // Dangling storages around the account 3 + hash = decKey(hashData([]byte("acc-3")).Bytes()) + populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) + hash = incKey(hashData([]byte("acc-3")).Bytes()) + populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) + + // Dangling storages of the random account + populate(testrand.Hash(), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) + populate(testrand.Hash(), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) + populate(testrand.Hash(), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) +} + +func TestGenerateCompleteSnapshotWithDanglingStorage(t *testing.T) { + var helper = newGenTester() + + stRoot := helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) + + helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + + helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) + helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) + + populateDangling(helper.diskdb) + + _, dl := helper.CommitAndGenerate() + select { + case <-dl.generator.done: + // Snapshot generation succeeded + + case <-time.After(3 * time.Second): + t.Errorf("Snapshot generation failed") + } + // TODO(rjl493456442) enable the snapshot tests + // checkSnapRoot(t, snap, root) + + // Signal abortion to the generator and wait for it to tear down + dl.generator.stop() +} + +func TestGenerateBrokenSnapshotWithDanglingStorage(t *testing.T) { + var helper = newGenTester() + + stRoot := helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) + + helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) + + populateDangling(helper.diskdb) + + _, dl := helper.CommitAndGenerate() + select { + case <-dl.generator.done: + // Snapshot generation succeeded + + case <-time.After(3 * time.Second): + t.Errorf("Snapshot generation failed") + } + // TODO(rjl493456442) enable the snapshot tests + // checkSnapRoot(t, snap, root) + + // Signal abortion to the generator and wait for it to tear down + dl.generator.stop() +} diff --git a/triedb/pathdb/iterator.go b/triedb/pathdb/iterator.go index 980f228cf5d..84ea08ddd37 100644 --- a/triedb/pathdb/iterator.go +++ b/triedb/pathdb/iterator.go @@ -91,15 +91,14 @@ type diffAccountIterator struct { } // newDiffAccountIterator creates an account iterator over the given state set. -func newDiffAccountIterator(seek common.Hash, states *stateSet, fn loadAccount) AccountIterator { +func newDiffAccountIterator(seek common.Hash, accountList []common.Hash, fn loadAccount) AccountIterator { // Seek out the requested starting account - hashes := states.accountList() - index := sort.Search(len(hashes), func(i int) bool { - return bytes.Compare(seek[:], hashes[i][:]) <= 0 + index := sort.Search(len(accountList), func(i int) bool { + return bytes.Compare(seek[:], accountList[i][:]) <= 0 }) // Assemble and returned the already seeked iterator return &diffAccountIterator{ - keys: hashes[index:], + keys: accountList[index:], loadFn: fn, } } @@ -236,15 +235,14 @@ type diffStorageIterator struct { } // newDiffStorageIterator creates a storage iterator over a single diff layer. -func newDiffStorageIterator(account common.Hash, seek common.Hash, states *stateSet, fn loadStorage) StorageIterator { - hashes := states.storageList(account) - index := sort.Search(len(hashes), func(i int) bool { - return bytes.Compare(seek[:], hashes[i][:]) <= 0 +func newDiffStorageIterator(account common.Hash, seek common.Hash, storageList []common.Hash, fn loadStorage) StorageIterator { + index := sort.Search(len(storageList), func(i int) bool { + return bytes.Compare(seek[:], storageList[i][:]) <= 0 }) // Assemble and returned the already seeked iterator return &diffStorageIterator{ account: account, - keys: hashes[index:], + keys: storageList[index:], loadFn: fn, } } diff --git a/triedb/pathdb/iterator_binary.go b/triedb/pathdb/iterator_binary.go index dec31e8f3f5..0620081d0c1 100644 --- a/triedb/pathdb/iterator_binary.go +++ b/triedb/pathdb/iterator_binary.go @@ -45,6 +45,12 @@ type binaryIterator struct { // accounts in a slow, but easily verifiable way. Note this function is used // for initialization, use `newBinaryAccountIterator` as the API. func (dl *diskLayer) initBinaryAccountIterator(seek common.Hash) *binaryIterator { + // The state set in the disk layer is mutable, hold the lock before obtaining + // the account list to prevent concurrent map iteration and write. + dl.lock.RLock() + accountList := dl.buffer.states.accountList() + dl.lock.RUnlock() + // Create two iterators for state buffer and the persistent state in disk // respectively and combine them as a binary iterator. l := &binaryIterator{ @@ -54,7 +60,7 @@ func (dl *diskLayer) initBinaryAccountIterator(seek common.Hash) *binaryIterator // The account key list for iteration is deterministic once the iterator // is constructed, no matter the referenced disk layer is stale or not // later. - a: newDiffAccountIterator(seek, dl.buffer.states, nil), + a: newDiffAccountIterator(seek, accountList, nil), b: newDiskAccountIterator(dl.db.diskdb, seek), } l.aDone = !l.a.Next() @@ -68,6 +74,9 @@ func (dl *diskLayer) initBinaryAccountIterator(seek common.Hash) *binaryIterator func (dl *diffLayer) initBinaryAccountIterator(seek common.Hash) *binaryIterator { parent, ok := dl.parent.(*diffLayer) if !ok { + // The state set in diff layer is immutable and will never be stale, + // so the read lock protection is unnecessary. + accountList := dl.states.stateSet.accountList() l := &binaryIterator{ // The account loader function is unnecessary; the account key list // produced by the supplied state set alone is sufficient for iteration. @@ -75,13 +84,16 @@ func (dl *diffLayer) initBinaryAccountIterator(seek common.Hash) *binaryIterator // The account key list for iteration is deterministic once the iterator // is constructed, no matter the referenced disk layer is stale or not // later. - a: newDiffAccountIterator(seek, dl.states.stateSet, nil), + a: newDiffAccountIterator(seek, accountList, nil), b: dl.parent.(*diskLayer).initBinaryAccountIterator(seek), } l.aDone = !l.a.Next() l.bDone = !l.b.Next() return l } + // The state set in diff layer is immutable and will never be stale, + // so the read lock protection is unnecessary. + accountList := dl.states.stateSet.accountList() l := &binaryIterator{ // The account loader function is unnecessary; the account key list // produced by the supplied state set alone is sufficient for iteration. @@ -89,7 +101,7 @@ func (dl *diffLayer) initBinaryAccountIterator(seek common.Hash) *binaryIterator // The account key list for iteration is deterministic once the iterator // is constructed, no matter the referenced disk layer is stale or not // later. - a: newDiffAccountIterator(seek, dl.states.stateSet, nil), + a: newDiffAccountIterator(seek, accountList, nil), b: parent.initBinaryAccountIterator(seek), } l.aDone = !l.a.Next() @@ -101,6 +113,12 @@ func (dl *diffLayer) initBinaryAccountIterator(seek common.Hash) *binaryIterator // storage slots in a slow, but easily verifiable way. Note this function is used // for initialization, use `newBinaryStorageIterator` as the API. func (dl *diskLayer) initBinaryStorageIterator(account common.Hash, seek common.Hash) *binaryIterator { + // The state set in the disk layer is mutable, hold the lock before obtaining + // the storage list to prevent concurrent map iteration and write. + dl.lock.RLock() + storageList := dl.buffer.states.storageList(account) + dl.lock.RUnlock() + // Create two iterators for state buffer and the persistent state in disk // respectively and combine them as a binary iterator. l := &binaryIterator{ @@ -110,7 +128,7 @@ func (dl *diskLayer) initBinaryStorageIterator(account common.Hash, seek common. // The storage key list for iteration is deterministic once the iterator // is constructed, no matter the referenced disk layer is stale or not // later. - a: newDiffStorageIterator(account, seek, dl.buffer.states, nil), + a: newDiffStorageIterator(account, seek, storageList, nil), b: newDiskStorageIterator(dl.db.diskdb, account, seek), } l.aDone = !l.a.Next() @@ -124,6 +142,9 @@ func (dl *diskLayer) initBinaryStorageIterator(account common.Hash, seek common. func (dl *diffLayer) initBinaryStorageIterator(account common.Hash, seek common.Hash) *binaryIterator { parent, ok := dl.parent.(*diffLayer) if !ok { + // The state set in diff layer is immutable and will never be stale, + // so the read lock protection is unnecessary. + storageList := dl.states.stateSet.storageList(account) l := &binaryIterator{ // The storage loader function is unnecessary; the storage key list // produced by the supplied state set alone is sufficient for iteration. @@ -131,13 +152,16 @@ func (dl *diffLayer) initBinaryStorageIterator(account common.Hash, seek common. // The storage key list for iteration is deterministic once the iterator // is constructed, no matter the referenced disk layer is stale or not // later. - a: newDiffStorageIterator(account, seek, dl.states.stateSet, nil), + a: newDiffStorageIterator(account, seek, storageList, nil), b: dl.parent.(*diskLayer).initBinaryStorageIterator(account, seek), } l.aDone = !l.a.Next() l.bDone = !l.b.Next() return l } + // The state set in diff layer is immutable and will never be stale, + // so the read lock protection is unnecessary. + storageList := dl.states.stateSet.storageList(account) l := &binaryIterator{ // The storage loader function is unnecessary; the storage key list // produced by the supplied state set alone is sufficient for iteration. @@ -145,7 +169,7 @@ func (dl *diffLayer) initBinaryStorageIterator(account common.Hash, seek common. // The storage key list for iteration is deterministic once the iterator // is constructed, no matter the referenced disk layer is stale or not // later. - a: newDiffStorageIterator(account, seek, dl.states.stateSet, nil), + a: newDiffStorageIterator(account, seek, storageList, nil), b: parent.initBinaryStorageIterator(account, seek), } l.aDone = !l.a.Next() diff --git a/triedb/pathdb/iterator_fast.go b/triedb/pathdb/iterator_fast.go index 966e37a7cb4..87b2dd567ac 100644 --- a/triedb/pathdb/iterator_fast.go +++ b/triedb/pathdb/iterator_fast.go @@ -76,11 +76,17 @@ func newFastIterator(db *Database, root common.Hash, account common.Hash, seek c if accountIterator { switch dl := current.(type) { case *diskLayer: + // The state set in the disk layer is mutable, hold the lock before obtaining + // the account list to prevent concurrent map iteration and write. + dl.lock.RLock() + accountList := dl.buffer.states.accountList() + dl.lock.RUnlock() + fi.iterators = append(fi.iterators, &weightedIterator{ // The state set in the disk layer is mutable, and the entire state becomes stale // if a diff layer above is merged into it. Therefore, staleness must be checked, // and the storage slot should be retrieved with read lock protection. - it: newDiffAccountIterator(seek, dl.buffer.states, func(hash common.Hash) ([]byte, error) { + it: newDiffAccountIterator(seek, accountList, func(hash common.Hash) ([]byte, error) { dl.lock.RLock() defer dl.lock.RUnlock() @@ -98,19 +104,26 @@ func newFastIterator(db *Database, root common.Hash, account common.Hash, seek c case *diffLayer: // The state set in diff layer is immutable and will never be stale, // so the read lock protection is unnecessary. + accountList := dl.states.accountList() fi.iterators = append(fi.iterators, &weightedIterator{ - it: newDiffAccountIterator(seek, dl.states.stateSet, dl.states.mustAccount), + it: newDiffAccountIterator(seek, accountList, dl.states.mustAccount), priority: depth, }) } } else { switch dl := current.(type) { case *diskLayer: + // The state set in the disk layer is mutable, hold the lock before obtaining + // the storage list to prevent concurrent map iteration and write. + dl.lock.RLock() + storageList := dl.buffer.states.storageList(account) + dl.lock.RUnlock() + fi.iterators = append(fi.iterators, &weightedIterator{ // The state set in the disk layer is mutable, and the entire state becomes stale // if a diff layer above is merged into it. Therefore, staleness must be checked, // and the storage slot should be retrieved with read lock protection. - it: newDiffStorageIterator(account, seek, dl.buffer.states, func(addrHash common.Hash, slotHash common.Hash) ([]byte, error) { + it: newDiffStorageIterator(account, seek, storageList, func(addrHash common.Hash, slotHash common.Hash) ([]byte, error) { dl.lock.RLock() defer dl.lock.RUnlock() @@ -126,10 +139,14 @@ func newFastIterator(db *Database, root common.Hash, account common.Hash, seek c priority: depth + 1, }) case *diffLayer: + // The state set in diff layer is immutable and will never be stale, + // so the read lock protection is unnecessary. + storageList := dl.states.storageList(account) + // The state set in diff layer is immutable and will never be stale, // so the read lock protection is unnecessary. fi.iterators = append(fi.iterators, &weightedIterator{ - it: newDiffStorageIterator(account, seek, dl.states.stateSet, dl.states.mustStorage), + it: newDiffStorageIterator(account, seek, storageList, dl.states.mustStorage), priority: depth, }) } diff --git a/triedb/pathdb/iterator_test.go b/triedb/pathdb/iterator_test.go index 38941180340..6517735d7e9 100644 --- a/triedb/pathdb/iterator_test.go +++ b/triedb/pathdb/iterator_test.go @@ -132,18 +132,15 @@ func TestAccountIteratorBasics(t *testing.T) { } } states := newStates(accounts, storage, false) - it := newDiffAccountIterator(common.Hash{}, states, nil) + it := newDiffAccountIterator(common.Hash{}, states.accountList(), nil) verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator - // TODO reenable these tests once the persistent state iteration - // is implemented. - - //db := rawdb.NewMemoryDatabase() - //batch := db.NewBatch() - //states.write(db, batch, nil, nil) - //batch.Write() - //it = newDiskAccountIterator(db, common.Hash{}) - //verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator + db := rawdb.NewMemoryDatabase() + batch := db.NewBatch() + states.write(batch, nil, nil) + batch.Write() + it = newDiskAccountIterator(db, common.Hash{}) + verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator } // TestStorageIteratorBasics tests some simple single-layer(diff and disk) iteration for storage @@ -173,21 +170,18 @@ func TestStorageIteratorBasics(t *testing.T) { } states := newStates(accounts, storage, false) for account := range accounts { - it := newDiffStorageIterator(account, common.Hash{}, states, nil) + it := newDiffStorageIterator(account, common.Hash{}, states.storageList(account), nil) verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator } - // TODO reenable these tests once the persistent state iteration - // is implemented. - - //db := rawdb.NewMemoryDatabase() - //batch := db.NewBatch() - //states.write(db, batch, nil, nil) - //batch.Write() - //for account := range accounts { - // it := newDiskStorageIterator(db, account, common.Hash{}) - // verifyIterator(t, 100-nilStorage[account], it, verifyNothing) // Nil is allowed for single layer iterator - //} + db := rawdb.NewMemoryDatabase() + batch := db.NewBatch() + states.write(batch, nil, nil) + batch.Write() + for account := range accounts { + it := newDiskStorageIterator(db, account, common.Hash{}) + verifyIterator(t, 100-nilStorage[account], it, verifyNothing) // Nil is allowed for single layer iterator + } } type testIterator struct { @@ -263,7 +257,7 @@ func TestAccountIteratorTraversal(t *testing.T) { WriteBufferSize: 0, } db := New(rawdb.NewMemoryDatabase(), config, false) - // db.WaitGeneration() + db.waitGeneration() // Stack three diff layers on top with various overlaps db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 0, trienode.NewMergedNodeSet(), @@ -279,7 +273,7 @@ func TestAccountIteratorTraversal(t *testing.T) { head := db.tree.get(common.HexToHash("0x04")) // singleLayer: 0xcc, 0xf0, 0xff - it := newDiffAccountIterator(common.Hash{}, head.(*diffLayer).states.stateSet, nil) + it := newDiffAccountIterator(common.Hash{}, head.(*diffLayer).states.stateSet.accountList(), nil) verifyIterator(t, 3, it, verifyNothing) // binaryIterator: 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xf0, 0xff @@ -290,19 +284,16 @@ func TestAccountIteratorTraversal(t *testing.T) { verifyIterator(t, 7, it, verifyAccount) it.Release() - // TODO reenable these tests once the persistent state iteration - // is implemented. - // Test after persist some bottom-most layers into the disk, // the functionalities still work. - //db.tree.cap(common.HexToHash("0x04"), 2) - - //head = db.tree.get(common.HexToHash("0x04")) - //verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount) - // - //it, _ = db.AccountIterator(common.HexToHash("0x04"), common.Hash{}) - //verifyIterator(t, 7, it, verifyAccount) - //it.Release() + db.tree.cap(common.HexToHash("0x04"), 2) + + head = db.tree.get(common.HexToHash("0x04")) + verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(common.Hash{}), verifyAccount) + + it, _ = db.AccountIterator(common.HexToHash("0x04"), common.Hash{}) + verifyIterator(t, 7, it, verifyAccount) + it.Release() } func TestStorageIteratorTraversal(t *testing.T) { @@ -310,7 +301,7 @@ func TestStorageIteratorTraversal(t *testing.T) { WriteBufferSize: 0, } db := New(rawdb.NewMemoryDatabase(), config, false) - // db.WaitGeneration() + db.waitGeneration() // Stack three diff layers on top with various overlaps db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 0, trienode.NewMergedNodeSet(), @@ -326,7 +317,7 @@ func TestStorageIteratorTraversal(t *testing.T) { head := db.tree.get(common.HexToHash("0x04")) // singleLayer: 0x1, 0x2, 0x3 - diffIter := newDiffStorageIterator(common.HexToHash("0xaa"), common.Hash{}, head.(*diffLayer).states.stateSet, nil) + diffIter := newDiffStorageIterator(common.HexToHash("0xaa"), common.Hash{}, head.(*diffLayer).states.stateSet.storageList(common.HexToHash("0xaa")), nil) verifyIterator(t, 3, diffIter, verifyNothing) // binaryIterator: 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 @@ -337,17 +328,14 @@ func TestStorageIteratorTraversal(t *testing.T) { verifyIterator(t, 6, it, verifyStorage) it.Release() - // TODO reenable these tests once the persistent state iteration - // is implemented. - // Test after persist some bottom-most layers into the disk, // the functionalities still work. - //db.tree.cap(common.HexToHash("0x04"), 2) - //verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa")), verifyStorage) - // - //it, _ = db.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{}) - //verifyIterator(t, 6, it, verifyStorage) - //it.Release() + db.tree.cap(common.HexToHash("0x04"), 2) + verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), verifyStorage) + + it, _ = db.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{}) + verifyIterator(t, 6, it, verifyStorage) + it.Release() } // TestAccountIteratorTraversalValues tests some multi-layer iteration, where we @@ -357,7 +345,7 @@ func TestAccountIteratorTraversalValues(t *testing.T) { WriteBufferSize: 0, } db := New(rawdb.NewMemoryDatabase(), config, false) - // db.WaitGeneration() + db.waitGeneration() // Create a batch of account sets to seed subsequent layers with var ( @@ -434,26 +422,38 @@ func TestAccountIteratorTraversalValues(t *testing.T) { } it.Release() - // TODO reenable these tests once the persistent state iteration - // is implemented. - // Test after persist some bottom-most layers into the disk, // the functionalities still work. - //db.tree.cap(common.HexToHash("0x09"), 2) - // - //it, _ = db.AccountIterator(common.HexToHash("0x09"), common.Hash{}) - //for it.Next() { - // hash := it.Hash() - // account, err := head.Account(hash) - // if err != nil { - // t.Fatalf("failed to retrieve expected account: %v", err) - // } - // want, _ := rlp.EncodeToBytes(account) - // if have := it.Account(); !bytes.Equal(want, have) { - // t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want) - // } - //} - //it.Release() + db.tree.cap(common.HexToHash("0x09"), 2) + + // binaryIterator + head = db.tree.get(common.HexToHash("0x09")) + it = head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) + for it.Next() { + hash := it.Hash() + want, err := r.(*reader).AccountRLP(hash) + if err != nil { + t.Fatalf("failed to retrieve expected account: %v", err) + } + if have := it.Account(); !bytes.Equal(want, have) { + t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want) + } + } + it.Release() + + // fastIterator + it, _ = db.AccountIterator(common.HexToHash("0x09"), common.Hash{}) + for it.Next() { + hash := it.Hash() + want, err := r.(*reader).AccountRLP(hash) + if err != nil { + t.Fatalf("failed to retrieve expected account: %v", err) + } + if have := it.Account(); !bytes.Equal(want, have) { + t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want) + } + } + it.Release() } func TestStorageIteratorTraversalValues(t *testing.T) { @@ -461,7 +461,7 @@ func TestStorageIteratorTraversalValues(t *testing.T) { WriteBufferSize: 0, } db := New(rawdb.NewMemoryDatabase(), config, false) - // db.WaitGeneration() + db.waitGeneration() wrapStorage := func(storage map[common.Hash][]byte) map[common.Hash]map[common.Hash][]byte { return map[common.Hash]map[common.Hash][]byte{ @@ -543,25 +543,38 @@ func TestStorageIteratorTraversalValues(t *testing.T) { } it.Release() - // TODO reenable these tests once the persistent state iteration - // is implemented. - // Test after persist some bottom-most layers into the disk, // the functionalities still work. - //db.tree.cap(common.HexToHash("0x09"), 2) - // - //it, _ = db.StorageIterator(common.HexToHash("0x09"), common.HexToHash("0xaa"), common.Hash{}) - //for it.Next() { - // hash := it.Hash() - // want, err := head.Storage(common.HexToHash("0xaa"), hash) - // if err != nil { - // t.Fatalf("failed to retrieve expected slot: %v", err) - // } - // if have := it.Slot(); !bytes.Equal(want, have) { - // t.Fatalf("hash %x: slot mismatch: have %x, want %x", hash, have, want) - // } - //} - //it.Release() + db.tree.cap(common.HexToHash("0x09"), 2) + + // binaryIterator + head = db.tree.get(common.HexToHash("0x09")) + it = head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}) + for it.Next() { + hash := it.Hash() + want, err := r.Storage(common.HexToHash("0xaa"), hash) + if err != nil { + t.Fatalf("failed to retrieve expected account: %v", err) + } + if have := it.Slot(); !bytes.Equal(want, have) { + t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want) + } + } + it.Release() + + // fastIterator + it, _ = db.StorageIterator(common.HexToHash("0x09"), common.HexToHash("0xaa"), common.Hash{}) + for it.Next() { + hash := it.Hash() + want, err := r.Storage(common.HexToHash("0xaa"), hash) + if err != nil { + t.Fatalf("failed to retrieve expected storage slot: %v", err) + } + if have := it.Slot(); !bytes.Equal(want, have) { + t.Fatalf("hash %x: slot mismatch: have %x, want %x", hash, have, want) + } + } + it.Release() } // This testcase is notorious, all layers contain the exact same 200 accounts. @@ -581,7 +594,8 @@ func TestAccountIteratorLargeTraversal(t *testing.T) { WriteBufferSize: 0, } db := New(rawdb.NewMemoryDatabase(), config, false) - // db.WaitGeneration() + db.waitGeneration() + for i := 1; i < 128; i++ { parent := types.EmptyRootHash if i == 1 { @@ -592,25 +606,22 @@ func TestAccountIteratorLargeTraversal(t *testing.T) { } // Iterate the entire stack and ensure everything is hit only once head := db.tree.get(common.HexToHash("0x80")) - verifyIterator(t, 200, newDiffAccountIterator(common.Hash{}, head.(*diffLayer).states.stateSet, nil), verifyNothing) + verifyIterator(t, 200, newDiffAccountIterator(common.Hash{}, head.(*diffLayer).states.stateSet.accountList(), nil), verifyNothing) verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(common.Hash{}), verifyAccount) it, _ := db.AccountIterator(common.HexToHash("0x80"), common.Hash{}) verifyIterator(t, 200, it, verifyAccount) it.Release() - // TODO reenable these tests once the persistent state iteration - // is implemented. - // Test after persist some bottom-most layers into the disk, // the functionalities still work. - //db.tree.cap(common.HexToHash("0x80"), 2) - // - //verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount) - // - //it, _ = db.AccountIterator(common.HexToHash("0x80"), common.Hash{}) - //verifyIterator(t, 200, it, verifyAccount) - //it.Release() + db.tree.cap(common.HexToHash("0x80"), 2) + + verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(common.Hash{}), verifyAccount) + + it, _ = db.AccountIterator(common.HexToHash("0x80"), common.Hash{}) + verifyIterator(t, 200, it, verifyAccount) + it.Release() } // TestAccountIteratorFlattening tests what happens when we @@ -622,7 +633,7 @@ func TestAccountIteratorFlattening(t *testing.T) { WriteBufferSize: 10 * 1024, } db := New(rawdb.NewMemoryDatabase(), config, false) - // db.WaitGeneration() + db.waitGeneration() // Create a stack of diffs on top db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), @@ -655,7 +666,7 @@ func TestAccountIteratorSeek(t *testing.T) { WriteBufferSize: 0, } db := New(rawdb.NewMemoryDatabase(), config, false) - // db.WaitGeneration() + db.waitGeneration() db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil, nil, nil, false)) @@ -727,7 +738,7 @@ func testStorageIteratorSeek(t *testing.T, newIterator func(db *Database, root, WriteBufferSize: 0, } db := New(rawdb.NewMemoryDatabase(), config, false) - // db.WaitGeneration() + db.waitGeneration() // Stack three diff layers on top with various overlaps db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), @@ -799,7 +810,7 @@ func testAccountIteratorDeletions(t *testing.T, newIterator func(db *Database, r WriteBufferSize: 0, } db := New(rawdb.NewMemoryDatabase(), config, false) - // db.WaitGeneration() + db.waitGeneration() // Stack three diff layers on top with various overlaps db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), @@ -839,7 +850,7 @@ func TestStorageIteratorDeletions(t *testing.T) { WriteBufferSize: 0, } db := New(rawdb.NewMemoryDatabase(), config, false) - // db.WaitGeneration() + db.waitGeneration() // Stack three diff layers on top with various overlaps db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), @@ -907,7 +918,7 @@ func testStaleIterator(t *testing.T, newIter func(db *Database, hash common.Hash WriteBufferSize: 16 * 1024 * 1024, } db := New(rawdb.NewMemoryDatabase(), config, false) - // db.WaitGeneration() + db.waitGeneration() // [02 (disk), 03] db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), @@ -962,7 +973,7 @@ func BenchmarkAccountIteratorTraversal(b *testing.B) { WriteBufferSize: 0, } db := New(rawdb.NewMemoryDatabase(), config, false) - // db.WaitGeneration() + db.waitGeneration() for i := 1; i <= 100; i++ { parent := types.EmptyRootHash @@ -1057,7 +1068,7 @@ func BenchmarkAccountIteratorLargeBaselayer(b *testing.B) { WriteBufferSize: 0, } db := New(rawdb.NewMemoryDatabase(), config, false) - // db.WaitGeneration() + db.waitGeneration() db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(makeAccounts(2000), nil, nil, nil, false)) for i := 2; i <= 100; i++ { diff --git a/triedb/pathdb/journal.go b/triedb/pathdb/journal.go index 79a7a22e0b0..5dc6da92b85 100644 --- a/triedb/pathdb/journal.go +++ b/triedb/pathdb/journal.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" ) @@ -90,6 +91,56 @@ func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) { return head, nil } +// journalGenerator is a disk layer entry containing the generator progress marker. +type journalGenerator struct { + // Indicator that whether the database was in progress of being wiped. + // It's deprecated but keep it here for backward compatibility. + Wiping bool + + Done bool // Whether the generator finished creating the snapshot + Marker []byte + Accounts uint64 + Slots uint64 + Storage uint64 +} + +// loadGenerator loads the state generation progress marker from the database. +func loadGenerator(db ethdb.KeyValueReader, hash nodeHasher) (*journalGenerator, common.Hash, error) { + trieRoot, err := hash(rawdb.ReadAccountTrieNode(db, nil)) + if err != nil { + return nil, common.Hash{}, err + } + // State generation progress marker is lost, rebuild it + blob := rawdb.ReadSnapshotGenerator(db) + if len(blob) == 0 { + log.Info("State snapshot generator is not found") + return nil, trieRoot, nil + } + // State generation progress marker is not compatible, rebuild it + var generator journalGenerator + if err := rlp.DecodeBytes(blob, &generator); err != nil { + log.Info("State snapshot generator is not compatible") + return nil, trieRoot, nil + } + // The state snapshot is inconsistent with the trie data and must + // be rebuilt. + // + // Note: The SnapshotRoot and SnapshotGenerator are always consistent + // with each other, both in the legacy state snapshot and the path database. + // Therefore, if the SnapshotRoot does not match the trie root, + // the entire generator is considered stale and must be discarded. + stateRoot := rawdb.ReadSnapshotRoot(db) + if trieRoot != stateRoot { + log.Info("State snapshot is not consistent", "trie", trieRoot, "state", stateRoot) + return nil, trieRoot, nil + } + // Slice null-ness is lost after rlp decoding, reset it back to empty + if !generator.Done && generator.Marker == nil { + generator.Marker = []byte{} + } + return &generator, trieRoot, nil +} + // loadLayers loads a pre-existing state layer backed by a key-value store. func (db *Database) loadLayers() layer { // Retrieve the root node of persistent state. @@ -109,7 +160,7 @@ func (db *Database) loadLayers() layer { log.Info("Failed to load journal, discard it", "err", err) } // Return single layer with persistent state. - return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0)) + return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0)) } // loadDiskLayer reads the binary blob from the layer journal, reconstructing @@ -141,7 +192,7 @@ func (db *Database) loadDiskLayer(r *rlp.Stream) (layer, error) { if err := states.decode(r); err != nil { return nil, err } - return newDiskLayer(root, id, db, nil, newBuffer(db.config.WriteBufferSize, &nodes, &states, id-stored)), nil + return newDiskLayer(root, id, db, nil, nil, newBuffer(db.config.WriteBufferSize, &nodes, &states, id-stored)), nil } // loadDiffLayer reads the next sections of a layer journal, reconstructing a new @@ -250,6 +301,10 @@ func (db *Database) Journal(root common.Hash) error { } else { // disk layer only on noop runs (likely) or deep reorgs (unlikely) log.Info("Persisting dirty state to disk", "root", root, "layers", disk.buffer.layers) } + // Terminate the background state generation if it's active + if disk.generator != nil { + disk.generator.stop() + } start := time.Now() // Run the journaling diff --git a/triedb/pathdb/metrics.go b/triedb/pathdb/metrics.go index abe2dfe1f64..502c34fc62e 100644 --- a/triedb/pathdb/metrics.go +++ b/triedb/pathdb/metrics.go @@ -24,16 +24,26 @@ var ( cleanNodeReadMeter = metrics.NewRegisteredMeter("pathdb/clean/node/read", nil) cleanNodeWriteMeter = metrics.NewRegisteredMeter("pathdb/clean/node/write", nil) + cleanStateHitMeter = metrics.NewRegisteredMeter("pathdb/clean/state/hit", nil) + cleanStateMissMeter = metrics.NewRegisteredMeter("pathdb/clean/state/miss", nil) + cleanStateReadMeter = metrics.NewRegisteredMeter("pathdb/clean/state/read", nil) + cleanStateWriteMeter = metrics.NewRegisteredMeter("pathdb/clean/state/write", nil) + dirtyNodeHitMeter = metrics.NewRegisteredMeter("pathdb/dirty/node/hit", nil) dirtyNodeMissMeter = metrics.NewRegisteredMeter("pathdb/dirty/node/miss", nil) dirtyNodeReadMeter = metrics.NewRegisteredMeter("pathdb/dirty/node/read", nil) dirtyNodeWriteMeter = metrics.NewRegisteredMeter("pathdb/dirty/node/write", nil) dirtyNodeHitDepthHist = metrics.NewRegisteredHistogram("pathdb/dirty/node/depth", nil, metrics.NewExpDecaySample(1028, 0.015)) - stateAccountInexMeter = metrics.NewRegisteredMeter("pathdb/state/account/inex/total", nil) - stateStorageInexMeter = metrics.NewRegisteredMeter("pathdb/state/storage/inex/total", nil) - stateAccountExistMeter = metrics.NewRegisteredMeter("pathdb/state/account/exist/total", nil) - stateStorageExistMeter = metrics.NewRegisteredMeter("pathdb/state/storage/exist/total", nil) + stateAccountInexMeter = metrics.NewRegisteredMeter("pathdb/state/account/inex/total", nil) + stateStorageInexMeter = metrics.NewRegisteredMeter("pathdb/state/storage/inex/total", nil) + stateAccountInexDiskMeter = metrics.NewRegisteredMeter("pathdb/state/account/inex/disk", nil) + stateStorageInexDiskMeter = metrics.NewRegisteredMeter("pathdb/state/storage/inex/disk", nil) + + stateAccountExistMeter = metrics.NewRegisteredMeter("pathdb/state/account/exist/total", nil) + stateStorageExistMeter = metrics.NewRegisteredMeter("pathdb/state/storage/exist/total", nil) + stateAccountExistDiskMeter = metrics.NewRegisteredMeter("pathdb/state/account/exist/disk", nil) + stateStorageExistDiskMeter = metrics.NewRegisteredMeter("pathdb/state/storage/exist/disk", nil) dirtyStateHitMeter = metrics.NewRegisteredMeter("pathdb/dirty/state/hit", nil) dirtyStateMissMeter = metrics.NewRegisteredMeter("pathdb/dirty/state/miss", nil) @@ -46,9 +56,11 @@ var ( nodeDiskFalseMeter = metrics.NewRegisteredMeter("pathdb/disk/false", nil) nodeDiffFalseMeter = metrics.NewRegisteredMeter("pathdb/diff/false", nil) - commitTimeTimer = metrics.NewRegisteredResettingTimer("pathdb/commit/time", nil) - commitNodesMeter = metrics.NewRegisteredMeter("pathdb/commit/nodes", nil) - commitBytesMeter = metrics.NewRegisteredMeter("pathdb/commit/bytes", nil) + commitTimeTimer = metrics.NewRegisteredResettingTimer("pathdb/commit/time", nil) + commitNodesMeter = metrics.NewRegisteredMeter("pathdb/commit/nodes", nil) + commitAccountsMeter = metrics.NewRegisteredMeter("pathdb/commit/accounts", nil) + commitStoragesMeter = metrics.NewRegisteredMeter("pathdb/commit/slots", nil) + commitBytesMeter = metrics.NewRegisteredMeter("pathdb/commit/bytes", nil) gcTrieNodeMeter = metrics.NewRegisteredMeter("pathdb/gc/node/count", nil) gcTrieNodeBytesMeter = metrics.NewRegisteredMeter("pathdb/gc/node/bytes", nil) @@ -61,3 +73,28 @@ var ( historyDataBytesMeter = metrics.NewRegisteredMeter("pathdb/history/bytes/data", nil) historyIndexBytesMeter = metrics.NewRegisteredMeter("pathdb/history/bytes/index", nil) ) + +// Metrics in generation +var ( + generatedAccountMeter = metrics.NewRegisteredMeter("pathdb/generation/account/generated", nil) + recoveredAccountMeter = metrics.NewRegisteredMeter("pathdb/generation/account/recovered", nil) + wipedAccountMeter = metrics.NewRegisteredMeter("pathdb/generation/account/wiped", nil) + missallAccountMeter = metrics.NewRegisteredMeter("pathdb/generation/account/missall", nil) + generatedStorageMeter = metrics.NewRegisteredMeter("pathdb/generation/storage/generated", nil) + recoveredStorageMeter = metrics.NewRegisteredMeter("pathdb/generation/storage/recovered", nil) + wipedStorageMeter = metrics.NewRegisteredMeter("pathdb/generation/storage/wiped", nil) + missallStorageMeter = metrics.NewRegisteredMeter("pathdb/generation/storage/missall", nil) + danglingStorageMeter = metrics.NewRegisteredMeter("pathdb/generation/storage/dangling", nil) + successfulRangeProofMeter = metrics.NewRegisteredMeter("pathdb/generation/proof/success", nil) + failedRangeProofMeter = metrics.NewRegisteredMeter("pathdb/generation/proof/failure", nil) + + accountProveCounter = metrics.NewRegisteredCounter("pathdb/generation/duration/account/prove", nil) + accountTrieReadCounter = metrics.NewRegisteredCounter("pathdb/generation/duration/account/trieread", nil) + accountSnapReadCounter = metrics.NewRegisteredCounter("pathdb/generation/duration/account/snapread", nil) + accountWriteCounter = metrics.NewRegisteredCounter("pathdb/generation/duration/account/write", nil) + storageProveCounter = metrics.NewRegisteredCounter("pathdb/generation/duration/storage/prove", nil) + storageTrieReadCounter = metrics.NewRegisteredCounter("pathdb/generation/duration/storage/trieread", nil) + storageSnapReadCounter = metrics.NewRegisteredCounter("pathdb/generation/duration/storage/snapread", nil) + storageWriteCounter = metrics.NewRegisteredCounter("pathdb/generation/duration/storage/write", nil) + storageCleanCounter = metrics.NewRegisteredCounter("state/snapshot/generation/duration/storage/clean", nil) +) diff --git a/triedb/pathdb/states.go b/triedb/pathdb/states.go index 0a83b2f2cc1..2a88d74f5f6 100644 --- a/triedb/pathdb/states.go +++ b/triedb/pathdb/states.go @@ -23,8 +23,10 @@ import ( "slices" "sync" + "github.com/VictoriaMetrics/fastcache" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" @@ -416,6 +418,11 @@ func (s *stateSet) decode(r *rlp.Stream) error { return nil } +// write flushes state mutations into the provided database batch as a whole. +func (s *stateSet) write(batch ethdb.Batch, genMarker []byte, clean *fastcache.Cache) (int, int) { + return writeStates(batch, genMarker, s.accountData, s.storageData, clean) +} + // reset clears all cached state data, including any optional sorted lists that // may have been generated. func (s *stateSet) reset() { @@ -427,8 +434,6 @@ func (s *stateSet) reset() { } // dbsize returns the approximate size for db write. -// -// nolint:unused func (s *stateSet) dbsize() int { m := len(s.accountData) * len(rawdb.SnapshotAccountPrefix) for _, slots := range s.storageData { From 7e7925460507db9988088223ecd069af507815d6 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 16 May 2025 17:10:47 +0200 Subject: [PATCH 180/658] eth/protocols/eth: implement eth/69 (#29158) This PR implements eth/69. This protocol version drops the bloom filter from receipts messages, reducing the amount of data needed for a sync by ~530GB (2.3B txs * 256 byte) uncompressed. Compressed this will be reduced to ~100GB The new version also changes the Status message and introduces the BlockRangeUpdate message to relay information about the available history range. --------- Co-authored-by: Felix Lange --- cmd/devp2p/internal/ethtest/conn.go | 28 +- cmd/devp2p/internal/ethtest/protocol.go | 2 +- cmd/devp2p/internal/ethtest/suite.go | 73 ++- cmd/utils/cmd.go | 3 +- core/blockchain.go | 13 +- core/blockchain_reader.go | 14 +- core/blockchain_test.go | 22 +- core/filtermaps/chain_view.go | 4 +- core/filtermaps/indexer_test.go | 2 +- core/rawdb/accessors_chain.go | 25 +- core/rawdb/accessors_chain_test.go | 12 +- core/txindexer_test.go | 8 +- core/types/bloom9.go | 21 +- core/types/receipt.go | 24 +- core/types/receipt_test.go | 290 +++++------ eth/downloader/downloader.go | 7 +- eth/downloader/downloader_test.go | 9 +- .../fetchers_concurrent_receipts.go | 2 +- eth/downloader/queue.go | 9 +- eth/downloader/queue_test.go | 6 +- eth/filters/filter_system_test.go | 7 +- eth/handler.go | 160 +++++- eth/handler_eth_test.go | 13 +- eth/peer.go | 18 +- eth/protocols/eth/handler.go | 27 +- eth/protocols/eth/handler_test.go | 17 +- eth/protocols/eth/handlers.go | 140 ++++-- eth/protocols/eth/handshake.go | 174 +++++-- eth/protocols/eth/handshake_test.go | 10 +- eth/protocols/eth/peer.go | 16 + eth/protocols/eth/protocol.go | 65 ++- eth/protocols/eth/protocol_test.go | 35 +- eth/protocols/eth/receipt.go | 462 ++++++++++++++++++ eth/protocols/eth/receipt_test.go | 158 ++++++ eth/protocols/eth/tracker.go | 26 - 35 files changed, 1513 insertions(+), 389 deletions(-) create mode 100644 eth/protocols/eth/receipt.go create mode 100644 eth/protocols/eth/receipt_test.go delete mode 100644 eth/protocols/eth/tracker.go diff --git a/cmd/devp2p/internal/ethtest/conn.go b/cmd/devp2p/internal/ethtest/conn.go index a7bc70cbf57..6d8e0f1f7e6 100644 --- a/cmd/devp2p/internal/ethtest/conn.go +++ b/cmd/devp2p/internal/ethtest/conn.go @@ -66,10 +66,9 @@ func (s *Suite) dialAs(key *ecdsa.PrivateKey) (*Conn, error) { return nil, err } conn.caps = []p2p.Cap{ - {Name: "eth", Version: 67}, - {Name: "eth", Version: 68}, + {Name: "eth", Version: 69}, } - conn.ourHighestProtoVersion = 68 + conn.ourHighestProtoVersion = 69 return &conn, nil } @@ -156,7 +155,7 @@ func (c *Conn) ReadEth() (any, error) { var msg any switch int(code) { case eth.StatusMsg: - msg = new(eth.StatusPacket) + msg = new(eth.StatusPacket69) case eth.GetBlockHeadersMsg: msg = new(eth.GetBlockHeadersPacket) case eth.BlockHeadersMsg: @@ -231,7 +230,7 @@ func (c *Conn) ReadSnap() (any, error) { // peer performs both the protocol handshake and the status message // exchange with the node in order to peer with it. -func (c *Conn) peer(chain *Chain, status *eth.StatusPacket) error { +func (c *Conn) peer(chain *Chain, status *eth.StatusPacket69) error { if err := c.handshake(); err != nil { return fmt.Errorf("handshake failed: %v", err) } @@ -304,7 +303,7 @@ func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) { } // statusExchange performs a `Status` message exchange with the given node. -func (c *Conn) statusExchange(chain *Chain, status *eth.StatusPacket) error { +func (c *Conn) statusExchange(chain *Chain, status *eth.StatusPacket69) error { loop: for { code, data, err := c.Read() @@ -313,12 +312,16 @@ loop: } switch code { case eth.StatusMsg + protoOffset(ethProto): - msg := new(eth.StatusPacket) + msg := new(eth.StatusPacket69) if err := rlp.DecodeBytes(data, &msg); err != nil { return fmt.Errorf("error decoding status packet: %w", err) } - if have, want := msg.Head, chain.blocks[chain.Len()-1].Hash(); have != want { - return fmt.Errorf("wrong head block in status, want: %#x (block %d) have %#x", + if have, want := msg.LatestBlock, chain.blocks[chain.Len()-1].NumberU64(); have != want { + return fmt.Errorf("wrong head block in status, want: %d, have %d", + want, have) + } + if have, want := msg.LatestBlockHash, chain.blocks[chain.Len()-1].Hash(); have != want { + return fmt.Errorf("wrong head block in status, want: %#x (block %d) have %#x", want, chain.blocks[chain.Len()-1].NumberU64(), have) } if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) { @@ -348,13 +351,14 @@ loop: } if status == nil { // default status message - status = ð.StatusPacket{ + status = ð.StatusPacket69{ ProtocolVersion: uint32(c.negotiatedProtoVersion), NetworkID: chain.config.ChainID.Uint64(), - TD: chain.TD(), - Head: chain.blocks[chain.Len()-1].Hash(), Genesis: chain.blocks[0].Hash(), ForkID: chain.ForkID(), + EarliestBlock: 0, + LatestBlock: chain.blocks[chain.Len()-1].NumberU64(), + LatestBlockHash: chain.blocks[chain.Len()-1].Hash(), } } if err := c.Write(ethProto, eth.StatusMsg, status); err != nil { diff --git a/cmd/devp2p/internal/ethtest/protocol.go b/cmd/devp2p/internal/ethtest/protocol.go index 5c2f7d9e48c..a21d1ca7a1d 100644 --- a/cmd/devp2p/internal/ethtest/protocol.go +++ b/cmd/devp2p/internal/ethtest/protocol.go @@ -32,7 +32,7 @@ const ( // Unexported devp2p protocol lengths from p2p package. const ( baseProtoLen = 16 - ethProtoLen = 17 + ethProtoLen = 18 snapProtoLen = 8 ) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index a16d308dfd4..b90ecf3ca30 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -74,8 +74,9 @@ func (s *Suite) EthTests() []utesting.Test { {Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests}, {Name: "SameRequestID", Fn: s.TestSameRequestID}, {Name: "ZeroRequestID", Fn: s.TestZeroRequestID}, - // get block bodies + // get history {Name: "GetBlockBodies", Fn: s.TestGetBlockBodies}, + {Name: "GetReceipts", Fn: s.TestGetReceipts}, // // malicious handshakes + status {Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake}, // test transactions @@ -418,6 +419,51 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { } } +func (s *Suite) TestGetReceipts(t *utesting.T) { + t.Log(`This test sends GetReceipts requests to the node for known blocks in the test chain.`) + + conn, err := s.dial() + if err != nil { + t.Fatalf("dial failed: %v", err) + } + defer conn.Close() + if err := conn.peer(s.chain, nil); err != nil { + t.Fatalf("peering failed: %v", err) + } + + // Find some blocks containing receipts. + var hashes = make([]common.Hash, 0, 3) + for i := range s.chain.Len() { + block := s.chain.GetBlock(i) + if len(block.Transactions()) > 0 { + hashes = append(hashes, block.Hash()) + } + if len(hashes) == cap(hashes) { + break + } + } + + // Create block bodies request. + req := ð.GetReceiptsPacket{ + RequestId: 66, + GetReceiptsRequest: (eth.GetReceiptsRequest)(hashes), + } + if err := conn.Write(ethProto, eth.GetReceiptsMsg, req); err != nil { + t.Fatalf("could not write to connection: %v", err) + } + // Wait for response. + resp := new(eth.ReceiptsPacket[*eth.ReceiptList69]) + if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil { + t.Fatalf("error reading block bodies msg: %v", err) + } + if got, want := resp.RequestId, req.RequestId; got != want { + t.Fatalf("unexpected request id in respond", got, want) + } + if len(resp.List) != len(req.GetReceiptsRequest) { + t.Fatalf("wrong bodies in response: expected %d bodies, got %d", len(req.GetReceiptsRequest), len(resp.List)) + } +} + // randBuf makes a random buffer size kilobytes large. func randBuf(size int) []byte { buf := make([]byte, size*1024) @@ -500,6 +546,31 @@ func (s *Suite) TestMaliciousHandshake(t *utesting.T) { } } +func (s *Suite) TestInvalidBlockRangeUpdate(t *utesting.T) { + t.Log(`This test sends an invalid BlockRangeUpdate message to the node and expects to be disconnected.`) + + conn, err := s.dial() + if err != nil { + t.Fatalf("dial failed: %v", err) + } + defer conn.Close() + if err := conn.peer(s.chain, nil); err != nil { + t.Fatalf("peering failed: %v", err) + } + + conn.Write(ethProto, eth.BlockRangeUpdateMsg, ð.BlockRangeUpdatePacket{ + EarliestBlock: 10, + LatestBlock: 8, + LatestBlockHash: s.chain.GetBlock(8).Hash(), + }) + + if code, _, err := conn.Read(); err != nil { + t.Fatalf("expected disconnect, got err: %v", err) + } else if code != discMsg { + t.Fatalf("expected disconnect message, got msg code %d", code) + } +} + func (s *Suite) TestTransaction(t *utesting.T) { t.Log(`This test sends a valid transaction to the node and checks if the transaction gets propagated.`) diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index d34e15ebc08..b332a060dee 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -309,7 +309,8 @@ func ImportHistory(chain *core.BlockChain, dir string, network string) error { if err != nil { return fmt.Errorf("error reading receipts %d: %w", it.Number(), err) } - if _, err := chain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{receipts}, 2^64-1); err != nil { + encReceipts := types.EncodeBlockReceiptLists([]types.Receipts{receipts}) + if _, err := chain.InsertReceiptChain([]*types.Block{block}, encReceipts, 2^64-1); err != nil { return fmt.Errorf("error inserting body %d: %w", it.Number(), err) } imported += 1 diff --git a/core/blockchain.go b/core/blockchain.go index f93cddfe44b..64345bc1a35 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1309,12 +1309,11 @@ const ( // // The optional ancientLimit can also be specified and chain segment before that // will be directly stored in the ancient, getting rid of the chain migration. -func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts, ancientLimit uint64) (int, error) { +func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []rlp.RawValue, ancientLimit uint64) (int, error) { // Verify the supplied headers before insertion without lock var headers []*types.Header for _, block := range blockChain { headers = append(headers, block.Header()) - // Here we also validate that blob transactions in the block do not // contain a sidecar. While the sidecar does not affect the block hash // or tx hash, sending blobs within a block is not allowed. @@ -1357,11 +1356,11 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ // // this function only accepts canonical chain data. All side chain will be reverted // eventually. - writeAncient := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) { + writeAncient := func(blockChain types.Blocks, receiptChain []rlp.RawValue) (int, error) { // Ensure genesis is in the ancient store if blockChain[0].NumberU64() == 1 { if frozen, _ := bc.db.Ancients(); frozen == 0 { - writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil}) + writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList}) if err != nil { log.Error("Error writing genesis to ancients", "err", err) return 0, err @@ -1404,7 +1403,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ // existing local chain segments (reorg around the chain tip). The reorganized part // will be included in the provided chain segment, and stale canonical markers will be // silently rewritten. Therefore, no explicit reorg logic is needed. - writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) { + writeLive := func(blockChain types.Blocks, receiptChain []rlp.RawValue) (int, error) { var ( skipPresenceCheck = false batch = bc.db.NewBatch() @@ -1429,7 +1428,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ // Write all the data out into the database rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64()) rawdb.WriteBlock(batch, block) - rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i]) + rawdb.WriteRawReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i]) // Write everything belongs to the blocks into the database. So that // we can ensure all components of body is completed(body, receipts) @@ -2650,7 +2649,7 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e first = headers[0].Number.Uint64() ) if first == 1 && frozen == 0 { - _, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil}) + _, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList}) if err != nil { log.Error("Error writing genesis to ancients", "err", err) return 0, err diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index fefeb375424..c54948122d4 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -234,12 +234,22 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts { return receipts } -func (bc *BlockChain) GetRawReceiptsByHash(hash common.Hash) types.Receipts { +// GetRawReceipts retrieves the receipts for all transactions in a given block +// without deriving the internal fields and the Bloom. +func (bc *BlockChain) GetRawReceipts(hash common.Hash, number uint64) types.Receipts { + if receipts, ok := bc.receiptsCache.Get(hash); ok { + return receipts + } + return rawdb.ReadRawReceipts(bc.db, hash, number) +} + +// GetReceiptsRLP retrieves the receipts of a block. +func (bc *BlockChain) GetReceiptsRLP(hash common.Hash) rlp.RawValue { number := rawdb.ReadHeaderNumber(bc.db, hash) if number == nil { return nil } - return rawdb.ReadRawReceipts(bc.db, hash, *number) + return rawdb.ReadReceiptsRLP(bc.db, hash, *number) } // GetUnclesInChain retrieves all the uncles from a given block backwards until diff --git a/core/blockchain_test.go b/core/blockchain_test.go index b981c33f21e..434b494490e 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -734,7 +734,7 @@ func testFastVsFullChains(t *testing.T, scheme string) { fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) defer fast.Stop() - if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil { + if n, err := fast.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } // Freezer style fast import the chain. @@ -747,7 +747,7 @@ func testFastVsFullChains(t *testing.T, scheme string) { ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) defer ancient.Stop() - if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(len(blocks)/2)); err != nil { + if n, err := ancient.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), uint64(len(blocks)/2)); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } @@ -871,7 +871,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) { fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) defer fast.Stop() - if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil { + if n, err := fast.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } assert(t, "fast", fast, height, height, 0) @@ -884,7 +884,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) { ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) defer ancient.Stop() - if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil { + if n, err := ancient.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), uint64(3*len(blocks)/4)); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } assert(t, "ancient", ancient, height, height, 0) @@ -1696,7 +1696,7 @@ func testBlockchainRecovery(t *testing.T, scheme string) { defer ancientDb.Close() ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) - if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil { + if n, err := ancient.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), uint64(3*len(blocks)/4)); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } rawdb.WriteLastPivotNumber(ancientDb, blocks[len(blocks)-1].NumberU64()) // Force fast sync behavior @@ -1991,7 +1991,7 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) { } } else if typ == "receipts" { inserter = func(blocks []*types.Block, receipts []types.Receipts) error { - _, err = chain.InsertReceiptChain(blocks, receipts, 0) + _, err = chain.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0) return err } asserter = func(t *testing.T, block *types.Block) { @@ -2157,7 +2157,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i } } else if typ == "receipts" { inserter = func(blocks []*types.Block, receipts []types.Receipts) error { - _, err = chain.InsertReceiptChain(blocks, receipts, 0) + _, err = chain.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0) return err } asserter = func(t *testing.T, block *types.Block) { @@ -4205,10 +4205,10 @@ func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) { chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) defer chain.Stop() - if n, err := chain.InsertReceiptChain(blocks, receipts, ancientLimit); err != nil { + if n, err := chain.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), ancientLimit); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } - if n, err := chain.InsertReceiptChain(chainA, receiptsA, ancientLimit); err != nil { + if n, err := chain.InsertReceiptChain(chainA, types.EncodeBlockReceiptLists(receiptsA), ancientLimit); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } // If the common ancestor is below the ancient limit, rewind the chain head. @@ -4218,7 +4218,7 @@ func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) { rawdb.WriteLastPivotNumber(db, ancestor) chain.SetHead(ancestor) } - if n, err := chain.InsertReceiptChain(chainB, receiptsB, ancientLimit); err != nil { + if n, err := chain.InsertReceiptChain(chainB, types.EncodeBlockReceiptLists(receiptsB), ancientLimit); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } head := chain.CurrentSnapBlock() @@ -4336,7 +4336,7 @@ func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64, if n, err := chain.InsertHeadersBeforeCutoff(headersBefore); err != nil { t.Fatalf("failed to insert headers before cutoff %d: %v", n, err) } - if n, err := chain.InsertReceiptChain(blocksAfter, receiptsAfter, ancientLimit); err != nil { + if n, err := chain.InsertReceiptChain(blocksAfter, types.EncodeBlockReceiptLists(receiptsAfter), ancientLimit); err != nil { t.Fatalf("failed to insert receipt %d: %v", n, err) } headSnap := chain.CurrentSnapBlock() diff --git a/core/filtermaps/chain_view.go b/core/filtermaps/chain_view.go index 874ff19e31e..433ca07cd0f 100644 --- a/core/filtermaps/chain_view.go +++ b/core/filtermaps/chain_view.go @@ -29,7 +29,7 @@ type blockchain interface { GetHeader(hash common.Hash, number uint64) *types.Header GetCanonicalHash(number uint64) common.Hash GetReceiptsByHash(hash common.Hash) types.Receipts - GetRawReceiptsByHash(hash common.Hash) types.Receipts + GetRawReceipts(hash common.Hash, number uint64) types.Receipts } // ChainView represents an immutable view of a chain with a block id and a set @@ -117,7 +117,7 @@ func (cv *ChainView) RawReceipts(number uint64) types.Receipts { log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber) return nil } - return cv.chain.GetRawReceiptsByHash(blockHash) + return cv.chain.GetRawReceipts(blockHash, number) } // SharedRange returns the block range shared by two chain views. diff --git a/core/filtermaps/indexer_test.go b/core/filtermaps/indexer_test.go index 2782b2cbe6b..5ed397a90e0 100644 --- a/core/filtermaps/indexer_test.go +++ b/core/filtermaps/indexer_test.go @@ -515,7 +515,7 @@ func (tc *testChain) GetReceiptsByHash(hash common.Hash) types.Receipts { return tc.receipts[hash] } -func (tc *testChain) GetRawReceiptsByHash(hash common.Hash) types.Receipts { +func (tc *testChain) GetRawReceipts(hash common.Hash, number uint64) types.Receipts { tc.lock.RLock() defer tc.lock.RUnlock() diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 2f62d86e4b7..2386246caf7 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -545,8 +545,8 @@ func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawVa } // ReadRawReceipts retrieves all the transaction receipts belonging to a block. -// The receipt metadata fields are not guaranteed to be populated, so they -// should not be used. Use ReadReceipts instead if the metadata is needed. +// The receipt metadata fields and the Bloom are not guaranteed to be populated, +// so they should not be used. Use ReadReceipts instead if the metadata is needed. func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts { // Retrieve the flattened receipt slice data := ReadReceiptsRLP(db, hash, number) @@ -621,6 +621,14 @@ func WriteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rec } } +// WriteRawReceipts stores all the transaction receipts belonging to a block. +func WriteRawReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, receipts rlp.RawValue) { + // Store the flattened receipt slice + if err := db.Put(blockReceiptsKey(number, hash), receipts); err != nil { + log.Crit("Failed to store block receipts", "err", err) + } +} + // DeleteReceipts removes all receipt data associated with a block hash. func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { if err := db.Delete(blockReceiptsKey(number, hash)); err != nil { @@ -701,18 +709,11 @@ func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) { } // WriteAncientBlocks writes entire block data into ancient store and returns the total written size. -func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts) (int64, error) { - var stReceipts []*types.ReceiptForStorage - +func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []rlp.RawValue) (int64, error) { return db.ModifyAncients(func(op ethdb.AncientWriteOp) error { for i, block := range blocks { - // Convert receipts to storage format and sum up total difficulty. - stReceipts = stReceipts[:0] - for _, receipt := range receipts[i] { - stReceipts = append(stReceipts, (*types.ReceiptForStorage)(receipt)) - } header := block.Header() - if err := writeAncientBlock(op, block, header, stReceipts); err != nil { + if err := writeAncientBlock(op, block, header, receipts[i]); err != nil { return err } } @@ -720,7 +721,7 @@ func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts }) } -func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts []*types.ReceiptForStorage) error { +func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts rlp.RawValue) error { num := block.NumberU64() if err := op.AppendRaw(ChainFreezerHashTable, num, block.Hash().Bytes()); err != nil { return fmt.Errorf("can't add block %d hash: %v", num, err) diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 247e2775829..d98fc9a1eb3 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -377,7 +377,11 @@ func TestBlockReceiptStorage(t *testing.T) { t.Fatalf("receipts returned when body was deleted: %v", rs) } // Ensure that receipts without metadata can be returned without the block body too - if err := checkReceiptsRLP(ReadRawReceipts(db, hash, 0), receipts); err != nil { + raw := ReadRawReceipts(db, hash, 0) + for _, r := range raw { + r.Bloom = types.CreateBloom(r) + } + if err := checkReceiptsRLP(raw, receipts); err != nil { t.Fatal(err) } // Sanity check that body alone without the receipt is a full purge @@ -439,7 +443,7 @@ func TestAncientStorage(t *testing.T) { } // Write and verify the header in the database - WriteAncientBlocks(db, []*types.Block{block}, []types.Receipts{nil}) + WriteAncientBlocks(db, []*types.Block{block}, types.EncodeBlockReceiptLists([]types.Receipts{nil})) if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 { t.Fatalf("no header returned") @@ -609,7 +613,7 @@ func BenchmarkWriteAncientBlocks(b *testing.B) { blocks := allBlocks[i : i+length] receipts := batchReceipts[:length] - writeSize, err := WriteAncientBlocks(db, blocks, receipts) + writeSize, err := WriteAncientBlocks(db, blocks, types.EncodeBlockReceiptLists(receipts)) if err != nil { b.Fatal(err) } @@ -909,7 +913,7 @@ func TestHeadersRLPStorage(t *testing.T) { } receipts := make([]types.Receipts, 100) // Write first half to ancients - WriteAncientBlocks(db, chain[:50], receipts[:50]) + WriteAncientBlocks(db, chain[:50], types.EncodeBlockReceiptLists(receipts[:50])) // Write second half to db for i := 50; i < 100; i++ { WriteCanonicalHash(db, chain[i].Hash(), chain[i].NumberU64()) diff --git a/core/txindexer_test.go b/core/txindexer_test.go index 6543ff429dc..615a34de41b 100644 --- a/core/txindexer_test.go +++ b/core/txindexer_test.go @@ -117,7 +117,7 @@ func TestTxIndexer(t *testing.T) { } for _, c := range cases { db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) - rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...)) + rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), types.EncodeBlockReceiptLists(append([]types.Receipts{{}}, receipts...))) // Index the initial blocks from ancient store indexer := &txIndexer{ @@ -236,7 +236,8 @@ func TestTxIndexerRepair(t *testing.T) { } for _, c := range cases { db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) - rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...)) + encReceipts := types.EncodeBlockReceiptLists(append([]types.Receipts{{}}, receipts...)) + rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), encReceipts) // Index the initial blocks from ancient store indexer := &txIndexer{ @@ -426,7 +427,8 @@ func TestTxIndexerReport(t *testing.T) { } for _, c := range cases { db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) - rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...)) + encReceipts := types.EncodeBlockReceiptLists(append([]types.Receipts{{}}, receipts...)) + rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), encReceipts) // Index the initial blocks from ancient store indexer := &txIndexer{ diff --git a/core/types/bloom9.go b/core/types/bloom9.go index 962ba46d475..5a6e49c220d 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -59,11 +59,12 @@ func (b *Bloom) SetBytes(d []byte) { // Add adds d to the filter. Future calls of Test(d) will return true. func (b *Bloom) Add(d []byte) { - b.add(d, make([]byte, 6)) + var buf [6]byte + b.AddWithBuffer(d, &buf) } // add is internal version of Add, which takes a scratch buffer for reuse (needs to be at least 6 bytes) -func (b *Bloom) add(d []byte, buf []byte) { +func (b *Bloom) AddWithBuffer(d []byte, buf *[6]byte) { i1, v1, i2, v2, i3, v3 := bloomValues(d, buf) b[i1] |= v1 b[i2] |= v2 @@ -84,7 +85,8 @@ func (b Bloom) Bytes() []byte { // Test checks if the given topic is present in the bloom filter func (b Bloom) Test(topic []byte) bool { - i1, v1, i2, v2, i3, v3 := bloomValues(topic, make([]byte, 6)) + var buf [6]byte + i1, v1, i2, v2, i3, v3 := bloomValues(topic, &buf) return v1 == v1&b[i1] && v2 == v2&b[i2] && v3 == v3&b[i3] @@ -104,12 +106,12 @@ func (b *Bloom) UnmarshalText(input []byte) error { func CreateBloom(receipt *Receipt) Bloom { var ( bin Bloom - buf = make([]byte, 6) + buf [6]byte ) for _, log := range receipt.Logs { - bin.add(log.Address.Bytes(), buf) + bin.AddWithBuffer(log.Address.Bytes(), &buf) for _, b := range log.Topics { - bin.add(b[:], buf) + bin.AddWithBuffer(b[:], &buf) } } return bin @@ -139,21 +141,20 @@ func Bloom9(data []byte) []byte { } // bloomValues returns the bytes (index-value pairs) to set for the given data -func bloomValues(data []byte, hashbuf []byte) (uint, byte, uint, byte, uint, byte) { +func bloomValues(data []byte, hashbuf *[6]byte) (uint, byte, uint, byte, uint, byte) { sha := hasherPool.Get().(crypto.KeccakState) sha.Reset() sha.Write(data) - sha.Read(hashbuf) + sha.Read(hashbuf[:]) hasherPool.Put(sha) // The actual bits to flip v1 := byte(1 << (hashbuf[1] & 0x7)) v2 := byte(1 << (hashbuf[3] & 0x7)) v3 := byte(1 << (hashbuf[5] & 0x7)) // The indices for the bytes to OR in - i1 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf)&0x7ff)>>3) - 1 + i1 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[0:])&0x7ff)>>3) - 1 i2 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[2:])&0x7ff)>>3) - 1 i3 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[4:])&0x7ff)>>3) - 1 - return i1, v1, i2, v2, i3, v3 } diff --git a/core/types/receipt.go b/core/types/receipt.go index e52a0c64771..ec99fbf888f 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -258,7 +259,7 @@ func (r *Receipt) Size() common.StorageSize { } // ReceiptForStorage is a wrapper around a Receipt with RLP serialization -// that omits the Bloom field and deserialization that re-computes it. +// that omits the Bloom field. The Bloom field is recomputed by DeriveFields. type ReceiptForStorage Receipt // EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt @@ -291,7 +292,6 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { } r.CumulativeGasUsed = stored.CumulativeGasUsed r.Logs = stored.Logs - r.Bloom = CreateBloom((*Receipt)(r)) return nil } @@ -372,6 +372,26 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu rs[i].Logs[j].Index = logIndex logIndex++ } + // also derive the Bloom if not derived yet + rs[i].Bloom = CreateBloom(rs[i]) } return nil } + +// EncodeBlockReceiptLists encodes a list of block receipt lists into RLP. +func EncodeBlockReceiptLists(receipts []Receipts) []rlp.RawValue { + var storageReceipts []*ReceiptForStorage + result := make([]rlp.RawValue, len(receipts)) + for i, receipt := range receipts { + storageReceipts = storageReceipts[:0] + for _, r := range receipt { + storageReceipts = append(storageReceipts, (*ReceiptForStorage)(r)) + } + bytes, err := rlp.EncodeToBytes(storageReceipts) + if err != nil { + log.Crit("Failed to encode block receipts", "err", err) + } + result[i] = bytes + } + return result +} diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index 78b43c7e493..06cd0ff14cf 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -22,6 +22,7 @@ import ( "math" "math/big" "reflect" + "sync" "testing" "github.com/ethereum/go-ethereum/common" @@ -154,147 +155,160 @@ var ( blockNumber = big.NewInt(1) blockTime = uint64(2) blockHash = common.BytesToHash([]byte{0x03, 0x14}) +) - // Create the corresponding receipts - receipts = Receipts{ - &Receipt{ - Status: ReceiptStatusFailed, - CumulativeGasUsed: 1, - Logs: []*Log{ - { - Address: common.BytesToAddress([]byte{0x11}), - Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, - // derived fields: - BlockNumber: blockNumber.Uint64(), - TxHash: txs[0].Hash(), - TxIndex: 0, - BlockHash: blockHash, - Index: 0, - }, - { - Address: common.BytesToAddress([]byte{0x01, 0x11}), - Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, - // derived fields: - BlockNumber: blockNumber.Uint64(), - TxHash: txs[0].Hash(), - TxIndex: 0, - BlockHash: blockHash, - Index: 1, +var receiptsOnce sync.Once +var testReceipts Receipts + +func getTestReceipts() Receipts { + // Compute the blooms only once + receiptsOnce.Do(func() { + // Create the corresponding receipts + r := Receipts{ + &Receipt{ + Status: ReceiptStatusFailed, + CumulativeGasUsed: 1, + Logs: []*Log{ + { + Address: common.BytesToAddress([]byte{0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[0].Hash(), + TxIndex: 0, + BlockHash: blockHash, + Index: 0, + }, + { + Address: common.BytesToAddress([]byte{0x01, 0x11}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[0].Hash(), + TxIndex: 0, + BlockHash: blockHash, + Index: 1, + }, }, + // derived fields: + TxHash: txs[0].Hash(), + ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"), + GasUsed: 1, + EffectiveGasPrice: big.NewInt(11), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 0, }, - // derived fields: - TxHash: txs[0].Hash(), - ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"), - GasUsed: 1, - EffectiveGasPrice: big.NewInt(11), - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: 0, - }, - &Receipt{ - PostState: common.Hash{2}.Bytes(), - CumulativeGasUsed: 3, - Logs: []*Log{ - { - Address: common.BytesToAddress([]byte{0x22}), - Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, - // derived fields: - BlockNumber: blockNumber.Uint64(), - TxHash: txs[1].Hash(), - TxIndex: 1, - BlockHash: blockHash, - Index: 2, - }, - { - Address: common.BytesToAddress([]byte{0x02, 0x22}), - Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, - // derived fields: - BlockNumber: blockNumber.Uint64(), - TxHash: txs[1].Hash(), - TxIndex: 1, - BlockHash: blockHash, - Index: 3, + &Receipt{ + PostState: common.Hash{2}.Bytes(), + CumulativeGasUsed: 3, + Logs: []*Log{ + { + Address: common.BytesToAddress([]byte{0x22}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[1].Hash(), + TxIndex: 1, + BlockHash: blockHash, + Index: 2, + }, + { + Address: common.BytesToAddress([]byte{0x02, 0x22}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + // derived fields: + BlockNumber: blockNumber.Uint64(), + TxHash: txs[1].Hash(), + TxIndex: 1, + BlockHash: blockHash, + Index: 3, + }, }, + // derived fields: + TxHash: txs[1].Hash(), + GasUsed: 2, + EffectiveGasPrice: big.NewInt(22), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 1, }, - // derived fields: - TxHash: txs[1].Hash(), - GasUsed: 2, - EffectiveGasPrice: big.NewInt(22), - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: 1, - }, - &Receipt{ - Type: AccessListTxType, - PostState: common.Hash{3}.Bytes(), - CumulativeGasUsed: 6, - Logs: []*Log{}, - // derived fields: - TxHash: txs[2].Hash(), - GasUsed: 3, - EffectiveGasPrice: big.NewInt(33), - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: 2, - }, - &Receipt{ - Type: DynamicFeeTxType, - PostState: common.Hash{4}.Bytes(), - CumulativeGasUsed: 10, - Logs: []*Log{}, - // derived fields: - TxHash: txs[3].Hash(), - GasUsed: 4, - EffectiveGasPrice: big.NewInt(1044), - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: 3, - }, - &Receipt{ - Type: DynamicFeeTxType, - PostState: common.Hash{5}.Bytes(), - CumulativeGasUsed: 15, - Logs: []*Log{}, - // derived fields: - TxHash: txs[4].Hash(), - GasUsed: 5, - EffectiveGasPrice: big.NewInt(1055), - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: 4, - }, - &Receipt{ - Type: BlobTxType, - PostState: common.Hash{6}.Bytes(), - CumulativeGasUsed: 21, - Logs: []*Log{}, - // derived fields: - TxHash: txs[5].Hash(), - GasUsed: 6, - EffectiveGasPrice: big.NewInt(1066), - BlobGasUsed: params.BlobTxBlobGasPerBlob, - BlobGasPrice: big.NewInt(920), - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: 5, - }, - &Receipt{ - Type: BlobTxType, - PostState: common.Hash{7}.Bytes(), - CumulativeGasUsed: 28, - Logs: []*Log{}, - // derived fields: - TxHash: txs[6].Hash(), - GasUsed: 7, - EffectiveGasPrice: big.NewInt(1077), - BlobGasUsed: 3 * params.BlobTxBlobGasPerBlob, - BlobGasPrice: big.NewInt(920), - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: 6, - }, - } -) + &Receipt{ + Type: AccessListTxType, + PostState: common.Hash{3}.Bytes(), + CumulativeGasUsed: 6, + Logs: []*Log{}, + // derived fields: + TxHash: txs[2].Hash(), + GasUsed: 3, + EffectiveGasPrice: big.NewInt(33), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 2, + }, + &Receipt{ + Type: DynamicFeeTxType, + PostState: common.Hash{4}.Bytes(), + CumulativeGasUsed: 10, + Logs: []*Log{}, + // derived fields: + TxHash: txs[3].Hash(), + GasUsed: 4, + EffectiveGasPrice: big.NewInt(1044), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 3, + }, + &Receipt{ + Type: DynamicFeeTxType, + PostState: common.Hash{5}.Bytes(), + CumulativeGasUsed: 15, + Logs: []*Log{}, + // derived fields: + TxHash: txs[4].Hash(), + GasUsed: 5, + EffectiveGasPrice: big.NewInt(1055), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 4, + }, + &Receipt{ + Type: BlobTxType, + PostState: common.Hash{6}.Bytes(), + CumulativeGasUsed: 21, + Logs: []*Log{}, + // derived fields: + TxHash: txs[5].Hash(), + GasUsed: 6, + EffectiveGasPrice: big.NewInt(1066), + BlobGasUsed: params.BlobTxBlobGasPerBlob, + BlobGasPrice: big.NewInt(920), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 5, + }, + &Receipt{ + Type: BlobTxType, + PostState: common.Hash{7}.Bytes(), + CumulativeGasUsed: 28, + Logs: []*Log{}, + // derived fields: + TxHash: txs[6].Hash(), + GasUsed: 7, + EffectiveGasPrice: big.NewInt(1077), + BlobGasUsed: 3 * params.BlobTxBlobGasPerBlob, + BlobGasPrice: big.NewInt(920), + BlockHash: blockHash, + BlockNumber: blockNumber, + TransactionIndex: 6, + }, + } + for _, receipt := range r { + receipt.Bloom = CreateBloom(receipt) + } + testReceipts = r + }) + return testReceipts +} func TestDecodeEmptyTypedReceipt(t *testing.T) { input := []byte{0x80} @@ -310,6 +324,7 @@ func TestDeriveFields(t *testing.T) { // Re-derive receipts. basefee := big.NewInt(1000) blobGasPrice := big.NewInt(920) + receipts := getTestReceipts() derivedReceipts := clearComputedFieldsOnReceipts(receipts) err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, basefee, blobGasPrice, txs) if err != nil { @@ -335,6 +350,7 @@ func TestDeriveFields(t *testing.T) { // Test that we can marshal/unmarshal receipts to/from json without errors. // This also confirms that our test receipts contain all the required fields. func TestReceiptJSON(t *testing.T) { + receipts := getTestReceipts() for i := range receipts { b, err := receipts[i].MarshalJSON() if err != nil { @@ -351,6 +367,7 @@ func TestReceiptJSON(t *testing.T) { // Test we can still parse receipt without EffectiveGasPrice for backwards compatibility, even // though it is required per the spec. func TestEffectiveGasPriceNotRequired(t *testing.T) { + receipts := getTestReceipts() r := *receipts[0] r.EffectiveGasPrice = nil b, err := r.MarshalJSON() @@ -511,6 +528,7 @@ func clearComputedFieldsOnReceipt(receipt *Receipt) *Receipt { cpy.EffectiveGasPrice = big.NewInt(0) cpy.BlobGasUsed = 0 cpy.BlobGasPrice = nil + cpy.Bloom = CreateBloom(&cpy) return &cpy } diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 4d13ae304c2..762fb9283ea 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/triedb" ) @@ -202,7 +203,7 @@ type BlockChain interface { // into the local chain. Blocks older than the specified `ancientLimit` // are stored directly in the ancient store, while newer blocks are stored // in the live key-value store. - InsertReceiptChain(types.Blocks, []types.Receipts, uint64) (int, error) + InsertReceiptChain(types.Blocks, []rlp.RawValue, uint64) (int, error) // Snapshots returns the blockchain snapshot tree to paused it during sync. Snapshots() *snapshot.Tree @@ -1034,7 +1035,7 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state "lastnumn", last.Number, "lasthash", last.Hash(), ) blocks := make([]*types.Block, len(results)) - receipts := make([]types.Receipts, len(results)) + receipts := make([]rlp.RawValue, len(results)) for i, result := range results { blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.body()) receipts[i] = result.Receipts @@ -1051,7 +1052,7 @@ func (d *Downloader) commitPivotBlock(result *fetchResult) error { log.Debug("Committing snap sync pivot as new head", "number", block.Number(), "hash", block.Hash()) // Commit the pivot block as the new head, will require full sync from here on - if _, err := d.blockchain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{result.Receipts}, d.ancientLimit); err != nil { + if _, err := d.blockchain.InsertReceiptChain([]*types.Block{block}, []rlp.RawValue{result.Receipts}, d.ancientLimit); err != nil { return err } if err := d.blockchain.SnapSyncCommitHead(block.Hash()); err != nil { diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 3a145b19581..ecc820fd353 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -255,23 +255,24 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et // peer in the download tester. The returned function can be used to retrieve // batches of block receipts from the particularly requested peer. func (dlp *downloadTesterPeer) RequestReceipts(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) { - blobs := eth.ServiceGetReceiptsQuery(dlp.chain, hashes) + blobs := eth.ServiceGetReceiptsQuery68(dlp.chain, hashes) - receipts := make([][]*types.Receipt, len(blobs)) + receipts := make([]types.Receipts, len(blobs)) for i, blob := range blobs { rlp.DecodeBytes(blob, &receipts[i]) } hasher := trie.NewStackTrie(nil) hashes = make([]common.Hash, len(receipts)) for i, receipt := range receipts { - hashes[i] = types.DeriveSha(types.Receipts(receipt), hasher) + hashes[i] = types.DeriveSha(receipt, hasher) } req := ð.Request{ Peer: dlp.id, } + resp := eth.ReceiptsRLPResponse(types.EncodeBlockReceiptLists(receipts)) res := ð.Response{ Req: req, - Res: (*eth.ReceiptsResponse)(&receipts), + Res: &resp, Meta: hashes, Time: 1, Done: make(chan error, 1), // Ignore the returned status diff --git a/eth/downloader/fetchers_concurrent_receipts.go b/eth/downloader/fetchers_concurrent_receipts.go index 3169f030ba1..dbea30e881e 100644 --- a/eth/downloader/fetchers_concurrent_receipts.go +++ b/eth/downloader/fetchers_concurrent_receipts.go @@ -88,7 +88,7 @@ func (q *receiptQueue) request(peer *peerConnection, req *fetchRequest, resCh ch // deliver is responsible for taking a generic response packet from the concurrent // fetcher, unpacking the receipt data and delivering it to the downloader's queue. func (q *receiptQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) { - receipts := *packet.Res.(*eth.ReceiptsResponse) + receipts := *packet.Res.(*eth.ReceiptsRLPResponse) hashes := packet.Meta.([]common.Hash) // {receipt hashes} accepted, err := q.queue.DeliverReceipts(peer.id, receipts, hashes) diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 000ad97ca9c..bd7acadfc4e 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -34,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" ) const ( @@ -69,7 +70,7 @@ type fetchResult struct { Header *types.Header Uncles []*types.Header Transactions types.Transactions - Receipts types.Receipts + Receipts rlp.RawValue Withdrawals types.Withdrawals } @@ -318,9 +319,7 @@ func (q *queue) Results(block bool) []*fetchResult { for _, uncle := range result.Uncles { size += uncle.Size() } - for _, receipt := range result.Receipts { - size += receipt.Size() - } + size += common.StorageSize(len(result.Receipts)) for _, tx := range result.Transactions { size += common.StorageSize(tx.Size()) } @@ -631,7 +630,7 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH // DeliverReceipts injects a receipt retrieval response into the results queue. // The method returns the number of transaction receipts accepted from the delivery // and also wakes any threads waiting for data delivery. -func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt, receiptListHashes []common.Hash) (int, error) { +func (q *queue) DeliverReceipts(id string, receiptList []rlp.RawValue, receiptListHashes []common.Hash) (int, error) { q.lock.Lock() defer q.lock.Unlock() diff --git a/eth/downloader/queue_test.go b/eth/downloader/queue_test.go index 857ac4813a7..854acf3d8f1 100644 --- a/eth/downloader/queue_test.go +++ b/eth/downloader/queue_test.go @@ -358,16 +358,16 @@ func XTestDelivery(t *testing.T) { for { f, _, _ := q.ReserveReceipts(peer, rand.Intn(50)) if f != nil { - var rcs [][]*types.Receipt + var rcs []types.Receipts for _, hdr := range f.Headers { rcs = append(rcs, world.getReceipts(hdr.Number.Uint64())) } hasher := trie.NewStackTrie(nil) hashes := make([]common.Hash, len(rcs)) for i, receipt := range rcs { - hashes[i] = types.DeriveSha(types.Receipts(receipt), hasher) + hashes[i] = types.DeriveSha(receipt, hasher) } - _, err := q.DeliverReceipts(peer.id, rcs, hashes) + _, err := q.DeliverReceipts(peer.id, types.EncodeBlockReceiptLists(rcs), hashes) if err != nil { fmt.Printf("delivered %d receipts %v\n", len(rcs), err) } diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 122bdaeda42..85d4a339131 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -80,11 +80,8 @@ func (b *testBackend) GetReceiptsByHash(hash common.Hash) types.Receipts { return r } -func (b *testBackend) GetRawReceiptsByHash(hash common.Hash) types.Receipts { - if number := rawdb.ReadHeaderNumber(b.db, hash); number != nil { - return rawdb.ReadRawReceipts(b.db, hash, *number) - } - return nil +func (b *testBackend) GetRawReceipts(hash common.Hash, number uint64) types.Receipts { + return rawdb.ReadRawReceipts(b.db, hash, number) } func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { diff --git a/eth/handler.go b/eth/handler.go index f563b47138e..033a44b3bb0 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -18,15 +18,16 @@ package eth import ( "errors" + "maps" "math" "math/big" + "slices" "sync" "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" @@ -49,6 +50,9 @@ const ( // The number is referenced from the size of tx pool. txChanSize = 4096 + // chainHeadChanSize is the size of channel listening to ChainHeadEvent. + chainHeadChanSize = 128 + // txMaxBroadcastSize is the max size of a transaction that will be broadcasted. // All transactions with a higher size will be announced and need to be fetched // by the peer. @@ -104,9 +108,8 @@ type handlerConfig struct { } type handler struct { - nodeID enode.ID - networkID uint64 - forkFilter forkid.Filter // Fork ID filter, constant across the lifetime of the node + nodeID enode.ID + networkID uint64 snapSync atomic.Bool // Flag whether snap sync is enabled (gets disabled if we already have blocks) synced atomic.Bool // Flag whether we're considered synchronised (enables transaction processing) @@ -120,9 +123,10 @@ type handler struct { txFetcher *fetcher.TxFetcher peers *peerSet - eventMux *event.TypeMux - txsCh chan core.NewTxsEvent - txsSub event.Subscription + eventMux *event.TypeMux + txsCh chan core.NewTxsEvent + txsSub event.Subscription + blockRange *blockRangeState requiredBlocks map[uint64]common.Hash @@ -144,7 +148,6 @@ func newHandler(config *handlerConfig) (*handler, error) { h := &handler{ nodeID: config.NodeID, networkID: config.Network, - forkFilter: forkid.NewFilter(config.Chain), eventMux: config.EventMux, database: config.Database, txpool: config.TxPool, @@ -257,14 +260,7 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error { } // Execute the Ethereum handshake - var ( - genesis = h.chain.Genesis() - head = h.chain.CurrentHeader() - hash = head.Hash() - number = head.Number.Uint64() - ) - forkID := forkid.NewID(h.chain.Config(), genesis, number, head.Time) - if err := peer.Handshake(h.networkID, hash, genesis.Hash(), forkID, h.forkFilter); err != nil { + if err := peer.Handshake(h.networkID, h.chain, h.blockRange.currentRange()); err != nil { peer.Log().Debug("Ethereum handshake failed", "err", err) return err } @@ -435,6 +431,11 @@ func (h *handler) Start(maxPeers int) { h.txsSub = h.txpool.SubscribeTransactions(h.txsCh, false) go h.txBroadcastLoop() + // broadcast block range + h.wg.Add(1) + h.blockRange = newBlockRangeState(h.chain, h.eventMux) + go h.blockRangeLoop(h.blockRange) + // start sync handlers h.txFetcher.Start() @@ -445,6 +446,7 @@ func (h *handler) Start(maxPeers int) { func (h *handler) Stop() { h.txsSub.Unsubscribe() // quits txBroadcastLoop + h.blockRange.stop() h.txFetcher.Stop() h.downloader.Terminate() @@ -566,3 +568,129 @@ func (h *handler) enableSyncedFeatures() { h.snapSync.Store(false) } } + +// blockRangeState holds the state of the block range update broadcasting mechanism. +type blockRangeState struct { + prev eth.BlockRangeUpdatePacket + next atomic.Pointer[eth.BlockRangeUpdatePacket] + headCh chan core.ChainHeadEvent + headSub event.Subscription + syncSub *event.TypeMuxSubscription +} + +func newBlockRangeState(chain *core.BlockChain, typeMux *event.TypeMux) *blockRangeState { + headCh := make(chan core.ChainHeadEvent, chainHeadChanSize) + headSub := chain.SubscribeChainHeadEvent(headCh) + syncSub := typeMux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) + st := &blockRangeState{ + headCh: headCh, + headSub: headSub, + syncSub: syncSub, + } + st.update(chain, chain.CurrentBlock()) + st.prev = *st.next.Load() + return st +} + +// blockRangeBroadcastLoop announces changes in locally-available block range to peers. +// The range to announce is the range that is available in the store, so it's not just +// about imported blocks. +func (h *handler) blockRangeLoop(st *blockRangeState) { + defer h.wg.Done() + + for { + select { + case ev := <-st.syncSub.Chan(): + if ev == nil { + continue + } + if _, ok := ev.Data.(downloader.StartEvent); ok && h.snapSync.Load() { + h.blockRangeWhileSnapSyncing(st) + } + case <-st.headCh: + st.update(h.chain, h.chain.CurrentBlock()) + if st.shouldSend() { + h.broadcastBlockRange(st) + } + case <-st.headSub.Err(): + return + } + } +} + +// blockRangeWhileSnapSyncing announces block range updates during snap sync. +// Here we poll the CurrentSnapBlock on a timer and announce updates to it. +func (h *handler) blockRangeWhileSnapSyncing(st *blockRangeState) { + tick := time.NewTicker(1 * time.Minute) + defer tick.Stop() + + for { + select { + case <-tick.C: + st.update(h.chain, h.chain.CurrentSnapBlock()) + if st.shouldSend() { + h.broadcastBlockRange(st) + } + // back to processing head block updates when sync is done + case ev := <-st.syncSub.Chan(): + if ev == nil { + continue + } + switch ev.Data.(type) { + case downloader.FailedEvent, downloader.DoneEvent: + return + } + // ignore head updates, but exit when the subscription ends + case <-st.headCh: + case <-st.headSub.Err(): + return + } + } +} + +// broadcastBlockRange sends a range update when one is due. +func (h *handler) broadcastBlockRange(state *blockRangeState) { + h.peers.lock.Lock() + peerlist := slices.Collect(maps.Values(h.peers.peers)) + h.peers.lock.Unlock() + if len(peerlist) == 0 { + return + } + msg := state.currentRange() + log.Debug("Sending BlockRangeUpdate", "peers", len(peerlist), "earliest", msg.EarliestBlock, "latest", msg.LatestBlock) + for _, p := range peerlist { + p.SendBlockRangeUpdate(msg) + } + state.prev = *state.next.Load() +} + +// update assigns the values of the next block range update from the chain. +func (st *blockRangeState) update(chain *core.BlockChain, latest *types.Header) { + earliest, _ := chain.HistoryPruningCutoff() + st.next.Store(ð.BlockRangeUpdatePacket{ + EarliestBlock: min(latest.Number.Uint64(), earliest), + LatestBlock: latest.Number.Uint64(), + LatestBlockHash: latest.Hash(), + }) +} + +// shouldSend decides whether it is time to send a block range update. We don't want to +// send these updates constantly, so they will usually only be sent every 32 blocks. +// However, there is a special case: if the range would move back, i.e. due to SetHead, we +// want to send it immediately. +func (st *blockRangeState) shouldSend() bool { + next := st.next.Load() + return next.LatestBlock < st.prev.LatestBlock || + next.LatestBlock-st.prev.LatestBlock >= 32 +} + +func (st *blockRangeState) stop() { + st.syncSub.Unsubscribe() + st.headSub.Unsubscribe() +} + +// currentRange returns the current block range. +// This is safe to call from any goroutine. +func (st *blockRangeState) currentRange() eth.BlockRangeUpdatePacket { + return *st.next.Load() +} diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go index 8d572ca9664..2446c5fccbb 100644 --- a/eth/handler_eth_test.go +++ b/eth/handler_eth_test.go @@ -25,7 +25,6 @@ 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/forkid" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -257,11 +256,7 @@ func testRecvTransactions(t *testing.T, protocol uint) { return eth.Handle((*ethHandler)(handler.handler), peer) }) // Run the handshake locally to avoid spinning up a source handler - var ( - genesis = handler.chain.Genesis() - head = handler.chain.CurrentBlock() - ) - if err := src.Handshake(1, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil { + if err := src.Handshake(1, handler.chain, eth.BlockRangeUpdatePacket{}); err != nil { t.Fatalf("failed to run protocol handshake") } // Send the transaction to the sink and verify that it's added to the tx pool @@ -316,11 +311,7 @@ func testSendTransactions(t *testing.T, protocol uint) { return eth.Handle((*ethHandler)(handler.handler), peer) }) // Run the handshake locally to avoid spinning up a source handler - var ( - genesis = handler.chain.Genesis() - head = handler.chain.CurrentBlock() - ) - if err := sink.Handshake(1, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil { + if err := sink.Handshake(1, handler.chain, eth.BlockRangeUpdatePacket{}); err != nil { t.Fatalf("failed to run protocol handshake") } // After the handshake completes, the source handler should stream the sink diff --git a/eth/peer.go b/eth/peer.go index 76187777166..5808c3a3c55 100644 --- a/eth/peer.go +++ b/eth/peer.go @@ -17,6 +17,7 @@ package eth import ( + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/snap" ) @@ -25,6 +26,13 @@ import ( // about a connected peer. type ethPeerInfo struct { Version uint `json:"version"` // Ethereum protocol version negotiated + *peerBlockRange +} + +type peerBlockRange struct { + Earliest uint64 `json:"earliestBlock"` + Latest uint64 `json:"latestBlock"` + LatestHash common.Hash `json:"latestBlockHash"` } // ethPeer is a wrapper around eth.Peer to maintain a few extra metadata. @@ -35,9 +43,15 @@ type ethPeer struct { // info gathers and returns some `eth` protocol metadata known about a peer. func (p *ethPeer) info() *ethPeerInfo { - return ðPeerInfo{ - Version: p.Version(), + info := ðPeerInfo{Version: p.Version()} + if br := p.BlockRange(); br != nil { + info.peerBlockRange = &peerBlockRange{ + Earliest: br.EarliestBlock, + Latest: br.LatestBlock, + LatestHash: br.LatestBlockHash, + } } + return info } // snapPeerInfo represents a short summary of the `snap` sub-protocol metadata known diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go index f2a3cb02926..2467e0c713b 100644 --- a/eth/protocols/eth/handler.go +++ b/eth/protocols/eth/handler.go @@ -175,12 +175,26 @@ var eth68 = map[uint64]msgHandler{ BlockHeadersMsg: handleBlockHeaders, GetBlockBodiesMsg: handleGetBlockBodies, BlockBodiesMsg: handleBlockBodies, - GetReceiptsMsg: handleGetReceipts, - ReceiptsMsg: handleReceipts, + GetReceiptsMsg: handleGetReceipts68, + ReceiptsMsg: handleReceipts[*ReceiptList68], GetPooledTransactionsMsg: handleGetPooledTransactions, PooledTransactionsMsg: handlePooledTransactions, } +var eth69 = map[uint64]msgHandler{ + TransactionsMsg: handleTransactions, + NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes, + GetBlockHeadersMsg: handleGetBlockHeaders, + BlockHeadersMsg: handleBlockHeaders, + GetBlockBodiesMsg: handleGetBlockBodies, + BlockBodiesMsg: handleBlockBodies, + GetReceiptsMsg: handleGetReceipts69, + ReceiptsMsg: handleReceipts[*ReceiptList69], + GetPooledTransactionsMsg: handleGetPooledTransactions, + PooledTransactionsMsg: handlePooledTransactions, + BlockRangeUpdateMsg: handleBlockRangeUpdate, +} + // handleMessage is invoked whenever an inbound message is received from a remote // peer. The remote connection is torn down upon returning any error. func handleMessage(backend Backend, peer *Peer) error { @@ -194,7 +208,14 @@ func handleMessage(backend Backend, peer *Peer) error { } defer msg.Discard() - var handlers = eth68 + var handlers map[uint64]msgHandler + if peer.version == ETH68 { + handlers = eth68 + } else if peer.version == ETH69 { + handlers = eth69 + } else { + return fmt.Errorf("unknown eth protocol version: %v", peer.version) + } // Track the amount of time it takes to serve the request and run the handler if metrics.Enabled() { diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index fa031d98997..2fa10dfa9d3 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -529,22 +529,23 @@ func testGetBlockReceipts(t *testing.T, protocol uint) { // Collect the hashes to request, and the response to expect var ( hashes []common.Hash - receipts [][]*types.Receipt + receipts []*ReceiptList68 ) for i := uint64(0); i <= backend.chain.CurrentBlock().Number.Uint64(); i++ { block := backend.chain.GetBlockByNumber(i) - hashes = append(hashes, block.Hash()) - receipts = append(receipts, backend.chain.GetReceiptsByHash(block.Hash())) + trs := backend.chain.GetReceiptsByHash(block.Hash()) + receipts = append(receipts, NewReceiptList68(trs)) } + // Send the hash request and verify the response p2p.Send(peer.app, GetReceiptsMsg, &GetReceiptsPacket{ RequestId: 123, GetReceiptsRequest: hashes, }) - if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, &ReceiptsPacket{ - RequestId: 123, - ReceiptsResponse: receipts, + if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, &ReceiptsPacket[*ReceiptList68]{ + RequestId: 123, + List: receipts, }); err != nil { t.Errorf("receipts mismatch: %v", err) } @@ -612,10 +613,10 @@ func setup() (*testBackend, *testPeer) { } func FuzzEthProtocolHandlers(f *testing.F) { - handlers := eth68 + handlers := eth69 backend, peer := setup() f.Fuzz(func(t *testing.T, code byte, msg []byte) { - handler := handlers[uint64(code)%protocolLengths[ETH68]] + handler := handlers[uint64(code)%protocolLengths[ETH69]] if handler == nil { return } diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index fda650da1cf..15ad048bcf4 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -20,20 +20,25 @@ import ( "encoding/json" "errors" "fmt" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/tracker" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" ) +// requestTracker is a singleton tracker for eth/66 and newer request times. +var requestTracker = tracker.New(ProtocolName, 5*time.Minute) + func handleGetBlockHeaders(backend Backend, msg Decoder, peer *Peer) error { // Decode the complex header query var query GetBlockHeadersPacket if err := msg.Decode(&query); err != nil { - return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + return err } response := ServiceGetBlockHeadersQuery(backend.Chain(), query.GetBlockHeadersRequest, peer) return peer.ReplyBlockHeadersRLP(query.RequestId, response) @@ -216,7 +221,7 @@ func handleGetBlockBodies(backend Backend, msg Decoder, peer *Peer) error { // Decode the block body retrieval message var query GetBlockBodiesPacket if err := msg.Decode(&query); err != nil { - return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + return err } response := ServiceGetBlockBodiesQuery(backend.Chain(), query.GetBlockBodiesRequest) return peer.ReplyBlockBodiesRLP(query.RequestId, response) @@ -243,19 +248,29 @@ func ServiceGetBlockBodiesQuery(chain *core.BlockChain, query GetBlockBodiesRequ return bodies } -func handleGetReceipts(backend Backend, msg Decoder, peer *Peer) error { +func handleGetReceipts68(backend Backend, msg Decoder, peer *Peer) error { // Decode the block receipts retrieval message var query GetReceiptsPacket if err := msg.Decode(&query); err != nil { - return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + return err } - response := ServiceGetReceiptsQuery(backend.Chain(), query.GetReceiptsRequest) + response := ServiceGetReceiptsQuery68(backend.Chain(), query.GetReceiptsRequest) return peer.ReplyReceiptsRLP(query.RequestId, response) } -// ServiceGetReceiptsQuery assembles the response to a receipt query. It is +func handleGetReceipts69(backend Backend, msg Decoder, peer *Peer) error { + // Decode the block receipts retrieval message + var query GetReceiptsPacket + if err := msg.Decode(&query); err != nil { + return err + } + response := serviceGetReceiptsQuery69(backend.Chain(), query.GetReceiptsRequest) + return peer.ReplyReceiptsRLP(query.RequestId, response) +} + +// ServiceGetReceiptsQuery68 assembles the response to a receipt query. It is // exposed to allow external packages to test protocol behavior. -func ServiceGetReceiptsQuery(chain *core.BlockChain, query GetReceiptsRequest) []rlp.RawValue { +func ServiceGetReceiptsQuery68(chain *core.BlockChain, query GetReceiptsRequest) []rlp.RawValue { // Gather state data until the fetch or network limits is reached var ( bytes int @@ -267,19 +282,62 @@ func ServiceGetReceiptsQuery(chain *core.BlockChain, query GetReceiptsRequest) [ break } // Retrieve the requested block's receipts - results := chain.GetReceiptsByHash(hash) + results := chain.GetReceiptsRLP(hash) if results == nil { if header := chain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { continue } + } else { + body := chain.GetBodyRLP(hash) + if body == nil { + continue + } + var err error + results, err = blockReceiptsToNetwork68(results, body) + if err != nil { + log.Error("Error in block receipts conversion", "hash", hash, "err", err) + continue + } } - // If known, encode and queue for response packet - if encoded, err := rlp.EncodeToBytes(results); err != nil { - log.Error("Failed to encode receipt", "err", err) + receipts = append(receipts, results) + bytes += len(results) + } + return receipts +} + +// serviceGetReceiptsQuery69 assembles the response to a receipt query. +// It does not send the bloom filters for the receipts +func serviceGetReceiptsQuery69(chain *core.BlockChain, query GetReceiptsRequest) []rlp.RawValue { + // Gather state data until the fetch or network limits is reached + var ( + bytes int + receipts []rlp.RawValue + ) + for lookups, hash := range query { + if bytes >= softResponseLimit || len(receipts) >= maxReceiptsServe || + lookups >= 2*maxReceiptsServe { + break + } + // Retrieve the requested block's receipts + results := chain.GetReceiptsRLP(hash) + if results == nil { + if header := chain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { + continue + } } else { - receipts = append(receipts, encoded) - bytes += len(encoded) + body := chain.GetBodyRLP(hash) + if body == nil { + continue + } + var err error + results, err = blockReceiptsToNetwork69(results, body) + if err != nil { + log.Error("Error in block receipts conversion", "hash", hash, "err", err) + continue + } } + receipts = append(receipts, results) + bytes += len(results) } return receipts } @@ -296,7 +354,7 @@ func handleBlockHeaders(backend Backend, msg Decoder, peer *Peer) error { // A batch of headers arrived to one of our previous requests res := new(BlockHeadersPacket) if err := msg.Decode(res); err != nil { - return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + return err } metadata := func() interface{} { hashes := make([]common.Hash, len(res.BlockHeadersRequest)) @@ -316,7 +374,7 @@ func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error { // A batch of block bodies arrived to one of our previous requests res := new(BlockBodiesPacket) if err := msg.Decode(res); err != nil { - return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + return err } metadata := func() interface{} { var ( @@ -341,24 +399,35 @@ func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error { }, metadata) } -func handleReceipts(backend Backend, msg Decoder, peer *Peer) error { +func handleReceipts[L ReceiptsList](backend Backend, msg Decoder, peer *Peer) error { // A batch of receipts arrived to one of our previous requests - res := new(ReceiptsPacket) + res := new(ReceiptsPacket[L]) if err := msg.Decode(res); err != nil { - return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + return err } + // Assign temporary hashing buffer to each list item, the same buffer is shared + // between all receipt list instances. + buffers := new(receiptListBuffers) + for i := range res.List { + res.List[i].setBuffers(buffers) + } + metadata := func() interface{} { hasher := trie.NewStackTrie(nil) - hashes := make([]common.Hash, len(res.ReceiptsResponse)) - for i, receipt := range res.ReceiptsResponse { - hashes[i] = types.DeriveSha(types.Receipts(receipt), hasher) + hashes := make([]common.Hash, len(res.List)) + for i := range res.List { + hashes[i] = types.DeriveSha(res.List[i], hasher) } return hashes } + var enc ReceiptsRLPResponse + for i := range res.List { + enc = append(enc, res.List[i].EncodeForStorage()) + } return peer.dispatchResponse(&Response{ id: res.RequestId, code: ReceiptsMsg, - Res: &res.ReceiptsResponse, + Res: &enc, }, metadata) } @@ -370,10 +439,10 @@ func handleNewPooledTransactionHashes(backend Backend, msg Decoder, peer *Peer) } ann := new(NewPooledTransactionHashesPacket) if err := msg.Decode(ann); err != nil { - return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + return err } if len(ann.Hashes) != len(ann.Types) || len(ann.Hashes) != len(ann.Sizes) { - return fmt.Errorf("%w: message %v: invalid len of fields: %v %v %v", errDecode, msg, len(ann.Hashes), len(ann.Types), len(ann.Sizes)) + return fmt.Errorf("NewPooledTransactionHashes: invalid len of fields in %v %v %v", len(ann.Hashes), len(ann.Types), len(ann.Sizes)) } // Schedule all the unknown hashes for retrieval for _, hash := range ann.Hashes { @@ -386,7 +455,7 @@ func handleGetPooledTransactions(backend Backend, msg Decoder, peer *Peer) error // Decode the pooled transactions retrieval message var query GetPooledTransactionsPacket if err := msg.Decode(&query); err != nil { - return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + return err } hashes, txs := answerGetPooledTransactions(backend, query.GetPooledTransactionsRequest) return peer.ReplyPooledTransactionsRLP(query.RequestId, hashes, txs) @@ -423,12 +492,12 @@ func handleTransactions(backend Backend, msg Decoder, peer *Peer) error { // Transactions can be processed, parse all of them and deliver to the pool var txs TransactionsPacket if err := msg.Decode(&txs); err != nil { - return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + return err } for i, tx := range txs { // Validate and mark the remote transaction if tx == nil { - return fmt.Errorf("%w: transaction %d is nil", errDecode, i) + return fmt.Errorf("Transactions: transaction %d is nil", i) } peer.markTransaction(tx.Hash()) } @@ -443,12 +512,12 @@ func handlePooledTransactions(backend Backend, msg Decoder, peer *Peer) error { // Transactions can be processed, parse all of them and deliver to the pool var txs PooledTransactionsPacket if err := msg.Decode(&txs); err != nil { - return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) + return err } for i, tx := range txs.PooledTransactionsResponse { // Validate and mark the remote transaction if tx == nil { - return fmt.Errorf("%w: transaction %d is nil", errDecode, i) + return fmt.Errorf("PooledTransactions: transaction %d is nil", i) } peer.markTransaction(tx.Hash()) } @@ -456,3 +525,16 @@ func handlePooledTransactions(backend Backend, msg Decoder, peer *Peer) error { return backend.Handle(peer, &txs.PooledTransactionsResponse) } + +func handleBlockRangeUpdate(backend Backend, msg Decoder, peer *Peer) error { + var update BlockRangeUpdatePacket + if err := msg.Decode(&update); err != nil { + return err + } + if err := update.Validate(); err != nil { + return err + } + // We don't do anything with these messages for now, just store them on the peer. + peer.lastRange.Store(&update) + return nil +} diff --git a/eth/protocols/eth/handshake.go b/eth/protocols/eth/handshake.go index 0b6f110e3d4..824e49fb2bb 100644 --- a/eth/protocols/eth/handshake.go +++ b/eth/protocols/eth/handshake.go @@ -19,10 +19,10 @@ package eth import ( "errors" "fmt" - "math/big" "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/p2p" @@ -36,44 +36,122 @@ const ( // Handshake executes the eth protocol handshake, negotiating version number, // network IDs, difficulties, head and genesis blocks. -func (p *Peer) Handshake(network uint64, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) error { - // Send out own handshake in a new thread +func (p *Peer) Handshake(networkID uint64, chain *core.BlockChain, rangeMsg BlockRangeUpdatePacket) error { + switch p.version { + case ETH69: + return p.handshake69(networkID, chain, rangeMsg) + case ETH68: + return p.handshake68(networkID, chain) + default: + return errors.New("unsupported protocol version") + } +} + +func (p *Peer) handshake68(networkID uint64, chain *core.BlockChain) error { + var ( + genesis = chain.Genesis() + latest = chain.CurrentBlock() + forkID = forkid.NewID(chain.Config(), genesis, latest.Number.Uint64(), latest.Time) + forkFilter = forkid.NewFilter(chain) + ) errc := make(chan error, 2) + go func() { + pkt := &StatusPacket68{ + ProtocolVersion: uint32(p.version), + NetworkID: networkID, + Head: latest.Hash(), + Genesis: genesis.Hash(), + ForkID: forkID, + } + errc <- p2p.Send(p.rw, StatusMsg, pkt) + }() + var status StatusPacket68 // safe to read after two values have been received from errc + go func() { + errc <- p.readStatus68(networkID, &status, genesis.Hash(), forkFilter) + }() + + return waitForHandshake(errc, p) +} + +func (p *Peer) readStatus68(networkID uint64, status *StatusPacket68, genesis common.Hash, forkFilter forkid.Filter) error { + if err := p.readStatusMsg(status); err != nil { + return err + } + if status.NetworkID != networkID { + return fmt.Errorf("%w: %d (!= %d)", errNetworkIDMismatch, status.NetworkID, networkID) + } + if uint(status.ProtocolVersion) != p.version { + return fmt.Errorf("%w: %d (!= %d)", errProtocolVersionMismatch, status.ProtocolVersion, p.version) + } + if status.Genesis != genesis { + return fmt.Errorf("%w: %x (!= %x)", errGenesisMismatch, status.Genesis, genesis) + } + if err := forkFilter(status.ForkID); err != nil { + return fmt.Errorf("%w: %v", errForkIDRejected, err) + } + return nil +} - var status StatusPacket // safe to read after two values have been received from errc +func (p *Peer) handshake69(networkID uint64, chain *core.BlockChain, rangeMsg BlockRangeUpdatePacket) error { + var ( + genesis = chain.Genesis() + latest = chain.CurrentBlock() + forkID = forkid.NewID(chain.Config(), genesis, latest.Number.Uint64(), latest.Time) + forkFilter = forkid.NewFilter(chain) + ) + errc := make(chan error, 2) go func() { - errc <- p2p.Send(p.rw, StatusMsg, &StatusPacket{ + pkt := &StatusPacket69{ ProtocolVersion: uint32(p.version), - NetworkID: network, - TD: new(big.Int), // unknown for post-merge tail=pruned networks - Head: head, - Genesis: genesis, + NetworkID: networkID, + Genesis: genesis.Hash(), ForkID: forkID, - }) + EarliestBlock: rangeMsg.EarliestBlock, + LatestBlock: rangeMsg.LatestBlock, + LatestBlockHash: rangeMsg.LatestBlockHash, + } + errc <- p2p.Send(p.rw, StatusMsg, pkt) }() + var status StatusPacket69 // safe to read after two values have been received from errc go func() { - errc <- p.readStatus(network, &status, genesis, forkFilter) + errc <- p.readStatus69(networkID, &status, genesis.Hash(), forkFilter) }() - timeout := time.NewTimer(handshakeTimeout) - defer timeout.Stop() - for i := 0; i < 2; i++ { - select { - case err := <-errc: - if err != nil { - markError(p, err) - return err - } - case <-timeout.C: - markError(p, p2p.DiscReadTimeout) - return p2p.DiscReadTimeout - } + + return waitForHandshake(errc, p) +} + +func (p *Peer) readStatus69(networkID uint64, status *StatusPacket69, genesis common.Hash, forkFilter forkid.Filter) error { + if err := p.readStatusMsg(status); err != nil { + return err + } + if status.NetworkID != networkID { + return fmt.Errorf("%w: %d (!= %d)", errNetworkIDMismatch, status.NetworkID, networkID) + } + if uint(status.ProtocolVersion) != p.version { + return fmt.Errorf("%w: %d (!= %d)", errProtocolVersionMismatch, status.ProtocolVersion, p.version) + } + if status.Genesis != genesis { + return fmt.Errorf("%w: %x (!= %x)", errGenesisMismatch, status.Genesis, genesis) + } + if err := forkFilter(status.ForkID); err != nil { + return fmt.Errorf("%w: %v", errForkIDRejected, err) + } + // Handle initial block range. + initRange := &BlockRangeUpdatePacket{ + EarliestBlock: status.EarliestBlock, + LatestBlock: status.LatestBlock, + LatestBlockHash: status.LatestBlockHash, + } + if err := initRange.Validate(); err != nil { + return fmt.Errorf("%w: %v", errInvalidBlockRange, err) } + p.lastRange.Store(initRange) return nil } -// readStatus reads the remote handshake message. -func (p *Peer) readStatus(network uint64, status *StatusPacket, genesis common.Hash, forkFilter forkid.Filter) error { +// readStatusMsg reads the first message on the connection. +func (p *Peer) readStatusMsg(dst any) error { msg, err := p.rw.ReadMsg() if err != nil { return err @@ -84,21 +162,26 @@ func (p *Peer) readStatus(network uint64, status *StatusPacket, genesis common.H if msg.Size > maxMessageSize { return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize) } - // Decode the handshake and make sure everything matches - if err := msg.Decode(&status); err != nil { - return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) - } - if status.NetworkID != network { - return fmt.Errorf("%w: %d (!= %d)", errNetworkIDMismatch, status.NetworkID, network) - } - if uint(status.ProtocolVersion) != p.version { - return fmt.Errorf("%w: %d (!= %d)", errProtocolVersionMismatch, status.ProtocolVersion, p.version) - } - if status.Genesis != genesis { - return fmt.Errorf("%w: %x (!= %x)", errGenesisMismatch, status.Genesis, genesis) + if err := msg.Decode(dst); err != nil { + return err } - if err := forkFilter(status.ForkID); err != nil { - return fmt.Errorf("%w: %v", errForkIDRejected, err) + return nil +} + +func waitForHandshake(errc <-chan error, p *Peer) error { + timeout := time.NewTimer(handshakeTimeout) + defer timeout.Stop() + for range 2 { + select { + case err := <-errc: + if err != nil { + markError(p, err) + return err + } + case <-timeout.C: + markError(p, p2p.DiscReadTimeout) + return p2p.DiscReadTimeout + } } return nil } @@ -124,3 +207,14 @@ func markError(p *Peer, err error) { m.peerError.Mark(1) } } + +// Validate checks basic validity of a block range announcement. +func (p *BlockRangeUpdatePacket) Validate() error { + if p.EarliestBlock > p.LatestBlock { + return errors.New("earliest > latest") + } + if p.LatestBlockHash == (common.Hash{}) { + return errors.New("zero latest hash") + } + return nil +} diff --git a/eth/protocols/eth/handshake_test.go b/eth/protocols/eth/handshake_test.go index 1d1de3ec163..2fab3ea5a87 100644 --- a/eth/protocols/eth/handshake_test.go +++ b/eth/protocols/eth/handshake_test.go @@ -52,19 +52,19 @@ func testHandshake(t *testing.T, protocol uint) { want: errNoStatusMsg, }, { - code: StatusMsg, data: StatusPacket{10, 1, new(big.Int), head.Hash(), genesis.Hash(), forkID}, + code: StatusMsg, data: StatusPacket68{10, 1, new(big.Int), head.Hash(), genesis.Hash(), forkID}, want: errProtocolVersionMismatch, }, { - code: StatusMsg, data: StatusPacket{uint32(protocol), 999, new(big.Int), head.Hash(), genesis.Hash(), forkID}, + code: StatusMsg, data: StatusPacket68{uint32(protocol), 999, new(big.Int), head.Hash(), genesis.Hash(), forkID}, want: errNetworkIDMismatch, }, { - code: StatusMsg, data: StatusPacket{uint32(protocol), 1, new(big.Int), head.Hash(), common.Hash{3}, forkID}, + code: StatusMsg, data: StatusPacket68{uint32(protocol), 1, new(big.Int), head.Hash(), common.Hash{3}, forkID}, want: errGenesisMismatch, }, { - code: StatusMsg, data: StatusPacket{uint32(protocol), 1, new(big.Int), head.Hash(), genesis.Hash(), forkid.ID{Hash: [4]byte{0x00, 0x01, 0x02, 0x03}}}, + code: StatusMsg, data: StatusPacket68{uint32(protocol), 1, new(big.Int), head.Hash(), genesis.Hash(), forkid.ID{Hash: [4]byte{0x00, 0x01, 0x02, 0x03}}}, want: errForkIDRejected, }, } @@ -80,7 +80,7 @@ func testHandshake(t *testing.T, protocol uint) { // Send the junk test with one peer, check the handshake failure go p2p.Send(app, test.code, test.data) - err := peer.Handshake(1, head.Hash(), genesis.Hash(), forkID, forkid.NewFilter(backend.chain)) + err := peer.Handshake(1, backend.chain, BlockRangeUpdatePacket{}) if err == nil { t.Errorf("test %d: protocol returned nil error, want %q", i, test.want) } else if !errors.Is(err, test.want) { diff --git a/eth/protocols/eth/peer.go b/eth/protocols/eth/peer.go index 31a35eb186d..40c54a35705 100644 --- a/eth/protocols/eth/peer.go +++ b/eth/protocols/eth/peer.go @@ -18,6 +18,7 @@ package eth import ( "math/rand" + "sync/atomic" mapset "github.com/deckarep/golang-set/v2" "github.com/ethereum/go-ethereum/common" @@ -47,6 +48,7 @@ type Peer struct { *p2p.Peer // The embedded P2P package peer rw p2p.MsgReadWriter // Input/output streams for snap version uint // Protocol version negotiated + lastRange atomic.Pointer[BlockRangeUpdatePacket] txpool TxPool // Transaction pool used by the broadcasters for liveness checks knownTxs *knownCache // Set of transaction hashes known to be known by this peer @@ -102,6 +104,12 @@ func (p *Peer) Version() uint { return p.version } +// BlockRange returns the latest announced block range. +// This will be nil for peers below protocol version eth/69. +func (p *Peer) BlockRange() *BlockRangeUpdatePacket { + return p.lastRange.Load() +} + // KnownTransaction returns whether peer is known to already have a transaction. func (p *Peer) KnownTransaction(hash common.Hash) bool { return p.knownTxs.Contains(hash) @@ -343,6 +351,14 @@ func (p *Peer) RequestTxs(hashes []common.Hash) error { }) } +// SendBlockRangeUpdate sends a notification about our available block range to the peer. +func (p *Peer) SendBlockRangeUpdate(msg BlockRangeUpdatePacket) error { + if p.version < ETH69 { + return nil + } + return p2p.Send(p.rw, BlockRangeUpdateMsg, &msg) +} + // knownCache is a cache for known hashes. type knownCache struct { hashes mapset.Set[common.Hash] diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go index aeef4330ff4..7c41e7a9963 100644 --- a/eth/protocols/eth/protocol.go +++ b/eth/protocols/eth/protocol.go @@ -31,6 +31,7 @@ import ( // Constants to match up protocol versions and messages const ( ETH68 = 68 + ETH69 = 69 ) // ProtocolName is the official short name of the `eth` protocol used during @@ -39,11 +40,11 @@ const ProtocolName = "eth" // ProtocolVersions are the supported versions of the `eth` protocol (first // is primary). -var ProtocolVersions = []uint{ETH68} +var ProtocolVersions = []uint{ETH69, ETH68} // protocolLengths are the number of implemented message corresponding to // different protocol versions. -var protocolLengths = map[uint]uint64{ETH68: 17} +var protocolLengths = map[uint]uint64{ETH68: 17, ETH69: 18} // maxMessageSize is the maximum cap on the size of a protocol message. const maxMessageSize = 10 * 1024 * 1024 @@ -62,17 +63,19 @@ const ( PooledTransactionsMsg = 0x0a GetReceiptsMsg = 0x0f ReceiptsMsg = 0x10 + BlockRangeUpdateMsg = 0x11 ) var ( - errNoStatusMsg = errors.New("no status message") errMsgTooLarge = errors.New("message too long") - errDecode = errors.New("invalid message") errInvalidMsgCode = errors.New("invalid message code") errProtocolVersionMismatch = errors.New("protocol version mismatch") - errNetworkIDMismatch = errors.New("network ID mismatch") - errGenesisMismatch = errors.New("genesis mismatch") - errForkIDRejected = errors.New("fork ID rejected") + // handshake errors + errNoStatusMsg = errors.New("no status message") + errNetworkIDMismatch = errors.New("network ID mismatch") + errGenesisMismatch = errors.New("genesis mismatch") + errForkIDRejected = errors.New("fork ID rejected") + errInvalidBlockRange = errors.New("invalid block range in status") ) // Packet represents a p2p message in the `eth` protocol. @@ -82,7 +85,7 @@ type Packet interface { } // StatusPacket is the network packet for the status message. -type StatusPacket struct { +type StatusPacket68 struct { ProtocolVersion uint32 NetworkID uint64 TD *big.Int @@ -91,6 +94,18 @@ type StatusPacket struct { ForkID forkid.ID } +// StatusPacket69 is the network packet for the status message. +type StatusPacket69 struct { + ProtocolVersion uint32 + NetworkID uint64 + Genesis common.Hash + ForkID forkid.ID + // initial available block range + EarliestBlock uint64 + LatestBlock uint64 + LatestBlockHash common.Hash +} + // NewBlockHashesPacket is the network packet for the block announcements. type NewBlockHashesPacket []struct { Hash common.Hash // Hash of one particular block being announced @@ -250,13 +265,21 @@ type GetReceiptsPacket struct { } // ReceiptsResponse is the network packet for block receipts distribution. -type ReceiptsResponse [][]*types.Receipt +type ReceiptsResponse []types.Receipts + +// ReceiptsList is a type constraint for block receceipt list types. +type ReceiptsList interface { + *ReceiptList68 | *ReceiptList69 + setBuffers(*receiptListBuffers) + EncodeForStorage() rlp.RawValue + types.DerivableList +} // ReceiptsPacket is the network packet for block receipts distribution with // request ID wrapping. -type ReceiptsPacket struct { +type ReceiptsPacket[L ReceiptsList] struct { RequestId uint64 - ReceiptsResponse + List []L } // ReceiptsRLPResponse is used for receipts, when we already have it encoded @@ -304,8 +327,18 @@ type PooledTransactionsRLPPacket struct { PooledTransactionsRLPResponse } -func (*StatusPacket) Name() string { return "Status" } -func (*StatusPacket) Kind() byte { return StatusMsg } +// BlockRangeUpdatePacket is an announcement of the node's available block range. +type BlockRangeUpdatePacket struct { + EarliestBlock uint64 + LatestBlock uint64 + LatestBlockHash common.Hash +} + +func (*StatusPacket68) Name() string { return "Status" } +func (*StatusPacket68) Kind() byte { return StatusMsg } + +func (*StatusPacket69) Name() string { return "Status" } +func (*StatusPacket69) Kind() byte { return StatusMsg } func (*NewBlockHashesPacket) Name() string { return "NewBlockHashes" } func (*NewBlockHashesPacket) Kind() byte { return NewBlockHashesMsg } @@ -342,3 +375,9 @@ func (*GetReceiptsRequest) Kind() byte { return GetReceiptsMsg } func (*ReceiptsResponse) Name() string { return "Receipts" } func (*ReceiptsResponse) Kind() byte { return ReceiptsMsg } + +func (*ReceiptsRLPResponse) Name() string { return "Receipts" } +func (*ReceiptsRLPResponse) Kind() byte { return ReceiptsMsg } + +func (*BlockRangeUpdatePacket) Name() string { return "BlockRangeUpdate" } +func (*BlockRangeUpdatePacket) Kind() byte { return BlockRangeUpdateMsg } diff --git a/eth/protocols/eth/protocol_test.go b/eth/protocols/eth/protocol_test.go index bc2545dea28..8a2559a6c50 100644 --- a/eth/protocols/eth/protocol_test.go +++ b/eth/protocols/eth/protocol_test.go @@ -75,7 +75,7 @@ func TestEmptyMessages(t *testing.T) { // All empty messages encodes to the same format want := common.FromHex("c4820457c0") - for i, msg := range []interface{}{ + for i, msg := range []any{ // Headers GetBlockHeadersPacket{1111, nil}, BlockHeadersPacket{1111, nil}, @@ -85,7 +85,6 @@ func TestEmptyMessages(t *testing.T) { BlockBodiesRLPPacket{1111, nil}, // Receipts GetReceiptsPacket{1111, nil}, - ReceiptsPacket{1111, nil}, // Transactions GetPooledTransactionsPacket{1111, nil}, PooledTransactionsPacket{1111, nil}, @@ -99,7 +98,8 @@ func TestEmptyMessages(t *testing.T) { BlockBodiesRLPPacket{1111, BlockBodiesRLPResponse([]rlp.RawValue{})}, // Receipts GetReceiptsPacket{1111, GetReceiptsRequest([]common.Hash{})}, - ReceiptsPacket{1111, ReceiptsResponse([][]*types.Receipt{})}, + ReceiptsPacket[*ReceiptList68]{1111, []*ReceiptList68{}}, + ReceiptsPacket[*ReceiptList69]{1111, []*ReceiptList69{}}, // Transactions GetPooledTransactionsPacket{1111, GetPooledTransactionsRequest([]common.Hash{})}, PooledTransactionsPacket{1111, PooledTransactionsResponse([]*types.Transaction{})}, @@ -168,7 +168,7 @@ func TestMessages(t *testing.T) { receipts = []*types.Receipt{ { Status: types.ReceiptStatusFailed, - CumulativeGasUsed: 1, + CumulativeGasUsed: 333, Logs: []*types.Log{ { Address: common.BytesToAddress([]byte{0x11}), @@ -176,11 +176,21 @@ func TestMessages(t *testing.T) { Data: []byte{0x01, 0x00, 0xff}, }, }, - TxHash: hashes[0], - ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), - GasUsed: 111111, + }, + { + Status: types.ReceiptStatusSuccessful, + CumulativeGasUsed: 444, + Logs: []*types.Log{ + { + Address: common.BytesToAddress([]byte{0x22}), + Topics: []common.Hash{common.HexToHash("05668"), common.HexToHash("9773")}, + Data: []byte{0x02, 0x0f, 0x0f, 0x0f, 0x06, 0x08}, + }, + }, }, } + miniDeriveFields(receipts[0], 0) + miniDeriveFields(receipts[1], 1) rlpData, err := rlp.EncodeToBytes(receipts) if err != nil { t.Fatal(err) @@ -221,12 +231,17 @@ func TestMessages(t *testing.T) { common.FromHex("f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"), }, { - ReceiptsPacket{1111, ReceiptsResponse([][]*types.Receipt{receipts})}, - common.FromHex("f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"), + ReceiptsPacket[*ReceiptList68]{1111, []*ReceiptList68{NewReceiptList68(receipts)}}, + common.FromHex("f902e6820457f902e0f902ddf901688082014db9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000004000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ffb9016f01f9016b018201bcb9010000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000001000000000000000000000000000000000000000000000000040000000000000000000000000004000000000000000000000000000000000000000000000000000000008000400000000000000000000000000000000000000000000000000000000000000000000000000000040f862f860940000000000000000000000000000000000000022f842a00000000000000000000000000000000000000000000000000000000000005668a0000000000000000000000000000000000000000000000000000000000000977386020f0f0f0608"), }, { + // Identical to the eth/68 encoding above. ReceiptsRLPPacket{1111, ReceiptsRLPResponse([]rlp.RawValue{receiptsRlp})}, - common.FromHex("f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"), + common.FromHex("f902e6820457f902e0f902ddf901688082014db9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000004000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ffb9016f01f9016b018201bcb9010000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000001000000000000000000000000000000000000000000000000040000000000000000000000000004000000000000000000000000000000000000000000000000000000008000400000000000000000000000000000000000000000000000000000000000000000000000000000040f862f860940000000000000000000000000000000000000022f842a00000000000000000000000000000000000000000000000000000000000005668a0000000000000000000000000000000000000000000000000000000000000977386020f0f0f0608"), + }, + { + ReceiptsPacket[*ReceiptList69]{1111, []*ReceiptList69{NewReceiptList69(receipts)}}, + common.FromHex("f8da820457f8d5f8d3f866808082014df85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff86901018201bcf862f860940000000000000000000000000000000000000022f842a00000000000000000000000000000000000000000000000000000000000005668a0000000000000000000000000000000000000000000000000000000000000977386020f0f0f0608"), }, { GetPooledTransactionsPacket{1111, GetPooledTransactionsRequest(hashes)}, diff --git a/eth/protocols/eth/receipt.go b/eth/protocols/eth/receipt.go new file mode 100644 index 00000000000..45c4766b173 --- /dev/null +++ b/eth/protocols/eth/receipt.go @@ -0,0 +1,462 @@ +// Copyright 2024 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 eth + +import ( + "bytes" + "fmt" + "io" + "iter" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" +) + +// This is just a sanity limit for the size of a single receipt. +const maxReceiptSize = 16 * 1024 * 1024 + +// Receipt is the representation of receipts for networking purposes. +type Receipt struct { + TxType byte + PostStateOrStatus []byte + GasUsed uint64 + Logs rlp.RawValue +} + +func newReceipt(tr *types.Receipt) Receipt { + r := Receipt{TxType: tr.Type, GasUsed: tr.CumulativeGasUsed} + if tr.PostState != nil { + r.PostStateOrStatus = tr.PostState + } else { + r.PostStateOrStatus = new(big.Int).SetUint64(tr.Status).Bytes() + } + r.Logs, _ = rlp.EncodeToBytes(tr.Logs) + return r +} + +// decode68 parses a receipt in the eth/68 network encoding. +func (r *Receipt) decode68(buf *receiptListBuffers, s *rlp.Stream) error { + k, size, err := s.Kind() + if err != nil { + return err + } + + *r = Receipt{} + if k == rlp.List { + // Legacy receipt. + return r.decodeInnerList(s, false, true) + } + // Typed receipt. + if size < 2 || size > maxReceiptSize { + return fmt.Errorf("invalid receipt size %d", size) + } + buf.tmp.Reset() + buf.tmp.Grow(int(size)) + payload := buf.tmp.Bytes()[:int(size)] + if err := s.ReadBytes(payload); err != nil { + return err + } + r.TxType = payload[0] + s2 := rlp.NewStream(bytes.NewReader(payload[1:]), 0) + return r.decodeInnerList(s2, false, true) +} + +// decode69 parses a receipt in the eth/69 network encoding. +func (r *Receipt) decode69(s *rlp.Stream) error { + *r = Receipt{} + return r.decodeInnerList(s, true, false) +} + +// decodeDatabase parses a receipt in the basic database encoding. +func (r *Receipt) decodeDatabase(txType byte, s *rlp.Stream) error { + *r = Receipt{TxType: txType} + return r.decodeInnerList(s, false, false) +} + +func (r *Receipt) decodeInnerList(s *rlp.Stream, readTxType, readBloom bool) error { + _, err := s.List() + if err != nil { + return err + } + if readTxType { + r.TxType, err = s.Uint8() + if err != nil { + return fmt.Errorf("invalid txType: %w", err) + } + } + r.PostStateOrStatus, err = s.Bytes() + if err != nil { + return fmt.Errorf("invalid postStateOrStatus: %w", err) + } + r.GasUsed, err = s.Uint64() + if err != nil { + return fmt.Errorf("invalid gasUsed: %w", err) + } + if readBloom { + var b types.Bloom + if err := s.ReadBytes(b[:]); err != nil { + return fmt.Errorf("invalid bloom: %v", err) + } + } + r.Logs, err = s.Raw() + if err != nil { + return fmt.Errorf("invalid logs: %w", err) + } + return s.ListEnd() +} + +// encodeForStorage produces the the storage encoding, i.e. the result matches +// the RLP encoding of types.ReceiptForStorage. +func (r *Receipt) encodeForStorage(w *rlp.EncoderBuffer) { + list := w.List() + w.WriteBytes(r.PostStateOrStatus) + w.WriteUint64(r.GasUsed) + w.Write(r.Logs) + w.ListEnd(list) +} + +// encodeForNetwork68 produces the eth/68 network protocol encoding of a receipt. +// Note this recomputes the bloom filter of the receipt. +func (r *Receipt) encodeForNetwork68(buf *receiptListBuffers, w *rlp.EncoderBuffer) { + writeInner := func(w *rlp.EncoderBuffer) { + list := w.List() + w.WriteBytes(r.PostStateOrStatus) + w.WriteUint64(r.GasUsed) + bloom := r.bloom(&buf.bloom) + w.WriteBytes(bloom[:]) + w.Write(r.Logs) + w.ListEnd(list) + } + + if r.TxType == 0 { + writeInner(w) + } else { + buf.tmp.Reset() + buf.tmp.WriteByte(r.TxType) + buf.enc.Reset(&buf.tmp) + writeInner(&buf.enc) + buf.enc.Flush() + w.WriteBytes(buf.tmp.Bytes()) + } +} + +// encodeForNetwork69 produces the eth/69 network protocol encoding of a receipt. +func (r *Receipt) encodeForNetwork69(w *rlp.EncoderBuffer) { + list := w.List() + w.WriteUint64(uint64(r.TxType)) + w.WriteBytes(r.PostStateOrStatus) + w.WriteUint64(r.GasUsed) + w.Write(r.Logs) + w.ListEnd(list) +} + +// encodeForHash encodes a receipt for the block receiptsRoot derivation. +func (r *Receipt) encodeForHash(buf *receiptListBuffers, out *bytes.Buffer) { + // For typed receipts, add the tx type. + if r.TxType != 0 { + out.WriteByte(r.TxType) + } + // Encode list = [postStateOrStatus, gasUsed, bloom, logs]. + w := &buf.enc + w.Reset(out) + l := w.List() + w.WriteBytes(r.PostStateOrStatus) + w.WriteUint64(r.GasUsed) + bloom := r.bloom(&buf.bloom) + w.WriteBytes(bloom[:]) + w.Write(r.Logs) + w.ListEnd(l) + w.Flush() +} + +// bloom computes the bloom filter of the receipt. +// Note this doesn't check the validity of encoding, and will produce an invalid filter +// for invalid input. This is acceptable for the purpose of this function, which is +// recomputing the receipt hash. +func (r *Receipt) bloom(buffer *[6]byte) types.Bloom { + var b types.Bloom + logsIter, err := rlp.NewListIterator(r.Logs) + if err != nil { + return b + } + for logsIter.Next() { + log, _, _ := rlp.SplitList(logsIter.Value()) + address, log, _ := rlp.SplitString(log) + b.AddWithBuffer(address, buffer) + topicsIter, err := rlp.NewListIterator(log) + if err != nil { + return b + } + for topicsIter.Next() { + topic, _, _ := rlp.SplitString(topicsIter.Value()) + b.AddWithBuffer(topic, buffer) + } + } + return b +} + +type receiptListBuffers struct { + enc rlp.EncoderBuffer + bloom [6]byte + tmp bytes.Buffer +} + +func initBuffers(buf **receiptListBuffers) { + if *buf == nil { + *buf = new(receiptListBuffers) + } +} + +// encodeForStorage encodes a list of receipts for the database. +func (buf *receiptListBuffers) encodeForStorage(rs []Receipt) rlp.RawValue { + var out bytes.Buffer + w := &buf.enc + w.Reset(&out) + outer := w.List() + for _, receipts := range rs { + receipts.encodeForStorage(w) + } + w.ListEnd(outer) + w.Flush() + return out.Bytes() +} + +// ReceiptList68 is a block receipt list as downloaded by eth/68. +// This also implements types.DerivableList for validation purposes. +type ReceiptList68 struct { + buf *receiptListBuffers + items []Receipt +} + +// NewReceiptList68 creates a receipt list. +// This is slow, and exists for testing purposes. +func NewReceiptList68(trs []*types.Receipt) *ReceiptList68 { + rl := &ReceiptList68{items: make([]Receipt, len(trs))} + for i, tr := range trs { + rl.items[i] = newReceipt(tr) + } + return rl +} + +func blockReceiptsToNetwork68(blockReceipts, blockBody rlp.RawValue) ([]byte, error) { + txTypesIter, err := txTypesInBody(blockBody) + if err != nil { + return nil, fmt.Errorf("invalid block body: %v", err) + } + nextTxType, stopTxTypes := iter.Pull(txTypesIter) + defer stopTxTypes() + + var ( + out bytes.Buffer + buf receiptListBuffers + ) + blockReceiptIter, _ := rlp.NewListIterator(blockReceipts) + innerReader := bytes.NewReader(nil) + innerStream := rlp.NewStream(innerReader, 0) + w := rlp.NewEncoderBuffer(&out) + outer := w.List() + for i := 0; blockReceiptIter.Next(); i++ { + content := blockReceiptIter.Value() + innerReader.Reset(content) + innerStream.Reset(innerReader, uint64(len(content))) + var r Receipt + txType, _ := nextTxType() + if err := r.decodeDatabase(txType, innerStream); err != nil { + return nil, fmt.Errorf("invalid database receipt %d: %v", i, err) + } + r.encodeForNetwork68(&buf, &w) + } + w.ListEnd(outer) + w.Flush() + return out.Bytes(), nil +} + +// setBuffers implements ReceiptsList. +func (rl *ReceiptList68) setBuffers(buf *receiptListBuffers) { + rl.buf = buf +} + +// EncodeForStorage encodes the receipts for storage into the database. +func (rl *ReceiptList68) EncodeForStorage() rlp.RawValue { + initBuffers(&rl.buf) + return rl.buf.encodeForStorage(rl.items) +} + +// Len implements types.DerivableList. +func (rl *ReceiptList68) Len() int { + return len(rl.items) +} + +// EncodeIndex implements types.DerivableList. +func (rl *ReceiptList68) EncodeIndex(i int, out *bytes.Buffer) { + initBuffers(&rl.buf) + rl.items[i].encodeForHash(rl.buf, out) +} + +// DecodeRLP decodes a list of receipts from the network format. +func (rl *ReceiptList68) DecodeRLP(s *rlp.Stream) error { + initBuffers(&rl.buf) + if _, err := s.List(); err != nil { + return err + } + for i := 0; s.MoreDataInList(); i++ { + var item Receipt + err := item.decode68(rl.buf, s) + if err != nil { + return fmt.Errorf("receipt %d: %v", i, err) + } + rl.items = append(rl.items, item) + } + return s.ListEnd() +} + +// EncodeRLP encodes the list into the network format of eth/68. +func (rl *ReceiptList68) EncodeRLP(_w io.Writer) error { + initBuffers(&rl.buf) + w := rlp.NewEncoderBuffer(_w) + outer := w.List() + for i := range rl.items { + rl.items[i].encodeForNetwork68(rl.buf, &w) + } + w.ListEnd(outer) + return w.Flush() +} + +// ReceiptList69 is the block receipt list as downloaded by eth/69. +// This implements types.DerivableList for validation purposes. +type ReceiptList69 struct { + buf *receiptListBuffers + items []Receipt +} + +// NewReceiptList69 creates a receipt list. +// This is slow, and exists for testing purposes. +func NewReceiptList69(trs []*types.Receipt) *ReceiptList69 { + rl := &ReceiptList69{items: make([]Receipt, len(trs))} + for i, tr := range trs { + rl.items[i] = newReceipt(tr) + } + return rl +} + +// setBuffers implements ReceiptsList. +func (rl *ReceiptList69) setBuffers(buf *receiptListBuffers) { + rl.buf = buf +} + +// EncodeForStorage encodes the receipts for storage into the database. +func (rl *ReceiptList69) EncodeForStorage() rlp.RawValue { + initBuffers(&rl.buf) + return rl.buf.encodeForStorage(rl.items) +} + +// Len implements types.DerivableList. +func (rl *ReceiptList69) Len() int { + return len(rl.items) +} + +// EncodeIndex implements types.DerivableList. +func (rl *ReceiptList69) EncodeIndex(i int, out *bytes.Buffer) { + initBuffers(&rl.buf) + rl.items[i].encodeForHash(rl.buf, out) +} + +// DecodeRLP decodes a list receipts from the network format. +func (rl *ReceiptList69) DecodeRLP(s *rlp.Stream) error { + if _, err := s.List(); err != nil { + return err + } + for i := 0; s.MoreDataInList(); i++ { + var item Receipt + err := item.decode69(s) + if err != nil { + return fmt.Errorf("receipt %d: %v", i, err) + } + rl.items = append(rl.items, item) + } + return s.ListEnd() +} + +// EncodeRLP encodes the list into the network format of eth/69. +func (rl *ReceiptList69) EncodeRLP(_w io.Writer) error { + w := rlp.NewEncoderBuffer(_w) + outer := w.List() + for i := range rl.items { + rl.items[i].encodeForNetwork69(&w) + } + w.ListEnd(outer) + return w.Flush() +} + +// blockReceiptsToNetwork69 takes a slice of rlp-encoded receipts, and transactions, +// and applies the type-encoding on the receipts (for non-legacy receipts). +// e.g. for non-legacy receipts: receipt-data -> {tx-type || receipt-data} +func blockReceiptsToNetwork69(blockReceipts, blockBody rlp.RawValue) ([]byte, error) { + txTypesIter, err := txTypesInBody(blockBody) + if err != nil { + return nil, fmt.Errorf("invalid block body: %v", err) + } + nextTxType, stopTxTypes := iter.Pull(txTypesIter) + defer stopTxTypes() + + var ( + out bytes.Buffer + enc = rlp.NewEncoderBuffer(&out) + it, _ = rlp.NewListIterator(blockReceipts) + ) + outer := enc.List() + for i := 0; it.Next(); i++ { + txType, _ := nextTxType() + content, _, _ := rlp.SplitList(it.Value()) + receiptList := enc.List() + enc.WriteUint64(uint64(txType)) + enc.Write(content) + enc.ListEnd(receiptList) + } + enc.ListEnd(outer) + enc.Flush() + return out.Bytes(), nil +} + +// txTypesInBody parses the transactions list of an encoded block body, returning just the types. +func txTypesInBody(body rlp.RawValue) (iter.Seq[byte], error) { + bodyFields, _, err := rlp.SplitList(body) + if err != nil { + return nil, err + } + txsIter, err := rlp.NewListIterator(bodyFields) + if err != nil { + return nil, err + } + return func(yield func(byte) bool) { + for txsIter.Next() { + var txType byte + switch k, content, _, _ := rlp.Split(txsIter.Value()); k { + case rlp.List: + txType = 0 + case rlp.String: + if len(content) > 0 { + txType = content[0] + } + } + if !yield(txType) { + return + } + } + }, nil +} diff --git a/eth/protocols/eth/receipt_test.go b/eth/protocols/eth/receipt_test.go new file mode 100644 index 00000000000..3c73c07396d --- /dev/null +++ b/eth/protocols/eth/receipt_test.go @@ -0,0 +1,158 @@ +// Copyright 2024 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 eth + +import ( + "bytes" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +// miniDeriveFields derives the necessary receipt fields to make types.DeriveSha work. +func miniDeriveFields(r *types.Receipt, txType byte) { + r.Type = txType + r.Bloom = types.CreateBloom(r) +} + +var receiptsTestLogs1 = []*types.Log{{Address: common.Address{1}, Topics: []common.Hash{{1}}}} +var receiptsTestLogs2 = []*types.Log{ + {Address: common.Address{2}, Topics: []common.Hash{{21}, {22}}, Data: []byte{2, 2, 32, 32}}, + {Address: common.Address{3}, Topics: []common.Hash{{31}, {32}}, Data: []byte{3, 3, 32, 32}}, +} + +var receiptsTests = []struct { + input []types.ReceiptForStorage + txs []*types.Transaction + root common.Hash +}{ + { + input: []types.ReceiptForStorage{{CumulativeGasUsed: 555, Status: 1, Logs: nil}}, + txs: []*types.Transaction{types.NewTx(&types.LegacyTx{})}, + }, + { + input: []types.ReceiptForStorage{{CumulativeGasUsed: 555, Status: 1, Logs: nil}}, + txs: []*types.Transaction{types.NewTx(&types.DynamicFeeTx{})}, + }, + { + input: []types.ReceiptForStorage{{CumulativeGasUsed: 555, Status: 1, Logs: nil}}, + txs: []*types.Transaction{types.NewTx(&types.AccessListTx{})}, + }, + { + input: []types.ReceiptForStorage{{CumulativeGasUsed: 555, Status: 1, Logs: receiptsTestLogs1}}, + txs: []*types.Transaction{types.NewTx(&types.LegacyTx{})}, + }, + { + input: []types.ReceiptForStorage{{CumulativeGasUsed: 555, Status: 1, Logs: receiptsTestLogs2}}, + txs: []*types.Transaction{types.NewTx(&types.AccessListTx{})}, + }, +} + +func init() { + for i := range receiptsTests { + // derive basic fields + for j := range receiptsTests[i].input { + r := (*types.Receipt)(&receiptsTests[i].input[j]) + txType := receiptsTests[i].txs[j].Type() + miniDeriveFields(r, txType) + } + // compute expected root + receipts := make(types.Receipts, len(receiptsTests[i].input)) + for j, sr := range receiptsTests[i].input { + r := types.Receipt(sr) + receipts[j] = &r + } + receiptsTests[i].root = types.DeriveSha(receipts, trie.NewStackTrie(nil)) + } +} + +func TestReceiptList69(t *testing.T) { + for i, test := range receiptsTests { + // encode receipts from types.ReceiptForStorage object. + canonDB, _ := rlp.EncodeToBytes(test.input) + + // encode block body from types object. + blockBody := types.Body{Transactions: test.txs} + canonBody, _ := rlp.EncodeToBytes(blockBody) + + // convert from storage encoding to network encoding + network, err := blockReceiptsToNetwork69(canonDB, canonBody) + if err != nil { + t.Fatalf("test[%d]: blockReceiptsToNetwork69 error: %v", i, err) + } + + // parse as Receipts response list from network encoding + var rl ReceiptList69 + if err := rlp.DecodeBytes(network, &rl); err != nil { + t.Fatalf("test[%d]: can't decode network receipts: %v", i, err) + } + rlStorageEnc := rl.EncodeForStorage() + if !bytes.Equal(rlStorageEnc, canonDB) { + t.Fatalf("test[%d]: re-encoded receipts not equal\nhave: %x\nwant: %x", i, rlStorageEnc, canonDB) + } + rlNetworkEnc, _ := rlp.EncodeToBytes(&rl) + if !bytes.Equal(rlNetworkEnc, network) { + t.Fatalf("test[%d]: re-encoded network receipt list not equal\nhave: %x\nwant: %x", i, rlNetworkEnc, network) + } + + // compute root hash from ReceiptList69 and compare. + responseHash := types.DeriveSha(&rl, trie.NewStackTrie(nil)) + if responseHash != test.root { + t.Fatalf("test[%d]: wrong root hash from ReceiptList69\nhave: %v\nwant: %v", i, responseHash, test.root) + } + } +} + +func TestReceiptList68(t *testing.T) { + for i, test := range receiptsTests { + // encode receipts from types.ReceiptForStorage object. + canonDB, _ := rlp.EncodeToBytes(test.input) + + // encode block body from types object. + blockBody := types.Body{Transactions: test.txs} + canonBody, _ := rlp.EncodeToBytes(blockBody) + + // convert from storage encoding to network encoding + network, err := blockReceiptsToNetwork68(canonDB, canonBody) + if err != nil { + t.Fatalf("test[%d]: blockReceiptsToNetwork68 error: %v", i, err) + } + + // parse as Receipts response list from network encoding + var rl ReceiptList68 + if err := rlp.DecodeBytes(network, &rl); err != nil { + t.Fatalf("test[%d]: can't decode network receipts: %v", i, err) + } + rlStorageEnc := rl.EncodeForStorage() + if !bytes.Equal(rlStorageEnc, canonDB) { + t.Fatalf("test[%d]: re-encoded receipts not equal\nhave: %x\nwant: %x", i, rlStorageEnc, canonDB) + } + rlNetworkEnc, _ := rlp.EncodeToBytes(&rl) + if !bytes.Equal(rlNetworkEnc, network) { + t.Fatalf("test[%d]: re-encoded network receipt list not equal\nhave: %x\nwant: %x", i, rlNetworkEnc, network) + } + + // compute root hash from ReceiptList68 and compare. + responseHash := types.DeriveSha(&rl, trie.NewStackTrie(nil)) + if responseHash != test.root { + t.Fatalf("test[%d]: wrong root hash from ReceiptList68\nhave: %v\nwant: %v", i, responseHash, test.root) + } + } +} diff --git a/eth/protocols/eth/tracker.go b/eth/protocols/eth/tracker.go deleted file mode 100644 index 324fd22839c..00000000000 --- a/eth/protocols/eth/tracker.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2021 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 eth - -import ( - "time" - - "github.com/ethereum/go-ethereum/p2p/tracker" -) - -// requestTracker is a singleton tracker for eth/66 and newer request times. -var requestTracker = tracker.New(ProtocolName, 5*time.Minute) From c8be0f9a74fdabe5f82fa5b647e9973c9c3567ef Mon Sep 17 00:00:00 2001 From: maskpp Date: Sat, 17 May 2025 01:35:17 +0800 Subject: [PATCH 181/658] eth: use headers in debug.GetModifiedAccountsBy* (#31765) Small optimization in debug_getModifiedAccountsBy* to avoid fetching block body. --- eth/api_debug.go | 50 +++++++++---------- eth/api_debug_test.go | 112 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 25 deletions(-) diff --git a/eth/api_debug.go b/eth/api_debug.go index d5e4dda1401..188dee11aa5 100644 --- a/eth/api_debug.go +++ b/eth/api_debug.go @@ -271,26 +271,26 @@ func storageRangeAt(statedb *state.StateDB, root common.Hash, address common.Add // // With one parameter, returns the list of accounts modified in the specified block. func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) { - var startBlock, endBlock *types.Block + var startHeader, endHeader *types.Header - startBlock = api.eth.blockchain.GetBlockByNumber(startNum) - if startBlock == nil { + startHeader = api.eth.blockchain.GetHeaderByNumber(startNum) + if startHeader == nil { return nil, fmt.Errorf("start block %x not found", startNum) } if endNum == nil { - endBlock = startBlock - startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash()) - if startBlock == nil { - return nil, fmt.Errorf("block %x has no parent", endBlock.Number()) + endHeader = startHeader + startHeader = api.eth.blockchain.GetHeaderByHash(startHeader.ParentHash) + if startHeader == nil { + return nil, fmt.Errorf("block %x has no parent", endHeader.Number) } } else { - endBlock = api.eth.blockchain.GetBlockByNumber(*endNum) - if endBlock == nil { + endHeader = api.eth.blockchain.GetHeaderByNumber(*endNum) + if endHeader == nil { return nil, fmt.Errorf("end block %d not found", *endNum) } } - return api.getModifiedAccounts(startBlock, endBlock) + return api.getModifiedAccounts(startHeader, endHeader) } // GetModifiedAccountsByHash returns all accounts that have changed between the @@ -299,38 +299,38 @@ func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64 // // With one parameter, returns the list of accounts modified in the specified block. func (api *DebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) { - var startBlock, endBlock *types.Block - startBlock = api.eth.blockchain.GetBlockByHash(startHash) - if startBlock == nil { + var startHeader, endHeader *types.Header + startHeader = api.eth.blockchain.GetHeaderByHash(startHash) + if startHeader == nil { return nil, fmt.Errorf("start block %x not found", startHash) } if endHash == nil { - endBlock = startBlock - startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash()) - if startBlock == nil { - return nil, fmt.Errorf("block %x has no parent", endBlock.Number()) + endHeader = startHeader + startHeader = api.eth.blockchain.GetHeaderByHash(startHeader.ParentHash) + if startHeader == nil { + return nil, fmt.Errorf("block %x has no parent", endHeader.Number) } } else { - endBlock = api.eth.blockchain.GetBlockByHash(*endHash) - if endBlock == nil { + endHeader = api.eth.blockchain.GetHeaderByHash(*endHash) + if endHeader == nil { return nil, fmt.Errorf("end block %x not found", *endHash) } } - return api.getModifiedAccounts(startBlock, endBlock) + return api.getModifiedAccounts(startHeader, endHeader) } -func (api *DebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]common.Address, error) { - if startBlock.Number().Uint64() >= endBlock.Number().Uint64() { - return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startBlock.Number().Uint64(), endBlock.Number().Uint64()) +func (api *DebugAPI) getModifiedAccounts(startHeader, endHeader *types.Header) ([]common.Address, error) { + if startHeader.Number.Uint64() >= endHeader.Number.Uint64() { + return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startHeader.Number.Uint64(), endHeader.Number.Uint64()) } triedb := api.eth.BlockChain().TrieDB() - oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startBlock.Root()), triedb) + oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startHeader.Root), triedb) if err != nil { return nil, err } - newTrie, err := trie.NewStateTrie(trie.StateTrieID(endBlock.Root()), triedb) + newTrie, err := trie.NewStateTrie(trie.StateTrieID(endHeader.Root), triedb) if err != nil { return nil, err } diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go index 02b85f69fde..90fd22498a2 100644 --- a/eth/api_debug_test.go +++ b/eth/api_debug_test.go @@ -18,25 +18,74 @@ package eth import ( "bytes" + "crypto/ecdsa" "fmt" + "math/big" "reflect" "slices" "strings" "testing" + "time" "github.com/davecgh/go-spew/spew" "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/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/triedb" "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" ) var dumper = spew.ConfigState{Indent: " "} +type Account struct { + key *ecdsa.PrivateKey + addr common.Address +} + +func newAccounts(n int) (accounts []Account) { + for i := 0; i < n; i++ { + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + accounts = append(accounts, Account{key: key, addr: addr}) + } + slices.SortFunc(accounts, func(a, b Account) int { return a.addr.Cmp(b.addr) }) + return accounts +} + +// newTestBlockChain creates a new test blockchain. OBS: After test is done, teardown must be +// invoked in order to release associated resources. +func newTestBlockChain(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *core.BlockChain { + engine := ethash.NewFaker() + // Generate blocks for testing + _, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, n, generator) + + // Import the canonical chain + cacheConfig := &core.CacheConfig{ + TrieCleanLimit: 256, + TrieDirtyLimit: 256, + TrieTimeLimit: 5 * time.Minute, + SnapshotLimit: 0, + Preimages: true, + TrieDirtyDisabled: true, // Archive mode + } + chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), cacheConfig, gspec, nil, engine, vm.Config{}, nil) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + if n, err := chain.InsertChain(blocks); err != nil { + t.Fatalf("block %d: failed to insert into chain: %v", n, err) + } + return chain +} + func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, start common.Hash, requestedNum int, expectedNum int) state.Dump { result := statedb.RawDump(&state.DumpConfig{ SkipCode: true, @@ -224,3 +273,66 @@ func TestStorageRangeAt(t *testing.T) { } } } + +func TestGetModifiedAccounts(t *testing.T) { + t.Parallel() + + // Initialize test accounts + accounts := newAccounts(4) + genesis := &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{ + accounts[0].addr: {Balance: big.NewInt(params.Ether)}, + accounts[1].addr: {Balance: big.NewInt(params.Ether)}, + accounts[2].addr: {Balance: big.NewInt(params.Ether)}, + accounts[3].addr: {Balance: big.NewInt(params.Ether)}, + }, + } + genBlocks := 1 + signer := types.HomesteadSigner{} + blockChain := newTestBlockChain(t, genBlocks, genesis, func(_ int, b *core.BlockGen) { + // Transfer from account[0] to account[1] + // value: 1000 wei + // fee: 0 wei + for _, account := range accounts[:3] { + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: 0, + To: &accounts[3].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, account.key) + b.AddTx(tx) + } + }) + defer blockChain.Stop() + + // Create a debug API instance. + api := NewDebugAPI(&Ethereum{blockchain: blockChain}) + + // Test GetModifiedAccountsByNumber + t.Run("GetModifiedAccountsByNumber", func(t *testing.T) { + addrs, err := api.GetModifiedAccountsByNumber(uint64(genBlocks), nil) + assert.NoError(t, err) + assert.Len(t, addrs, len(accounts)+1) // +1 for the coinbase + for _, account := range accounts { + if !slices.Contains(addrs, account.addr) { + t.Fatalf("account %s not found in modified accounts", account.addr.Hex()) + } + } + }) + + // Test GetModifiedAccountsByHash + t.Run("GetModifiedAccountsByHash", func(t *testing.T) { + header := blockChain.GetHeaderByNumber(uint64(genBlocks)) + addrs, err := api.GetModifiedAccountsByHash(header.Hash(), nil) + assert.NoError(t, err) + assert.Len(t, addrs, len(accounts)+1) // +1 for the coinbase + for _, account := range accounts { + if !slices.Contains(addrs, account.addr) { + t.Fatalf("account %s not found in modified accounts", account.addr.Hex()) + } + } + }) +} From 85ae3e16f1a3456c158ba0109dad07502c4046e9 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sun, 18 May 2025 10:25:16 +0200 Subject: [PATCH 182/658] cmd/devp2p/internal/ethtest: tests for BlockRangeUpdate (#31843) I added a test for BlockRangeUpdate in #29158 but forgot to enable it. Here I'm adding two more tests for it. Also applied a small refactoring to combine calls to `dial()` and `peer()` into a single function, since these two calls are duplicated in each test. --- cmd/devp2p/internal/ethtest/conn.go | 12 ++ cmd/devp2p/internal/ethtest/suite.go | 158 +++++++++++++++++---------- 2 files changed, 110 insertions(+), 60 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/conn.go b/cmd/devp2p/internal/ethtest/conn.go index 6d8e0f1f7e6..4a7a2c76d8f 100644 --- a/cmd/devp2p/internal/ethtest/conn.go +++ b/cmd/devp2p/internal/ethtest/conn.go @@ -228,6 +228,18 @@ func (c *Conn) ReadSnap() (any, error) { } } +// dialAndPeer creates a peer connection and runs the handshake. +func (s *Suite) dialAndPeer(status *eth.StatusPacket69) (*Conn, error) { + c, err := s.dial() + if err != nil { + return nil, err + } + if err = c.peer(s.chain, status); err != nil { + c.Close() + } + return c, err +} + // peer performs both the protocol handshake and the status message // exchange with the node in order to peer with it. func (c *Conn) peer(chain *Chain, status *eth.StatusPacket69) error { diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index b90ecf3ca30..97e8fd84faf 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -68,6 +68,10 @@ func (s *Suite) EthTests() []utesting.Test { return []utesting.Test{ // status {Name: "Status", Fn: s.TestStatus}, + {Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake}, + {Name: "BlockRangeUpdateExpired", Fn: s.TestBlockRangeUpdateHistoryExp}, + {Name: "BlockRangeUpdateFuture", Fn: s.TestBlockRangeUpdateFuture}, + {Name: "BlockRangeUpdateInvalid", Fn: s.TestBlockRangeUpdateInvalid}, // get block headers {Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders}, {Name: "GetNonexistentBlockHeaders", Fn: s.TestGetNonexistentBlockHeaders}, @@ -77,8 +81,6 @@ func (s *Suite) EthTests() []utesting.Test { // get history {Name: "GetBlockBodies", Fn: s.TestGetBlockBodies}, {Name: "GetReceipts", Fn: s.TestGetReceipts}, - // // malicious handshakes + status - {Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake}, // test transactions {Name: "LargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true}, {Name: "Transaction", Fn: s.TestTransaction}, @@ -102,15 +104,11 @@ func (s *Suite) SnapTests() []utesting.Test { func (s *Suite) TestStatus(t *utesting.T) { t.Log(`This test is just a sanity check. It performs an eth protocol handshake.`) - - conn, err := s.dial() + conn, err := s.dialAndPeer(nil) if err != nil { - t.Fatalf("dial failed: %v", err) - } - defer conn.Close() - if err := conn.peer(s.chain, nil); err != nil { - t.Fatalf("peering failed: %v", err) + t.Fatal("peering failed:", err) } + conn.Close() } // headersMatch returns whether the received headers match the given request @@ -120,15 +118,12 @@ func headersMatch(expected []*types.Header, headers []*types.Header) bool { func (s *Suite) TestGetBlockHeaders(t *utesting.T) { t.Log(`This test requests block headers from the node.`) - - conn, err := s.dial() + conn, err := s.dialAndPeer(nil) if err != nil { - t.Fatalf("dial failed: %v", err) - } - defer conn.Close() - if err = conn.peer(s.chain, nil); err != nil { t.Fatalf("peering failed: %v", err) } + defer conn.Close() + // Send headers request. req := ð.GetBlockHeadersPacket{ RequestId: 33, @@ -161,18 +156,13 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) { } func (s *Suite) TestGetNonexistentBlockHeaders(t *utesting.T) { - t.Log(`This test sends GetBlockHeaders requests for nonexistent blocks (using max uint64 value) + t.Log(`This test sends GetBlockHeaders requests for nonexistent blocks (using max uint64 value) to check if the node disconnects after receiving multiple invalid requests.`) - - conn, err := s.dial() + conn, err := s.dialAndPeer(nil) if err != nil { - t.Fatalf("dial failed: %v", err) - } - defer conn.Close() - - if err := conn.peer(s.chain, nil); err != nil { t.Fatalf("peering failed: %v", err) } + defer conn.Close() // Create request with max uint64 value for a nonexistent block badReq := ð.GetBlockHeadersPacket{ @@ -205,15 +195,11 @@ to check if the node disconnects after receiving multiple invalid requests.`) func (s *Suite) TestSimultaneousRequests(t *utesting.T) { t.Log(`This test requests blocks headers from the node, performing two requests concurrently, with different request IDs.`) - - conn, err := s.dial() + conn, err := s.dialAndPeer(nil) if err != nil { - t.Fatalf("dial failed: %v", err) - } - defer conn.Close() - if err := conn.peer(s.chain, nil); err != nil { t.Fatalf("peering failed: %v", err) } + defer conn.Close() // Create two different requests. req1 := ð.GetBlockHeadersPacket{ @@ -279,15 +265,11 @@ concurrently, with different request IDs.`) func (s *Suite) TestSameRequestID(t *utesting.T) { t.Log(`This test requests block headers, performing two concurrent requests with the same request ID. The node should handle the request by responding to both requests.`) - - conn, err := s.dial() + conn, err := s.dialAndPeer(nil) if err != nil { - t.Fatalf("dial failed: %v", err) - } - defer conn.Close() - if err := conn.peer(s.chain, nil); err != nil { t.Fatalf("peering failed: %v", err) } + defer conn.Close() // Create two different requests with the same ID. reqID := uint64(1234) @@ -350,15 +332,12 @@ same request ID. The node should handle the request by responding to both reques func (s *Suite) TestZeroRequestID(t *utesting.T) { t.Log(`This test sends a GetBlockHeaders message with a request-id of zero, and expects a response.`) - - conn, err := s.dial() + conn, err := s.dialAndPeer(nil) if err != nil { - t.Fatalf("dial failed: %v", err) - } - defer conn.Close() - if err := conn.peer(s.chain, nil); err != nil { t.Fatalf("peering failed: %v", err) } + defer conn.Close() + req := ð.GetBlockHeadersPacket{ GetBlockHeadersRequest: ð.GetBlockHeadersRequest{ Origin: eth.HashOrNumber{Number: 0}, @@ -385,15 +364,12 @@ and expects a response.`) func (s *Suite) TestGetBlockBodies(t *utesting.T) { t.Log(`This test sends GetBlockBodies requests to the node for known blocks in the test chain.`) - - conn, err := s.dial() + conn, err := s.dialAndPeer(nil) if err != nil { - t.Fatalf("dial failed: %v", err) - } - defer conn.Close() - if err := conn.peer(s.chain, nil); err != nil { t.Fatalf("peering failed: %v", err) } + defer conn.Close() + // Create block bodies request. req := ð.GetBlockBodiesPacket{ RequestId: 55, @@ -421,15 +397,11 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { func (s *Suite) TestGetReceipts(t *utesting.T) { t.Log(`This test sends GetReceipts requests to the node for known blocks in the test chain.`) - - conn, err := s.dial() + conn, err := s.dialAndPeer(nil) if err != nil { - t.Fatalf("dial failed: %v", err) - } - defer conn.Close() - if err := conn.peer(s.chain, nil); err != nil { t.Fatalf("peering failed: %v", err) } + defer conn.Close() // Find some blocks containing receipts. var hashes = make([]common.Hash, 0, 3) @@ -546,17 +518,13 @@ func (s *Suite) TestMaliciousHandshake(t *utesting.T) { } } -func (s *Suite) TestInvalidBlockRangeUpdate(t *utesting.T) { +func (s *Suite) TestBlockRangeUpdateInvalid(t *utesting.T) { t.Log(`This test sends an invalid BlockRangeUpdate message to the node and expects to be disconnected.`) - - conn, err := s.dial() + conn, err := s.dialAndPeer(nil) if err != nil { - t.Fatalf("dial failed: %v", err) + t.Fatal(err) } defer conn.Close() - if err := conn.peer(s.chain, nil); err != nil { - t.Fatalf("peering failed: %v", err) - } conn.Write(ethProto, eth.BlockRangeUpdateMsg, ð.BlockRangeUpdatePacket{ EarliestBlock: 10, @@ -571,6 +539,76 @@ func (s *Suite) TestInvalidBlockRangeUpdate(t *utesting.T) { } } +func (s *Suite) TestBlockRangeUpdateFuture(t *utesting.T) { + t.Log(`This test sends a BlockRangeUpdate that is beyond the chain head. +The node should accept the update and should not disonnect.`) + conn, err := s.dialAndPeer(nil) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + head := s.chain.Head().NumberU64() + var hash common.Hash + rand.Read(hash[:]) + conn.Write(ethProto, eth.BlockRangeUpdateMsg, ð.BlockRangeUpdatePacket{ + EarliestBlock: head + 10, + LatestBlock: head + 50, + LatestBlockHash: hash, + }) + + // Ensure the node does not disconnect us. + // Just send a few ping messages. + for range 10 { + time.Sleep(100 * time.Millisecond) + if err := conn.Write(baseProto, pingMsg, []any{}); err != nil { + t.Fatal("write error:", err) + } + code, _, err := conn.Read() + switch { + case err != nil: + t.Fatal("read error:", err) + case code == discMsg: + t.Fatal("got disconnect") + case code == pongMsg: + } + } +} + +func (s *Suite) TestBlockRangeUpdateHistoryExp(t *utesting.T) { + t.Log(`This test sends a BlockRangeUpdate announcing incomplete (expired) history. +The node should accept the update and should not disonnect.`) + conn, err := s.dialAndPeer(nil) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + head := s.chain.Head() + conn.Write(ethProto, eth.BlockRangeUpdateMsg, ð.BlockRangeUpdatePacket{ + EarliestBlock: head.NumberU64() - 10, + LatestBlock: head.NumberU64(), + LatestBlockHash: head.Hash(), + }) + + // Ensure the node does not disconnect us. + // Just send a few ping messages. + for range 10 { + time.Sleep(100 * time.Millisecond) + if err := conn.Write(baseProto, pingMsg, []any{}); err != nil { + t.Fatal("write error:", err) + } + code, _, err := conn.Read() + switch { + case err != nil: + t.Fatal("read error:", err) + case code == discMsg: + t.Fatal("got disconnect") + case code == pongMsg: + } + } +} + func (s *Suite) TestTransaction(t *utesting.T) { t.Log(`This test sends a valid transaction to the node and checks if the transaction gets propagated.`) From 15057e7f7fbcb6b22f2f0a7e740b65db2959b78b Mon Sep 17 00:00:00 2001 From: Zhou Date: Mon, 19 May 2025 03:59:35 +0200 Subject: [PATCH 183/658] core: don't emit the warning of log indexing if the db was not initialized (#31845) --- core/filtermaps/filtermaps.go | 2 +- core/rawdb/accessors_indexes.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/filtermaps/filtermaps.go b/core/filtermaps/filtermaps.go index b2c63f2e581..667d7c76239 100644 --- a/core/filtermaps/filtermaps.go +++ b/core/filtermaps/filtermaps.go @@ -227,7 +227,7 @@ type Config struct { // NewFilterMaps creates a new FilterMaps and starts the indexer. func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, finalBlock uint64, params Params, config Config) *FilterMaps { rs, initialized, err := rawdb.ReadFilterMapsRange(db) - if err != nil || rs.Version != databaseVersion { + if err != nil || (initialized && rs.Version != databaseVersion) { rs, initialized = rawdb.FilterMapsRange{}, false log.Warn("Invalid log index database version; resetting log index") } diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go index 1a5c414c8e9..fafde25606b 100644 --- a/core/rawdb/accessors_indexes.go +++ b/core/rawdb/accessors_indexes.go @@ -446,7 +446,7 @@ type FilterMapsRange struct { // database entry is not present, that is interpreted as a valid non-initialized // state and returns a blank range structure and no error. func ReadFilterMapsRange(db ethdb.KeyValueReader) (FilterMapsRange, bool, error) { - if has, err := db.Has(filterMapsRangeKey); !has || err != nil { + if has, err := db.Has(filterMapsRangeKey); err != nil || !has { return FilterMapsRange{}, false, err } encRange, err := db.Get(filterMapsRangeKey) @@ -457,7 +457,8 @@ func ReadFilterMapsRange(db ethdb.KeyValueReader) (FilterMapsRange, bool, error) if err := rlp.DecodeBytes(encRange, &fmRange); err != nil { return FilterMapsRange{}, false, err } - return fmRange, true, err + + return fmRange, true, nil } // WriteFilterMapsRange stores the filter maps range data. From 0867c24662f5c954c60b7ebdb3e32fc44e9a1bb4 Mon Sep 17 00:00:00 2001 From: maskpp Date: Mon, 19 May 2025 10:03:59 +0800 Subject: [PATCH 184/658] trie: use common.Hash as the key in secKeyCache map (#31786) --- trie/secure_trie.go | 60 +++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/trie/secure_trie.go b/trie/secure_trie.go index 45d5fd63e7c..7852e16619c 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -19,6 +19,7 @@ package trie import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/triedb/database" @@ -63,8 +64,7 @@ type StateTrie struct { trie Trie db database.NodeDatabase preimages preimageStore - hashKeyBuf [common.HashLength]byte - secKeyCache map[string][]byte + secKeyCache map[common.Hash][]byte secKeyCacheOwner *StateTrie // Pointer to self, replace the key cache on mismatch } @@ -97,7 +97,7 @@ func NewStateTrie(id *ID, db database.NodeDatabase) (*StateTrie, error) { // This function will omit any encountered error but just // print out an error message. func (t *StateTrie) MustGet(key []byte) []byte { - return t.trie.MustGet(t.hashKey(key)) + return t.trie.MustGet(crypto.Keccak256(key)) } // GetStorage attempts to retrieve a storage slot with provided account address @@ -105,7 +105,7 @@ func (t *StateTrie) MustGet(key []byte) []byte { // If the specified storage slot is not in the trie, nil will be returned. // If a trie node is not found in the database, a MissingNodeError is returned. func (t *StateTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) { - enc, err := t.trie.Get(t.hashKey(key)) + enc, err := t.trie.Get(crypto.Keccak256(key)) if err != nil || len(enc) == 0 { return nil, err } @@ -117,7 +117,7 @@ func (t *StateTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) { // If the specified account is not in the trie, nil will be returned. // If a trie node is not found in the database, a MissingNodeError is returned. func (t *StateTrie) GetAccount(address common.Address) (*types.StateAccount, error) { - res, err := t.trie.Get(t.hashKey(address.Bytes())) + res, err := t.trie.Get(crypto.Keccak256(address.Bytes())) if res == nil || err != nil { return nil, err } @@ -157,9 +157,9 @@ func (t *StateTrie) GetNode(path []byte) ([]byte, int, error) { // This function will omit any encountered error but just print out an // error message. func (t *StateTrie) MustUpdate(key, value []byte) { - hk := t.hashKey(key) + hk := crypto.Keccak256(key) t.trie.MustUpdate(hk, value) - t.getSecKeyCache()[string(hk)] = common.CopyBytes(key) + t.getSecKeyCache()[common.Hash(hk)] = common.CopyBytes(key) } // UpdateStorage associates key with value in the trie. Subsequent calls to @@ -171,19 +171,19 @@ func (t *StateTrie) MustUpdate(key, value []byte) { // // If a node is not found in the database, a MissingNodeError is returned. func (t *StateTrie) UpdateStorage(_ common.Address, key, value []byte) error { - hk := t.hashKey(key) + hk := crypto.Keccak256(key) v, _ := rlp.EncodeToBytes(value) err := t.trie.Update(hk, v) if err != nil { return err } - t.getSecKeyCache()[string(hk)] = common.CopyBytes(key) + t.getSecKeyCache()[common.Hash(hk)] = common.CopyBytes(key) return nil } // UpdateAccount will abstract the write of an account to the secure trie. func (t *StateTrie) UpdateAccount(address common.Address, acc *types.StateAccount, _ int) error { - hk := t.hashKey(address.Bytes()) + hk := crypto.Keccak256(address.Bytes()) data, err := rlp.EncodeToBytes(acc) if err != nil { return err @@ -191,7 +191,7 @@ func (t *StateTrie) UpdateAccount(address common.Address, acc *types.StateAccoun if err := t.trie.Update(hk, data); err != nil { return err } - t.getSecKeyCache()[string(hk)] = address.Bytes() + t.getSecKeyCache()[common.Hash(hk)] = address.Bytes() return nil } @@ -202,8 +202,8 @@ func (t *StateTrie) UpdateContractCode(_ common.Address, _ common.Hash, _ []byte // MustDelete removes any existing value for key from the trie. This function // will omit any encountered error but just print out an error message. func (t *StateTrie) MustDelete(key []byte) { - hk := t.hashKey(key) - delete(t.getSecKeyCache(), string(hk)) + hk := crypto.Keccak256(key) + delete(t.getSecKeyCache(), common.Hash(hk)) t.trie.MustDelete(hk) } @@ -211,22 +211,22 @@ func (t *StateTrie) MustDelete(key []byte) { // If the specified trie node is not in the trie, nothing will be changed. // If a node is not found in the database, a MissingNodeError is returned. func (t *StateTrie) DeleteStorage(_ common.Address, key []byte) error { - hk := t.hashKey(key) - delete(t.getSecKeyCache(), string(hk)) + hk := crypto.Keccak256(key) + delete(t.getSecKeyCache(), common.Hash(hk)) return t.trie.Delete(hk) } // DeleteAccount abstracts an account deletion from the trie. func (t *StateTrie) DeleteAccount(address common.Address) error { - hk := t.hashKey(address.Bytes()) - delete(t.getSecKeyCache(), string(hk)) + hk := crypto.Keccak256(address.Bytes()) + delete(t.getSecKeyCache(), common.Hash(hk)) return t.trie.Delete(hk) } // GetKey returns the sha3 preimage of a hashed key that was // previously used to store a value. func (t *StateTrie) GetKey(shaKey []byte) []byte { - if key, ok := t.getSecKeyCache()[string(shaKey)]; ok { + if key, ok := t.getSecKeyCache()[common.BytesToHash(shaKey)]; ok { return key } if t.preimages == nil { @@ -251,13 +251,9 @@ func (t *StateTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet) { // Write all the pre-images to the actual disk database if len(t.getSecKeyCache()) > 0 { if t.preimages != nil { - preimages := make(map[common.Hash][]byte, len(t.secKeyCache)) - for hk, key := range t.secKeyCache { - preimages[common.BytesToHash([]byte(hk))] = key - } - t.preimages.InsertPreimage(preimages) + t.preimages.InsertPreimage(t.secKeyCache) } - t.secKeyCache = make(map[string][]byte) + t.secKeyCache = make(map[common.Hash][]byte) } // Commit the trie and return its modified nodeset. return t.trie.Commit(collectLeaf) @@ -291,25 +287,13 @@ func (t *StateTrie) MustNodeIterator(start []byte) NodeIterator { return t.trie.MustNodeIterator(start) } -// hashKey returns the hash of key as an ephemeral buffer. -// The caller must not hold onto the return value because it will become -// invalid on the next call to hashKey or secKey. -func (t *StateTrie) hashKey(key []byte) []byte { - h := newHasher(false) - h.sha.Reset() - h.sha.Write(key) - h.sha.Read(t.hashKeyBuf[:]) - returnHasherToPool(h) - return t.hashKeyBuf[:] -} - // getSecKeyCache returns the current secure key cache, creating a new one if // ownership changed (i.e. the current secure trie is a copy of another owning // the actual cache). -func (t *StateTrie) getSecKeyCache() map[string][]byte { +func (t *StateTrie) getSecKeyCache() map[common.Hash][]byte { if t != t.secKeyCacheOwner { t.secKeyCacheOwner = t - t.secKeyCache = make(map[string][]byte) + t.secKeyCache = make(map[common.Hash][]byte) } return t.secKeyCache } From e79177d1bca6bd21ed78bd8c2bf7144b36395c78 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Mon, 19 May 2025 12:56:12 +0200 Subject: [PATCH 185/658] cmd/utils: remove duplicate code for influxDB tags (#31854) remove duplicate code Signed-off-by: Csaba Kiraly --- cmd/utils/flags.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 169f5ed49eb..81c4172a53d 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2020,7 +2020,6 @@ func SetupMetrics(cfg *metrics.Config) { log.Info("Enabling metrics export to InfluxDB") go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", tagsMap) } else if enableExportV2 { - tagsMap := SplitTagsFlag(cfg.InfluxDBTags) log.Info("Enabling metrics export to InfluxDB (v2)") go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, token, bucket, organization, "geth.", tagsMap) } From 33c5031a2e6888dbac3dba0628c786442ceabc33 Mon Sep 17 00:00:00 2001 From: Zhou Date: Mon, 19 May 2025 10:55:17 -0300 Subject: [PATCH 186/658] internal: remove eth_{compile,getWork,submitWork} from console (#31856) The `compile` and `eth_getWork` `eth_submitWork` apis were not available anymore, so try to remove them --- internal/jsre/deps/web3.js | 37 +------------------------------------ 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js index a4141bac43c..0aa738a2af4 100644 --- a/internal/jsre/deps/web3.js +++ b/internal/jsre/deps/web3.js @@ -5435,36 +5435,6 @@ var methods = function () { outputFormatter: utils.toDecimal }); - var compileSolidity = new Method({ - name: 'compile.solidity', - call: 'eth_compileSolidity', - params: 1 - }); - - var compileLLL = new Method({ - name: 'compile.lll', - call: 'eth_compileLLL', - params: 1 - }); - - var compileSerpent = new Method({ - name: 'compile.serpent', - call: 'eth_compileSerpent', - params: 1 - }); - - var submitWork = new Method({ - name: 'submitWork', - call: 'eth_submitWork', - params: 3 - }); - - var getWork = new Method({ - name: 'getWork', - call: 'eth_getWork', - params: 0 - }); - return [ getBalance, getStorageAt, @@ -5483,12 +5453,7 @@ var methods = function () { sendRawTransaction, signTransaction, sendTransaction, - sign, - compileSolidity, - compileLLL, - compileSerpent, - submitWork, - getWork + sign ]; }; From a67ea0c57dd7b387fc49138f4bdc1590c91e5a51 Mon Sep 17 00:00:00 2001 From: James Niken Date: Tue, 20 May 2025 09:58:00 +0200 Subject: [PATCH 187/658] internal/reexec: fix broken link to Docker/Moby in comment (#31859) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hey team—noticed a dead link, replaced it with a working URL https://github.com/moby/moby/blob/master/pkg/reexec/reexec.go - old link https://github.com/moby/moby/blob/master/pkg/reexec/reexec_deprecated.go - new link --- internal/reexec/reexec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/reexec/reexec.go b/internal/reexec/reexec.go index 7dc6d9222e9..50c0f43ab80 100644 --- a/internal/reexec/reexec.go +++ b/internal/reexec/reexec.go @@ -1,5 +1,5 @@ // This file originates from Docker/Moby, -// https://github.com/moby/moby/blob/master/pkg/reexec/reexec.go +// https://github.com/moby/moby/blob/master/pkg/reexec/reexec_deprecated.go // Licensed under Apache License 2.0: https://github.com/moby/moby/blob/master/LICENSE // Copyright 2013-2018 Docker, Inc. // From 24771fdba4fb7b414bfd5491b182ac0d22dfb33f Mon Sep 17 00:00:00 2001 From: VolodymyrBg Date: Tue, 20 May 2025 11:04:37 +0300 Subject: [PATCH 188/658] README: update broken JSON-RPC API documentation link (#31860) I've updated the broken link to point to the current official Ethereum JSON-RPC API documentation at https://ethereum.org/en/developers/docs/apis/json-rpc/. This is the correct and up-to-date location for the Ethereum Execution Layer APIs documentation. The link should now work properly. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ccfe933aa6..78a56baecea 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ accessible from the outside. As a developer, sooner rather than later you'll want to start interacting with `geth` and the Ethereum network via your own programs and not manually through the console. To aid -this, `geth` has built-in support for a JSON-RPC based APIs ([standard APIs](https://ethereum.github.io/execution-apis/api-documentation/) +this, `geth` has built-in support for a JSON-RPC based APIs ([standard APIs](https://ethereum.org/en/developers/docs/apis/json-rpc/) and [`geth` specific APIs](https://geth.ethereum.org/docs/interacting-with-geth/rpc)). These can be exposed via HTTP, WebSockets and IPC (UNIX sockets on UNIX based platforms, and named pipes on Windows). From e4a8ecb9473dfbc809d3f413412df1d05a78607c Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 20 May 2025 20:57:01 +0800 Subject: [PATCH 189/658] core/txpool/legacypool: fix flaky test TestAllowedTxSize #30975 (#31836) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some tests involving transactions near the txMaxSize limit were flaky. This was due to ECDSA signatures occasionally having leading zeros, which are omitted during RLP encoding — making the final transaction size 1 byte smaller than expected. To address this, a new helper function pricedDataTransactionWithFixedSignature was added. It ensures both r and s are exactly 32 bytes (i.e., no leading zeros), producing transactions with deterministic size. --- core/txpool/legacypool/legacypool_test.go | 34 +++++++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index 2fdf8903203..1ba080b749e 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -108,11 +108,33 @@ func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ec return tx } -func pricedDataTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey, bytes uint64) *types.Transaction { - data := make([]byte, bytes) - crand.Read(data) - - tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(0), gaslimit, gasprice, data), types.HomesteadSigner{}, key) +// pricedDataTransaction generates a signed transaction with fixed-size data, +// and ensures that the resulting signature components (r and s) are exactly 32 bytes each, +// producing transactions with deterministic size. +// +// This avoids variability in transaction size caused by leading zeros being omitted in +// RLP encoding of r/s. Since r and s are derived from ECDSA, they occasionally have leading +// zeros and thus can be shorter than 32 bytes. +// +// For example: +// +// r: 0 leading zeros, bytesSize: 32, bytes: [221 ... 101] +// s: 1 leading zeros, bytesSize: 31, bytes: [0 75 ... 47] +func pricedDataTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey, dataBytes uint64) *types.Transaction { + var tx *types.Transaction + + // 10 attempts is statistically sufficient since leading zeros in ECDSA signatures are rare and randomly distributed. + var retryTimes = 10 + for i := 0; i < retryTimes; i++ { + data := make([]byte, dataBytes) + crand.Read(data) + + tx, _ = types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(0), gaslimit, gasprice, data), types.HomesteadSigner{}, key) + _, r, s := tx.RawSignatureValues() + if len(r.Bytes()) == 32 && len(s.Bytes()) == 32 { + break + } + } return tx } @@ -1239,7 +1261,7 @@ func TestAllowedTxSize(t *testing.T) { const largeDataLength = txMaxSize - 200 // enough to have a 5 bytes RLP encoding of the data length number txWithLargeData := pricedDataTransaction(0, pool.currentHead.Load().GasLimit, big.NewInt(1), key, largeDataLength) maxTxLengthWithoutData := txWithLargeData.Size() - largeDataLength // 103 bytes - maxTxDataLength := txMaxSize - maxTxLengthWithoutData // 131072 - 103 = 130953 bytes + maxTxDataLength := txMaxSize - maxTxLengthWithoutData // 131072 - 103 = 130969 bytes // Try adding a transaction with maximal allowed size tx := pricedDataTransaction(0, pool.currentHead.Load().GasLimit, big.NewInt(1), key, maxTxDataLength) From 62aa6b2621631f61c51e678948556750eabfc620 Mon Sep 17 00:00:00 2001 From: shahafn Date: Tue, 20 May 2025 16:38:33 +0300 Subject: [PATCH 190/658] eth/tracers/native: add erc7562 tracer (#31006) This PR introduces a new native tracer for AA bundlers. Bundlers participating in the alternative mempool will need to validate userops. This tracer will return sufficient information for them to decide whether griefing is possible. Resolves #30546 --------- Co-authored-by: Sina M <1591639+s1na@users.noreply.github.com> --- .../internal/tracetest/erc7562_tracer_test.go | 144 +++++ .../erc7562Tracer.test_deployer.json | 110 ++++ .../erc7562Tracer.test_paymaster.json | 110 ++++ .../erc7562Tracer.test_simple.json | 246 ++++++++ eth/tracers/native/erc7562.go | 530 ++++++++++++++++++ .../native/gen_callframewithopcodes_json.go | 159 ++++++ 6 files changed, 1299 insertions(+) create mode 100644 eth/tracers/internal/tracetest/erc7562_tracer_test.go create mode 100644 eth/tracers/internal/tracetest/testdata/erc7562_tracer/erc7562Tracer.test_deployer.json create mode 100644 eth/tracers/internal/tracetest/testdata/erc7562_tracer/erc7562Tracer.test_paymaster.json create mode 100644 eth/tracers/internal/tracetest/testdata/erc7562_tracer/erc7562Tracer.test_simple.json create mode 100644 eth/tracers/native/erc7562.go create mode 100644 eth/tracers/native/gen_callframewithopcodes_json.go diff --git a/eth/tracers/internal/tracetest/erc7562_tracer_test.go b/eth/tracers/internal/tracetest/erc7562_tracer_test.go new file mode 100644 index 00000000000..f6e81f5886f --- /dev/null +++ b/eth/tracers/internal/tracetest/erc7562_tracer_test.go @@ -0,0 +1,144 @@ +// 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 tracetest + +import ( + "encoding/json" + "math/big" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/tests" + "github.com/stretchr/testify/require" +) + +type accessedSlots struct { + Reads map[string][]string `json:"reads"` + Writes map[string]uint64 `json:"writes"` + TransientReads map[string]uint64 `json:"transientReads"` + TransientWrites map[string]uint64 `json:"transientWrites"` +} +type contractSizeWithOpcode struct { + ContractSize int `json:"contractSize"` + Opcode vm.OpCode `json:"opcode"` +} + +// erc7562Trace is the result of a erc7562Tracer run. +type erc7562Trace struct { + From common.Address `json:"from"` + Gas *hexutil.Uint64 `json:"gas"` + GasUsed *hexutil.Uint64 `json:"gasUsed"` + To *common.Address `json:"to,omitempty" rlp:"optional"` + Input hexutil.Bytes `json:"input" rlp:"optional"` + Output hexutil.Bytes `json:"output,omitempty" rlp:"optional"` + Error string `json:"error,omitempty" rlp:"optional"` + RevertReason string `json:"revertReason,omitempty"` + Logs []callLog `json:"logs,omitempty" rlp:"optional"` + Value *hexutil.Big `json:"value,omitempty" rlp:"optional"` + AccessedSlots accessedSlots `json:"accessedSlots"` + ExtCodeAccessInfo []common.Address `json:"extCodeAccessInfo"` + UsedOpcodes map[hexutil.Uint64]uint64 `json:"usedOpcodes"` + ContractSize map[common.Address]*contractSizeWithOpcode `json:"contractSize"` + OutOfGas bool `json:"outOfGas"` + Calls []erc7562Trace `json:"calls,omitempty" rlp:"optional"` + Keccak []hexutil.Bytes `json:"keccak,omitempty"` + Type string `json:"type"` +} + +// erc7562TracerTest defines a single test to check the erc7562 tracer against. +type erc7562TracerTest struct { + tracerTestEnv + Result *erc7562Trace `json:"result"` +} + +func TestErc7562Tracer(t *testing.T) { + dirPath := "erc7562_tracer" + tracerName := "erc7562Tracer" + files, err := os.ReadDir(filepath.Join("testdata", dirPath)) + if err != nil { + t.Fatalf("failed to retrieve tracer test suite: %v", err) + } + for _, file := range files { + if !strings.HasSuffix(file.Name(), ".json") { + continue + } + t.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(t *testing.T) { + t.Parallel() + + var ( + test = new(erc7562TracerTest) + tx = new(types.Transaction) + ) + // erc7562 tracer test found, read if from disk + if blob, err := os.ReadFile(filepath.Join("testdata", dirPath, file.Name())); err != nil { + t.Fatalf("failed to read testcase: %v", err) + } else if err := json.Unmarshal(blob, test); err != nil { + t.Fatalf("failed to parse testcase: %v", err) + } + if err := tx.UnmarshalBinary(common.FromHex(test.Input)); err != nil { + t.Fatalf("failed to parse testcase input: %v", err) + } + // Configure a blockchain with the given prestate + var ( + signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) + context = test.Context.toBlockContext(test.Genesis) + st = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) + ) + st.Close() + + tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config) + if err != nil { + t.Fatalf("failed to create erc7562 tracer: %v", err) + } + logState := vm.StateDB(st.StateDB) + if tracer.Hooks != nil { + logState = state.NewHookedState(st.StateDB, tracer.Hooks) + } + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) + if err != nil { + t.Fatalf("failed to prepare transaction for tracing: %v", err) + } + evm := vm.NewEVM(context, logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) + vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) + if err != nil { + t.Fatalf("failed to execute transaction: %v", err) + } + tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) + // Retrieve the trace result and compare against the expected. + res, err := tracer.GetResult() + if err != nil { + t.Fatalf("failed to retrieve trace result: %v", err) + } + want, err := json.Marshal(test.Result) + if err != nil { + t.Fatalf("failed to marshal test: %v", err) + } + require.JSONEq(t, string(res), string(want)) + }) + } +} diff --git a/eth/tracers/internal/tracetest/testdata/erc7562_tracer/erc7562Tracer.test_deployer.json b/eth/tracers/internal/tracetest/testdata/erc7562_tracer/erc7562Tracer.test_deployer.json new file mode 100644 index 00000000000..2d785a16551 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/erc7562_tracer/erc7562Tracer.test_deployer.json @@ -0,0 +1,110 @@ +{ + "genesis": { + "baseFeePerGas": "1664", + "blobGasUsed": "0", + "difficulty": "0", + "excessBlobGas": "0", + "extraData": "0xd883010e0d846765746888676f312e32322e36856c696e7578", + "gasLimit": "30000000", + "hash": "0xc567fbb11719be3bc47e77269365baef50b1fc0149f2a366a35f82ddba608b28", + "miner": "0x0000000000000000000000000000000000000000", + "mixHash": "0x792aa153a3b49ad9eb965809fedc3b11c343da37b1edebe101401711c63eb53c", + "nonce": "0x0000000000000000", + "number": "114", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stateRoot": "0xf18f522f27f64c34e56338820450991b6fcb7a2a311224d2bfba7afa5734888b", + "timestamp": "1738267577", + "withdrawals": [], + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "alloc": { + "0x0000000000000000000000000000000000000000": { + "balance": "0x285013864dbff82" + }, + "0x0000000071727de22e5e9d8baf0edac6f37da032": { + "balance": "0x49c2dbeb2e8d0f042", + "code": "0x60806040526004361015610024575b361561001957600080fd5b61002233612748565b005b60003560e01c806242dc5314611b0057806301ffc9a7146119ae5780630396cb60146116765780630bd28e3b146115fa5780631b2e01b814611566578063205c2878146113d157806322cdde4c1461136b57806335567e1a146112b35780635287ce12146111a557806370a0823114611140578063765e827f14610e82578063850aaf6214610dc35780639b249f6914610c74578063b760faf914610c3a578063bb9fe6bf14610a68578063c23a5cea146107c4578063dbed18e0146101a15763fc7e286d0361000e573461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff61013a61229f565b16600052600060205260a0604060002065ffffffffffff6001825492015460405192835260ff8116151560208401526dffffffffffffffffffffffffffff8160081c16604084015263ffffffff8160781c16606084015260981c166080820152f35b600080fd5b3461019c576101af36612317565b906101b86129bd565b60009160005b82811061056f57506101d08493612588565b6000805b8481106102fc5750507fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000809360005b81811061024757610240868660007f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d8180a2613ba7565b6001600255005b6102a261025582848a612796565b73ffffffffffffffffffffffffffffffffffffffff6102766020830161282a565b167f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d600080a2806127d6565b906000915b8083106102b957505050600101610209565b909194976102f36102ed6001926102e78c8b6102e0826102da8e8b8d61269d565b9261265a565b5191613597565b90612409565b99612416565b950191906102a7565b6020610309828789612796565b61031f61031682806127d6565b9390920161282a565b9160009273ffffffffffffffffffffffffffffffffffffffff8091165b8285106103505750505050506001016101d4565b909192939561037f83610378610366848c61265a565b516103728b898b61269d565b856129f6565b9290613dd7565b9116840361050a576104a5576103958491613dd7565b9116610440576103b5576103aa600191612416565b96019392919061033c565b60a487604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f65000000000000000000000000000000000000000000000000000000000000006084820152fd5b608488604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413334207369676e6174757265206572726f720000000000000000000000006064820152fd5b608488604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f414132322065787069726564206f72206e6f74206475650000000000000000006064820152fd5b608489604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413234207369676e6174757265206572726f720000000000000000000000006064820152fd5b61057a818487612796565b9361058585806127d6565b919095602073ffffffffffffffffffffffffffffffffffffffff6105aa82840161282a565b1697600192838a1461076657896105da575b5050505060019293949550906105d191612409565b939291016101be565b8060406105e892019061284b565b918a3b1561019c57929391906040519485937f2dd8113300000000000000000000000000000000000000000000000000000000855288604486016040600488015252606490818601918a60051b8701019680936000915b8c83106106e657505050505050838392610684927ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8560009803016024860152612709565b03818a5afa90816106d7575b506106c657602486604051907f86a9f7500000000000000000000000000000000000000000000000000000000082526004820152fd5b93945084936105d1600189806105bc565b6106e0906121bd565b88610690565b91939596977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c908a9294969a0301865288357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18336030181121561019c57836107538793858394016128ec565b9a0196019301909189979695949261063f565b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601760248201527f4141393620696e76616c69642061676772656761746f720000000000000000006044820152fd5b3461019c576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c576107fc61229f565b33600052600082526001604060002001908154916dffffffffffffffffffffffffffff8360081c16928315610a0a5765ffffffffffff8160981c1680156109ac57421061094e5760009373ffffffffffffffffffffffffffffffffffffffff859485947fffffffffffffff000000000000000000000000000000000000000000000000ff86951690556040517fb7c918e0e249f999e965cafeb6c664271b3f4317d296461500e71da39f0cbda33391806108da8786836020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b0390a2165af16108e8612450565b50156108f057005b606490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601860248201527f6661696c656420746f207769746864726177207374616b6500000000000000006044820152fd5b606485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601b60248201527f5374616b65207769746864726177616c206973206e6f742064756500000000006044820152fd5b606486604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601d60248201527f6d7573742063616c6c20756e6c6f636b5374616b6528292066697273740000006044820152fd5b606485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601460248201527f4e6f207374616b6520746f2077697468647261770000000000000000000000006044820152fd5b3461019c5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c573360005260006020526001604060002001805463ffffffff8160781c16908115610bdc5760ff1615610b7e5765ffffffffffff908142160191818311610b4f5780547fffffffffffffff000000000000ffffffffffffffffffffffffffffffffffff001678ffffffffffff00000000000000000000000000000000000000609885901b161790556040519116815233907ffa9b3c14cc825c412c9ed81b3ba365a5b459439403f18829e572ed53a4180f0a90602090a2005b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f616c726561647920756e7374616b696e670000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f6e6f74207374616b6564000000000000000000000000000000000000000000006044820152fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c57610022610c6f61229f565b612748565b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043567ffffffffffffffff811161019c576020610cc8610d1b9236906004016122c2565b919073ffffffffffffffffffffffffffffffffffffffff9260405194859283927f570e1a360000000000000000000000000000000000000000000000000000000084528560048501526024840191612709565b03816000857f000000000000000000000000efc2c1444ebcc4db75e7613d20c6a62ff67a167c165af1908115610db757602492600092610d86575b50604051917f6ca7b806000000000000000000000000000000000000000000000000000000008352166004820152fd5b610da991925060203d602011610db0575b610da181836121ed565b8101906126dd565b9083610d56565b503d610d97565b6040513d6000823e3d90fd5b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c57610dfa61229f565b60243567ffffffffffffffff811161019c57600091610e1e839236906004016122c2565b90816040519283928337810184815203915af4610e39612450565b90610e7e6040519283927f99410554000000000000000000000000000000000000000000000000000000008452151560048401526040602484015260448301906123c6565b0390fd5b3461019c57610e9036612317565b610e9b9291926129bd565b610ea483612588565b60005b848110610f1c57506000927fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000915b858310610eec576102408585613ba7565b909193600190610f12610f0087898761269d565b610f0a888661265a565b519088613597565b0194019190610edb565b610f47610f40610f2e8385979561265a565b51610f3a84898761269d565b846129f6565b9190613dd7565b73ffffffffffffffffffffffffffffffffffffffff929183166110db5761107657610f7190613dd7565b911661101157610f8657600101929092610ea7565b60a490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f65000000000000000000000000000000000000000000000000000000000000006084820152fd5b608482604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413334207369676e6174757265206572726f720000000000000000000000006064820152fd5b608483604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f414132322065787069726564206f72206e6f74206475650000000000000000006064820152fd5b608484604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413234207369676e6174757265206572726f720000000000000000000000006064820152fd5b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff61118c61229f565b1660005260006020526020604060002054604051908152f35b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff6111f161229f565b6000608060405161120181612155565b828152826020820152826040820152826060820152015216600052600060205260a06040600020608060405161123681612155565b6001835493848352015490602081019060ff8316151582526dffffffffffffffffffffffffffff60408201818560081c16815263ffffffff936060840193858760781c16855265ffffffffffff978891019660981c1686526040519788525115156020880152511660408601525116606084015251166080820152f35b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760206112ec61229f565b73ffffffffffffffffffffffffffffffffffffffff6113096122f0565b911660005260018252604060002077ffffffffffffffffffffffffffffffffffffffffffffffff821660005282526040600020547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006040519260401b16178152f35b3461019c577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60208136011261019c576004359067ffffffffffffffff821161019c5761012090823603011261019c576113c9602091600401612480565b604051908152f35b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5761140861229f565b60243590336000526000602052604060002090815491828411611508576000808573ffffffffffffffffffffffffffffffffffffffff8295839561144c848a612443565b90556040805173ffffffffffffffffffffffffffffffffffffffff831681526020810185905233917fd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb91a2165af16114a2612450565b50156114aa57005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6661696c656420746f20776974686472617700000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f576974686472617720616d6f756e7420746f6f206c61726765000000000000006044820152fd5b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5761159d61229f565b73ffffffffffffffffffffffffffffffffffffffff6115ba6122f0565b9116600052600160205277ffffffffffffffffffffffffffffffffffffffffffffffff604060002091166000526020526020604060002054604051908152f35b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043577ffffffffffffffffffffffffffffffffffffffffffffffff811680910361019c5733600052600160205260406000209060005260205260406000206116728154612416565b9055005b6020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043563ffffffff9182821680920361019c5733600052600081526040600020928215611950576001840154908160781c1683106118f2576116f86dffffffffffffffffffffffffffff9182349160081c16612409565b93841561189457818511611836579065ffffffffffff61180592546040519061172082612155565b8152848101926001845260408201908816815260608201878152600160808401936000855233600052600089526040600020905181550194511515917fffffffffffffffffffffffffff0000000000000000000000000000000000000060ff72ffffffff0000000000000000000000000000006effffffffffffffffffffffffffff008954945160081b16945160781b1694169116171717835551167fffffffffffffff000000000000ffffffffffffffffffffffffffffffffffffff78ffffffffffff0000000000000000000000000000000000000083549260981b169116179055565b6040519283528201527fa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c0160403392a2005b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152600e60248201527f7374616b65206f766572666c6f770000000000000000000000000000000000006044820152fd5b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601260248201527f6e6f207374616b652073706563696669656400000000000000000000000000006044820152fd5b606482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601c60248201527f63616e6e6f7420646563726561736520756e7374616b652074696d65000000006044820152fd5b606482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601a60248201527f6d757374207370656369667920756e7374616b652064656c61790000000000006044820152fd5b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c576004357fffffffff00000000000000000000000000000000000000000000000000000000811680910361019c57807f60fc6b6e0000000000000000000000000000000000000000000000000000000060209214908115611ad6575b8115611aac575b8115611a82575b8115611a58575b506040519015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501482611a4d565b7f3e84f0210000000000000000000000000000000000000000000000000000000081149150611a46565b7fcf28ef970000000000000000000000000000000000000000000000000000000081149150611a3f565b7f915074d80000000000000000000000000000000000000000000000000000000081149150611a38565b3461019c576102007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5767ffffffffffffffff60043581811161019c573660238201121561019c57611b62903690602481600401359101612268565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016101c0811261019c5761014060405191611b9e83612155565b1261019c5760405192611bb0846121a0565b60243573ffffffffffffffffffffffffffffffffffffffff8116810361019c578452602093604435858201526064356040820152608435606082015260a435608082015260c43560a082015260e43560c08201526101043573ffffffffffffffffffffffffffffffffffffffff8116810361019c5760e08201526101243561010082015261014435610120820152825261016435848301526101843560408301526101a43560608301526101c43560808301526101e43590811161019c57611c7c9036906004016122c2565b905a3033036120f7578351606081015195603f5a0260061c61271060a0840151890101116120ce5760009681519182611ff0575b5050505090611cca915a9003608085015101923691612268565b925a90600094845193611cdc85613ccc565b9173ffffffffffffffffffffffffffffffffffffffff60e0870151168015600014611ea957505073ffffffffffffffffffffffffffffffffffffffff855116935b5a9003019360a06060820151910151016080860151850390818111611e95575b50508302604085015192818410600014611dce5750506003811015611da157600203611d79576113c99293508093611d7481613d65565b613cf6565b5050507fdeadaa51000000000000000000000000000000000000000000000000000000008152fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b81611dde92979396940390613c98565b506003841015611e6857507f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f60808683015192519473ffffffffffffffffffffffffffffffffffffffff865116948873ffffffffffffffffffffffffffffffffffffffff60e0890151169701519160405192835215898301528760408301526060820152a46113c9565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526021600452fd5b6064919003600a0204909301928780611d3d565b8095918051611eba575b5050611d1d565b6003861015611fc1576002860315611eb35760a088015190823b1561019c57600091611f2491836040519586809581947f7c627b210000000000000000000000000000000000000000000000000000000083528d60048401526080602484015260848301906123c6565b8b8b0260448301528b60648301520393f19081611fad575b50611fa65787893d610800808211611f9e575b506040519282828501016040528184528284013e610e7e6040519283927fad7954bc000000000000000000000000000000000000000000000000000000008452600484015260248301906123c6565b905083611f4f565b8980611eb3565b611fb89199506121bd565b6000978a611f3c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91600092918380938c73ffffffffffffffffffffffffffffffffffffffff885116910192f115612023575b808080611cb0565b611cca929195503d6108008082116120c6575b5060405190888183010160405280825260008983013e805161205f575b5050600194909161201b565b7f1c4fada7374c0a9ee8841fc38afe82932dc0f8e69012e927f061a8bae611a20188870151918973ffffffffffffffffffffffffffffffffffffffff8551169401516120bc604051928392835260408d84015260408301906123c6565b0390a38680612053565b905088612036565b877fdeaddead000000000000000000000000000000000000000000000000000000006000526000fd5b606486604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601760248201527f4141393220696e7465726e616c2063616c6c206f6e6c790000000000000000006044820152fd5b60a0810190811067ffffffffffffffff82111761217157604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610140810190811067ffffffffffffffff82111761217157604052565b67ffffffffffffffff811161217157604052565b6060810190811067ffffffffffffffff82111761217157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761217157604052565b67ffffffffffffffff811161217157601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926122748261222e565b9161228260405193846121ed565b82948184528183011161019c578281602093846000960137010152565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361019c57565b9181601f8401121561019c5782359167ffffffffffffffff831161019c576020838186019501011161019c57565b6024359077ffffffffffffffffffffffffffffffffffffffffffffffff8216820361019c57565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261019c5760043567ffffffffffffffff9283821161019c578060238301121561019c57816004013593841161019c5760248460051b8301011161019c57602401919060243573ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b60005b8381106123b65750506000910152565b81810151838201526020016123a6565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093612402815180928187528780880191016123a3565b0116010190565b91908201809211610b4f57565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610b4f5760010190565b91908203918211610b4f57565b3d1561247b573d906124618261222e565b9161246f60405193846121ed565b82523d6000602084013e565b606090565b604061248e8183018361284b565b90818351918237206124a3606084018461284b565b90818451918237209260c06124bb60e083018361284b565b908186519182372091845195602087019473ffffffffffffffffffffffffffffffffffffffff833516865260208301358789015260608801526080870152608081013560a087015260a081013582870152013560e08501526101009081850152835261012083019167ffffffffffffffff918484108385111761217157838252845190206101408501908152306101608601524661018086015260608452936101a00191821183831017612171575251902090565b67ffffffffffffffff81116121715760051b60200190565b9061259282612570565b6040906125a260405191826121ed565b8381527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06125d08295612570565b019160005b8381106125e25750505050565b60209082516125f081612155565b83516125fb816121a0565b600081526000849181838201528187820152816060818184015260809282848201528260a08201528260c08201528260e082015282610100820152826101208201528652818587015281898701528501528301528286010152016125d5565b805182101561266e5760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b919081101561266e5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18136030182121561019c570190565b9081602091031261019c575173ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b7f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c4602073ffffffffffffffffffffffffffffffffffffffff61278a3485613c98565b936040519485521692a2565b919081101561266e5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18136030182121561019c570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561019c570180359067ffffffffffffffff821161019c57602001918160051b3603831361019c57565b3573ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561019c570180359067ffffffffffffffff821161019c5760200191813603831361019c57565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561019c57016020813591019167ffffffffffffffff821161019c57813603831361019c57565b61012091813573ffffffffffffffffffffffffffffffffffffffff811680910361019c576129626129476129ba9561299b93855260208601356020860152612937604087018761289c565b9091806040880152860191612709565b612954606086018661289c565b908583036060870152612709565b6080840135608084015260a084013560a084015260c084013560c084015261298d60e085018561289c565b9084830360e0860152612709565b916129ac610100918281019061289c565b929091818503910152612709565b90565b60028054146129cc5760028055565b60046040517f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b926000905a93805194843573ffffffffffffffffffffffffffffffffffffffff811680910361019c5786526020850135602087015260808501356fffffffffffffffffffffffffffffffff90818116606089015260801c604088015260a086013560c088015260c086013590811661010088015260801c610120870152612a8060e086018661284b565b801561357b576034811061351d578060141161019c578060241161019c5760341161019c57602481013560801c60a0880152601481013560801c60808801523560601c60e08701525b612ad285612480565b60208301526040860151946effffffffffffffffffffffffffffff8660c08901511760608901511760808901511760a0890151176101008901511761012089015117116134bf57604087015160608801510160808801510160a08801510160c0880151016101008801510296835173ffffffffffffffffffffffffffffffffffffffff81511690612b66604085018561284b565b806131e4575b505060e0015173ffffffffffffffffffffffffffffffffffffffff1690600082156131ac575b6020612bd7918b828a01516000868a604051978896879586937f19822f7c00000000000000000000000000000000000000000000000000000000855260048501613db5565b0393f160009181613178575b50612c8b573d8c610800808311612c83575b50604051916020818401016040528083526000602084013e610e7e6040519283927f65c8fd4d000000000000000000000000000000000000000000000000000000008452600484015260606024840152600d60648401527f4141323320726576657274656400000000000000000000000000000000000000608484015260a0604484015260a48301906123c6565b915082612bf5565b9a92939495969798999a91156130f2575b509773ffffffffffffffffffffffffffffffffffffffff835116602084015190600052600160205260406000208160401c60005260205267ffffffffffffffff604060002091825492612cee84612416565b9055160361308d575a8503116130285773ffffffffffffffffffffffffffffffffffffffff60e0606093015116612d42575b509060a09184959697986040608096015260608601520135905a900301910152565b969550505a9683519773ffffffffffffffffffffffffffffffffffffffff60e08a01511680600052600060205260406000208054848110612fc3576080612dcd9a9b9c600093878094039055015192602089015183604051809d819582947f52b7512c0000000000000000000000000000000000000000000000000000000084528c60048501613db5565b039286f1978860009160009a612f36575b50612e86573d8b610800808311612e7e575b50604051916020818401016040528083526000602084013e610e7e6040519283927f65c8fd4d000000000000000000000000000000000000000000000000000000008452600484015260606024840152600d60648401527f4141333320726576657274656400000000000000000000000000000000000000608484015260a0604484015260a48301906123c6565b915082612df0565b9991929394959697989998925a900311612eab57509096959094939291906080612d20565b60a490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602760448201527f41413336206f766572207061796d6173746572566572696669636174696f6e4760648201527f61734c696d6974000000000000000000000000000000000000000000000000006084820152fd5b915098503d90816000823e612f4b82826121ed565b604081838101031261019c5780519067ffffffffffffffff821161019c57828101601f83830101121561019c578181015191612f868361222e565b93612f9460405195866121ed565b838552820160208483850101011161019c57602092612fba9184808701918501016123a3565b01519838612dde565b60848b604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601e60448201527f41413331207061796d6173746572206465706f73697420746f6f206c6f7700006064820152fd5b608490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601e60448201527f41413236206f76657220766572696669636174696f6e4761734c696d697400006064820152fd5b608482604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601a60448201527f4141323520696e76616c6964206163636f756e74206e6f6e63650000000000006064820152fd5b600052600060205260406000208054808c11613113578b9003905538612c9c565b608484604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f41413231206469646e2774207061792070726566756e640000000000000000006064820152fd5b9091506020813d6020116131a4575b81613194602093836121ed565b8101031261019c57519038612be3565b3d9150613187565b508060005260006020526040600020548a81116000146131d75750612bd7602060005b915050612b92565b6020612bd7918c036131cf565b833b61345a57604088510151602060405180927f570e1a360000000000000000000000000000000000000000000000000000000082528260048301528160008161323260248201898b612709565b039273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000efc2c1444ebcc4db75e7613d20c6a62ff67a167c1690f1908115610db75760009161343b575b5073ffffffffffffffffffffffffffffffffffffffff811680156133d6578503613371573b1561330c5760141161019c5773ffffffffffffffffffffffffffffffffffffffff9183887fd51a9c61267aa6196961883ecf5ff2da6619c37dac0fa92122513fb32c032d2d604060e0958787602086015195510151168251913560601c82526020820152a391612b6c565b60848d604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602060448201527f4141313520696e6974436f6465206d757374206372656174652073656e6465726064820152fd5b60848e604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602060448201527f4141313420696e6974436f6465206d7573742072657475726e2073656e6465726064820152fd5b60848f604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601b60448201527f4141313320696e6974436f6465206661696c6564206f72204f4f4700000000006064820152fd5b613454915060203d602011610db057610da181836121ed565b3861327c565b60848d604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601f60448201527f414131302073656e64657220616c726561647920636f6e7374727563746564006064820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f41413934206761732076616c756573206f766572666c6f7700000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4141393320696e76616c6964207061796d6173746572416e64446174610000006044820152fd5b5050600060e087015260006080870152600060a0870152612ac9565b9092915a906060810151916040928351967fffffffff00000000000000000000000000000000000000000000000000000000886135d7606084018461284b565b600060038211613b9f575b7f8dd7712f0000000000000000000000000000000000000000000000000000000094168403613a445750505061379d6000926136b292602088015161363a8a5193849360208501528b602485015260648401906128ec565b90604483015203906136727fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0928381018352826121ed565b61379189519485927e42dc5300000000000000000000000000000000000000000000000000000000602085015261020060248501526102248401906123c6565b613760604484018b60806101a091805173ffffffffffffffffffffffffffffffffffffffff808251168652602082015160208701526040820151604087015260608201516060870152838201518487015260a082015160a087015260c082015160c087015260e08201511660e0860152610100808201519086015261012080910151908501526020810151610140850152604081015161016085015260608101516101808501520151910152565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83820301610204840152876123c6565b039081018352826121ed565b6020918183809351910182305af1600051988652156137bf575b505050505050565b909192939495965060003d8214613a3a575b7fdeaddead00000000000000000000000000000000000000000000000000000000810361385b57608487878051917f220266b600000000000000000000000000000000000000000000000000000000835260048301526024820152600f60448201527f41413935206f7574206f662067617300000000000000000000000000000000006064820152fd5b7fdeadaa510000000000000000000000000000000000000000000000000000000091929395949650146000146138c55750506138a961389e6138b8935a90612443565b608085015190612409565b9083015183611d748295613d65565b905b3880808080806137b7565b909261395290828601518651907ff62676f440ff169a3a9afdbf812e89e7f95975ee8e5c31214ffdef631c5f479273ffffffffffffffffffffffffffffffffffffffff9580878551169401516139483d610800808211613a32575b508a519084818301018c5280825260008583013e8a805194859485528401528a8301906123c6565b0390a35a90612443565b916139636080860193845190612409565b926000905a94829488519761397789613ccc565b948260e08b0151168015600014613a1857505050875116955b5a9003019560a06060820151910151019051860390818111613a04575b5050840290850151928184106000146139de57505080611e68575090816139d89293611d7481613d65565b906138ba565b6139ee9082849397950390613c98565b50611e68575090826139ff92613cf6565b6139d8565b6064919003600a02049094019338806139ad565b90919892509751613a2a575b50613990565b955038613a24565b905038613920565b8181803e516137d1565b613b97945082935090613a8c917e42dc53000000000000000000000000000000000000000000000000000000006020613b6b9501526102006024860152610224850191612709565b613b3a604484018860806101a091805173ffffffffffffffffffffffffffffffffffffffff808251168652602082015160208701526040820151604087015260608201516060870152838201518487015260a082015160a087015260c082015160c087015260e08201511660e0860152610100808201519086015261012080910151908501526020810151610140850152604081015161016085015260608101516101808501520151910152565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83820301610204840152846123c6565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018952886121ed565b60008761379d565b5081356135e2565b73ffffffffffffffffffffffffffffffffffffffff168015613c3a57600080809381935af1613bd4612450565b5015613bdc57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f41413931206661696c65642073656e6420746f2062656e6566696369617279006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4141393020696e76616c69642062656e656669636961727900000000000000006044820152fd5b73ffffffffffffffffffffffffffffffffffffffff166000526000602052613cc66040600020918254612409565b80915590565b610120610100820151910151808214613cf257480180821015613ced575090565b905090565b5090565b9190917f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f6080602083015192519473ffffffffffffffffffffffffffffffffffffffff946020868851169660e089015116970151916040519283526000602084015260408301526060820152a4565b60208101519051907f67b4fa9642f42120bf031f3051d1824b0fe25627945b27b8a6a65d5761d5482e60208073ffffffffffffffffffffffffffffffffffffffff855116940151604051908152a3565b613dcd604092959493956060835260608301906128ec565b9460208201520152565b8015613e6457600060408051613dec816121d1565b828152826020820152015273ffffffffffffffffffffffffffffffffffffffff811690604065ffffffffffff91828160a01c16908115613e5c575b60d01c92825191613e37836121d1565b8583528460208401521691829101524211908115613e5457509091565b905042109091565b839150613e27565b5060009060009056fea2646970667358221220b094fd69f04977ae9458e5ba422d01cd2d20dbcfca0992ff37f19aa07deec25464736f6c63430008170033", + "nonce": "2", + "storage": { + "0xa3b2ff63dddb6717733673f0c1cf67be4e4eecc50d4e5fd777cf82e814f7242f": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "0x7ab5742e5b448c142a35c92699fd2dd6b8930cbd": { + "balance": "0xfffffffffffffffffffffffffffffffffffffffffffffffb0dc02146cf089f65", + "nonce": "110" + } + }, + "config": { + "chainId": 1337, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "rip7560block": 0, + "rip7712block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "shanghaiTime": 0, + "cancunTime": 0, + "pragueTime": 0, + "terminalTotalDifficulty": 0, + "depositContractAddress": "0x0000000000000000000000000000000000000000", + "blobSchedule": { + "cancun": { + "max": 6, + "target": 3, + "baseFeeUpdateFraction": 3338477 + }, + "prague": { + "max": 9, + "target": 6, + "baseFeeUpdateFraction": 5007716 + } + } + } + }, + "context": { + "number": "115", + "difficulty": "0", + "timestamp": "1738267578", + "gasLimit": "30000000", + "miner": "0x0000000000000000000000000000000000000000", + "baseFeePerGas": "1457" + }, + "input": "0x02f8998205396e843b99d302843b99e00283023a3c940000000071727de22e5e9d8baf0edac6f37da032880de0b6b3a7640000a4b760faf90000000000000000000000005604b855b3708057705f8dfc0e6470917082c43ac001a03a94ab9585bd0a378ddfcaa078a3982bb88e60c22407de299ad314b8a8631a31a0720f8eb0d92906238bc842b387ee147519d9b05e22d8c77d5a855cb16744d660", + "result": { + "accessedSlots": { + "reads": { + "0xa3b2ff63dddb6717733673f0c1cf67be4e4eecc50d4e5fd777cf82e814f7242f": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ] + }, + "transientReads": {}, + "transientWrites": {}, + "writes": { + "0xa3b2ff63dddb6717733673f0c1cf67be4e4eecc50d4e5fd777cf82e814f7242f": 1 + } + }, + "contractSize": {}, + "extCodeAccessInfo": [], + "from": "0x7ab5742e5b448c142a35c92699fd2dd6b8930cbd", + "gas": "0x23a3c", + "gasUsed": "0xb21f", + "input": "0xb760faf90000000000000000000000005604b855b3708057705f8dfc0e6470917082c43a", + "keccak": [ + "0x0000000000000000000000005604b855b3708057705f8dfc0e6470917082c43a0000000000000000000000000000000000000000000000000000000000000000" + ], + "outOfGas": false, + "to": "0x0000000071727de22e5e9d8baf0edac6f37da032", + "type": "CALL", + "usedOpcodes": {"0x0":1, "0x20":1, "0x34":1, "0x35":2, "0x36":2, "0x51":1, "0x52":4, "0x54":1, "0x55":1, "0x56":8, "0x57":18, "0x5b":10, "0xa2":1}, + "value": "0xde0b6b3a7640000" + } +} diff --git a/eth/tracers/internal/tracetest/testdata/erc7562_tracer/erc7562Tracer.test_paymaster.json b/eth/tracers/internal/tracetest/testdata/erc7562_tracer/erc7562Tracer.test_paymaster.json new file mode 100644 index 00000000000..a7e09f761e4 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/erc7562_tracer/erc7562Tracer.test_paymaster.json @@ -0,0 +1,110 @@ +{ + "genesis": { + "baseFeePerGas": "63230", + "blobGasUsed": "0", + "difficulty": "0", + "excessBlobGas": "0", + "extraData": "0xd883010e0d846765746888676f312e32322e36856c696e7578", + "gasLimit": "30000000", + "hash": "0x38e598c8c0600233b8353fb28aa12e987be7216fcb141cffb03d941141dac085", + "miner": "0x0000000000000000000000000000000000000000", + "mixHash": "0xc406f87ac4eb5e4d1d95345ceb92a3f17a7c97f6e9d4c1c857601960cd91d3b2", + "nonce": "0x0000000000000000", + "number": "84", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stateRoot": "0xc0df2d6f5e7199feff4e49bf23d05f5758d85b146159b1b5b53700bee1b0c073", + "timestamp": "1738266969", + "withdrawals": [], + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "alloc": { + "0x0000000000000000000000000000000000000000": { + "balance": "0x1fbe9bb2534e03d" + }, + "0x0000000071727de22e5e9d8baf0edac6f37da032": { + "balance": "0x3a21a6c6853747fff", + "code": "0x60806040526004361015610024575b361561001957600080fd5b61002233612748565b005b60003560e01c806242dc5314611b0057806301ffc9a7146119ae5780630396cb60146116765780630bd28e3b146115fa5780631b2e01b814611566578063205c2878146113d157806322cdde4c1461136b57806335567e1a146112b35780635287ce12146111a557806370a0823114611140578063765e827f14610e82578063850aaf6214610dc35780639b249f6914610c74578063b760faf914610c3a578063bb9fe6bf14610a68578063c23a5cea146107c4578063dbed18e0146101a15763fc7e286d0361000e573461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff61013a61229f565b16600052600060205260a0604060002065ffffffffffff6001825492015460405192835260ff8116151560208401526dffffffffffffffffffffffffffff8160081c16604084015263ffffffff8160781c16606084015260981c166080820152f35b600080fd5b3461019c576101af36612317565b906101b86129bd565b60009160005b82811061056f57506101d08493612588565b6000805b8481106102fc5750507fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000809360005b81811061024757610240868660007f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d8180a2613ba7565b6001600255005b6102a261025582848a612796565b73ffffffffffffffffffffffffffffffffffffffff6102766020830161282a565b167f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d600080a2806127d6565b906000915b8083106102b957505050600101610209565b909194976102f36102ed6001926102e78c8b6102e0826102da8e8b8d61269d565b9261265a565b5191613597565b90612409565b99612416565b950191906102a7565b6020610309828789612796565b61031f61031682806127d6565b9390920161282a565b9160009273ffffffffffffffffffffffffffffffffffffffff8091165b8285106103505750505050506001016101d4565b909192939561037f83610378610366848c61265a565b516103728b898b61269d565b856129f6565b9290613dd7565b9116840361050a576104a5576103958491613dd7565b9116610440576103b5576103aa600191612416565b96019392919061033c565b60a487604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f65000000000000000000000000000000000000000000000000000000000000006084820152fd5b608488604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413334207369676e6174757265206572726f720000000000000000000000006064820152fd5b608488604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f414132322065787069726564206f72206e6f74206475650000000000000000006064820152fd5b608489604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413234207369676e6174757265206572726f720000000000000000000000006064820152fd5b61057a818487612796565b9361058585806127d6565b919095602073ffffffffffffffffffffffffffffffffffffffff6105aa82840161282a565b1697600192838a1461076657896105da575b5050505060019293949550906105d191612409565b939291016101be565b8060406105e892019061284b565b918a3b1561019c57929391906040519485937f2dd8113300000000000000000000000000000000000000000000000000000000855288604486016040600488015252606490818601918a60051b8701019680936000915b8c83106106e657505050505050838392610684927ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8560009803016024860152612709565b03818a5afa90816106d7575b506106c657602486604051907f86a9f7500000000000000000000000000000000000000000000000000000000082526004820152fd5b93945084936105d1600189806105bc565b6106e0906121bd565b88610690565b91939596977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c908a9294969a0301865288357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18336030181121561019c57836107538793858394016128ec565b9a0196019301909189979695949261063f565b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601760248201527f4141393620696e76616c69642061676772656761746f720000000000000000006044820152fd5b3461019c576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c576107fc61229f565b33600052600082526001604060002001908154916dffffffffffffffffffffffffffff8360081c16928315610a0a5765ffffffffffff8160981c1680156109ac57421061094e5760009373ffffffffffffffffffffffffffffffffffffffff859485947fffffffffffffff000000000000000000000000000000000000000000000000ff86951690556040517fb7c918e0e249f999e965cafeb6c664271b3f4317d296461500e71da39f0cbda33391806108da8786836020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b0390a2165af16108e8612450565b50156108f057005b606490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601860248201527f6661696c656420746f207769746864726177207374616b6500000000000000006044820152fd5b606485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601b60248201527f5374616b65207769746864726177616c206973206e6f742064756500000000006044820152fd5b606486604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601d60248201527f6d7573742063616c6c20756e6c6f636b5374616b6528292066697273740000006044820152fd5b606485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601460248201527f4e6f207374616b6520746f2077697468647261770000000000000000000000006044820152fd5b3461019c5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c573360005260006020526001604060002001805463ffffffff8160781c16908115610bdc5760ff1615610b7e5765ffffffffffff908142160191818311610b4f5780547fffffffffffffff000000000000ffffffffffffffffffffffffffffffffffff001678ffffffffffff00000000000000000000000000000000000000609885901b161790556040519116815233907ffa9b3c14cc825c412c9ed81b3ba365a5b459439403f18829e572ed53a4180f0a90602090a2005b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f616c726561647920756e7374616b696e670000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f6e6f74207374616b6564000000000000000000000000000000000000000000006044820152fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c57610022610c6f61229f565b612748565b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043567ffffffffffffffff811161019c576020610cc8610d1b9236906004016122c2565b919073ffffffffffffffffffffffffffffffffffffffff9260405194859283927f570e1a360000000000000000000000000000000000000000000000000000000084528560048501526024840191612709565b03816000857f000000000000000000000000efc2c1444ebcc4db75e7613d20c6a62ff67a167c165af1908115610db757602492600092610d86575b50604051917f6ca7b806000000000000000000000000000000000000000000000000000000008352166004820152fd5b610da991925060203d602011610db0575b610da181836121ed565b8101906126dd565b9083610d56565b503d610d97565b6040513d6000823e3d90fd5b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c57610dfa61229f565b60243567ffffffffffffffff811161019c57600091610e1e839236906004016122c2565b90816040519283928337810184815203915af4610e39612450565b90610e7e6040519283927f99410554000000000000000000000000000000000000000000000000000000008452151560048401526040602484015260448301906123c6565b0390fd5b3461019c57610e9036612317565b610e9b9291926129bd565b610ea483612588565b60005b848110610f1c57506000927fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000915b858310610eec576102408585613ba7565b909193600190610f12610f0087898761269d565b610f0a888661265a565b519088613597565b0194019190610edb565b610f47610f40610f2e8385979561265a565b51610f3a84898761269d565b846129f6565b9190613dd7565b73ffffffffffffffffffffffffffffffffffffffff929183166110db5761107657610f7190613dd7565b911661101157610f8657600101929092610ea7565b60a490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f65000000000000000000000000000000000000000000000000000000000000006084820152fd5b608482604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413334207369676e6174757265206572726f720000000000000000000000006064820152fd5b608483604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f414132322065787069726564206f72206e6f74206475650000000000000000006064820152fd5b608484604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413234207369676e6174757265206572726f720000000000000000000000006064820152fd5b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff61118c61229f565b1660005260006020526020604060002054604051908152f35b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff6111f161229f565b6000608060405161120181612155565b828152826020820152826040820152826060820152015216600052600060205260a06040600020608060405161123681612155565b6001835493848352015490602081019060ff8316151582526dffffffffffffffffffffffffffff60408201818560081c16815263ffffffff936060840193858760781c16855265ffffffffffff978891019660981c1686526040519788525115156020880152511660408601525116606084015251166080820152f35b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760206112ec61229f565b73ffffffffffffffffffffffffffffffffffffffff6113096122f0565b911660005260018252604060002077ffffffffffffffffffffffffffffffffffffffffffffffff821660005282526040600020547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006040519260401b16178152f35b3461019c577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60208136011261019c576004359067ffffffffffffffff821161019c5761012090823603011261019c576113c9602091600401612480565b604051908152f35b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5761140861229f565b60243590336000526000602052604060002090815491828411611508576000808573ffffffffffffffffffffffffffffffffffffffff8295839561144c848a612443565b90556040805173ffffffffffffffffffffffffffffffffffffffff831681526020810185905233917fd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb91a2165af16114a2612450565b50156114aa57005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6661696c656420746f20776974686472617700000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f576974686472617720616d6f756e7420746f6f206c61726765000000000000006044820152fd5b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5761159d61229f565b73ffffffffffffffffffffffffffffffffffffffff6115ba6122f0565b9116600052600160205277ffffffffffffffffffffffffffffffffffffffffffffffff604060002091166000526020526020604060002054604051908152f35b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043577ffffffffffffffffffffffffffffffffffffffffffffffff811680910361019c5733600052600160205260406000209060005260205260406000206116728154612416565b9055005b6020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043563ffffffff9182821680920361019c5733600052600081526040600020928215611950576001840154908160781c1683106118f2576116f86dffffffffffffffffffffffffffff9182349160081c16612409565b93841561189457818511611836579065ffffffffffff61180592546040519061172082612155565b8152848101926001845260408201908816815260608201878152600160808401936000855233600052600089526040600020905181550194511515917fffffffffffffffffffffffffff0000000000000000000000000000000000000060ff72ffffffff0000000000000000000000000000006effffffffffffffffffffffffffff008954945160081b16945160781b1694169116171717835551167fffffffffffffff000000000000ffffffffffffffffffffffffffffffffffffff78ffffffffffff0000000000000000000000000000000000000083549260981b169116179055565b6040519283528201527fa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c0160403392a2005b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152600e60248201527f7374616b65206f766572666c6f770000000000000000000000000000000000006044820152fd5b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601260248201527f6e6f207374616b652073706563696669656400000000000000000000000000006044820152fd5b606482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601c60248201527f63616e6e6f7420646563726561736520756e7374616b652074696d65000000006044820152fd5b606482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601a60248201527f6d757374207370656369667920756e7374616b652064656c61790000000000006044820152fd5b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c576004357fffffffff00000000000000000000000000000000000000000000000000000000811680910361019c57807f60fc6b6e0000000000000000000000000000000000000000000000000000000060209214908115611ad6575b8115611aac575b8115611a82575b8115611a58575b506040519015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501482611a4d565b7f3e84f0210000000000000000000000000000000000000000000000000000000081149150611a46565b7fcf28ef970000000000000000000000000000000000000000000000000000000081149150611a3f565b7f915074d80000000000000000000000000000000000000000000000000000000081149150611a38565b3461019c576102007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5767ffffffffffffffff60043581811161019c573660238201121561019c57611b62903690602481600401359101612268565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016101c0811261019c5761014060405191611b9e83612155565b1261019c5760405192611bb0846121a0565b60243573ffffffffffffffffffffffffffffffffffffffff8116810361019c578452602093604435858201526064356040820152608435606082015260a435608082015260c43560a082015260e43560c08201526101043573ffffffffffffffffffffffffffffffffffffffff8116810361019c5760e08201526101243561010082015261014435610120820152825261016435848301526101843560408301526101a43560608301526101c43560808301526101e43590811161019c57611c7c9036906004016122c2565b905a3033036120f7578351606081015195603f5a0260061c61271060a0840151890101116120ce5760009681519182611ff0575b5050505090611cca915a9003608085015101923691612268565b925a90600094845193611cdc85613ccc565b9173ffffffffffffffffffffffffffffffffffffffff60e0870151168015600014611ea957505073ffffffffffffffffffffffffffffffffffffffff855116935b5a9003019360a06060820151910151016080860151850390818111611e95575b50508302604085015192818410600014611dce5750506003811015611da157600203611d79576113c99293508093611d7481613d65565b613cf6565b5050507fdeadaa51000000000000000000000000000000000000000000000000000000008152fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b81611dde92979396940390613c98565b506003841015611e6857507f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f60808683015192519473ffffffffffffffffffffffffffffffffffffffff865116948873ffffffffffffffffffffffffffffffffffffffff60e0890151169701519160405192835215898301528760408301526060820152a46113c9565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526021600452fd5b6064919003600a0204909301928780611d3d565b8095918051611eba575b5050611d1d565b6003861015611fc1576002860315611eb35760a088015190823b1561019c57600091611f2491836040519586809581947f7c627b210000000000000000000000000000000000000000000000000000000083528d60048401526080602484015260848301906123c6565b8b8b0260448301528b60648301520393f19081611fad575b50611fa65787893d610800808211611f9e575b506040519282828501016040528184528284013e610e7e6040519283927fad7954bc000000000000000000000000000000000000000000000000000000008452600484015260248301906123c6565b905083611f4f565b8980611eb3565b611fb89199506121bd565b6000978a611f3c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91600092918380938c73ffffffffffffffffffffffffffffffffffffffff885116910192f115612023575b808080611cb0565b611cca929195503d6108008082116120c6575b5060405190888183010160405280825260008983013e805161205f575b5050600194909161201b565b7f1c4fada7374c0a9ee8841fc38afe82932dc0f8e69012e927f061a8bae611a20188870151918973ffffffffffffffffffffffffffffffffffffffff8551169401516120bc604051928392835260408d84015260408301906123c6565b0390a38680612053565b905088612036565b877fdeaddead000000000000000000000000000000000000000000000000000000006000526000fd5b606486604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601760248201527f4141393220696e7465726e616c2063616c6c206f6e6c790000000000000000006044820152fd5b60a0810190811067ffffffffffffffff82111761217157604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610140810190811067ffffffffffffffff82111761217157604052565b67ffffffffffffffff811161217157604052565b6060810190811067ffffffffffffffff82111761217157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761217157604052565b67ffffffffffffffff811161217157601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926122748261222e565b9161228260405193846121ed565b82948184528183011161019c578281602093846000960137010152565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361019c57565b9181601f8401121561019c5782359167ffffffffffffffff831161019c576020838186019501011161019c57565b6024359077ffffffffffffffffffffffffffffffffffffffffffffffff8216820361019c57565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261019c5760043567ffffffffffffffff9283821161019c578060238301121561019c57816004013593841161019c5760248460051b8301011161019c57602401919060243573ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b60005b8381106123b65750506000910152565b81810151838201526020016123a6565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093612402815180928187528780880191016123a3565b0116010190565b91908201809211610b4f57565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610b4f5760010190565b91908203918211610b4f57565b3d1561247b573d906124618261222e565b9161246f60405193846121ed565b82523d6000602084013e565b606090565b604061248e8183018361284b565b90818351918237206124a3606084018461284b565b90818451918237209260c06124bb60e083018361284b565b908186519182372091845195602087019473ffffffffffffffffffffffffffffffffffffffff833516865260208301358789015260608801526080870152608081013560a087015260a081013582870152013560e08501526101009081850152835261012083019167ffffffffffffffff918484108385111761217157838252845190206101408501908152306101608601524661018086015260608452936101a00191821183831017612171575251902090565b67ffffffffffffffff81116121715760051b60200190565b9061259282612570565b6040906125a260405191826121ed565b8381527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06125d08295612570565b019160005b8381106125e25750505050565b60209082516125f081612155565b83516125fb816121a0565b600081526000849181838201528187820152816060818184015260809282848201528260a08201528260c08201528260e082015282610100820152826101208201528652818587015281898701528501528301528286010152016125d5565b805182101561266e5760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b919081101561266e5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18136030182121561019c570190565b9081602091031261019c575173ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b7f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c4602073ffffffffffffffffffffffffffffffffffffffff61278a3485613c98565b936040519485521692a2565b919081101561266e5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18136030182121561019c570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561019c570180359067ffffffffffffffff821161019c57602001918160051b3603831361019c57565b3573ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561019c570180359067ffffffffffffffff821161019c5760200191813603831361019c57565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561019c57016020813591019167ffffffffffffffff821161019c57813603831361019c57565b61012091813573ffffffffffffffffffffffffffffffffffffffff811680910361019c576129626129476129ba9561299b93855260208601356020860152612937604087018761289c565b9091806040880152860191612709565b612954606086018661289c565b908583036060870152612709565b6080840135608084015260a084013560a084015260c084013560c084015261298d60e085018561289c565b9084830360e0860152612709565b916129ac610100918281019061289c565b929091818503910152612709565b90565b60028054146129cc5760028055565b60046040517f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b926000905a93805194843573ffffffffffffffffffffffffffffffffffffffff811680910361019c5786526020850135602087015260808501356fffffffffffffffffffffffffffffffff90818116606089015260801c604088015260a086013560c088015260c086013590811661010088015260801c610120870152612a8060e086018661284b565b801561357b576034811061351d578060141161019c578060241161019c5760341161019c57602481013560801c60a0880152601481013560801c60808801523560601c60e08701525b612ad285612480565b60208301526040860151946effffffffffffffffffffffffffffff8660c08901511760608901511760808901511760a0890151176101008901511761012089015117116134bf57604087015160608801510160808801510160a08801510160c0880151016101008801510296835173ffffffffffffffffffffffffffffffffffffffff81511690612b66604085018561284b565b806131e4575b505060e0015173ffffffffffffffffffffffffffffffffffffffff1690600082156131ac575b6020612bd7918b828a01516000868a604051978896879586937f19822f7c00000000000000000000000000000000000000000000000000000000855260048501613db5565b0393f160009181613178575b50612c8b573d8c610800808311612c83575b50604051916020818401016040528083526000602084013e610e7e6040519283927f65c8fd4d000000000000000000000000000000000000000000000000000000008452600484015260606024840152600d60648401527f4141323320726576657274656400000000000000000000000000000000000000608484015260a0604484015260a48301906123c6565b915082612bf5565b9a92939495969798999a91156130f2575b509773ffffffffffffffffffffffffffffffffffffffff835116602084015190600052600160205260406000208160401c60005260205267ffffffffffffffff604060002091825492612cee84612416565b9055160361308d575a8503116130285773ffffffffffffffffffffffffffffffffffffffff60e0606093015116612d42575b509060a09184959697986040608096015260608601520135905a900301910152565b969550505a9683519773ffffffffffffffffffffffffffffffffffffffff60e08a01511680600052600060205260406000208054848110612fc3576080612dcd9a9b9c600093878094039055015192602089015183604051809d819582947f52b7512c0000000000000000000000000000000000000000000000000000000084528c60048501613db5565b039286f1978860009160009a612f36575b50612e86573d8b610800808311612e7e575b50604051916020818401016040528083526000602084013e610e7e6040519283927f65c8fd4d000000000000000000000000000000000000000000000000000000008452600484015260606024840152600d60648401527f4141333320726576657274656400000000000000000000000000000000000000608484015260a0604484015260a48301906123c6565b915082612df0565b9991929394959697989998925a900311612eab57509096959094939291906080612d20565b60a490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602760448201527f41413336206f766572207061796d6173746572566572696669636174696f6e4760648201527f61734c696d6974000000000000000000000000000000000000000000000000006084820152fd5b915098503d90816000823e612f4b82826121ed565b604081838101031261019c5780519067ffffffffffffffff821161019c57828101601f83830101121561019c578181015191612f868361222e565b93612f9460405195866121ed565b838552820160208483850101011161019c57602092612fba9184808701918501016123a3565b01519838612dde565b60848b604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601e60448201527f41413331207061796d6173746572206465706f73697420746f6f206c6f7700006064820152fd5b608490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601e60448201527f41413236206f76657220766572696669636174696f6e4761734c696d697400006064820152fd5b608482604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601a60448201527f4141323520696e76616c6964206163636f756e74206e6f6e63650000000000006064820152fd5b600052600060205260406000208054808c11613113578b9003905538612c9c565b608484604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f41413231206469646e2774207061792070726566756e640000000000000000006064820152fd5b9091506020813d6020116131a4575b81613194602093836121ed565b8101031261019c57519038612be3565b3d9150613187565b508060005260006020526040600020548a81116000146131d75750612bd7602060005b915050612b92565b6020612bd7918c036131cf565b833b61345a57604088510151602060405180927f570e1a360000000000000000000000000000000000000000000000000000000082528260048301528160008161323260248201898b612709565b039273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000efc2c1444ebcc4db75e7613d20c6a62ff67a167c1690f1908115610db75760009161343b575b5073ffffffffffffffffffffffffffffffffffffffff811680156133d6578503613371573b1561330c5760141161019c5773ffffffffffffffffffffffffffffffffffffffff9183887fd51a9c61267aa6196961883ecf5ff2da6619c37dac0fa92122513fb32c032d2d604060e0958787602086015195510151168251913560601c82526020820152a391612b6c565b60848d604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602060448201527f4141313520696e6974436f6465206d757374206372656174652073656e6465726064820152fd5b60848e604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602060448201527f4141313420696e6974436f6465206d7573742072657475726e2073656e6465726064820152fd5b60848f604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601b60448201527f4141313320696e6974436f6465206661696c6564206f72204f4f4700000000006064820152fd5b613454915060203d602011610db057610da181836121ed565b3861327c565b60848d604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601f60448201527f414131302073656e64657220616c726561647920636f6e7374727563746564006064820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f41413934206761732076616c756573206f766572666c6f7700000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4141393320696e76616c6964207061796d6173746572416e64446174610000006044820152fd5b5050600060e087015260006080870152600060a0870152612ac9565b9092915a906060810151916040928351967fffffffff00000000000000000000000000000000000000000000000000000000886135d7606084018461284b565b600060038211613b9f575b7f8dd7712f0000000000000000000000000000000000000000000000000000000094168403613a445750505061379d6000926136b292602088015161363a8a5193849360208501528b602485015260648401906128ec565b90604483015203906136727fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0928381018352826121ed565b61379189519485927e42dc5300000000000000000000000000000000000000000000000000000000602085015261020060248501526102248401906123c6565b613760604484018b60806101a091805173ffffffffffffffffffffffffffffffffffffffff808251168652602082015160208701526040820151604087015260608201516060870152838201518487015260a082015160a087015260c082015160c087015260e08201511660e0860152610100808201519086015261012080910151908501526020810151610140850152604081015161016085015260608101516101808501520151910152565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83820301610204840152876123c6565b039081018352826121ed565b6020918183809351910182305af1600051988652156137bf575b505050505050565b909192939495965060003d8214613a3a575b7fdeaddead00000000000000000000000000000000000000000000000000000000810361385b57608487878051917f220266b600000000000000000000000000000000000000000000000000000000835260048301526024820152600f60448201527f41413935206f7574206f662067617300000000000000000000000000000000006064820152fd5b7fdeadaa510000000000000000000000000000000000000000000000000000000091929395949650146000146138c55750506138a961389e6138b8935a90612443565b608085015190612409565b9083015183611d748295613d65565b905b3880808080806137b7565b909261395290828601518651907ff62676f440ff169a3a9afdbf812e89e7f95975ee8e5c31214ffdef631c5f479273ffffffffffffffffffffffffffffffffffffffff9580878551169401516139483d610800808211613a32575b508a519084818301018c5280825260008583013e8a805194859485528401528a8301906123c6565b0390a35a90612443565b916139636080860193845190612409565b926000905a94829488519761397789613ccc565b948260e08b0151168015600014613a1857505050875116955b5a9003019560a06060820151910151019051860390818111613a04575b5050840290850151928184106000146139de57505080611e68575090816139d89293611d7481613d65565b906138ba565b6139ee9082849397950390613c98565b50611e68575090826139ff92613cf6565b6139d8565b6064919003600a02049094019338806139ad565b90919892509751613a2a575b50613990565b955038613a24565b905038613920565b8181803e516137d1565b613b97945082935090613a8c917e42dc53000000000000000000000000000000000000000000000000000000006020613b6b9501526102006024860152610224850191612709565b613b3a604484018860806101a091805173ffffffffffffffffffffffffffffffffffffffff808251168652602082015160208701526040820151604087015260608201516060870152838201518487015260a082015160a087015260c082015160c087015260e08201511660e0860152610100808201519086015261012080910151908501526020810151610140850152604081015161016085015260608101516101808501520151910152565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83820301610204840152846123c6565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018952886121ed565b60008761379d565b5081356135e2565b73ffffffffffffffffffffffffffffffffffffffff168015613c3a57600080809381935af1613bd4612450565b5015613bdc57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f41413931206661696c65642073656e6420746f2062656e6566696369617279006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4141393020696e76616c69642062656e656669636961727900000000000000006044820152fd5b73ffffffffffffffffffffffffffffffffffffffff166000526000602052613cc66040600020918254612409565b80915590565b610120610100820151910151808214613cf257480180821015613ced575090565b905090565b5090565b9190917f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f6080602083015192519473ffffffffffffffffffffffffffffffffffffffff946020868851169660e089015116970151916040519283526000602084015260408301526060820152a4565b60208101519051907f67b4fa9642f42120bf031f3051d1824b0fe25627945b27b8a6a65d5761d5482e60208073ffffffffffffffffffffffffffffffffffffffff855116940151604051908152a3565b613dcd604092959493956060835260608301906128ec565b9460208201520152565b8015613e6457600060408051613dec816121d1565b828152826020820152015273ffffffffffffffffffffffffffffffffffffffff811690604065ffffffffffff91828160a01c16908115613e5c575b60d01c92825191613e37836121d1565b8583528460208401521691829101524211908115613e5457509091565b905042109091565b839150613e27565b5060009060009056fea2646970667358221220b094fd69f04977ae9458e5ba422d01cd2d20dbcfca0992ff37f19aa07deec25464736f6c63430008170033", + "nonce": "2", + "storage": { + "0xfd8a9a5105ec447cdc63b84f6e5700b432121f67627f5d0269ac1b8f5e1b5507": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "0x7ab5742e5b448c142a35c92699fd2dd6b8930cbd": { + "balance": "0xfffffffffffffffffffffffffffffffffffffffffffffffc4dc3b19c23c7dc28", + "nonce": "81" + } + }, + "config": { + "chainId": 1337, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "rip7560block": 0, + "rip7712block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "shanghaiTime": 0, + "cancunTime": 0, + "pragueTime": 0, + "terminalTotalDifficulty": 0, + "depositContractAddress": "0x0000000000000000000000000000000000000000", + "blobSchedule": { + "cancun": { + "max": 6, + "target": 3, + "baseFeeUpdateFraction": 3338477 + }, + "prague": { + "max": 9, + "target": 6, + "baseFeeUpdateFraction": 5007716 + } + } + } + }, + "context": { + "number": "85", + "difficulty": "0", + "timestamp": "1738266970", + "gasLimit": "30000000", + "miner": "0x0000000000000000000000000000000000000000", + "baseFeePerGas": "55726" + }, + "input": "0x02f89882053951843b82b63d843b84a43983023a3c940000000071727de22e5e9d8baf0edac6f37da032874a9b6384487fffa4b760faf900000000000000000000000093e4af629481a69da4e7335892703ae52113b3e7c001a003d55576c3186202ffa9e00ea2ef84b3699ec1a77285a8a54876915689c8e946a00e59f2fdddd26270454a759c24609bd8eb20b899f8d2a99922c3a3adc586e366", + "result": { + "accessedSlots": { + "reads": { + "0xfd8a9a5105ec447cdc63b84f6e5700b432121f67627f5d0269ac1b8f5e1b5507": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ] + }, + "transientReads": {}, + "transientWrites": {}, + "writes": { + "0xfd8a9a5105ec447cdc63b84f6e5700b432121f67627f5d0269ac1b8f5e1b5507": 1 + } + }, + "contractSize": {}, + "extCodeAccessInfo": [], + "from": "0x7ab5742e5b448c142a35c92699fd2dd6b8930cbd", + "gas": "0x23a3c", + "gasUsed": "0xb21f", + "input": "0xb760faf900000000000000000000000093e4af629481a69da4e7335892703ae52113b3e7", + "keccak": [ + "0x00000000000000000000000093e4af629481a69da4e7335892703ae52113b3e70000000000000000000000000000000000000000000000000000000000000000" + ], + "outOfGas": false, + "to": "0x0000000071727de22e5e9d8baf0edac6f37da032", + "type": "CALL", + "usedOpcodes": {"0x0":1, "0x20":1, "0x34":1, "0x35":2, "0x36":2, "0x51":1, "0x52":4, "0x54":1, "0x55":1, "0x56":8, "0x57":18, "0x5b":10, "0xa2":1}, + "value": "0x4a9b6384487fff" + } +} diff --git a/eth/tracers/internal/tracetest/testdata/erc7562_tracer/erc7562Tracer.test_simple.json b/eth/tracers/internal/tracetest/testdata/erc7562_tracer/erc7562Tracer.test_simple.json new file mode 100644 index 00000000000..f0a0fc16556 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/erc7562_tracer/erc7562Tracer.test_simple.json @@ -0,0 +1,246 @@ +{ + "genesis": { + "baseFeePerGas": "42949", + "blobGasUsed": "0", + "difficulty": "0", + "excessBlobGas": "0", + "extraData": "0xd883010e0d846765746888676f312e32322e36856c696e7578", + "gasLimit": "30000000", + "hash": "0x6f85304f332c41070b645fbed7ad468db1f80d3b52776d8a3f3c10c9d106db17", + "miner": "0x0000000000000000000000000000000000000000", + "mixHash": "0x4917b13b44dc41c98ec2099dd0f549c640b98d3e358cc7fcdef7988153093510", + "nonce": "0x0000000000000000", + "number": "87", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "stateRoot": "0xb17f1114a85e834ee373ad5be38854b7efc921bdcef77acef657fdc63ab25cf8", + "timestamp": "1738267404", + "withdrawals": [], + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "alloc": { + "0x0000000000000000000000000000000000000000": { + "balance": "0x20113869f420bed" + }, + "0x0000000071727de22e5e9d8baf0edac6f37da032": { + "balance": "0x3be2675332684fffe", + "code": "0x60806040526004361015610024575b361561001957600080fd5b61002233612748565b005b60003560e01c806242dc5314611b0057806301ffc9a7146119ae5780630396cb60146116765780630bd28e3b146115fa5780631b2e01b814611566578063205c2878146113d157806322cdde4c1461136b57806335567e1a146112b35780635287ce12146111a557806370a0823114611140578063765e827f14610e82578063850aaf6214610dc35780639b249f6914610c74578063b760faf914610c3a578063bb9fe6bf14610a68578063c23a5cea146107c4578063dbed18e0146101a15763fc7e286d0361000e573461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff61013a61229f565b16600052600060205260a0604060002065ffffffffffff6001825492015460405192835260ff8116151560208401526dffffffffffffffffffffffffffff8160081c16604084015263ffffffff8160781c16606084015260981c166080820152f35b600080fd5b3461019c576101af36612317565b906101b86129bd565b60009160005b82811061056f57506101d08493612588565b6000805b8481106102fc5750507fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000809360005b81811061024757610240868660007f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d8180a2613ba7565b6001600255005b6102a261025582848a612796565b73ffffffffffffffffffffffffffffffffffffffff6102766020830161282a565b167f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d600080a2806127d6565b906000915b8083106102b957505050600101610209565b909194976102f36102ed6001926102e78c8b6102e0826102da8e8b8d61269d565b9261265a565b5191613597565b90612409565b99612416565b950191906102a7565b6020610309828789612796565b61031f61031682806127d6565b9390920161282a565b9160009273ffffffffffffffffffffffffffffffffffffffff8091165b8285106103505750505050506001016101d4565b909192939561037f83610378610366848c61265a565b516103728b898b61269d565b856129f6565b9290613dd7565b9116840361050a576104a5576103958491613dd7565b9116610440576103b5576103aa600191612416565b96019392919061033c565b60a487604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f65000000000000000000000000000000000000000000000000000000000000006084820152fd5b608488604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413334207369676e6174757265206572726f720000000000000000000000006064820152fd5b608488604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f414132322065787069726564206f72206e6f74206475650000000000000000006064820152fd5b608489604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413234207369676e6174757265206572726f720000000000000000000000006064820152fd5b61057a818487612796565b9361058585806127d6565b919095602073ffffffffffffffffffffffffffffffffffffffff6105aa82840161282a565b1697600192838a1461076657896105da575b5050505060019293949550906105d191612409565b939291016101be565b8060406105e892019061284b565b918a3b1561019c57929391906040519485937f2dd8113300000000000000000000000000000000000000000000000000000000855288604486016040600488015252606490818601918a60051b8701019680936000915b8c83106106e657505050505050838392610684927ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8560009803016024860152612709565b03818a5afa90816106d7575b506106c657602486604051907f86a9f7500000000000000000000000000000000000000000000000000000000082526004820152fd5b93945084936105d1600189806105bc565b6106e0906121bd565b88610690565b91939596977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c908a9294969a0301865288357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18336030181121561019c57836107538793858394016128ec565b9a0196019301909189979695949261063f565b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601760248201527f4141393620696e76616c69642061676772656761746f720000000000000000006044820152fd5b3461019c576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c576107fc61229f565b33600052600082526001604060002001908154916dffffffffffffffffffffffffffff8360081c16928315610a0a5765ffffffffffff8160981c1680156109ac57421061094e5760009373ffffffffffffffffffffffffffffffffffffffff859485947fffffffffffffff000000000000000000000000000000000000000000000000ff86951690556040517fb7c918e0e249f999e965cafeb6c664271b3f4317d296461500e71da39f0cbda33391806108da8786836020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b0390a2165af16108e8612450565b50156108f057005b606490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601860248201527f6661696c656420746f207769746864726177207374616b6500000000000000006044820152fd5b606485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601b60248201527f5374616b65207769746864726177616c206973206e6f742064756500000000006044820152fd5b606486604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601d60248201527f6d7573742063616c6c20756e6c6f636b5374616b6528292066697273740000006044820152fd5b606485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601460248201527f4e6f207374616b6520746f2077697468647261770000000000000000000000006044820152fd5b3461019c5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c573360005260006020526001604060002001805463ffffffff8160781c16908115610bdc5760ff1615610b7e5765ffffffffffff908142160191818311610b4f5780547fffffffffffffff000000000000ffffffffffffffffffffffffffffffffffff001678ffffffffffff00000000000000000000000000000000000000609885901b161790556040519116815233907ffa9b3c14cc825c412c9ed81b3ba365a5b459439403f18829e572ed53a4180f0a90602090a2005b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f616c726561647920756e7374616b696e670000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f6e6f74207374616b6564000000000000000000000000000000000000000000006044820152fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c57610022610c6f61229f565b612748565b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043567ffffffffffffffff811161019c576020610cc8610d1b9236906004016122c2565b919073ffffffffffffffffffffffffffffffffffffffff9260405194859283927f570e1a360000000000000000000000000000000000000000000000000000000084528560048501526024840191612709565b03816000857f000000000000000000000000efc2c1444ebcc4db75e7613d20c6a62ff67a167c165af1908115610db757602492600092610d86575b50604051917f6ca7b806000000000000000000000000000000000000000000000000000000008352166004820152fd5b610da991925060203d602011610db0575b610da181836121ed565b8101906126dd565b9083610d56565b503d610d97565b6040513d6000823e3d90fd5b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c57610dfa61229f565b60243567ffffffffffffffff811161019c57600091610e1e839236906004016122c2565b90816040519283928337810184815203915af4610e39612450565b90610e7e6040519283927f99410554000000000000000000000000000000000000000000000000000000008452151560048401526040602484015260448301906123c6565b0390fd5b3461019c57610e9036612317565b610e9b9291926129bd565b610ea483612588565b60005b848110610f1c57506000927fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000915b858310610eec576102408585613ba7565b909193600190610f12610f0087898761269d565b610f0a888661265a565b519088613597565b0194019190610edb565b610f47610f40610f2e8385979561265a565b51610f3a84898761269d565b846129f6565b9190613dd7565b73ffffffffffffffffffffffffffffffffffffffff929183166110db5761107657610f7190613dd7565b911661101157610f8657600101929092610ea7565b60a490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f65000000000000000000000000000000000000000000000000000000000000006084820152fd5b608482604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413334207369676e6174757265206572726f720000000000000000000000006064820152fd5b608483604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f414132322065787069726564206f72206e6f74206475650000000000000000006064820152fd5b608484604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413234207369676e6174757265206572726f720000000000000000000000006064820152fd5b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff61118c61229f565b1660005260006020526020604060002054604051908152f35b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff6111f161229f565b6000608060405161120181612155565b828152826020820152826040820152826060820152015216600052600060205260a06040600020608060405161123681612155565b6001835493848352015490602081019060ff8316151582526dffffffffffffffffffffffffffff60408201818560081c16815263ffffffff936060840193858760781c16855265ffffffffffff978891019660981c1686526040519788525115156020880152511660408601525116606084015251166080820152f35b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760206112ec61229f565b73ffffffffffffffffffffffffffffffffffffffff6113096122f0565b911660005260018252604060002077ffffffffffffffffffffffffffffffffffffffffffffffff821660005282526040600020547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006040519260401b16178152f35b3461019c577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60208136011261019c576004359067ffffffffffffffff821161019c5761012090823603011261019c576113c9602091600401612480565b604051908152f35b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5761140861229f565b60243590336000526000602052604060002090815491828411611508576000808573ffffffffffffffffffffffffffffffffffffffff8295839561144c848a612443565b90556040805173ffffffffffffffffffffffffffffffffffffffff831681526020810185905233917fd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb91a2165af16114a2612450565b50156114aa57005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6661696c656420746f20776974686472617700000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f576974686472617720616d6f756e7420746f6f206c61726765000000000000006044820152fd5b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5761159d61229f565b73ffffffffffffffffffffffffffffffffffffffff6115ba6122f0565b9116600052600160205277ffffffffffffffffffffffffffffffffffffffffffffffff604060002091166000526020526020604060002054604051908152f35b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043577ffffffffffffffffffffffffffffffffffffffffffffffff811680910361019c5733600052600160205260406000209060005260205260406000206116728154612416565b9055005b6020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043563ffffffff9182821680920361019c5733600052600081526040600020928215611950576001840154908160781c1683106118f2576116f86dffffffffffffffffffffffffffff9182349160081c16612409565b93841561189457818511611836579065ffffffffffff61180592546040519061172082612155565b8152848101926001845260408201908816815260608201878152600160808401936000855233600052600089526040600020905181550194511515917fffffffffffffffffffffffffff0000000000000000000000000000000000000060ff72ffffffff0000000000000000000000000000006effffffffffffffffffffffffffff008954945160081b16945160781b1694169116171717835551167fffffffffffffff000000000000ffffffffffffffffffffffffffffffffffffff78ffffffffffff0000000000000000000000000000000000000083549260981b169116179055565b6040519283528201527fa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c0160403392a2005b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152600e60248201527f7374616b65206f766572666c6f770000000000000000000000000000000000006044820152fd5b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601260248201527f6e6f207374616b652073706563696669656400000000000000000000000000006044820152fd5b606482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601c60248201527f63616e6e6f7420646563726561736520756e7374616b652074696d65000000006044820152fd5b606482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601a60248201527f6d757374207370656369667920756e7374616b652064656c61790000000000006044820152fd5b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c576004357fffffffff00000000000000000000000000000000000000000000000000000000811680910361019c57807f60fc6b6e0000000000000000000000000000000000000000000000000000000060209214908115611ad6575b8115611aac575b8115611a82575b8115611a58575b506040519015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501482611a4d565b7f3e84f0210000000000000000000000000000000000000000000000000000000081149150611a46565b7fcf28ef970000000000000000000000000000000000000000000000000000000081149150611a3f565b7f915074d80000000000000000000000000000000000000000000000000000000081149150611a38565b3461019c576102007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5767ffffffffffffffff60043581811161019c573660238201121561019c57611b62903690602481600401359101612268565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016101c0811261019c5761014060405191611b9e83612155565b1261019c5760405192611bb0846121a0565b60243573ffffffffffffffffffffffffffffffffffffffff8116810361019c578452602093604435858201526064356040820152608435606082015260a435608082015260c43560a082015260e43560c08201526101043573ffffffffffffffffffffffffffffffffffffffff8116810361019c5760e08201526101243561010082015261014435610120820152825261016435848301526101843560408301526101a43560608301526101c43560808301526101e43590811161019c57611c7c9036906004016122c2565b905a3033036120f7578351606081015195603f5a0260061c61271060a0840151890101116120ce5760009681519182611ff0575b5050505090611cca915a9003608085015101923691612268565b925a90600094845193611cdc85613ccc565b9173ffffffffffffffffffffffffffffffffffffffff60e0870151168015600014611ea957505073ffffffffffffffffffffffffffffffffffffffff855116935b5a9003019360a06060820151910151016080860151850390818111611e95575b50508302604085015192818410600014611dce5750506003811015611da157600203611d79576113c99293508093611d7481613d65565b613cf6565b5050507fdeadaa51000000000000000000000000000000000000000000000000000000008152fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b81611dde92979396940390613c98565b506003841015611e6857507f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f60808683015192519473ffffffffffffffffffffffffffffffffffffffff865116948873ffffffffffffffffffffffffffffffffffffffff60e0890151169701519160405192835215898301528760408301526060820152a46113c9565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526021600452fd5b6064919003600a0204909301928780611d3d565b8095918051611eba575b5050611d1d565b6003861015611fc1576002860315611eb35760a088015190823b1561019c57600091611f2491836040519586809581947f7c627b210000000000000000000000000000000000000000000000000000000083528d60048401526080602484015260848301906123c6565b8b8b0260448301528b60648301520393f19081611fad575b50611fa65787893d610800808211611f9e575b506040519282828501016040528184528284013e610e7e6040519283927fad7954bc000000000000000000000000000000000000000000000000000000008452600484015260248301906123c6565b905083611f4f565b8980611eb3565b611fb89199506121bd565b6000978a611f3c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91600092918380938c73ffffffffffffffffffffffffffffffffffffffff885116910192f115612023575b808080611cb0565b611cca929195503d6108008082116120c6575b5060405190888183010160405280825260008983013e805161205f575b5050600194909161201b565b7f1c4fada7374c0a9ee8841fc38afe82932dc0f8e69012e927f061a8bae611a20188870151918973ffffffffffffffffffffffffffffffffffffffff8551169401516120bc604051928392835260408d84015260408301906123c6565b0390a38680612053565b905088612036565b877fdeaddead000000000000000000000000000000000000000000000000000000006000526000fd5b606486604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601760248201527f4141393220696e7465726e616c2063616c6c206f6e6c790000000000000000006044820152fd5b60a0810190811067ffffffffffffffff82111761217157604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610140810190811067ffffffffffffffff82111761217157604052565b67ffffffffffffffff811161217157604052565b6060810190811067ffffffffffffffff82111761217157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761217157604052565b67ffffffffffffffff811161217157601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926122748261222e565b9161228260405193846121ed565b82948184528183011161019c578281602093846000960137010152565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361019c57565b9181601f8401121561019c5782359167ffffffffffffffff831161019c576020838186019501011161019c57565b6024359077ffffffffffffffffffffffffffffffffffffffffffffffff8216820361019c57565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261019c5760043567ffffffffffffffff9283821161019c578060238301121561019c57816004013593841161019c5760248460051b8301011161019c57602401919060243573ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b60005b8381106123b65750506000910152565b81810151838201526020016123a6565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093612402815180928187528780880191016123a3565b0116010190565b91908201809211610b4f57565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610b4f5760010190565b91908203918211610b4f57565b3d1561247b573d906124618261222e565b9161246f60405193846121ed565b82523d6000602084013e565b606090565b604061248e8183018361284b565b90818351918237206124a3606084018461284b565b90818451918237209260c06124bb60e083018361284b565b908186519182372091845195602087019473ffffffffffffffffffffffffffffffffffffffff833516865260208301358789015260608801526080870152608081013560a087015260a081013582870152013560e08501526101009081850152835261012083019167ffffffffffffffff918484108385111761217157838252845190206101408501908152306101608601524661018086015260608452936101a00191821183831017612171575251902090565b67ffffffffffffffff81116121715760051b60200190565b9061259282612570565b6040906125a260405191826121ed565b8381527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06125d08295612570565b019160005b8381106125e25750505050565b60209082516125f081612155565b83516125fb816121a0565b600081526000849181838201528187820152816060818184015260809282848201528260a08201528260c08201528260e082015282610100820152826101208201528652818587015281898701528501528301528286010152016125d5565b805182101561266e5760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b919081101561266e5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18136030182121561019c570190565b9081602091031261019c575173ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b7f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c4602073ffffffffffffffffffffffffffffffffffffffff61278a3485613c98565b936040519485521692a2565b919081101561266e5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18136030182121561019c570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561019c570180359067ffffffffffffffff821161019c57602001918160051b3603831361019c57565b3573ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561019c570180359067ffffffffffffffff821161019c5760200191813603831361019c57565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561019c57016020813591019167ffffffffffffffff821161019c57813603831361019c57565b61012091813573ffffffffffffffffffffffffffffffffffffffff811680910361019c576129626129476129ba9561299b93855260208601356020860152612937604087018761289c565b9091806040880152860191612709565b612954606086018661289c565b908583036060870152612709565b6080840135608084015260a084013560a084015260c084013560c084015261298d60e085018561289c565b9084830360e0860152612709565b916129ac610100918281019061289c565b929091818503910152612709565b90565b60028054146129cc5760028055565b60046040517f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b926000905a93805194843573ffffffffffffffffffffffffffffffffffffffff811680910361019c5786526020850135602087015260808501356fffffffffffffffffffffffffffffffff90818116606089015260801c604088015260a086013560c088015260c086013590811661010088015260801c610120870152612a8060e086018661284b565b801561357b576034811061351d578060141161019c578060241161019c5760341161019c57602481013560801c60a0880152601481013560801c60808801523560601c60e08701525b612ad285612480565b60208301526040860151946effffffffffffffffffffffffffffff8660c08901511760608901511760808901511760a0890151176101008901511761012089015117116134bf57604087015160608801510160808801510160a08801510160c0880151016101008801510296835173ffffffffffffffffffffffffffffffffffffffff81511690612b66604085018561284b565b806131e4575b505060e0015173ffffffffffffffffffffffffffffffffffffffff1690600082156131ac575b6020612bd7918b828a01516000868a604051978896879586937f19822f7c00000000000000000000000000000000000000000000000000000000855260048501613db5565b0393f160009181613178575b50612c8b573d8c610800808311612c83575b50604051916020818401016040528083526000602084013e610e7e6040519283927f65c8fd4d000000000000000000000000000000000000000000000000000000008452600484015260606024840152600d60648401527f4141323320726576657274656400000000000000000000000000000000000000608484015260a0604484015260a48301906123c6565b915082612bf5565b9a92939495969798999a91156130f2575b509773ffffffffffffffffffffffffffffffffffffffff835116602084015190600052600160205260406000208160401c60005260205267ffffffffffffffff604060002091825492612cee84612416565b9055160361308d575a8503116130285773ffffffffffffffffffffffffffffffffffffffff60e0606093015116612d42575b509060a09184959697986040608096015260608601520135905a900301910152565b969550505a9683519773ffffffffffffffffffffffffffffffffffffffff60e08a01511680600052600060205260406000208054848110612fc3576080612dcd9a9b9c600093878094039055015192602089015183604051809d819582947f52b7512c0000000000000000000000000000000000000000000000000000000084528c60048501613db5565b039286f1978860009160009a612f36575b50612e86573d8b610800808311612e7e575b50604051916020818401016040528083526000602084013e610e7e6040519283927f65c8fd4d000000000000000000000000000000000000000000000000000000008452600484015260606024840152600d60648401527f4141333320726576657274656400000000000000000000000000000000000000608484015260a0604484015260a48301906123c6565b915082612df0565b9991929394959697989998925a900311612eab57509096959094939291906080612d20565b60a490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602760448201527f41413336206f766572207061796d6173746572566572696669636174696f6e4760648201527f61734c696d6974000000000000000000000000000000000000000000000000006084820152fd5b915098503d90816000823e612f4b82826121ed565b604081838101031261019c5780519067ffffffffffffffff821161019c57828101601f83830101121561019c578181015191612f868361222e565b93612f9460405195866121ed565b838552820160208483850101011161019c57602092612fba9184808701918501016123a3565b01519838612dde565b60848b604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601e60448201527f41413331207061796d6173746572206465706f73697420746f6f206c6f7700006064820152fd5b608490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601e60448201527f41413236206f76657220766572696669636174696f6e4761734c696d697400006064820152fd5b608482604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601a60448201527f4141323520696e76616c6964206163636f756e74206e6f6e63650000000000006064820152fd5b600052600060205260406000208054808c11613113578b9003905538612c9c565b608484604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f41413231206469646e2774207061792070726566756e640000000000000000006064820152fd5b9091506020813d6020116131a4575b81613194602093836121ed565b8101031261019c57519038612be3565b3d9150613187565b508060005260006020526040600020548a81116000146131d75750612bd7602060005b915050612b92565b6020612bd7918c036131cf565b833b61345a57604088510151602060405180927f570e1a360000000000000000000000000000000000000000000000000000000082528260048301528160008161323260248201898b612709565b039273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000efc2c1444ebcc4db75e7613d20c6a62ff67a167c1690f1908115610db75760009161343b575b5073ffffffffffffffffffffffffffffffffffffffff811680156133d6578503613371573b1561330c5760141161019c5773ffffffffffffffffffffffffffffffffffffffff9183887fd51a9c61267aa6196961883ecf5ff2da6619c37dac0fa92122513fb32c032d2d604060e0958787602086015195510151168251913560601c82526020820152a391612b6c565b60848d604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602060448201527f4141313520696e6974436f6465206d757374206372656174652073656e6465726064820152fd5b60848e604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602060448201527f4141313420696e6974436f6465206d7573742072657475726e2073656e6465726064820152fd5b60848f604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601b60448201527f4141313320696e6974436f6465206661696c6564206f72204f4f4700000000006064820152fd5b613454915060203d602011610db057610da181836121ed565b3861327c565b60848d604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601f60448201527f414131302073656e64657220616c726561647920636f6e7374727563746564006064820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f41413934206761732076616c756573206f766572666c6f7700000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4141393320696e76616c6964207061796d6173746572416e64446174610000006044820152fd5b5050600060e087015260006080870152600060a0870152612ac9565b9092915a906060810151916040928351967fffffffff00000000000000000000000000000000000000000000000000000000886135d7606084018461284b565b600060038211613b9f575b7f8dd7712f0000000000000000000000000000000000000000000000000000000094168403613a445750505061379d6000926136b292602088015161363a8a5193849360208501528b602485015260648401906128ec565b90604483015203906136727fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0928381018352826121ed565b61379189519485927e42dc5300000000000000000000000000000000000000000000000000000000602085015261020060248501526102248401906123c6565b613760604484018b60806101a091805173ffffffffffffffffffffffffffffffffffffffff808251168652602082015160208701526040820151604087015260608201516060870152838201518487015260a082015160a087015260c082015160c087015260e08201511660e0860152610100808201519086015261012080910151908501526020810151610140850152604081015161016085015260608101516101808501520151910152565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83820301610204840152876123c6565b039081018352826121ed565b6020918183809351910182305af1600051988652156137bf575b505050505050565b909192939495965060003d8214613a3a575b7fdeaddead00000000000000000000000000000000000000000000000000000000810361385b57608487878051917f220266b600000000000000000000000000000000000000000000000000000000835260048301526024820152600f60448201527f41413935206f7574206f662067617300000000000000000000000000000000006064820152fd5b7fdeadaa510000000000000000000000000000000000000000000000000000000091929395949650146000146138c55750506138a961389e6138b8935a90612443565b608085015190612409565b9083015183611d748295613d65565b905b3880808080806137b7565b909261395290828601518651907ff62676f440ff169a3a9afdbf812e89e7f95975ee8e5c31214ffdef631c5f479273ffffffffffffffffffffffffffffffffffffffff9580878551169401516139483d610800808211613a32575b508a519084818301018c5280825260008583013e8a805194859485528401528a8301906123c6565b0390a35a90612443565b916139636080860193845190612409565b926000905a94829488519761397789613ccc565b948260e08b0151168015600014613a1857505050875116955b5a9003019560a06060820151910151019051860390818111613a04575b5050840290850151928184106000146139de57505080611e68575090816139d89293611d7481613d65565b906138ba565b6139ee9082849397950390613c98565b50611e68575090826139ff92613cf6565b6139d8565b6064919003600a02049094019338806139ad565b90919892509751613a2a575b50613990565b955038613a24565b905038613920565b8181803e516137d1565b613b97945082935090613a8c917e42dc53000000000000000000000000000000000000000000000000000000006020613b6b9501526102006024860152610224850191612709565b613b3a604484018860806101a091805173ffffffffffffffffffffffffffffffffffffffff808251168652602082015160208701526040820151604087015260608201516060870152838201518487015260a082015160a087015260c082015160c087015260e08201511660e0860152610100808201519086015261012080910151908501526020810151610140850152604081015161016085015260608101516101808501520151910152565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83820301610204840152846123c6565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018952886121ed565b60008761379d565b5081356135e2565b73ffffffffffffffffffffffffffffffffffffffff168015613c3a57600080809381935af1613bd4612450565b5015613bdc57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f41413931206661696c65642073656e6420746f2062656e6566696369617279006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4141393020696e76616c69642062656e656669636961727900000000000000006044820152fd5b73ffffffffffffffffffffffffffffffffffffffff166000526000602052613cc66040600020918254612409565b80915590565b610120610100820151910151808214613cf257480180821015613ced575090565b905090565b5090565b9190917f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f6080602083015192519473ffffffffffffffffffffffffffffffffffffffff946020868851169660e089015116970151916040519283526000602084015260408301526060820152a4565b60208101519051907f67b4fa9642f42120bf031f3051d1824b0fe25627945b27b8a6a65d5761d5482e60208073ffffffffffffffffffffffffffffffffffffffff855116940151604051908152a3565b613dcd604092959493956060835260608301906128ec565b9460208201520152565b8015613e6457600060408051613dec816121d1565b828152826020820152015273ffffffffffffffffffffffffffffffffffffffff811690604065ffffffffffff91828160a01c16908115613e5c575b60d01c92825191613e37836121d1565b8583528460208401521691829101524211908115613e5457509091565b905042109091565b839150613e27565b5060009060009056fea2646970667358221220b094fd69f04977ae9458e5ba422d01cd2d20dbcfca0992ff37f19aa07deec25464736f6c63430008170033", + "nonce": "2", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x83d2064309e31181791f895d99cc244865c480f99200cd4d4f2412a7a3a265b6": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xe18cacf0f1fc038f916461d29104232bd93d822359dfc137927858cc71f1e4ed": "0x0000000000000000000000000000000000000000000000001bc16d674ec80000" + } + }, + "0x8c9d927336adc963536122f8e0d269319e79ed7a": { + "balance": "0x0", + "code": "0x60806040526004361015610015575b3661039257005b61001f5f3561007e565b806319822f7c14610079578063408aee4214610074578063451711591461006f578063a9cc47181461006a578063a9e966b7146100655763c19d93fb0361000e5761035d565b6102ee565b6102b6565b61028c565b6101b3565b61016e565b60e01c90565b60405190565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b90816101209103126100a95790565b610096565b90565b6100ba816100ae565b036100c157565b5f80fd5b905035906100d2826100b1565b565b90565b6100e0816100d4565b036100e757565b5f80fd5b905035906100f8826100d7565b565b9091606082840312610147575f82013567ffffffffffffffff8111610142576101288461013f92850161009a565b9361013681602086016100c5565b936040016100eb565b90565b610092565b61008e565b610155906100d4565b9052565b919061016c905f6020850194019061014c565b565b3461019f5761019b61018a6101843660046100fa565b916107d1565b610192610084565b91829182610159565b0390f35b61008a565b5f9103126101ae57565b61008e565b346101e3576101c33660046101a4565b6101df6101ce610988565b6101d6610084565b91829182610159565b0390f35b61008a565b60018060a01b031690565b6101fc906101e8565b90565b610208906101f3565b90565b610214816101ff565b0361021b57565b5f80fd5b9050359061022c8261020b565b565b63ffffffff1690565b6102408161022e565b0361024757565b5f80fd5b9050359061025882610237565b565b9190604083820312610282578061027661027f925f860161021f565b9360200161024b565b90565b61008e565b5f0190565b6102a061029a36600461025a565b906109de565b6102a8610084565b806102b281610287565b0390f35b346102cb576102c63660046101a4565b610ac3565b61008a565b906020828203126102e9576102e6915f016100eb565b90565b61008e565b3461031c576103066103013660046102d0565b610b3f565b61030e610084565b8061031881610287565b0390f35b61008a565b1c90565b90565b61033890600861033d9302610321565b610325565b90565b9061034b9154610328565b90565b61035a60015f90610340565b90565b3461038d5761036d3660046101a4565b61038961037861034e565b610380610084565b91829182610159565b0390f35b61008a565b5f80fd5b5f90565b5f80fd5b5f80fd5b5f80fd5b9035906001602003813603038212156103e8570180359067ffffffffffffffff82116103e3576020019160018202360383136103de57565b6103a2565b61039e565b61039a565b5090565b90565b90565b61040b610406610410926103f1565b6103f4565b6100d4565b90565b6bffffffffffffffffffffffff191690565b1b90565b9061043761043e91836103ed565b9135610413565b906014811061044c575b5090565b61046a906bffffffffffffffffffffffff1990601403600802610425565b165f610448565b60601c90565b61048b610486610490926101e8565b6103f4565b6101e8565b90565b61049f6104a491610471565b610477565b90565b6104b090610493565b90565b6104bc90610477565b90565b6104c8906104b3565b90565b6104d490610477565b90565b6104e0906104cb565b90565b6104ec906104cb565b90565b5f80fd5b601f801991011690565b634e487b7160e01b5f52604160045260245ffd5b9061051b906104f3565b810190811067ffffffffffffffff82111761053557604052565b6104fd565b60e01b90565b9050519061054d826100d7565b565b9060208282031261056857610565915f01610540565b90565b61008e565b610576906101f3565b9052565b919061058d905f6020850194019061056d565b565b610597610084565b3d5f823e3d90fd5b90565b6105b66105b16105bb9261059f565b6103f4565b6100d4565b90565b905090565b6105ce5f80926105be565b0190565b6105db906105c3565b90565b906105f16105ea610084565b9283610511565b565b67ffffffffffffffff81116106115761060d6020916104f3565b0190565b6104fd565b90610628610623836105f3565b6105de565b918252565b606090565b3d5f1461064d576106423d610616565b903d5f602084013e5b565b61065561062d565b9061064b565b61ffff60f01b1690565b9061067361067a91836103ed565b913561065b565b9060028110610688575b5090565b61069e9061ffff60f01b90600203600802610425565b165f610684565b90565b60f01b90565b6106c26106bd6106c7926106a5565b6106a8565b61065b565b90565b60209181520190565b5f7f7465737457616c6c65743a2064656164207369676e6174757265000000000000910152565b610707601a6020926106ca565b610710816106d3565b0190565b6107299060208101905f8183039101526106fa565b90565b1561073357565b61073b610084565b62461bcd60e51b81528061075160048201610714565b0390fd5b90565b61076c61076761077192610755565b6106a8565b61065b565b90565b60ff1690565b61078e6107896107939261059f565b6103f4565b610774565b90565b90565b6107ad6107a86107b292610796565b6103f4565b610774565b90565b6107c96107c46107ce92610774565b6103f4565b6100d4565b90565b9190506107dc610396565b506107f46107ee8360608101906103a6565b906103ed565b61080761080160146103f7565b916100d4565b146108cd575b610840918161083a926108286108225f6105a2565b916100d4565b116108a1575b506101008101906103a6565b90610665565b61085f8161085861085261dead6106ae565b9161065b565b141561072c565b61087361086d61deaf610758565b9161065b565b145f146108905761088d6108876001610799565b5b6107b5565b90565b61088d61089c5f61077a565b610888565b5f809133906108ae610084565b90816108b9816105d2565b03925af1506108c6610632565b505f61082e565b8160206109026108fd6108f86108f36108ed6109359860608101906103a6565b90610429565b6104a7565b6104bf565b6104d7565b631bab58f59061092a5f610915306104e3565b9361091e610084565b9889958694859361053a565b83526004830161057a565b03925af1918215610983576108409361083a93610957575b509150915061080d565b6109779060203d811161097c575b61096f8183610511565b81019061054f565b61094d565b503d610965565b61058f565b610990610396565b5060015f5d61099e5f6105a2565b90565b6109aa906104cb565b90565b5f9103126109b757565b61008e565b6109c59061022e565b9052565b91906109dc905f602085019401906109bc565b565b6109e7906109a1565b90630396cb60349290929190803b15610a6557610a175f93610a2295610a0b610084565b9687958694859361053a565b8352600483016109c9565b03925af18015610a6057610a34575b50565b610a53905f3d8111610a59575b610a4b8183610511565b8101906109ad565b5f610a31565b503d610a41565b61058f565b6104ef565b5f7f74657374206661696c0000000000000000000000000000000000000000000000910152565b610a9e60096020926106ca565b610aa781610a6a565b0190565b610ac09060208101905f818303910152610a91565b90565b610acb610084565b62461bcd60e51b815280610ae160048201610aab565b0390fd5b5f1b90565b90610af65f1991610ae5565b9181191691161790565b610b14610b0f610b19926100d4565b6103f4565b6100d4565b90565b90565b90610b34610b2f610b3b92610b00565b610b1c565b8254610aea565b9055565b610b4a906001610b1f565b56fea2646970667358221220b38b9fc26f62a27821d9b7ec4f8d872db3cb781cf35b39974360011b61dcacec64736f6c63430008190033", + "nonce": "1", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { + "balance": "0xda71b71dbd3438e", + "nonce": "2" + } + }, + "config": { + "chainId": 1337, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "rip7560block": 0, + "rip7712block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "shanghaiTime": 0, + "cancunTime": 0, + "pragueTime": 0, + "terminalTotalDifficulty": 0, + "depositContractAddress": "0x0000000000000000000000000000000000000000", + "blobSchedule": { + "cancun": { + "max": 6, + "target": 3, + "baseFeeUpdateFraction": 3338477 + }, + "prague": { + "max": 9, + "target": 6, + "baseFeeUpdateFraction": 5007716 + } + } + } + }, + "context": { + "number": "88", + "difficulty": "0", + "timestamp": "1738267405", + "gasLimit": "30000000", + "miner": "0x0000000000000000000000000000000000000000", + "baseFeePerGas": "37852" + }, + "input": "0x02f902f3820539028459682f008459697e8a8305fe79940000000071727de22e5e9d8baf0edac6f37da03280b90284765e827f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000008c9d927336adc963536122f8e0d269319e79ed7a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000f4240000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000493e0000000000000000000000000b2d05e00000000000000000000000000ee6b280000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024a9e966b7000000000000000000000000000000000000000000000000000000000010f4470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002face000000000000000000000000000000000000000000000000000000000000c080a076f549a86d5973384cf79cabbcf2d9ab0ae3478e8d4b5c643d1e45ea9a57af51a052dd4f3bc56de5c971dbcad1dc5d3ea3a6e7fc037cfb9447ce4a73a12db7430d", + "result": { + "accessedSlots": { + "reads": { + "0x0000000000000000000000000000000000000000000000000000000000000002": [ + "0x0000000000000000000000000000000000000000000000000000000000000001" + ], + "0x83d2064309e31181791f895d99cc244865c480f99200cd4d4f2412a7a3a265b6": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "0xe18cacf0f1fc038f916461d29104232bd93d822359dfc137927858cc71f1e4ed": [ + "0x0000000000000000000000000000000000000000000000001bc16d674ec80000" + ] + }, + "transientReads": {}, + "transientWrites": {}, + "writes": { + "0x0000000000000000000000000000000000000000000000000000000000000002": 2, + "0x83d2064309e31181791f895d99cc244865c480f99200cd4d4f2412a7a3a265b6": 1, + "0xe18cacf0f1fc038f916461d29104232bd93d822359dfc137927858cc71f1e4ed": 1 + } + }, + "calls": [ + { + "accessedSlots": { + "reads": {}, + "transientReads": {}, + "transientWrites": {}, + "writes": {} + }, + "contractSize": {}, + "extCodeAccessInfo": [], + "from": "0x0000000071727de22e5e9d8baf0edac6f37da032", + "gas": "0x54dda", + "gasUsed": "0x8ec", + "input": "0x19822f7c000000000000000000000000000000000000000000000000000000000000006088a9b2626e43da02f978ae6cc89feffb68afcd5860cb9239337352db4b694fe100000000000000000000000000000000000000000000000000000000000000000000000000000000000000008c9d927336adc963536122f8e0d269319e79ed7a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000f4240000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000493e0000000000000000000000000b2d05e00000000000000000000000000ee6b280000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024a9e966b7000000000000000000000000000000000000000000000000000000000010f4470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002face000000000000000000000000000000000000000000000000000000000000", + "outOfGas": false, + "output": "0x0000000000000000000000000000000000000000000000000000000000000000", + "to": "0x8c9d927336adc963536122f8e0d269319e79ed7a", + "type": "CALL", + "usedOpcodes": {"0x34":1, "0x35":9, "0x36":6, "0x51":1, "0x52":2, "0x56":102, "0x57":19, "0x5b":108, "0xf3":1}, + "value": "0x0" + }, + { + "accessedSlots": { + "reads": { + "0xe18cacf0f1fc038f916461d29104232bd93d822359dfc137927858cc71f1e4ed": [ + "0x0000000000000000000000000000000000000000000000001baab0a330380000" + ] + }, + "transientReads": {}, + "transientWrites": {}, + "writes": { + "0xe18cacf0f1fc038f916461d29104232bd93d822359dfc137927858cc71f1e4ed": 1 + } + }, + "calls": [ + { + "accessedSlots": { + "reads": { + "0x0000000000000000000000000000000000000000000000000000000000000001": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ] + }, + "transientReads": {}, + "transientWrites": {}, + "writes": { + "0x0000000000000000000000000000000000000000000000000000000000000001": 1 + } + }, + "contractSize": {}, + "extCodeAccessInfo": [], + "from": "0x0000000071727de22e5e9d8baf0edac6f37da032", + "gas": "0x493e0", + "gasUsed": "0x5956", + "input": "0xa9e966b7000000000000000000000000000000000000000000000000000000000010f447", + "outOfGas": false, + "to": "0x8c9d927336adc963536122f8e0d269319e79ed7a", + "type": "CALL", + "usedOpcodes": {"0x34":1, "0x35":2, "0x36":2, "0x51":1, "0x52":1, "0x54":1, "0x55":1, "0x56":33, "0x57":9, "0x5b":35, "0xf3":1}, + "value": "0x0" + } + ], + "contractSize": { + "0x8c9d927336adc963536122f8e0d269319e79ed7a": { + "contractSize": 2946, + "opcode": 241 + } + }, + "extCodeAccessInfo": [], + "from": "0x0000000071727de22e5e9d8baf0edac6f37da032", + "gas": "0x4d7ac", + "gasUsed": "0x6ff7", + "input": "0x0042dc5300000000000000000000000000000000000000000000000000000000000002000000000000000000000000008c9d927336adc963536122f8e0d269319e79ed7a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000493e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee6b280000000000000000000000000000000000000000000000000000000000b2d05e0088a9b2626e43da02f978ae6cc89feffb68afcd5860cb9239337352db4b694fe10000000000000000000000000000000000000000000000000016bcc41e900000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000522d600000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000024a9e966b7000000000000000000000000000000000000000000000000000000000010f447000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "outOfGas": false, + "output": "0x000000000000000000000000000000000000000000000000000421bab3f40fbc", + "to": "0x0000000071727de22e5e9d8baf0edac6f37da032", + "type": "CALL", + "usedOpcodes": {"0x20":1, "0x30":1, "0x33":1, "0x34":1, "0x35":19, "0x36":7, "0x37":2, "0x48":1, "0x51":26, "0x52":31, "0x54":1, "0x55":1, "0x56":27, "0x57":33, "0x5a":5, "0x5b":35, "0xa4":1, "0xf1":1, "0xf3":1}, + "value": "0x0" + }, + { + "accessedSlots": { + "reads": {}, + "transientReads": {}, + "transientWrites": {}, + "writes": {} + }, + "contractSize": {}, + "extCodeAccessInfo": [], + "from": "0x0000000071727de22e5e9d8baf0edac6f37da032", + "gas": "0x44ece", + "gasUsed": "0x0", + "input": "0x", + "outOfGas": false, + "to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "type": "CALL", + "usedOpcodes": {}, + "value": "0x421bab3f40fbc" + } + ], + "contractSize": { + "0x0000000071727de22e5e9d8baf0edac6f37da032": { + "contractSize": 16035, + "opcode": 241 + }, + "0x8c9d927336adc963536122f8e0d269319e79ed7a": { + "contractSize": 2946, + "opcode": 241 + }, + "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { + "contractSize": 0, + "opcode": 241 + } + }, + "extCodeAccessInfo": [], + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "gas": "0x5fe79", + "gasUsed": "0x19415", + "input": "0x765e827f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000008c9d927336adc963536122f8e0d269319e79ed7a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000f4240000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000493e0000000000000000000000000b2d05e00000000000000000000000000ee6b280000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024a9e966b7000000000000000000000000000000000000000000000000000000000010f4470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002face000000000000000000000000000000000000000000000000000000000000", + "keccak": [ + "0x", + "0x0000000000000000000000000000000000000000000000000000000000000000916f81e4e1b2122d13f0474f4c323777192f91bb579723004f6f3062b5fedc68", + "0x0000000000000000000000008c9d927336adc963536122f8e0d269319e79ed7a0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000008c9d927336adc963536122f8e0d269319e79ed7a0000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470ede3d138a3c0ac5537239d818f52c7c86b466472a500982b0e7dff43ab38975d000000000000000000000000000f4240000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000493e0000000000000000000000000b2d05e00000000000000000000000000ee6b2800c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "0x0000000000000000000000008c9d927336adc963536122f8e0d269319e79ed7a0000000000000000000000000000000000000000000000000000000000000001", + "0xa9e966b7000000000000000000000000000000000000000000000000000000000010f447", + "0xc72bc304a44b01f425579bd71219ccd5676c732ce2aed103da05dc145f00fe340000000000000000000000000000000071727de22e5e9d8baf0edac6f37da0320000000000000000000000000000000000000000000000000000000000000539" + ], + "outOfGas": false, + "to": "0x0000000071727de22e5e9d8baf0edac6f37da032", + "type": "CALL", + "usedOpcodes": {"0x0":1, "0x20":9, "0x30":2, "0x34":1, "0x35":43, "0x36":24, "0x37":8, "0x38":2, "0x3d":2, "0x46":1, "0x51":56, "0x52":104, "0x54":4, "0x55":4, "0x56":92, "0x57":101, "0x5a":4, "0x5b":116, "0xa1":1, "0xf1":3}, + "value": "0x0" + } +} \ No newline at end of file diff --git a/eth/tracers/native/erc7562.go b/eth/tracers/native/erc7562.go new file mode 100644 index 00000000000..3ab98c7132a --- /dev/null +++ b/eth/tracers/native/erc7562.go @@ -0,0 +1,530 @@ +// 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 native + +import ( + "bytes" + "encoding/json" + "errors" + "math/big" + "slices" + "sync/atomic" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/internal" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" +) + +//go:generate go run github.com/fjl/gencodec -type callFrameWithOpcodes -field-override callFrameWithOpcodesMarshaling -out gen_callframewithopcodes_json.go + +func init() { + tracers.DefaultDirectory.Register("erc7562Tracer", newErc7562Tracer, false) +} + +type contractSizeWithOpcode struct { + ContractSize int `json:"contractSize"` + Opcode vm.OpCode `json:"opcode"` +} + +type callFrameWithOpcodes struct { + Type vm.OpCode `json:"-"` + From common.Address `json:"from"` + Gas uint64 `json:"gas"` + GasUsed uint64 `json:"gasUsed"` + To *common.Address `json:"to,omitempty" rlp:"optional"` + Input []byte `json:"input" rlp:"optional"` + Output []byte `json:"output,omitempty" rlp:"optional"` + Error string `json:"error,omitempty" rlp:"optional"` + RevertReason string `json:"revertReason,omitempty"` + Logs []callLog `json:"logs,omitempty" rlp:"optional"` + Value *big.Int `json:"value,omitempty" rlp:"optional"` + revertedSnapshot bool + + AccessedSlots accessedSlots `json:"accessedSlots"` + ExtCodeAccessInfo []common.Address `json:"extCodeAccessInfo"` + UsedOpcodes map[vm.OpCode]uint64 `json:"usedOpcodes"` + ContractSize map[common.Address]*contractSizeWithOpcode `json:"contractSize"` + OutOfGas bool `json:"outOfGas"` + // Keccak preimages for the whole transaction are stored in the + // root call frame. + KeccakPreimages [][]byte `json:"keccak,omitempty"` + Calls []callFrameWithOpcodes `json:"calls,omitempty" rlp:"optional"` +} + +func (f callFrameWithOpcodes) TypeString() string { + return f.Type.String() +} + +func (f callFrameWithOpcodes) failed() bool { + return len(f.Error) > 0 && f.revertedSnapshot +} + +func (f *callFrameWithOpcodes) processOutput(output []byte, err error, reverted bool) { + output = common.CopyBytes(output) + // Clear error if tx wasn't reverted. This happened + // for pre-homestead contract storage OOG. + if err != nil && !reverted { + err = nil + } + if err == nil { + f.Output = output + return + } + f.Error = err.Error() + f.revertedSnapshot = reverted + if f.Type == vm.CREATE || f.Type == vm.CREATE2 { + f.To = nil + } + if !errors.Is(err, vm.ErrExecutionReverted) || len(output) == 0 { + return + } + f.Output = output + if len(output) < 4 { + return + } + if unpacked, err := abi.UnpackRevert(output); err == nil { + f.RevertReason = unpacked + } +} + +type callFrameWithOpcodesMarshaling struct { + TypeString string `json:"type"` + Gas hexutil.Uint64 + GasUsed hexutil.Uint64 + Value *hexutil.Big + Input hexutil.Bytes + Output hexutil.Bytes + UsedOpcodes map[hexutil.Uint64]uint64 + KeccakPreimages []hexutil.Bytes +} + +type accessedSlots struct { + Reads map[common.Hash][]common.Hash `json:"reads"` + Writes map[common.Hash]uint64 `json:"writes"` + TransientReads map[common.Hash]uint64 `json:"transientReads"` + TransientWrites map[common.Hash]uint64 `json:"transientWrites"` +} + +type opcodeWithPartialStack struct { + Opcode vm.OpCode + StackTopItems []uint256.Int +} + +type erc7562Tracer struct { + config erc7562TracerConfig + gasLimit uint64 + interrupt atomic.Bool // Atomic flag to signal execution interruption + reason error // Textual reason for the interruption + env *tracing.VMContext + + ignoredOpcodes map[vm.OpCode]struct{} + callstackWithOpcodes []callFrameWithOpcodes + lastOpWithStack *opcodeWithPartialStack + keccakPreimages map[string]struct{} +} + +// newErc7562Tracer returns a native go tracer which tracks +// call frames of a tx, and implements vm.EVMLogger. +func newErc7562Tracer(ctx *tracers.Context, cfg json.RawMessage, _ *params.ChainConfig) (*tracers.Tracer, error) { + t, err := newErc7562TracerObject(cfg) + if err != nil { + return nil, err + } + return &tracers.Tracer{ + Hooks: &tracing.Hooks{ + OnTxStart: t.OnTxStart, + OnOpcode: t.OnOpcode, + OnTxEnd: t.OnTxEnd, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnLog: t.OnLog, + }, + GetResult: t.GetResult, + Stop: t.Stop, + }, nil +} + +type erc7562TracerConfig struct { + StackTopItemsSize int `json:"stackTopItemsSize"` + IgnoredOpcodes []hexutil.Uint64 `json:"ignoredOpcodes"` // Opcodes to ignore during OnOpcode hook execution + WithLog bool `json:"withLog"` // If true, erc7562 tracer will collect event logs +} + +func getFullConfiguration(partial erc7562TracerConfig) erc7562TracerConfig { + config := partial + + if config.IgnoredOpcodes == nil { + config.IgnoredOpcodes = defaultIgnoredOpcodes() + } + if config.StackTopItemsSize == 0 { + config.StackTopItemsSize = 3 + } + + return config +} + +func newErc7562TracerObject(cfg json.RawMessage) (*erc7562Tracer, error) { + var config erc7562TracerConfig + if cfg != nil { + if err := json.Unmarshal(cfg, &config); err != nil { + return nil, err + } + } + fullConfig := getFullConfiguration(config) + // Create a map of ignored opcodes for fast lookup + ignoredOpcodes := make(map[vm.OpCode]struct{}, len(fullConfig.IgnoredOpcodes)) + for _, op := range fullConfig.IgnoredOpcodes { + ignoredOpcodes[vm.OpCode(op)] = struct{}{} + } + // First callframe contains tx context info + // and is populated on start and end. + return &erc7562Tracer{ + callstackWithOpcodes: make([]callFrameWithOpcodes, 0, 1), + config: fullConfig, + keccakPreimages: make(map[string]struct{}), + ignoredOpcodes: ignoredOpcodes, + }, nil +} + +func (t *erc7562Tracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { + t.env = env + t.gasLimit = tx.Gas() +} + +// OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). +func (t *erc7562Tracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + // Skip if tracing was interrupted + if t.interrupt.Load() { + return + } + + toCopy := to + call := callFrameWithOpcodes{ + Type: vm.OpCode(typ), + From: from, + To: &toCopy, + Input: common.CopyBytes(input), + Gas: gas, + Value: value, + AccessedSlots: accessedSlots{ + Reads: map[common.Hash][]common.Hash{}, + Writes: map[common.Hash]uint64{}, + TransientReads: map[common.Hash]uint64{}, + TransientWrites: map[common.Hash]uint64{}, + }, + UsedOpcodes: map[vm.OpCode]uint64{}, + ExtCodeAccessInfo: make([]common.Address, 0), + ContractSize: map[common.Address]*contractSizeWithOpcode{}, + } + if depth == 0 { + call.Gas = t.gasLimit + } + t.callstackWithOpcodes = append(t.callstackWithOpcodes, call) +} + +func (t *erc7562Tracer) captureEnd(output []byte, err error, reverted bool) { + if len(t.callstackWithOpcodes) != 1 { + return + } + t.callstackWithOpcodes[0].processOutput(output, err, reverted) +} + +// OnExit is called when EVM exits a scope, even if the scope didn't +// execute any code. +func (t *erc7562Tracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + if t.interrupt.Load() { + return + } + if depth == 0 { + t.captureEnd(output, err, reverted) + return + } + + size := len(t.callstackWithOpcodes) + if size <= 1 { + return + } + // Pop call. + call := t.callstackWithOpcodes[size-1] + t.callstackWithOpcodes = t.callstackWithOpcodes[:size-1] + size -= 1 + + if errors.Is(err, vm.ErrCodeStoreOutOfGas) || errors.Is(err, vm.ErrOutOfGas) { + call.OutOfGas = true + } + call.GasUsed = gasUsed + call.processOutput(output, err, reverted) + // Nest call into parent. + t.callstackWithOpcodes[size-1].Calls = append(t.callstackWithOpcodes[size-1].Calls, call) +} + +func (t *erc7562Tracer) OnTxEnd(receipt *types.Receipt, err error) { + if t.interrupt.Load() { + return + } + // Error happened during tx validation. + if err != nil { + return + } + t.callstackWithOpcodes[0].GasUsed = receipt.GasUsed + if t.config.WithLog { + // Logs are not emitted when the call fails + t.clearFailedLogs(&t.callstackWithOpcodes[0], false) + } +} + +func (t *erc7562Tracer) OnLog(log1 *types.Log) { + // Only logs need to be captured via opcode processing + if !t.config.WithLog { + return + } + // Skip if tracing was interrupted + if t.interrupt.Load() { + return + } + l := callLog{ + Address: log1.Address, + Topics: log1.Topics, + Data: log1.Data, + Position: hexutil.Uint(len(t.callstackWithOpcodes[len(t.callstackWithOpcodes)-1].Calls)), + } + t.callstackWithOpcodes[len(t.callstackWithOpcodes)-1].Logs = append(t.callstackWithOpcodes[len(t.callstackWithOpcodes)-1].Logs, l) +} + +// GetResult returns the json-encoded nested list of call traces, and any +// error arising from the encoding or forceful termination (via `Stop`). +func (t *erc7562Tracer) GetResult() (json.RawMessage, error) { + if t.interrupt.Load() { + return nil, t.reason + } + if len(t.callstackWithOpcodes) != 1 { + return nil, errors.New("incorrect number of top-level calls") + } + + keccak := make([][]byte, 0, len(t.callstackWithOpcodes[0].KeccakPreimages)) + for k := range t.keccakPreimages { + keccak = append(keccak, []byte(k)) + } + t.callstackWithOpcodes[0].KeccakPreimages = keccak + slices.SortFunc(keccak, func(a, b []byte) int { + return bytes.Compare(a, b) + }) + + enc, err := json.Marshal(t.callstackWithOpcodes[0]) + if err != nil { + return nil, err + } + + return enc, t.reason +} + +// Stop terminates execution of the tracer at the first opportune moment. +func (t *erc7562Tracer) Stop(err error) { + t.reason = err + t.interrupt.Store(true) +} + +// clearFailedLogs clears the logs of a callframe and all its children +// in case of execution failure. +func (t *erc7562Tracer) clearFailedLogs(cf *callFrameWithOpcodes, parentFailed bool) { + failed := cf.failed() || parentFailed + // Clear own logs + if failed { + cf.Logs = nil + } + for i := range cf.Calls { + t.clearFailedLogs(&cf.Calls[i], failed) + } +} + +func (t *erc7562Tracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + if t.interrupt.Load() { + return + } + var ( + opcode = vm.OpCode(op) + opcodeWithStack *opcodeWithPartialStack + stackSize = len(scope.StackData()) + stackLimit = min(stackSize, t.config.StackTopItemsSize) + stackTopItems = make([]uint256.Int, stackLimit) + ) + for i := 0; i < stackLimit; i++ { + stackTopItems[i] = *peepStack(scope.StackData(), i) + } + opcodeWithStack = &opcodeWithPartialStack{ + Opcode: opcode, + StackTopItems: stackTopItems, + } + t.handleReturnRevert(opcode) + size := len(t.callstackWithOpcodes) + currentCallFrame := &t.callstackWithOpcodes[size-1] + if t.lastOpWithStack != nil { + t.handleExtOpcodes(opcode, currentCallFrame) + } + t.handleAccessedContractSize(opcode, scope, currentCallFrame) + if t.lastOpWithStack != nil { + t.handleGasObserved(opcode, currentCallFrame) + } + t.storeUsedOpcode(opcode, currentCallFrame) + t.handleStorageAccess(opcode, scope, currentCallFrame) + t.storeKeccak(opcode, scope) + t.lastOpWithStack = opcodeWithStack +} + +func (t *erc7562Tracer) handleReturnRevert(opcode vm.OpCode) { + if opcode == vm.REVERT || opcode == vm.RETURN { + t.lastOpWithStack = nil + } +} + +func (t *erc7562Tracer) handleGasObserved(opcode vm.OpCode, currentCallFrame *callFrameWithOpcodes) { + // [OP-012] + pendingGasObserved := t.lastOpWithStack.Opcode == vm.GAS && !isCall(opcode) + if pendingGasObserved { + currentCallFrame.UsedOpcodes[vm.GAS]++ + } +} + +func (t *erc7562Tracer) storeUsedOpcode(opcode vm.OpCode, currentCallFrame *callFrameWithOpcodes) { + // ignore "unimportant" opcodes + if opcode != vm.GAS && !t.isIgnoredOpcode(opcode) { + currentCallFrame.UsedOpcodes[opcode]++ + } +} + +func (t *erc7562Tracer) handleStorageAccess(opcode vm.OpCode, scope tracing.OpContext, currentCallFrame *callFrameWithOpcodes) { + if opcode == vm.SLOAD || opcode == vm.SSTORE || opcode == vm.TLOAD || opcode == vm.TSTORE { + slot := common.BytesToHash(peepStack(scope.StackData(), 0).Bytes()) + addr := scope.Address() + + if opcode == vm.SLOAD { + // read slot values before this UserOp was created + // (so saving it if it was written before the first read) + _, rOk := currentCallFrame.AccessedSlots.Reads[slot] + _, wOk := currentCallFrame.AccessedSlots.Writes[slot] + if !rOk && !wOk { + currentCallFrame.AccessedSlots.Reads[slot] = append(currentCallFrame.AccessedSlots.Reads[slot], t.env.StateDB.GetState(addr, slot)) + } + } else if opcode == vm.SSTORE { + currentCallFrame.AccessedSlots.Writes[slot]++ + } else if opcode == vm.TLOAD { + currentCallFrame.AccessedSlots.TransientReads[slot]++ + } else { + currentCallFrame.AccessedSlots.TransientWrites[slot]++ + } + } +} + +func (t *erc7562Tracer) storeKeccak(opcode vm.OpCode, scope tracing.OpContext) { + if opcode == vm.KECCAK256 { + dataOffset := peepStack(scope.StackData(), 0).Uint64() + dataLength := peepStack(scope.StackData(), 1).Uint64() + preimage, err := internal.GetMemoryCopyPadded(scope.MemoryData(), int64(dataOffset), int64(dataLength)) + if err != nil { + log.Warn("erc7562Tracer: failed to copy keccak preimage from memory", "err", err) + return + } + t.keccakPreimages[string(preimage)] = struct{}{} + } +} + +func (t *erc7562Tracer) handleExtOpcodes(opcode vm.OpCode, currentCallFrame *callFrameWithOpcodes) { + if isEXT(t.lastOpWithStack.Opcode) { + addr := common.HexToAddress(t.lastOpWithStack.StackTopItems[0].Hex()) + + // only store the last EXTCODE* opcode per address - could even be a boolean for our current use-case + // [OP-051] + + if !(t.lastOpWithStack.Opcode == vm.EXTCODESIZE && opcode == vm.ISZERO) { + currentCallFrame.ExtCodeAccessInfo = append(currentCallFrame.ExtCodeAccessInfo, addr) + } + } +} + +func (t *erc7562Tracer) handleAccessedContractSize(opcode vm.OpCode, scope tracing.OpContext, currentCallFrame *callFrameWithOpcodes) { + // [OP-041] + if isEXTorCALL(opcode) { + n := 0 + if !isEXT(opcode) { + n = 1 + } + addr := common.BytesToAddress(peepStack(scope.StackData(), n).Bytes()) + if _, ok := currentCallFrame.ContractSize[addr]; !ok { + currentCallFrame.ContractSize[addr] = &contractSizeWithOpcode{ + ContractSize: len(t.env.StateDB.GetCode(addr)), + Opcode: opcode, + } + } + } +} + +func peepStack(stackData []uint256.Int, n int) *uint256.Int { + return &stackData[len(stackData)-n-1] +} + +func isEXTorCALL(opcode vm.OpCode) bool { + return isEXT(opcode) || isCall(opcode) +} + +func isEXT(opcode vm.OpCode) bool { + return opcode == vm.EXTCODEHASH || + opcode == vm.EXTCODESIZE || + opcode == vm.EXTCODECOPY +} + +func isCall(opcode vm.OpCode) bool { + return opcode == vm.CALL || + opcode == vm.CALLCODE || + opcode == vm.DELEGATECALL || + opcode == vm.STATICCALL +} + +// Check if this opcode is ignored for the purposes of generating the used opcodes report +func (t *erc7562Tracer) isIgnoredOpcode(opcode vm.OpCode) bool { + if _, ok := t.ignoredOpcodes[opcode]; ok { + return true + } + return false +} + +func defaultIgnoredOpcodes() []hexutil.Uint64 { + ignored := make([]hexutil.Uint64, 0, 64) + + // Allow all PUSHx, DUPx and SWAPx opcodes as they have sequential codes + for op := vm.PUSH0; op < vm.SWAP16; op++ { + ignored = append(ignored, hexutil.Uint64(op)) + } + + for _, op := range []vm.OpCode{ + vm.POP, vm.ADD, vm.SUB, vm.MUL, + vm.DIV, vm.EQ, vm.LT, vm.GT, + vm.SLT, vm.SGT, vm.SHL, vm.SHR, + vm.AND, vm.OR, vm.NOT, vm.ISZERO, + } { + ignored = append(ignored, hexutil.Uint64(op)) + } + + return ignored +} diff --git a/eth/tracers/native/gen_callframewithopcodes_json.go b/eth/tracers/native/gen_callframewithopcodes_json.go new file mode 100644 index 00000000000..f3d5dde8f13 --- /dev/null +++ b/eth/tracers/native/gen_callframewithopcodes_json.go @@ -0,0 +1,159 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package native + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/vm" +) + +var _ = (*callFrameWithOpcodesMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (c callFrameWithOpcodes) MarshalJSON() ([]byte, error) { + type callFrameWithOpcodes0 struct { + Type vm.OpCode `json:"-"` + From common.Address `json:"from"` + Gas hexutil.Uint64 `json:"gas"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + To *common.Address `json:"to,omitempty" rlp:"optional"` + Input hexutil.Bytes `json:"input" rlp:"optional"` + Output hexutil.Bytes `json:"output,omitempty" rlp:"optional"` + Error string `json:"error,omitempty" rlp:"optional"` + RevertReason string `json:"revertReason,omitempty"` + Logs []callLog `json:"logs,omitempty" rlp:"optional"` + Value *hexutil.Big `json:"value,omitempty" rlp:"optional"` + AccessedSlots accessedSlots `json:"accessedSlots"` + ExtCodeAccessInfo []common.Address `json:"extCodeAccessInfo"` + UsedOpcodes map[hexutil.Uint64]uint64 `json:"usedOpcodes"` + ContractSize map[common.Address]*contractSizeWithOpcode `json:"contractSize"` + OutOfGas bool `json:"outOfGas"` + KeccakPreimages []hexutil.Bytes `json:"keccak,omitempty"` + Calls []callFrameWithOpcodes `json:"calls,omitempty" rlp:"optional"` + TypeString string `json:"type"` + } + var enc callFrameWithOpcodes0 + enc.Type = c.Type + enc.From = c.From + enc.Gas = hexutil.Uint64(c.Gas) + enc.GasUsed = hexutil.Uint64(c.GasUsed) + enc.To = c.To + enc.Input = c.Input + enc.Output = c.Output + enc.Error = c.Error + enc.RevertReason = c.RevertReason + enc.Logs = c.Logs + enc.Value = (*hexutil.Big)(c.Value) + enc.AccessedSlots = c.AccessedSlots + enc.ExtCodeAccessInfo = c.ExtCodeAccessInfo + if c.UsedOpcodes != nil { + enc.UsedOpcodes = make(map[hexutil.Uint64]uint64, len(c.UsedOpcodes)) + for k, v := range c.UsedOpcodes { + enc.UsedOpcodes[hexutil.Uint64(k)] = v + } + } + enc.ContractSize = c.ContractSize + enc.OutOfGas = c.OutOfGas + if c.KeccakPreimages != nil { + enc.KeccakPreimages = make([]hexutil.Bytes, len(c.KeccakPreimages)) + for k, v := range c.KeccakPreimages { + enc.KeccakPreimages[k] = v + } + } + enc.Calls = c.Calls + enc.TypeString = c.TypeString() + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (c *callFrameWithOpcodes) UnmarshalJSON(input []byte) error { + type callFrameWithOpcodes0 struct { + Type *vm.OpCode `json:"-"` + From *common.Address `json:"from"` + Gas *hexutil.Uint64 `json:"gas"` + GasUsed *hexutil.Uint64 `json:"gasUsed"` + To *common.Address `json:"to,omitempty" rlp:"optional"` + Input *hexutil.Bytes `json:"input" rlp:"optional"` + Output *hexutil.Bytes `json:"output,omitempty" rlp:"optional"` + Error *string `json:"error,omitempty" rlp:"optional"` + RevertReason *string `json:"revertReason,omitempty"` + Logs []callLog `json:"logs,omitempty" rlp:"optional"` + Value *hexutil.Big `json:"value,omitempty" rlp:"optional"` + AccessedSlots *accessedSlots `json:"accessedSlots"` + ExtCodeAccessInfo []common.Address `json:"extCodeAccessInfo"` + UsedOpcodes map[hexutil.Uint64]uint64 `json:"usedOpcodes"` + ContractSize map[common.Address]*contractSizeWithOpcode `json:"contractSize"` + OutOfGas *bool `json:"outOfGas"` + KeccakPreimages []hexutil.Bytes `json:"keccak,omitempty"` + Calls []callFrameWithOpcodes `json:"calls,omitempty" rlp:"optional"` + } + var dec callFrameWithOpcodes0 + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Type != nil { + c.Type = *dec.Type + } + if dec.From != nil { + c.From = *dec.From + } + if dec.Gas != nil { + c.Gas = uint64(*dec.Gas) + } + if dec.GasUsed != nil { + c.GasUsed = uint64(*dec.GasUsed) + } + if dec.To != nil { + c.To = dec.To + } + if dec.Input != nil { + c.Input = *dec.Input + } + if dec.Output != nil { + c.Output = *dec.Output + } + if dec.Error != nil { + c.Error = *dec.Error + } + if dec.RevertReason != nil { + c.RevertReason = *dec.RevertReason + } + if dec.Logs != nil { + c.Logs = dec.Logs + } + if dec.Value != nil { + c.Value = (*big.Int)(dec.Value) + } + if dec.AccessedSlots != nil { + c.AccessedSlots = *dec.AccessedSlots + } + if dec.ExtCodeAccessInfo != nil { + c.ExtCodeAccessInfo = dec.ExtCodeAccessInfo + } + if dec.UsedOpcodes != nil { + c.UsedOpcodes = make(map[vm.OpCode]uint64, len(dec.UsedOpcodes)) + for k, v := range dec.UsedOpcodes { + c.UsedOpcodes[vm.OpCode(k)] = v + } + } + if dec.ContractSize != nil { + c.ContractSize = dec.ContractSize + } + if dec.OutOfGas != nil { + c.OutOfGas = *dec.OutOfGas + } + if dec.KeccakPreimages != nil { + c.KeccakPreimages = make([][]byte, len(dec.KeccakPreimages)) + for k, v := range dec.KeccakPreimages { + c.KeccakPreimages[k] = v + } + } + if dec.Calls != nil { + c.Calls = dec.Calls + } + return nil +} From 63740b7aca732f6c204c56a9e4b4932f5251e4e0 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 20 May 2025 18:31:01 +0200 Subject: [PATCH 191/658] core/state: reduce allocation in updateStateObject (#31861) Optimize updateStateObject to reduce an allocation. --- core/state/statedb.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 2453d67f3ee..51453055c3c 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -562,9 +562,8 @@ func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common // updateStateObject writes the given object to the trie. func (s *StateDB) updateStateObject(obj *stateObject) { // Encode the account and update the account trie - addr := obj.Address() - if err := s.trie.UpdateAccount(addr, &obj.data, len(obj.code)); err != nil { - s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err)) + if err := s.trie.UpdateAccount(obj.Address(), &obj.data, len(obj.code)); err != nil { + s.setError(fmt.Errorf("updateStateObject (%x) error: %v", obj.Address(), err)) } if obj.dirtyCode { s.trie.UpdateContractCode(obj.Address(), common.BytesToHash(obj.CodeHash()), obj.code) From a4959685a2ca2eba558ac9c233c4b67c9fd0edac Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 21 May 2025 11:53:29 +0200 Subject: [PATCH 192/658] eth/catalyst: move witness methods from engine api (#31867) No functional changes, just moves the witness methods into its own file --- eth/catalyst/api.go | 265 ------------------------------------ eth/catalyst/witness.go | 295 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 295 insertions(+), 265 deletions(-) create mode 100644 eth/catalyst/witness.go diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index be25712c970..b5f035b6d2d 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -27,11 +27,8 @@ import ( "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/version" @@ -245,58 +242,6 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, pa return api.forkchoiceUpdated(update, params, engine.PayloadV3, false) } -// ForkchoiceUpdatedWithWitnessV1 is analogous to ForkchoiceUpdatedV1, only it -// generates an execution witness too if block building was requested. -func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { - if payloadAttributes != nil { - switch { - case payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil: - return engine.STATUS_INVALID, paramsErr("withdrawals and beacon root not supported in V1") - case !api.checkFork(payloadAttributes.Timestamp, forks.Paris, forks.Shanghai): - return engine.STATUS_INVALID, paramsErr("fcuV1 called post-shanghai") - } - } - return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, true) -} - -// ForkchoiceUpdatedWithWitnessV2 is analogous to ForkchoiceUpdatedV2, only it -// generates an execution witness too if block building was requested. -func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { - if params != nil { - switch { - case params.BeaconRoot != nil: - return engine.STATUS_INVALID, attributesErr("unexpected beacon root") - case api.checkFork(params.Timestamp, forks.Paris) && params.Withdrawals != nil: - return engine.STATUS_INVALID, attributesErr("withdrawals before shanghai") - case api.checkFork(params.Timestamp, forks.Shanghai) && params.Withdrawals == nil: - return engine.STATUS_INVALID, attributesErr("missing withdrawals") - case !api.checkFork(params.Timestamp, forks.Paris, forks.Shanghai): - return engine.STATUS_INVALID, unsupportedForkErr("fcuV2 must only be called with paris or shanghai payloads") - } - } - return api.forkchoiceUpdated(update, params, engine.PayloadV2, true) -} - -// ForkchoiceUpdatedWithWitnessV3 is analogous to ForkchoiceUpdatedV3, only it -// generates an execution witness too if block building was requested. -func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { - if params != nil { - switch { - case params.Withdrawals == nil: - return engine.STATUS_INVALID, attributesErr("missing withdrawals") - case params.BeaconRoot == nil: - return engine.STATUS_INVALID, attributesErr("missing beacon root") - case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague): - return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads") - } - } - // TODO(matt): the spec requires that fcu is applied when called on a valid - // hash, even if params are wrong. To do this we need to split up - // forkchoiceUpdate into a function that only updates the head and then a - // function that kicks off block construction. - return api.forkchoiceUpdated(update, params, engine.PayloadV3, true) -} - func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes, payloadVersion engine.PayloadVersion, payloadWitness bool) (engine.ForkChoiceResponse, error) { api.forkchoiceLock.Lock() defer api.forkchoiceLock.Unlock() @@ -615,160 +560,6 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas return api.newPayload(params, versionedHashes, beaconRoot, requests, false) } -// NewPayloadWithWitnessV1 is analogous to NewPayloadV1, only it also generates -// and returns a stateless witness after running the payload. -func (api *ConsensusAPI) NewPayloadWithWitnessV1(params engine.ExecutableData) (engine.PayloadStatusV1, error) { - if params.Withdrawals != nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1")) - } - return api.newPayload(params, nil, nil, nil, true) -} - -// NewPayloadWithWitnessV2 is analogous to NewPayloadV2, only it also generates -// and returns a stateless witness after running the payload. -func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) { - var ( - cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp) - shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp) - ) - switch { - case cancun: - return invalidStatus, paramsErr("can't use newPayloadV2 post-cancun") - case shanghai && params.Withdrawals == nil: - return invalidStatus, paramsErr("nil withdrawals post-shanghai") - case !shanghai && params.Withdrawals != nil: - return invalidStatus, paramsErr("non-nil withdrawals pre-shanghai") - case params.ExcessBlobGas != nil: - return invalidStatus, paramsErr("non-nil excessBlobGas pre-cancun") - case params.BlobGasUsed != nil: - return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun") - } - return api.newPayload(params, nil, nil, nil, true) -} - -// NewPayloadWithWitnessV3 is analogous to NewPayloadV3, only it also generates -// and returns a stateless witness after running the payload. -func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) { - switch { - case params.Withdrawals == nil: - return invalidStatus, paramsErr("nil withdrawals post-shanghai") - case params.ExcessBlobGas == nil: - return invalidStatus, paramsErr("nil excessBlobGas post-cancun") - case params.BlobGasUsed == nil: - return invalidStatus, paramsErr("nil blobGasUsed post-cancun") - case versionedHashes == nil: - return invalidStatus, paramsErr("nil versionedHashes post-cancun") - case beaconRoot == nil: - return invalidStatus, paramsErr("nil beaconRoot post-cancun") - case !api.checkFork(params.Timestamp, forks.Cancun): - return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") - } - return api.newPayload(params, versionedHashes, beaconRoot, nil, true) -} - -// NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates -// and returns a stateless witness after running the payload. -func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) { - switch { - case params.Withdrawals == nil: - return invalidStatus, paramsErr("nil withdrawals post-shanghai") - case params.ExcessBlobGas == nil: - return invalidStatus, paramsErr("nil excessBlobGas post-cancun") - case params.BlobGasUsed == nil: - return invalidStatus, paramsErr("nil blobGasUsed post-cancun") - case versionedHashes == nil: - return invalidStatus, paramsErr("nil versionedHashes post-cancun") - case beaconRoot == nil: - return invalidStatus, paramsErr("nil beaconRoot post-cancun") - case executionRequests == nil: - return invalidStatus, paramsErr("nil executionRequests post-prague") - case !api.checkFork(params.Timestamp, forks.Prague): - return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") - } - requests := convertRequests(executionRequests) - if err := validateRequests(requests); err != nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(err) - } - return api.newPayload(params, versionedHashes, beaconRoot, requests, true) -} - -// ExecuteStatelessPayloadV1 is analogous to NewPayloadV1, only it operates in -// a stateless mode on top of a provided witness instead of the local database. -func (api *ConsensusAPI) ExecuteStatelessPayloadV1(params engine.ExecutableData, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { - if params.Withdrawals != nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1")) - } - return api.executeStatelessPayload(params, nil, nil, nil, opaqueWitness) -} - -// ExecuteStatelessPayloadV2 is analogous to NewPayloadV2, only it operates in -// a stateless mode on top of a provided witness instead of the local database. -func (api *ConsensusAPI) ExecuteStatelessPayloadV2(params engine.ExecutableData, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { - var ( - cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp) - shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp) - ) - switch { - case cancun: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("can't use newPayloadV2 post-cancun") - case shanghai && params.Withdrawals == nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai") - case !shanghai && params.Withdrawals != nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil withdrawals pre-shanghai") - case params.ExcessBlobGas != nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil excessBlobGas pre-cancun") - case params.BlobGasUsed != nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil blobGasUsed pre-cancun") - } - return api.executeStatelessPayload(params, nil, nil, nil, opaqueWitness) -} - -// ExecuteStatelessPayloadV3 is analogous to NewPayloadV3, only it operates in -// a stateless mode on top of a provided witness instead of the local database. -func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { - switch { - case params.Withdrawals == nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai") - case params.ExcessBlobGas == nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil excessBlobGas post-cancun") - case params.BlobGasUsed == nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil blobGasUsed post-cancun") - case versionedHashes == nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil versionedHashes post-cancun") - case beaconRoot == nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun") - case !api.checkFork(params.Timestamp, forks.Cancun): - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") - } - return api.executeStatelessPayload(params, versionedHashes, beaconRoot, nil, opaqueWitness) -} - -// ExecuteStatelessPayloadV4 is analogous to NewPayloadV4, only it operates in -// a stateless mode on top of a provided witness instead of the local database. -func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { - switch { - case params.Withdrawals == nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai") - case params.ExcessBlobGas == nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil excessBlobGas post-cancun") - case params.BlobGasUsed == nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil blobGasUsed post-cancun") - case versionedHashes == nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil versionedHashes post-cancun") - case beaconRoot == nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun") - case executionRequests == nil: - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil executionRequests post-prague") - case !api.checkFork(params.Timestamp, forks.Prague): - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") - } - requests := convertRequests(executionRequests) - if err := validateRequests(requests); err != nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(err) - } - return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requests, opaqueWitness) -} - func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, witness bool) (engine.PayloadStatusV1, error) { // The locking here is, strictly, not required. Without these locks, this can happen: // @@ -884,62 +675,6 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe return engine.PayloadStatusV1{Status: engine.VALID, Witness: ow, LatestValidHash: &hash}, nil } -func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { - log.Trace("Engine API request received", "method", "ExecuteStatelessPayload", "number", params.Number, "hash", params.BlockHash) - block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot, requests) - if err != nil { - bgu := "nil" - if params.BlobGasUsed != nil { - bgu = strconv.Itoa(int(*params.BlobGasUsed)) - } - ebg := "nil" - if params.ExcessBlobGas != nil { - ebg = strconv.Itoa(int(*params.ExcessBlobGas)) - } - log.Warn("Invalid ExecuteStatelessPayload params", - "params.Number", params.Number, - "params.ParentHash", params.ParentHash, - "params.BlockHash", params.BlockHash, - "params.StateRoot", params.StateRoot, - "params.FeeRecipient", params.FeeRecipient, - "params.LogsBloom", common.PrettyBytes(params.LogsBloom), - "params.Random", params.Random, - "params.GasLimit", params.GasLimit, - "params.GasUsed", params.GasUsed, - "params.Timestamp", params.Timestamp, - "params.ExtraData", common.PrettyBytes(params.ExtraData), - "params.BaseFeePerGas", params.BaseFeePerGas, - "params.BlobGasUsed", bgu, - "params.ExcessBlobGas", ebg, - "len(params.Transactions)", len(params.Transactions), - "len(params.Withdrawals)", len(params.Withdrawals), - "beaconRoot", beaconRoot, - "len(requests)", len(requests), - "error", err) - errorMsg := err.Error() - return engine.StatelessPayloadStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}, nil - } - witness := new(stateless.Witness) - if err := rlp.DecodeBytes(opaqueWitness, witness); err != nil { - log.Warn("Invalid ExecuteStatelessPayload witness", "err", err) - errorMsg := err.Error() - return engine.StatelessPayloadStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}, nil - } - // Stash away the last update to warn the user if the beacon client goes offline - api.lastNewPayloadLock.Lock() - api.lastNewPayloadUpdate = time.Now() - api.lastNewPayloadLock.Unlock() - - log.Trace("Executing block statelessly", "number", block.Number(), "hash", params.BlockHash) - stateRoot, receiptRoot, err := core.ExecuteStateless(api.config(), vm.Config{}, block, witness) - if err != nil { - log.Warn("ExecuteStatelessPayload: execution failed", "err", err) - errorMsg := err.Error() - return engine.StatelessPayloadStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}, nil - } - return engine.StatelessPayloadStatusV1{Status: engine.VALID, StateRoot: stateRoot, ReceiptsRoot: receiptRoot}, nil -} - // delayPayloadImport stashes the given block away for import at a later time, // either via a forkchoice update or a sync extension. This method is meant to // be called by the newpayload command when the block seems to be ok, but some diff --git a/eth/catalyst/witness.go b/eth/catalyst/witness.go new file mode 100644 index 00000000000..214bee57a40 --- /dev/null +++ b/eth/catalyst/witness.go @@ -0,0 +1,295 @@ +// 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 catalyst + +import ( + "errors" + "strconv" + "time" + + "github.com/ethereum/go-ethereum/beacon/engine" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/stateless" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params/forks" + "github.com/ethereum/go-ethereum/rlp" +) + +// ForkchoiceUpdatedWithWitnessV1 is analogous to ForkchoiceUpdatedV1, only it +// generates an execution witness too if block building was requested. +func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { + if payloadAttributes != nil { + switch { + case payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil: + return engine.STATUS_INVALID, paramsErr("withdrawals and beacon root not supported in V1") + case !api.checkFork(payloadAttributes.Timestamp, forks.Paris, forks.Shanghai): + return engine.STATUS_INVALID, paramsErr("fcuV1 called post-shanghai") + } + } + return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, true) +} + +// ForkchoiceUpdatedWithWitnessV2 is analogous to ForkchoiceUpdatedV2, only it +// generates an execution witness too if block building was requested. +func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { + if params != nil { + switch { + case params.BeaconRoot != nil: + return engine.STATUS_INVALID, attributesErr("unexpected beacon root") + case api.checkFork(params.Timestamp, forks.Paris) && params.Withdrawals != nil: + return engine.STATUS_INVALID, attributesErr("withdrawals before shanghai") + case api.checkFork(params.Timestamp, forks.Shanghai) && params.Withdrawals == nil: + return engine.STATUS_INVALID, attributesErr("missing withdrawals") + case !api.checkFork(params.Timestamp, forks.Paris, forks.Shanghai): + return engine.STATUS_INVALID, unsupportedForkErr("fcuV2 must only be called with paris or shanghai payloads") + } + } + return api.forkchoiceUpdated(update, params, engine.PayloadV2, true) +} + +// ForkchoiceUpdatedWithWitnessV3 is analogous to ForkchoiceUpdatedV3, only it +// generates an execution witness too if block building was requested. +func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { + if params != nil { + switch { + case params.Withdrawals == nil: + return engine.STATUS_INVALID, attributesErr("missing withdrawals") + case params.BeaconRoot == nil: + return engine.STATUS_INVALID, attributesErr("missing beacon root") + case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague): + return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads") + } + } + // TODO(matt): the spec requires that fcu is applied when called on a valid + // hash, even if params are wrong. To do this we need to split up + // forkchoiceUpdate into a function that only updates the head and then a + // function that kicks off block construction. + return api.forkchoiceUpdated(update, params, engine.PayloadV3, true) +} + +// NewPayloadWithWitnessV1 is analogous to NewPayloadV1, only it also generates +// and returns a stateless witness after running the payload. +func (api *ConsensusAPI) NewPayloadWithWitnessV1(params engine.ExecutableData) (engine.PayloadStatusV1, error) { + if params.Withdrawals != nil { + return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1")) + } + return api.newPayload(params, nil, nil, nil, true) +} + +// NewPayloadWithWitnessV2 is analogous to NewPayloadV2, only it also generates +// and returns a stateless witness after running the payload. +func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) { + var ( + cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp) + shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp) + ) + switch { + case cancun: + return invalidStatus, paramsErr("can't use newPayloadV2 post-cancun") + case shanghai && params.Withdrawals == nil: + return invalidStatus, paramsErr("nil withdrawals post-shanghai") + case !shanghai && params.Withdrawals != nil: + return invalidStatus, paramsErr("non-nil withdrawals pre-shanghai") + case params.ExcessBlobGas != nil: + return invalidStatus, paramsErr("non-nil excessBlobGas pre-cancun") + case params.BlobGasUsed != nil: + return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun") + } + return api.newPayload(params, nil, nil, nil, true) +} + +// NewPayloadWithWitnessV3 is analogous to NewPayloadV3, only it also generates +// and returns a stateless witness after running the payload. +func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) { + switch { + case params.Withdrawals == nil: + return invalidStatus, paramsErr("nil withdrawals post-shanghai") + case params.ExcessBlobGas == nil: + return invalidStatus, paramsErr("nil excessBlobGas post-cancun") + case params.BlobGasUsed == nil: + return invalidStatus, paramsErr("nil blobGasUsed post-cancun") + case versionedHashes == nil: + return invalidStatus, paramsErr("nil versionedHashes post-cancun") + case beaconRoot == nil: + return invalidStatus, paramsErr("nil beaconRoot post-cancun") + case !api.checkFork(params.Timestamp, forks.Cancun): + return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") + } + return api.newPayload(params, versionedHashes, beaconRoot, nil, true) +} + +// NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates +// and returns a stateless witness after running the payload. +func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) { + switch { + case params.Withdrawals == nil: + return invalidStatus, paramsErr("nil withdrawals post-shanghai") + case params.ExcessBlobGas == nil: + return invalidStatus, paramsErr("nil excessBlobGas post-cancun") + case params.BlobGasUsed == nil: + return invalidStatus, paramsErr("nil blobGasUsed post-cancun") + case versionedHashes == nil: + return invalidStatus, paramsErr("nil versionedHashes post-cancun") + case beaconRoot == nil: + return invalidStatus, paramsErr("nil beaconRoot post-cancun") + case executionRequests == nil: + return invalidStatus, paramsErr("nil executionRequests post-prague") + case !api.checkFork(params.Timestamp, forks.Prague): + return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") + } + requests := convertRequests(executionRequests) + if err := validateRequests(requests); err != nil { + return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(err) + } + return api.newPayload(params, versionedHashes, beaconRoot, requests, true) +} + +// ExecuteStatelessPayloadV1 is analogous to NewPayloadV1, only it operates in +// a stateless mode on top of a provided witness instead of the local database. +func (api *ConsensusAPI) ExecuteStatelessPayloadV1(params engine.ExecutableData, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { + if params.Withdrawals != nil { + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1")) + } + return api.executeStatelessPayload(params, nil, nil, nil, opaqueWitness) +} + +// ExecuteStatelessPayloadV2 is analogous to NewPayloadV2, only it operates in +// a stateless mode on top of a provided witness instead of the local database. +func (api *ConsensusAPI) ExecuteStatelessPayloadV2(params engine.ExecutableData, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { + var ( + cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp) + shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp) + ) + switch { + case cancun: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("can't use newPayloadV2 post-cancun") + case shanghai && params.Withdrawals == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai") + case !shanghai && params.Withdrawals != nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil withdrawals pre-shanghai") + case params.ExcessBlobGas != nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil excessBlobGas pre-cancun") + case params.BlobGasUsed != nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil blobGasUsed pre-cancun") + } + return api.executeStatelessPayload(params, nil, nil, nil, opaqueWitness) +} + +// ExecuteStatelessPayloadV3 is analogous to NewPayloadV3, only it operates in +// a stateless mode on top of a provided witness instead of the local database. +func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { + switch { + case params.Withdrawals == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai") + case params.ExcessBlobGas == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil excessBlobGas post-cancun") + case params.BlobGasUsed == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil blobGasUsed post-cancun") + case versionedHashes == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil versionedHashes post-cancun") + case beaconRoot == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun") + case !api.checkFork(params.Timestamp, forks.Cancun): + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") + } + return api.executeStatelessPayload(params, versionedHashes, beaconRoot, nil, opaqueWitness) +} + +// ExecuteStatelessPayloadV4 is analogous to NewPayloadV4, only it operates in +// a stateless mode on top of a provided witness instead of the local database. +func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { + switch { + case params.Withdrawals == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai") + case params.ExcessBlobGas == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil excessBlobGas post-cancun") + case params.BlobGasUsed == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil blobGasUsed post-cancun") + case versionedHashes == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil versionedHashes post-cancun") + case beaconRoot == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun") + case executionRequests == nil: + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil executionRequests post-prague") + case !api.checkFork(params.Timestamp, forks.Prague): + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") + } + requests := convertRequests(executionRequests) + if err := validateRequests(requests); err != nil { + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(err) + } + return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requests, opaqueWitness) +} + +func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { + log.Trace("Engine API request received", "method", "ExecuteStatelessPayload", "number", params.Number, "hash", params.BlockHash) + block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot, requests) + if err != nil { + bgu := "nil" + if params.BlobGasUsed != nil { + bgu = strconv.Itoa(int(*params.BlobGasUsed)) + } + ebg := "nil" + if params.ExcessBlobGas != nil { + ebg = strconv.Itoa(int(*params.ExcessBlobGas)) + } + log.Warn("Invalid ExecuteStatelessPayload params", + "params.Number", params.Number, + "params.ParentHash", params.ParentHash, + "params.BlockHash", params.BlockHash, + "params.StateRoot", params.StateRoot, + "params.FeeRecipient", params.FeeRecipient, + "params.LogsBloom", common.PrettyBytes(params.LogsBloom), + "params.Random", params.Random, + "params.GasLimit", params.GasLimit, + "params.GasUsed", params.GasUsed, + "params.Timestamp", params.Timestamp, + "params.ExtraData", common.PrettyBytes(params.ExtraData), + "params.BaseFeePerGas", params.BaseFeePerGas, + "params.BlobGasUsed", bgu, + "params.ExcessBlobGas", ebg, + "len(params.Transactions)", len(params.Transactions), + "len(params.Withdrawals)", len(params.Withdrawals), + "beaconRoot", beaconRoot, + "len(requests)", len(requests), + "error", err) + errorMsg := err.Error() + return engine.StatelessPayloadStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}, nil + } + witness := new(stateless.Witness) + if err := rlp.DecodeBytes(opaqueWitness, witness); err != nil { + log.Warn("Invalid ExecuteStatelessPayload witness", "err", err) + errorMsg := err.Error() + return engine.StatelessPayloadStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}, nil + } + // Stash away the last update to warn the user if the beacon client goes offline + api.lastNewPayloadLock.Lock() + api.lastNewPayloadUpdate = time.Now() + api.lastNewPayloadLock.Unlock() + + log.Trace("Executing block statelessly", "number", block.Number(), "hash", params.BlockHash) + stateRoot, receiptRoot, err := core.ExecuteStateless(api.config(), vm.Config{}, block, witness) + if err != nil { + log.Warn("ExecuteStatelessPayload: execution failed", "err", err) + errorMsg := err.Error() + return engine.StatelessPayloadStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}, nil + } + return engine.StatelessPayloadStatusV1{Status: engine.VALID, StateRoot: stateRoot, ReceiptsRoot: receiptRoot}, nil +} From 0287666b7d9beffb93f5a7c6500d242d00dbd085 Mon Sep 17 00:00:00 2001 From: wellna Date: Wed, 21 May 2025 13:20:36 +0100 Subject: [PATCH 193/658] eth/tracers: Improve test coverage for toWord (#31846) --- eth/tracers/js/goja.go | 1 - eth/tracers/js/tracer_test.go | 8 +++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index d1e65bf7f44..7ec737f4e49 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -480,7 +480,6 @@ func (t *jsTracer) setBuiltinFunctions() { return hexutil.Encode(b) }) vm.Set("toWord", func(v goja.Value) goja.Value { - // TODO: add test with []byte len < 32 or > 32 b, err := t.fromBuf(vm, v, true) if err != nil { vm.Interrupt(err) diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index dbfc7308f77..a12b990a939 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -122,9 +122,15 @@ func TestTracer(t *testing.T) { }, { // tests gasUsed code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasPrice+'.'+ctx.gasUsed; }}", want: `"100000.21006"`, - }, { + }, { // tests toWord with byte array length < 32 code: "{res: null, step: function(log) {}, fault: function() {}, result: function() { return toWord('0xffaa') }}", want: `{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":255,"31":170}`, + }, { // tests toWord with byte array length = 32 + code: "{step: function() {}, fault: function() {}, result: function() { return toWord('0x1234567890123456789012345678901234567890123456789012345678901234'); }}", + want: `{"0":18,"1":52,"2":86,"3":120,"4":144,"5":18,"6":52,"7":86,"8":120,"9":144,"10":18,"11":52,"12":86,"13":120,"14":144,"15":18,"16":52,"17":86,"18":120,"19":144,"20":18,"21":52,"22":86,"23":120,"24":144,"25":18,"26":52,"27":86,"28":120,"29":144,"30":18,"31":52}`, + }, { // tests toWord with byte array length > 32 + code: "{step: function() {}, fault: function() {}, result: function() { return toWord('0x1234567890123456789012345678901234567890123456789012345678901234567890'); }}", + want: `{"0":120,"1":144,"2":18,"3":52,"4":86,"5":120,"6":144,"7":18,"8":52,"9":86,"10":120,"11":144,"12":18,"13":52,"14":86,"15":120,"16":144,"17":18,"18":52,"19":86,"20":120,"21":144,"22":18,"23":52,"24":86,"25":120,"26":144,"27":18,"28":52,"29":86,"30":120,"31":144}`, }, { // test feeding a buffer back into go code: "{res: null, step: function(log) { var address = log.contract.getAddress(); this.res = toAddress(address); }, fault: function() {}, result: function() { return this.res }}", want: `{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0}`, From 20ad4f500e7fafab93f6d94fa171a5c0309de6ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Thu, 22 May 2025 11:30:20 +0200 Subject: [PATCH 194/658] core/txpool: add explicit max blob count limit (#31837) Fixes #31792. --------- Co-authored-by: lightclient --- core/txpool/blobpool/blobpool.go | 15 +++++-- core/txpool/blobpool/blobpool_test.go | 59 +++++++++++++++++++++++++++ core/txpool/errors.go | 4 ++ core/txpool/validation.go | 10 +++-- 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index e506da228d9..2602d821040 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -62,6 +62,12 @@ const ( // limit can never hurt. txMaxSize = 1024 * 1024 + // maxBlobsPerTx is the maximum number of blobs that a single transaction can + // carry. We choose a smaller limit than the protocol-permitted MaxBlobsPerBlock + // in order to ensure network and txpool stability. + // Note: if you increase this, validation will fail on txMaxSize. + maxBlobsPerTx = 7 + // maxTxsPerAccount is the maximum number of blob transactions admitted from // a single account. The limit is enforced to minimize the DoS potential of // a private tx cancelling publicly propagated blobs. @@ -1095,10 +1101,11 @@ func (p *BlobPool) SetGasTip(tip *big.Int) { // and does not require the pool mutex to be held. func (p *BlobPool) ValidateTxBasics(tx *types.Transaction) error { opts := &txpool.ValidationOptions{ - Config: p.chain.Config(), - Accept: 1 << types.BlobTxType, - MaxSize: txMaxSize, - MinTip: p.gasTip.ToBig(), + Config: p.chain.Config(), + Accept: 1 << types.BlobTxType, + MaxSize: txMaxSize, + MinTip: p.gasTip.ToBig(), + MaxBlobCount: maxBlobsPerTx, } return txpool.ValidateTransaction(tx, p.head, p.signer, opts) } diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 0a323179a6a..12b64bf6748 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -1142,6 +1142,65 @@ func TestChangingSlotterSize(t *testing.T) { } } +// TestBlobCountLimit tests the blobpool enforced limits on the max blob count. +func TestBlobCountLimit(t *testing.T) { + var ( + key1, _ = crypto.GenerateKey() + key2, _ = crypto.GenerateKey() + + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) + ) + + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + statedb.Commit(0, true, false) + + // Make Prague-enabled custom chain config. + cancunTime := uint64(0) + pragueTime := uint64(0) + config := ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + LondonBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + CancunTime: &cancunTime, + PragueTime: &pragueTime, + BlobScheduleConfig: ¶ms.BlobScheduleConfig{ + Cancun: params.DefaultCancunBlobConfig, + Prague: params.DefaultPragueBlobConfig, + }, + } + chain := &testBlockChain{ + config: config, + basefee: uint256.NewInt(1050), + blobfee: uint256.NewInt(105), + statedb: statedb, + } + pool := New(Config{Datadir: t.TempDir()}, chain, nil) + if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil { + t.Fatalf("failed to create blob pool: %v", err) + } + + // Attempt to add transactions. + var ( + tx1 = makeMultiBlobTx(0, 1, 1000, 100, 7, key1) + tx2 = makeMultiBlobTx(0, 1, 800, 70, 8, key2) + ) + errs := pool.Add([]*types.Transaction{tx1, tx2}, true) + + // Check that first succeeds second fails. + if errs[0] != nil { + t.Fatalf("expected tx with 7 blobs to succeed") + } + if !errors.Is(errs[1], txpool.ErrTxBlobLimitExceeded) { + t.Fatalf("expected tx with 8 blobs to fail, got: %v", errs[1]) + } + + verifyPoolInternals(t, pool) + pool.Close() +} + // Tests that adding transaction will correctly store it in the persistent store // and update all the indices. // diff --git a/core/txpool/errors.go b/core/txpool/errors.go index 968c9d95423..9bc435d67ee 100644 --- a/core/txpool/errors.go +++ b/core/txpool/errors.go @@ -58,6 +58,10 @@ var ( // making the transaction invalid, rather a DOS protection. ErrOversizedData = errors.New("oversized data") + // ErrTxBlobLimitExceeded is returned if a transaction would exceed the number + // of blobs allowed by blobpool. + ErrTxBlobLimitExceeded = errors.New("transaction blob limit exceeded") + // ErrAlreadyReserved is returned if the sender address has a pending transaction // in a different subpool. For example, this error is returned in response to any // input transaction of non-blob type when a blob transaction from this sender diff --git a/core/txpool/validation.go b/core/txpool/validation.go index e370f2ce84f..720d0d3b722 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -42,9 +42,10 @@ var ( type ValidationOptions struct { Config *params.ChainConfig // Chain configuration to selectively validate based on current fork rules - Accept uint8 // Bitmap of transaction types that should be accepted for the calling pool - MaxSize uint64 // Maximum size of a transaction that the caller can meaningfully handle - MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool + Accept uint8 // Bitmap of transaction types that should be accepted for the calling pool + MaxSize uint64 // Maximum size of a transaction that the caller can meaningfully handle + MaxBlobCount int // Maximum number of blobs allowed per transaction + MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool } // ValidationFunction is an method type which the pools use to perform the tx-validations which do not @@ -63,6 +64,9 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types if opts.Accept&(1< opts.MaxBlobCount { + return fmt.Errorf("%w: blob count %v, limit %v", ErrTxBlobLimitExceeded, blobCount, opts.MaxBlobCount) + } // Before performing any expensive validations, sanity check that the tx is // smaller than the maximum limit the pool can meaningfully handle if tx.Size() > opts.MaxSize { From 29a87e442645bc34d08e5c8709c619411c6c3624 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Thu, 22 May 2025 15:49:11 -0600 Subject: [PATCH 195/658] build: Update EEST to v4.5.0 (#31880) We deleted outdated pectra-devnet-6@v1.0.0 release by mistake, so this PR updates the referenced EEST release to the correct latest version. @s1na I removed the TODO comment because I think this solves it, unless it meant something else. --------- Co-authored-by: MariusVanDerWijden --- build/checksums.txt | 6 +++--- build/ci.go | 2 +- tests/block_test.go | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/build/checksums.txt b/build/checksums.txt index c819429969f..cf52172c2ec 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -1,9 +1,9 @@ # This file contains sha256 checksums of optional build dependencies. -# version:spec-tests pectra-devnet-6@v1.0.0 +# version:spec-tests v4.5.0 # https://github.com/ethereum/execution-spec-tests/releases -# https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-6%40v1.0.0/ -b69211752a3029083c020dc635fe12156ca1a6725a08559da540a0337586a77e fixtures_pectra-devnet-6.tar.gz +# https://github.com/ethereum/execution-spec-tests/releases/download/v4.5.0/ +58afb92a0075a2cb7c4dec1281f7cb88b21b02afbedad096b580f3f8cc14c54c fixtures_develop.tar.gz # version:golang 1.24.3 # https://go.dev/dl/ diff --git a/build/ci.go b/build/ci.go index 88e4e82282a..58c769e0d79 100644 --- a/build/ci.go +++ b/build/ci.go @@ -332,7 +332,7 @@ func doTest(cmdline []string) { // downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures. func downloadSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string { ext := ".tar.gz" - base := "fixtures_pectra-devnet-6" // TODO(s1na) rename once the version becomes part of the filename + base := "fixtures_develop" archivePath := filepath.Join(cachedir, base+ext) if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil { log.Fatal(err) diff --git a/tests/block_test.go b/tests/block_test.go index f146e4ee6ae..91d9f2e653a 100644 --- a/tests/block_test.go +++ b/tests/block_test.go @@ -81,6 +81,9 @@ func TestExecutionSpecBlocktests(t *testing.T) { } bt := new(testMatcher) + bt.skipLoad(".*prague/eip7251_consolidations/contract_deployment/system_contract_deployment.json") + bt.skipLoad(".*prague/eip7002_el_triggerable_withdrawals/contract_deployment/system_contract_deployment.json") + bt.walk(t, executionSpecBlockchainTestDir, func(t *testing.T, name string, test *BlockTest) { execBlockTest(t, bt, test) }) From 1fd806d3e59b1da29297611e0046df0bc742658d Mon Sep 17 00:00:00 2001 From: Merkel Tranjes <140164174+rnkrtt@users.noreply.github.com> Date: Fri, 23 May 2025 09:21:03 +0200 Subject: [PATCH 196/658] internal/era: update link to documentation (#31879) Updated reference URL in accumulator.go comment to point to the correct location of the historical-hashes-accumulator documentation in the Ethereum portal network specs --- internal/era/accumulator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/era/accumulator.go b/internal/era/accumulator.go index cb383d8e63f..83a761f1fdc 100644 --- a/internal/era/accumulator.go +++ b/internal/era/accumulator.go @@ -49,7 +49,7 @@ func ComputeAccumulator(hashes []common.Hash, tds []*big.Int) (common.Hash, erro // headerRecord is an individual record for a historical header. // -// See https://github.com/ethereum/portal-network-specs/blob/master/history-network.md#the-header-accumulator +// See https://github.com/ethereum/portal-network-specs/blob/master/history/history-network.md#the-historical-hashes-accumulator // for more information. type headerRecord struct { Hash common.Hash From a53fdf1fe6eac47899738dccba5a652936aebb84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Irmak?= Date: Fri, 23 May 2025 12:14:40 +0300 Subject: [PATCH 197/658] crypto: use pure Go signature implementation in tinygo (#31878) tinygo is having problems compiling the C implementation --- crypto/signature_cgo.go | 4 ++-- crypto/signature_nocgo.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go index 5c7810c14ce..18b78f4aac6 100644 --- a/crypto/signature_cgo.go +++ b/crypto/signature_cgo.go @@ -14,8 +14,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -//go:build !nacl && !js && !wasip1 && cgo && !gofuzz -// +build !nacl,!js,!wasip1,cgo,!gofuzz +//go:build !nacl && !js && !wasip1 && cgo && !gofuzz && !tinygo +// +build !nacl,!js,!wasip1,cgo,!gofuzz,!tinygo package crypto diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go index 2d35b49403b..d76127c2588 100644 --- a/crypto/signature_nocgo.go +++ b/crypto/signature_nocgo.go @@ -14,8 +14,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -//go:build nacl || js || wasip1 || !cgo || gofuzz -// +build nacl js wasip1 !cgo gofuzz +//go:build nacl || js || wasip1 || !cgo || gofuzz || tinygo +// +build nacl js wasip1 !cgo gofuzz tinygo package crypto From f21adaf245e320a809f9bb6ec96c330726c9078f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Irmak?= Date: Fri, 23 May 2025 12:29:01 +0300 Subject: [PATCH 198/658] consensus: remove clique RPC APIs (#31875) --- consensus/beacon/consensus.go | 6 - consensus/clique/api.go | 235 ---------------------------------- consensus/clique/clique.go | 10 -- consensus/consensus.go | 4 - consensus/ethash/ethash.go | 7 - eth/backend.go | 3 - 6 files changed, 265 deletions(-) delete mode 100644 consensus/clique/api.go diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index cc9f44e460a..f9a5a3233bc 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -30,7 +30,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" "github.com/holiman/uint256" ) @@ -445,11 +444,6 @@ func (beacon *Beacon) CalcDifficulty(chain consensus.ChainHeaderReader, time uin return beaconDifficulty } -// APIs implements consensus.Engine, returning the user facing RPC APIs. -func (beacon *Beacon) APIs(chain consensus.ChainHeaderReader) []rpc.API { - return beacon.ethone.APIs(chain) -} - // Close shutdowns the consensus engine func (beacon *Beacon) Close() error { return beacon.ethone.Close() diff --git a/consensus/clique/api.go b/consensus/clique/api.go deleted file mode 100644 index 374b50692d8..00000000000 --- a/consensus/clique/api.go +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2017 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 clique - -import ( - "encoding/json" - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" -) - -// API is a user facing RPC API to allow controlling the signer and voting -// mechanisms of the proof-of-authority scheme. -type API struct { - chain consensus.ChainHeaderReader - clique *Clique -} - -// GetSnapshot retrieves the state snapshot at a given block. -func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) { - // Retrieve the requested block number (or current if none requested) - var header *types.Header - if number == nil || *number == rpc.LatestBlockNumber { - header = api.chain.CurrentHeader() - } else { - header = api.chain.GetHeaderByNumber(uint64(number.Int64())) - } - // Ensure we have an actually valid block and return its snapshot - if header == nil { - return nil, errUnknownBlock - } - return api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) -} - -// GetSnapshotAtHash retrieves the state snapshot at a given block. -func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) { - header := api.chain.GetHeaderByHash(hash) - if header == nil { - return nil, errUnknownBlock - } - return api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) -} - -// GetSigners retrieves the list of authorized signers at the specified block. -func (api *API) GetSigners(number *rpc.BlockNumber) ([]common.Address, error) { - // Retrieve the requested block number (or current if none requested) - var header *types.Header - if number == nil || *number == rpc.LatestBlockNumber { - header = api.chain.CurrentHeader() - } else { - header = api.chain.GetHeaderByNumber(uint64(number.Int64())) - } - // Ensure we have an actually valid block and return the signers from its snapshot - if header == nil { - return nil, errUnknownBlock - } - snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) - if err != nil { - return nil, err - } - return snap.signers(), nil -} - -// GetSignersAtHash retrieves the list of authorized signers at the specified block. -func (api *API) GetSignersAtHash(hash common.Hash) ([]common.Address, error) { - header := api.chain.GetHeaderByHash(hash) - if header == nil { - return nil, errUnknownBlock - } - snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) - if err != nil { - return nil, err - } - return snap.signers(), nil -} - -// Proposals returns the current proposals the node tries to uphold and vote on. -func (api *API) Proposals() map[common.Address]bool { - api.clique.lock.RLock() - defer api.clique.lock.RUnlock() - - proposals := make(map[common.Address]bool) - for address, auth := range api.clique.proposals { - proposals[address] = auth - } - return proposals -} - -// Propose injects a new authorization proposal that the signer will attempt to -// push through. -func (api *API) Propose(address common.Address, auth bool) { - api.clique.lock.Lock() - defer api.clique.lock.Unlock() - - api.clique.proposals[address] = auth -} - -// Discard drops a currently running proposal, stopping the signer from casting -// further votes (either for or against). -func (api *API) Discard(address common.Address) { - api.clique.lock.Lock() - defer api.clique.lock.Unlock() - - delete(api.clique.proposals, address) -} - -type status struct { - InturnPercent float64 `json:"inturnPercent"` - SigningStatus map[common.Address]int `json:"sealerActivity"` - NumBlocks uint64 `json:"numBlocks"` -} - -// Status returns the status of the last N blocks, -// - the number of active signers, -// - the number of signers, -// - the percentage of in-turn blocks -func (api *API) Status() (*status, error) { - var ( - numBlocks = uint64(64) - header = api.chain.CurrentHeader() - diff = uint64(0) - optimals = 0 - ) - snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) - if err != nil { - return nil, err - } - var ( - signers = snap.signers() - end = header.Number.Uint64() - start = end - numBlocks - ) - if numBlocks > end { - start = 1 - numBlocks = end - start - } - signStatus := make(map[common.Address]int) - for _, s := range signers { - signStatus[s] = 0 - } - for n := start; n < end; n++ { - h := api.chain.GetHeaderByNumber(n) - if h == nil { - return nil, fmt.Errorf("missing block %d", n) - } - if h.Difficulty.Cmp(diffInTurn) == 0 { - optimals++ - } - diff += h.Difficulty.Uint64() - sealer, err := api.clique.Author(h) - if err != nil { - return nil, err - } - signStatus[sealer]++ - } - return &status{ - InturnPercent: float64(100*optimals) / float64(numBlocks), - SigningStatus: signStatus, - NumBlocks: numBlocks, - }, nil -} - -type blockNumberOrHashOrRLP struct { - *rpc.BlockNumberOrHash - RLP hexutil.Bytes `json:"rlp,omitempty"` -} - -func (sb *blockNumberOrHashOrRLP) UnmarshalJSON(data []byte) error { - bnOrHash := new(rpc.BlockNumberOrHash) - // Try to unmarshal bNrOrHash - if err := bnOrHash.UnmarshalJSON(data); err == nil { - sb.BlockNumberOrHash = bnOrHash - return nil - } - // Try to unmarshal RLP - var input string - if err := json.Unmarshal(data, &input); err != nil { - return err - } - blob, err := hexutil.Decode(input) - if err != nil { - return err - } - sb.RLP = blob - return nil -} - -// GetSigner returns the signer for a specific clique block. -// Can be called with a block number, a block hash or a rlp encoded blob. -// The RLP encoded blob can either be a block or a header. -func (api *API) GetSigner(rlpOrBlockNr *blockNumberOrHashOrRLP) (common.Address, error) { - if len(rlpOrBlockNr.RLP) == 0 { - blockNrOrHash := rlpOrBlockNr.BlockNumberOrHash - var header *types.Header - if blockNrOrHash == nil { - header = api.chain.CurrentHeader() - } else if hash, ok := blockNrOrHash.Hash(); ok { - header = api.chain.GetHeaderByHash(hash) - } else if number, ok := blockNrOrHash.Number(); ok { - header = api.chain.GetHeaderByNumber(uint64(number.Int64())) - } - if header == nil { - return common.Address{}, fmt.Errorf("missing block %v", blockNrOrHash.String()) - } - return api.clique.Author(header) - } - block := new(types.Block) - if err := rlp.DecodeBytes(rlpOrBlockNr.RLP, block); err == nil { - return api.clique.Author(block.Header()) - } - header := new(types.Header) - if err := rlp.DecodeBytes(rlpOrBlockNr.RLP, header); err != nil { - return common.Address{}, err - } - return api.clique.Author(header) -} diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index d31efd74451..b593d2117d2 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -41,7 +41,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" "golang.org/x/crypto/sha3" ) @@ -641,15 +640,6 @@ func (c *Clique) Close() error { return nil } -// APIs implements consensus.Engine, returning the user facing RPC API to allow -// controlling the signer voting. -func (c *Clique) APIs(chain consensus.ChainHeaderReader) []rpc.API { - return []rpc.API{{ - Namespace: "clique", - Service: &API{chain: chain, clique: c}, - }} -} - // SealHash returns the hash of a block prior to it being sealed. func SealHash(header *types.Header) (hash common.Hash) { hasher := sha3.NewLegacyKeccak256() diff --git a/consensus/consensus.go b/consensus/consensus.go index c59b9a47447..a68351f7ffa 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -25,7 +25,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" ) // ChainHeaderReader defines a small collection of methods needed to access the local @@ -109,9 +108,6 @@ type Engine interface { // that a new block should have. CalcDifficulty(chain ChainHeaderReader, time uint64, parent *types.Header) *big.Int - // APIs returns the RPC APIs this consensus engine provides. - APIs(chain ChainHeaderReader) []rpc.API - // Close terminates any background threads maintained by the consensus engine. Close() error } diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go index f37ec260567..f624f738757 100644 --- a/consensus/ethash/ethash.go +++ b/consensus/ethash/ethash.go @@ -22,7 +22,6 @@ import ( "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rpc" ) // Ethash is a consensus engine based on proof-of-work implementing the ethash @@ -71,12 +70,6 @@ func (ethash *Ethash) Close() error { return nil } -// APIs implements consensus.Engine, returning no APIs as ethash is an empty -// shell in the post-merge world. -func (ethash *Ethash) APIs(chain consensus.ChainHeaderReader) []rpc.API { - return []rpc.API{} -} - // Seal generates a new sealing request for the given input block and pushes // the result into the given channel. For the ethash engine, this method will // just panic as sealing is not supported anymore. diff --git a/eth/backend.go b/eth/backend.go index 6d1b6bae991..15243ad5c98 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -337,9 +337,6 @@ func makeExtraData(extra []byte) []byte { func (s *Ethereum) APIs() []rpc.API { apis := ethapi.GetAPIs(s.APIBackend) - // Append any APIs exposed explicitly by the consensus engine - apis = append(apis, s.engine.APIs(s.BlockChain())...) - // Append all the local APIs and return return append(apis, []rpc.API{ { From 94481d13513f94ef4a76ef847e7c3d436fde2546 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 23 May 2025 12:33:43 +0200 Subject: [PATCH 199/658] .gitea: add initial workflow file (#31885) --- .gitea/workflows/release.yml | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .gitea/workflows/release.yml diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 00000000000..fa7b7f25aba --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,38 @@ +name: Release Builds + +on: + push: + branches: [ master ] + +jobs: + docker: + name: Docker Image + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.23.0 + cache: false + + - name: Run docker build + run: | + go run build/ci.go docker -platform linux/amd64,linux/arm64 + + linux: + name: Docker Image + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.23.0 + cache: false + + - name: Run build + run: | + go run build/ci.go install From 8f598e85ade64653b6490b8ca742a51274ec328a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 23 May 2025 12:44:30 +0200 Subject: [PATCH 200/658] .gitea: update release build actions (#31886) Trying to make the docker build work. --- .gitea/workflows/release.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index fa7b7f25aba..4a169127eaa 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -11,18 +11,24 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Set up Go uses: actions/setup-go@v5 with: - go-version: 1.23.0 + go-version: 1.24 cache: false - name: Run docker build run: | - go run build/ci.go docker -platform linux/amd64,linux/arm64 + go run build/ci.go dockerx -platform linux/amd64,linux/arm64,linux/riscv64 linux: - name: Docker Image + name: Linux Build runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -30,7 +36,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: 1.23.0 + go-version: 1.24 cache: false - name: Run build From 8781e93013d286538a8848b695f2b1e102f4af6a Mon Sep 17 00:00:00 2001 From: buddho Date: Fri, 23 May 2025 19:10:10 +0800 Subject: [PATCH 201/658] core/state: fix copy of storageChange (#31874) Missing field origvalue when copying storageChange. --- core/state/journal.go | 1 + 1 file changed, 1 insertion(+) diff --git a/core/state/journal.go b/core/state/journal.go index 13332dbd793..f3f976f24fe 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -408,6 +408,7 @@ func (ch storageChange) copy() journalEntry { account: ch.account, key: ch.key, prevvalue: ch.prevvalue, + origvalue: ch.origvalue, } } From b97198379b824d8e331d9e024afead4fe03cc50a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 23 May 2025 16:21:08 +0200 Subject: [PATCH 202/658] .gitea: add cron build script (#31890) Also swaps the push build scripts and adds environment output. --- .gitea/workflows/release-cron.yml | 42 +++++++++++++++++++++++++++++++ .gitea/workflows/release.yml | 34 ++++++++++++++----------- 2 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 .gitea/workflows/release-cron.yml diff --git a/.gitea/workflows/release-cron.yml b/.gitea/workflows/release-cron.yml new file mode 100644 index 00000000000..3a03d0ef149 --- /dev/null +++ b/.gitea/workflows/release-cron.yml @@ -0,0 +1,42 @@ +name: Release Builds (cron) + +on: + schedule: + cron: '0 0 * * *' + +jobs: + azure-cleanup: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.24 + cache: false + + - name: Run cleanup script + run: | + go run build/ci.go purge -store gethstore/builds -days 14 + + ppa: + name: PPA Upload (master) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.24 + cache: false + + - name: Install deb toolchain + run: | + apt-get -yq --no-install-suggests --no-install-recommends install devscripts debhelper dput fakeroot + + - name: Run ci.go + run: | + echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts + go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder " diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 4a169127eaa..a91bebda7f0 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -1,44 +1,48 @@ -name: Release Builds +name: Release Builds (push) on: push: branches: [ master ] jobs: - docker: - name: Docker Image + linux: + name: Linux Build runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Set up Go uses: actions/setup-go@v5 with: go-version: 1.24 cache: false - - name: Run docker build + - name: display environment run: | - go run build/ci.go dockerx -platform linux/amd64,linux/arm64,linux/riscv64 + env - linux: - name: Linux Build + - name: Run build + run: | + go run build/ci.go install + + docker: + name: Docker Image runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Set up Go uses: actions/setup-go@v5 with: go-version: 1.24 cache: false - - name: Run build + - name: Run docker build run: | - go run build/ci.go install + go run build/ci.go dockerx -platform linux/amd64,linux/arm64,linux/riscv64 From f14813fea99f69d5ff11a6e0c96aebd44a2fc288 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 23 May 2025 17:48:15 +0200 Subject: [PATCH 203/658] internal/build: add support for Github Actions CI environment (#31891) This adds support for the Github actions environment in the build tool. Information from environment variables, like the build number and branch/tag name, is used to make decisions about uploads and package filenames. --- internal/build/env.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/internal/build/env.go b/internal/build/env.go index 35b2cd6ae7b..23501d0ece9 100644 --- a/internal/build/env.go +++ b/internal/build/env.go @@ -92,6 +92,33 @@ func Env() Environment { IsPullRequest: os.Getenv("APPVEYOR_PULL_REQUEST_NUMBER") != "", IsCronJob: os.Getenv("APPVEYOR_SCHEDULED_BUILD") == "True", } + case os.Getenv("CI") == "true" && os.Getenv("GITHUB_ACTIONS") == "true": + commit := os.Getenv("GITHUB_SHA") + reftype := os.Getenv("GITHUB_REF_TYPE") + isPR := os.Getenv("GITHUB_HEAD_REF") != "" + tag := "" + branch := "" + switch { + case isPR: + branch = os.Getenv("GITHUB_BASE_REF") + case reftype == "branch": + branch = os.Getenv("GITHUB_REF_NAME") + case reftype == "tag": + tag = os.Getenv("GITHUB_REF_NAME") + } + return Environment{ + CI: true, + Name: "github-actions", + Repo: os.Getenv("GITHUB_REPOSITORY"), + Commit: commit, + Date: getDate(commit), + Branch: branch, + Tag: tag, + IsPullRequest: isPR, + Buildnum: os.Getenv("GITHUB_RUN_ID"), + IsCronJob: os.Getenv("GITHUB_EVENT_NAME") == "schedule", + } + default: return LocalEnv() } From 9fd3f8a0dd0a82514bf0ab5ed65dbdcf48c5e155 Mon Sep 17 00:00:00 2001 From: Marcel <153717436+MonkeyMarcel@users.noreply.github.com> Date: Fri, 23 May 2025 23:50:25 +0800 Subject: [PATCH 204/658] core: remove unused queued import status (#31870) --- core/blockchain_insert.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/core/blockchain_insert.go b/core/blockchain_insert.go index 695aa6679b4..d7c2696f085 100644 --- a/core/blockchain_insert.go +++ b/core/blockchain_insert.go @@ -27,10 +27,10 @@ import ( // insertStats tracks and reports on block insertion. type insertStats struct { - queued, processed, ignored int - usedGas uint64 - lastIndex int - startTime mclock.AbsTime + processed, ignored int + usedGas uint64 + lastIndex int + startTime mclock.AbsTime } // statsReportLimit is the time limit during import and export after which we @@ -78,9 +78,6 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn } context = append(context, []interface{}{"triedirty", triebufNodes}...) - if st.queued > 0 { - context = append(context, []interface{}{"queued", st.queued}...) - } if st.ignored > 0 { context = append(context, []interface{}{"ignored", st.ignored}...) } From 3f7b8bc976fe5e9b810b91da359b3e0cb7d010c8 Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Mon, 26 May 2025 10:45:41 +0200 Subject: [PATCH 205/658] core/vm: fix bls benchmark (#31896) Fixes #31893 --- core/vm/contracts_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index b627f2ada50..537d7877574 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -370,7 +370,7 @@ func BenchmarkPrecompiledBLS12381G1MultiExpWorstCase(b *testing.B) { Name: "WorstCaseG1", NoBenchmark: false, } - benchmarkPrecompiled("f0c", testcase, b) + benchmarkPrecompiled("f0b", testcase, b) } // BenchmarkPrecompiledBLS12381G2MultiExpWorstCase benchmarks the worst case we could find that still fits a gaslimit of 10MGas. @@ -391,5 +391,5 @@ func BenchmarkPrecompiledBLS12381G2MultiExpWorstCase(b *testing.B) { Name: "WorstCaseG2", NoBenchmark: false, } - benchmarkPrecompiled("f0f", testcase, b) + benchmarkPrecompiled("f0d", testcase, b) } From 8b9f2d4e36dfcdb2af94dc0d3b63f30c8ad159a9 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Wed, 28 May 2025 19:31:42 +0800 Subject: [PATCH 206/658] triedb/pathdb: introduce lookup structure to optimize state access (#30971) This pull request introduces a mechanism to improve state lookup efficiency in pathdb by maintaining a lookup structure that eliminates unnecessary iteration over diff layers. The core idea is to track a mutation history for each dirty state entry residing in the diff layers. This history records the state roots of all layers in which the entry was modified, sorted from oldest to newest. During state lookup, this mutation history is queried to find the most recent layer whose state root either matches the target root or is a descendant of it. This allows us to quickly identify the layer containing the relevant data, avoiding the need to iterate through all diff layers from top to bottom. Besides, the overhead for state lookup is constant, no matter how many diff layers are retained in the pathdb, which unlocks the potential to hold more diff layers. Of course, maintaining this lookup structure introduces some overhead. For each state transition, we need to: (a) update the mutation records for the modified state entries, and (b) remove stale mutation records associated with outdated layers. On our benchmark machine, it will introduce around 1ms overhead which is acceptable. --- triedb/pathdb/database.go | 4 +- triedb/pathdb/difflayer.go | 4 +- triedb/pathdb/disklayer.go | 9 - triedb/pathdb/layertree.go | 193 +++++-- triedb/pathdb/layertree_test.go | 885 ++++++++++++++++++++++++++++++++ triedb/pathdb/lookup.go | 278 ++++++++++ triedb/pathdb/metrics.go | 3 + triedb/pathdb/reader.go | 56 +- 8 files changed, 1382 insertions(+), 50 deletions(-) create mode 100644 triedb/pathdb/layertree_test.go create mode 100644 triedb/pathdb/lookup.go diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index c2064603159..3174a7c964b 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -487,7 +487,7 @@ func (db *Database) Enable(root common.Hash) error { // Re-construct a new disk layer backed by persistent state // and schedule the state snapshot generation if it's permitted. - db.tree.reset(generateSnapshot(db, root, db.isVerkle || db.config.SnapshotNoBuild)) + db.tree.init(generateSnapshot(db, root, db.isVerkle || db.config.SnapshotNoBuild)) log.Info("Rebuilt trie database", "root", root) return nil } @@ -529,7 +529,7 @@ func (db *Database) Recover(root common.Hash) error { // reset layer with newly created disk layer. It must be // done after each revert operation, otherwise the new // disk layer won't be accessible from outside. - db.tree.reset(dl) + db.tree.init(dl) } rawdb.DeleteTrieJournal(db.diskdb) diff --git a/triedb/pathdb/difflayer.go b/triedb/pathdb/difflayer.go index 2e1d83c74a9..ac05b4a0fbe 100644 --- a/triedb/pathdb/difflayer.go +++ b/triedb/pathdb/difflayer.go @@ -156,7 +156,7 @@ func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes *no } // persist flushes the diff layer and all its parent layers to disk layer. -func (dl *diffLayer) persist(force bool) (layer, error) { +func (dl *diffLayer) persist(force bool) (*diskLayer, error) { if parent, ok := dl.parentLayer().(*diffLayer); ok { // Hold the lock to prevent any read operation until the new // parent is linked correctly. @@ -183,7 +183,7 @@ func (dl *diffLayer) size() uint64 { // diffToDisk merges a bottom-most diff into the persistent disk layer underneath // it. The method will panic if called onto a non-bottom-most diff layer. -func diffToDisk(layer *diffLayer, force bool) (layer, error) { +func diffToDisk(layer *diffLayer, force bool) (*diskLayer, error) { disk, ok := layer.parentLayer().(*diskLayer) if !ok { panic(fmt.Sprintf("unknown layer type: %T", layer.parentLayer())) diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go index bee4cd1e8a4..1c9efb024bd 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -94,15 +94,6 @@ func (dl *diskLayer) setGenerator(generator *generator) { dl.generator = generator } -// isStale return whether this layer has become stale (was flattened across) or if -// it's still live. -func (dl *diskLayer) isStale() bool { - dl.lock.RLock() - defer dl.lock.RUnlock() - - return dl.stale -} - // markStale sets the stale flag as true. func (dl *diskLayer) markStale() { dl.lock.Lock() diff --git a/triedb/pathdb/layertree.go b/triedb/pathdb/layertree.go index 85a5e470e7f..b2f3f7f37de 100644 --- a/triedb/pathdb/layertree.go +++ b/triedb/pathdb/layertree.go @@ -31,29 +31,50 @@ import ( // thread-safe to use. However, callers need to ensure the thread-safety // of the referenced layer by themselves. type layerTree struct { - lock sync.RWMutex + base *diskLayer layers map[common.Hash]layer + + // descendants is a two-dimensional map where the keys represent + // an ancestor state root, and the values are the state roots of + // all its descendants. + // + // For example: r -> [c1, c2, ..., cn], where c1 through cn are + // the descendants of state r. + // + // This map includes all the existing diff layers and the disk layer. + descendants map[common.Hash]map[common.Hash]struct{} + lookup *lookup + lock sync.RWMutex } // newLayerTree constructs the layerTree with the given head layer. func newLayerTree(head layer) *layerTree { tree := new(layerTree) - tree.reset(head) + tree.init(head) return tree } -// reset initializes the layerTree by the given head layer. -// All the ancestors will be iterated out and linked in the tree. -func (tree *layerTree) reset(head layer) { +// init initializes the layerTree by the given head layer. +func (tree *layerTree) init(head layer) { tree.lock.Lock() defer tree.lock.Unlock() - var layers = make(map[common.Hash]layer) - for head != nil { - layers[head.rootHash()] = head - head = head.parentLayer() + current := head + tree.layers = make(map[common.Hash]layer) + tree.descendants = make(map[common.Hash]map[common.Hash]struct{}) + + for { + tree.layers[current.rootHash()] = current + tree.fillAncestors(current) + + parent := current.parentLayer() + if parent == nil { + break + } + current = parent } - tree.layers = layers + tree.base = current.(*diskLayer) // panic if it's not a disk layer + tree.lookup = newLookup(head, tree.isDescendant) } // get retrieves a layer belonging to the given state root. @@ -64,6 +85,43 @@ func (tree *layerTree) get(root common.Hash) layer { return tree.layers[root] } +// isDescendant returns whether the specified layer with given root is a +// descendant of a specific ancestor. +// +// This function assumes the read lock has been held. +func (tree *layerTree) isDescendant(root common.Hash, ancestor common.Hash) bool { + subset := tree.descendants[ancestor] + if subset == nil { + return false + } + _, ok := subset[root] + return ok +} + +// fillAncestors identifies the ancestors of the given layer and populates the +// descendants set. The ancestors include the diff layers below the supplied +// layer and also the disk layer. +// +// This function assumes the write lock has been held. +func (tree *layerTree) fillAncestors(layer layer) { + hash := layer.rootHash() + for { + parent := layer.parentLayer() + if parent == nil { + break + } + layer = parent + + phash := parent.rootHash() + subset := tree.descendants[phash] + if subset == nil { + subset = make(map[common.Hash]struct{}) + tree.descendants[phash] = subset + } + subset[hash] = struct{}{} + } +} + // forEach iterates the stored layers inside and applies the // given callback on them. func (tree *layerTree) forEach(onLayer func(layer)) { @@ -101,8 +159,16 @@ func (tree *layerTree) add(root common.Hash, parentRoot common.Hash, block uint6 l := parent.update(root, parent.stateID()+1, block, newNodeSet(nodes.Flatten()), states) tree.lock.Lock() + defer tree.lock.Unlock() + + // Link the given layer into the layer set tree.layers[l.rootHash()] = l - tree.lock.Unlock() + + // Link the given layer into its ancestors (up to the current disk layer) + tree.fillAncestors(l) + + // Link the given layer into the state mutation history + tree.lookup.addLayer(l) return nil } @@ -127,8 +193,16 @@ func (tree *layerTree) cap(root common.Hash, layers int) error { if err != nil { return err } - // Replace the entire layer tree with the flat base - tree.layers = map[common.Hash]layer{base.rootHash(): base} + tree.base = base + + // Reset the layer tree with the single new disk layer + tree.layers = map[common.Hash]layer{ + base.rootHash(): base, + } + // Resets the descendants map, since there's only a single disk layer + // with no descendants. + tree.descendants = make(map[common.Hash]map[common.Hash]struct{}) + tree.lookup = newLookup(base, tree.isDescendant) return nil } // Dive until we run out of layers or reach the persistent database @@ -143,6 +217,11 @@ func (tree *layerTree) cap(root common.Hash, layers int) error { } // We're out of layers, flatten anything below, stopping if it's the disk or if // the memory limit is not yet exceeded. + var ( + err error + replaced layer + newBase *diskLayer + ) switch parent := diff.parentLayer().(type) { case *diskLayer: return nil @@ -152,14 +231,33 @@ func (tree *layerTree) cap(root common.Hash, layers int) error { // parent is linked correctly. diff.lock.Lock() - base, err := parent.persist(false) + // Hold the reference of the original layer being replaced + replaced = parent + + // Replace the original parent layer with new disk layer. The procedure + // can be illustrated as below: + // + // Before change: + // Chain: + // C1->C2->C3->C4 (HEAD) + // ->C2'->C3'->C4' + // + // After change: + // Chain: + // (a) C3->C4 (HEAD) + // (b) C1->C2 + // ->C2'->C3'->C4' + // The original C3 is replaced by the new base (with root C3) + // Dangling layers in (b) will be removed later + newBase, err = parent.persist(false) if err != nil { diff.lock.Unlock() return err } - tree.layers[base.rootHash()] = base - diff.parent = base + tree.layers[newBase.rootHash()] = newBase + // Link the new parent and release the lock + diff.parent = newBase diff.lock.Unlock() default: @@ -173,19 +271,28 @@ func (tree *layerTree) cap(root common.Hash, layers int) error { children[parent] = append(children[parent], root) } } + clearDiff := func(layer layer) { + diff, ok := layer.(*diffLayer) + if !ok { + return + } + tree.lookup.removeLayer(diff) + } var remove func(root common.Hash) remove = func(root common.Hash) { + clearDiff(tree.layers[root]) + + // Unlink the layer from the layer tree and cascade to its children + delete(tree.descendants, root) delete(tree.layers, root) for _, child := range children[root] { remove(child) } delete(children, root) } - for root, layer := range tree.layers { - if dl, ok := layer.(*diskLayer); ok && dl.isStale() { - remove(root) - } - } + remove(tree.base.rootHash()) // remove the old/stale disk layer + clearDiff(replaced) // remove the lookup data of the stale parent being replaced + tree.base = newBase // update the base layer with newly constructed one return nil } @@ -194,17 +301,41 @@ func (tree *layerTree) bottom() *diskLayer { tree.lock.RLock() defer tree.lock.RUnlock() - if len(tree.layers) == 0 { - return nil // Shouldn't happen, empty tree + return tree.base +} + +// lookupAccount returns the layer that is guaranteed to contain the account data +// corresponding to the specified state root being queried. +func (tree *layerTree) lookupAccount(accountHash common.Hash, state common.Hash) (layer, error) { + // Hold the read lock to prevent the unexpected layer changes + tree.lock.RLock() + defer tree.lock.RUnlock() + + tip := tree.lookup.accountTip(accountHash, state, tree.base.root) + if tip == (common.Hash{}) { + return nil, fmt.Errorf("[%#x] %w", state, errSnapshotStale) } - // pick a random one as the entry point - var current layer - for _, layer := range tree.layers { - current = layer - break + l := tree.layers[tip] + if l == nil { + return nil, fmt.Errorf("triedb layer [%#x] missing", tip) } - for current.parentLayer() != nil { - current = current.parentLayer() + return l, nil +} + +// lookupStorage returns the layer that is guaranteed to contain the storage slot +// data corresponding to the specified state root being queried. +func (tree *layerTree) lookupStorage(accountHash common.Hash, slotHash common.Hash, state common.Hash) (layer, error) { + // Hold the read lock to prevent the unexpected layer changes + tree.lock.RLock() + defer tree.lock.RUnlock() + + tip := tree.lookup.storageTip(accountHash, slotHash, state, tree.base.root) + if tip == (common.Hash{}) { + return nil, fmt.Errorf("[%#x] %w", state, errSnapshotStale) + } + l := tree.layers[tip] + if l == nil { + return nil, fmt.Errorf("triedb layer [%#x] missing", tip) } - return current.(*diskLayer) + return l, nil } diff --git a/triedb/pathdb/layertree_test.go b/triedb/pathdb/layertree_test.go new file mode 100644 index 00000000000..a72ff5899ee --- /dev/null +++ b/triedb/pathdb/layertree_test.go @@ -0,0 +1,885 @@ +// Copyright 2024 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 pathdb + +import ( + "errors" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/trie/trienode" +) + +func newTestLayerTree() *layerTree { + db := New(rawdb.NewMemoryDatabase(), nil, false) + l := newDiskLayer(common.Hash{0x1}, 0, db, nil, nil, newBuffer(0, nil, nil, 0)) + t := newLayerTree(l) + return t +} + +func TestLayerCap(t *testing.T) { + var cases = []struct { + init func() *layerTree + head common.Hash + layers int + base common.Hash + snapshot map[common.Hash]struct{} + }{ + { + // Chain: + // C1->C2->C3->C4 (HEAD) + init: func() *layerTree { + tr := newTestLayerTree() + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return tr + }, + // Chain: + // C2->C3->C4 (HEAD) + head: common.Hash{0x4}, + layers: 2, + base: common.Hash{0x2}, + snapshot: map[common.Hash]struct{}{ + common.Hash{0x2}: {}, + common.Hash{0x3}: {}, + common.Hash{0x4}: {}, + }, + }, + { + // Chain: + // C1->C2->C3->C4 (HEAD) + init: func() *layerTree { + tr := newTestLayerTree() + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return tr + }, + // Chain: + // C3->C4 (HEAD) + head: common.Hash{0x4}, + layers: 1, + base: common.Hash{0x3}, + snapshot: map[common.Hash]struct{}{ + common.Hash{0x3}: {}, + common.Hash{0x4}: {}, + }, + }, + { + // Chain: + // C1->C2->C3->C4 (HEAD) + init: func() *layerTree { + tr := newTestLayerTree() + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return tr + }, + // Chain: + // C4 (HEAD) + head: common.Hash{0x4}, + layers: 0, + base: common.Hash{0x4}, + snapshot: map[common.Hash]struct{}{ + common.Hash{0x4}: {}, + }, + }, + { + // Chain: + // C1->C2->C3->C4 (HEAD) + // ->C2'->C3'->C4' + init: func() *layerTree { + tr := newTestLayerTree() + tr.add(common.Hash{0x2a}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3a}, common.Hash{0x2a}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2b}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3b}, common.Hash{0x2b}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return tr + }, + // Chain: + // C2->C3->C4 (HEAD) + head: common.Hash{0x4a}, + layers: 2, + base: common.Hash{0x2a}, + snapshot: map[common.Hash]struct{}{ + common.Hash{0x4a}: {}, + common.Hash{0x3a}: {}, + common.Hash{0x2a}: {}, + }, + }, + { + // Chain: + // C1->C2->C3->C4 (HEAD) + // ->C2'->C3'->C4' + init: func() *layerTree { + tr := newTestLayerTree() + tr.add(common.Hash{0x2a}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3a}, common.Hash{0x2a}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2b}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3b}, common.Hash{0x2b}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return tr + }, + // Chain: + // C3->C4 (HEAD) + head: common.Hash{0x4a}, + layers: 1, + base: common.Hash{0x3a}, + snapshot: map[common.Hash]struct{}{ + common.Hash{0x4a}: {}, + common.Hash{0x3a}: {}, + }, + }, + { + // Chain: + // C1->C2->C3->C4 (HEAD) + // ->C3'->C4' + init: func() *layerTree { + tr := newTestLayerTree() + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3a}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3b}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return tr + }, + // Chain: + // C2->C3->C4 (HEAD) + // ->C3'->C4' + head: common.Hash{0x4a}, + layers: 2, + base: common.Hash{0x2}, + snapshot: map[common.Hash]struct{}{ + common.Hash{0x4a}: {}, + common.Hash{0x3a}: {}, + common.Hash{0x4b}: {}, + common.Hash{0x3b}: {}, + common.Hash{0x2}: {}, + }, + }, + } + for _, c := range cases { + tr := c.init() + if err := tr.cap(c.head, c.layers); err != nil { + t.Fatalf("Failed to cap the layer tree %v", err) + } + if tr.bottom().root != c.base { + t.Fatalf("Unexpected bottom layer tree root, want %v, got %v", c.base, tr.bottom().root) + } + if len(c.snapshot) != len(tr.layers) { + t.Fatalf("Unexpected layer tree size, want %v, got %v", len(c.snapshot), len(tr.layers)) + } + for h := range tr.layers { + if _, ok := c.snapshot[h]; !ok { + t.Fatalf("Unexpected layer %v", h) + } + } + } +} + +func TestBaseLayer(t *testing.T) { + tr := newTestLayerTree() + + var cases = []struct { + op func() + base common.Hash + }{ + // Chain: + // C1 (HEAD) + { + func() {}, + common.Hash{0x1}, + }, + // Chain: + // C1->C2->C3 (HEAD) + { + func() { + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + }, + common.Hash{0x1}, + }, + // Chain: + // C3 (HEAD) + { + func() { + tr.cap(common.Hash{0x3}, 0) + }, + common.Hash{0x3}, + }, + // Chain: + // C4->C5->C6 (HEAD) + { + func() { + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x5}, common.Hash{0x4}, 4, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x6}, common.Hash{0x5}, 5, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.cap(common.Hash{0x6}, 2) + }, + common.Hash{0x4}, + }, + } + for _, c := range cases { + c.op() + if tr.base.rootHash() != c.base { + t.Fatalf("Unexpected base root, want %v, got: %v", c.base, tr.base.rootHash()) + } + } +} + +func TestDescendant(t *testing.T) { + var cases = []struct { + init func() *layerTree + snapshotA map[common.Hash]map[common.Hash]struct{} + op func(tr *layerTree) + snapshotB map[common.Hash]map[common.Hash]struct{} + }{ + { + // Chain: + // C1->C2 (HEAD) + init: func() *layerTree { + tr := newTestLayerTree() + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return tr + }, + snapshotA: map[common.Hash]map[common.Hash]struct{}{ + common.Hash{0x1}: { + common.Hash{0x2}: {}, + }, + }, + // Chain: + // C1->C2->C3 (HEAD) + op: func(tr *layerTree) { + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + }, + snapshotB: map[common.Hash]map[common.Hash]struct{}{ + common.Hash{0x1}: { + common.Hash{0x2}: {}, + common.Hash{0x3}: {}, + }, + common.Hash{0x2}: { + common.Hash{0x3}: {}, + }, + }, + }, + { + // Chain: + // C1->C2->C3->C4 (HEAD) + init: func() *layerTree { + tr := newTestLayerTree() + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return tr + }, + snapshotA: map[common.Hash]map[common.Hash]struct{}{ + common.Hash{0x1}: { + common.Hash{0x2}: {}, + common.Hash{0x3}: {}, + common.Hash{0x4}: {}, + }, + common.Hash{0x2}: { + common.Hash{0x3}: {}, + common.Hash{0x4}: {}, + }, + common.Hash{0x3}: { + common.Hash{0x4}: {}, + }, + }, + // Chain: + // C2->C3->C4 (HEAD) + op: func(tr *layerTree) { + tr.cap(common.Hash{0x4}, 2) + }, + snapshotB: map[common.Hash]map[common.Hash]struct{}{ + common.Hash{0x2}: { + common.Hash{0x3}: {}, + common.Hash{0x4}: {}, + }, + common.Hash{0x3}: { + common.Hash{0x4}: {}, + }, + }, + }, + { + // Chain: + // C1->C2->C3->C4 (HEAD) + init: func() *layerTree { + tr := newTestLayerTree() + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return tr + }, + snapshotA: map[common.Hash]map[common.Hash]struct{}{ + common.Hash{0x1}: { + common.Hash{0x2}: {}, + common.Hash{0x3}: {}, + common.Hash{0x4}: {}, + }, + common.Hash{0x2}: { + common.Hash{0x3}: {}, + common.Hash{0x4}: {}, + }, + common.Hash{0x3}: { + common.Hash{0x4}: {}, + }, + }, + // Chain: + // C3->C4 (HEAD) + op: func(tr *layerTree) { + tr.cap(common.Hash{0x4}, 1) + }, + snapshotB: map[common.Hash]map[common.Hash]struct{}{ + common.Hash{0x3}: { + common.Hash{0x4}: {}, + }, + }, + }, + { + // Chain: + // C1->C2->C3->C4 (HEAD) + init: func() *layerTree { + tr := newTestLayerTree() + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return tr + }, + snapshotA: map[common.Hash]map[common.Hash]struct{}{ + common.Hash{0x1}: { + common.Hash{0x2}: {}, + common.Hash{0x3}: {}, + common.Hash{0x4}: {}, + }, + common.Hash{0x2}: { + common.Hash{0x3}: {}, + common.Hash{0x4}: {}, + }, + common.Hash{0x3}: { + common.Hash{0x4}: {}, + }, + }, + // Chain: + // C4 (HEAD) + op: func(tr *layerTree) { + tr.cap(common.Hash{0x4}, 0) + }, + snapshotB: map[common.Hash]map[common.Hash]struct{}{}, + }, + { + // Chain: + // C1->C2->C3->C4 (HEAD) + // ->C2'->C3'->C4' + init: func() *layerTree { + tr := newTestLayerTree() + tr.add(common.Hash{0x2a}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3a}, common.Hash{0x2a}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2b}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3b}, common.Hash{0x2b}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return tr + }, + snapshotA: map[common.Hash]map[common.Hash]struct{}{ + common.Hash{0x1}: { + common.Hash{0x2a}: {}, + common.Hash{0x3a}: {}, + common.Hash{0x4a}: {}, + common.Hash{0x2b}: {}, + common.Hash{0x3b}: {}, + common.Hash{0x4b}: {}, + }, + common.Hash{0x2a}: { + common.Hash{0x3a}: {}, + common.Hash{0x4a}: {}, + }, + common.Hash{0x3a}: { + common.Hash{0x4a}: {}, + }, + common.Hash{0x2b}: { + common.Hash{0x3b}: {}, + common.Hash{0x4b}: {}, + }, + common.Hash{0x3b}: { + common.Hash{0x4b}: {}, + }, + }, + // Chain: + // C2->C3->C4 (HEAD) + op: func(tr *layerTree) { + tr.cap(common.Hash{0x4a}, 2) + }, + snapshotB: map[common.Hash]map[common.Hash]struct{}{ + common.Hash{0x2a}: { + common.Hash{0x3a}: {}, + common.Hash{0x4a}: {}, + }, + common.Hash{0x3a}: { + common.Hash{0x4a}: {}, + }, + }, + }, + { + // Chain: + // C1->C2->C3->C4 (HEAD) + // ->C2'->C3'->C4' + init: func() *layerTree { + tr := newTestLayerTree() + tr.add(common.Hash{0x2a}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3a}, common.Hash{0x2a}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x2b}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3b}, common.Hash{0x2b}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return tr + }, + snapshotA: map[common.Hash]map[common.Hash]struct{}{ + common.Hash{0x1}: { + common.Hash{0x2a}: {}, + common.Hash{0x3a}: {}, + common.Hash{0x4a}: {}, + common.Hash{0x2b}: {}, + common.Hash{0x3b}: {}, + common.Hash{0x4b}: {}, + }, + common.Hash{0x2a}: { + common.Hash{0x3a}: {}, + common.Hash{0x4a}: {}, + }, + common.Hash{0x3a}: { + common.Hash{0x4a}: {}, + }, + common.Hash{0x2b}: { + common.Hash{0x3b}: {}, + common.Hash{0x4b}: {}, + }, + common.Hash{0x3b}: { + common.Hash{0x4b}: {}, + }, + }, + // Chain: + // C3->C4 (HEAD) + op: func(tr *layerTree) { + tr.cap(common.Hash{0x4a}, 1) + }, + snapshotB: map[common.Hash]map[common.Hash]struct{}{ + common.Hash{0x3a}: { + common.Hash{0x4a}: {}, + }, + }, + }, + { + // Chain: + // C1->C2->C3->C4 (HEAD) + // ->C3'->C4' + init: func() *layerTree { + tr := newTestLayerTree() + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3a}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4a}, common.Hash{0x3a}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x3b}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + tr.add(common.Hash{0x4b}, common.Hash{0x3b}, 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(nil, nil, nil, nil, false)) + return tr + }, + snapshotA: map[common.Hash]map[common.Hash]struct{}{ + common.Hash{0x1}: { + common.Hash{0x2}: {}, + common.Hash{0x3a}: {}, + common.Hash{0x4a}: {}, + common.Hash{0x3b}: {}, + common.Hash{0x4b}: {}, + }, + common.Hash{0x2}: { + common.Hash{0x3a}: {}, + common.Hash{0x4a}: {}, + common.Hash{0x3b}: {}, + common.Hash{0x4b}: {}, + }, + common.Hash{0x3a}: { + common.Hash{0x4a}: {}, + }, + common.Hash{0x3b}: { + common.Hash{0x4b}: {}, + }, + }, + // Chain: + // C2->C3->C4 (HEAD) + // ->C3'->C4' + op: func(tr *layerTree) { + tr.cap(common.Hash{0x4a}, 2) + }, + snapshotB: map[common.Hash]map[common.Hash]struct{}{ + common.Hash{0x2}: { + common.Hash{0x3a}: {}, + common.Hash{0x4a}: {}, + common.Hash{0x3b}: {}, + common.Hash{0x4b}: {}, + }, + common.Hash{0x3a}: { + common.Hash{0x4a}: {}, + }, + common.Hash{0x3b}: { + common.Hash{0x4b}: {}, + }, + }, + }, + } + check := func(setA, setB map[common.Hash]map[common.Hash]struct{}) bool { + if len(setA) != len(setB) { + return false + } + for h, subA := range setA { + subB, ok := setB[h] + if !ok { + return false + } + if len(subA) != len(subB) { + return false + } + for hh := range subA { + if _, ok := subB[hh]; !ok { + return false + } + } + } + return true + } + for _, c := range cases { + tr := c.init() + if !check(c.snapshotA, tr.descendants) { + t.Fatalf("Unexpected descendants") + } + c.op(tr) + if !check(c.snapshotB, tr.descendants) { + t.Fatalf("Unexpected descendants") + } + } +} + +func TestAccountLookup(t *testing.T) { + // Chain: + // C1->C2->C3->C4 (HEAD) + tr := newTestLayerTree() // base = 0x1 + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xa"), nil, nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xb"), nil, nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xa", "0xc"), nil, nil, nil, false)) + + var cases = []struct { + account common.Hash + state common.Hash + expect common.Hash + }{ + { + // unknown account + common.HexToHash("0xd"), common.Hash{0x4}, common.Hash{0x1}, + }, + /* + lookup account from the top + */ + { + common.HexToHash("0xa"), common.Hash{0x4}, common.Hash{0x4}, + }, + { + common.HexToHash("0xb"), common.Hash{0x4}, common.Hash{0x3}, + }, + { + common.HexToHash("0xc"), common.Hash{0x4}, common.Hash{0x4}, + }, + /* + lookup account from the middle + */ + { + common.HexToHash("0xa"), common.Hash{0x3}, common.Hash{0x2}, + }, + { + common.HexToHash("0xb"), common.Hash{0x3}, common.Hash{0x3}, + }, + { + common.HexToHash("0xc"), common.Hash{0x3}, common.Hash{0x1}, // not found + }, + { + common.HexToHash("0xa"), common.Hash{0x2}, common.Hash{0x2}, + }, + { + common.HexToHash("0xb"), common.Hash{0x2}, common.Hash{0x1}, // not found + }, + { + common.HexToHash("0xc"), common.Hash{0x2}, common.Hash{0x1}, // not found + }, + /* + lookup account from the bottom + */ + { + common.HexToHash("0xa"), common.Hash{0x1}, common.Hash{0x1}, // not found + }, + { + common.HexToHash("0xb"), common.Hash{0x1}, common.Hash{0x1}, // not found + }, + { + common.HexToHash("0xc"), common.Hash{0x1}, common.Hash{0x1}, // not found + }, + } + for i, c := range cases { + l, err := tr.lookupAccount(c.account, c.state) + if err != nil { + t.Fatalf("%d: %v", i, err) + } + if l.rootHash() != c.expect { + t.Errorf("Unexpected tiphash, %d, want: %x, got: %x", i, c.expect, l.rootHash()) + } + } + + // Chain: + // C3->C4 (HEAD) + tr.cap(common.Hash{0x4}, 1) + + cases2 := []struct { + account common.Hash + state common.Hash + expect common.Hash + expectErr error + }{ + { + // unknown account + common.HexToHash("0xd"), common.Hash{0x4}, common.Hash{0x3}, nil, + }, + /* + lookup account from the top + */ + { + common.HexToHash("0xa"), common.Hash{0x4}, common.Hash{0x4}, nil, + }, + { + common.HexToHash("0xb"), common.Hash{0x4}, common.Hash{0x3}, nil, + }, + { + common.HexToHash("0xc"), common.Hash{0x4}, common.Hash{0x4}, nil, + }, + /* + lookup account from the bottom + */ + { + common.HexToHash("0xa"), common.Hash{0x3}, common.Hash{0x3}, nil, + }, + { + common.HexToHash("0xb"), common.Hash{0x3}, common.Hash{0x3}, nil, + }, + { + common.HexToHash("0xc"), common.Hash{0x3}, common.Hash{0x3}, nil, // not found + }, + /* + stale states + */ + { + common.HexToHash("0xa"), common.Hash{0x2}, common.Hash{}, errSnapshotStale, + }, + { + common.HexToHash("0xb"), common.Hash{0x2}, common.Hash{}, errSnapshotStale, + }, + { + common.HexToHash("0xc"), common.Hash{0x2}, common.Hash{}, errSnapshotStale, + }, + { + common.HexToHash("0xa"), common.Hash{0x1}, common.Hash{}, errSnapshotStale, + }, + { + common.HexToHash("0xb"), common.Hash{0x1}, common.Hash{}, errSnapshotStale, + }, + { + common.HexToHash("0xc"), common.Hash{0x1}, common.Hash{}, errSnapshotStale, + }, + } + for i, c := range cases2 { + l, err := tr.lookupAccount(c.account, c.state) + if c.expectErr != nil { + if !errors.Is(err, c.expectErr) { + t.Fatalf("%d: unexpected error, want %v, got %v", i, c.expectErr, err) + } + } + if c.expectErr == nil { + if err != nil { + t.Fatalf("%d: %v", i, err) + } + if l.rootHash() != c.expect { + t.Errorf("Unexpected tiphash, %d, want: %x, got: %x", i, c.expect, l.rootHash()) + } + } + } +} + +func TestStorageLookup(t *testing.T) { + // Chain: + // C1->C2->C3->C4 (HEAD) + tr := newTestLayerTree() // base = 0x1 + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xa"), randomStorageSet([]string{"0xa"}, [][]string{{"0x1"}}, nil), nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xa"), randomStorageSet([]string{"0xa"}, [][]string{{"0x2"}}, nil), nil, nil, false)) + tr.add(common.Hash{0x4}, common.Hash{0x3}, 3, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xa"), randomStorageSet([]string{"0xa"}, [][]string{{"0x1", "0x3"}}, nil), nil, nil, false)) + + var cases = []struct { + storage common.Hash + state common.Hash + expect common.Hash + }{ + { + // unknown storage slot + common.HexToHash("0x4"), common.Hash{0x4}, common.Hash{0x1}, + }, + /* + lookup storage slot from the top + */ + { + common.HexToHash("0x1"), common.Hash{0x4}, common.Hash{0x4}, + }, + { + common.HexToHash("0x2"), common.Hash{0x4}, common.Hash{0x3}, + }, + { + common.HexToHash("0x3"), common.Hash{0x4}, common.Hash{0x4}, + }, + /* + lookup storage slot from the middle + */ + { + common.HexToHash("0x1"), common.Hash{0x3}, common.Hash{0x2}, + }, + { + common.HexToHash("0x2"), common.Hash{0x3}, common.Hash{0x3}, + }, + { + common.HexToHash("0x3"), common.Hash{0x3}, common.Hash{0x1}, // not found + }, + { + common.HexToHash("0x1"), common.Hash{0x2}, common.Hash{0x2}, + }, + { + common.HexToHash("0x2"), common.Hash{0x2}, common.Hash{0x1}, // not found + }, + { + common.HexToHash("0x3"), common.Hash{0x2}, common.Hash{0x1}, // not found + }, + /* + lookup storage slot from the bottom + */ + { + common.HexToHash("0x1"), common.Hash{0x1}, common.Hash{0x1}, // not found + }, + { + common.HexToHash("0x2"), common.Hash{0x1}, common.Hash{0x1}, // not found + }, + { + common.HexToHash("0x3"), common.Hash{0x1}, common.Hash{0x1}, // not found + }, + } + for i, c := range cases { + l, err := tr.lookupStorage(common.HexToHash("0xa"), c.storage, c.state) + if err != nil { + t.Fatalf("%d: %v", i, err) + } + if l.rootHash() != c.expect { + t.Errorf("Unexpected tiphash, %d, want: %x, got: %x", i, c.expect, l.rootHash()) + } + } + + // Chain: + // C3->C4 (HEAD) + tr.cap(common.Hash{0x4}, 1) + + cases2 := []struct { + storage common.Hash + state common.Hash + expect common.Hash + expectErr error + }{ + { + // unknown storage slot + common.HexToHash("0x4"), common.Hash{0x4}, common.Hash{0x3}, nil, + }, + /* + lookup account from the top + */ + { + common.HexToHash("0x1"), common.Hash{0x4}, common.Hash{0x4}, nil, + }, + { + common.HexToHash("0x2"), common.Hash{0x4}, common.Hash{0x3}, nil, + }, + { + common.HexToHash("0x3"), common.Hash{0x4}, common.Hash{0x4}, nil, + }, + /* + lookup account from the bottom + */ + { + common.HexToHash("0x1"), common.Hash{0x3}, common.Hash{0x3}, nil, + }, + { + common.HexToHash("0x2"), common.Hash{0x3}, common.Hash{0x3}, nil, + }, + { + common.HexToHash("0x3"), common.Hash{0x3}, common.Hash{0x3}, nil, // not found + }, + /* + stale states + */ + { + common.HexToHash("0x1"), common.Hash{0x2}, common.Hash{}, errSnapshotStale, + }, + { + common.HexToHash("0x2"), common.Hash{0x2}, common.Hash{}, errSnapshotStale, + }, + { + common.HexToHash("0x3"), common.Hash{0x2}, common.Hash{}, errSnapshotStale, + }, + { + common.HexToHash("0x1"), common.Hash{0x1}, common.Hash{}, errSnapshotStale, + }, + { + common.HexToHash("0x2"), common.Hash{0x1}, common.Hash{}, errSnapshotStale, + }, + { + common.HexToHash("0x3"), common.Hash{0x1}, common.Hash{}, errSnapshotStale, + }, + } + for i, c := range cases2 { + l, err := tr.lookupStorage(common.HexToHash("0xa"), c.storage, c.state) + if c.expectErr != nil { + if !errors.Is(err, c.expectErr) { + t.Fatalf("%d: unexpected error, want %v, got %v", i, c.expectErr, err) + } + } + if c.expectErr == nil { + if err != nil { + t.Fatalf("%d: %v", i, err) + } + if l.rootHash() != c.expect { + t.Errorf("Unexpected tiphash, %d, want: %x, got: %x", i, c.expect, l.rootHash()) + } + } + } +} diff --git a/triedb/pathdb/lookup.go b/triedb/pathdb/lookup.go new file mode 100644 index 00000000000..8b092730f8f --- /dev/null +++ b/triedb/pathdb/lookup.go @@ -0,0 +1,278 @@ +// Copyright 2024 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 pathdb + +import ( + "fmt" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "golang.org/x/sync/errgroup" +) + +// storageKey returns a key for uniquely identifying the storage slot. +func storageKey(accountHash common.Hash, slotHash common.Hash) [64]byte { + var key [64]byte + copy(key[:32], accountHash[:]) + copy(key[32:], slotHash[:]) + return key +} + +// lookup is an internal structure used to efficiently determine the layer in +// which a state entry resides. +type lookup struct { + // accounts represents the mutation history for specific accounts. + // The key is the account address hash, and the value is a slice + // of **diff layer** IDs indicating where the account was modified, + // with the order from oldest to newest. + accounts map[common.Hash][]common.Hash + + // storages represents the mutation history for specific storage + // slot. The key is the account address hash and the storage key + // hash, the value is a slice of **diff layer** IDs indicating + // where the slot was modified, with the order from oldest to newest. + storages map[[64]byte][]common.Hash + + // descendant is the callback indicating whether the layer with + // given root is a descendant of the one specified by `ancestor`. + descendant func(state common.Hash, ancestor common.Hash) bool +} + +// newLookup initializes the lookup structure. +func newLookup(head layer, descendant func(state common.Hash, ancestor common.Hash) bool) *lookup { + var ( + current = head + layers []layer + ) + for current != nil { + layers = append(layers, current) + current = current.parentLayer() + } + l := &lookup{ + accounts: make(map[common.Hash][]common.Hash), + storages: make(map[[64]byte][]common.Hash), + descendant: descendant, + } + // Apply the diff layers from bottom to top + for i := len(layers) - 1; i >= 0; i-- { + switch diff := layers[i].(type) { + case *diskLayer: + continue + case *diffLayer: + l.addLayer(diff) + } + } + return l +} + +// accountTip traverses the layer list associated with the given account in +// reverse order to locate the first entry that either matches the specified +// stateID or is a descendant of it. +// +// If found, the account data corresponding to the supplied stateID resides +// in that layer. Otherwise, two scenarios are possible: +// +// (a) the account remains unmodified from the current disk layer up to the state +// layer specified by the stateID: fallback to the disk layer for data retrieval, +// (b) or the layer specified by the stateID is stale: reject the data retrieval. +func (l *lookup) accountTip(accountHash common.Hash, stateID common.Hash, base common.Hash) common.Hash { + // Traverse the mutation history from latest to oldest one. Several + // scenarios are possible: + // + // Chain: + // D->C1->C2->C3->C4 (HEAD) + // ->C1'->C2'->C3' + // State: + // x: [C1, C1', C3', C3] + // y: [] + // + // - (x, C4) => C3 + // - (x, C3) => C3 + // - (x, C2) => C1 + // - (x, C3') => C3' + // - (x, C2') => C1' + // - (y, C4) => D + // - (y, C3') => D + // - (y, C0) => null + list := l.accounts[accountHash] + for i := len(list) - 1; i >= 0; i-- { + // If the current state matches the stateID, or the requested state is a + // descendant of it, return the current state as the most recent one + // containing the modified data. Otherwise, the current state may be ahead + // of the requested one or belong to a different branch. + if list[i] == stateID || l.descendant(stateID, list[i]) { + return list[i] + } + } + // No layer matching the stateID or its descendants was found. Use the + // current disk layer as a fallback. + if base == stateID || l.descendant(stateID, base) { + return base + } + // The layer associated with 'stateID' is not the descendant of the current + // disk layer, it's already stale, return nothing. + return common.Hash{} +} + +// storageTip traverses the layer list associated with the given account and +// slot hash in reverse order to locate the first entry that either matches +// the specified stateID or is a descendant of it. +// +// If found, the storage data corresponding to the supplied stateID resides +// in that layer. Otherwise, two scenarios are possible: +// +// (a) the storage slot remains unmodified from the current disk layer up to +// the state layer specified by the stateID: fallback to the disk layer for +// data retrieval, (b) or the layer specified by the stateID is stale: reject +// the data retrieval. +func (l *lookup) storageTip(accountHash common.Hash, slotHash common.Hash, stateID common.Hash, base common.Hash) common.Hash { + list := l.storages[storageKey(accountHash, slotHash)] + for i := len(list) - 1; i >= 0; i-- { + // If the current state matches the stateID, or the requested state is a + // descendant of it, return the current state as the most recent one + // containing the modified data. Otherwise, the current state may be ahead + // of the requested one or belong to a different branch. + if list[i] == stateID || l.descendant(stateID, list[i]) { + return list[i] + } + } + // No layer matching the stateID or its descendants was found. Use the + // current disk layer as a fallback. + if base == stateID || l.descendant(stateID, base) { + return base + } + // The layer associated with 'stateID' is not the descendant of the current + // disk layer, it's already stale, return nothing. + return common.Hash{} +} + +// addLayer traverses the state data retained in the specified diff layer and +// integrates it into the lookup set. +// +// This function assumes that all layers older than the provided one have already +// been processed, ensuring that layers are processed strictly in a bottom-to-top +// order. +func (l *lookup) addLayer(diff *diffLayer) { + defer func(now time.Time) { + lookupAddLayerTimer.UpdateSince(now) + }(time.Now()) + + var ( + wg sync.WaitGroup + state = diff.rootHash() + ) + wg.Add(1) + go func() { + defer wg.Done() + for accountHash := range diff.states.accountData { + list, exists := l.accounts[accountHash] + if !exists { + list = make([]common.Hash, 0, 16) // TODO(rjl493456442) use sync pool + } + list = append(list, state) + l.accounts[accountHash] = list + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + for accountHash, slots := range diff.states.storageData { + for slotHash := range slots { + key := storageKey(accountHash, slotHash) + list, exists := l.storages[key] + if !exists { + list = make([]common.Hash, 0, 16) // TODO(rjl493456442) use sync pool + } + list = append(list, state) + l.storages[key] = list + } + } + }() + wg.Wait() +} + +// removeFromList removes the specified element from the provided list. +// It returns a flag indicating whether the element was found and removed. +func removeFromList(list []common.Hash, element common.Hash) (bool, []common.Hash) { + // Traverse the list from oldest to newest to quickly locate the element. + for i := 0; i < len(list); i++ { + if list[i] == element { + if i != 0 { + list = append(list[:i], list[i+1:]...) + } else { + // Remove the first element by shifting the slice forward. + // Pros: zero-copy. + // Cons: may retain large backing array, causing memory leaks. + // Mitigation: release the array if capacity exceeds threshold. + list = list[1:] + if cap(list) > 1024 { + list = append(make([]common.Hash, 0, len(list)), list...) + } + } + return true, list + } + } + return false, nil +} + +// removeLayer traverses the state data retained in the specified diff layer and +// unlink them from the lookup set. +func (l *lookup) removeLayer(diff *diffLayer) error { + defer func(now time.Time) { + lookupRemoveLayerTimer.UpdateSince(now) + }(time.Now()) + + var ( + eg errgroup.Group + state = diff.rootHash() + ) + eg.Go(func() error { + for accountHash := range diff.states.accountData { + found, list := removeFromList(l.accounts[accountHash], state) + if !found { + return fmt.Errorf("account lookup is not found, %x, state: %x", accountHash, state) + } + if len(list) != 0 { + l.accounts[accountHash] = list + } else { + delete(l.accounts, accountHash) + } + } + return nil + }) + + eg.Go(func() error { + for accountHash, slots := range diff.states.storageData { + for slotHash := range slots { + key := storageKey(accountHash, slotHash) + found, list := removeFromList(l.storages[key], state) + if !found { + return fmt.Errorf("storage lookup is not found, %x %x, state: %x", accountHash, slotHash, state) + } + if len(list) != 0 { + l.storages[key] = list + } else { + delete(l.storages, key) + } + } + } + return nil + }) + return eg.Wait() +} diff --git a/triedb/pathdb/metrics.go b/triedb/pathdb/metrics.go index 502c34fc62e..6d40c5713b4 100644 --- a/triedb/pathdb/metrics.go +++ b/triedb/pathdb/metrics.go @@ -72,6 +72,9 @@ var ( historyBuildTimeMeter = metrics.NewRegisteredResettingTimer("pathdb/history/time", nil) historyDataBytesMeter = metrics.NewRegisteredMeter("pathdb/history/bytes/data", nil) historyIndexBytesMeter = metrics.NewRegisteredMeter("pathdb/history/bytes/index", nil) + + lookupAddLayerTimer = metrics.NewRegisteredResettingTimer("pathdb/lookup/add/time", nil) + lookupRemoveLayerTimer = metrics.NewRegisteredResettingTimer("pathdb/lookup/remove/time", nil) ) // Metrics in generation diff --git a/triedb/pathdb/reader.go b/triedb/pathdb/reader.go index 30f75d10582..bc72db34e34 100644 --- a/triedb/pathdb/reader.go +++ b/triedb/pathdb/reader.go @@ -17,6 +17,7 @@ package pathdb import ( + "errors" "fmt" "github.com/ethereum/go-ethereum/common" @@ -50,8 +51,10 @@ func (loc *nodeLoc) string() string { // reader implements the database.NodeReader interface, providing the functionalities to // retrieve trie nodes by wrapping the internal state layer. type reader struct { - layer layer + db *Database + state common.Hash noHashCheck bool + layer layer } // Node implements database.NodeReader interface, retrieving the node with specified @@ -94,7 +97,23 @@ func (r *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, // - the returned account data is not a copy, please don't modify it // - no error will be returned if the requested account is not found in database func (r *reader) AccountRLP(hash common.Hash) ([]byte, error) { - return r.layer.account(hash, 0) + l, err := r.db.tree.lookupAccount(hash, r.state) + if err != nil { + return nil, err + } + // If the located layer is stale, fall back to the slow path to retrieve + // the account data. This is an edge case where the located layer is the + // disk layer (e.g., the requested account was not changed in all the diff + // layers), and it becomes stale within a very short time window. + // + // This fallback mechanism is essential, because the traversal starts from + // the entry point layer and goes down, the staleness of the disk layer does + // not affect the result unless the entry point layer is also stale. + blob, err := l.account(hash, 0) + if errors.Is(err, errSnapshotStale) { + return r.layer.account(hash, 0) + } + return blob, err } // Account directly retrieves the account associated with a particular hash in @@ -105,7 +124,7 @@ func (r *reader) AccountRLP(hash common.Hash) ([]byte, error) { // - the returned account object is safe to modify // - no error will be returned if the requested account is not found in database func (r *reader) Account(hash common.Hash) (*types.SlimAccount, error) { - blob, err := r.layer.account(hash, 0) + blob, err := r.AccountRLP(hash) if err != nil { return nil, err } @@ -127,7 +146,23 @@ func (r *reader) Account(hash common.Hash) (*types.SlimAccount, error) { // - the returned storage data is not a copy, please don't modify it // - no error will be returned if the requested slot is not found in database func (r *reader) Storage(accountHash, storageHash common.Hash) ([]byte, error) { - return r.layer.storage(accountHash, storageHash, 0) + l, err := r.db.tree.lookupStorage(accountHash, storageHash, r.state) + if err != nil { + return nil, err + } + // If the located layer is stale, fall back to the slow path to retrieve + // the storage data. This is an edge case where the located layer is the + // disk layer (e.g., the requested account was not changed in all the diff + // layers), and it becomes stale within a very short time window. + // + // This fallback mechanism is essential, because the traversal starts from + // the entry point layer and goes down, the staleness of the disk layer does + // not affect the result unless the entry point layer is also stale. + blob, err := l.storage(accountHash, storageHash, 0) + if errors.Is(err, errSnapshotStale) { + return r.layer.storage(accountHash, storageHash, 0) + } + return blob, err } // NodeReader retrieves a layer belonging to the given state root. @@ -136,7 +171,12 @@ func (db *Database) NodeReader(root common.Hash) (database.NodeReader, error) { if layer == nil { return nil, fmt.Errorf("state %#x is not available", root) } - return &reader{layer: layer, noHashCheck: db.isVerkle}, nil + return &reader{ + db: db, + state: root, + noHashCheck: db.isVerkle, + layer: layer, + }, nil } // StateReader returns a reader that allows access to the state data associated @@ -146,5 +186,9 @@ func (db *Database) StateReader(root common.Hash) (database.StateReader, error) if layer == nil { return nil, fmt.Errorf("state %#x is not available", root) } - return &reader{layer: layer}, nil + return &reader{ + db: db, + state: root, + layer: layer, + }, nil } From 1695182b55f49df20bb3046a0bbc1ca2665f551d Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 28 May 2025 17:51:47 +0200 Subject: [PATCH 207/658] .gitea: adjust cron schedule (#31915) This should make the scheduled build actually run. --- .gitea/workflows/release-cron.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/release-cron.yml b/.gitea/workflows/release-cron.yml index 3a03d0ef149..11798630ddd 100644 --- a/.gitea/workflows/release-cron.yml +++ b/.gitea/workflows/release-cron.yml @@ -2,7 +2,7 @@ name: Release Builds (cron) on: schedule: - cron: '0 0 * * *' + - cron: '0 16 * * *' jobs: azure-cleanup: From 0110b659c1db7612664e7fdfadbe005796992931 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 28 May 2025 18:24:43 +0200 Subject: [PATCH 208/658] .gitea: add env to scheduled builds (#31918) --- .gitea/workflows/release-cron.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitea/workflows/release-cron.yml b/.gitea/workflows/release-cron.yml index 11798630ddd..383c9887d60 100644 --- a/.gitea/workflows/release-cron.yml +++ b/.gitea/workflows/release-cron.yml @@ -3,6 +3,7 @@ name: Release Builds (cron) on: schedule: - cron: '0 16 * * *' + workflow_dispatch: jobs: azure-cleanup: @@ -17,6 +18,8 @@ jobs: cache: false - name: Run cleanup script + env: + AZURE_BLOBSTORE_TOKEN: "{{ secrets.AZURE_BLOBSTORE_TOKEN }}" run: | go run build/ci.go purge -store gethstore/builds -days 14 @@ -37,6 +40,9 @@ jobs: apt-get -yq --no-install-suggests --no-install-recommends install devscripts debhelper dput fakeroot - name: Run ci.go + env: + DOCKER_HUB_USERNAME: "{{ secrets.DOCKER_HUB_USERNAME }}" + DOCKER_HUB_PASSWORD: "{{ secrets.DOCKER_HUB_PASSWORD }}" run: | echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder " From 25f6206137bcfd260a577396e34fa3e9bdafc85e Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 28 May 2025 19:01:33 +0200 Subject: [PATCH 209/658] .gitea: port more builds from travis and fix PPA env (#31919) --- .gitea/workflows/release-cron.yml | 4 +- .gitea/workflows/release.yml | 70 ++++++++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/.gitea/workflows/release-cron.yml b/.gitea/workflows/release-cron.yml index 383c9887d60..4f586f5284d 100644 --- a/.gitea/workflows/release-cron.yml +++ b/.gitea/workflows/release-cron.yml @@ -41,8 +41,8 @@ jobs: - name: Run ci.go env: - DOCKER_HUB_USERNAME: "{{ secrets.DOCKER_HUB_USERNAME }}" - DOCKER_HUB_PASSWORD: "{{ secrets.DOCKER_HUB_PASSWORD }}" + PPA_SIGNING_KEY: "{{ secrets.PPA_SIGNING_KEY }}" + PPA_SSH_KEY: "{{ secrets.PPA_SSH_KEY }}" run: | echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder " diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index a91bebda7f0..29154c6ef06 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -5,7 +5,7 @@ on: branches: [ master ] jobs: - linux: + linux-intel: name: Linux Build runs-on: ubuntu-latest steps: @@ -17,13 +17,68 @@ jobs: go-version: 1.24 cache: false - - name: display environment + - name: Run build (amd64) + env: + LINUX_SIGNING_KEY: "{{ secrets.LINUX_SIGNING_KEY }}" run: | - env + go run build/ci.go install -arch amd64 -dlgo + go run build/ci.go archive -arch amd64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - - name: Run build + - name: Run build (386) + env: + LINUX_SIGNING_KEY: "{{ secrets.LINUX_SIGNING_KEY }}" run: | - go run build/ci.go install + apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib + go run build/ci.go install -arch 386 -dlgo + go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds + + linux-arm: + name: Linux Build (arm) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.24 + cache: false + + - name: Set up cross toolchain + run: | + apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross + ln -s /usr/include/asm-generic /usr/include/asm + + - name: Run build (arm64) + env: + LINUX_SIGNING_KEY: "{{ secrets.LINUX_SIGNING_KEY }}" + run: | + go run build/ci.go install -dlgo -arch arm64 -cc arm-linux-gnueabi-gcc + go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds + + - name: Run build (armv5) + env: + GOARM: "5" + LINUX_SIGNING_KEY: "{{ secrets.LINUX_SIGNING_KEY }}" + run: | + go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc + go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds + + - name: Run build (armv6) + env: + GOARM: "6" + LINUX_SIGNING_KEY: "{{ secrets.LINUX_SIGNING_KEY }}" + run: | + go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc + go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds + + - name: Run build (armv7) + env: + GOARM: "7" + LINUX_SIGNING_KEY: "{{ secrets.LINUX_SIGNING_KEY }}" + run: | + go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc + go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds docker: name: Docker Image @@ -44,5 +99,8 @@ jobs: cache: false - name: Run docker build + env: + DOCKER_HUB_USERNAME: "{{ secrets.DOCKER_HUB_USERNAME }}" + DOCKER_HUB_PASSWORD: "{{ secrets.DOCKER_HUB_PASSWORD }}" run: | - go run build/ci.go dockerx -platform linux/amd64,linux/arm64,linux/riscv64 + go run build/ci.go dockerx -platform linux/amd64,linux/arm64,linux/riscv64 -upload From e817a569fb9b84e252316a29ab491d7b76ce0ad8 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 28 May 2025 20:14:48 +0200 Subject: [PATCH 210/658] .gitea: fix secrets passing (#31920) --- .gitea/workflows/release-cron.yml | 6 +++--- .gitea/workflows/release.yml | 28 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.gitea/workflows/release-cron.yml b/.gitea/workflows/release-cron.yml index 4f586f5284d..735404129a9 100644 --- a/.gitea/workflows/release-cron.yml +++ b/.gitea/workflows/release-cron.yml @@ -19,7 +19,7 @@ jobs: - name: Run cleanup script env: - AZURE_BLOBSTORE_TOKEN: "{{ secrets.AZURE_BLOBSTORE_TOKEN }}" + AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }} run: | go run build/ci.go purge -store gethstore/builds -days 14 @@ -41,8 +41,8 @@ jobs: - name: Run ci.go env: - PPA_SIGNING_KEY: "{{ secrets.PPA_SIGNING_KEY }}" - PPA_SSH_KEY: "{{ secrets.PPA_SSH_KEY }}" + PPA_SIGNING_KEY: ${{ secrets.PPA_SIGNING_KEY }} + PPA_SSH_KEY: ${{ secrets.PPA_SSH_KEY }} run: | echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder " diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 29154c6ef06..7675c75e164 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -19,18 +19,18 @@ jobs: - name: Run build (amd64) env: - LINUX_SIGNING_KEY: "{{ secrets.LINUX_SIGNING_KEY }}" + LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} run: | go run build/ci.go install -arch amd64 -dlgo - go run build/ci.go archive -arch amd64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds + go run build/ci.go archive -arch amd64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - name: Run build (386) env: - LINUX_SIGNING_KEY: "{{ secrets.LINUX_SIGNING_KEY }}" + LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} run: | apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib go run build/ci.go install -arch 386 -dlgo - go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds + go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds linux-arm: name: Linux Build (arm) @@ -51,34 +51,34 @@ jobs: - name: Run build (arm64) env: - LINUX_SIGNING_KEY: "{{ secrets.LINUX_SIGNING_KEY }}" + LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} run: | go run build/ci.go install -dlgo -arch arm64 -cc arm-linux-gnueabi-gcc - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds + go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - name: Run build (armv5) env: GOARM: "5" - LINUX_SIGNING_KEY: "{{ secrets.LINUX_SIGNING_KEY }}" + LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} run: | go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc - go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds + go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - name: Run build (armv6) env: GOARM: "6" - LINUX_SIGNING_KEY: "{{ secrets.LINUX_SIGNING_KEY }}" + LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} run: | go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc - go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds + go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - name: Run build (armv7) env: GOARM: "7" - LINUX_SIGNING_KEY: "{{ secrets.LINUX_SIGNING_KEY }}" + LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} run: | go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc - go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds + go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds docker: name: Docker Image @@ -100,7 +100,7 @@ jobs: - name: Run docker build env: - DOCKER_HUB_USERNAME: "{{ secrets.DOCKER_HUB_USERNAME }}" - DOCKER_HUB_PASSWORD: "{{ secrets.DOCKER_HUB_PASSWORD }}" + DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }} + DOCKER_HUB_PASSWORD: ${{ secrets.DOCKER_HUB_PASSWORD }} run: | go run build/ci.go dockerx -platform linux/amd64,linux/arm64,linux/riscv64 -upload From 31d898d586b6388ce47c1965eae8f2c7114d194f Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 28 May 2025 22:40:26 +0200 Subject: [PATCH 211/658] .gitea: fix apt update (#31921) --- .gitea/workflows/release-cron.yml | 1 + .gitea/workflows/release.yml | 25 +++++++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/release-cron.yml b/.gitea/workflows/release-cron.yml index 735404129a9..1a8eea28908 100644 --- a/.gitea/workflows/release-cron.yml +++ b/.gitea/workflows/release-cron.yml @@ -37,6 +37,7 @@ jobs: - name: Install deb toolchain run: | + apt-get update apt-get -yq --no-install-suggests --no-install-recommends install devscripts debhelper dput fakeroot - name: Run ci.go diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 7675c75e164..8efd453642d 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -17,20 +17,32 @@ jobs: go-version: 1.24 cache: false - - name: Run build (amd64) + - name: Install cross toolchain + run: | + apt-get update + apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib + + - name: Build (amd64) + run: | + go run build/ci.go install -arch amd64 -dlgo + + - name: Create archive (amd64) env: LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} run: | - go run build/ci.go install -arch amd64 -dlgo go run build/ci.go archive -arch amd64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + rm -f build/bin/* + + - name: Build (386) + run: | + go run build/ci.go install -arch 386 -dlgo - - name: Run build (386) + - name: Create archive (386) env: LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} run: | - apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib - go run build/ci.go install -arch 386 -dlgo go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + rm -f build/bin/* linux-arm: name: Linux Build (arm) @@ -46,7 +58,8 @@ jobs: - name: Set up cross toolchain run: | - apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross + apt-get update + apt-get -yq --no-install-suggests --no-install-recommends install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross ln -s /usr/include/asm-generic /usr/include/asm - name: Run build (arm64) From b065aa6740e4ed109b5e3efd9abadb12b439870c Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 28 May 2025 22:57:58 +0200 Subject: [PATCH 212/658] .gitea: fix archive uploads and run PPA upload on release push (#31922) --- .gitea/workflows/release-azure-cleanup.yml | 22 ++++++ .../{release-cron.yml => release-ppa.yml} | 33 +++------ .gitea/workflows/release.yml | 69 +++++++++++++------ 3 files changed, 80 insertions(+), 44 deletions(-) create mode 100644 .gitea/workflows/release-azure-cleanup.yml rename .gitea/workflows/{release-cron.yml => release-ppa.yml} (73%) diff --git a/.gitea/workflows/release-azure-cleanup.yml b/.gitea/workflows/release-azure-cleanup.yml new file mode 100644 index 00000000000..da2400ee268 --- /dev/null +++ b/.gitea/workflows/release-azure-cleanup.yml @@ -0,0 +1,22 @@ +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +jobs: + azure-cleanup: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.24 + cache: false + + - name: Run cleanup script + run: | + go run build/ci.go purge -store gethstore/builds -days 14 + env: + AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }} diff --git a/.gitea/workflows/release-cron.yml b/.gitea/workflows/release-ppa.yml similarity index 73% rename from .gitea/workflows/release-cron.yml rename to .gitea/workflows/release-ppa.yml index 1a8eea28908..23bffe773d7 100644 --- a/.gitea/workflows/release-cron.yml +++ b/.gitea/workflows/release-ppa.yml @@ -1,30 +1,14 @@ -name: Release Builds (cron) - on: schedule: - cron: '0 16 * * *' + push: + branches: + - "release/*" workflow_dispatch: jobs: - azure-cleanup: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: 1.24 - cache: false - - - name: Run cleanup script - env: - AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }} - run: | - go run build/ci.go purge -store gethstore/builds -days 14 - ppa: - name: PPA Upload (master) + name: PPA Upload runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -40,10 +24,13 @@ jobs: apt-get update apt-get -yq --no-install-suggests --no-install-recommends install devscripts debhelper dput fakeroot + - name: Add launchpad to known_hosts + run: | + echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts + - name: Run ci.go + run: | + go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder " env: PPA_SIGNING_KEY: ${{ secrets.PPA_SIGNING_KEY }} PPA_SSH_KEY: ${{ secrets.PPA_SSH_KEY }} - run: | - echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts - go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder " diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 8efd453642d..10e0f7aa7ff 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -1,8 +1,8 @@ -name: Release Builds (push) - on: push: - branches: [ master ] + branches: + - "master" + - "release/*" jobs: linux-intel: @@ -26,23 +26,25 @@ jobs: run: | go run build/ci.go install -arch amd64 -dlgo - - name: Create archive (amd64) - env: - LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} + - name: Create/upload archive (amd64) run: | go run build/ci.go archive -arch amd64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds rm -f build/bin/* + env: + LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} + AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }} - name: Build (386) run: | go run build/ci.go install -arch 386 -dlgo - - name: Create archive (386) - env: - LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} + - name: Create/upload archive (386) run: | go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds rm -f build/bin/* + env: + LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} + AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }} linux-arm: name: Linux Build (arm) @@ -56,42 +58,67 @@ jobs: go-version: 1.24 cache: false - - name: Set up cross toolchain + - name: Install cross toolchain run: | apt-get update apt-get -yq --no-install-suggests --no-install-recommends install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross ln -s /usr/include/asm-generic /usr/include/asm - - name: Run build (arm64) - env: - LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} + - name: Build (arm64) run: | go run build/ci.go install -dlgo -arch arm64 -cc arm-linux-gnueabi-gcc + + - name: Create/upload archive (arm64) + run: | go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + rm -fr build/bin/* + env: + LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} + AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }} + + - name: Run build (arm5) + run: | + go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc + env: + GOARM: "5" - - name: Run build (armv5) + - name: Create/upload archive (arm5) + run: | + go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds env: GOARM: "5" LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} + AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }} + + - name: Run build (arm6) run: | go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc - go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + env: + GOARM: "6" - - name: Run build (armv6) + - name: Create/upload archive (arm6) + run: | + go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + rm -fr build/bin/* env: GOARM: "6" LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} + AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }} + + - name: Run build (arm7) run: | go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc - go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - - - name: Run build (armv7) env: GOARM: "7" - LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} + + - name: Create/upload archive (arm7) run: | - go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + rm -fr build/bin/* + env: + GOARM: "7" + LINUX_SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }} + AZURE_BLOBSTORE_TOKEN: ${{ secrets.AZURE_BLOBSTORE_TOKEN }} docker: name: Docker Image From 3524499d558ce85b51d5e803154e01b9210b3913 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 28 May 2025 23:16:51 +0200 Subject: [PATCH 213/658] .gitea: fix arm64 build (#31923) --- .gitea/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 10e0f7aa7ff..f1637bd5ae0 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -66,7 +66,7 @@ jobs: - name: Build (arm64) run: | - go run build/ci.go install -dlgo -arch arm64 -cc arm-linux-gnueabi-gcc + go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc - name: Create/upload archive (arm64) run: | From e9e4e8b05ae99c8f4e22ae2e6db4f75ce5b848b6 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 28 May 2025 23:19:13 +0200 Subject: [PATCH 214/658] .gitea: run release build on tag push (#31924) --- .gitea/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index f1637bd5ae0..04e724ccf45 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -2,7 +2,8 @@ on: push: branches: - "master" - - "release/*" + tags: + - "v*" jobs: linux-intel: From d821f7f297259b2ed5ab0d3c99c0e4aca9663ac8 Mon Sep 17 00:00:00 2001 From: Arya Nair Date: Thu, 29 May 2025 09:50:39 +0530 Subject: [PATCH 215/658] cmd/geth, cmd/utils: log prefunded account/key in ephemeral development mode (#31898) This PR modifies the disclaimer/banner that is printed when starting up Geth in dev mode: * if the client is spun up in ephemeral dev mode with a keystore override, the address of the first (prefunded) account is printed. * if the client is spun up in ephemeral mode without a keystore override, the genesis allocation contains a single static prefunded EOA account. It's address and private key are logged. * the banner is printed at the end of client initialization to make it more prominent. Previously, it was logged towards the beginning of client initialization and subsequent logging from startup filled the terminal, pushing it out of view of the user. Other change is that we now use a static prefunded dev account instead of generating a random one when instantiating a new dev mode chain. This is an example of what the banner looks like: ``` WARN [05-28|23:05:16.475] You are running Geth in --dev mode. Please note the following: WARN [05-28|23:05:16.475] WARN [05-28|23:05:16.475] 1. This mode is only intended for fast, iterative development without assumptions on WARN [05-28|23:05:16.475] security or persistence. WARN [05-28|23:05:16.475] 2. The database is created in memory unless specified otherwise. Therefore, shutting down WARN [05-28|23:05:16.475] your computer or losing power will wipe your entire block data and chain state for WARN [05-28|23:05:16.475] your dev environment. WARN [05-28|23:05:16.475] 3. A random, pre-allocated developer account will be available and unlocked as WARN [05-28|23:05:16.475] eth.coinbase, which can be used for testing. The random dev account is temporary, WARN [05-28|23:05:16.475] stored on a ramdisk, and will be lost if your machine is restarted. WARN [05-28|23:05:16.475] 4. Mining is enabled by default. However, the client will only seal blocks if transactions WARN [05-28|23:05:16.475] are pending in the mempool. The miner's minimum accepted gas price is 1. WARN [05-28|23:05:16.475] 5. Networking is disabled; there is no listen-address, the maximum number of peers is set WARN [05-28|23:05:16.475] to 0, and discovery is disabled. WARN [05-28|23:05:16.475] WARN [05-28|23:05:16.475] WARN [05-28|23:05:16.475] Running in ephemeral mode. The following account has been prefunded in the genesis: WARN [05-28|23:05:16.475] WARN [05-28|23:05:16.475] Account WARN [05-28|23:05:16.475] ------------------ WARN [05-28|23:05:16.475] 0x71562b71999873db5b286df957af199ec94617f7 (10^49 ETH) WARN [05-28|23:05:16.475] WARN [05-28|23:05:16.475] Private Key WARN [05-28|23:05:16.475] ------------------ WARN [05-28|23:05:16.475] 0xb71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291 WARN [05-28|23:05:16.475] ``` closes #31796 --------- Co-authored-by: jwasinger --- cmd/geth/config.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ cmd/geth/main.go | 18 ------------------ cmd/utils/flags.go | 20 +++++++++++++++----- 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 4215403234a..3eb2b6dd37f 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/flags" @@ -180,6 +181,45 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) { return stack, cfg } +// constructs the disclaimer text block which will be printed in the logs upon +// startup when Geth is running in dev mode. +func constructDevModeBanner(ctx *cli.Context, cfg gethConfig) string { + devModeBanner := `You are running Geth in --dev mode. Please note the following: + + 1. This mode is only intended for fast, iterative development without assumptions on + security or persistence. + 2. The database is created in memory unless specified otherwise. Therefore, shutting down + your computer or losing power will wipe your entire block data and chain state for + your dev environment. + 3. A random, pre-allocated developer account will be available and unlocked as + eth.coinbase, which can be used for testing. The random dev account is temporary, + stored on a ramdisk, and will be lost if your machine is restarted. + 4. Mining is enabled by default. However, the client will only seal blocks if transactions + are pending in the mempool. The miner's minimum accepted gas price is 1. + 5. Networking is disabled; there is no listen-address, the maximum number of peers is set + to 0, and discovery is disabled. +` + if !ctx.IsSet(utils.DataDirFlag.Name) { + devModeBanner += fmt.Sprintf(` + + Running in ephemeral mode. The following account has been prefunded in the genesis: + + Account + ------------------ + 0x%x (10^49 ETH) +`, cfg.Eth.Miner.PendingFeeRecipient) + if cfg.Eth.Miner.PendingFeeRecipient == utils.DeveloperAddr { + devModeBanner += fmt.Sprintf(` + Private Key + ------------------ + 0x%x +`, crypto.FromECDSA(utils.DeveloperKey)) + } + } + + return devModeBanner +} + // makeFullNode loads geth configuration and creates the Ethereum backend. func makeFullNode(ctx *cli.Context) *node.Node { stack, cfg := makeConfigNode(ctx) @@ -239,6 +279,11 @@ func makeFullNode(ctx *cli.Context) *node.Node { } catalyst.RegisterSimulatedBeaconAPIs(stack, simBeacon) stack.RegisterLifecycle(simBeacon) + + banner := constructDevModeBanner(ctx, cfg) + for _, line := range strings.Split(banner, "\n") { + log.Warn(line) + } } else if ctx.IsSet(utils.BeaconApiFlag.Name) { // Start blsync mode. srv := rpc.NewServer() diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 733c9cf2302..289af908283 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -294,24 +294,6 @@ func prepare(ctx *cli.Context) { case ctx.IsSet(utils.HoodiFlag.Name): log.Info("Starting Geth on Hoodi testnet...") - case ctx.IsSet(utils.DeveloperFlag.Name): - log.Info("Starting Geth in ephemeral dev mode...") - log.Warn(`You are running Geth in --dev mode. Please note the following: - - 1. This mode is only intended for fast, iterative development without assumptions on - security or persistence. - 2. The database is created in memory unless specified otherwise. Therefore, shutting down - your computer or losing power will wipe your entire block data and chain state for - your dev environment. - 3. A random, pre-allocated developer account will be available and unlocked as - eth.coinbase, which can be used for testing. The random dev account is temporary, - stored on a ramdisk, and will be lost if your machine is restarted. - 4. Mining is enabled by default. However, the client will only seal blocks if transactions - are pending in the mempool. The miner's minimum accepted gas price is 1. - 5. Networking is disabled; there is no listen-address, the maximum number of peers is set - to 0, and discovery is disabled. -`) - case !ctx.IsSet(utils.NetworkIdFlag.Name): log.Info("Starting Geth on Ethereum mainnet...") } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 81c4172a53d..60a2cc13f11 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -984,6 +984,12 @@ var ( } ) +// default account to prefund when running Geth in dev mode +var ( + DeveloperKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + DeveloperAddr = crypto.PubkeyToAddress(DeveloperKey.PublicKey) +) + // MakeDataDir retrieves the currently requested data directory, terminating // if none (or the empty string) is specified. If the node is starting a testnet, // then a subdirectory of the specified datadir will be used. @@ -1769,9 +1775,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } else if accs := ks.Accounts(); len(accs) > 0 { developer = ks.Accounts()[0] } else { - developer, err = ks.NewAccount(passphrase) + developer, err = ks.ImportECDSA(DeveloperKey, passphrase) if err != nil { - Fatalf("Failed to create developer account: %v", err) + Fatalf("Failed to import developer account: %v", err) } } // Make sure the address is configured as fee recipient, otherwise @@ -1786,14 +1792,18 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } log.Info("Using developer account", "address", developer.Address) - // Create a new developer genesis block or reuse existing one + // configure default developer genesis which will be used unless a + // datadir is specified and a chain is preexisting at that location. cfg.Genesis = core.DeveloperGenesisBlock(ctx.Uint64(DeveloperGasLimitFlag.Name), &developer.Address) + + // If a datadir is specified, ensure that any preexisting chain in that location + // has a configuration that is compatible with dev mode: it must be merged at genesis. if ctx.IsSet(DataDirFlag.Name) { chaindb := tryMakeReadOnlyDatabase(ctx, stack) if rawdb.ReadCanonicalHash(chaindb, 0) != (common.Hash{}) { - cfg.Genesis = nil // fallback to db content + // signal fallback to preexisting chain on disk + cfg.Genesis = nil - //validate genesis has PoS enabled in block 0 genesis, err := core.ReadGenesis(chaindb) if err != nil { Fatalf("Could not read genesis from database: %v", err) From 8712a2cdbab8a6c23b6b7a6e379f35227490f011 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 29 May 2025 12:23:38 +0200 Subject: [PATCH 216/658] .travis.yml: remove master branch push builds (#31927) Release artefact building has been migrated to another system (Gitea), so we can finally stop using Travis CI. However, in order to have a fail-safe for the release, I'm leaving the config in and it will still trigger builds on Travis for tagged releases. That way, if our new system fails to work for the next release, we will still have the option of using Travis. --- .travis.yml | 65 +++++++++++++++-------------------------------------- 1 file changed, 18 insertions(+), 47 deletions(-) diff --git a/.travis.yml b/.travis.yml index 43f8ced19c1..28d934c6556 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ jobs: include: # This builder create and push the Docker images for all architectures - stage: build - if: type = push + if: type = push && tag ~= /^v[0-9]/ os: linux arch: amd64 dist: focal @@ -21,9 +21,25 @@ jobs: script: - go run build/ci.go dockerx -platform "linux/amd64,linux/arm64,linux/riscv64" -hub ethereum/client-go -upload + # This builder does the Ubuntu PPA nightly uploads + - stage: build + if: type = push && tag ~= /^v[0-9]/ + os: linux + dist: focal + go: 1.24.x + env: + - ubuntu-ppa + git: + submodules: false # avoid cloning ethereum/tests + before_install: + - sudo -E apt-get -yq --no-install-suggests --no-install-recommends install devscripts debhelper dput fakeroot + script: + - echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts + - go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder " + # This builder does the Linux Azure uploads - stage: build - if: type = push + if: type = push && tag ~= /^v[0-9]/ os: linux dist: focal sudo: required @@ -56,40 +72,6 @@ jobs: - go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - # These builders run the tests - - stage: build - if: type = push - os: linux - arch: amd64 - dist: focal - go: 1.24.x - script: - - travis_wait 45 go run build/ci.go test $TEST_PACKAGES - - - stage: build - if: type = push - os: linux - dist: focal - go: 1.23.x - script: - - travis_wait 45 go run build/ci.go test $TEST_PACKAGES - - # This builder does the Ubuntu PPA nightly uploads - - stage: build - if: type = cron || (type = push && tag ~= /^v[0-9]/) - os: linux - dist: focal - go: 1.24.x - env: - - ubuntu-ppa - git: - submodules: false # avoid cloning ethereum/tests - before_install: - - sudo -E apt-get -yq --no-install-suggests --no-install-recommends install devscripts debhelper dput fakeroot - script: - - echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts - - go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder " - # This builder does the Azure archive purges to avoid accumulating junk - stage: build if: type = cron @@ -102,14 +84,3 @@ jobs: submodules: false # avoid cloning ethereum/tests script: - go run build/ci.go purge -store gethstore/builds -days 14 - - # This builder executes race tests - - stage: build - if: type = cron - os: linux - dist: focal - go: 1.24.x - env: - - racetests - script: - - travis_wait 60 go run build/ci.go test -race $TEST_PACKAGES From 2a1784baef0e9d0c0641bc97d1a3478048de5c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Irmak?= Date: Thu, 29 May 2025 13:26:50 +0300 Subject: [PATCH 217/658] go.mod: bump flock package version (#31909) The newer version has a stub implementation for unsupported targets, which allows us to compile to more exotic targets. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d27af647ece..6c192c85d7d 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/fjl/gencodec v0.1.0 github.com/fsnotify/fsnotify v1.6.0 github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff - github.com/gofrs/flock v0.8.1 + github.com/gofrs/flock v0.12.1 github.com/golang-jwt/jwt/v4 v4.5.1 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb github.com/google/gofuzz v1.2.0 diff --git a/go.sum b/go.sum index 200b3725eaa..c91e3496565 100644 --- a/go.sum +++ b/go.sum @@ -144,8 +144,8 @@ github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyL github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= From 99c0ed11547a0ac1a4527c390cd04da9b9214368 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 2 Jun 2025 13:00:58 +0200 Subject: [PATCH 218/658] .gitea: modify scheduled workflow times (#31946) --- .gitea/workflows/release-azure-cleanup.yml | 2 +- .gitea/workflows/release-ppa.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/release-azure-cleanup.yml b/.gitea/workflows/release-azure-cleanup.yml index da2400ee268..311b2196bb4 100644 --- a/.gitea/workflows/release-azure-cleanup.yml +++ b/.gitea/workflows/release-azure-cleanup.yml @@ -1,6 +1,6 @@ on: schedule: - - cron: '0 0 * * *' + - cron: '0 14 * * *' workflow_dispatch: jobs: diff --git a/.gitea/workflows/release-ppa.yml b/.gitea/workflows/release-ppa.yml index 23bffe773d7..6c0631dc990 100644 --- a/.gitea/workflows/release-ppa.yml +++ b/.gitea/workflows/release-ppa.yml @@ -1,6 +1,6 @@ on: schedule: - - cron: '0 16 * * *' + - cron: '0 15 * * *' push: branches: - "release/*" From c87b856c1a7daff56b46be70cdb7092adc519b7c Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 2 Jun 2025 16:11:19 +0200 Subject: [PATCH 219/658] eth: return null for not-found in BlockByNumberOrHash (#31949) This changes the API backend to return null for not-found blocks. This behavior is required by the RPC When `BlockByNumberOrHash` always returned an error for this case ever since being added in https://github.com/ethereum/go-ethereum/pull/19491. The backend method has a couple of call sites, and all of them handle a `nil` block result because `BlockByNumber` returns `nil` for not-found. The only case where this makes a real difference is for `eth_getBlockReceipts`, which was changed in #31361 to actually forward the error from `BlockByNumberOrHash` to the caller. --- eth/api_backend.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/eth/api_backend.go b/eth/api_backend.go index 57f5a508374..8ec19308f93 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -196,7 +196,9 @@ func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash r if hash, ok := blockNrOrHash.Hash(); ok { header := b.eth.blockchain.GetHeaderByHash(hash) if header == nil { - return nil, errors.New("header for hash not found") + // Return 'null' and no error if block is not found. + // This behavior is required by RPC spec. + return nil, nil } if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash { return nil, errors.New("hash is not currently canonical") From a7d9b52eafd8242d6e015dea0903d78f717bf8b2 Mon Sep 17 00:00:00 2001 From: Sina M <1591639+s1na@users.noreply.github.com> Date: Tue, 3 Jun 2025 10:47:38 +0200 Subject: [PATCH 220/658] core/rawdb: integrate eradb backend for RPC (#31604) This implements a backing store for chain history based on era1 files. The new store is integrated with the freezer. Queries for blocks and receipts below the current freezer tail are handled by the era store. --------- Co-authored-by: Gary Rong Co-authored-by: Felix Lange Co-authored-by: lightclient --- cmd/geth/chaincmd.go | 8 +- cmd/utils/flags.go | 19 +- cmd/utils/history_test.go | 2 +- common/lru/basiclru.go | 17 +- core/blockchain_repair_test.go | 8 +- core/blockchain_sethead_test.go | 2 +- core/blockchain_snapshot_test.go | 4 +- core/blockchain_test.go | 22 +- core/rawdb/accessors_chain_test.go | 8 +- core/rawdb/ancienttest/testsuite.go | 21 -- core/rawdb/chain_freezer.go | 110 +++++- core/rawdb/database.go | 51 ++- core/rawdb/eradb/eradb.go | 345 ++++++++++++++++++ core/rawdb/eradb/eradb_test.go | 103 ++++++ .../testdata/sepolia-00000-643a00f7.era1 | Bin 0 -> 3267454 bytes .../testdata/sepolia-00021-b8814b14.era1 | Bin 0 -> 3733097 bytes core/rawdb/freezer.go | 14 +- core/rawdb/freezer_memory.go | 19 - core/rawdb/freezer_resettable.go | 9 - core/rawdb/freezer_table.go | 6 - core/rawdb/freezer_test.go | 6 - core/rawdb/table.go | 6 - core/txindexer_test.go | 6 +- eth/backend.go | 9 +- eth/downloader/downloader_test.go | 2 +- eth/ethconfig/config.go | 1 + eth/ethconfig/gen_config.go | 6 + ethdb/database.go | 4 - ethdb/remotedb/remotedb.go | 7 - internal/era/era.go | 56 ++- internal/era/era_test.go | 25 +- node/database.go | 70 ++-- node/node.go | 75 ++-- triedb/pathdb/database_test.go | 2 +- 34 files changed, 773 insertions(+), 270 deletions(-) create mode 100644 core/rawdb/eradb/eradb.go create mode 100644 core/rawdb/eradb/eradb_test.go create mode 100644 core/rawdb/eradb/testdata/sepolia-00000-643a00f7.era1 create mode 100644 core/rawdb/eradb/testdata/sepolia-00021-b8814b14.era1 diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index dc071725c11..d8488b82682 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -45,6 +45,7 @@ import ( "github.com/ethereum/go-ethereum/internal/era/eradl" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/urfave/cli/v2" ) @@ -277,10 +278,7 @@ func initGenesis(ctx *cli.Context) error { overrides.OverrideVerkle = &v } - chaindb, err := stack.OpenDatabaseWithFreezer("chaindata", 0, 0, ctx.String(utils.AncientFlag.Name), "", false) - if err != nil { - utils.Fatalf("Failed to open database: %v", err) - } + chaindb := utils.MakeChainDatabase(ctx, stack, false) defer chaindb.Close() triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle()) @@ -317,7 +315,7 @@ func dumpGenesis(ctx *cli.Context) error { // dump whatever already exists in the datadir stack, _ := makeConfigNode(ctx) - db, err := stack.OpenDatabase("chaindata", 0, 0, "", true) + db, err := stack.OpenDatabaseWithOptions("chaindata", node.DatabaseOptions{ReadOnly: true}) if err != nil { return err } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 60a2cc13f11..debec7278dc 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -111,6 +111,11 @@ var ( Usage: "Root directory for ancient data (default = inside chaindata)", Category: flags.EthCategory, } + EraFlag = &flags.DirectoryFlag{ + Name: "datadir.era", + Usage: "Root directory for era1 history (default = inside ancient/chain)", + Category: flags.EthCategory, + } MinFreeDiskSpaceFlag = &flags.DirectoryFlag{ Name: "datadir.minfreedisk", Usage: "Minimum free disk space in MB, once reached triggers auto shut down (default = --cache.gc converted to MB, 0 = disabled)", @@ -977,6 +982,7 @@ var ( DatabaseFlags = []cli.Flag{ DataDirFlag, AncientFlag, + EraFlag, RemoteDBFlag, DBEngineFlag, StateSchemeFlag, @@ -1613,6 +1619,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if ctx.IsSet(AncientFlag.Name) { cfg.DatabaseFreezer = ctx.String(AncientFlag.Name) } + if ctx.IsSet(EraFlag.Name) { + cfg.DatabaseEra = ctx.String(EraFlag.Name) + } if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" { Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name) @@ -2082,7 +2091,15 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb. } chainDb = remotedb.New(client) default: - chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", cache, handles, ctx.String(AncientFlag.Name), "eth/db/chaindata/", readonly) + options := node.DatabaseOptions{ + ReadOnly: readonly, + Cache: cache, + Handles: handles, + AncientsDirectory: ctx.String(AncientFlag.Name), + MetricsNamespace: "eth/db/chaindata/", + EraDirectory: ctx.String(EraFlag.Name), + } + chainDb, err = stack.OpenDatabaseWithOptions("chaindata", options) } if err != nil { Fatalf("Could not open database: %v", err) diff --git a/cmd/utils/history_test.go b/cmd/utils/history_test.go index d3c6bda1c50..be51803f8c1 100644 --- a/cmd/utils/history_test.go +++ b/cmd/utils/history_test.go @@ -158,7 +158,7 @@ func TestHistoryImportAndExport(t *testing.T) { } // Now import Era. - db2, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + db2, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{}) if err != nil { panic(err) } diff --git a/common/lru/basiclru.go b/common/lru/basiclru.go index 7386c77840a..154831a7960 100644 --- a/common/lru/basiclru.go +++ b/common/lru/basiclru.go @@ -47,30 +47,37 @@ func NewBasicLRU[K comparable, V any](capacity int) BasicLRU[K, V] { // Add adds a value to the cache. Returns true if an item was evicted to store the new item. func (c *BasicLRU[K, V]) Add(key K, value V) (evicted bool) { + _, _, evicted = c.Add3(key, value) + return evicted +} + +// Add3 adds a value to the cache. If an item was evicted to store the new one, it returns the evicted item. +func (c *BasicLRU[K, V]) Add3(key K, value V) (ek K, ev V, evicted bool) { item, ok := c.items[key] if ok { - // Already exists in cache. item.value = value c.items[key] = item c.list.moveToFront(item.elem) - return false + return ek, ev, false } var elem *listElem[K] if c.Len() >= c.cap { elem = c.list.removeLast() - delete(c.items, elem.v) evicted = true + ek = elem.v + ev = c.items[ek].value + delete(c.items, ek) } else { elem = new(listElem[K]) } // Store the new item. - // Note that, if another item was evicted, we re-use its list element here. + // Note that if another item was evicted, we re-use its list element here. elem.v = key c.items[key] = cacheItem[K, V]{elem, value} c.list.pushElem(elem) - return evicted + return ek, ev, evicted } // Contains reports whether the given key exists in the cache. diff --git a/core/blockchain_repair_test.go b/core/blockchain_repair_test.go index edc8854892f..9661cee4c7b 100644 --- a/core/blockchain_repair_test.go +++ b/core/blockchain_repair_test.go @@ -1769,7 +1769,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } - db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + db, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient}) if err != nil { t.Fatalf("Failed to create persistent freezer database: %v", err) } @@ -1854,7 +1854,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s if err != nil { t.Fatalf("Failed to reopen persistent key-value database: %v", err) } - db, err = rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + db, err = rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient}) if err != nil { t.Fatalf("Failed to reopen persistent freezer database: %v", err) } @@ -1919,7 +1919,7 @@ func testIssue23496(t *testing.T, scheme string) { if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } - db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + db, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient}) if err != nil { t.Fatalf("Failed to create persistent freezer database: %v", err) } @@ -1979,7 +1979,7 @@ func testIssue23496(t *testing.T, scheme string) { if err != nil { t.Fatalf("Failed to reopen persistent key-value database: %v", err) } - db, err = rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + db, err = rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient}) if err != nil { t.Fatalf("Failed to reopen persistent freezer database: %v", err) } diff --git a/core/blockchain_sethead_test.go b/core/blockchain_sethead_test.go index 51e2a5275fe..b094ed3b650 100644 --- a/core/blockchain_sethead_test.go +++ b/core/blockchain_sethead_test.go @@ -1973,7 +1973,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } - db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + db, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient}) if err != nil { t.Fatalf("Failed to create persistent freezer database: %v", err) } diff --git a/core/blockchain_snapshot_test.go b/core/blockchain_snapshot_test.go index 23640fe8433..e12a0c67c4e 100644 --- a/core/blockchain_snapshot_test.go +++ b/core/blockchain_snapshot_test.go @@ -70,7 +70,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } - db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + db, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient}) if err != nil { t.Fatalf("Failed to create persistent freezer database: %v", err) } @@ -265,7 +265,7 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) { if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } - newdb, err := rawdb.NewDatabaseWithFreezer(pdb, snaptest.ancient, "", false) + newdb, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: snaptest.ancient}) if err != nil { t.Fatalf("Failed to create persistent freezer database: %v", err) } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 434b494490e..2401402f321 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -738,7 +738,7 @@ func testFastVsFullChains(t *testing.T, scheme string) { t.Fatalf("failed to insert receipt %d: %v", n, err) } // Freezer style fast import the chain. - ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + ancientDb, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{}) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -824,7 +824,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) { // makeDb creates a db instance for testing. makeDb := func() ethdb.Database { - db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + db, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{}) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -1623,7 +1623,7 @@ func testLargeReorgTrieGC(t *testing.T, scheme string) { competitor, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*state.TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) }) // Import the shared chain and the original canonical one - db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{}) defer db.Close() chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil) @@ -1689,7 +1689,7 @@ func testBlockchainRecovery(t *testing.T, scheme string) { _, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), int(height), nil) // Import the chain as a ancient-first node and ensure all pointers are updated - ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false) + ancientDb, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{Ancient: t.TempDir()}) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -1747,7 +1747,7 @@ func testLowDiffLongChain(t *testing.T, scheme string) { }) // Import the canonical chain - diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + diskdb, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{}) defer diskdb.Close() chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil) @@ -1959,7 +1959,7 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) { b.OffsetTime(-9) // A higher difficulty }) // Import the shared chain and the original canonical one - chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + chaindb, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{}) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -2122,7 +2122,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i } }) // Import the shared chain and the original canonical one - chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + chaindb, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{}) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -2496,7 +2496,7 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) { if err != nil { t.Fatalf("Failed to create persistent key-value database: %v", err) } - db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + db, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient}) if err != nil { t.Fatalf("Failed to create persistent freezer database: %v", err) } @@ -3403,7 +3403,7 @@ func testSetCanonical(t *testing.T, scheme string) { } gen.AddTx(tx) }) - diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + diskdb, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{}) defer diskdb.Close() chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil) @@ -4199,7 +4199,7 @@ func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) { gen.SetCoinbase(common.Address{0: byte(0xb), 19: byte(i)}) }) - db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{}) defer db.Close() chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) @@ -4315,7 +4315,7 @@ func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64, config := DefaultCacheConfigWithScheme(rawdb.PathScheme) config.ChainHistoryMode = history.KeepPostMerge - db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false) + db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{}) defer db.Close() chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), genesis, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) defer chain.Stop() diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index d98fc9a1eb3..862b8cd2fd0 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -416,7 +416,7 @@ func checkReceiptsRLP(have, want types.Receipts) error { func TestAncientStorage(t *testing.T) { // Freezer style fast import the chain. frdir := t.TempDir() - db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false) + db, err := Open(NewMemoryDatabase(), OpenOptions{Ancient: frdir}) if err != nil { t.Fatalf("failed to create database with ancient backend") } @@ -469,7 +469,7 @@ func TestAncientStorage(t *testing.T) { } func TestWriteAncientHeaderChain(t *testing.T) { - db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), t.TempDir(), "", false) + db, err := Open(NewMemoryDatabase(), OpenOptions{Ancient: t.TempDir()}) if err != nil { t.Fatalf("failed to create database with ancient backend") } @@ -586,7 +586,7 @@ func TestHashesInRange(t *testing.T) { func BenchmarkWriteAncientBlocks(b *testing.B) { // Open freezer database. frdir := b.TempDir() - db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false) + db, err := Open(NewMemoryDatabase(), OpenOptions{Ancient: frdir}) if err != nil { b.Fatalf("failed to create database with ancient backend") } @@ -890,7 +890,7 @@ func TestHeadersRLPStorage(t *testing.T) { // Have N headers in the freezer frdir := t.TempDir() - db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false) + db, err := Open(NewMemoryDatabase(), OpenOptions{Ancient: frdir}) if err != nil { t.Fatalf("failed to create database with ancient backend") } diff --git a/core/rawdb/ancienttest/testsuite.go b/core/rawdb/ancienttest/testsuite.go index 70de263c043..e33e7689473 100644 --- a/core/rawdb/ancienttest/testsuite.go +++ b/core/rawdb/ancienttest/testsuite.go @@ -77,13 +77,6 @@ func basicRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { } for _, c := range cases { for i := c.start; i < c.limit; i++ { - exist, err := db.HasAncient("a", uint64(i)) - if err != nil { - t.Fatalf("Failed to check presence, %v", err) - } - if exist { - t.Fatalf("Item %d is already truncated", uint64(i)) - } _, err = db.Ancient("a", uint64(i)) if err == nil { t.Fatal("Error is expected for non-existent item") @@ -93,13 +86,6 @@ func basicRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { // Test the items in range should be reachable for i := 10; i < 90; i++ { - exist, err := db.HasAncient("a", uint64(i)) - if err != nil { - t.Fatalf("Failed to check presence, %v", err) - } - if !exist { - t.Fatalf("Item %d is missing", uint64(i)) - } blob, err := db.Ancient("a", uint64(i)) if err != nil { t.Fatalf("Failed to retrieve item, %v", err) @@ -110,13 +96,6 @@ func basicRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { } // Test the items in unknown table shouldn't be reachable - exist, err := db.HasAncient("b", uint64(0)) - if err != nil { - t.Fatalf("Failed to check presence, %v", err) - } - if exist { - t.Fatal("Item in unknown table shouldn't be found") - } _, err = db.Ancient("b", uint64(0)) if err == nil { t.Fatal("Error is expected for unknown table") diff --git a/core/rawdb/chain_freezer.go b/core/rawdb/chain_freezer.go index cc7a62df329..3a5218c0237 100644 --- a/core/rawdb/chain_freezer.go +++ b/core/rawdb/chain_freezer.go @@ -23,6 +23,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb/eradb" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -43,7 +44,10 @@ const ( // feature. The background thread will keep moving ancient chain segments from // key-value database to flat files for saving space on live database. type chainFreezer struct { - ethdb.AncientStore // Ancient store for storing cold chain segment + ancients ethdb.AncientStore // Ancient store for storing cold chain segment + + // Optional Era database used as a backup for the pruned chain. + eradb *eradb.Store quit chan struct{} wg sync.WaitGroup @@ -56,23 +60,27 @@ type chainFreezer struct { // state freezer (e.g. dev mode). // - if non-empty directory is given, initializes the regular file-based // state freezer. -func newChainFreezer(datadir string, namespace string, readonly bool) (*chainFreezer, error) { - var ( - err error - freezer ethdb.AncientStore - ) +func newChainFreezer(datadir string, eraDir string, namespace string, readonly bool) (*chainFreezer, error) { if datadir == "" { - freezer = NewMemoryFreezer(readonly, chainFreezerTableConfigs) - } else { - freezer, err = NewFreezer(datadir, namespace, readonly, freezerTableSize, chainFreezerTableConfigs) + return &chainFreezer{ + ancients: NewMemoryFreezer(readonly, chainFreezerTableConfigs), + quit: make(chan struct{}), + trigger: make(chan chan struct{}), + }, nil + } + freezer, err := NewFreezer(datadir, namespace, readonly, freezerTableSize, chainFreezerTableConfigs) + if err != nil { + return nil, err } + edb, err := eradb.New(resolveChainEraDir(datadir, eraDir)) if err != nil { return nil, err } return &chainFreezer{ - AncientStore: freezer, - quit: make(chan struct{}), - trigger: make(chan chan struct{}), + ancients: freezer, + eradb: edb, + quit: make(chan struct{}), + trigger: make(chan chan struct{}), }, nil } @@ -84,7 +92,11 @@ func (f *chainFreezer) Close() error { close(f.quit) } f.wg.Wait() - return f.AncientStore.Close() + + if f.eradb != nil { + f.eradb.Close() + } + return f.ancients.Close() } // readHeadNumber returns the number of chain head block. 0 is returned if the @@ -334,3 +346,75 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash }) return hashes, err } + +// Ancient retrieves an ancient binary blob from the append-only immutable files. +func (f *chainFreezer) Ancient(kind string, number uint64) ([]byte, error) { + // Lookup the entry in the underlying ancient store, assuming that + // headers and hashes are always available. + if kind == ChainFreezerHeaderTable || kind == ChainFreezerHashTable { + return f.ancients.Ancient(kind, number) + } + tail, err := f.ancients.Tail() + if err != nil { + return nil, err + } + // Lookup the entry in the underlying ancient store if it's not pruned + if number >= tail { + return f.ancients.Ancient(kind, number) + } + // Lookup the entry in the optional era backend + if f.eradb == nil { + return nil, errOutOfBounds + } + switch kind { + case ChainFreezerBodiesTable: + return f.eradb.GetRawBody(number) + case ChainFreezerReceiptTable: + return f.eradb.GetRawReceipts(number) + } + return nil, errUnknownTable +} + +// ReadAncients executes an operation while preventing mutations to the freezer, +// i.e. if fn performs multiple reads, they will be consistent with each other. +func (f *chainFreezer) ReadAncients(fn func(ethdb.AncientReaderOp) error) (err error) { + if store, ok := f.ancients.(*Freezer); ok { + store.writeLock.Lock() + defer store.writeLock.Unlock() + } + return fn(f) +} + +// Methods below are just pass-through to the underlying ancient store. + +func (f *chainFreezer) Ancients() (uint64, error) { + return f.ancients.Ancients() +} + +func (f *chainFreezer) Tail() (uint64, error) { + return f.ancients.Tail() +} + +func (f *chainFreezer) AncientSize(kind string) (uint64, error) { + return f.ancients.AncientSize(kind) +} + +func (f *chainFreezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) { + return f.ancients.AncientRange(kind, start, count, maxBytes) +} + +func (f *chainFreezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (int64, error) { + return f.ancients.ModifyAncients(fn) +} + +func (f *chainFreezer) TruncateHead(items uint64) (uint64, error) { + return f.ancients.TruncateHead(items) +} + +func (f *chainFreezer) TruncateTail(items uint64) (uint64, error) { + return f.ancients.TruncateTail(items) +} + +func (f *chainFreezer) SyncAncient() error { + return f.ancients.SyncAncient() +} diff --git a/core/rawdb/database.go b/core/rawdb/database.go index a03dbafb1f4..86f4ac19cbc 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -86,11 +86,6 @@ type nofreezedb struct { ethdb.KeyValueStore } -// HasAncient returns an error as we don't have a backing chain freezer. -func (db *nofreezedb) HasAncient(kind string, number uint64) (bool, error) { - return false, errNotSupported -} - // Ancient returns an error as we don't have a backing chain freezer. func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) { return nil, errNotSupported @@ -186,19 +181,49 @@ func resolveChainFreezerDir(ancient string) string { return freezer } -// NewDatabaseWithFreezer creates a high level database on top of a given key- -// value data store with a freezer moving immutable chain segments into cold -// storage. The passed ancient indicates the path of root ancient directory -// where the chain freezer can be opened. +// resolveChainEraDir is a helper function which resolves the absolute path of era database. +func resolveChainEraDir(chainFreezerDir string, era string) string { + switch { + case era == "": + return filepath.Join(chainFreezerDir, "era") + case !filepath.IsAbs(era): + return filepath.Join(chainFreezerDir, era) + default: + return era + } +} + +// NewDatabaseWithFreezer creates a high level database on top of a given key-value store. +// The passed ancient indicates the path of root ancient directory where the chain freezer +// can be opened. +// +// Deprecated: use Open. func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace string, readonly bool) (ethdb.Database, error) { + return Open(db, OpenOptions{ + Ancient: ancient, + MetricsNamespace: namespace, + ReadOnly: readonly, + }) +} + +// OpenOptions specifies options for opening the database. +type OpenOptions struct { + Ancient string // ancients directory + Era string // era files directory + MetricsNamespace string // prefix added to freezer metric names + ReadOnly bool +} + +// Open creates a high-level database wrapper for the given key-value store. +func Open(db ethdb.KeyValueStore, opts OpenOptions) (ethdb.Database, error) { // Create the idle freezer instance. If the given ancient directory is empty, // in-memory chain freezer is used (e.g. dev mode); otherwise the regular // file-based freezer is created. - chainFreezerDir := ancient + chainFreezerDir := opts.Ancient if chainFreezerDir != "" { chainFreezerDir = resolveChainFreezerDir(chainFreezerDir) } - frdb, err := newChainFreezer(chainFreezerDir, namespace, readonly) + frdb, err := newChainFreezer(chainFreezerDir, opts.Era, opts.MetricsNamespace, opts.ReadOnly) if err != nil { printChainMetadata(db) return nil, err @@ -282,7 +307,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st } } // Freezer is consistent with the key-value database, permit combining the two - if !readonly { + if !opts.ReadOnly { frdb.wg.Add(1) go func() { frdb.freeze(db) @@ -290,7 +315,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st }() } return &freezerdb{ - ancientRoot: ancient, + ancientRoot: opts.Ancient, KeyValueStore: db, chainFreezer: frdb, }, nil diff --git a/core/rawdb/eradb/eradb.go b/core/rawdb/eradb/eradb.go new file mode 100644 index 00000000000..29e658798e9 --- /dev/null +++ b/core/rawdb/eradb/eradb.go @@ -0,0 +1,345 @@ +// 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 eradb implements a history backend using era1 files. +package eradb + +import ( + "bytes" + "errors" + "fmt" + "io/fs" + "path/filepath" + "sync" + + "github.com/ethereum/go-ethereum/common/lru" + "github.com/ethereum/go-ethereum/internal/era" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" +) + +const openFileLimit = 64 + +var errClosed = errors.New("era store is closed") + +// Store manages read access to a directory of era1 files. +// The getter methods are thread-safe. +type Store struct { + datadir string + + // The mutex protects all remaining fields. + mu sync.Mutex + cond *sync.Cond + lru lru.BasicLRU[uint64, *fileCacheEntry] + opening map[uint64]*fileCacheEntry + closing bool +} + +type fileCacheEntry struct { + refcount int // reference count. This is protected by Store.mu! + opened chan struct{} // signals opening of file has completed + file *era.Era // the file + err error // error from opening the file +} + +type fileCacheStatus byte + +const ( + storeClosing fileCacheStatus = iota + fileIsNew + fileIsOpening + fileIsCached +) + +// New opens the store directory. +func New(datadir string) (*Store, error) { + db := &Store{ + datadir: datadir, + lru: lru.NewBasicLRU[uint64, *fileCacheEntry](openFileLimit), + opening: make(map[uint64]*fileCacheEntry), + } + db.cond = sync.NewCond(&db.mu) + log.Info("Opened Era store", "datadir", datadir) + return db, nil +} + +// Close closes all open era1 files in the cache. +func (db *Store) Close() { + db.mu.Lock() + defer db.mu.Unlock() + + // Prevent new cache additions. + db.closing = true + + // Deref all active files. Since inactive files have a refcount of one, they will be + // closed right here and now after decrementing. Files which are currently being used + // have a refcount > 1 and will hit zero when their access finishes. + for _, epoch := range db.lru.Keys() { + entry, _ := db.lru.Peek(epoch) + if entry.derefAndClose(epoch) { + db.lru.Remove(epoch) + } + } + + // Wait for all store access to finish. + for db.lru.Len() > 0 || len(db.opening) > 0 { + db.cond.Wait() + } +} + +// GetRawBody returns the raw body for a given block number. +func (db *Store) GetRawBody(number uint64) ([]byte, error) { + epoch := number / uint64(era.MaxEra1Size) + entry := db.getEraByEpoch(epoch) + if entry.err != nil { + if errors.Is(entry.err, fs.ErrNotExist) { + return nil, nil + } + return nil, entry.err + } + defer db.doneWithFile(epoch, entry) + + return entry.file.GetRawBodyByNumber(number) +} + +// GetRawReceipts returns the raw receipts for a given block number. +func (db *Store) GetRawReceipts(number uint64) ([]byte, error) { + epoch := number / uint64(era.MaxEra1Size) + entry := db.getEraByEpoch(epoch) + if entry.err != nil { + if errors.Is(entry.err, fs.ErrNotExist) { + return nil, nil + } + return nil, entry.err + } + defer db.doneWithFile(epoch, entry) + + data, err := entry.file.GetRawReceiptsByNumber(number) + if err != nil { + return nil, err + } + return convertReceipts(data) +} + +// convertReceipts transforms an encoded block receipts list from the format +// used by era1 into the 'storage' format used by the go-ethereum ancients database. +func convertReceipts(input []byte) ([]byte, error) { + var ( + out bytes.Buffer + enc = rlp.NewEncoderBuffer(&out) + ) + blockListIter, err := rlp.NewListIterator(input) + if err != nil { + return nil, fmt.Errorf("invalid block receipts list: %v", err) + } + outerList := enc.List() + for i := 0; blockListIter.Next(); i++ { + kind, content, _, err := rlp.Split(blockListIter.Value()) + if err != nil { + return nil, fmt.Errorf("receipt %d invalid: %v", i, err) + } + var receiptData []byte + switch kind { + case rlp.Byte: + return nil, fmt.Errorf("receipt %d is single byte", i) + case rlp.String: + // Typed receipt - skip type. + receiptData = content[1:] + case rlp.List: + // Legacy receipt + receiptData = blockListIter.Value() + } + // Convert data list. + // Input is [status, gas-used, bloom, logs] + // Output is [status, gas-used, logs], i.e. we need to skip the bloom. + dataIter, err := rlp.NewListIterator(receiptData) + if err != nil { + return nil, fmt.Errorf("receipt %d has invalid data: %v", i, err) + } + innerList := enc.List() + for field := 0; dataIter.Next(); field++ { + if field == 2 { + continue // skip bloom + } + enc.Write(dataIter.Value()) + } + enc.ListEnd(innerList) + if dataIter.Err() != nil { + return nil, fmt.Errorf("receipt %d iterator error: %v", i, dataIter.Err()) + } + } + enc.ListEnd(outerList) + if blockListIter.Err() != nil { + return nil, fmt.Errorf("block receipt list iterator error: %v", blockListIter.Err()) + } + enc.Flush() + return out.Bytes(), nil +} + +// getEraByEpoch opens an era file or gets it from the cache. +// The caller can freely access the returned entry's .file and .err +// db.doneWithFile must be called when it is done reading the file. +func (db *Store) getEraByEpoch(epoch uint64) *fileCacheEntry { + stat, entry := db.getCacheEntry(epoch) + + switch stat { + case storeClosing: + return &fileCacheEntry{err: errClosed} + + case fileIsNew: + // Open the file and put it into the cache. + e, err := db.openEraFile(epoch) + if err != nil { + db.fileFailedToOpen(epoch, entry, err) + } else { + db.fileOpened(epoch, entry, e) + } + close(entry.opened) + + case fileIsOpening: + // Wait for open to finish. + <-entry.opened + + case fileIsCached: + // Nothing to do. + + default: + panic(fmt.Sprintf("invalid file state %d", stat)) + } + return entry +} + +// getCacheEntry gets an open era file from the cache. +func (db *Store) getCacheEntry(epoch uint64) (stat fileCacheStatus, entry *fileCacheEntry) { + db.mu.Lock() + defer db.mu.Unlock() + + if db.closing { + return storeClosing, nil + } + if entry = db.opening[epoch]; entry != nil { + stat = fileIsOpening + } else if entry, _ = db.lru.Get(epoch); entry != nil { + stat = fileIsCached + } else { + // It's a new file, create an entry in the opening table. Note the entry is + // created with an initial refcount of one. We increment the count once more + // before returning, but the count will return to one when the file has been + // accessed. When the store is closed or the file gets evicted from the cache, + // refcount will be decreased by one, thus allowing it to hit zero. + entry = &fileCacheEntry{refcount: 1, opened: make(chan struct{})} + db.opening[epoch] = entry + stat = fileIsNew + } + entry.refcount++ + return stat, entry +} + +// fileOpened is called after an era file has been successfully opened. +func (db *Store) fileOpened(epoch uint64, entry *fileCacheEntry, file *era.Era) { + db.mu.Lock() + defer db.mu.Unlock() + + delete(db.opening, epoch) + db.cond.Signal() // db.opening was modified + + // The database may have been closed while opening the file. When that happens, we + // need to close the file here, since it isn't tracked by the LRU yet. + if db.closing { + entry.err = errClosed + file.Close() + return + } + + // Add it to the LRU. This may evict an existing item, which we have to close. + entry.file = file + evictedEpoch, evictedEntry, _ := db.lru.Add3(epoch, entry) + if evictedEntry != nil { + evictedEntry.derefAndClose(evictedEpoch) + } +} + +// fileFailedToOpen is called when an era file could not be opened. +func (db *Store) fileFailedToOpen(epoch uint64, entry *fileCacheEntry, err error) { + db.mu.Lock() + defer db.mu.Unlock() + + delete(db.opening, epoch) + db.cond.Signal() // db.opening was modified + entry.err = err +} + +func (db *Store) openEraFile(epoch uint64) (*era.Era, error) { + // File name scheme is --. + glob := fmt.Sprintf("*-%05d-*.era1", epoch) + matches, err := filepath.Glob(filepath.Join(db.datadir, glob)) + if err != nil { + return nil, err + } + if len(matches) > 1 { + return nil, fmt.Errorf("multiple era1 files found for epoch %d", epoch) + } + if len(matches) == 0 { + return nil, fs.ErrNotExist + } + filename := matches[0] + + e, err := era.Open(filename) + if err != nil { + return nil, err + } + // Sanity-check start block. + if e.Start()%uint64(era.MaxEra1Size) != 0 { + return nil, fmt.Errorf("pre-merge era1 file has invalid boundary. %d %% %d != 0", e.Start(), era.MaxEra1Size) + } + log.Debug("Opened era1 file", "epoch", epoch) + return e, nil +} + +// doneWithFile signals that the caller has finished using a file. +// This decrements the refcount and ensures the file is closed by the last user. +func (db *Store) doneWithFile(epoch uint64, entry *fileCacheEntry) { + db.mu.Lock() + defer db.mu.Unlock() + + if entry.err != nil { + return + } + if entry.derefAndClose(epoch) { + // Delete closed entry from LRU if it is still present. + if e, _ := db.lru.Peek(epoch); e == entry { + db.lru.Remove(epoch) + db.cond.Signal() // db.lru was modified + } + } +} + +// derefAndClose decrements the reference counter and closes the file +// when it hits zero. +func (entry *fileCacheEntry) derefAndClose(epoch uint64) (closed bool) { + entry.refcount-- + if entry.refcount > 0 { + return false + } + + closeErr := entry.file.Close() + if closeErr == nil { + log.Debug("Closed era1 file", "epoch", epoch) + } else { + log.Warn("Error closing era1 file", "epoch", epoch, "err", closeErr) + } + return true +} diff --git a/core/rawdb/eradb/eradb_test.go b/core/rawdb/eradb/eradb_test.go new file mode 100644 index 00000000000..41047dbbe99 --- /dev/null +++ b/core/rawdb/eradb/eradb_test.go @@ -0,0 +1,103 @@ +// 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 eradb + +import ( + "sync" + "testing" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEraDatabase(t *testing.T) { + db, err := New("testdata") + require.NoError(t, err) + defer db.Close() + + r, err := db.GetRawBody(175881) + require.NoError(t, err) + var body *types.Body + err = rlp.DecodeBytes(r, &body) + require.NoError(t, err) + require.NotNil(t, body, "block body not found") + assert.Equal(t, 3, len(body.Transactions)) + + r, err = db.GetRawReceipts(175881) + require.NoError(t, err) + var receipts []*types.ReceiptForStorage + err = rlp.DecodeBytes(r, &receipts) + require.NoError(t, err) + require.NotNil(t, receipts, "receipts not found") + assert.Equal(t, 3, len(receipts), "receipts length mismatch") +} + +func TestEraDatabaseConcurrentOpen(t *testing.T) { + db, err := New("testdata") + require.NoError(t, err) + defer db.Close() + + const N = 25 + var wg sync.WaitGroup + wg.Add(N) + for range N { + go func() { + defer wg.Done() + r, err := db.GetRawBody(1024) + if err != nil { + t.Error("err:", err) + } + if len(r) == 0 { + t.Error("empty body") + } + }() + } + wg.Wait() +} + +func TestEraDatabaseConcurrentOpenClose(t *testing.T) { + db, err := New("testdata") + require.NoError(t, err) + defer db.Close() + + const N = 10 + var wg sync.WaitGroup + wg.Add(N) + for range N { + go func() { + defer wg.Done() + r, err := db.GetRawBody(1024) + if err == errClosed { + return + } + if err != nil { + t.Error("err:", err) + } + if len(r) == 0 { + t.Error("empty body") + } + }() + } + wg.Add(1) + go func() { + defer wg.Done() + db.Close() + }() + wg.Wait() +} diff --git a/core/rawdb/eradb/testdata/sepolia-00000-643a00f7.era1 b/core/rawdb/eradb/testdata/sepolia-00000-643a00f7.era1 new file mode 100644 index 0000000000000000000000000000000000000000..a601a40c23b4f0030d15a4a19684aea0ddb61864 GIT binary patch literal 3267454 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%0Arwi(V1i0j85)F71PNS`TohfrJYD>zLuPp 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�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 z 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�GPWRO3V}J86 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�?3J+T7Tchu`-vK+Zdck8 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
    EXj1pd*pkeUP0gNEq2am=X)n7{+^Vu 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

    AuvRvo9O$WQ;n z${5d2o)f5-3@(i*a34y3hCTooS^`J@4r?IPUK;cxof-0b@M!$uc3nx!%c&2sN4FHU z?1aL7<^nzeWYELJb$x7`id?@sFg>cf!eZB7(@VAC?%*Lv)ua&>Q-=67q+vin{xAkI zFf>>AyC!RCO3g28vynGbz5Y85i)1Ny0wO(u2L@E($nlp9cC}V;Gelmm2EgzQJ5c{B z4eH|ibT`K2ygjMyR66=qkYkkd&5tx5Ww`JPQ`8QcvH)bb8={k(sGc4=Y&QqSZC?`W8Ln8454f?*+mb zD8NwkO`!)GBl%_|ie&yV%+tM(`CXPg!VlGXzldr^65;$gl+?@l;7$Xh0ZZn;m*_Eb ziYHeLUzoN^og*n2HihllRA{vd^JSpn_we7mWULR6%C{B!3@{BK=D-<-`OwA6kF-DP zkk3d5TAv+h$xZ~JZnk=8p&Ah)o}v6=)?N^dff5Ww?JW%AY0Z={FlT%4`0mnb%$}>X zdklkiK&<|3p{s? zE4kx^g;YYbi46ox0Wz@Yesnr;({1EW3ExJ|A=(QkH8)nhWo&2SUYDCBktBe4hMH#( zP%w;v3Je`2fA`4v`?PdrmH?812LqV-aUCNC%i=fElNHg=#&h|Xhf#q0owIIOJmxSROLi|rz`6bi&MG+$gZ34t+CgP~cu??j;j_1}{399Y?&RemG1 z#Azz+>VIm0OFJp9ZTF`{+w9T7%^;J|1AxJ1VSVz7fqPu}uJI3!M#4t)P1oJkPMbm60{|HakBrfY&5pN82IuYG)_e^?$`m2Q3874GefIf|4x0`H@eEx! z5Kt(Lfd&kX!5VXNPEI(_kDD}P0-Gk_OzGjK^M+O9pI4}$`P8vorokN}18xR&Q`D?~ zuML$0r8HLzDmn~$QRRzE zJoKi6#pOxZPfsM>W(h*+>>55jnq?BiGYnkJ=njK1(1M|loKEnv#U9FKzs0I)o&AE6 zK&9GSfJ4}~gOR@F_(kW>O;Fth_!%AwJOIG3fJN?q#o%8U+lDHwtzY5fH?5JJ|Nhwk zxB}HNPww{gl$7iwod-Y$&FQziGGnKKGlbHCGQtt*d(?uQYxixs4bO}{_&>a)LOjFR z#ZAy~7y}&`nrPwfa!cVlv&aqs(Y!Ix>Uw_t-i4M+qI z02t~GZSb!criGfXE#KoKwodN8yR-D`$x|5c#qy8ND?M#WTC zDHKCHN~&3$s#S8rS%&bELA}ffZieLTYyb>r-x9^H800=G+uTj=N>rsd!e#6kD{gkT z{4{u1njA7AjsM%&jsRd963q15nf;1C%>}D4>CvnHAi=$-P%9DQdp89we<0)ZKHzKdz_-*`7HXLoOM#klf*BAfA{8 zzz_`<{&B_dS%rpGnna0xcbcAMDbV|r;}Jxfv%mZ>s&ISywPt_=Kn9dIAvWAX`85ab zBUGWxk%rlv_BYk6FahG2; z4sFc-WWuRMI$`6wy7^~jhOR&SPgpJ6Z-4yv5^X_d{CBLC`OV4V5|j~YrTyv7)+3N+ zNE+|k{zx^$HSm!+4&y}&1x1(q;#8~Nn_TZwv$C!s&>+4PSyX@{!Nv#iQ_-PFnJ7*| zO=g*yh}6{qObHC>tpT?)ng>vC^zJ0Np_ijWea`O4kvD!#nkKbZAZ29UB7R2^>7V}& z@eI57AfRX%0}~i(PQZBm`Rw4=>&d@S_U3-=P4;v(7Uhp0;ZB8#B=235xlDt>L-;38 zCf6tcU@#lA*Skss{}OSGh?Q%R=!YgD#Ww=#b>@xo@r(4s!Qb8JdtS1%0A!$Ib44-d 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

    X}EWQWZdpyA=*F$-L8@iWsitB_jTy7;eVrt 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