diff --git a/.github/workflows/ci_zkevm.yml b/.github/workflows/ci_zkevm.yml index 2f4e5b11991..c152e14473c 100644 --- a/.github/workflows/ci_zkevm.yml +++ b/.github/workflows/ci_zkevm.yml @@ -21,9 +21,9 @@ jobs: tests: strategy: matrix: - os: [ ubuntu-20.04, macos-13 ] # list of os: https://github.com/actions/virtual-environments + os: [ ubuntu-20.04, macos-13-xlarge ] # list of os: https://github.com/actions/virtual-environments runs-on: ${{ matrix.os }} - timeout-minutes: ${{ matrix.os == 'macos-14' && 40 || 30 }} + timeout-minutes: ${{ matrix.os == 'macos-14-xlarge' && 40 || 30 }} steps: - uses: actions/checkout@v3 @@ -55,13 +55,19 @@ jobs: - name: Test run: make test + + - name: SonarCloud Scan + uses: SonarSource/sonarcloud-github-action@master + if: ${{ matrix.os == 'ubuntu-20.04' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} kurtosis-cdk: runs-on: ubuntu-latest - # TODO: Add "cdk-validium" once it's ready - # strategy: - # matrix: - # da-mode: [ "rollup" ] + strategy: + matrix: + da-mode: [ "rollup", "cdk-validium" ] steps: - name: Checkout cdk-erigon uses: actions/checkout@v4 @@ -100,9 +106,8 @@ jobs: - name: Configure Kurtosis CDK working-directory: ./kurtosis-cdk run: | - /usr/local/bin/yq -i '.args.data_availability_mode = "rollup"' params.yml + /usr/local/bin/yq -i '.args.data_availability_mode = "${{ matrix.da-mode }}"' params.yml /usr/local/bin/yq -i '.args.cdk_erigon_node_image = "cdk-erigon:local"' params.yml - /usr/local/bin/yq -i '.args.zkevm_bridge_service_image = "hermeznetwork/zkevm-bridge-service:v0.5.0-RC8"' params.yml - name: Deploy Kurtosis CDK package working-directory: ./kurtosis-cdk diff --git a/Dockerfile b/Dockerfile index 6888bf0ea23..586676198cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -82,7 +82,7 @@ COPY --from=builder /app/build/bin/sentry /usr/local/bin/sentry COPY --from=builder /app/build/bin/state /usr/local/bin/state COPY --from=builder /app/build/bin/txpool /usr/local/bin/txpool COPY --from=builder /app/build/bin/verkle /usr/local/bin/verkle - +COPY --from=builder /app/build/bin/acl /usr/local/bin/acl EXPOSE 8545 \ diff --git a/Dockerfile.debian b/Dockerfile.debian index 0128f799b01..5cc417a556c 100644 --- a/Dockerfile.debian +++ b/Dockerfile.debian @@ -74,6 +74,7 @@ COPY --from=builder /app/build/bin/sentry /usr/local/bin/sentry COPY --from=builder /app/build/bin/state /usr/local/bin/state COPY --from=builder /app/build/bin/txpool /usr/local/bin/txpool COPY --from=builder /app/build/bin/verkle /usr/local/bin/verkle +COPY --from=builder /app/build/bin/acl /usr/local/bin/acl COPY --from=builder /go/pkg/mod /go/pkg/mod diff --git a/Makefile b/Makefile index 77278244101..cc82fd8a549 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ GO_FLAGS += -ldflags "-X ${PACKAGE}/params.GitCommit=${GIT_COMMIT} -X ${PACKAGE} GOBUILD = $(CGO_CFLAGS) $(GO) build $(GO_FLAGS) GO_DBG_BUILD = $(GO) build $(GO_FLAGS) -tags $(BUILD_TAGS),debug -gcflags=all="-N -l" # see delve docs -GOTEST = $(CGO_CFLAGS) GODEBUG=cgocheck=0 $(GO) test $(GO_FLAGS) ./... -p 2 +GOTEST = $(CGO_CFLAGS) GODEBUG=cgocheck=0 $(GO) test $(GO_FLAGS) -coverprofile=coverage.out ./... -p 2 default: all @@ -149,7 +149,7 @@ db-tools: ## test: run unit tests with a 100s timeout test: - $(GOTEST) --timeout 200s + $(GOTEST) --timeout 10m test3: $(GOTEST) --timeout 200s -tags $(BUILD_TAGS),erigon3 diff --git a/chain/chain_config.go b/chain/chain_config.go index 917e6e104fa..9537b67c03a 100644 --- a/chain/chain_config.go +++ b/chain/chain_config.go @@ -102,7 +102,7 @@ type Config struct { ForkID11 *big.Int `json:"forkID11,omitempty"` ForkID12BananaBlock *big.Int `json:"forkID12BananaBlock,omitempty"` - SupportGasless bool `json:"supportGasless,omitempty"` + AllowFreeTransactions bool `json:"allowFreeTransactions,omitempty"` } func (c *Config) String() string { diff --git a/cmd/cdk-erigon/main.go b/cmd/cdk-erigon/main.go index a6461574412..6598464dea1 100644 --- a/cmd/cdk-erigon/main.go +++ b/cmd/cdk-erigon/main.go @@ -18,8 +18,8 @@ import ( "github.com/ledgerwatch/erigon/params" erigonapp "github.com/ledgerwatch/erigon/turbo/app" erigoncli "github.com/ledgerwatch/erigon/turbo/cli" - "github.com/ledgerwatch/erigon/turbo/node" "github.com/ledgerwatch/erigon/turbo/logging" + "github.com/ledgerwatch/erigon/turbo/node" ) func main() { @@ -109,13 +109,17 @@ func setFlagsFromConfigFile(ctx *cli.Context, filePath string) error { for i, v := range sliceInterface { s[i] = fmt.Sprintf("%v", v) } - err := ctx.Set(key, strings.Join(s, ",")) - if err != nil { + if err := ctx.Set(key, strings.Join(s, ",")); err != nil { + if deprecatedFlag, found := erigoncli.DeprecatedFlags[key]; found { + return fmt.Errorf("failed setting %s flag Flag is deprecated, use %s instead", key, deprecatedFlag) + } return fmt.Errorf("failed setting %s flag with values=%s error=%s", key, s, err) } } else { - err := ctx.Set(key, fmt.Sprintf("%v", value)) - if err != nil { + if err := ctx.Set(key, fmt.Sprintf("%v", value)); err != nil { + if deprecatedFlag, found := erigoncli.DeprecatedFlags[key]; found { + return fmt.Errorf("failed setting %s flag Flag is deprecated, use %s instead", key, deprecatedFlag) + } return fmt.Errorf("failed setting %s flag with value=%v error=%s", key, value, err) } } diff --git a/cmd/integration/commands/flags.go b/cmd/integration/commands/flags.go index 96e43da4d4a..ecf79ff77b5 100644 --- a/cmd/integration/commands/flags.go +++ b/cmd/integration/commands/flags.go @@ -31,6 +31,7 @@ var ( pruneTBefore, pruneCBefore uint64 experiments []string chain string // Which chain to use (mainnet, rinkeby, goerli, etc.) + config string commitmentMode string commitmentTrie string @@ -49,7 +50,7 @@ func must(err error) { } func withConfig(cmd *cobra.Command) { - cmd.Flags().String("config", "", "yaml/toml config file location") + cmd.Flags().StringVar(&config, "config", "", "yaml/toml config file location") } func withMining(cmd *cobra.Command) { diff --git a/cmd/integration/commands/stage_stages_zkevm.go b/cmd/integration/commands/stage_stages_zkevm.go index d2078b02e44..607072bb231 100644 --- a/cmd/integration/commands/stage_stages_zkevm.go +++ b/cmd/integration/commands/stage_stages_zkevm.go @@ -8,11 +8,8 @@ import ( common2 "github.com/gateway-fm/cdk-erigon-lib/common" "github.com/gateway-fm/cdk-erigon-lib/kv" - "github.com/ledgerwatch/erigon/core" - "github.com/ledgerwatch/erigon/eth/ethconfig" "github.com/ledgerwatch/erigon/eth/stagedsync/stages" smtdb "github.com/ledgerwatch/erigon/smt/pkg/db" - erigoncli "github.com/ledgerwatch/erigon/turbo/cli" "github.com/ledgerwatch/erigon/zk/hermez_db" "github.com/ledgerwatch/log/v3" "github.com/spf13/cobra" @@ -28,9 +25,6 @@ state_stages_zkevm --datadir=/datadirs/hermez-mainnet --unwind-batch-no=2 --chai Example: "go run ./cmd/integration state_stages_zkevm --config=... --verbosity=3 --unwind-batch-no=100", Run: func(cmd *cobra.Command, args []string) { ctx, _ := common2.RootContext() - ethConfig := ðconfig.Defaults - ethConfig.Genesis = core.GenesisBlockByChainName(chain) - erigoncli.ApplyFlagsForEthConfigCobra(cmd.Flags(), ethConfig) db := openDB(dbCfg(kv.ChainDB, chaindata), true) defer db.Close() diff --git a/cmd/integration/commands/stages_zkevm.go b/cmd/integration/commands/stages_zkevm.go index 79fbf578f2e..9322fc2e853 100644 --- a/cmd/integration/commands/stages_zkevm.go +++ b/cmd/integration/commands/stages_zkevm.go @@ -2,6 +2,12 @@ package commands import ( "context" + "encoding/json" + "math/big" + "os" + "path" + "path/filepath" + "strings" "github.com/c2h5oh/datasize" chain3 "github.com/gateway-fm/cdk-erigon-lib/chain" @@ -10,11 +16,14 @@ import ( "github.com/gateway-fm/cdk-erigon-lib/kv/kvcfg" "github.com/ledgerwatch/erigon/cmd/hack/tool/fromdb" "github.com/ledgerwatch/erigon/cmd/sentry/sentry" + "github.com/ledgerwatch/erigon/cmd/utils" "github.com/ledgerwatch/erigon/consensus" "github.com/ledgerwatch/erigon/core" + "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/core/vm" "github.com/ledgerwatch/erigon/eth/ethconfig" "github.com/ledgerwatch/erigon/eth/stagedsync" + "github.com/ledgerwatch/erigon/params" "github.com/ledgerwatch/erigon/turbo/shards" stages2 "github.com/ledgerwatch/erigon/turbo/stages" "github.com/ledgerwatch/erigon/zk/sequencer" @@ -26,7 +35,36 @@ func newSyncZk(ctx context.Context, db kv.RwDB) (consensus.Engine, *vm.Config, * vmConfig := &vm.Config{} - genesis := core.GenesisBlockByChainName(chain) + var genesis *types.Genesis + + if strings.HasPrefix(chain, "dynamic") { + if config == "" { + panic("Config file is required for dynamic chain") + } + + params.DynamicChainConfigPath = filepath.Dir(config) + genesis = core.GenesisBlockByChainName(chain) + filename := path.Join(params.DynamicChainConfigPath, chain+"-conf.json") + + dConf := utils.DynamicConfig{} + + if _, err := os.Stat(filename); err == nil { + dConfBytes, err := os.ReadFile(filename) + if err != nil { + panic(err) + } + if err := json.Unmarshal(dConfBytes, &dConf); err != nil { + panic(err) + } + } + + genesis.Timestamp = dConf.Timestamp + genesis.GasLimit = dConf.GasLimit + genesis.Difficulty = big.NewInt(dConf.Difficulty) + } else { + genesis = core.GenesisBlockByChainName(chain) + } + chainConfig, genesisBlock, genesisErr := core.CommitGenesisBlock(db, genesis, "") if _, ok := genesisErr.(*chain3.ConfigCompatError); genesisErr != nil && !ok { panic(genesisErr) diff --git a/cmd/rpcdaemon/cli/config.go b/cmd/rpcdaemon/cli/config.go index fa41f35df29..37a1e0a5bb6 100644 --- a/cmd/rpcdaemon/cli/config.go +++ b/cmd/rpcdaemon/cli/config.go @@ -96,6 +96,9 @@ func RootCommand() (*cobra.Command, *httpcfg.HttpCfg) { rootCmd.PersistentFlags().Uint64Var(&cfg.MaxTraces, "trace.maxtraces", 200, "Sets a limit on traces that can be returned in trace_filter") rootCmd.PersistentFlags().BoolVar(&cfg.WebsocketEnabled, "ws", false, "Enable Websockets - Same port as HTTP") rootCmd.PersistentFlags().BoolVar(&cfg.WebsocketCompression, "ws.compression", false, "Enable Websocket compression (RFC 7692)") + rootCmd.PersistentFlags().StringVar(&cfg.WebSocketListenAddress, "ws.addr", nodecfg.DefaultHTTPHost, "Websocket server listening interface") + rootCmd.PersistentFlags().IntVar(&cfg.WebSocketPort, "ws.port", nodecfg.DefaultHTTPPort, "Websocket server listening port") + rootCmd.PersistentFlags().StringSliceVar(&cfg.WebsocketCORSDomain, "ws.corsdomain", []string{}, "Comma separated list of domains from which to accept cross origin requests (browser enforced)") rootCmd.PersistentFlags().StringVar(&cfg.RpcAllowListFilePath, utils.RpcAccessListFlag.Name, "", "Specify granular (method-by-method) API allowlist") rootCmd.PersistentFlags().UintVar(&cfg.RpcBatchConcurrency, utils.RpcBatchConcurrencyFlag.Name, 2, utils.RpcBatchConcurrencyFlag.Usage) rootCmd.PersistentFlags().BoolVar(&cfg.RpcStreamingDisable, utils.RpcStreamingDisableFlag.Name, false, utils.RpcStreamingDisableFlag.Usage) @@ -533,14 +536,10 @@ func startRegularRpcServer(ctx context.Context, cfg httpcfg.HttpCfg, rpcAPI []rp } httpHandler := node.NewHTTPHandlerStack(srv, cfg.HttpCORSDomain, cfg.HttpVirtualHost, cfg.HttpCompression) - var wsHandler http.Handler - if cfg.WebsocketEnabled { - wsHandler = srv.WebsocketHandler([]string{"*"}, nil, cfg.WebsocketCompression) - } graphQLHandler := graphql.CreateHandler(defaultAPIList) - apiHandler, err := createHandler(cfg, defaultAPIList, httpHandler, wsHandler, graphQLHandler, nil) + apiHandler, err := createHandler(cfg, defaultAPIList, httpHandler, nil, graphQLHandler, nil) if err != nil { return err } @@ -609,6 +608,69 @@ func startRegularRpcServer(ctx context.Context, cfg httpcfg.HttpCfg, rpcAPI []rp log.Info("GRPC endpoint closed", "url", grpcEndpoint) } }() + + if cfg.WebsocketEnabled { + wsSrv := rpc.NewServer(cfg.RpcBatchConcurrency, cfg.TraceRequests, cfg.RpcStreamingDisable) + + allowListForRPC, err := parseAllowListForRPC(cfg.RpcAllowListFilePath) + if err != nil { + return err + } + + var wsApiFlags []string + for _, flag := range cfg.WebSocketApi { + if flag != "engine" { + wsApiFlags = append(wsApiFlags, flag) + } + } + + if err := node.RegisterApisFromWhitelist(defaultAPIList, wsApiFlags, wsSrv, false); err != nil { + return fmt.Errorf("could not start register WS apis: %w", err) + } + wsSrv.SetAllowList(allowListForRPC) + + wsSrv.SetBatchLimit(cfg.BatchLimit) + + var defaultAPIList []rpc.API + + for _, api := range rpcAPI { + if api.Namespace != "engine" { + defaultAPIList = append(defaultAPIList, api) + } + } + + var apiFlags []string + for _, flag := range cfg.API { + if flag != "engine" { + apiFlags = append(apiFlags, flag) + } + } + + if err := node.RegisterApisFromWhitelist(defaultAPIList, apiFlags, wsSrv, false); err != nil { + return fmt.Errorf("could not start register RPC apis: %w", err) + } + + wsEndpoint := fmt.Sprintf("%s:%d", cfg.WebSocketListenAddress, cfg.WebSocketPort) + + wsHttpHandler := wsSrv.WebsocketHandler(cfg.WebsocketCORSDomain, nil, cfg.WebsocketCompression) + wsHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + wsHttpHandler.ServeHTTP(w, r) + }) + + wsListener, wsHttpAddr, err := node.StartHTTPEndpoint(wsEndpoint, cfg.HTTPTimeouts, wsHandler) + if err != nil { + return fmt.Errorf("could not start ws RPC api: %w", err) + } + + defer func() { + wsSrv.Stop() + shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + _ = wsListener.Shutdown(shutdownCtx) + log.Info("WS endpoint closed", "url", wsHttpAddr) + }() + } + <-ctx.Done() log.Info("Exiting...") return nil @@ -685,7 +747,7 @@ func obtainJWTSecret(cfg httpcfg.HttpCfg) ([]byte, error) { return jwtSecret, nil } -func createHandler(cfg httpcfg.HttpCfg, apiList []rpc.API, httpHandler http.Handler, wsHandler http.Handler, graphQLHandler http.Handler, jwtSecret []byte) (http.Handler, error) { +func createHandler(cfg httpcfg.HttpCfg, apiList []rpc.API, httpHandler, wsHandler, graphQLHandler http.Handler, jwtSecret []byte) (http.Handler, error) { var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if cfg.GraphQLEnabled && graphql.ProcessGraphQLcheckIfNeeded(graphQLHandler, w, r) { return diff --git a/cmd/rpcdaemon/cli/httpcfg/http_cfg.go b/cmd/rpcdaemon/cli/httpcfg/http_cfg.go index f42bb94f38f..601c9218b78 100644 --- a/cmd/rpcdaemon/cli/httpcfg/http_cfg.go +++ b/cmd/rpcdaemon/cli/httpcfg/http_cfg.go @@ -32,15 +32,20 @@ type HttpCfg struct { MaxTraces uint64 WebsocketEnabled bool WebsocketCompression bool - RpcAllowListFilePath string - RpcBatchConcurrency uint - RpcStreamingDisable bool - DBReadConcurrency int - TraceCompatibility bool // Bug for bug compatibility for trace_ routines with OpenEthereum - TxPoolApiAddr string - StateCache kvcache.CoherentConfig - Snap ethconfig.Snapshot - Sync ethconfig.Sync + WebSocketListenAddress string + WebSocketPort int + WebsocketCORSDomain []string + WebSocketApi []string + + RpcAllowListFilePath string + RpcBatchConcurrency uint + RpcStreamingDisable bool + DBReadConcurrency int + TraceCompatibility bool // Bug for bug compatibility for trace_ routines with OpenEthereum + TxPoolApiAddr string + StateCache kvcache.CoherentConfig + Snap ethconfig.Snapshot + Sync ethconfig.Sync // GRPC server GRPCServerEnabled bool diff --git a/cmd/rpcdaemon/commands/eth_call.go b/cmd/rpcdaemon/commands/eth_call.go index b378e391438..4b069fb173c 100644 --- a/cmd/rpcdaemon/commands/eth_call.go +++ b/cmd/rpcdaemon/commands/eth_call.go @@ -207,7 +207,7 @@ func (api *APIImpl) EstimateGas(ctx context.Context, argsOrNil *ethapi2.CallArgs if transfer == nil { transfer = new(hexutil.Big) } - log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance, + log.Debug("Gas estimation capped by limited funds", "original", hi, "balance", balance, "sent", transfer.ToInt(), "maxFeePerGas", feeCap, "fundable", allowance) hi = allowance.Uint64() } @@ -215,7 +215,7 @@ func (api *APIImpl) EstimateGas(ctx context.Context, argsOrNil *ethapi2.CallArgs // Recap the highest gas allowance with specified gascap. if hi > api.GasCap { - log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", api.GasCap) + log.Debug("Caller gas above allowance, capping", "requested", hi, "cap", api.GasCap) hi = api.GasCap } gasCap = hi @@ -249,7 +249,7 @@ func (api *APIImpl) EstimateGas(ctx context.Context, argsOrNil *ethapi2.CallArgs } header := block.HeaderNoCopy() - caller, err := transactions.NewReusableCaller(engine, stateReader, nil, header, args, api.GasCap, latestNumOrHash, dbtx, api._blockReader, chainConfig, api.evmCallTimeout, api.VirtualCountersSmtReduction) + caller, err := transactions.NewReusableCaller(engine, stateReader, nil, header, args, api.GasCap, latestNumOrHash, dbtx, api._blockReader, chainConfig, api.evmCallTimeout, api.VirtualCountersSmtReduction, false) if err != nil { return 0, err } @@ -340,7 +340,7 @@ func (api *APIImpl) GetProof(ctx context.Context, address libcommon.Address, sto return nil, err } - latestBlock, err := rpchelper.GetLatestBlockNumber(tx) + latestBlock, err := rpchelper.GetLatestFinishedBlockNumber(tx) if err != nil { return nil, err } @@ -385,6 +385,9 @@ func (api *APIImpl) GetProof(ctx context.Context, address libcommon.Address, sto if err != nil { return nil, err } + if a == nil { + return nil, nil + } pr, err := trie.NewProofRetainer(address, a, storageKeys, rl) if err != nil { return nil, err diff --git a/cmd/rpcdaemon/commands/mocks/l1_syncer_mock.go b/cmd/rpcdaemon/commands/mocks/l1_syncer_mock.go new file mode 100644 index 00000000000..3f98f17f40f --- /dev/null +++ b/cmd/rpcdaemon/commands/mocks/l1_syncer_mock.go @@ -0,0 +1,151 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: /home/rr/Documentos/Iden3/cdk-erigon/zk/syncer/l1_syncer.go +// +// Generated by this command: +// +// mockgen -source /home/rr/Documentos/Iden3/cdk-erigon/zk/syncer/l1_syncer.go -destination /home/rr/Documentos/Iden3/cdk-erigon/cmd/rpcdaemon/commands/mock/l1_syncer_mock.go -package=mocks +// + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + big "math/big" + reflect "reflect" + + common "github.com/gateway-fm/cdk-erigon-lib/common" + ethereum "github.com/ledgerwatch/erigon" + types "github.com/ledgerwatch/erigon/core/types" + gomock "go.uber.org/mock/gomock" +) + +// MockIEtherman is a mock of IEtherman interface. +type MockIEtherman struct { + ctrl *gomock.Controller + recorder *MockIEthermanMockRecorder +} + +// MockIEthermanMockRecorder is the mock recorder for MockIEtherman. +type MockIEthermanMockRecorder struct { + mock *MockIEtherman +} + +// NewMockIEtherman creates a new mock instance. +func NewMockIEtherman(ctrl *gomock.Controller) *MockIEtherman { + mock := &MockIEtherman{ctrl: ctrl} + mock.recorder = &MockIEthermanMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIEtherman) EXPECT() *MockIEthermanMockRecorder { + return m.recorder +} + +// BlockByNumber mocks base method. +func (m *MockIEtherman) BlockByNumber(ctx context.Context, blockNumber *big.Int) (*types.Block, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BlockByNumber", ctx, blockNumber) + ret0, _ := ret[0].(*types.Block) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BlockByNumber indicates an expected call of BlockByNumber. +func (mr *MockIEthermanMockRecorder) BlockByNumber(ctx, blockNumber any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BlockByNumber", reflect.TypeOf((*MockIEtherman)(nil).BlockByNumber), ctx, blockNumber) +} + +// CallContract mocks base method. +func (m *MockIEtherman) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CallContract", ctx, msg, blockNumber) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CallContract indicates an expected call of CallContract. +func (mr *MockIEthermanMockRecorder) CallContract(ctx, msg, blockNumber any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CallContract", reflect.TypeOf((*MockIEtherman)(nil).CallContract), ctx, msg, blockNumber) +} + +// CallContract indicates an expected call of CallContract. +func (m *MockIEtherman) StorageAt(ctx context.Context, contract common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StorageAt", ctx, contract, key, blockNumber) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CallContract indicates an expected call of CallContract. +func (mr *MockIEthermanMockRecorder) StorageAt(ctx, contract, key, blockNumber any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageAt", reflect.TypeOf((*MockIEtherman)(nil).StorageAt), ctx, contract, key, blockNumber) +} + + +// FilterLogs mocks base method. +func (m *MockIEtherman) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FilterLogs", ctx, query) + ret0, _ := ret[0].([]types.Log) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FilterLogs indicates an expected call of FilterLogs. +func (mr *MockIEthermanMockRecorder) FilterLogs(ctx, query any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FilterLogs", reflect.TypeOf((*MockIEtherman)(nil).FilterLogs), ctx, query) +} + +// HeaderByNumber mocks base method. +func (m *MockIEtherman) HeaderByNumber(ctx context.Context, blockNumber *big.Int) (*types.Header, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HeaderByNumber", ctx, blockNumber) + ret0, _ := ret[0].(*types.Header) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HeaderByNumber indicates an expected call of HeaderByNumber. +func (mr *MockIEthermanMockRecorder) HeaderByNumber(ctx, blockNumber any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HeaderByNumber", reflect.TypeOf((*MockIEtherman)(nil).HeaderByNumber), ctx, blockNumber) +} + +// TransactionByHash mocks base method. +func (m *MockIEtherman) TransactionByHash(ctx context.Context, hash common.Hash) (types.Transaction, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TransactionByHash", ctx, hash) + ret0, _ := ret[0].(types.Transaction) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// TransactionByHash indicates an expected call of TransactionByHash. +func (mr *MockIEthermanMockRecorder) TransactionByHash(ctx, hash any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransactionByHash", reflect.TypeOf((*MockIEtherman)(nil).TransactionByHash), ctx, hash) +} + +// TransactionReceipt mocks base method. +func (m *MockIEtherman) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TransactionReceipt", ctx, txHash) + ret0, _ := ret[0].(*types.Receipt) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TransactionReceipt indicates an expected call of TransactionReceipt. +func (mr *MockIEthermanMockRecorder) TransactionReceipt(ctx, txHash any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransactionReceipt", reflect.TypeOf((*MockIEtherman)(nil).TransactionReceipt), ctx, txHash) +} diff --git a/cmd/rpcdaemon/commands/tracing_zkevm.go b/cmd/rpcdaemon/commands/tracing_zkevm.go index 154eb371b5d..6d3030c6de1 100644 --- a/cmd/rpcdaemon/commands/tracing_zkevm.go +++ b/cmd/rpcdaemon/commands/tracing_zkevm.go @@ -105,6 +105,9 @@ func (api *PrivateDebugAPIImpl) traceBlock(ctx context.Context, blockNrOrHash rp for idx, txn := range txns { stream.WriteObjectStart() + stream.WriteObjectField("txHash") + stream.WriteString(txn.Hash().Hex()) + stream.WriteMore() stream.WriteObjectField("result") select { default: diff --git a/cmd/rpcdaemon/commands/txpool_api.go b/cmd/rpcdaemon/commands/txpool_api.go index 0065a7dbb7d..ab0e2e1b686 100644 --- a/cmd/rpcdaemon/commands/txpool_api.go +++ b/cmd/rpcdaemon/commands/txpool_api.go @@ -14,8 +14,8 @@ import ( "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/rlp" - "github.com/ledgerwatch/erigon/zkevm/jsonrpc/client" "github.com/ledgerwatch/erigon/zk/txpool" + "github.com/ledgerwatch/erigon/zkevm/jsonrpc/client" ) // NetAPI the interface for the net_ RPC commands @@ -156,8 +156,6 @@ func (api *TxPoolAPIImpl) Status(ctx context.Context) (interface{}, error) { } func (api *TxPoolAPIImpl) Limbo(ctx context.Context) (interface{}, error) { - // Get the limbo transactions - details := api.rawPool.GetLimboDetails() - + details := api.rawPool.GetInvalidLimboBlocksDetails() return details, nil } diff --git a/cmd/rpcdaemon/commands/zkevm_api.go b/cmd/rpcdaemon/commands/zkevm_api.go index 369bc55b78b..cacdbe257d9 100644 --- a/cmd/rpcdaemon/commands/zkevm_api.go +++ b/cmd/rpcdaemon/commands/zkevm_api.go @@ -13,6 +13,8 @@ import ( "github.com/gateway-fm/cdk-erigon-lib/kv" "github.com/gateway-fm/cdk-erigon-lib/kv/memdb" jsoniter "github.com/json-iterator/go" + zktypes "github.com/ledgerwatch/erigon/zk/types" + "github.com/ledgerwatch/log/v3" "github.com/holiman/uint256" "github.com/ledgerwatch/erigon/common/hexutil" @@ -43,13 +45,10 @@ import ( "github.com/ledgerwatch/erigon/zk/witness" "github.com/ledgerwatch/erigon/zkevm/hex" "github.com/ledgerwatch/erigon/zkevm/jsonrpc/client" - "github.com/ledgerwatch/log/v3" ) var sha3UncleHash = common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") -const ApiRollupId = 1 // todo [zkevm] this should be read from config really - // ZkEvmAPI is a collection of functions that are exposed in the type ZkEvmAPI interface { ConsolidatedBlockNumber(ctx context.Context) (hexutil.Uint64, error) @@ -75,6 +74,10 @@ type ZkEvmAPI interface { GetBatchCountersByNumber(ctx context.Context, batchNumRpc rpc.BlockNumber) (res json.RawMessage, err error) GetExitRootTable(ctx context.Context) ([]l1InfoTreeData, error) GetVersionHistory(ctx context.Context) (json.RawMessage, error) + GetForkId(ctx context.Context) (hexutil.Uint64, error) + GetForkById(ctx context.Context, forkId hexutil.Uint64) (res json.RawMessage, err error) + GetForkIdByBatchNumber(ctx context.Context, batchNumber rpc.BlockNumber) (hexutil.Uint64, error) + GetForks(ctx context.Context) (res json.RawMessage, err error) } const getBatchWitness = "getBatchWitness" @@ -159,7 +162,9 @@ func (api *ZkEvmAPIImpl) IsBlockConsolidated(ctx context.Context, blockNumber rp defer tx.Rollback() batchNum, err := getBatchNoByL2Block(tx, uint64(blockNumber.Int64())) - if err != nil { + if errors.Is(err, hermez_db.ErrorNotStored) { + return false, nil + } else if err != nil { return false, err } @@ -180,7 +185,9 @@ func (api *ZkEvmAPIImpl) IsBlockVirtualized(ctx context.Context, blockNumber rpc defer tx.Rollback() batchNum, err := getBatchNoByL2Block(tx, uint64(blockNumber.Int64())) - if err != nil { + if errors.Is(err, hermez_db.ErrorNotStored) { + return false, nil + } else if err != nil { return false, err } @@ -309,7 +316,7 @@ func (api *ZkEvmAPIImpl) GetBatchDataByNumbers(ctx context.Context, batchNumbers for _, batchNumber := range batchNumbers.Numbers { bd := &types.BatchDataSlim{ - Number: uint64(batchNumber.Int64()), + Number: types.ArgUint64(batchNumber.Int64()), Empty: false, } @@ -391,6 +398,59 @@ func (api *ZkEvmAPIImpl) GetBatchDataByNumbers(ctx context.Context, batchNumbers return populateBatchDataSlimDetails(bds) } +func generateBatchData( + tx kv.Tx, + hermezDb *hermez_db.HermezDbReader, + batchBlocks []*eritypes.Block, + forkId uint64, +) (batchL2Data []byte, err error) { + if len(batchBlocks) == 0 { + return batchL2Data, nil + } + + lastBlockNoInPreviousBatch := uint64(0) + if batchBlocks[0].NumberU64() != 0 { + lastBlockNoInPreviousBatch = batchBlocks[0].NumberU64() - 1 + } + + lastBlockInPreviousBatch, err := rawdb.ReadBlockByNumber(tx, lastBlockNoInPreviousBatch) + if err != nil { + return nil, err + } + + for i := 0; i < len(batchBlocks); i++ { + var dTs uint32 + if i == 0 { + dTs = uint32(batchBlocks[i].Time() - lastBlockInPreviousBatch.Time()) + } else { + dTs = uint32(batchBlocks[i].Time() - batchBlocks[i-1].Time()) + } + iti, err := hermezDb.GetBlockL1InfoTreeIndex(batchBlocks[i].NumberU64()) + if err != nil { + return nil, err + } + egTx := make(map[common.Hash]uint8) + for _, txn := range batchBlocks[i].Transactions() { + eg, err := hermezDb.GetEffectiveGasPricePercentage(txn.Hash()) + if err != nil { + return nil, err + } + egTx[txn.Hash()] = eg + } + + // block 0 does not geenrate any data (special case) + var bl2d []byte + if batchBlocks[i].NumberU64() != 0 { + if bl2d, err = zktx.GenerateBlockBatchL2Data(uint16(forkId), dTs, uint32(iti), batchBlocks[i].Transactions(), egTx); err != nil { + return nil, err + } + } + batchL2Data = append(batchL2Data, bl2d...) + } + + return batchL2Data, err +} + // GetBatchByNumber returns a batch from the current canonical chain. If number is nil, the // latest known batch is returned. func (api *ZkEvmAPIImpl) GetBatchByNumber(ctx context.Context, batchNumber rpc.BlockNumber, fullTx *bool) (json.RawMessage, error) { @@ -400,7 +460,6 @@ func (api *ZkEvmAPIImpl) GetBatchByNumber(ctx context.Context, batchNumber rpc.B } defer tx.Rollback() hermezDb := hermez_db.NewHermezDbReader(tx) - // use inbuilt rpc.BlockNumber type to implement the 'latest' behaviour // the highest block/batch is tied to last block synced // unless the node is still syncing - in which case 'current block' is used @@ -443,19 +502,16 @@ func (api *ZkEvmAPIImpl) GetBatchByNumber(ctx context.Context, batchNumber rpc.B Number: types.ArgUint64(batchNo), } - // mimic zkevm node null response if we don't have the batch - _, found, err := hermezDb.GetLowestBlockInBatch(batchNo) - if err != nil { - return nil, err - } - if !found && batchNo != 0 { - return nil, nil - } - - // highest block in batch - blockNo, err := hermezDb.GetHighestBlockInBatch(batchNo) - if err != nil { - return nil, err + // loop until we find a block in the batch + var found bool + var blockNo, counter uint64 + for !found { + // highest block in batch + blockNo, found, err = hermezDb.GetHighestBlockInBatch(batchNo - counter) + if err != nil { + return nil, err + } + counter++ } block, err := api.ethApi.BaseAPI.blockByNumberWithSenders(tx, blockNo) @@ -569,6 +625,16 @@ func (api *ZkEvmAPIImpl) GetBatchByNumber(ctx context.Context, batchNumber rpc.B batch.Transactions = nil } + if len(batch.Blocks) == 0 { + batch.Blocks = nil + } + + // batch l2 data - must build on the fly + forkId, err := hermezDb.GetForkId(batchNo) + if err != nil { + return nil, err + } + // global exit root of batch batchGer, _, err := hermezDb.GetLastBlockGlobalExitRoot(blockNo) if err != nil { @@ -582,7 +648,9 @@ func (api *ZkEvmAPIImpl) GetBatchByNumber(ctx context.Context, batchNumber rpc.B if err != nil { return nil, err } - if seq != nil { + if batchNo == 0 { + batch.SendSequencesTxHash = &common.Hash{0} + } else if seq != nil { batch.SendSequencesTxHash = &seq.L1TxHash } @@ -591,8 +659,7 @@ func (api *ZkEvmAPIImpl) GetBatchByNumber(ctx context.Context, batchNumber rpc.B batch.Timestamp = types.ArgUint64(block.Time()) } - _, found, err = hermezDb.GetLowestBlockInBatch(batchNo + 1) - if err != nil { + if _, found, err = hermezDb.GetLowestBlockInBatch(batchNo + 1); err != nil { return nil, err } // sequenced, genesis or injected batch 1 - special batches 0,1 will always be closed, if next batch has blocks, bn must be closed @@ -603,10 +670,10 @@ func (api *ZkEvmAPIImpl) GetBatchByNumber(ctx context.Context, batchNumber rpc.B if err != nil { return nil, err } - if ver == nil { - // TODO: this is the actual unverified batch behaviour probably set 0x00 - } - if ver != nil { + + if batchNo == 0 { + batch.VerifyBatchTxHash = &common.Hash{0} + } else if ver != nil { batch.VerifyBatchTxHash = &ver.L1TxHash } @@ -626,24 +693,22 @@ func (api *ZkEvmAPIImpl) GetBatchByNumber(ctx context.Context, batchNumber rpc.B } batch.LocalExitRoot = localExitRoot - // batch l2 data - must build on the fly - forkId, err := hermezDb.GetForkId(batchNo) - if err != nil { - return nil, err - } - - batchL2Data, err := utils.GenerateBatchData(tx, hermezDb, batchBlocks, forkId) + batchL2Data, err := generateBatchData(tx, hermezDb, batchBlocks, forkId) if err != nil { return nil, err } batch.BatchL2Data = batchL2Data - oldAccInputHash, err := api.l1Syncer.GetOldAccInputHash(ctx, &api.config.AddressRollup, ApiRollupId, batchNo) - if err != nil { - log.Warn("Failed to get old acc input hash", "err", err) - batch.AccInputHash = common.Hash{} + if api.l1Syncer != nil { + accInputHash, err := api.getAccInputHash(ctx, hermezDb, batchNo) + if err != nil { + log.Error(fmt.Sprintf("failed to get acc input hash for batch %d: %v", batchNo, err)) + } + if accInputHash == nil { + accInputHash = &common.Hash{} + } + batch.AccInputHash = *accInputHash } - batch.AccInputHash = oldAccInputHash // forkid exit roots logic // if forkid < 12 then we should only set the exit roots if they have changed, otherwise 0x00..00 @@ -651,7 +716,7 @@ func (api *ZkEvmAPIImpl) GetBatchByNumber(ctx context.Context, batchNumber rpc.B if forkId < uint64(constants.ForkID12Banana) { // get the previous batches exit roots prevBatchNo := batchNo - 1 - prevBatchHighestBlock, err := hermezDb.GetHighestBlockInBatch(prevBatchNo) + prevBatchHighestBlock, _, err := hermezDb.GetHighestBlockInBatch(prevBatchNo) if err != nil { return nil, err } @@ -670,6 +735,81 @@ func (api *ZkEvmAPIImpl) GetBatchByNumber(ctx context.Context, batchNumber rpc.B return populateBatchDetails(batch) } +type SequenceReader interface { + GetRangeSequencesByBatch(batchNo uint64) (*zktypes.L1BatchInfo, *zktypes.L1BatchInfo, error) + GetForkId(batchNo uint64) (uint64, error) +} + +func (api *ZkEvmAPIImpl) getAccInputHash(ctx context.Context, db SequenceReader, batchNum uint64) (accInputHash *common.Hash, err error) { + // get batch sequence + prevSequence, batchSequence, err := db.GetRangeSequencesByBatch(batchNum) + if err != nil { + return nil, fmt.Errorf("failed to get sequence range data for batch %d: %w", batchNum, err) + } + + if prevSequence == nil || batchSequence == nil { + return nil, fmt.Errorf("failed to get sequence data for batch %d", batchNum) + } + + // get batch range for sequence + prevSequenceBatch, currentSequenceBatch := prevSequence.BatchNo, batchSequence.BatchNo + // get call data for tx + l1Transaction, _, err := api.l1Syncer.GetTransaction(batchSequence.L1TxHash) + if err != nil { + return nil, fmt.Errorf("failed to get transaction data for tx %s: %w", batchSequence.L1TxHash, err) + } + sequenceBatchesCalldata := l1Transaction.GetData() + if len(sequenceBatchesCalldata) < 10 { + return nil, fmt.Errorf("calldata for tx %s is too short", batchSequence.L1TxHash) + } + + currentBatchForkId, err := db.GetForkId(currentSequenceBatch) + if err != nil { + return nil, fmt.Errorf("failed to get fork id for batch %d: %w", currentSequenceBatch, err) + } + + prevSequenceAccinputHash, err := api.GetccInputHash(ctx, currentBatchForkId, prevSequenceBatch) + if err != nil { + return nil, fmt.Errorf("failed to get old acc input hash for batch %d: %w", prevSequenceBatch, err) + } + + decodedSequenceInteerface, err := syncer.DecodeSequenceBatchesCalldata(sequenceBatchesCalldata) + if err != nil { + return nil, fmt.Errorf("failed to decode calldata for tx %s: %w", batchSequence.L1TxHash, err) + } + + accInputHashCalcFn, totalSequenceBatches, err := syncer.GetAccInputDataCalcFunction(batchSequence.L1InfoRoot, decodedSequenceInteerface) + if err != nil { + return nil, fmt.Errorf("failed to get accInputHash calculation func: %w", err) + } + + if totalSequenceBatches == 0 || batchNum-prevSequenceBatch > uint64(totalSequenceBatches) { + return nil, fmt.Errorf("batch %d is out of range of sequence calldata", batchNum) + } + + accInputHash = &prevSequenceAccinputHash + // calculate acc input hash + for i := 0; i < int(batchNum-prevSequenceBatch); i++ { + accInputHash = accInputHashCalcFn(prevSequenceAccinputHash, i) + } + + return +} + +func (api *ZkEvmAPIImpl) GetccInputHash(ctx context.Context, currentBatchForkId, lastSequenceBatchNumber uint64) (accInputHash common.Hash, err error) { + if currentBatchForkId < uint64(constants.ForkID8Elderberry) { + accInputHash, err = api.l1Syncer.GetPreElderberryAccInputHash(ctx, &api.config.AddressRollup, lastSequenceBatchNumber) + } else { + accInputHash, err = api.l1Syncer.GetElderberryAccInputHash(ctx, &api.config.AddressRollup, api.config.L1RollupId, lastSequenceBatchNumber) + } + + if err != nil { + err = fmt.Errorf("failed to get accInputHash batch %d: %w", lastSequenceBatchNumber, err) + } + + return +} + // GetFullBlockByNumber returns a full block from the current canonical chain. If number is nil, the // latest known block is returned. func (api *ZkEvmAPIImpl) GetFullBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (types.Block, error) { @@ -1010,9 +1150,15 @@ func (api *ZkEvmAPIImpl) GetProverInput(ctx context.Context, batchNumber uint64, return nil, err } - oldAccInputHash, err := api.l1Syncer.GetOldAccInputHash(ctx, &api.config.AddressRollup, ApiRollupId, batchNumber) - if err != nil { - return nil, err + var oldAccInputHash common.Hash + if batchNumber > 0 { + oaih, err := api.getAccInputHash(ctx, hDb, batchNumber-1) + if err != nil { + return nil, err + } + oldAccInputHash = *oaih + } else { + oldAccInputHash = common.Hash{} } timestampLimit := lastBlock.Time() @@ -1169,6 +1315,55 @@ func getBatchNoByL2Block(tx kv.Tx, l2BlockNo uint64) (uint64, error) { return reader.GetBatchNoByL2Block(l2BlockNo) } +func getForkIdByBatchNo(tx kv.Tx, batchNo uint64) (uint64, error) { + reader := hermez_db.NewHermezDbReader(tx) + return reader.GetForkId(batchNo) +} + +func getForkInterval(tx kv.Tx, forkId uint64) (*rpc.ForkInterval, error) { + reader := hermez_db.NewHermezDbReader(tx) + + forkInterval, found, err := reader.GetForkInterval(forkId) + if err != nil { + return nil, err + } else if !found { + return nil, nil + } + + result := rpc.ForkInterval{ + ForkId: hexutil.Uint64(forkInterval.ForkID), + FromBatchNumber: hexutil.Uint64(forkInterval.FromBatchNumber), + ToBatchNumber: hexutil.Uint64(forkInterval.ToBatchNumber), + Version: "", + BlockNumber: hexutil.Uint64(forkInterval.BlockNumber), + } + + return &result, nil +} + +func getForkIntervals(tx kv.Tx) ([]rpc.ForkInterval, error) { + reader := hermez_db.NewHermezDbReader(tx) + + forkIntervals, err := reader.GetAllForkIntervals() + if err != nil { + return nil, err + } + + result := make([]rpc.ForkInterval, 0, len(forkIntervals)) + + for _, forkInterval := range forkIntervals { + result = append(result, rpc.ForkInterval{ + ForkId: hexutil.Uint64(forkInterval.ForkID), + FromBatchNumber: hexutil.Uint64(forkInterval.FromBatchNumber), + ToBatchNumber: hexutil.Uint64(forkInterval.ToBatchNumber), + Version: "", + BlockNumber: hexutil.Uint64(forkInterval.BlockNumber), + }) + } + + return result, nil +} + func convertTransactionsReceipts( txs []eritypes.Transaction, receipts eritypes.Receipts, @@ -1394,9 +1589,7 @@ func populateBatchDetails(batch *types.Batch) (json.RawMessage, error) { jBatch["forcedBatchNumber"] = batch.ForcedBatchNumber } jBatch["closed"] = batch.Closed - if len(batch.BatchL2Data) > 0 { - jBatch["batchL2Data"] = batch.BatchL2Data - } + jBatch["batchL2Data"] = batch.BatchL2Data return json.Marshal(jBatch) } @@ -1405,7 +1598,7 @@ func populateBatchDataSlimDetails(batches []*types.BatchDataSlim) (json.RawMessa jBatches := make([]map[string]interface{}, 0, len(batches)) for _, b := range batches { jBatch := map[string]interface{}{} - jBatch["number"] = b.Number + jBatch["number"] = b.Number.Hex() jBatch["empty"] = b.Empty if !b.Empty { jBatch["batchL2Data"] = b.BatchL2Data @@ -1413,7 +1606,11 @@ func populateBatchDataSlimDetails(batches []*types.BatchDataSlim) (json.RawMessa jBatches = append(jBatches, jBatch) } - return json.Marshal(jBatches) + data := map[string]interface{}{ + "data": jBatches, + } + + return json.Marshal(data) } // GetProof @@ -1511,25 +1708,10 @@ func (zkapi *ZkEvmAPIImpl) GetProof(ctx context.Context, address common.Address, return nil, err } - balanceKey, err := smtUtils.KeyEthAddrBalance(address.String()) - if err != nil { - return nil, err - } - - nonceKey, err := smtUtils.KeyEthAddrNonce(address.String()) - if err != nil { - return nil, err - } - - codeHashKey, err := smtUtils.KeyContractCode(address.String()) - if err != nil { - return nil, err - } - - codeLengthKey, err := smtUtils.KeyContractLength(address.String()) - if err != nil { - return nil, err - } + balanceKey := smtUtils.KeyEthAddrBalance(address.String()) + nonceKey := smtUtils.KeyEthAddrNonce(address.String()) + codeHashKey := smtUtils.KeyContractCode(address.String()) + codeLengthKey := smtUtils.KeyContractLength(address.String()) balanceProofs := smt.FilterProofs(proofs, balanceKey) balanceBytes, err := smt.VerifyAndGetVal(stateRootNode, balanceProofs, balanceKey) @@ -1575,10 +1757,7 @@ func (zkapi *ZkEvmAPIImpl) GetProof(ctx context.Context, address common.Address, addressArrayBig := smtUtils.ScalarToArrayBig(smtUtils.ConvertHexToBigInt(address.String())) for _, k := range storageKeys { - storageKey, err := smtUtils.KeyContractStorage(addressArrayBig, k.String()) - if err != nil { - return nil, err - } + storageKey := smtUtils.KeyContractStorage(addressArrayBig, k.String()) storageProofs := smt.FilterProofs(proofs, storageKey) valueBytes, err := smt.VerifyAndGetVal(stateRootNode, storageProofs, storageKey) @@ -1597,3 +1776,86 @@ func (zkapi *ZkEvmAPIImpl) GetProof(ctx context.Context, address common.Address, return accProof, nil } + +// ForkId returns the network's current fork ID +func (api *ZkEvmAPIImpl) GetForkId(ctx context.Context) (hexutil.Uint64, error) { + tx, err := api.db.BeginRo(ctx) + if err != nil { + return hexutil.Uint64(0), err + } + defer tx.Rollback() + + currentBatchNumber, err := getLatestBatchNumber(tx) + if err != nil { + return 0, err + } + + currentForkId, err := getForkIdByBatchNo(tx, currentBatchNumber) + if err != nil { + return 0, err + } + + return hexutil.Uint64(currentForkId), err +} + +// GetForkById returns the network fork interval given the provided fork id +func (api *ZkEvmAPIImpl) GetForkById(ctx context.Context, forkId hexutil.Uint64) (res json.RawMessage, err error) { + tx, err := api.db.BeginRo(ctx) + if err != nil { + return nil, err + } + defer tx.Rollback() + + forkInterval, err := getForkInterval(tx, uint64(forkId)) + if err != nil { + return nil, err + } + + if forkInterval == nil { + return nil, nil + } + + forkJson, err := json.Marshal(forkInterval) + if err != nil { + return nil, err + } + + return forkJson, err +} + +// GetForkIdByBatchNumber returns the fork ID given the provided batch number +func (api *ZkEvmAPIImpl) GetForkIdByBatchNumber(ctx context.Context, batchNumber rpc.BlockNumber) (hexutil.Uint64, error) { + tx, err := api.db.BeginRo(ctx) + if err != nil { + return hexutil.Uint64(0), err + } + defer tx.Rollback() + + currentForkId, err := getForkIdByBatchNo(tx, uint64(batchNumber)) + if err != nil { + return 0, err + } + + return hexutil.Uint64(currentForkId), err +} + +// GetForks returns the network fork intervals +func (api *ZkEvmAPIImpl) GetForks(ctx context.Context) (res json.RawMessage, err error) { + tx, err := api.db.BeginRo(ctx) + if err != nil { + return nil, err + } + defer tx.Rollback() + + forkIntervals, err := getForkIntervals(tx) + if err != nil { + return nil, err + } + + forksJson, err := json.Marshal(forkIntervals) + if err != nil { + return nil, err + } + + return forksJson, err +} diff --git a/cmd/rpcdaemon/commands/zkevm_api_test.go b/cmd/rpcdaemon/commands/zkevm_api_test.go new file mode 100644 index 00000000000..c8d2c3860e7 --- /dev/null +++ b/cmd/rpcdaemon/commands/zkevm_api_test.go @@ -0,0 +1,1439 @@ +package commands + +import ( + "context" + "encoding/hex" + "encoding/json" + "fmt" + "math" + "math/big" + "testing" + "time" + + "github.com/gateway-fm/cdk-erigon-lib/common" + "github.com/gateway-fm/cdk-erigon-lib/common/datadir" + "github.com/gateway-fm/cdk-erigon-lib/kv/kvcache" + "github.com/holiman/uint256" + ethereum "github.com/ledgerwatch/erigon" + "github.com/ledgerwatch/erigon/accounts/abi/bind/backends" + "github.com/ledgerwatch/erigon/cmd/rpcdaemon/commands/mocks" + "github.com/ledgerwatch/erigon/common/hexutil" + "github.com/ledgerwatch/erigon/core/rawdb" + "github.com/ledgerwatch/erigon/core/types" + "github.com/ledgerwatch/erigon/crypto" + "github.com/ledgerwatch/erigon/eth/ethconfig" + "github.com/ledgerwatch/erigon/eth/stagedsync/stages" + "github.com/ledgerwatch/erigon/params" + "github.com/ledgerwatch/erigon/rpc" + "github.com/ledgerwatch/erigon/rpc/rpccfg" + "github.com/ledgerwatch/erigon/zk/erigon_db" + "github.com/ledgerwatch/erigon/zk/hermez_db" + rpctypes "github.com/ledgerwatch/erigon/zk/rpcdaemon" + "github.com/ledgerwatch/erigon/zk/syncer" + zktypes "github.com/ledgerwatch/erigon/zk/types" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" +) + +var ( + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + key1, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + address = crypto.PubkeyToAddress(key.PublicKey) + address1 = crypto.PubkeyToAddress(key1.PublicKey) + address2 = crypto.PubkeyToAddress(key2.PublicKey) + gspec = &types.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{ + address: {Balance: big.NewInt(9000000000000000000)}, + address1: {Balance: big.NewInt(200000000000000000)}, + address2: {Balance: big.NewInt(300000000000000000)}, + }, + GasLimit: 10000000, + } + chainID = big.NewInt(1337) + ctx = context.Background() + + addr1BalanceCheck = "70a08231" + "000000000000000000000000" + address1.Hex()[2:] + addr2BalanceCheck = "70a08231" + "000000000000000000000000" + address2.Hex()[2:] + transferAddr2 = "70a08231" + "000000000000000000000000" + address1.Hex()[2:] + "0000000000000000000000000000000000000000000000000000000000000064" +) + +func TestLatestConsolidatedBlockNumber(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + var l1Syncer *syncer.L1Syncer + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + for i := 1; i <= 10; i++ { + hDB.WriteBlockBatch(uint64(i), 1) + } + err = stages.SaveStageProgress(tx, stages.L1VerificationsBatchNo, 1) + assert.NoError(err) + tx.Commit() + blockNumber, err := zkEvmImpl.ConsolidatedBlockNumber(ctx) + assert.NoError(err) + t.Log("blockNumber: ", blockNumber) + + var expectedL2BlockNumber hexutil.Uint64 = 10 + assert.Equal(expectedL2BlockNumber, blockNumber) +} + +func TestIsBlockConsolidated(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + var l1Syncer *syncer.L1Syncer + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + isConsolidated, err := zkEvmImpl.IsBlockConsolidated(ctx, 11) + assert.NoError(err) + t.Logf("blockNumber: 11 -> %v", isConsolidated) + assert.False(isConsolidated) + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + for i := 1; i <= 10; i++ { + err := hDB.WriteBlockBatch(uint64(i), 1) + assert.NoError(err) + } + err = stages.SaveStageProgress(tx, stages.L1VerificationsBatchNo, 1) + assert.NoError(err) + tx.Commit() + for i := 1; i <= 10; i++ { + isConsolidated, err := zkEvmImpl.IsBlockConsolidated(ctx, rpc.BlockNumber(i)) + assert.NoError(err) + t.Logf("blockNumber: %d -> %v", i, isConsolidated) + assert.True(isConsolidated) + } + isConsolidated, err = zkEvmImpl.IsBlockConsolidated(ctx, 11) + assert.NoError(err) + t.Logf("blockNumber: 11 -> %v", isConsolidated) + assert.False(isConsolidated) +} + +func TestIsBlockVirtualized(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + var l1Syncer *syncer.L1Syncer + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + isVirtualized, err := zkEvmImpl.IsBlockVirtualized(ctx, 50) + assert.NoError(err) + t.Logf("blockNumber: 50 -> %v", isVirtualized) + assert.False(isVirtualized) + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + for i := 1; i <= 10; i++ { + err := hDB.WriteBlockBatch(uint64(i), 1) + assert.NoError(err) + err = hDB.WriteBlockBatch(uint64(i+10), 2) + assert.NoError(err) + err = hDB.WriteBlockBatch(uint64(i+20), 3) + assert.NoError(err) + err = hDB.WriteBlockBatch(uint64(i+30), 4) + assert.NoError(err) + } + err = hDB.WriteSequence(1, 4, common.HexToHash("0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85"), common.HexToHash("0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0"), common.HexToHash("0x0")) + assert.NoError(err) + tx.Commit() + for i := 1; i <= 40; i++ { + isVirtualized, err := zkEvmImpl.IsBlockVirtualized(ctx, rpc.BlockNumber(i)) + assert.NoError(err) + t.Logf("blockNumber: %d -> %v", i, isVirtualized) + assert.True(isVirtualized) + } + isVirtualized, err = zkEvmImpl.IsBlockVirtualized(ctx, 50) + assert.NoError(err) + t.Logf("blockNumber: 50 -> %v", isVirtualized) + assert.False(isVirtualized) +} + +func TestBatchNumberByBlockNumber(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + var l1Syncer *syncer.L1Syncer + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + batchNumber, err := zkEvmImpl.BatchNumberByBlockNumber(ctx, rpc.BlockNumber(10)) + assert.Error(err) + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + for i := 0; i < 10; i++ { + err := hDB.WriteBlockBatch(uint64(i), 1) + assert.NoError(err) + err = hDB.WriteBlockBatch(uint64(i+10), 2) + assert.NoError(err) + err = hDB.WriteBlockBatch(uint64(i+20), 3) + assert.NoError(err) + err = hDB.WriteBlockBatch(uint64(i+30), 4) + assert.NoError(err) + } + tx.Commit() + for i := 0; i < 40; i++ { + batchNumber, err := zkEvmImpl.BatchNumberByBlockNumber(ctx, rpc.BlockNumber(i)) + assert.NoError(err) + t.Log("i/10: ", i/10) + if i/10 < 1 { + assert.Equal(hexutil.Uint64(1), batchNumber) + } else if i/10 == 1 { + assert.Equal(hexutil.Uint64(2), batchNumber) + } else if i/10 == 2 { + assert.Equal(hexutil.Uint64(3), batchNumber) + } else if i/10 == 3 { + assert.Equal(hexutil.Uint64(4), batchNumber) + } else { + panic("batch out of range") + } + } + batchNumber, err = zkEvmImpl.BatchNumberByBlockNumber(ctx, rpc.BlockNumber(40)) + assert.Error(err) + batchNumber, err = zkEvmImpl.BatchNumberByBlockNumber(ctx, rpc.BlockNumber(50)) + assert.Error(err) + t.Log("batchNumber", batchNumber) +} + +func TestBatchNumber(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + var l1Syncer *syncer.L1Syncer + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + for i := 1; i <= 10; i++ { + err := hDB.WriteBlockBatch(uint64(i), uint64(i)) + assert.NoError(err) + } + err = hDB.WriteSequence(4, 4, common.HexToHash("0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85"), common.HexToHash("0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0"), common.HexToHash("0x0")) + assert.NoError(err) + err = hDB.WriteSequence(7, 7, common.HexToHash("0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba86"), common.HexToHash("0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e1"), common.HexToHash("0x0")) + assert.NoError(err) + for i := 1; i <= 4; i++ { + err = stages.SaveStageProgress(tx, stages.L1VerificationsBatchNo, uint64(i)) + assert.NoError(err) + } + tx.Commit() + batchNumber, err := zkEvmImpl.BatchNumber(ctx) + assert.NoError(err) + assert.Equal(hexutil.Uint64(10), batchNumber) +} + +func TestVirtualBatchNumber(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + var l1Syncer *syncer.L1Syncer + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + for i := 1; i <= 10; i++ { + err := hDB.WriteBlockBatch(uint64(i), uint64(i)) + assert.NoError(err) + } + err = hDB.WriteSequence(4, 4, common.HexToHash("0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85"), common.HexToHash("0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0"), common.HexToHash("0x0")) + assert.NoError(err) + err = hDB.WriteSequence(7, 7, common.HexToHash("0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba86"), common.HexToHash("0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e1"), common.HexToHash("0x0")) + assert.NoError(err) + for i := 1; i <= 4; i++ { + err = stages.SaveStageProgress(tx, stages.L1VerificationsBatchNo, uint64(i)) + assert.NoError(err) + } + tx.Commit() + virtualBatchNumber, err := zkEvmImpl.VirtualBatchNumber(ctx) + assert.NoError(err) + assert.Equal(hexutil.Uint64(7), virtualBatchNumber) +} + +func TestVerifiedBatchNumber(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + var l1Syncer *syncer.L1Syncer + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + for i := 1; i <= 10; i++ { + err := hDB.WriteBlockBatch(uint64(i), uint64(i)) + assert.NoError(err) + } + err = hDB.WriteSequence(4, 4, common.HexToHash("0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85"), common.HexToHash("0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0"), common.HexToHash("0x0")) + assert.NoError(err) + err = hDB.WriteSequence(7, 7, common.HexToHash("0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba86"), common.HexToHash("0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e1"), common.HexToHash("0x0")) + assert.NoError(err) + for i := 1; i <= 4; i++ { + err = stages.SaveStageProgress(tx, stages.L1VerificationsBatchNo, uint64(i)) + assert.NoError(err) + } + tx.Commit() + verifiedBatchNumber, err := zkEvmImpl.VerifiedBatchNumber(ctx) + assert.NoError(err) + assert.Equal(hexutil.Uint64(4), verifiedBatchNumber) +} + +func TestGetBatchByNumber(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + EthermanMock := mocks.NewMockIEtherman(mockCtrl) + + l1Syncer := syncer.NewL1Syncer( + ctx, + []syncer.IEtherman{EthermanMock}, + []common.Address{}, + [][]common.Hash{}, + 10, + 0, + "latest", + ) + cfg := ðconfig.Defaults + cfg.Zk.L1RollupId = 1 + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + erigonDB := erigon_db.NewErigonDb(tx) + + gers := []common.Hash{common.HexToHash("0xf010e584db63e18e207a2a2a09cfef322b8f8f185df5093ed17794ac365ef60e"), common.HexToHash("0x12021ea011bd6ebffee86ea47e2c3d08e4fe734ba7251f2ddbc9fa648af3b1e6"), common.HexToHash("0x055bbf062f8add981fd54801e5c36d404da37b8300a7babc2bd2585a54a2195a"), common.HexToHash("0x252feef2a0468f334e0efa3ec67ceb04dbe3d64204242b3774ce1850f8042760")} + parentHashes := []common.Hash{common.HexToHash("0x502b94aa765e198ecd736bcb3ec673e1fcb5985d8e610b1ba06bcf9fbdb965b2"), common.HexToHash("0x55a33ac3bf2cc61ceafdee10415448de84c2e64dc75b3f622fd61d250c1362ec"), common.HexToHash("0x88b7f001ebab21b77fe747af424320e23c039decd5e3f2bb2e074b6956079bdf"), common.HexToHash("0x89f4d0933bdcaf5a43266a701e081e4364e2f6d78ae8a82baea4b73e4531821e")} + hashes := []common.Hash{common.HexToHash("0x55a33ac3bf2cc61ceafdee10415448de84c2e64dc75b3f622fd61d250c1362ec"), common.HexToHash("0x88b7f001ebab21b77fe747af424320e23c039decd5e3f2bb2e074b6956079bdf"), common.HexToHash("0x89f4d0933bdcaf5a43266a701e081e4364e2f6d78ae8a82baea4b73e4531821e"), common.HexToHash("0x002241472c8ffeb86cd3c2bfe928cc41a47fdeffa0e98f56c1da3db6d50d29cb")} + stateRoots := []common.Hash{common.HexToHash("0x70ee58f4d74b706ce88307800983c06c0479f9808d38db5d751d7306f510c9b8"), common.HexToHash("0xba46d17db3364a059cc6efada4a1cc7bea472c559247aafdd920fbd017031fee"), common.HexToHash("0x7dbca3d3f5841bb8a5da985655235587c212826b0f21127e4f3470230d05b0f8"), common.HexToHash("0x551b6fdb2b0c156b104a946f815c3e2c87324be35fba14cf0ed3e4c1287d89bf")} + txsRoot := []common.Hash{common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")} + coinBase := common.HexToAddress("0x761d53b47334bee6612c0bd1467fb881435375b2") + times := []uint64{1714427000, 1714427003, 1714427006, 1714427009} + gasLimits := []uint64{1125899906842624, 1125899906842624, 1125899906842624, 1125899906842624} + mainnetExitRoots := []common.Hash{common.HexToHash("0x6d2478612063b2ecb19b1c75dda5add47630bbae42a2e84f7ccd33c1540db1de"), common.HexToHash("0x17aa73f0a1b0e1acd7ec05a686cfc83a746b3230480db81105d571272aff5936"), common.HexToHash("0xf1dcb7fa915388a4a7bac1da56bd37d3590524ad84cbe0ff42d22ec2be8dcb1d"), common.HexToHash("0x63ab7d9f3c87bc4bbcff42748b09ef7cf87e7e084f6d457d277fee13a4759872")} + rollupExitRoots := []common.Hash{common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0x2b911c2ea39040de58c4ddcc431c62ffde4abf63d36be05b7e5e8724056061b8"), common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0xc8500f8630165b35e61c846262a2ffa3cbe5608305115ec2c79f65bbad91d0b6")} + txs := [][]types.Transaction{{}, {}, {}, {}} + + for i := 0; i < 4; i++ { + err := hDB.WriteBlockBatch(uint64(i+1), 1) + assert.NoError(err) + err = hDB.WriteGlobalExitRoot(gers[i]) + assert.NoError(err) + err = hDB.WriteBlockGlobalExitRoot(uint64(i+1), gers[i]) + assert.NoError(err) + l1InforTree := &zktypes.L1InfoTreeUpdate{ + Index: uint64(i), + GER: gers[i], + MainnetExitRoot: mainnetExitRoots[i], + RollupExitRoot: rollupExitRoots[i], + ParentHash: parentHashes[i], + Timestamp: times[i], + BlockNumber: uint64(i + 1), + } + err = hDB.WriteL1InfoTreeUpdateToGer(l1InforTree) + assert.NoError(err) + } + + err = hDB.WriteBlockBatch(5, 2) + assert.NoError(err) + err = hDB.WriteSequence(4, 1, common.HexToHash("0x22ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba97"), stateRoots[len(stateRoots)-1], common.HexToHash("0x0")) + assert.NoError(err) + err = hDB.WriteSequence(5, 2, common.HexToHash("0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85"), common.HexToHash("0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0"), common.HexToHash("0x0")) + assert.NoError(err) + + err = hDB.WriteForkId(1, 7) + assert.NoError(err) + + for i := 1; i <= 2; i++ { + err = stages.SaveStageProgress(tx, stages.L1VerificationsBatchNo, uint64(i)) + assert.NoError(err) + } + + for i := 0; i < 4; i++ { + _, err := erigonDB.WriteHeader(big.NewInt(int64(i+1)), hashes[i], stateRoots[i], txsRoot[i], parentHashes[i], coinBase, times[i], gasLimits[i]) + assert.NoError(err) + err = erigonDB.WriteBody(big.NewInt(int64(i+1)), hashes[i], txs[i]) + assert.NoError(err) + } + tx.Commit() + var response []byte + accInputHash := common.HexToHash("0xd24388f39169a9187e66711e42c8da0dfd9f9089ec46c9a95d29a1f25a58234a") + response = append(response, accInputHash.Bytes()...) + response = append(response, common.Hash{}.Bytes()...) + response = append(response, common.FromHex(fmt.Sprintf("0x%064x", 1))...) + EthermanMock.EXPECT().CallContract(ctx, ethereum.CallMsg{ + To: &common.Address{}, + Data: common.FromHex("0x25280169" + fmt.Sprintf("%064x", 1) + fmt.Sprintf("%064x", 1)), + }, nil).Return(response, nil).AnyTimes() + + txByHashResponse := types.NewTransaction(uint64(0), common.HexToAddress("0x000"), uint256.NewInt(1000), params.TxGas, uint256.NewInt(1), nil) + txByHashResponse.Data = common.FromHex("0xdef57e5400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000066e141dd0000000000000000000000000000000000000000000000000000000000204a05000000000000000000000000148ee7daf16574cd020afa34cc658f8f3fbd280000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000050800000000000000000000000000000000000000000000000000000000000009e00000000000000000000000000000000000000000000000000000000000000cfc0000000000000000000000000000000000000000000000000000000000000fb400000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000018020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030b80b0000000300000000f9044e82674884017bf1a0830bcfc4948ff6508bdd71b535df073920c423e9eaee0b97af80b90424ec0ab6a70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000e0434ddf5273f71250a99c6fbcabfeca301de5f000000000000000000000000f6ad3ccf71abb3e12becf6b3d2a74c963859adcd00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000001a45ae401dc0000000000000000000000000000000000000000000000000000000066e13be200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000000000000000000000000000000000000000001f40000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af00000000000000000000000000000000000000000000000000000000000652e10000000000000000000000000000000000000000000000000163adcee8b87f200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000003d5320821bfca19fb0b5428f2c79d63bd5246f890000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e13be20000000000000000000000000000000000000000000000000163adcee8b87f2000000000000000000000000000000000000000000000002e2103547d3748000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080f1f06050464bbe1899db8975029243860aae9dbc153f3d0bde4cbf423605c0f36b575d04708f9a517c2cfe3c6af0d379c30bca1097b252776609f3c2ef706d681bff0b0000000400000000f901ae82662284017bf1a08308fc58948ff6508bdd71b535df073920c423e9eaee0b97af80b90184b61d27f6000000000000000000000000f6ad3ccf71abb3e12becf6b3d2a74c963859adcd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000003d5320821bfca19fb0b5428f2c79d63bd5246f890000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e13be200000000000000000000000000000000000000000000000001634a3fb8d2af5000000000000000000000000000000000000000000000002e14026ffdab72000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d80800fb8261fa1f177e41f88e7978b3cc48fe720a22ce153ad4bd88febf30f2bf8db3354cbb1d8a684aa733a0c9872a02c28dc350015d9ad47916a5e8d97b365ad8b1cff0b00000003000000000b0000000300000000f28231da84ee6b280082627094d20595ed0ed59d85d9359de5164b320a5c40fa6e875f9a48fae655c8840333e6b882044d80806ad649fc21c968300d2abebc5e256eafba5e069316dd680a6bb4feec0290e85b7d9831647c8d457a29b6efa64a88add3d0ab985436c7155238ed86435f30dc261bff0b00000003000000000b00000003000000000b0000000300000000f9010d821ed48401821fa88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d200000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009814ae9030c504000000000000000000000000000000000000000000000000000000000000000082044d8080f2c8fb37b8417e9a3d7245c8f428d932e87f43877e75b346c3d575a80700110b28ff4f9ac9d7882c8b61cf9929d2a20a8cb62d32aea89cbca06e3021eeaac3fd1cff0b00000003000000000b0000000300000000f903f23b8401620100830343f1941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f52e9f9451b39e2171a782aec26677586f35adc72ac984d0921cc7b9512c744e75000000000000000000000000000000000000000000000000000044364c5bb00000000000000000000000000081f6b887657b679d9e23153ffcd703e1110102400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5867796855613343726552507448414d4c75314a6f47796d6d734b6a66676252464e4b5943644172364541570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004137eaeccfc1a481ec514bda2863333d34977c77f56e4033ea74cef4c22a748db0717b9d2961adbeab964a3963b682bcab163f2cec97eefd02c5df5b66a4e71b521b0000000000000000000000000000000000000000000000000000000000000082044d8080ea7294305dca446fc2918900c201f24486d75b33cafe6e2e8bf369218f229293230e8b67c0bd2b47182ab77ffbaed1d7763fc5705e306d776b0f8f8e86efd88d1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ed5840166e3008307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d3e0000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982adc32888842000000000000000000000000000000000000000000000000000000000000000082044d808067fffeed9f7553babaf6570c8a11233f004d0758170e5d23484c095da5ea282b08682443b3ea253139b3ac01977c0a758f081fd6cd614a24205c1d67cbd9afbf1bff0b00000003000000000b0000000300000000f902ee826181840189ad40830804df945523985926aa12ba58dc5ad00ddca99678d7227e80b902c484d61c970000000000000000000000000000000000000000000000000000000000000060000000000000000000000000292fc50e4eb66c3f6514b9e402dbc25961824d62000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000001a4c23a4c88000000000000000000000000000000000000000000000000000110d9316ec0009bdafe8da6cde01e6040b2d2187f6b67a297205ae12cbfe9485196bfa1b3a8374f9dd0dda9066451633b471b04af8ef8204abfe2693c8bf8c450bc20dcec28d300000000000000000000000063e99e508a9f2b6ed180811f80db90ce7417426c000000000000000000000000000000000000000000000000045f2a42a47981960000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000f02bbc9de6e443efdf3fc41851529c2c3b9e5e0c0000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000447647691d000000000000000000000000000000000000000000000000045e1f2d2e761b0b00000000000000000000000063e99e508a9f2b6ed180811f80db90ce7417426c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000411e413bf834084915b547cc0d72d37e10a33d19426323e626c254fe8663b6984d4cc14841f43d8ca4842e10ab2fe3995cab429c30a54301522ea3a5a5272a91871b0000000000000000000000000000000000000000000000000000000000000082044d80804f5051f8a0057498cd6d2af0d1628a32348d13945f6bbdfd570b3544c7a08f8875aedee2be658959e23184a59bf6b92b43ee0fe5934f93f28b93ce4288de1f6a1cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903ce82264a8405f5e1008307a120943348053b13e22b2717c742cd4a60f5040564a53080b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300010100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000d23aaa8116edf941e1aba78b113d7a670002ce4a0207020601080400030509000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000023c2b0000000000000000000000000000000000000000000000000000000000023c316f00000000000000000000000000000000000000000000000000000000023c316f00000000000000000000000000000000000000000000000000000000023c5b7000000000000000000000000000000000000000000000000000000000023c7e8900000000000000000000000000000000000000000000000000000000023c828000000000000000000000000000000000000000000000000000000000023c954000000000000000000000000000000000000000000000000000000000023ca99000000000000000000000000000000000000000000000000000000000023f57ef00000000000000000000000000000000000000000000000000000000023f59fd0000000000000000000000000000000000000000000000000000000000000004650755b20d11b5cde1a5e1f721e10c74f5f078fac6800341d15be87da2e74a3c5acb65047658a523d0c57d88d94ca1d78f66ccd21b9b203f2df55700919e11bd2fbf684aea0d5dc6ef5cd78dc496afc655c627eb2e2da675143859fb439860329edfbc8fc5731ef33d3d50efcd525f27181b84a44fcf726ca066ebe622c15866000000000000000000000000000000000000000000000000000000000000000421bfca8453ac26e096cd1e78f31336665f46d385c6333d63a813a1bee82a0d97248c4d7624257e93bfbb42e7a8b901568660bea31df9cccf80f064263ac1977e495fb904f2b8477e6114f459116561463c2fb93122fffff08adf8c85aa5850f657c7fed085522823081dda81d1bd94235fc42fd1e70f70b38737760887e1ae0c82044d8080da940144b03a145e88a116e125b1a24eac784a85138a43a7bf4529d1e5fa33751fc270dd6380301802a94c1c6f1d398f5684f56ffd1d974231bcbabb1fe9ad791bff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ed684017eeb588307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d5c0000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009824b00e503048000000000000000000000000000000000000000000000000000000000000000082044d80805a0f08064fc456e60e242cee4f20ecde976a51c7e1c1b1245922d15696c32cba65dbc28c8984d233552830250b4be646f523d65f1e686aa09b038769469d40ec1cff0b0000000300000000f8b12f840238e8a08304ac1194222228060e7efbb1d78bb5d454581910e392222286352f5cb5673bb88432accbb900000000000000000000000000000000000000000000000000000000000000af00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000271bb7b9b0000000000000000000000000000d4ebba85fbd618d08c6911e6bea0e514280144682044d808080e4ceead86b43aca84d560e925df839a58fc08296edf87dc44985d38e1312113a39d3f053ece00a9741ef979d9b0457a11dc7b65ad04dc117ad9a49c52987901bff0b0000000300000000f13d84016caf608304443a94ee1727f5074e747716637e1776b7f7c7133f16b18732a5492d0cfb90841249c58b82044d80801633bcf1a8f14729079a42e6e7d86f2e304924ea015e3f1ddadefe9e31869263170df9e3efeb2ce79a7a320bcbcd31acf77e4d0a33fd4945f0d6724d688b88241cff0b000000040000ae44e94d8402faf08082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080317339510effaa798bc532fa32187f09b40c9b5cd12ccbeb7f7ab3fd3c157e680721490b51e44c59435ea2cd2143d71ad2b6316ae19d4055cfcea37d7a2cb9eb1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9018f830151d184018715308302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000010000000000000000000000003e0103ff5b7f4b338a8f977b35846aa7c01ed379000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000001635044eda5700000000000000000000000000000000000000000000000000000000000000014760dfebac75f07fa46b8e87d9151b673b8e33d4710293497a4a5c9524aedc8882044d8080a3566b6ff106e89842dda24f38fbc6ee0a105588e63240914f04986798835bf030b2f4d65e86d43514d0c03ee84e5c46a84fc637ac927233674c4cf7effe31101cff0b00000003000000000b0000000300000000f9010d821ed78401754e688307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d7a0000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982405427dd941000000000000000000000000000000000000000000000000000000000000000082044d80808cdf22284f15949ea3fbb6eeff81ee87a198722c1f8f827b1ce98ca30138b756444a20bc97269e363da23b33ee1badcfe012964ab8e73ee9c70f6f4af5ae99351bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f902ac258402625a00830494ba94b89a6778d1efe7a5b7096757a21b810cc2886fa180b902843593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e13c4b0000000000000000000000000000000000000000000000000000000000000002000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000376f2930000000000000000000000000000000000000000000000000057e3e6c76eb74600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000968980f6c8b41abeeadab481578614ad405f58890000000000000000000000000000000000000000000000000057e3e6c76eb74682044d8080388b027ca9400e25178e592726a62b4d39a6172d956bf11474ce49f65b0fa8a426f0e323ac7e8de7a4daa322ca42fa3c2cf66081047319341febcf2a8859af3b1cfff9028c208402625a008304932b94b89a6778d1efe7a5b7096757a21b810cc2886fa180b9026424856bc3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000039bac84000000000000000000000000000000000000000000000000005b8731fffd99f800000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000006ded4acd9fd87eec14f18b22e9046f000be0685a000000000000000000000000000000000000000000000000005b8731fffd99f882044d8080b53c40cb340c035c10a519f25f2de3fca0cd253015c28e7049b9d485ea98b1a5021c6938342e403283007f2ea6a9152a9089428f3fdee38461164b0fb89d690d1cfff9028c2e8402625a008305581d94b89a6778d1efe7a5b7096757a21b810cc2886fa180b9026424856bc3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000004710b280000000000000000000000000000000000000000000000000070a92a16ee367400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000e3119ac2c91cf8e2ca81b54d757c166c53647a2b0000000000000000000000000000000000000000000000000070a92a16ee367482044d808008fd8af756bc9385919f74d9a9573a3581faf800c7999d51e834423a5daa4000067f947784576968c91ae28a653c76771a15035f088241705df3780d3cb9df1c1cfff9028c1e8402625a008305581d94b89a6778d1efe7a5b7096757a21b810cc2886fa180b9026424856bc3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000047054780000000000000000000000000000000000000000000000000070971273bec4a100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000018c4dce3c0172a5322aefc459d69e85957d387890000000000000000000000000000000000000000000000000070971273bec4a182044d8080ba9906ac2e50febe56442a09ea73b14880b4f418e30f891402dedfc36e18e57b3b7ef3466f11fcd657b0d99da14c32249962f8f505656ccf48f0a5fa85946c2d1cff0b0000000300000000e97b8402d112408255f094e10add2ad591a7ac3ca46788a06290de017b9fb48084632a9a5282044d8080e2aeb5078a6bdcb30be8415f3bf338183d2b725380bb85cc21596c775e414b103b7de8fceeeb38b5089d6af9f7f4862f805c332651b87bd2c444f16180db50ad1cff0b0000000300000000f8b6028402c9ee9683024b46943a23f943181408eac424116af7b7790c94cb97a58701c6bf52634000b88800000182ad69fa4f0000000000000000000000000000000000000000000000000001c6bf52634000000000000000000000000000b675ed9441dd8ac6d3a206ab61357bcd47384c6c000000000000000000000000000000000000000000000000000000000000e70800000000000000000000000000000000000000000000000000000000000000cd82044d80808493c0e815b42ccfaf9a06a6ccf16f1e445a0412aab8f360dd19929adec226d2431b3cb925d2fc60b325d59f97e35b7b52ea4ef006860596f1b9d79afcf1d0181bfff9010d821ed884016548d88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d980000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009815ae8ed77f3c000000000000000000000000000000000000000000000000000000000000000082044d80808cfb084863d1694fdd8775bf4fac4410b1b9cf98c1201ba0741106879be8d41f36ca97ecaa1eb058f23208921fcfdde1cf82299c336c2e9303c15865d9a036311bff0b0000000300000000ee8308be5e8403fcd02082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080a3271eeb3e1a9ca9fda4117e8abb72638a128fdce0cb2efb9ef2f870f60b2f672741f52279c993542e2b9d52d8961b2ce486f5498db46deb246b5916a379f8b61babf9014f8301154b84019853408305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13b4200000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000007331f3647a294a90a1ca61c0000000000000000000000000000000000000000adf85b68ca582b83dd4bebe00000082044d8080b8cb2d6ac52d01ef955c17584d1abf7462b3014aedde6b0beeac237e3ff3bc104ae81fd1a2adbc78ec0681dfaf235f3ae18164c3af60a6f02dc9f6100818119f1bfff901ce82989884015445608305ea4c948ff6508bdd71b535df073920c423e9eaee0b97af80b901a4b61d27f60000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f40000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e13c6d00000000000000000000000000000000000000000000000001634ada69799d20000000000000000000000000000000000000000000000000000000000df1806400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080768c51f79db3186f54b0754da1b78ab5a79b6fdf23109910fd4cd6cd9d7f3c8c22e2c8aa620d0d71b4b842da4b0742a4e67da2a0df0aaa08b3cb5d4d1a5955931cfff9012f83018d5e840154456083069df4941b81d678ffb9c0263b24a97847620c99d213eb1480b90104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f400000000000000000000000023abcb82f468e3422a7212d4ad47cc2dd39162a30000000000000000000000000000000000000000000000000000000066e13c6e00000000000000000000000000000000000000000000000001634985c6047d20000000000000000000000000000000000000000000000000000000000df17315000000000000000000000000000000000000000000000000000000000000000082044d8080e56167524cec085263787a173ce728e393d5f361abcfcb92098b68de4fb3a7bc72ae91803810eaafb610b3c66b581c91c6474b137de853eacc2c8d7b73f5b8da1bff000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d7a0b0000000300000000f86b825b2a8401764c50830d57b69498d7c628086262ea7bd83a23ccbc72f8cd10cd8480b842d43b9dcbb61e6ccfbcfef9f21e1bb5064f1cb33f0503849c0ae884bfdc14dddeb7cae95494f3684148550102d6efe114c9b6058a20aab759e064f50544590914050382044d8080eed135980b8038da4e871a8e95e5644a40cb85fc3f97c42a3b8c9aeac897dad368eff35e0c6aa77b54885fbe3528e9b9a9e5193d3142d67226825f6e2b5fd8251bfff906ae8216d4840176a36c831396f094063bac84221b5b02b92ceb740ac129db0edfedc780b90684f740f3280000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000005e4e7d7ed2800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000003000000000000000000000000d63b7691dd98fa89a2ea5e1604700489c585aa7b000000000000000000000000d63b7691dd98fa89a2ea5e1604700489c585aa7b000000000000000000000000d63b7691dd98fa89a2ea5e1604700489c585aa7b000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb140000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb1400000000000000000000000098299ead1cd04fbf1659799b14a559206f3ed165000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000066ea75c3000000000000000000000000000000000000000000000000000000000000002b4f9a0e7fd2bf6067db6994cf12e4495df938e6e90001f4a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000066ea75c3000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500006437eaa0ef3549a5bb7d431be78a3d99bd360d19e500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000066ea75c3000000000000000000000000000000000000000000000000000000000000002b37eaa0ef3549a5bb7d431be78a3d99bd360d19e50001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080746ae602ceaec3387a089510db6ba15a6a45a6b60aeb564d2af783a2249308d935f550046db0988101d4d533b7d390b764e1bc7afc641fdd957f1c421e5c50491cff0b00000003000000000b0000000300000000ee8308be5f840158d94082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080fb504691bbf5c90ff09420f32385c4df07792dd1f0017568c765f1a180386bd54529e1b0819171dbced80e60b965c78a1afd887ad60450c0851fc4472a1aed711baaf8ee83019e5584019dd18083035f30945eb6b3db915d29fc624b8a0e42ac029e36a1d86b80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000ca0000000000000000000000000000000000000000000000012fb937557964d13c00000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000000001082044d8080bff5978438909bd3d0bf26d170678ba5ab948afebb9ed51824a16fca21bc2196068165970f68315889bcd05a63d9f7405722b2f0fdaceda25eb59c93b897e6d41cff0b0000000300000000f9028d818f840158d9408303a82b94678aa4bf4e210cf2166753e054d5b7c31cc7fa8680b902645ae401dc00000000000000000000000000000000000000000000000000000191dfe1d94000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104b858183f0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000004786e420000000000000000000000000000000000000000000000000070ec0f4c9ee97e000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004449404b7c0000000000000000000000000000000000000000000000000070ec0f4c9ee97e0000000000000000000000004abf4710e38fd914212ec0d30026d0fa9d51d40d0000000000000000000000000000000000000000000000000000000082044d8080d6fefcfe30a6a3903219dbcf5937bfd3ee6ee34564e643242a790393324905217ad5bb7e3950286ec8f627314e6e18d8715ac80379c8f7d36eda1672d0bb69481cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ed9840156dd708307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13db60000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c8260d3d200c7d000000000000000000000000000000000000000000000000000000000000000082044d8080098be2bc098d407c1a9d7cadf5840c2c250e338538dc1ae0f5df4ed073f8a85414e47ece3ccc969f95ce9251738051c03ae40575075b9b4b4309210dcfbe44411cff0b00000003000000000b0000000300000000ee821a198401793280825be09482ce01bba0c29bf9b61dc9cfa7968b1e0891f5678702f89f754a380d8082044d8080b2c54612390fc975b7d024f6e8eed9942a73c0d2101ebb04df518e3744db33b4009c3b2e21252b7c462b71a2c34f8a40ca9f84bb30ab6fac7b692840b383dc481cab0b00000003000000000b0000000300000000f9070e8201138402d33c58831d10fc946b2c0c7be2048daa9b5527982c29f48062b34d5880b906e4b80c2f0900000000000000000000000000000000000000000000000000313282fe1a7140000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e5000000000000000000000000000000000000000000000000000000000025b4a900000000000000000000000000000000000000000000000000000000002548190000000000000000000000000000000000000000000000000000000066e1497b0000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000006c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000025b4a9000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a500000000000000000000000000000000000000000000000000000000000000018000000000000000000027109591b8a30c3a52256ea93e98da49ee43afa136a80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a5000000000000000000000000000000000000000000000000000000000000000100000000000000000000271007a42e9f31c1fb7c2056b040e55e35c2ea6744770000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e5000000000000000000000000000000000000000000000000000000000000000082044d808057853a22fce3f85fcdd486d18e841d491de09b35e2151168a3af8bd9352e5e616a70f07e3aad14a6760f82ccb45077001a395a1c9a5556c0d33241225108ab081bff0b00000003000000000b00000004000000000b00000003000000000b00000003000000000b0000000300000000f9010d821eda8401607a608307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13dd40000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000981eaf19eb97c6000000000000000000000000000000000000000000000000000000000000000082044d8080bbaae2e5f840a60b75b397d51ffd3f6c648b7077d6d6091491237e9efb69d8c91620f7793c4dddb315cf997662a77b2c48cbbf881985c642d3cb63c4528a02b11bff0b00000003000000000b0000000300000000f02f8402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109875543df729c0000840333e82082044d8080e79b7308819654ce4d40697f7c1f044a00ec7796c73596957de663e9274d072a76e03b8eb9732d95e673501ed9cad4a63a32d33aa256caafc4f1867fb13eacc41cfff0268402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109873c6568f12e8000840333e82482044d80802cdb77771d166fa9140bd3ea0d17bdc5eeac3836d05d6144612c0434af52b5db2aeac6d0a8bdead3238ee12124dd9f387388ab5b52ab1100a1dadd6a8e9b740d1cfff01f8402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109874a9b6384488000840333e82382044d80806cd1e42d4d065497ce59e511d21e6d5819bf91ea9fa250786b757e0705f356eb5c43ae1da48a134429a5584590f1e4580d103310c3806cfde466a9c5231e35eb1cfff0218402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109873ff2e795f50000840333e82282044d808084e8d0f689fe1d5c190a4247b452a33fd6735de9d5f9f5003d8601d5b0c707d631c33cc82b2690e5df6efa0c2f44d68f62d47cd0922a94f802cdbedb115ee30a1cfff9018f830151d284018c1e408302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa0000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c201fab6ebde597669e39a9a69d724998e50b2f8000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000001630804d32c10000000000000000000000000000000000000000000000000000000000000001b75f9deb5f8d72d9cd3ea0037c11ac2401d310730bc692f37ec19861105e08c582044d808002793ca3270d2bdb8a4efb0eea532dec733de8f35e5a1c3f90bf185ed69c73a973cc8df40252607608de8728eb43d5e9fccbdabc4fce80c5d4fc858a36e928ad1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ea81808402625a0082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d80808805469c3fb84413a35485f506012ad1a6820b80738b3bf685b1d6175b9dfc79397f9b692337a0f633040da8401f8a5fc53852106d916911af513ff271e4aa261bff0b00000003000000000b0000000300000000f9010d821edb84017a1ce08307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13df20000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c82f0c5cacca38000000000000000000000000000000000000000000000000000000000000000082044d8080baa772220471b8149d55e5a5ac2bfbdd12c03eaeec654e08d9c74ec43b63d12a469bb9de8bf8ff2cd2edd8926daf89b887df334862e623a68cdc3b90bf7ba7391cff0b0000000300000000f84a318401681b808312c61094ee1727f5074e747716637e1776b7f7c7133f16b180a4db006a750000000000000000000000000000000000000000000000000000000008f58afa82044d80809489b2c6a4c5dc17ec3ec50d299e487c996839ed4d26bcc4c07f910dc21a53c04ff08798dea40e018cbd296ffa623776cc5382be8e8964a7383b1c8b9717ba2b1bcf00000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004cc20b00000001000000000b0000000300000000f86a228401681b8082b4e5940d1e753a25ebda689453309112904807625befbe80b844095ea7b30000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b500000000000000000000000000000000000000000000000026e055297acfc7bb82044d80802d542b44996d63d0e2ee4f8063c5c21b00ed851ab3f63911b2e0d4f9422287497b5085cca5dab841c5406c9bca6745b50a28a543431c355ee63d7e469e7b72971bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903ce821bc68405f5e1008307a12094d83729a90474d6ae09502ee3c58485cef40ac87280b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300010001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000093136a0f83405fd54b230fa9778a13300002cdfe0409010405000602070308000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000038408d1790000000000000000000000000000000000000000000000000000000384669fe00000000000000000000000000000000000000000000000000000000384669fe00000000000000000000000000000000000000000000000000000000384941d490000000000000000000000000000000000000000000000000000000384f1bda900000000000000000000000000000000000000000000000000000003850842c800000000000000000000000000000000000000000000000000000003851ac5b600000000000000000000000000000000000000000000000000000003851ac5b600000000000000000000000000000000000000000000000000000003853adfd00000000000000000000000000000000000000000000000000000000385578f900000000000000000000000000000000000000000000000000000000000000004208140e5e3dac54f5eb8cd8211adb203b85cc8f48b5d7d5768ecae9622df24a03866b2325c2c74955907e637e920627241ee7d4220e6aa6fe4b474fa0a1467c4ed42c8afac6f30478534bfd8a1b58ede8bfbef3d1908c0288a743fb6aeb7c12ae89fd84ca1a67ee72ea3d922c892a66e7caa0994879cace58eaf8e30ec0b641c000000000000000000000000000000000000000000000000000000000000000469aea40a060cd08cdc062fcb2e3a8b1b45faa0ff08b58607ff6941f18a2561184328d4b3414cda5fc8a80dcd28dee321564a1c1eaffabd2601892446321625b1324fff5e231ca6e5a020a57e4b2c8b83eac3067b76fcdfe8f990509b8bd6ba882f431699ec517fa2f99ea179d76f3f872438f6d787d9e6704c52145bc16b31b082044d808039cd808d19202040e8c498c2ab13bcdf3af0b148bac34a026b1925ccf02cdd89763a910a26e0d8c91449b9d39d62aa35f116b0f303de9ddbe0adb211226be4bf1bfff9010d821edc84015d46108307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13e100000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c7fc160522df2f000000000000000000000000000000000000000000000000000000000000000082044d8080bc426dda601756b4851b479be56dca7cbdcaca215314342d4e5cbefc7caeaa3b2115f87f94ec752953f052e46ffb2dea9db847ebabf21a239845e387e5d24fad1bfff86c82011484014ca44082ebf49437eaa0ef3549a5bb7d431be78a3d99bd360d19e580b844095ea7b30000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae0000000000000000000000000000000000000000000000000000000000f0719a82044d8080511a80bfb3d3116410992606d87913954e8731ed90a5131f21de84e035939b8273d0b786dc03ce855c47f5af2ca93567b0db3ed5fddb3f6cf5c19f8930e7f9d11cfff903ce8222f28405f5e1008307a120942e8932d5662ad426fe335162b233680a524395ee80b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000086e0a443f1c658191e265ab8658a37b300000a6c0405010609000708030402000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000023b4b9200000000000000000000000000000000000000000000000000000000023b749c00000000000000000000000000000000000000000000000000000000023b749c00000000000000000000000000000000000000000000000000000000023b749c00000000000000000000000000000000000000000000000000000000023b8a3900000000000000000000000000000000000000000000000000000000023bcb6800000000000000000000000000000000000000000000000000000000023cc1da00000000000000000000000000000000000000000000000000000000023db84b00000000000000000000000000000000000000000000000000000000023db84b00000000000000000000000000000000000000000000000000000000023f6bdf00000000000000000000000000000000000000000000000000000000000000043ff63dc58a98b2caba46ba7a58cfa6b0378efd7ffd05ca878dcb191d92a850679b626656e08c2977addd9799a357347e31d4965d125bbe06a7557899cbe5a7206c636624e5636f4f3d669960a863e778fc8a1d29779990238c08bc3a3f5df2908936ae2660fed387a6272501cd4e7f16a25ef0a5c05cce014666dacfb6e85cb0000000000000000000000000000000000000000000000000000000000000000456cd47dcbb6b4c21b5c5978aa6f26fdda9fc62555429ef0a40b98205fbd86617648c9b624b5c64efe94ed0c11d54cd14a1ef04c3bb18f4d0945a8e88ec84fd62470136dc0befd1267388d059cd270f7f0a706eded9d9d4c6f4578fa869525ef45073a43bb30b1cbaa3f4affad5b05288b4e0a8f0724deba4e6024f235cd711f182044d8080bf100f793640914b3157343b839411911773ed9a2a463b79f1ae0d6295f64f3f105d96335e24c7519de149fb1767d2c21c25f7ff0718c9ac0acd017449d799141bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f870058401a17b0082bd6194555a64968e4803e27669d64e349ef3d18fca089586b1aca8f532dab844e56461ad000000000000000000000000000000000000000000000000000000000000a4b10000000000000000000000006587b69f97af1c9e91455401cd3ea5594590bbf982044d8080fccdafcb3644954a5a889c5a673c6180b646da17f832cd853a74a6c2413cd51169f1cfabe17e29cdaebbcfd961d06fb56bb2da7b4b11f7cbaf137e84ec327a9f1cfff03284015be680828d9794a06e1351e2fd2d45b5d35633ca7ecf328684a109878a6f0ad96bd73e840333e8c682044d8080f16b6fa1af75307cdea0f6fa7170d8c353bbdc1af5099bd4b4b69f1ee48a4aba2fecbab3e4eeda03128b8308f6becfd6fddee743bde4b24bc7fe32fc0c9fee2a1bff0b00000003000000000b0000000300000000f903ce8222f38405f5e1008307a120940e7197e7200a706e4b8e0ecfd39ef8c45cff142280b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000b62c61f914e58e40747a3fd731525dd20002ce3b0308090106020403070005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000003e5e21b4000000000000000000000000000000000000000000000000000000003e5e21b4000000000000000000000000000000000000000000000000000000003e5e33a8000000000000000000000000000000000000000000000000000000003e5e6e40000000000000000000000000000000000000000000000000000000003e5f4130000000000000000000000000000000000000000000000000000000003e5f4130000000000000000000000000000000000000000000000000000000003e5f5904000000000000000000000000000000000000000000000000000000003e5f5904000000000000000000000000000000000000000000000000000000003e60a0c0000000000000000000000000000000000000000000000000000000003e60a0c0000000000000000000000000000000000000000000000000000000000000000427c6181796e378ecce0eb4756eb5cb3cb22c2f5c31ec76afbb61cdfc9f96c2999ac5dc98c1619fa6401ffa39d5343d7d5b3d32c7c84f294e8caea56fbd7912240fd00d9938f0a0f896ad6a9aaaad9ec782ae47546019d0b2bd0be88287821fe235539a12cc61ddf8f127b192a2724d095088635a3acaded33c14f154ca65ead300000000000000000000000000000000000000000000000000000000000000040217a78ca7b9fc8eed7813085e982f6e8d699f8d548699c4c401f9b74f2bce452d0d3f0f47a6220d36ac59f3c8cddaf9a270e932a234e473bb8507e1a7bd5b6b33cce711f84eaeda3afd58b99e2149fd24ec6aa607bae119a4b358c204fd90b6414015a5b4d6efe234bfb3d0e3b2e1042364cce80bf3286ea367d06ad7f0e27282044d808072d67c06cd88db7a351633d59042a3909e3564f6d707240357850526ee6c561a60ce10685dec1ad08cde99aaa47a3bcbd8f7bc647e08375d873f7a2584478b391cff0b0000000400000000ee8308be6084044f35e082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080813ec45766e4b4c8838d0f69507e663a3ae1280bc1021a063d71381ecd29f84302e1bcc769c6d76488c0346c2069ad8d142fcbdeb8a15ec4f729a2a14c0515141caa0b0000000300000000f9064e82011584016fbca0831143e7941231deb6f5749ef6ce6943a275a1d3e7486f4eae80b90624ca360ae0000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000631cd561b0c80000000000000000000000000000000000000000000000000017769505dbc5db0000000000000000000000000000000000000000000000000000000066e5304f0000000000000000000000000000000000000000000000000017769505dbc5db0000000000000000000000000000000000000000000000000000000066e5304f000000000000000000000000bd72882120508518fcba2ae58e134ecead18d979000000000000000000000000710bda329b2a6224e4b44833de30f38e7f81d5640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039400c73445066cf0991b56490031d9a8241645cd8c8532c84dccfc1a95f295600000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002fa5fcc93ac9b373ef0065708330f7cac493fea70000000000000000000000000000000000000000000000000017dfac08f2f2400000000000000000000000000000000000000000000000000000000000002105000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003686f700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6a756d7065722e65786368616e676500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000057bffa72db682f7eb6c132dae03ff36bbeb0c45900000000000000000000000057bffa72db682f7eb6c132dae03ff36bbeb0c45900000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0719a00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001a42646478b00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000000000000000000000000000000000000000f0719a000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000017dfac08f2f2400000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000b20237eaa0ef3549a5bb7d431be78a3d99bd360d19e501ffff01849c0ae884bfdc14dddeb7cae95494f3684148550157bffa72db682f7eb6c132dae03ff36bbeb0c45901a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03501ffff019f37552b87b68e7f169c442d595c1be7a0f03b920057bffa72db682f7eb6c132dae03ff36bbeb0c459014f9a0e7fd2bf6067db6994cf12e4495df938e6e901ffff02001231deb6f5749ef6ce6943a275a1d3e7486f4eae00000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d80802787b973f4d54062c810118dd824524f22139a2f95fc0c1847abfa0251e073ab71313f805daf772a06c1873d8390356030408d7cfaef18b7200e9f246c2012e41bff0b0000000300000000f9028e82393b843b9aca00830667f8943a464f746d23ab22155710f44db16dca53e0775e80b902647c39d130000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000012700000010000000100000001000000127000000000000000000000010000001170000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76c46702575bb24a61a3c6674a33b1c536418d418d7a2a5454760d0425f62a69690005804c0f4171353a8d70c267c85f228b5795422bb97e8701f57060b3afcacbda96828451185401ac923c55ed67ced54306dd4b6391546b221f111a5a62b01bc3c144011bb991df3e06a3f746ab38f98556f8e11b36c05ceb94aad204dffa5c664e9bd463791cf2b3fe4687153754968af9bcd9a70b8d25d31afef5e268e9aabb50aadf7d1ce0391fbf3eb6e6cf69572dd19bf6fc5081a462f752638d846b9d0862a6cc1dd706aadce7a16fa076ac160a7a7e59233f0659153b2efe2c60c5f302eb7cddaa4a1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d030005804c000827500000000000000000000000003ddc189d48a792d2c6262d20aa0f9f78e88a9d470000044d0000000000000000000000006ae540f1c3a96a8628d52b416ec1bf72932f83fe0000000000000000000000000000000000000000000000000000000001c9c409000000000000000000000000d2799cd96798074f709423369bf2fea60392852a0000000000000000000000000000000000000082044d80805b9b901758bfbabee0b9f3477ea901ad9b36199c881c5fbcb1fa4d241495f353045c800c11c133aab3e1fe1dd8de12d11392a42fffef25507c70ded109ad73891bffee8308be6184014b1da082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080406d26cc0725e8e4ec416f42fd563433cafed607432c1bf19783e749e2a8ad8939ea1ad189a766c6759c4a5210377b7cf68cff953b4447bf715010dba204d3dd1cab0b00000003000000000b00000003000000000b0000000300000000f9024f830151d384016c3a308305cae894555a64968e4803e27669d64e349ef3d18fca089580b902240ddedd8400000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000001ee4c000000000000000000000000000000000000000000000000005d423c655aa0000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000662d6dda64cfc7fc21d2b1005f406e984a178d4b0000000000000000000000009478dcdb5e5c190fb37f70358585430e1bb1168e000000000000000000000000199bee3bc56add7f1a6ad1995002730de45886d60000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000015b9a6cdab510000000000000000000000000000000000000000000000000000168458403b1f00000000000000000000000000000000000000000000000000001377a57a748600000000000000000000000000000000000000000000000000000000000000039dfb46e76740a84405448ea66a1d95618c9528481cd2730100d191ce78a360a487e98381ed3d5acc9ec5f7f31382ed1c794017648b3c7d25e8a8818173a65b8e690d56cc7f2e91fb3994a585e6553c4ce62cda42398be3ba59f1bd342f43dc4f82044d8080ea85b1543ba847d93e70fb47b0750c5197ebdf75f4efa28b37da97e41e68c12a57b1de35882e372f52bf5a16179a4ac2e453025d3b4731b212be3c2ddb3354b71cfff908cc2384016fbca083053197946131b5fae19ea4f9d964eac0408e4408b66337b580b908a4e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000006200000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000d1e753a25ebda689453309112904807625befbe000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000f91f8300f8200f43eecf89e7229f257f085ecad50000000000000000000000000000000000000000000000000000000066e1407c00000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004063407a490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca300000000000000000000000058684788c718d0cfec837ff65adda6c8721fe1e90000000000000000000000000d1e753a25ebda689453309112904807625befbe0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000000000000000000000000000026e055297acfc7bb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060a4587899c58e7cc2e54303188f56acecaea4779400000000000000001388000100000000000000000000000076ae48a40000000000000000000712ecdbf54b4d00000000000000000000000048b8419b2bc0fb63ee96e3a370e30b200cc2e6720000000000000000000000000d1e753a25ebda689453309112904807625befbe000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f91f8300f8200f43eecf89e7229f257f085ecad500000000000000000000000000000000000000000000000026e055297acfc7bb00000000000000000000000000000000000000000000000000070f4daa5dc3780000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000026e055297acfc7bb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022e7b22536f75726365223a22446566694c6c616d61222c22416d6f756e74496e555344223a22342e353935383138333735373437393135222c22416d6f756e744f7574555344223a22342e363531333730393136353932343332222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a2231393931313333333438383432333137222c2254696d657374616d70223a313732363033363934302c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a224968587873792f325642344f4a6366357074564f4a524472566676336c7941426173386e4e3151446e537770426b384765546b6d79637532347a4d4f58535a6b5059672b4d53394a6374336c79587745766b3679382b572b6b303643332b6b4369753337654d49484b47436c6136742f7377416b72625773337177322f474378536639325973353038754f764e6446624f5877705a6c3374794d30745833574a724c616551426c4d42466b77664f5632445168596c32716e41494e344563655a574c4a456a555865417545694a62685a39534844312b76746a4b457452355056796a4472727563476b54634d35745739523549755653554f754e6434632f39766c537269343569464234456f37316e424931624d2b446e78495837563137792b41634d6d585235336d7a685645476b59647848745a6e706f336b776f683841356751786148433678362f4a4d6e56686a5049716877673d3d227d7d00000000000000000000000000000000000082044d80802ca8348995a238faba4d03fc97638947ea65044c08547e8bb931231fb982886b1e4851515149a0b4f1e5131cf5f123de352c7172839852eef079aeb26bdcba361cff0b0000000300000000f86a0b84014b1da082b4cb94c5015b9d9161dca7e18e32f6f25c4ad850731fd480b844095ea7b30000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae00000000000000000000000000000000000000000000000098dbf0792f215d3882044d8080e1a347db720494ebbc892aefb0db8be1c1a4f61b3310d9528abde1ae2ee1cdde24efc0ecb27d55991eb644ba770a91c52ab8e69616ae78fdd3557d4c7122fec91cfff8b10784014b1da0830151b3942a66b9c88caebe9aefc4ecd7b5e7e60881f7e15c860d18c2e28000b884056b01ce00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000007ce66c50e2840000000000000000000000000000000000000000000000000000000000000000000a63727970746f72616e6b0000000000000000000000000000000000000000000082044d8080cf67643e57fe0e3d537c2c8fe40440a97de31e6606b6968a34ce382900b587c128946d1e7505038bf50427502088c8de36e7ebe911171ce1288c5bec04d91f521cff0b00000003000000000b0000000300000000ea10840c49018d830135909468c89f28d223be23967480c06057d22b3400293580845646c6b982044d808002b9b94c0a76cc3e16e09a8b9e9506f4d3f57ab9e8932717c80659bcc9837d0e631d462ca21abb98fd5a0c63877e2d59a8d332c10d30569ced47e5924f2100ef1cff0b00000003000000000b0000000300000000f9068c0c8401e915ba8307f1fc941231deb6f5749ef6ce6943a275a1d3e7486f4eae80b906642c57e88435f1a3a9536b95a72ff7601f7df30a5b8da95a0580587d62f17457034d663d6400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000002ac8900f549d90f11aa593db90a0fb57e753b805000000000000000000000000000000000000000000000000001087372423cc6f000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000086d746f7073776170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863346639646134323361626336393237393937383338313533656131386631616266396435613938000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000b49ead76fe09967d7ca0dbcef3c3a06eb3aa0cb4000000000000000000000000b49ead76fe09967d7ca0dbcef3c3a06eb3aa0cb4000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd400000000000000000000000000000000000000000000000098dbf0792f215d3800000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000084eedd56e1000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000000000000000000000000000004e438d8deb1321000000000000000000000000000000000000000000000000001390e3637ac4c8000000000000000000000000c4f9da423abc6927997838153ea18f1abf9d5a980000000000000000000000000000000000000000000000000000000000000000000000000000000057bffa72db682f7eb6c132dae03ff36bbeb0c45900000000000000000000000057bffa72db682f7eb6c132dae03ff36bbeb0c459000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000987a1c083dbb854f00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e42646478b000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000000000000000000000000000987a1c083dbb854f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000000001087372423cc6f0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000f402c5015b9d9161dca7e18e32f6f25c4ad850731fd401ffff019bc342259cceda0487e70b71a3161f002a95f0e80057bffa72db682f7eb6c132dae03ff36bbeb0c45901a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03501ffff01ca06375be938a2d6ef311dfafab7e326d55d23cc0057bffa72db682f7eb6c132dae03ff36bbeb0c459011e4a5963abfd975d8c9021ce480b42188849d41d01ffff01337af062ce32bb423010415196e315c4154d36c30157bffa72db682f7eb6c132dae03ff36bbeb0c459014f9a0e7fd2bf6067db6994cf12e4495df938e6e901ffff02001231deb6f5749ef6ce6943a275a1d3e7486f4eae0000000000000000000000000000000000000000000000000000000000000000000000000000000082044d808097c0b85dae5e497dc2def38b73fa7db167176b675e1ca0b9b5fb7acdaed7d33b3b01365195f76cdb02373c4d9553f9e6093d77ef79fb4980b74383688d7a06951bfff903f22f8401c9c38083048020941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5ab9c89ffcc1119514c1b92d64eb03aa97ed8fd52866dcd07383c2c7d4af347a5000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000009e47fbb2a2a27b3b02e4a63b3ef5a3dc863c02230000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d52696a487341633572727a65414e584d735334725a68555a6b576e35386151384a6776523832786a4351374d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000418824be9a966bd0478025bd02a796118c2fb8a23756cbbc2f82d61314f9c6d9d3341db0d02c63f9530b2ecc581ecec5acc10a6ff8a641d52071a0ff8421ce59701b0000000000000000000000000000000000000000000000000000000000000082044d80807034d54692cf1b649157e81c0bdf66632633f6485cdf329ceefb5d55042d38760b6da7e01b7a770f453bec69609332f517ac66c5e4b7798e75abef875a3791651cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f902d3138402625a008305aaaa947481c16e7782608ccba70029c0fd41d78aa6b56e87027ca57357c000b902a43593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e13d1100000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000027ca57357c0000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000027ca57357c000000000000000000000000000000000000000000000000000000000000018d0f800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000424f9a0e7fd2bf6067db6994cf12e4495df938e6e9000bb81e4a5963abfd975d8c9021ce480b42188849d41d000064a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000082044d80803baf44841d975b6bc565ace7c443e7ae7d9ddac53580a8a85ae9ed88c26907ee1e81aed1a48561a8238e7476616c026622e9e207b4a337dff4e87be321a5c4f41cff0b00000003000000000b0000000300000000f903f38190840189ad4083034cca941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f53796b8092eeb16d8bd97e1af7a200666a05419e70bad6c72d92f0e2af24f0d5c000000000000000000000000000000000000000000000000000044364c5bb00000000000000000000000000013988cef5721a77e9a873b9c988559abd698516a0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065a6572696f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5441676266396a785343475176785a476e6576435437394c334d7258794d6d35715957687a68476d3646586b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f95331b37d73776f15d46c38bdbe349eccc9f1f7928fb41f54b8fb9fd5228843643976c760da92db0f41c7b1506e1fc66bbb01f8b2dc70e5a96ce3cd0502b5351c0000000000000000000000000000000000000000000000000000000000000082044d808081ad4be405410a00887985f909b52ceefbf5b7414500559946678f167ec95b646c37fff789b69860bb5850da757344ac27d1c45c9b8c4374d7eafbe2d3d8b9141bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ec39840175d72082520894e4edb277e41dc89ab076a1f049f4a3efa700bce88705692911fe533b8082044d808000315fc52bc3d2f4aea5fd2de0229fae82cd273efe66b698c4ea990e20b60fde5ad8c57f6f5748d6b2709cc620626f50e34a577e7b30bbc2fbfbe8342793af081caa0b00000004000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ea2c840d42700b83013590948418962b8e0c95f386f89415d99883cd52226f808084fec0921482044d8080783253c33c7b79ecb24aa939f976a21069d7837a666da00eb8a2eacb5298d4457566c70d92178084f27c8538eab68e46df3d914512fdb1032ae5933071f699541bff0b0000000300000000f9014f8301154c8401b021008305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13c3100000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000733f79f5a91ea2abe3e50940000000000000000000000000000000000000000ae01687505aa45399c5393c00000082044d8080da4584b7d6fc09c1e15ad4cdc966ebede2301b221fe74a64f3b4b077c6b7d2bf2d38c74317a7901d41676c66859e0e26b28a4ce29ac544f67d8d2cb8b503e0481bff0b00000003000000000b00000003000000000b0000000300000000f903f381978401681b8083030015941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5e88a8ba0ef0f81c929520cdcdfaabfaa6b722c4048cec8c493456ad0ab0c325d000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000007df220262a4c9ca1fa9b7224bc6f7f4572a892cc0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008496e6a65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d506f336736676d4a72386d6b6467384a57764859576b4659486d4b47485777436d425175473276416a44315300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041bdf0d198c5f6ad13e9fb6c891a05b67ccced70655831525ee9febd3c21cb33595c536eb39f577fae252fb903477529ee1d4a5d15ae9731d06543a56bfe1fecdd1b0000000000000000000000000000000000000000000000000000000000000082044d80809de5af8deb33a1a58f32c6dcdac7c610ff048603e566c8cd0cb4fd54b65a38934d59fdd44897d94a3be46fca792bd7a75e88e363289921a8c1096967b30b62441cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ea818b8402faf08082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080612819e135e75602473c7c42e3a2fe293cf9e99ce22373be8ea63724f3381f6f746b65a1f24f8f3abf355f4c8dacdb2b96b48befa479cc96881163cc827e14841bff0b0000000300000000f903f2348401745080830343f1941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f545b5e3028d7045b7f9dea45daaa86de008257fba39d272d868c9d8edaf813fb4000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000005f9c7447129fa10af4cb51e24be323abff1759e50000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5138456f61454658787954393331636d42684653426447486168444d684d353473484b4a745a417072507168000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414b2ae38ceaeb1af116941ed4ec6eb66a86116d4d08a4920ac07d1feaa8ee607d6d5b635040118c5c37fa9f05b6c8a4cc07c3619a8c0def6821827ee9539530e81c0000000000000000000000000000000000000000000000000000000000000082044d80805aaeda75850432083fdb91bd4b910a0850c50f29c6a81e7f5661c0a0e19c2b282a15c71aeaa646bec001c6421af6b380d7321cdec496258e1a893529de2fa4621cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be62840418476082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080b7a139b28d8c5192cab1a5c55a8efcc93cebddfcdb1a0376188144df587e57b1302f1ec53be25bc03bf9fd84b1bc298006b6d0a3b4cb296342bcabd8254f9a4c1bab0b0000000300000000f8f108840174508083034de3942a66b9c88caebe9aefc4ecd7b5e7e60881f7e15c86a0285cf72142b8c4d559ccbb00000000000000000000000000000000000000000000000000000000000009dd000000000000000000000000422f746a9d5b73e25d41dcc18fc1ea09da88ee2a0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000a63727970746f72616e6b0000000000000000000000000000000000000000000082044d8080463a0e84cc018410c62c0f6cb7a9055556b94cf37fb5363e004e2952831ee3a109837118f4ea90e6753e6f16dc192a72053fc655a789d15767496bcdd45e5b961cff0b0000000300000000ee8308be63840169a22082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080dcada0e0a24e607958874f9206c0a0540fa170176d694c6c1d0546eb6d1b787b4b63a62905b6a8b302168d6a12fdec6a5c89b10ddadabd6ccf3d0f099ac835271babf28231db84ee6b280082627094580b1a559eca8c875381251f291f96777ef3af89871715c995317000840333e95a82044d8080f5a183cd5ecb9ccfb91a8c22d2a14191a32c2325742e13b48b8ab6d2b1ac8af65a2dda29b70368485ae17017f0c8570850b37600fc668c6547aa69cde7e2f6721cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000e90284015752a08255f094e10add2ad591a7ac3ca46788a06290de017b9fb48084632a9a5282044d8080aeb52a12273e7d18d4d61292b972b9cc460236c359d0b290d7440e6a9891e8f37ef89ab59bdaed7e90a33fd03dce86ee855f91a6afe8515009b4dc2542fc315d1bff0b00000004000000000b00000003000000000b00000003000000000b0000000300000000f9010d821edd84016bb1788307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13ee20000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c8390bcde7c066000000000000000000000000000000000000000000000000000000000000000082044d808030d0dce5dce8bd5dac0a2de99f3a997bc9b2720b37e0c916d521614aa96b2b295d3ed3ff7c6be137f9e895f4a0736de165dc14c87878a1d8b01222026689675c1cff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031150b0000000200000000f9010d821ede84016ee5c88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13e4c0000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c805935ba60ef3000000000000000000000000000000000000000000000000000000000000000082044d8080e6523db0d8d9f0fc01b8d28d354646bb37ee3a01d94299baf62a3f0513c1cd5b4706f80c68a13689d843d784ccbad2a69c049b87cf72f7a244bab58b25d3f2ad1bfff9010d821edf8401721a188307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13e6a0000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c7f7976b336093000000000000000000000000000000000000000000000000000000000000000082044d80805f7ba49687fc0feb05f0fb1b46b605610f94161628bb0d78a593085bb2d6328d67d03a13c5f7b0d080fc8585f1dc01eb567f4ec6dc22fe372d0dad7b2057b3aa1bff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000eb82013a8402625a0082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080a1e4294a95f81c0195243e271861ea2cda2b25132a4e4866cb58deb993128035280b82205bf5f93e6cd481ea9ce1b4408428898fc5813bd8bf58df3199de169f1cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000e9028402d301b68255f094e10add2ad591a7ac3ca46788a06290de017b9fb48084632a9a5282044d80803516937610ade172676e6ab42573bbde2ba8b143b82c019640b2c41168bc7e445254048ff8978b561f0a95d2d71f4da3d8c67c7f05cc90ed9521cd01e6da0c2a1bff0b0000000400000000f903ce8226f38405f5e1008307a1209467d0809ada627ab5bd623640332a2f370b2c906f80b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000084049d6baf6f3253bbdf284da6b5d1370002ce300301000706050902030408000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000005f5b5a40000000000000000000000000000000000000000000000000000000005f5b6080000000000000000000000000000000000000000000000000000000005f5b6080000000000000000000000000000000000000000000000000000000005f5cb840000000000000000000000000000000000000000000000000000000005f5ce0f0000000000000000000000000000000000000000000000000000000005f5dcbb0000000000000000000000000000000000000000000000000000000005f5dd180000000000000000000000000000000000000000000000000000000005f5dd180000000000000000000000000000000000000000000000000000000005f5dd180000000000000000000000000000000000000000000000000000000005f5dd18000000000000000000000000000000000000000000000000000000000000000420f5d5e5132ac16831c0c420b2e4348553e2835cf6b7764eefb1908997cc8501dfcf180d7ad72bf3313f016313fe7aa3a134b06b53f4f2317a74b5455878011a986cc2fd0b3eb2e32ac671e13fe59db59688a1f49fd71fc5ea1de3162975a6cd243ab73d9dfb19ed57b626487e0e8d4192de45e5084083cd1a03aeb6b004776b00000000000000000000000000000000000000000000000000000000000000046545e8567be21ffb54df1c55b30908e62cae7ab7acbd840698e425e4b4999ea36d28dd45cd30a8ed96f05762d723d83130ace27284c9bc868bb8f1ccae6726f731b09fe696a4b676a0cfdce2a8d0d58fd9d4aeb116110fe1918a8b4f403db20b35b9625924384aa070bbc25c7c24c2f2f39577af4e2bbbd2699560ee129687ad82044d8080270674afad2582de39abcfe1cb19d8243b9f6d31ac6df02257ee13e2d558e4961656fa5e782a66d6f59ff6881448d38b61fd630bffc81442ce9575081c9ea1be1cff0b00000003000000000b0000000300000000f901ef830151d4840154ba908304aaf894555a64968e4803e27669d64e349ef3d18fca089580b901c40ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000002557c000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000020000000000000000000000001757ee1ca5541182d242027a734cefb3d6a745fa0000000000000000000000008cb1d12040c282ce84f851763e2befc48a0810b10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000015705520846800000000000000000000000000000000000000000000000000001563c0e8ed6a00000000000000000000000000000000000000000000000000000000000000022bbc5cbbf99c0d37f3d2aae6daf5a4d7dfa26f8ad4ea658ceeacd1d16bea82961cb86c8ae18cd27416d0782aff31adf25117d75b85cac5c410df5b8de615c70a82044d808017f2d6241834bf9550f7264c42b27a3d05a2a8fb5315b9d82791edbaac56107a5c926d96702e5c401424c34f351eb84109cafe915de7d7d26636ea9806a814801bff0b00000003000000000b00000003000000000b0000000300000000f86b208408a4f1f8830156219468286607a1d43602d880d349187c3c48c0fd05e680b844095ea7b3000000000000000000000000c8a21fcd5a100c3ecc037c97e2f9c53a8d3a02a100000000000000000000000000000000000000000000000585333fe46a1a1a6c82044d80806b533a403af42468742ae5f3efdc12daf9bd38dfa3059924cd35e9d2d2a377444acf354174f9ca1f414ccedd2e434e0b433ff872f60a20da007ed0f80458d5851bff0b0000000300000000f8b12784021c58208304ac1194222228060e7efbb1d78bb5d454581910e392222286119763fd5872b88432accbb9000000000000000000000000000000000000000000000000000000000000007e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cbba106e000000000000000000000000000f78c3faf2046813f12fe6652ff952b2ba6ba845a82044d80808f22f2de07105da8d5d9dc8edb193fd108c8fd3c4858dc20a23e09acb3995eec3dbd05a55530b38f6b4fd7a1416914f04f13fa6c25bc4b1397a784abe13b15f71cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000eb82013b8402625a00828d4594b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080a54a20ef6241a24e66579a016490ddaa20ac5237cc3920240bea379f56c5f16519284d19c65b19253e95a448bc53b8e8674d4904eca64bfa9d6804a0b21462ec1cff0b00000003000000000b0000000300000000f903f26a840174508083030015941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5445b43df8087dc5aea1da71e16e6be5db16a5a065bb35fc5a7561c4fba8bf78d000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000009cc2cb595f85c9d1c527996555e21327fb5e47440000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008496e6a65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d51715667374e78715a51573550667737417932774a76486a445748577937624a6b567659667974754b47774400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cf056d549f465af9b6e55e1dc4762e9790374b2bb1e47bf465dff9a977521b291acfbd54361ebf15ca703a29f98ed7de9fcc1739d17123b634f88b24e1e3f5751b0000000000000000000000000000000000000000000000000000000000000082044d8080a67e87448e21458f2e0daa72470c7b3aba821cd5b07d4128054d5ea267941bf06cd1939cada4bb72f53fdbf3f479a9eada26885bb585c42ea28031f4c04c4a2e1cff0b00000003000000000b00000003000000000b0000000300000000f903f2258402d6b0f38309105c941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f57434eed86a2209cb61008d35514b33b5b56d69aa8d6051dcbf2adda360fd7abb000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000132239919653064455f78e683c365bdcefa8bb3d0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4f4b582057616c6c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d52663166534d787a51314b54654d5a4866657635706d736e61724a76725359784a6b7667393973334e7a4c6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004192bbfc4d495eb5a1784f3ff5109e78c2be43fed9874388b78bfa25c3224866d51712a636189146f18ec716c2ddcde6c64df64fe8f2936fc5da46ca656cff2aec1b0000000000000000000000000000000000000000000000000000000000000082044d808084ffe2036a8fa6ea7499f4346870a19b84fc74158e7aa047eb67cc820563e9df5eceae53db779c5ed39afa1177f3a62d9bec678fc3e4bf5ad4bca0a53d6fb9ee1bff0b00000003000000000b00000003000000000b0000000300000000ee81a68402625a00827b0c94009905bf008cca637185eeafe8f51bb56dd2aca7880429d069189e00008082044d80801b0200bc64259973edb845427dee669bba37b97e3ce801142133f133aa7a77e46719187d4f3d66ad9e37983725da1a3061178b2360912a0616e94c51a34bb7161caa0b0000000300000000f9010d821ee084018bbc988307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13f3c0000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009832b42064d1b7000000000000000000000000000000000000000000000000000000000000000082044d8080dcd2ef6ffc64607c3e8228e4a11c5ed700b6b617bb7a7622c9a6577c7e2aff874f66acc28f4e6a4ab30d106a7a2286af17a28eb5d2402c5552d0c9520d20d6901cfff9010d821ee184018553f88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13ea60000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c8198f0b7fc618000000000000000000000000000000000000000000000000000000000000000082044d8080c12153878eadcd2d15c857ef2792e9fc2db5d3bd6c27ae891ab4f16a25ba7294600e03e412be6aa0a2c4f040d26eb1242d254843ccea87ed5f696ce1699258941bfff9010d821ee284016ee5c88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13ec40000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c82e8c66cbf1a6000000000000000000000000000000000000000000000000000000000000000082044d8080fddf91e72906765982df34a767d163cbbd0c0b347658ab56a0a3a04cc1f14e124cc53b90924519309db15d1dd1fd2b1e575beb4bcc8cea65e68b054f9296ba4f1cfff9014f8301154d8401c445408305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13ce600000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000731958d2d429ea51c49f3b00000000000000000000000000000000000000000adf463a3704377c3ee8d91680000082044d8080545c76198bc384464dea22db921db86fb5648d1e859861a5d0eae70a2e46b75132b3c7d6c8988a27fd2b269cf097d4199ff2d2ee0dd71d759c097df85d7c47e21cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be64840413b38082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80801b1380301ae8db0eb3f94243c29ebe663ed4dad76f4affbbd516cef3379933ee0924b271cc7c68abbe2a716e18c485712d126d9e0897e17bc99739b86a98f9601bab0b00000003000000000b0000000300000000ee8308be6584015be68082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80808912439f3a0301bb9fe59b96f6977ef1bb83487fa42628a7d614d58718ad148b07c60f18aaaf59d36f2a560b9f13057a3bb48a50a31f7772d4281bcb302fdb0a1bab0b00000004000000000b00000003000000000b00000003000000000b0000000300000000f86a0784017bf1a082b48f941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b300000000000000000000000031c2f6fcff4f8759b3bd5bf0e1084a055615c7680000000000000000000000000000000000000000000000000000000000194b4782044d8080d06b79bc4df74c4a6ba59e8bc1e57e9c3194700dcbf8df2fc3c9fa2ef555844a0dbdcb8e23298a3dff13aecf21fc2287dbfa68df1cce01614f6659f7729ba8c51bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9044c0884017d78408304578d94b89a6778d1efe7a5b7096757a21b810cc2886fa180b904243593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e13e4000000000000000000000000000000000000000000000000000000000000000030a000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006708ca1f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b89a6778d1efe7a5b7096757a21b810cc2886fa10000000000000000000000000000000000000000000000000000000066e1442700000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000041d1a054cf52782e83d7b7111c466fa04e03472a9c2098b9a0dccdefb17745170c3fc27f88fa426fbdce2343c90a3662dc441e75c6ba37988ae18160ca98fa7f141c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000194b470000000000000000000000000000000000000000000000000002820037b6d0c600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002b1e4a5963abfd975d8c9021ce480b42188849d41d0000644f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000019581a7c83f846f74fc66fbefef1299521c65e390000000000000000000000000000000000000000000000000002820037b6d0c682044d80801efd30d23b23d0b0fbfe93446a107c35aff67967f83b03a5a93799323fc98f5568d4c4c7bef6650609c9d1b067eedacac74d49b715b587ec4d61bc4d275fb94f1cff0b00000003000000000b0000000300000000f9011381b684017d78408307249c94f6ad3ccf71abb3e12becf6b3d2a74c963859adcd8723a0f814eb3f9db8e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000b1db6eec8d8c629f9f7e28bf2680e3e264e929880000000000000000000000000000000000000000000000000000000066e141bb0000000000000000000000000000000000000000000000000023a0f814eb3f9d0000000000000000000000000000000000000000000000000000000001633e7d000000000000000000000000000000000000000000000000000000000000000082044d8080f3a405df3bdb20a750ee93584f05ab47603187da5842f402d2c9d766f50560717e471e18c695cc989d88de0aa59c4c67ca94b1093b05004db29a50d5f5751ae31bff0b0000000300000000f903ac0484017d784083077b6f94d8e1e7009802c914b0d39b31fc1759a865b727b180b90384ac9650d80000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000a40c49ccbe0000000000000000000000000000000000000000000000000000000000004fe0000000000000000000000000000000000000000000000000000003a2cd4f5c670000000000000000000000000000000000000000000000000000000001805e6400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066e141b1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084fc6f78650000000000000000000000000000000000000000000000000000000000004fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004469bc35b20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf65cc37241d426970b91720e8255140ade00f05000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064df2ab5bb0000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000001805e64000000000000000000000000cf65cc37241d426970b91720e8255140ade00f050000000000000000000000000000000000000000000000000000000082044d8080a3d508557b59df3d77b1fa113f2223b67f82fde7ad9d9cf6f8e2b3bdd9b52a672a2f437e010cd99922806b351f71ab6896a97723f26cbd7b5781c0305b64f81a1cff0b00000003000000000b00000003000000000b0000000300000000f9010d821ee384019bc2288307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13f960000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000981883f28b083b000000000000000000000000000000000000000000000000000000000000000082044d8080b5e4d0db289d0e6f61d922545bd7579bf20015b29bee82a2eb83bc9bbda09ea04b33b1f4c5f3e5aba7f5821bfa1a9f464373d9afbc99e4dcc58297a52ff062f61bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000e93a8402cdccb48255f094e10add2ad591a7ac3ca46788a06290de017b9fb48084632a9a5282044d8080be4c664f330b9151f51b9471b46aaffe652d5a410fa66eeb8d1f84d72f80cbdc3cc90a5514e45f2f576ec352449eb0313c6ea9a15769a11eb2ea11d86dc6f9ca1bff0b00000004000000000b0000000300000000f9010d821ee484019bc2288307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13fb40000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009834b4eb369439000000000000000000000000000000000000000000000000000000000000000082044d808075de06ec5466daebd6df45e07e32c8c4727338a259fdb82acb5feb48d3652fc838947db58b0d09a037a303b3c74274196df3c5de1142bcd2f88aabaebb2323881cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9018f830151d58401a6f9408302c78894555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c788000000000000000000000000000000000000000000000000005d423c655aa0000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000796b7bf65e570f59e08ac136dcc32e8f642f683e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000016442530c6a80000000000000000000000000000000000000000000000000000000000000001e093a1bf79cf96024c9b7b7ed3a8e26c709e92dbbed7918174b100d95d49e55682044d80800977d0591c4d658fa5bd677158b390bf73e8ae5d7218f7bc4614f3eed5126eed6226f3e972b5c62ec523064f6af4787e049c34f96e41dd9d77d4d25f0d2e76161cff0b0000000300000000f9010d821ee5840193bf608307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13fd20000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000098370b3e46923d000000000000000000000000000000000000000000000000000000000000000082044d80807df0dc6ae14e4160030559716f36cf546839515e3a14e78546cbab05e2107d09760212c06296915c5c1a92d990722bd3e26afeb0cf29bcc462985f9331f117981cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f901f30a840175d7208303465694678aa4bf4e210cf2166753e054d5b7c31cc7fa8687079fbc2a49e180b901c45ae401dc00000000000000000000000000000000000000000000000000000191dfea9b9b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000104b858183f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000af09364718f745b6855df11778d9b523eda1e4bc00000000000000000000000000000000000000000000000000079fbc2a49e180000000000000000000000000000000000000000000000000b55d73b977dd5698000000000000000000000000000000000000000000000000000000000000002b4f9a0e7fd2bf6067db6994cf12e4495df938e6e90009c4a2036f0538221a77a3937f1379699f44945018d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080438d26a5ab0788935cfeadd2658b8a1447d9448fba3f8bbcf8def6c0b74f77cc77c03cba29fcb4d3fdc1b913bcef92e36e10cbd8bd913da805760e2d9c5242ec1bff000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ad40b00000001000000000b00000003000000000b00000004000000000b00000003000000000b0000000300000000ee8308be66840433bea082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80806f7f1dc42b8cc34aeb9ee8206e47765be4cfdb5536272d3250950648aa4e3d5907cfece316b171d676cb399a7a75c4b11ed9ed5a3f27dd4122d8c909d4dc51a81bab0b0000000300000000f86c81b784016694e083010ec4941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b3000000000000000000000000331f3a300b7115a45ba31e3428ac002267bb6d7700000000000000000000000000000000000000000000000000000000000f424082044d80805fa04ce2bc7596aa78bff65f39212d4f8c6325cbf35deb9fe4fe916b9b2858e33756e78b77457b8c39c573cd729120935a76c6a1d6bc73459a16514fa1054b5e1cff0b0000000300000000ee8308be678401945ba082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d808090c0419282e01e7ccd1b49b104e6e722c161127a5197edd0bfef9447d4600a2a3e17a07b6523f4aea15c603004537da2e38cc7aec7ba9801099d516b53fbff0a1cab0b0000000300000000f8ee83019e568401e53ac083035f30945eb6b3db915d29fc624b8a0e42ac029e36a1d86b80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a1000000000000000000000000000000000000000000000000000ac720b1f97be20000000000000000000000000000000000000000000000000000000239000f7e000000000000000000000000000000000000000000000000000000000000001082044d8080443dbf96a2e3983c0cbb6e79cdd2a18777b63fa692b19ccfed0bd0b2ed0a6e667db137169578347abbb112cfbf718d3795e48e569f79b357a042c5f1b4837aac1bff0b00000003000000000b0000000300000000f9010d821ee68401a893688307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13fff0000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009834b4eb369439000000000000000000000000000000000000000000000000000000000000000082044d808049b78766cf06b54ad4071fdf9f0182f50a767cae6a31f64f2379b7ab9b6ec2e65fd26b7b443ef10cffc30988b7bbc73132033d1d6e64351a47baf21fc12bee9a1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ee78401b230588307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140180000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982a06a2b59803000000000000000000000000000000000000000000000000000000000000000082044d808086d7452f41889b2e6811591fc74bae161b77d3712a76ab034c6d7634f3e0041667e2f1c34dbe96c8c6486cebbaef1f7b3cff036f05e72662317089557121c1d41cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9014f8301154e8401c61a00830596399473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13dd600000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000731d4964ad65b18a4655c340000000000000000000000000000000000000000adf476e7c1ddbf9c2658d5180000082044d8080ee854267d400eb90253e3ed78b0ae5ec32fe0ba653de34d80f5ce044cc0fff77498cfa898a2ca54b3b84abab13ba9fd6658f4e0f644d940d33f269a9e6d7cba31cff0b0000000300000000f9018f830151d68401d0ef708302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000070637b4d8b21c764d0d9c08dd1d28b279bb2823500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000151c1d3bbe7200000000000000000000000000000000000000000000000000000000000000012a0a7392d80a1be757cdebf95b665fe0544e091c1b8fdb0ea7470aa9e0f6492082044d80809b8ab0a9bb0496a9099c6018c73ff2acaada0e299dcdae23bc20fb29fad715822cec0db8716a0edbff285acecc08b662b89a0412b629ba866e4c2abb80aab2f91bff0b0000000300000000ea4484018cba808302066e943df9e9ba39dc3ee6b6fb8556bccdfa87ee64fba680841249c58b82044d80807943273fee9a25c1549219629bd8844eaa6e9e327a7935d203e2f9d6d238a4837e3fbe0944dd0d07ccf2cdeec4e465bd25322949b697bb0efcaafe2c3c5316611bff0b0000000300000000f9010d821ee88401bbcd488307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140360000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982d87a827433e000000000000000000000000000000000000000000000000000000000000000082044d8080d03b301c79a78d1584e3c7eedaf2272a56f24adef479a8c3e045ce46ff16065d7cd5f8bd5528c8a634951d7e3f11c7c97c9d57cae43640818c7135a1a9284ae01cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ea81898402625a00827b5a9400cd1fb066573ad4215aa699e580af9ef4b3a4168084372500ab82044d80801c0e2075230c09e5f074cac36419c3eda1ded5447ad6470d15caf956eeb3f4d5305e11deed13c3947e01beafb0054fa692ee7b708525bd6438bb1283d9f5633f1bff0b00000004000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ee98401a22ac88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140540000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000983735fbc7f641000000000000000000000000000000000000000000000000000000000000000082044d8080590354f8346245d29a2193f306d6473bf0786bdbafbe91df5a84e8642e3570eb3a6da19a7f13a0e834f5884633dadd4d2e432c2c2bfa3324429be9b7ad04290a1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f86b2e840bf5ba4783015de6944dd70bec5984171a75003a6186219cb99ed79cbe80b844771602f70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000482044d808026293839e8593c6b85003aa27b0a4797069ba043803164bd5209b940d39c2bad7bfc0e2c652552a2606eb57f0dc6f7426805edf84ca2ad6fdbfcd9136ff73bca1cff0b00000003000000000b0000000300000000f86c81b884016b28c083010ed6941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b3000000000000000000000000c8a21fcd5a100c3ecc037c97e2f9c53a8d3a02a10000000000000000000000000000000000000000000000000000000000b3671882044d8080ee2364f3d622e422ec2a90a92137fc5d8a51510bfe0605b11ce2474538a7e5d90afc93bd57016193b3c0a416f3a471bbe24bf08eb385bfb9635425e3f84acc7c1bff0b0000000300000000f9010d821eea84017d51308307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140720000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982a3158e2273e000000000000000000000000000000000000000000000000000000000000000082044d80801bb6d2b5470d77ae8986b29496a0565ee37380f17c51fb34da2d9b4aa9b78ab66d72906a4d0f10e0663f4560942d246c895187c4287b43f9d26bfe128b4abb671cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000e94684015d6d2082a01994b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d808085262abae3391ea17e9032bf33e93d793771d42e14defe690719b53803d7085915ff9bd53ce52fb50282712974ff93950c0161b19a492c040c0606e855207f821cff0b00000003000000000b0000000300000000ee8308be6884040f1fa082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080bcbd6ba1c6cca5649a36a7e14c75a5561bac294fc4ea5cbd50e4c83a04b0039c5ab9e91f0f06c0c028f7a6aa146fa901ac23abdf57072083cb2113e5d53e7da81caa0b0000000300000000f9010c81c18402625a008303233f942a3dd3eb832af982ec71669e178424b10dca2ede80b8e4cd58657900000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e602c1920443f01cb100a57a7f894df8eb42f6600000000000000000000000000000000000000000000000098a7d9b8314c0000000000000000000000000000a2036f0538221a77a3937f1379699f44945018d0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000082044d808085cf2d27dfbfd35f2e62054951ea381ca212755366c3b51fff597c6ffdff170e4bfec23bd116bc191684d77da0f186d5202128c4a7cc68187ae9746b5c8f412c1cff0b0000000300000000ee8308be6984015a5fe082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d808020bdf52ff36fb31db16395c7eaa2bbff670a9dd200ea46062836d24f1b20e6e03cf5779a839573d635f939669f6d36f345a0bd444217df07950ad766e4c4efd41caa0b00000003000000000b0000000300000000f9010d821eeb84016bb1788307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140900000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982cb210d1b77d000000000000000000000000000000000000000000000000000000000000000082044d80805c32534154b68c162e322a19dcd13dd8fd1fd1183396f6b4c1e6c43609149f4566b01aed437e1992606c98b53406eae40a6b630dea0e7cda6a124ac4eef068121bfff9050d81b984015a5fe0830a10a994b83b554730d29ce4cb55bb42206c3e2c03e4a40a80b904e454e3f31b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000b2829a0000000000000000000000000000000000000000000000000011b27c73a01b7d0000000000000000000000000000000000000000000000000011c9409d5f4dcd00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000440000000000000000000000000b1db6eec8d8c629f9f7e28bf2680e3e264e929880000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000066e19297afc94fb216be4efcb60b0969a606731d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f6ad3ccf71abb3e12becf6b3d2a74c963859adcd000000000000000000000000b83b554730d29ce4cb55bb42206c3e2c03e4a40a0000000000000000000000000000000000000000000000000000000000000148c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000b83b554730d29ce4cb55bb42206c3e2c03e4a40a0000000000000000000000000000000000000000000000000000000066ea78b70000000000000000000000000000000000000000000000000000000000b2829a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000281e4a5963abfd975d8c9021ce480b42188849d41d4f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000e1829cfe0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001240000000000000000000000000000000000000000000000000000000000000148000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080c4b8a950b6fb2fd40ef9292e2e21f16ce93551a97b61f65dbc361e1b97c7e4fe353efe7bbaa446443a57106a013415b016a5de387499d032c14384b3b85e5a4d1bff0b0000000300000000f9018f830151d7840179a7b08302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000010000000000000000000000001e09278590cc10fb2bd69235d2f4372fd0e1ac44000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000002bd26ff6604000000000000000000000000000000000000000000000000000000000000000018bce34245eaa4dc895284ff7b987382cd12c8b40d2f55ace41c054e6b667f19082044d8080185025c2a18ff67f6619138b44a5262ba2181151be1526f23b3787bef28350337867a681c56458160a1ee95dec4ad4c9a3c47f1dfb9a39f9b39c67ad3c6f62551cff0b00000003000000000b0000000400000000ec0e84015752a082520894e4edb277e41dc89ab076a1f049f4a3efa700bce88708d8a44d7dc33d8082044d8080e5f393baeec28606eb9e5a973614ff7a3bb17e7df7d56de1c8dc34d71ae0b50d766c5c5d0a02d04a7b4140cef33d896eba5ae9c796e18f047b7b3bb7c0e7af981babe94584015752a082a01994b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d808064019d123c2bc2e40c10f63cef99518b130d358e122504f0028f2f14085f24204f3d2f2de773ce5bb4be2eb02d7978421bdaef8a98f8d2407d3ba53e0ef406061bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821eec8401754e688307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140ae0000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982cb210d1b77d000000000000000000000000000000000000000000000000000000000000000082044d8080f8fc4cd3cfd4f5669ef452ef2ad8c5082f1ce1bf2f0523a52e12a916b9ad8c6c12a08dc48c171e434cc75852b7c8161b0741f0465821e8bda5fc10ffaeebdd841cff0b00000003000000000b00000003000000000b0000000300000000f12a8402625a008307489e94dfb6baa334712cbbeb26b7537f62b81c2a87b1e8871a84698758700d84e9ed3d3082044d8080c9df68b81de01a7ddf57cd4037afa1df1f031e10810bc3c23c88347bba337b20343c98d810b700b8e27ca6243277b28771d484477cbd58366d02b57f3b8aeb731bfff86a0584016387a082b49b941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b3000000000000000000000000ad41c77d99e282267c1492cdefe528d7d50442530000000000000000000000000000000000000000000000000000000001805e6482044d8080bc02d6c00e37eecd89d84e5439fe5186d2b55f91e68bc952ae7803f7252afc0438bf92877c67810409bb9b40ea6d08b272f760015563aa9e052bb46ccd5e245f1bff0b00000003000000000b0000000300000000f8b133840225dda08304ac1194222228060e7efbb1d78bb5d454581910e39222228614edc04e0e0ab88432accbb900000000000000000000000000000000000000000000000000000000000000c6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b48eb57e000000000000000000000000000860db06d8c758c594143686d0ef9cb52543bae0382044d80803c9ebdd5f72aed2cfa0d36b5311cba9fd9a211cf7c9260502b5b3975751b24603678ebaa584a22aefcc118b5f8983f25e0201d6152c9b7d0d9dd6c7249760a211bff0b0000000300000000f86c81ba8401607a6083010ed6941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b3000000000000000000000000b5f43c2206e3cafecd62651f5fce9091a020748800000000000000000000000000000000000000000000000000000000001e848082044d8080f97d0cf4ce6c85eb1609a85f2c71839e2bb12896b7968547c085d447c99f23ac4f3af1959ed38d3fd76c379dbee00fe520c52c8ec791afe021b974d7442e9c261cff0b00000003000000000b0000000300000000f86c81bb84014b1da083010de8944f9a0e7fd2bf6067db6994cf12e4495df938e6e980b844095ea7b3000000000000000000000000b5f43c2206e3cafecd62651f5fce9091a0207488000000000000000000000000000000000000000000000000000739ecb738d98f82044d8080a78e3716c0eb3802c51e41bd1c7ca64c9a638221f07c7c06af9f6c94300b21d37ac0c2374cfce5539fe7d9c180e153b2fa10b1ccae8b727a8d4734dbd4b9287f1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f281bc840175d720830107e5944f9a0e7fd2bf6067db6994cf12e4495df938e6e9870739ecb738d98f84d0e30db082044d80801049e7b2658e6542463bbe11e60a7198f23cc761a536410d5383e00aa6f6cc68440573478137a9ce3486ff41a903bfb738c44469e56c7d1dc667ee4c18750dbd1cff0b0000000300000000f90432018402625a0083053b1f941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b90404d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000a26ed90933f9410c967efc64452681f501477ca7c7bb0efe1a8e16b8063c3ad420b4781cf348b8a0a4851fc3c2ff2553000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000807edc9cca4620e59b6919cf51b699ec6a2bde550000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008496e6a65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d544869314d7366795965714573576178696f4e445734344a504d476d346b6e734e486567705473675668514a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000023c5fc38090643b410c8c06c4c0f3246059e98ee0000000000000000000000000000000000000000000000000000000000000ce40000000000000000000000000000000000000000000000000000000000000041ecd952920c1aff799bb35cc6b1306371afbc5f683cdefb2ee2e31ae21bd48a8270797e9889904a574fa1918a3bb13fa2b28d83ed3ab567b3aceea912b8d04f7f1b0000000000000000000000000000000000000000000000000000000000000082044d8080feec2c92b7ecf47d4dbf93f7041e0082d822ea2bedafe3c6ae11d8426678fb643c29bab890f68593a4f8421415f5a68481621730b71dac5b16a41b7c31ef762c1cfff84a06840174508083074ad694ad41c77d99e282267c1492cdefe528d7d504425380a4a0712d6800000000000000000000000000000000000000000000000000000000016e360082044d808067b5300ca47a28ce2521475a6224c2fe27e4b28da3a2cab92231440d3938603724b3ee0a7fc6bca38ad52b7ca90b534e67e8d3a55ca472e3eba0ac51bd5fbc7e1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9018f830151d884018c1e408302042094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000020420000000000000000000000000000000000000000000000000005d423c655aa0000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000da930060b712589736018c6d891ec57b0399db7800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000763bfbd2200000000000000000000000000000000000000000000000000000000000000000017f0ea037712c6b94793ed1db0d924ab44381912765b13d871e7eb101ac652d3982044d8080ec0c9e107850e44db65fd11d35d41ee8ddadfd9a2e989dbc619a60c52adcd2860092874af3ca89dfdd56ce708439cc6cc538ee10b7fb9023d4125f7abca62f0c1cff0b00000004000000000b00000003000000000b00000003000000000b0000000300000000e92b8401499700829f8094b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d80805b33a4bea9274b6838fc8c299e7e1d463d8a9866847ed34961dfa742b1e09e7960336c7a436d7c78f8c055607e77072b8f1ddcdeea4757c9236b8dbfea38a1181bff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e080b00000000000000000b00000003000000000b00000003000000000b0000000300000000f84a0784016e36008305dc8f9468d9baa40394da2e2c1ca05d30bf33f52823ee7b80a4c5ebeaec000000000000000000000000000000000000000000000000000000000112a88082044d8080b0b78bcb15ccad6e34965bb75310d919de9c98653338a4e621bb68d4e1158bc071db0cd0eee106b2a15820242d3a30d78cab313ce696b38609712e9b97b4f2211cf6f00c840a85ad1c82fd67944f9a0e7fd2bf6067db6994cf12e4495df938e6e987131d14dce4400084d0e30db082044d808061485ee80be12c1cf4118d79c8240621c4d61342aa6e21313bcb338e0c4d3f371f1589ae7a144279671a249bf3d2514d9f39c850081cfe35070f9df8b5bc52b71cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be6a840433bea082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080ae7ad2d365c97fd210a6f105189d8f8845c7607a5304f61a75e3f95b30d0278654bd6071ea8dc8eb258293e33b8662588af561e361eeec88f2c222dab85904a21caaf9014f8301154f8401aaa2c08305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13ec600000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000007322d2c4642c149072472c80000000000000000000000000000000000000000ade71ffb3551101896108d800000082044d80802d5cadf3e11640c1e52b9bceb4f465b5be3247fc0ec042d85c44575e2e71c3ac0542b94b31e12bdecca3fbfa292da254413c91cff46b7a09a6a264a4b58fa1941bff0b0000000300000000f84a0d840b405b068301519d944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d00000000000000000000000000000000000000000000000000131d14dce4400082044d808005ff907a16f3f1614d5f81b3fb224f805038d423f46f7a3201533568f87cf5032028fb37a272fa727550e1dce4af167b67b51157e24de676306f97faecf6ceac1bff0b0000000300000000ef808402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a10986246139ca8000840333f38b82044d8080e1a97f847a204044e3f9e326c84f4d5369fc9bdb762c0aa38122fd4f9e5321d0660038ec1b8f5c2273ccfe875ffc274d7b809cfb2ab742631683613108c7e8501cffee8308be6b84016387a082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d808086767ef08deb78094341e575335c29fa45b39ae1dfa57003b2f1b8e0f29dd57f13cd26cdd53e10c6c35df7eeecf0369bc77a701d0732dbeea119688fa09f7d021caa0b00000003000000000b00000003000000000b0000000300000000f00e840764acfe82c6ee944f9a0e7fd2bf6067db6994cf12e4495df938e6e98712dc81e31cd00084d0e30db082044d8080cbfcaa3444f14e64cb0fa9e0259f2fd1210b5425a8c23842150c77d1c9e8578d79aea37c19621cdc2d60eb47fdef1ddfb63a0dcf97dd37f639b862a80d81aab31cff0b0000000300000000f903f20a8402b900fb8309e24a941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f53ae48ab8aba0990d601236bfbbeb5f26e910d68867594ac5def0ee45b27ce819000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000ec90f6634a907165e5d18a14c1dc51e32b9de8b60000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4f4b582057616c6c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d6252327345444c614761345a33664a4278386d6b65636e59646b6b323451586443556a737253466a62745161000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417ee5aac8e9e510dbe7b12d5757762ea0b0eed32189dce8ceb044ba9bd16daac61415af82e6bdac2e90d2227bfe8336c383ac24ce03af40f159412e86b49e02f71c0000000000000000000000000000000000000000000000000000000000000082044d808054c2fcd741aff604618f9331edd8de2062dc15c3ca37a0ae759881f21547c1dd491a44cf2cc85c20f72575718f39a8026b3989a1f07f4c9de324b8fbf62d03801bff0b00000003000000000b0000000300000000f84a0f840842bf86830127cf944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000012dc81e31cd00082044d80803d7108665030a686303828a8590515a325439a383cd915a8510b1ead08fdbe64726b280ca7a73a844d6ff801f53ad44480c39059472080c96b4207f2b41eeb141bff0b00000003000000000b00000004000000000b00000003000000000b0000000300000000f010840cd2e41082ec87944f9a0e7fd2bf6067db6994cf12e4495df938e6e987141476cc45400084d0e30db082044d8080bb1a5745409e78f126e0f3d301d1bbb8e829404909bda7bdba47b38a8e5cc5022975deb7d293bdb69210a4a50cb606efce11040df82ec34008b179a51447d3a41bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f84a11840b840af383014099944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d00000000000000000000000000000000000000000000000000141476cc45400082044d80800f0a3bb81e483365b2f6196c60d0d97882e9d2816fbfb78689cd3850a33f7d9e0f6732a7d860fea63d323bdd924b39043a592e79fc5cb42593cf5a3722b854de1cff0b00000003000000000b0000000300000000f86a0884017d784082b49b94a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03580b844095ea7b300000000000000000000000046a15b0b27311cedf172ab29e4f4766fbe7f4364000000000000000000000000000000000000000000000000000000000112a88082044d8080325b6bc0360425e4a14b8d91cd8855d8649bf946352c4ad927a31e56d22c004731500e69af69ce8b80b06946735af113f7ea67749f57af617d5deae89eb1f3d61bff0b0000000300000000f9018f830151d98401a1f0308302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000010000000000000000000000007f35a6e27c542fe3bb6bdda638abc8bc65c3c0960000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000015c7ff2eadb60000000000000000000000000000000000000000000000000000000000000001a93b38316983d650e0fdfc2642ca16b840420a6bd6962cfbf417bf5a790d681b82044d8080738091a8459376230f139c30ca51cebeb6deeab09492d46a6009f33a090953b219d8f52e737caa178042069504eb99b0554f51963c9943204724b283f347c10c1cff0b0000000300000000f00484018519608287f19465a4b8a0927c7fd899aed24356bf83810f7b9a3f871c6bf52634000084db6b524682044d808050f3c9ba5b616396e065717c29365860afd49c5e9aa275eff0f37446d3eb5e3611e8626c0be00efd62ed5d31b51cc334dd8793c6bc8c62808616faebc97cb7b71bff0b0000000300000000f112840cd321b68301312c944f9a0e7fd2bf6067db6994cf12e4495df938e6e9871382f1e51b400084d0e30db082044d80809585c9b5b7689c9abd1823e19e0f8d592f57f419f94cca0ae20ed021b68aaeb556273263752bc1e78c8508d5af41a05d2b886e07b78330d63df42177f02110621cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f84a13840891001683010317944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d000000000000000000000000000000000000000000000000001382f1e51b400082044d80802eeb9e8ba20f90a05d7584a87870c63edc5a0952fd52d1299d54a19098af421408f9882e7e62d42d0afdff81a06c4614ada1367758b701289f3aac12f5ec13e71bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f114840be4d1b6830126d3944f9a0e7fd2bf6067db6994cf12e4495df938e6e987145b3905a2500084d0e30db082044d8080ddfa4814082f9ca8947b3a9263cb97c0957bed97d3032e8fa38df43eaf7669743b15fc3cce18b60300063396898de729594c2af72e147d8dafbf7dbb3e9e745d1cff0b00000003000000000b00000003000000000b0000000300000000f84a158409de70b683013343944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d00000000000000000000000000000000000000000000000000145b3905a2500082044d80800c288f5c663955ad276d8e8bed2233997f07bcaeade6992c98b05f99151c6c2a27d0a04fb3e07473e11b88176db0f5baa46dec2c48eb704e4d6bcd78467b25fe1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f1168409d2a29e830141d0944f9a0e7fd2bf6067db6994cf12e4495df938e6e98713a7531ee5c00084d0e30db082044d8080f22f024c3df0f0582d53d27586bc0b534636d4bbbdecb6cba76d31d9ec26c24a2fc8e613aa6d783ef7541ca0463908b696f7a3cac12710986820bd17613a05ee1cff0b00000004000000000b00000003000000000b000000030000ae450b0000000300000000f84a17840843742f83014a3d944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000013a7531ee5c00082044d808093acdfe33b2bf0475202506781b23511c98ddf648158adc2480626821c8462f765d5b53638bf280519dca4c3c6d1b8e62068dfab0aa3ee45702e69dd44d70e881bff0b00000003000000000b0000000300000000f9010d8263618401810e38831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d00000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e141af000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000000000000000042a6800000000000000000000000000000000000000000000000000000000000000000082044d8080787ff95ee62900d9971c92e31e4a72c46ccb514a1f429be155b8bf471369fc0610e0132cde21bd011d3efc70d70cdf470af7ad9b5aaf86170803e31f29a94bd21bfff9012e8263628401810e38831e8480941b81d678ffb9c0263b24a97847620c99d213eb1480b90104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f400000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e141af000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000000000000000042b949a000000000000000000000000000000000000000000000000000000000000000082044d8080812562f17bf5daeaa60ed73ad30eac5996a784b57451663ad68d2a3a1a4ceffd0e98775ae6ade38d54ffb4a21778eb2a047d280b8408451096b13019394cc5eb1cfff9010d8263638401747b78831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e13bf40000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000980faec989d805000000000000000000000000000000000000000000000000000000000000000082044d8080d6ae2347276fa3cfd245d9379684454bb7f0bcc3cca6d642d827809ff5d836922ec796bffe17a15a93e05d6d62d524588b2e083a3baff8736a9ba633ae1f7cf51bfff9010d8263648401875798831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e13c180000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009819595140ba78000000000000000000000000000000000000000000000000000000000000000082044d808008a9fd6bf5a08b08d9611be3a3243fdc5809a0e51aec5629bf321cf09436fc67600bd37403aa4f6155045256cd67620aa2bc7e7118c6b2dcd5f72db9c30a433e1bfff9010d8263658401600d00831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e13c3c0000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000981383eb6618c6000000000000000000000000000000000000000000000000000000000000000082044d80809be365822aadb974a913e75d18dd5280b322409c861bfb10d545c17c72979d847b52b9ca8a4f49f5dba9016e98d2e55536a8ce32fdf06b6a13320aab4a6869821bfff9012e82636684016331b0831e8480941b81d678ffb9c0263b24a97847620c99d213eb1480b90104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f400000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e13d9d000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000000000000000042ebcea000000000000000000000000000000000000000000000000000000000000000082044d8080cbbc34a2b94e3d2e56260b5e0034028c2f6bc1c7306fe9a9f1956b670c027dfd70a38bd41a95f94774d84e470ea2a25a42c499e1e4b971dc91a426139f61a09e1bff0b0000000300000000ee8308be6c840453c9c082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80809fa5bd47d6b5cc939b9b27f080a9ee8477b2baf9fc9ac7b51c023ba5eab004ed22ec4afd14c073c5eb9f9a2802eac4f24ac04c34b7ef33de4ecba895e8749e141baaf901ce826cd284017143408306dcda948ff6508bdd71b535df073920c423e9eaee0b97af80b901a4b61d27f60000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f40000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e14087000000000000000000000000000000000000000000000000016348a452209ad0000000000000000000000000000000000000000000000000000000000de701c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080aaa0ea5c36cb0e4fd0bce18e23632f9c8ea2b06b5351c2c69a2adfec23fff4a43f84b301c33c51f2744abdfab346fc91d47c8dbe1b2c91849163ea7d0dc871531cff0b0000000300000000f9010d826b8984017143408308e02294f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000098f0f120de21a90f220b0027a9c70029df9bbde40000000000000000000000000000000000000000000000000000000066e14088000000000000000000000000000000000000000000000000016348961fbb5800000000000000000000000000000000000000000000000000000000000de6fe26000000000000000000000000000000000000000000000000000000000000000082044d80809c6fef4b141e8e4a07c9cbb2511753153fcabf3a831eec7216bb4af90abb7b7d6fb5002e74953de4a87a5aada51b25c9b1b05a961546421ecdda070420f081621cff0b0000000300000000f9010e83018d5f84017143408308d82094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000023abcb82f468e3422a7212d4ad47cc2dd39162a30000000000000000000000000000000000000000000000000000000066e1408a000000000000000000000000000000000000000000000000016349e13e663f90000000000000000000000000000000000000000000000000000000000de70b11000000000000000000000000000000000000000000000000000000000000000082044d8080637884a6f0f95a88b0fee2f9bb436161c0032965d91166bea89124fe6513d31c5527771f11b4ed3cb90e8b757c9f4c9fd74c6bcdc82af2e68521ffd5e0875cea1cffee8308be6d840171434082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080c501252774527c8bd4e9fd808914868352ec182c325575824ae9e83523ba33ce46a6b82b2ebb32e4194110443e5c67ad08df9d8db9bc6fa8aea72192c1192e221baa0b0000000300000000f018840a88762382e3fa944f9a0e7fd2bf6067db6994cf12e4495df938e6e98714191a3bf5780084d0e30db082044d808076d4330b8513208b9814415391c70d0cfec79655b397fd2632d5e82c57962cb87956a8c4b93809d7904baadce5b66e8e720ea8f6ffc5c9765226b0952f7dafd01bfff901ae826749840169a22083091f30948ff6508bdd71b535df073920c423e9eaee0b97af80b90184b61d27f6000000000000000000000000f6ad3ccf71abb3e12becf6b3d2a74c963859adcd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e14090000000000000000000000000000000000000000000000000016348d202444f70000000000000000000000000000000000000000000000000000000000de6ff7800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d80807838e9e5f7ffa45112082937cf4c13ac9a4b34d2d89c0f88a6d1e5b8fbbabe68450963e324bff7087380a48b63fd683577784f3eb7c926b9a9ec08acc1cce13f1bff0b00000003000000000b00000003000000000b0000000300000000f903f215840169a220830343e5941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5552350c0f4036746139ed62c05a8199a5364297d41a85bbdf9d08e25c0b01711000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000c2f02570540a1e49f72b83365db2aed22ee6cbad0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075068616e746f6d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d526e4b355959546b517155556361316f505241637157456f4878737847576658517670364e4c43435647705600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041303e333a0a05a77d8b21e3acd4fee5daaf7544b8b4d685934b1343ccc154afe811166033a30ae22ed0dfb9c4ccdec95eed70ac788e04d58b361f0a5f38260a011b0000000000000000000000000000000000000000000000000000000000000082044d808057a8cf819dd9e7deefa39df29b556f3298ecf2343570849e33ca211713427ed3666812d6fc31f2b1269f4d272d30c4c4d6fe42bcfd0ace89e156199a62e72e771cff0b00000003000000000b0000000300000000f90bae82244c8401650e408303e4a494d46f8e428a06789b5884df54e029e738277388d180b90b84cdd1b25d00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000008e000000000000000000000000000000000000000000000000000000000000000740a14de1e598b81620773454588b85d6b5d4eec32573e12145ec8cd4881eba87279f5f243eb89ea9383e677c61a144f9a0e7fd2bf6067db6994cf12e4495df938e6e922072671ce2724c2d62888ce0330cd083a20623f6e1a2edccbb959c2ee656b3497c5155af15a0ceaa8b0cd940c4fe9792d12000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000004138c5d370f4c6b199ed42c2e346cef963a8fdf9512270c9518698cac23166fccd4145a7fe09b7b53828ce325cacb2368e5917ccf999f0454697e0edb2333ef67c1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414815d2c0438852df0d6e1f6e079682e1cdc5f56410cf9260224db24c1c2ff2ea25ea8b65b7966110962eebc47a78f4d77a8b1dd0a3855d73ca97da7e89cccfa31b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410f45357656c66529a2f7c6fca35dfc1f13f7db5cd546cc958503ecba7d9d55ca51b1682a289b9ae1daaf82ec950e0005e71c01b0bff347c68ca80999802cf0fe1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004166d8d1d855e266c5688f8a45503d9448366fea91b50d4c8ba12ebda394ef76a84d003f8bf256cafc0791d521dc3fd681710fc1e0dc49d373fe1a3be3ef9645ea1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f2d31bb75fc66cbc2f75af278c95a028b6b764f0d056d65f3f50bd9d8ad857c53c229fd9cc844192595b6e8c892edd4566e32f17add3ec25102f06edeac4de4a1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f1d037092dba92c2b718f8cc6d1d0274087cc7e033e6aeffe8ef39246188a2e34ef3761e29604612724fc57bd129d9ca0b1ca246c3f7c18113cc3b8acfdfd4b91b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041aaecb7b28127e84f580d3944c59e7de10d46f2cb121f0b56364ce9d843c1fd783e28f41e1e33378f071dacd4788a44db8314174eca4757a19470cce0ef98fd5a1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000419a9ea0bf5e36016e3380ee472c85ac21cd072153f7265ac450f9c2853782cf715a8105e3826f08d47139e61146f7768af4b55b3ab485772ed6332c66150d80961b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000241a100333eefa2efc389ec836a6ff619fc1c644000000000000000000000000273035e10f106499eface385dba07135e7cc8e5400000000000000000000000055f4a1bfc655cf55ed325f2338a1dee84f754df200000000000000000000000057c96a00f9ff7b25cb5cf964f1a191be9321b8c8000000000000000000000000870cf8dd5d9c8eb1403dfd6e6a4753f4d617a53800000000000000000000000095016e36adb4e0151735ced3992a7fa54e16bd08000000000000000000000000954adc74481634b4d278c459853b4e6cc17ae8d200000000000000000000000098e9d288743839e96a8005a6b51c770bbf7788c00000000000000000000000009a66644084108a1bc23a9ccd50d6d63e53098db60000000000000000000000009a8cfacf513fb3d5e39f5952c8608e985b3dc6ef0000000000000000000000009ac5279013edfec74c5c2976fc831ad0527402e00000000000000000000000009cd5006e1bff785dad5869efd81a2c42545c9d9b000000000000000000000000a73b339c3fae27bedf7cb72d9d000b08fc899609000000000000000000000000bfa2f68bf9ad60dc3cfb1cef04730eb7fa251424000000000000000000000000c74acab8c0a340f585d008cb521d64d2554171a8000000000000000000000000cf12dd34d7597d06ff98f85d2b9483d9d5f7d952000000000000000000000000d10c833f4305e1053a64bc738c550381f48104ca000000000000000000000000f4151eebfa1b9c87dd92c8243a18b1baef8c1813000000000000000000000000f5ad7f3782e8a67bffa297684e27cf9fcc781be1000000000000000000000000f6e93eb288658de5e2e982f99d2b378b22959d1500000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000002f447e9b1e8adca8df0000000000000000000000000000000000000000000000440370c65f337c6835c0000000000000000000000000000000000000000000002c9067d217c36159ff0000000000000000000000000000000000000000000000530db71af8da0bfebd000000000000000000000000000000000000000000000051e62edada099ff54000000000000000000000000000000000000000000000001999bb4b4f342a0dbd00000000000000000000000000000000000000000000002c08f0c7601de23e6a0000000000000000000000000000000000000000000000ec1deea13ae957d41000000000000000000000000000000000000000000000011be0842d86f7e8e736e6c50000000000000000000000000000000000000000003e4b325079a76d1d368000000000000000000000000000000000000000000000394b6fd351bc3090d200000000000000000000000000000000000000000000010792503324e9fba39d000000000000000000000000000000000000000000000059fd816a8470289cecac00000000000000000000000000000000000000000000d2faebf995c3095183ab000000000000000000000000000000000000000000009158ca2eac127d67ba00000000000000000000000000000000000000000000003bda588740b8d2e76f00000000000000000000000000000000000000000000008ac2e56a51bc65072900000000000000000000000000000000000000000000004383436ecf64559db0000000000000000000000000000000000000000000000031c05fae36f04651b4000000000000000000000000000000000000000000000010250608f677cbded0000082044d80805d36d9762307593a13e91e19fb81e6c0a17830d4834e18d66c52d6411dc7b22009da393426cdda33d3b5d7385fae3ee7b625f0febf8d72c3785dc733aada5e7f1bff0b00000003000000000b0000000300000000f84a19840af6f75283015497944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000014191a3bf5780082044d8080021f1ce93bb9218374d2ea45dbd2bc8d97f6fa5aad023474b71e12f2ccc583e03ab6cf9edaf16b142da8d07b44b1811aff429a4bdf6b0af997d0707f98f295061cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f01a840b695c7082e6e8944f9a0e7fd2bf6067db6994cf12e4495df938e6e987145b679690200084d0e30db082044d80804b2eb02436c30f483e54e8af28f2f1c59db7f5d56a24c83d0b3d2c4f47065ecf68f4895fb2a2e27e0af1b416d34da70f50affc5291e02a2a13f29ae09f92ab1a1cff0b00000003000000000b00000003000000000b0000000400000000f903f3818684015d6d20830399fa941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5b38fb2ebaa1e7167ed74018ce74a588d28794f3dbe74ab6185c751c3f31a2d81000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000007e4c51597dba086fd1a07751db43836a3acd20bf0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4269746765742057616c6c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d545050343333525078526b70515170476b5831365176573558694c6e334d46643833577a37524334764a647000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041aaddceedfb48b401c31abb8555f78cf18080778613ebbcace5332467725ef3096d0aff50b740abfb1372f7511caa6e46683774e7697c2c3fb360621b0aed16761b0000000000000000000000000000000000000000000000000000000000000082044d80804858cae0f925fe74ffa06631cc0e9bb9955a839c9eaa30bdd8b11a0b7b8de9371e19d31182948a1e2d730e3f9cab2f67d1a5af4e2a62f386d8951d34f94367f41bff0b00000003000000000b00000003000000000b0000000300000000f84a1b84086ce4f483012ee7944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d00000000000000000000000000000000000000000000000000145b679690200082044d80803ce9647fac84541c0f88f2635be838c489ac64d29edc0759905e1677a53a996c4ba90cf64b959dfe09642aa7a67fdaf495c1cd9cbee52efca91f26932b9506401bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f11c8409ef8bf783015963944f9a0e7fd2bf6067db6994cf12e4495df938e6e98713c0ca60f3800084d0e30db082044d80803e3309c130e2bbd0bc32bd9282681387467a68ef88003d71ef2fcba5fecce4c41cf843eb96e923daf4dddad4176d421115cf36e97ca5b794b7bb1330900ff1681bff0b0000000300000000f904351a8401d1168083040eb680870130ae43fff000b9041a60806040526040516103ba3803806103ba83398101604081905261002291610133565b813410156100765760405162461bcd60e51b815260206004820152601b60248201527f496e73756666696369656e74206465706c6f796d656e74206665650000000000604482015260640160405180910390fd5b600080546001600160a01b0319163317815560028290556040516001600160a01b0385169134919081818185875af1925050503d80600081146100d5576040519150601f19603f3d011682016040523d82523d6000602084013e6100da565b606091505b50506040805134815260208101849052428183015290513092506001600160a01b0386169133917f21e7cd7f82cd180cc2efc8f6d34988374ee142862fb2f9a8f60192e9b3cba1f99181900360600190a4505050610174565b600080600060608486031215610147578283fd5b83516001600160a01b038116811461015d578384fd5b602085015160409095015190969495509392505050565b610237806101836000396000f3fe60806040526004361061003f5760003560e01c8063b0910a5814610044578063c0129d431461006d578063efdee94f14610077578063f7b98453146100af575b600080fd5b34801561005057600080fd5b5061005a60025481565b6040519081526020015b60405180910390f35b6100756100dc565b005b34801561008357600080fd5b50600054610097906001600160a01b031681565b6040516001600160a01b039091168152602001610064565b3480156100bb57600080fd5b5061005a6100ca3660046101d3565b60016020526000908152604090205481565b6002543410156101285760405162461bcd60e51b8152602060048201526013602482015272496e73756666696369656e7420676d2066656560681b604482015260640160405180910390fd5b33600090815260016020526040808220429055815490516001600160a01b039091169134919081818185875af1925050503d8060008114610185576040519150601f19603f3d011682016040523d82523d6000602084013e61018a565b606091505b505060005460405142815230925033916001600160a01b0316907ffa30e9e1a69333c24ba36197dd28ca3e0c5f35e57ddd18903b4fd4ea7d4912339060200160405180910390a4565b6000602082840312156101e4578081fd5b81356001600160a01b03811681146101fa578182fd5b939250505056fea26469706673582212205084495e17d7e67c22e09899255168827489804e496d7d6f983ad2752ca58a2764736f6c63430008040033000000000000000000000000c451b0191351ce308fdfd779d73814c910fc5ecb000000000000000000000000000000000000000000000000000130ae43fff00000000000000000000000000000000000000000000000000000001e77d399980082044d8080e753eab3daad33e5406b6e9ccf7fdd0268c3eea1b2799d1052a1a16fbc83a6e317be62f32646d8c452718d299d38228c41dd5a1dc67961dbcd542bb551b391f41bff0b00000003000000000b0000000300000000f903f23584016cb4338303977b941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5a5b86aa0e7b0c70701b8db6d1e5e8a7ee65e36fb3fac08e68c65eaee771351f0000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000006158a9ffe6404eef6a2bdf331feba141f17235870000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007696d546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5579436d583974784a4545776f7756365646633566386e76576936717a414b46424d64767a6835535a334a340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004133474e9c238d06b566181d591c630a71d9e10eeb72ca063a40dadadcd0c0f6fd4d6b0326c4943466f3b886510d4a14429e394fc1a8abc8a0e2794ef07b91ca561c0000000000000000000000000000000000000000000000000000000000000082044d808030e5d2dbbab99819c571447b15a332129be20c6d875c480cad06e720a32b11d84bf57d2fc8d9eea3820202f47268b26a94e700afdb728edb396bd7312b8d8d621bff0b0000000300000000f90139058402625a00830284788080b9012560806040526000805461ffff1916905534801561001b57600080fd5b5060fb8061002a6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80630c55699c146037578063b49004e914605b575b600080fd5b60005460449061ffff1681565b60405161ffff909116815260200160405180910390f35b60616063565b005b60008054600191908190607a90849061ffff166096565b92506101000a81548161ffff021916908361ffff160217905550565b61ffff81811683821601908082111560be57634e487b7160e01b600052601160045260246000fd5b509291505056fea2646970667358221220666c87ec501268817295a4ca1fc6e3859faf241f38dd688f145135970920009264736f6c6343000812003382044d808044a18dbe91f5ed18370200a54bfa8738d390e49046e22faf1b6320fa25a8b23450dd1c97b91e2cac7fe4618bebc997644786d82b3432cb304df14213b6a782a11bfff9010c81b88404c4b40083032174942a3dd3eb832af982ec71669e178424b10dca2ede80b8e4cd586579000000000000000000000000000000000000000000000000000000000000000000000000000000000000000092de600f86669d5bc23fd33c4b93689d49c55aae000000000000000000000000000000000000000000000000a688906bd8b00000000000000000000000000000a2036f0538221a77a3937f1379699f44945018d0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000082044d8080689c42d280e7d7a501e3b8a03727d170815bb230e407c1adbe02c056943267013fe64d7ddd8e1dd996ec63c86a17964c9982076c6f51dbe46034e05b8cf2068f1bfff8491d840bdac5d982e3d2944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000013c0ca60f3800082044d8080f0658ba891342b29cb36b03d7c7f4ef697ab9453400c9bbc07e6689f2442a27d283e986cbe0569d209a00ce4a0bb790909b20727d04e17df540cad993cffaf681cff0b0000000300000000f9018c09840178e4608308474c9446a15b0b27311cedf172ab29e4f4766fbe7f436480b90164883164560000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000000000000000000000000000000000000000000064fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcc905fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcd6740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000112a8800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000112a880000000000000000000000000cf65cc37241d426970b91720e8255140ade00f050000000000000000000000000000000000000000000000000000000066e140ce82044d8080cce7a5d5d6aadae5e27baa53001df17380db45b57e5be0ace2a6cf6b062e722e32fadbd8ab50149b54214c9542ae78c7defd5928ab2f23fde10078dfb00c3e9f1cfff9014f830115508401b59f408305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13fb500000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e1000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000072f21faa37999cc861c82500000000000000000000000000000000000000000add83f3e7c6394511984a9080000082044d8080262b264f0820837db4a0fffcfe5309152b391bf1a80d105d564051c6bb5c5c644dd1e64849aadc9a6650a6d15dd17f1a0dfea656b7f190de92b7c3867d4c46b11cff0b0000000300000000f903f2038402625a008304e632941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5f1f3689562d1c0dab2e8a2cc0cb879b692ec71495e8cda15ae8d2ce622c7c6ea000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000000f9bccf01d2a27b0b6f7060c238a3c499814c7290000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c52616262792057616c6c657400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d597346367139725173466b42344d51453766334a35333843594748316f61696539766b5643616b573875697100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041547595cdab235f61e0e57fce4721337a52a434ab432daa2749642b01f769f773340113e8ed17a36f42e4fb82c5e8a9947fd69f29b12d76902d8b6091d7589e831c0000000000000000000000000000000000000000000000000000000000000082044d80801dafcb03a4ae839365fa5edd4e370763a965b1956859fbc950e985137707683f5cb0463918a45f865a13728c1d2785953a8820ae39ed5c5cce11c3d9f798155e1cff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000357f0b00000003000000000b00000003000000000b0000000300000000f01e8409868dda82c553944f9a0e7fd2bf6067db6994cf12e4495df938e6e98714138df7a0300084d0e30db082044d8080c3e8e282b8d2318bb3eacfb610c28212435b08a9a9a7957ccf3b7db78ccf6362259552814b9d6fadf088245eedf9894d2715ea943553730ee3a0e37de0d2bd8e1bff0b00000003000000000b0000000300000000e9068402625a0082ff7294e38fac030c30a806f1a18348cb35b1f9796b89bd8084b49004e982044d8080d4af995d1407374e10081bc81f21a015911814ec43220eb6a75ac5d347da563104c194b2dad08167c9b8018db7cb67cbd4730f7ae1ef58f508e1df803ca7738b1bff0b00000003000000000b00000003000000000b0000000300000000f8491f8407b3caf482ec15944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000014138df7a0300082044d80807a2a304783534f2b6627a011dcd5655a14f601f08f8ed1a5ceb48cfea15d246a1aa2513734725e55a1aebf47662192fb6b51f3ee33f5885df867aa9251519b951bff0b00000003000000000b0000000300000000ea1684015a5fe0830275e094c790f82bf6f8709aa4a56dc11afad7af7c2a986780843d18b91282044d80808d203c5fc7ac06efeaa4989ea373b55b32440f50b4969ed35eed578101407e2e485fbf235e553ff03b12aaa183b8ba39eefd649cba4a64d01ff298b6fbcc67331bff0b00000003000000000b0000000300000000f020840c0ac30582c5cc944f9a0e7fd2bf6067db6994cf12e4495df938e6e9871301cbf18c600084d0e30db082044d8080b2da0efc52a37be0b0abb72e2c878e2d894135e5903aa4b7510215dfc06644434894408779c15dc2e31b3c26bc9eedd420ba0b8cc62c91a40a79c93bc211b4781bffe9078402625a00829b4094e38fac030c30a806f1a18348cb35b1f9796b89bd8084b49004e982044d8080a96eb803ae5fa5e9bd6330e833372d275f55c50f2abd9a783c76011c171bb9a854f7ba60b09c1d684ed56f94254f87f6a3f064e1a1d542dbd09b67a36ea583611bff0b00000003000000000b00000003000000000b00000004000000000b0000000300000000f86c820582840175d720829d91946e6b8f03c407cfce46ba9847231fec40dce9308380b84440c10f1900000000000000000000000092db6815e62f6a8ef196306502c0283297f73b75000000000000000000000000000000000000000000000001f399b1438a10000082044d808097103c7391adfca69c78cbba1820b0bd440608827f76f970ebabee9770d106a325407087010a8ecceaf780e93d7db2140e934a94ff35ec90a2eca49a466f852e1bff0b0000000300000000f84921840aa60ab382e3b8944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d000000000000000000000000000000000000000000000000001301cbf18c600082044d8080a93e5a2fcd9fc6e8431efecde6b5119e2ed7bdfbce535819481e7c4a8afe7afa48bb9219bd7bf591c3b4c1677320ef5961415c7192e2e564945cb87cdceb51661cff0b00000003000000000b0000000300000000ee8308be6e840175d72082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d808024468cb79bb0bdcd83559601af894fea6b58b2c042c87a274da30b03304085ad289f7d82b0e6df87cfdf16b69a1671915fb2fb2779831748932db2ef39172c301caa0b00000003000000000b0000000300000000f86c82058384017d7840829d91946e6b8f03c407cfce46ba9847231fec40dce9308380b84440c10f1900000000000000000000000092db6815e62f6a8ef196306502c0283297f73b75000000000000000000000000000000000000000000000003afb087b87690000082044d8080079b0b01fc85f171207c07f0864e86783059697bffc98962dc0573ae40c413786a55a9c8e8cb9e58d24936de95f05fa3f28cac23efb6de2f65f455652c45cd631bfff909ae82129684017d7840830993579489e830972c7b453d4715ba1d3894aa096f219f0280b9098457ffc8610000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000006ac983efafa917bf04342e3684a863c9212e1fe9642eba19e81b0eab36c223645f2b84aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000000000000000000000000000012659eafec31743a8a1b09826b378842d4b42910da64ae7c0be9774b3490711b80e76dc2d6e18b40e24de42f1bb55971bd666fdeb1a4a463c0a4154a072796517000000000000000000000000000000000000000000000000000000000000094000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000004e70004b01987b4fd7dacab348f0b39e9ae261100000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000bf4f119c5bbfe0000000000000000000000000000000000000000000000000000000000000066e13ff10000000000000000000000000000000000000000000000000000000000000002afa96d133fb7ca1e775405c5ec53a83986cc81317bfa06200d34061675b76adced276e8201ff7defde2bac4a9e387413b3da71f8d1de09ebac9a5490ed0bc189000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004e70004b01987b4fd7dacab348f0b39e9ae26110000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000007e6125df4bd46800000000000000000000000000000000000000000000000000000000000066e13ff1000000000000000000000000000000000000000000000000000000000000000283ca785cb646b1762da8d7c98b01f56e69f4ffe07463f3ece57a61331ca1817bed276e8201ff7defde2bac4a9e387413b3da71f8d1de09ebac9a5490ed0bc189000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000004e70004b01987b4fd7dacab348f0b39e9ae2611000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000ef4ebe4c98404800000000000000000000000000000000000000000000000000000000066e13ff100000000000000000000000000000000000000000000000000000000000000026957de8f52722139f85b1fc83e50f75e52000cfd5e2f6a1814eaac236579b00bfce0e08ce4fc8588953f31682cb0cbddf9b74ab8f00f2e8ff6837ebeaa953f31000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000040000000000000000000000004e70004b01987b4fd7dacab348f0b39e9ae26110000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000f3cd9dd0f7b2b000000000000000000000000000000000000000000000000000000000066e13ff1000000000000000000000000000000000000000000000000000000000000000228f70f83d2d2c2012ccc8e6d043c4421144c676e19ad77b3f5d06a55532ea2d9fce0e08ce4fc8588953f31682cb0cbddf9b74ab8f00f2e8ff6837ebeaa953f310000000000000000000000000000000000000000000000000000000000000002ff0100000000000000000000000000000000000000000000000000000000000082044d8080a928627bca2a83841e6a08cac57dc3f01cce3d139d5371895f14a66f284721a3598e5b04b9282a7b1b5abd1c4b80e125256206fa48c41c6203b40defbcfbd8e01cff0b00000004000000000b0000000300000000f122840a1b74f88301535c944f9a0e7fd2bf6067db6994cf12e4495df938e6e98712a07b108eb00084d0e30db082044d8080b672c58ef0e2fb2431dfc2e188bdaccc549afd9453f907295a9b78242cc392e20797400e2cadd16e89f6490992082c84dd9200eaf4135c3af743c8eed63212d01bffea81cd8402625a0082ef4094b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080cf5c96744631e4660952c51533ff1331d8f92e6f5dfe5fc50e8ae59af5d654ba0992777ecad55c9fa7751777c9674d850059586adf1e25263c674a6affd8fbc51bff0b00000003000000000b00000003000000000b0000000300000000f86c82058484017d7840829d91946e6b8f03c407cfce46ba9847231fec40dce9308380b84440c10f1900000000000000000000000092db6815e62f6a8ef196306502c0283297f73b75000000000000000000000000000000000000000000000001ae361fc1451c000082044d80806d429321175f5602fd6772119c4ca54dc2ebf6837a3d2c7da3cf16e92cbee35f13d4a4f4d2a454164430f73b7c646d1a9ba2589463c8c4a01fbfb57190756a461bff0b00000003000000000b0000000300000000f86c820585840186a000829d91946e6b8f03c407cfce46ba9847231fec40dce9308380b84440c10f1900000000000000000000000092db6815e62f6a8ef196306502c0283297f73b75000000000000000000000000000000000000000000000006d499ec6c6338000082044d8080e7af27a209e255c5d8dac95f112ba887fae9d1e14a2e4bd2ab28c0db0405add0789ea99836c9f7006838a5c8a343c7910fa04fe62359c5840021a1e8ecacb5cb1bff0b0000000300000000f84a23840929944583010d17944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000012a07b108eb00082044d808070235f900cc55b78e1e6928ef4ce66827bf7fe71c143477be736d589379811ac253eaa21eeb61a16813aa811386885afe8290602dd5690383627722ba9d7e2211cfff86b188402c37cf0830243db941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b300000000000000000000000057df6092665eb6058de53939612413ff4b09114effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d8080c40b5b896d81653b619a8913d90eee62c5d619b4962ecdee41ea9832aa95020349132258d75204c62c8fc76d9f8283b1326d4ee913a0b723657c54fbfb1bb2f71bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9048c198402c7c8ea83135048946b2c0c7be2048daa9b5527982c29f48062b34d5880b90464b80c2f0900000000000000000000000000000000000000000000000000313295693672400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000a16c02000000000000000000000000000000000000000000000000000feb937a2911570000000000000000000000000000000000000000000000000000000066e14e3200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000a16c02000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a500000000000000000000000000000000000000000000000000000000000000010000000000000000000027104412c7152c658967a3360f0a1472e701bdbeca9e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000082044d8080b3e971ff6fd58ceb07da1d98787e1144e20bbb3a4bc4898427f95bb1883b209b79f23b51651aa2197d379428541ad7ab7f2baed6ea46c94ee62fe302d16eb4c41cff0b00000003000000000b00000003000000000b0000000300000000ee8308be6f8401820c2082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80805c8c929f5ee6b9d6d3ee54c1745ed9009dac82133aa764d8b66f952ce4af2e0072573744d41b291cc6384aa9c9b5057a4a1d979fdab2c590ea251fcdb75fc49d1bac0b00000003000000000b0000000300000000f9018f830151da8401a8a6f08302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000050b52227f968abe8cba85ec45a4bda8f65d1d1670000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000015b392fb030c00000000000000000000000000000000000000000000000000000000000000018da49930ea8edc4bf2ba83646893e29e20703a5b2534c8e97ce446e41bc6ffe682044d8080c1a1b89b29b44083c19bc4b74e77b2a9190047f3ed5ce33198f142333eb479ab19eb434a8c761191c6fe9f02162be2d0bb5e163eb2b8528d493c44413fc512671cfff86d82010584018e41208304f8d394dfb6baa334712cbbeb26b7537f62b81c2a87b1e880b8446e4bb3b5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000182044d8080fc53deb3a49af4a7f3bdbe490ec121cf4b137ee3e28bce917494eabf24b148b91aeaf809a6a552b1e7bcebbb955ed6a54d004ce4fa883b0d53c9faac21cc542b1bff0b00000003000000000b00000003000000000b00000004000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903f220840186a00083030009941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5e9924268a07bf7d21c823217668de1706f45cf6eedfaf6528e79c479eb926f00000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000cf6ea21f3928460e36e30150815f212fca2f9a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5166436f4d73464e784d7274756a31507054614c444261554c586551694453626b5a63524533687839516b68000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000418550cca2cf3bfcec208c53cdd1cc838b0ffb5c5256bba0d4166875e47c099b175f181a3a95b5c0706963513659d182ba900782a379d0eb74715719e59a6f58c11c0000000000000000000000000000000000000000000000000000000000000082044d8080e58acba608a369d18c6d73f43366584f723671662e22cd7bb1807671b3d2f71f03a1f7a8ef7d198a54345cbb4f226888cc8867e55b6b20e231e0228d71d79cb71cfff8b12b84025317c08304ac1194222228060e7efbb1d78bb5d454581910e392222286043d112b3c31b88432accbb900000000000000000000000000000000000000000000000000000000000000e6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b48eb57e000000000000000000000000000ea6e4548bb97b9237fd08bd32ad9a6f8e59eb10b82044d80808c2c47c45393f93b84a8a00f3e965a8a3a532e4d03d0da39a78f6ed20f91faf017d9d1405441a4a8b6d81421ff34ce60c607ff5843b029907fbeb58ee371f34e1cff0b0000000300000000f903f22184028dd3a78309e223941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f53d6aea38052471c5a2ab59bb49640603e17c99abdaa8a597c19480f99e27963d000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000000673962924875bd4cbd2e0b22ed63c85cecac2e40000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4f4b582057616c6c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d55316142655a5a6139787756357746507855734a37416d6276714a38574a784375656f4452666567595a42790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004159b0d36df5531ae9fad1f1382129122fb7cdf909728e403412134e87d1c1542a4d2a58cc08a4005b8f8dfa3a594103b3176813122f947790cd52a2fe8df5f5c41c0000000000000000000000000000000000000000000000000000000000000082044d80805c95ef7a6c28a8e2e82f28d6d894b5b9ee5171b03a1c22968ce432462eede80b24163ce4e126a85062e602cd6e83bc9fa4855353b979476e4e8a6d495d56d5f21bff0b00000003000000000b0000000300000000ea819d8401a39de0827181943f0a9f960341dc53747d43d27e4e1a2678f7983e8084753899e982044d80803e62d33d344773f096582827acedb468e005027390ecd7e427d3bfee95cc734e4932744bfea8adcf4dabaebb2115f1d742b8f346984fc496f7e7a795cb71d6f31bff0b00000003000000000b00000003000000000b0000000300000000f86e8301c9918401aa54a0830186a09465a4b8a0927c7fd899aed24356bf83810f7b9a3f80b844535b355c000000000000000000000000a703654c31dd3bc26dff3c6ec524540c602380390000000000000000000000000000000000000000000000000002659289813c0082044d80804de874ddea4656d09ad455f64143e57454f56e7191364e43bbbceabefc4ca03002f35052b4e2b6d4be7b637a6dcb4c3b37d6e536f7a5dcc64b147469ade0037c1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f8cb0c840b7f6358830a623f94c28a4ec9f09e4071e3707eaaca5c3754fa4f5faa80b8a48fb8b6c780b56aeeab2dc46113b09dd8069578d16c679cf639475dd6e0b8e60b7a3b21d9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000082044d808014cb2bfb965493c5dca083a997eec444184bd9f3f79aa4c555bae2f5fe5d567058f2dc8d1eb1aa371a97d86c65095e572232021dc7a3d453c2a2ec11c955092d1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903f2248402625a0083048056941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5f51a6c4d02c18d30b29fa792091edfdeeb4a51d55a18c953784a33e54d7fe50a000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000f65a8d44c6ec9823ff7be42b3cac3b457ae8d5200000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c52616262792057616c6c657400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d64696179665a6f6f7a676a64625374764a657a48467664325a39384e656669517736594a4144456a56554e4100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b37d89a9353bd6b5183ab63e9080c97cb5a24fc814b5fadf0d531acd6272dcdf00a568d77e7e5ef3b1821820166377891930eddcfd50fde885affcc1320f61d31b0000000000000000000000000000000000000000000000000000000000000082044d808076fb7660fdc1ef605bc860a4d1b98537f6bb3c18c507dffec61c663c14950ee06415ecdb58bfe14b8642ce2d909378beda578c5353356a559a6ae1f176eb204f1cffec0184028e2965825208944570f01acc78003c270b68217b819d783cc892f487179d7b0b84e6678082044d8080829f6ee74f6fcf2ad05e10267d898068704c651ec8c3af9bc1b06d48b721e8ed5d928a25a8942496fa4b557e61bf6e6552e5d95c53ae3d1ec9bcd843379689381baa0b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be708404c63aa082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080331d3409eb28ac0f3aa116833654fa85ebc0ce15d7d6045703ea5cabed548b2e15c7a437215b8e74d70939efc3842b0dd571788b3abd34449555b16d18c6b6c11caa0b00000003000000000b0000000400000000ee8308be7184019768e082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80804993bb9121b4238b115f0591a929482dd9ff7c2e6974e85901a475fef18636417feb9d1551cfb7d465738a2d001cb5f7ba47e65fd4c1ab2e6d67117fa835a3361caa0b00000003000000000b00000003000000000b0000000300000000f9018f830151db8401b466c08302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000010000000000000000000000003e5cb35901d40efb3c6d38aacfb0f4b0cd5258650000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000045cb32e45f28000000000000000000000000000000000000000000000000000000000000000142b576c33464f5ecbf09fa2b1575d07772c814bcebababe6c099862cafb4fccb82044d80805dd0484cc33cf12d031a9a675192d937ebc6aa79432bdedb9190f4fe84410b6029c8935f77a6f8cc99964f47afdeb4c41790eeeb636babf8e3f034742f8c98b81cff0b00000003000000000b0000000300000000f00e840172c9e0830139f594b58f5110855fbef7a715d325d60543e7d4c1814386a758d6a38000841249c58b82044d808045d3b812f522cf8f1b5a670f65c1fe24a0806c563eb3dcd9ad2bffb3e2aceeaa0884dea66e76f89a5047243e18d3bacc1a849ec983f640a958ee410c804916741bff0b0000000300000000f382010684019768e08305576d94dfb6baa334712cbbeb26b7537f62b81c2a87b1e887254db1c224400084e9ed3d3082044d80800b1d6a122d7289631fdfb4b72e4a6a86c293afa79c1f3337c9a188badcf82dbb2efba7d16402ebaa03b0059aa68f7f3a445d2fd3425cab4179d50a9d06df0aca1bffec81a78402faf0808252089433fd1acc271574f6e5c2eb4a0ea1264251ba2119860abb620a3c008082044d8080c9a1e1b5d798f7af1ffbd19e89c74fa39119c8b261e9f84fa39d9af2c67200d43ee954e07bf11fb21588f5d62e6c5c7dd8da23de14658664a4389b4fefd13f6d1cab0b0000000300000000f903f21f8402625a0083048056941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f522f0b9754d4d2585628043451776bc2eb65ac80a097992cdd46586be7aad1739000000000000000000000000000000000000000000000000000044364c5bb00000000000000000000000000033fd1acc271574f6e5c2eb4a0ea1264251ba21190000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c52616262792057616c6c657400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5374533974704d3479584e42543463474437554b4a77616a4b67443363444b7971347a71764e45657248434d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414ed93ff5c116db4089def64910db6449c30e8196e78031d25816d7e4f92b119f62e583f8b2003c9b042427ace41ef3a6a1619130f2fcc00fee151d0acbc804191b0000000000000000000000000000000000000000000000000000000000000082044d80805341f33dd6e64568431b0846bbc656fdc618ce107b9812dd2bf3f2e91c44f0240c3281cd38136e751fee0902452bca959da52d667a092d2fbc70ef493af7cef81bff00000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040110b00000000000000000b0000000300000000f9014f830115518401bcf240830596219473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e140a500000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000731412d863de818aedc2ac00000000000000000000000000000000000000000ae12c46db9b6fc047c306c500000082044d80801d46c877c9b34d2ab10700459c548bbdd97b4c71ddaaae59eb4aa9829063a3456742ada3207659a4d8499dbb6600f91b9e7ecef2807fb739d2a63af6538555161cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f901b30f84019bfcc083039dd894b58f5110855fbef7a715d325d60543e7d4c18143870b7f8bdfda5884b901845190563600000000000000000000000090321b082c1da51d0ec59ec7d02f5b65d1c70ed3000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000b7951c00000000000000000000000090321b082c1da51d0ec59ec7d02f5b65d1c70ed300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001490321b082c1da51d0ec59ec7d02f5b65d1c70ed3000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200010000000000000000000000000000000000000000000000000000000000061a8000000000000000000000000000000000000000000000000000000000000082044d8080fc542a4b3b5caadfd5c04117a3f8512c467f8a59866aaea320aa3b5bd891f92b068e84eeb6846cea45c443f478e7c48968d14a2034cf6ea9005facf652f51ea91bff0b00000003000000000b0000000400000000f86c81d08402625a0083011108940d1e753a25ebda689453309112904807625befbe80b844095ea7b300000000000000000000000031c2f6fcff4f8759b3bd5bf0e1084a055615c768ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d8080a984967c135177e8261c40769322acd476a9a79a07d7266cf441b0329b3b009533e8693879324ede3e8028f2baa8c51ea3aa1212ad5bf5c94dc49f14a2831ff61bffef808402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109863691d6afc000840333f63e82044d80803f154443bd814efa2e144586740cbb0024d5f3ea553b60c131823aa61e2be4e73adab50e89b656b3c7e4c9e2f511754dc45d14ee7894de88498f2cd23e825a501bff0b00000003000000000b00000003000000000b000000030000ae460b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9044d81d18402625a0083044f4e94b89a6778d1efe7a5b7096757a21b810cc2886fa180b904243593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e141e700000000000000000000000000000000000000000000000000000000000000030a080c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000d1e753a25ebda689453309112904807625befbe000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006708cdc50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b89a6778d1efe7a5b7096757a21b810cc2886fa10000000000000000000000000000000000000000000000000000000066e147cd00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000041ce9c062459de7685af53cae3d9c022ce609093f04ce875111314f8aa1366bfd579a369788bb5ca6b5cf908cff33ffda44e1afa0c4165caab252665bb02ece9861b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000007355b876be771900000000000000000000000000000000000000000000000000001541e01138ed100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000d1e753a25ebda689453309112904807625befbe0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ab07bd0ac2502a7fd0a5005b420b96f94104fcf80000000000000000000000000000000000000000000000000001541e01138ed182044d808002d7ed117ceaf8a71c9325711c8ebe173ffb02a537a631170f2bbb140ad8532b2e3f3dcec0e5c85384fbb643e3508cce4625696ffedfb0f5d16c517a11ea1bdd1bff0b00000003000000000b0000000300000000f86a808402d8fce182c16f94652a38fa87f60a122aef360eeefcaf6258eddf6a80b844095ea7b30000000000000000000000000a6b1904369fe59e002ad0713ae89d4e3df5a7cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d808096c77cebc8ba97dbcb33a46b05001e3f26206f91c497007900507d8297919a5e042728715790ea437a06e937ffb322b65031db0ba633dcbe0351a1ca78a70c991cff0b00000003000000000b0000000300000000f86b1a8402d06b49830243db941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b300000000000000000000000057df6092665eb6058de53939612413ff4b09114effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d8080005ee956dfbd7ecdf8ca2c5fa9b5a21aedb2a425d4d1568032332b5ec3e689fa1988b83c9fc83a17142953dfcfd62d2e79c15f761b013b042537a77602933ddf1cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f90a6e8204ae84019a762083023cc494709944a48caf83535e43471680fda4905fb3920a80b90a44437b91160000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000074000000000000000000000000000000000000000000000000000000000000008e000000000000000000000000000000000000000000000000000000000000001641a0a0b3e000000000000000000000000c52eea00154b4ff1ebbf8ba39fde37f1ac3b9fd46575f2c29a636fa55eaa8aa86706b6d3c33a21e0e79e1647259718dac070efc40000000000000000000000000000000000000000000000000000000066e140ab00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000937596f639e540000000000000000000000000000000000000000000000000000000000000004172b3eb5cd3fc78267d5672b9ad5317286aa8f3d66e55d866dcdbc9e5d1255c6d26535ad3d1b26b4b8fbc6bfe8cc543d3dd612138909a4aeb47117f908bbb30321c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001641a0a0b3e000000000000000000000000bc6471e88d8afe936a45beb8bd20a210ebef68221998d1c5e381c1edec0feec35e8bc3c9d7cb39ace96d9279b6f9a07a7ea454240000000000000000000000000000000000000000000000000000000066e140a800000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000009463846edd3200000000000000000000000000000000000000000000000000000000000000004168362b70473277d046be65bc395f0b0c8b98173b46adb4192c6f4127df5d1867494705d3ceac8368afe2ece88a7545794f4e51fd431667febdddfd66a2b152aa1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001641a0a0b3e000000000000000000000000a924847354c551c79bae7e75529364ba0449e51a39001761c965b7719e6b39822f09c891d103009d2703a4989cf7bc2e007582fc0000000000000000000000000000000000000000000000000000000066e140ac00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000945b55a7e0000000000000000000000000000000000000000000000000000000000000000000413dc56655400801a9c210ecdecebbc3da45c92de696f13b97bf5e73b6044e27366fa1e651e74e5f57d6a610791ec51a8eb5f2e8c482a5b5e22f939c0f0b1eeb391c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001641a0a0b3e000000000000000000000000c9b494d3c6ea3fd42779df9a136db10374c98d80c7dd05baff9d2948c0f215ffa84c4de82694e959f2fa7b70f2f2a1f398381e900000000000000000000000000000000000000000000000000000000066e140af00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000009455be8a47413a000000000000000000000000000000000000000000000000000000000000004152452f2785fe8cc8876e4d7c70e6656fa7d2fd5d48d1ba751829ec73a2ac5a607e300939c863047c82dc39ad2e28db75546263bf6c8fa3aa3ae7009607e8db191c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001641a0a0b3e0000000000000000000000006b56e47dccfbc82d63df3da417d26e8b1b877f0f5d7cab00186de4dd836b0e3f89da0565a5a68597e1772792cd3c63dc4505d9bb0000000000000000000000000000000000000000000000000000000066e140a700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000093a95d5afc400000000000000000000000000000000000000000000000000000000000000000418ac1031836570d4ca35d6a2cbfc9e7cc8ee5da912831a8769c87b4b5df1b74604428cea4bc23413a82b246008ae57b93c1f917ab594b6c63c60b009db44cfe871c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e400aae33f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005750fe3cc44fb71eb6dcda12a40ba96a779ff3292e4ee0b83e480607fa18a253c25dc66d59c09b65362270bd4ba318502886dd51323d42cc672bb118a2f377d937701240fd1a97e184401c372d22d78050175399403b5fde50faf03bab1c7573ac1c918bd24675fed06d7c1ca44882492b3ae0462c3b0c6e7cae84702c2d9708be6812ce7d565ac54474e619953d3b2588ad818c5b7328ec16de6304fa43badee0000000000000000000000000000000000000000000000000000000082044d8080aa51d9561dc2f3f50bc646119dc5d2288be28526b01facb218bfe12777bb470b73d45a28e9f367be9c9ab1e9823735251e7c58ab8b527dd8864c32531d9ef4431cff0b0000000300000000f8723c8401a524808301d9779454c3ef8ce719ed72c2050ee42a05aca9e9aa1e1f8701c6bf52634000b8441d7df19100000000000000000000000030280ba7411118147b835ea31029481b01284175000000000000000000000000000000000000000000000000000000000000000182044d8080c4a02d3c640f35c2628bf24777bcb8157adac99f25a33a66aa0833e12e0692ba77820fa9aa795375809f045555ba5cc8167c7891ed03506dc2c67b64ee7112be1bff0b0000000300000000f86c81d28402625a00830110e194a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03580b844095ea7b300000000000000000000000031c2f6fcff4f8759b3bd5bf0e1084a055615c768ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d80804660394e29ceeb7d28d161134a042cb966b23c6ed12f2bf645022ada6f27fc3143c01a84374564d949e10dc0df1c9c103f78651b831bd12482d8e3442f024a881cff0b0000000300000000f9048c1b8402c9f9b583138a26946b2c0c7be2048daa9b5527982c29f48062b34d5880b90464b80c2f090000000000000000000000000000000000000000000000000031329898bf72400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000990e21000000000000000000000000000000000000000000000000000f1b8b1e74b2040000000000000000000000000000000000000000000000000000000066e14f0200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000990e21000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a500000000000000000000000000000000000000000000000000000000000000010000000000000000000027104412c7152c658967a3360f0a1472e701bdbeca9e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000082044d8080c52fcc136946cb5b9089d62a52d13d661c9588fb367523e6404b34932fc651fd108b70a62f080f0a280357b74a7eabc52b5b807bee6d4ee85f85bdffdce996441bff0b0000000300000000f8cb018402c9f9b58302724d940a6b1904369fe59e002ad0713ae89d4e3df5a7cf80b8a491695586000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003148e44ea107a00000000000000000000000000000000000000000000000000031167b0f044b80000000000000000000000000000000000000000000000000000000066ea7b6882044d8080c54a7d2f5e3aaa2f9e932ea0b5c6fbdb5166c3bb4226725e8cf0c5acbb677a637ce0e52990ef54755786e983d449c7c6adc824f57fe7a50f4960fba85ec8d44c1cfff86b198402c4c487830243db941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b300000000000000000000000057df6092665eb6058de53939612413ff4b09114effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d80805a91813eb1f40d2394eee6ad6410d547b8189e6be17bf23c90dbc1d01ba6edff103a22f55e0986304b0d36b367e905b3a5ecbf94755bb2670b563e25b6d174921cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9048c1a8402b3d0168313849b946b2c0c7be2048daa9b5527982c29f48062b34d5880b90464b80c2f0900000000000000000000000000000000000000000000000000313298f36971400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000991c85000000000000000000000000000000000000000000000000000f1cf6bcc4e4c20000000000000000000000000000000000000000000000000000000066e14f1a00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000991c85000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a500000000000000000000000000000000000000000000000000000000000000010000000000000000000027104412c7152c658967a3360f0a1472e701bdbeca9e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000082044d8080f24aed3d22f15fbcc57fafe44d53a1a9e60f4ba51291fa4f22b53e20d79ddfea5553060fbced61fca5aa4d1d333d067b1c3904ecae9d9c71c47d5693a257e8cc1bff0b00000003000000000b00000003000000000b00000003000000000b0000000400000000f9044d81d38402faf08083052a5194b89a6778d1efe7a5b7096757a21b810cc2886fa180b904243593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e1422900000000000000000000000000000000000000000000000000000000000000030a000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc035000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006708cdfc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b89a6778d1efe7a5b7096757a21b810cc2886fa10000000000000000000000000000000000000000000000000000000066e1480400000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000004159610a05dec35ea87031acdfdc775d81b7d21424be26f4198d7963c054b6486102f7bb552f766607a24314a4d4674ec5a8b6cae07000ecc918b6ef1b92b1dad51c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000002a055b00000000000000000000000000000000000000000000000000042ae8f761201200000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ab07bd0ac2502a7fd0a5005b420b96f94104fcf800000000000000000000000000000000000000000000000000042ae8f761201282044d8080fa6c7ff44fb469090f7f796a4dd27d3dee25540259f1b8307ce80915f329281f1643255ab2b3498b2827900ca99996c2cf60c4e2b058cab244d2783263fc07c31bff0b00000003000000000b0000000300000000ee8308be728401a6ab2082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80808cae305056836f1b109e76a38a29a95e699ca239c636e820c5221cc0bfc1ae8e08b630842cda6c38fb50cbb4676c4a61e00b01f55b68f27f51123de8cffd652e1baae96a8402faf08082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080d0a9dbdd118b85ea2dd4a860354598e348085c2c448ffe77b9b9ebfc87bbd4b3416de323e24a7e3ff04007cad827b524e6054943ac7f7d51942392f1c1b9199f1bff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903f2028402faf0808304e5ea941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5fb2f110de3cb09ff94ef99619912ce386a0f42d2f466e7d3e480d7129bacc121000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000a703654c31dd3bc26dff3c6ec524540c602380390000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008496e6a65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d55656151777944734138474a4858594c794248344333565564504d6f5142474c457a3648476d423152696f5600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f2eb6584e3d4bfdae352806a3a5f344ad70fe77bacc930a7a40b925f409ffe4f4c8a88c6e07314b8a109757251949b0932d806ef8a608b54f8dee0f447ba37381c0000000000000000000000000000000000000000000000000000000000000082044d8080f2eed7742d047224403c36decbbc5c7a6f1bf7f67057cb078633cf30c96449ac7d2a665aba4252c99bba96742960a7228167514b354f24d9a97b6f5777197bb11cff0b00000003000000000b00000003000000000b0000000300000000e93a8402faf08082ef4094b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080f512c7a6222205683b9012666c5455c998de80ca017dcae80b4b8353f2d9c6e7444e694afe71445e1f73011c9416982fcd2013de37f5a6356b62d4786b2e01dd1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be738401acc5a082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80800d61afecc903851650d8243af5e3ec20654db98d9c0fe14f7cdb86d9e50f2fd7188796e923b771c47a7674636d3a9b4e14fce759226a2445a470836e8aeb1fc31babf9014f830115528401f78a408305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e1415a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e1000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000072fa529d46831fa4ea1365c0000000000000000000000000000000000000000ae065f41c16c30440f13ba800000082044d80806bfcacaee014344f6c6bf79fa3af76f7baf85705a22c6b6022aa3d2275044f515e385563fc9f791dd33b51658d81b061b0e582896fd33f745f00b4208500200a1cff0b00000003000000000b0000000300000000ec038402faf080827b0c945e809a85aa182a9921edd10a4163745bb3e362848704adcdf52b50068082044d808072e2f7ccdfbf0c9967dc8d2983a80a846f075f22068fc67764df88c72dfc3f206fa80b91470d803b7f94b181278d72809f042b83b4eb3b242998ac89f12491ee1caa0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000400000000e9768402faf08082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080f7c406958a3b283f040ba8e3b99e77961e478a6849858a513f9950fe2474271660a78cdff6ac241cd222b97a27685de4da616036e123ebe6947bc531590760df1bff0b0000000300000000f86c81d48402faf080830163f89437eaa0ef3549a5bb7d431be78a3d99bd360d19e580b844095ea7b300000000000000000000000031c2f6fcff4f8759b3bd5bf0e1084a055615c76800000000000000000000000000000000000000000000000000000000000927c082044d8080e3b16d5bfd529eaefa4d8ad988900a3ac14748b9b787645fe42498619e4b85001f4ec92e43b5ccdd3e61780fee963ddf0fbaa9b6b7bde93e6e6bbda3abed8d811cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d8263678401ab7d80831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc651188000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e143f80000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000098176ab6ed58bc000000000000000000000000000000000000000000000000000000000000000082044d8080f44f214824c6406324f3caf06ca6aad692bfbce83fb69685723885047c1978a0009f698a2561f6fd301af75d83ba928ae9718dd2d175bee9e2a70260a91980151cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be748404cace8082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80802c732f67940b07b16d4ff7c2ed509c81df86e1aa948e04a97f4c1cc90de679a07a50c2c1df215261e32b211a422ec0cc62994adcc02962b201a058db5485e0661bab0b00000003000000000b0000000300000000ee8308be75840198ef8082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080365e902ec6677c927098c294ccd1808009bd6baacd6cc18272b1f8e4a3a88a940c88b52ac0c9568948d082d6298474169873f7fa0dc09bd1db8b6be09ec6016d1cab0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f86a1384018e412082b4cb94c5015b9d9161dca7e18e32f6f25c4ad850731fd480b844095ea7b30000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae0000000000000000000000000000000000000000000000003ddd14cefbe04ae482044d808090ed685bc17c39f23da886bec59a15265f557d06b5388c26f489d199e2c9dbbd4e5964f35d8272171d695b20f1aece1e56bad6a0f93b5992ff69f774527ac2751cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9088c1484026ccc90830c45a3941231deb6f5749ef6ce6943a275a1d3e7486f4eae80b908642c57e884ba378f2fce937d7762985fbb68cb4ae55a699ae984949e2401b561a7327abe8f00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000003f2825e0ffd694adee76c9581fe707606074aa900000000000000000000000000000000000000000000000000006b01f2257e4a2000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000086d746f7073776170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863346639646134323361626336393237393937383338313533656131386631616266396435613938000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000b49ead76fe09967d7ca0dbcef3c3a06eb3aa0cb4000000000000000000000000b49ead76fe09967d7ca0dbcef3c3a06eb3aa0cb4000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd40000000000000000000000000000000000000000000000003ddd14cefbe04ae400000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000084eedd56e1000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000000000000000000000000000001fac95eaffefc40000000000000000000000000000000000000000000000000007eb257abffbf1000000000000000000000000c4f9da423abc6927997838153ea18f1abf9d5a98000000000000000000000000000000000000000000000000000000000000000000000000000000006a000f20005980200259b80c51020030400010680000000000000000000000006a000f20005980200259b80c5102003040001068000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003db57d1396205f2f00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e4e3ead59e0000000000000000000000005f0000d4780a00d2dce0a00004000800cb0e5041000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000003db57d1396205f2f0000000000000000000000000000000000000000000000000006b01f2257e4a20000000000000000000000000000000000000000000000000006c16a70441813d770acd5d9f04dc897d42ce2f295f18700000000000000000000000000f2a81500000000000000000000000000000000000000000000000000000000000000008c208b7b5625d78deb49240ef28126cbe2738098100000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000240f6ad3ccf71abb3e12becf6b3d2a74c963859adcd00000140008401000000000b00000000000000000000000000000000000000000000000000000000c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000005f0000d4780a00d2dce0a00004000800cb0e50410000000000000000000000000000000000000000000000000000000066ea7c4c0000000000000000000000000000000000000000000000003db57d1396205f2f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003cc5015b9d9161dca7e18e32f6f25c4ad850731fd4a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0354f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000004000040000ff000007000000000000000000000000000000000000000000000000000000002e1a7d4d0000000000000000000000000000000000000000000000000006c16a704418136a000f20005980200259b80c51020030400010680000002000000000ff04000900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080d8991732646a94259a6feeec42e5a4fc65fd2a806c109c58b6da6c6b9aa175a024d45fb5eb3d774b1b9a50a6ee7dd40b8104d1caec3bdbe82885a18aea9308621bff0b0000000300000000f8b12e840263c1608304ac1194222228060e7efbb1d78bb5d454581910e39222228614edc409dd73b88432accbb900000000000000000000000000000000000000000000000000000000000000c600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000246139ca80000000000000000000000000000532b4600bc0667326bbec62c1f55a4e962b21a782044d8080dd3a56e7bb40299dab4d821c8860cde90383a218003cf93ada19e6ac7762fbb2248a75809c92a48152c303d8197289a58956d0318ae6d816ba67054d954ad5181cff0b0000000300000000000000000000000000000000000000") + EthermanMock.EXPECT().TransactionByHash(ctx, common.HexToHash("0x22ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba97")).Return(txByHashResponse, true, nil).AnyTimes() + + storageAtResponse := []byte{} + EthermanMock.EXPECT().StorageAt(ctx, common.HexToAddress("0x000"), common.HexToHash("0xb5ad54240dc61c51d3a3e8d3f925722e010966ae263d67344c5fb60bddebddae"), nil).Return(storageAtResponse, nil).AnyTimes() + + var response2 []byte + response2 = append(response2, accInputHash.Bytes()...) + response2 = append(response2, common.Hash{}.Bytes()...) + response2 = append(response2, common.FromHex(fmt.Sprintf("0x%064x", 0))...) + EthermanMock.EXPECT().CallContract(ctx, ethereum.CallMsg{ + To: &common.Address{}, + Data: common.FromHex("0x25280169" + fmt.Sprintf("%064x", 1) + fmt.Sprintf("%064x", 0)), + }, nil).Return(response2, nil).AnyTimes() + + rawBatch, err := zkEvmImpl.GetBatchByNumber(ctx, 1, nil) + assert.NoError(err) + var batch *rpctypes.Batch + err = json.Unmarshal(rawBatch, &batch) + assert.NoError(err) + t.Logf("batch: %+v", batch) + assert.Equal(rpctypes.ArgUint64(1), batch.Number) + assert.Equal(common.HexToAddress("0x761d53b47334bEe6612c0Bd1467FB881435375B2"), batch.Coinbase) + assert.Equal(common.HexToHash("0x551b6fdb2b0c156b104a946f815c3e2c87324be35fba14cf0ed3e4c1287d89bf"), batch.StateRoot) + assert.Equal(gers[len(gers)-1], batch.GlobalExitRoot) + assert.Equal(mainnetExitRoots[len(mainnetExitRoots)-1], batch.MainnetExitRoot) + assert.Equal(rollupExitRoots[len(rollupExitRoots)-1], batch.RollupExitRoot) + assert.Equal(common.HexToHash("0x97d1524156ccb46723e5c3c87951da9a390499ba288161d879df1dbc03d49afc"), batch.AccInputHash) + assert.Equal(common.HexToHash("0x22ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba97"), *batch.SendSequencesTxHash) + assert.Equal(rpctypes.ArgUint64(1714427009), batch.Timestamp) + assert.Equal(true, batch.Closed) + assert.Equal("0x55a33ac3bf2cc61ceafdee10415448de84c2e64dc75b3f622fd61d250c1362ec", batch.Blocks[0]) + assert.Equal("0x88b7f001ebab21b77fe747af424320e23c039decd5e3f2bb2e074b6956079bdf", batch.Blocks[1]) + assert.Equal("0x89f4d0933bdcaf5a43266a701e081e4364e2f6d78ae8a82baea4b73e4531821e", batch.Blocks[2]) + assert.Equal(0, len(batch.Transactions)) + assert.Equal("0x0b66301478000000000b00000003000000000b00000003000000000b0000000300000000", "0x"+hex.EncodeToString(batch.BatchL2Data)) +} + +func TestGetBatchDataByNumber(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, nil, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + erigonDB := erigon_db.NewErigonDb(tx) + + gers := []common.Hash{common.HexToHash("0xf010e584db63e18e207a2a2a09cfef322b8f8f185df5093ed17794ac365ef60e"), common.HexToHash("0x12021ea011bd6ebffee86ea47e2c3d08e4fe734ba7251f2ddbc9fa648af3b1e6"), common.HexToHash("0x055bbf062f8add981fd54801e5c36d404da37b8300a7babc2bd2585a54a2195a"), common.HexToHash("0x252feef2a0468f334e0efa3ec67ceb04dbe3d64204242b3774ce1850f8042760")} + parentHashes := []common.Hash{common.HexToHash("0x502b94aa765e198ecd736bcb3ec673e1fcb5985d8e610b1ba06bcf9fbdb965b2"), common.HexToHash("0x55a33ac3bf2cc61ceafdee10415448de84c2e64dc75b3f622fd61d250c1362ec"), common.HexToHash("0x88b7f001ebab21b77fe747af424320e23c039decd5e3f2bb2e074b6956079bdf"), common.HexToHash("0x89f4d0933bdcaf5a43266a701e081e4364e2f6d78ae8a82baea4b73e4531821e")} + hashes := []common.Hash{common.HexToHash("0x55a33ac3bf2cc61ceafdee10415448de84c2e64dc75b3f622fd61d250c1362ec"), common.HexToHash("0x88b7f001ebab21b77fe747af424320e23c039decd5e3f2bb2e074b6956079bdf"), common.HexToHash("0x89f4d0933bdcaf5a43266a701e081e4364e2f6d78ae8a82baea4b73e4531821e"), common.HexToHash("0x002241472c8ffeb86cd3c2bfe928cc41a47fdeffa0e98f56c1da3db6d50d29cb")} + stateRoots := []common.Hash{common.HexToHash("0x70ee58f4d74b706ce88307800983c06c0479f9808d38db5d751d7306f510c9b8"), common.HexToHash("0xba46d17db3364a059cc6efada4a1cc7bea472c559247aafdd920fbd017031fee"), common.HexToHash("0x7dbca3d3f5841bb8a5da985655235587c212826b0f21127e4f3470230d05b0f8"), common.HexToHash("0x551b6fdb2b0c156b104a946f815c3e2c87324be35fba14cf0ed3e4c1287d89bf")} + txsRoot := []common.Hash{common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")} + coinBase := common.HexToAddress("0x761d53b47334bee6612c0bd1467fb881435375b2") + times := []uint64{1714427000, 1714427003, 1714427006, 1714427009} + gasLimits := []uint64{1125899906842624, 1125899906842624, 1125899906842624, 1125899906842624} + mainnetExitRoots := []common.Hash{common.HexToHash("0x6d2478612063b2ecb19b1c75dda5add47630bbae42a2e84f7ccd33c1540db1de"), common.HexToHash("0x17aa73f0a1b0e1acd7ec05a686cfc83a746b3230480db81105d571272aff5936"), common.HexToHash("0xf1dcb7fa915388a4a7bac1da56bd37d3590524ad84cbe0ff42d22ec2be8dcb1d"), common.HexToHash("0x63ab7d9f3c87bc4bbcff42748b09ef7cf87e7e084f6d457d277fee13a4759872")} + rollupExitRoots := []common.Hash{common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0x2b911c2ea39040de58c4ddcc431c62ffde4abf63d36be05b7e5e8724056061b8"), common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0xc8500f8630165b35e61c846262a2ffa3cbe5608305115ec2c79f65bbad91d0b6")} + txs := [][]types.Transaction{{}, {}, {}, {}} + + for i := 0; i < 4; i++ { + err := hDB.WriteBlockBatch(uint64(i+1), 1) + assert.NoError(err) + err = hDB.WriteGlobalExitRoot(gers[i]) + assert.NoError(err) + err = hDB.WriteBlockGlobalExitRoot(uint64(i+1), gers[i]) + assert.NoError(err) + l1InforTree := &zktypes.L1InfoTreeUpdate{ + Index: uint64(i), + GER: gers[i], + MainnetExitRoot: mainnetExitRoots[i], + RollupExitRoot: rollupExitRoots[i], + ParentHash: parentHashes[i], + Timestamp: times[i], + BlockNumber: uint64(i + 1), + } + err = hDB.WriteL1InfoTreeUpdateToGer(l1InforTree) + assert.NoError(err) + } + + err = hDB.WriteSequence(4, 1, common.HexToHash("0x22ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba97"), stateRoots[len(stateRoots)-1], common.HexToHash("0x0")) + assert.NoError(err) + err = hDB.WriteSequence(5, 2, common.HexToHash("0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85"), common.HexToHash("0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0"), common.HexToHash("0x0")) + assert.NoError(err) + err = hDB.WriteSequence(6, 3, common.HexToHash("0x20ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba74"), common.HexToHash("0x10fad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e1"), common.HexToHash("0x0")) + assert.NoError(err) + err = hDB.WriteSequence(7, 4, common.HexToHash("0x19ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba63"), common.HexToHash("0x20fad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e2"), common.HexToHash("0x0")) + assert.NoError(err) + err = hDB.WriteSequence(8, 5, common.HexToHash("0x18ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba52"), common.HexToHash("0x30fad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e3"), common.HexToHash("0x0")) + assert.NoError(err) + err = hDB.WriteSequence(9, 6, common.HexToHash("0x17ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba41"), common.HexToHash("0x40fad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e4"), common.HexToHash("0x0")) + assert.NoError(err) + for i := 0; i < 5; i++ { + err = hDB.WriteBlockBatch(uint64(i+5), uint64(i+2)) + assert.NoError(err) + } + + for i := 1; i < 7; i++ { + err = hDB.WriteForkId(uint64(i), 7) + assert.NoError(err) + } + + for i := 1; i <= 2; i++ { + err = stages.SaveStageProgress(tx, stages.L1VerificationsBatchNo, uint64(i)) + assert.NoError(err) + } + err = stages.SaveStageProgress(tx, stages.Execution, 9) + assert.NoError(err) + err = rawdb.WriteCanonicalHash(tx, common.Hash{}, 9) + assert.NoError(err) + + for i := 0; i < 4; i++ { + _, err := erigonDB.WriteHeader(big.NewInt(int64(i+1)), hashes[i], stateRoots[i], txsRoot[i], parentHashes[i], coinBase, times[i], gasLimits[i]) + assert.NoError(err) + err = erigonDB.WriteBody(big.NewInt(int64(i+1)), hashes[i], txs[i]) + assert.NoError(err) + } + _, err = erigonDB.WriteHeader(big.NewInt(8), common.HexToHash("0x67ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a84b7ba01"), common.HexToHash("0x57ddb9a336815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7bf47"), common.HexToHash("0x67ddb9a356813c3f4c1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba57"), common.HexToHash("0x87ddb9a356812c3fac1026b6dec5df31245fbadb485c9ba5a3e3398a04b7ba68"), coinBase, 1714427021, 1125899906842624) + assert.NoError(err) + err = erigonDB.WriteBody(big.NewInt(8), common.HexToHash("0x67ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a84b7ba01"), []types.Transaction{}) + assert.NoError(err) + _, err = erigonDB.WriteHeader(big.NewInt(9), common.HexToHash("0x27ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a84b7ba81"), common.HexToHash("0x37ddb9a336815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba43"), common.HexToHash("0x87ddb9a356815c3f4c1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba56"), common.HexToHash("0x67ddb9a356815c3fac1026b6dec5df31245fbadb485c9ba5a3e3398a04b7ba48"), coinBase, 1714427024, 1125899906842624) + assert.NoError(err) + err = erigonDB.WriteBody(big.NewInt(9), common.HexToHash("0x27ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a84b7ba81"), []types.Transaction{}) + assert.NoError(err) + + batchesL2Data := [][]byte{common.FromHex("27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d75727ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757"), + common.FromHex("28ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d75727ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d758"), + common.FromHex("29ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d75727ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d759"), + common.FromHex("2aae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d75727ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d75a"), + } + for i, bd := range batchesL2Data { + err = hDB.WriteL1BatchData(uint64(i+2), bd) + assert.NoError(err) + } + tx.Commit() + rawBatch, err := zkEvmImpl.GetBatchDataByNumbers(ctx, rpc.RpcNumberArray{Numbers: []rpc.BlockNumber{1, 2, 3, 4, 5, 6, 7}}) + assert.NoError(err) + var bData map[string][]rpctypes.BatchDataSlim + err = json.Unmarshal(rawBatch, &bData) + assert.NoError(err) + batchesData := bData["data"] + t.Logf("batch: %+v", batchesData) + for i, b := range batchesData { + assert.Equal(rpctypes.ArgUint64(i+1), b.Number) + if i == 0 { + assert.Equal("0x0b66301478000000000b00000003000000000b00000003000000000b0000000300000000", b.BatchL2Data.Hex(), "Batch 1 doesn't match") + } else if i <= len(batchesData)-3 { + assert.Equal("0x"+hex.EncodeToString(batchesL2Data[i-1]), b.BatchL2Data.Hex(), fmt.Sprintf("Batch %d doesn't match", i+1)) + } else if i <= len(batchesData)-2 { + assert.Equal("0x0b0000000300000000", b.BatchL2Data.Hex(), fmt.Sprintf("Batch %d doesn't match", i+1)) + } else { + assert.Equal("0x", b.BatchL2Data.Hex(), fmt.Sprintf("Batch %d doesn't match", i+1)) + } + if i <= len(batchesData)-2 { + assert.Equal(false, b.Empty, fmt.Sprintf("Batch %d doesn't match", i+1)) + } else { + assert.Equal(true, b.Empty, fmt.Sprintf("Batch %d doesn't match", i+1)) + } + } +} + +func TestGetExitRootsByGER(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + EthermanMock := mocks.NewMockIEtherman(mockCtrl) + + l1Syncer := syncer.NewL1Syncer( + ctx, + []syncer.IEtherman{EthermanMock}, + []common.Address{}, + [][]common.Hash{}, + 10, + 0, + "latest", + ) + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + + gers := []common.Hash{common.HexToHash("0xf010e584db63e18e207a2a2a09cfef322b8f8f185df5093ed17794ac365ef60e"), common.HexToHash("0x12021ea011bd6ebffee86ea47e2c3d08e4fe734ba7251f2ddbc9fa648af3b1e6"), common.HexToHash("0x055bbf062f8add981fd54801e5c36d404da37b8300a7babc2bd2585a54a2195a"), common.HexToHash("0x252feef2a0468f334e0efa3ec67ceb04dbe3d64204242b3774ce1850f8042760")} + parentHashes := []common.Hash{common.HexToHash("0x502b94aa765e198ecd736bcb3ec673e1fcb5985d8e610b1ba06bcf9fbdb965b2"), common.HexToHash("0x55a33ac3bf2cc61ceafdee10415448de84c2e64dc75b3f622fd61d250c1362ec"), common.HexToHash("0x88b7f001ebab21b77fe747af424320e23c039decd5e3f2bb2e074b6956079bdf"), common.HexToHash("0x89f4d0933bdcaf5a43266a701e081e4364e2f6d78ae8a82baea4b73e4531821e")} + stateRoots := []common.Hash{common.HexToHash("0x70ee58f4d74b706ce88307800983c06c0479f9808d38db5d751d7306f510c9b8"), common.HexToHash("0xba46d17db3364a059cc6efada4a1cc7bea472c559247aafdd920fbd017031fee"), common.HexToHash("0x7dbca3d3f5841bb8a5da985655235587c212826b0f21127e4f3470230d05b0f8"), common.HexToHash("0x551b6fdb2b0c156b104a946f815c3e2c87324be35fba14cf0ed3e4c1287d89bf")} + times := []uint64{1714427000, 1714427003, 1714427006, 1714427009} + mainnetExitRoots := []common.Hash{common.HexToHash("0x6d2478612063b2ecb19b1c75dda5add47630bbae42a2e84f7ccd33c1540db1de"), common.HexToHash("0x17aa73f0a1b0e1acd7ec05a686cfc83a746b3230480db81105d571272aff5936"), common.HexToHash("0xf1dcb7fa915388a4a7bac1da56bd37d3590524ad84cbe0ff42d22ec2be8dcb1d"), common.HexToHash("0x63ab7d9f3c87bc4bbcff42748b09ef7cf87e7e084f6d457d277fee13a4759872")} + rollupExitRoots := []common.Hash{common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0x2b911c2ea39040de58c4ddcc431c62ffde4abf63d36be05b7e5e8724056061b8"), common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0xc8500f8630165b35e61c846262a2ffa3cbe5608305115ec2c79f65bbad91d0b6")} + + for i := 0; i < 4; i++ { + err := hDB.WriteBlockBatch(uint64(i+1), 1) + assert.NoError(err) + err = hDB.WriteGlobalExitRoot(gers[i]) + assert.NoError(err) + err = hDB.WriteBlockGlobalExitRoot(uint64(i+1), gers[i]) + assert.NoError(err) + l1InforTree := &zktypes.L1InfoTreeUpdate{ + Index: uint64(i), + GER: gers[i], + MainnetExitRoot: mainnetExitRoots[i], + RollupExitRoot: rollupExitRoots[i], + ParentHash: parentHashes[i], + Timestamp: times[i], + BlockNumber: uint64(i + 1), + } + err = hDB.WriteL1InfoTreeUpdateToGer(l1InforTree) + assert.NoError(err) + } + + err = hDB.WriteSequence(4, 1, common.HexToHash("0x22ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba97"), stateRoots[len(stateRoots)-1], common.HexToHash("0x0")) + assert.NoError(err) + + tx.Commit() + for i, g := range gers { + exitRoots, err := zkEvmImpl.GetExitRootsByGER(ctx, g) + assert.NoError(err) + t.Logf("exitRoots: %+v", exitRoots) + assert.Equal(rpctypes.ArgUint64(i+1), exitRoots.BlockNumber) + assert.Equal(mainnetExitRoots[i], exitRoots.MainnetExitRoot) + assert.Equal(rollupExitRoots[i], exitRoots.RollupExitRoot) + assert.Equal(rpctypes.ArgUint64(times[i]), exitRoots.Timestamp) + } +} + +func TestLatestGlobalExitRoot(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + EthermanMock := mocks.NewMockIEtherman(mockCtrl) + + l1Syncer := syncer.NewL1Syncer( + ctx, + []syncer.IEtherman{EthermanMock}, + []common.Address{}, + [][]common.Hash{}, + 10, + 0, + "latest", + ) + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + + gers := []common.Hash{common.HexToHash("0xf010e584db63e18e207a2a2a09cfef322b8f8f185df5093ed17794ac365ef60e"), common.HexToHash("0x12021ea011bd6ebffee86ea47e2c3d08e4fe734ba7251f2ddbc9fa648af3b1e6"), common.HexToHash("0x055bbf062f8add981fd54801e5c36d404da37b8300a7babc2bd2585a54a2195a"), common.HexToHash("0x252feef2a0468f334e0efa3ec67ceb04dbe3d64204242b3774ce1850f8042760")} + parentHashes := []common.Hash{common.HexToHash("0x502b94aa765e198ecd736bcb3ec673e1fcb5985d8e610b1ba06bcf9fbdb965b2"), common.HexToHash("0x55a33ac3bf2cc61ceafdee10415448de84c2e64dc75b3f622fd61d250c1362ec"), common.HexToHash("0x88b7f001ebab21b77fe747af424320e23c039decd5e3f2bb2e074b6956079bdf"), common.HexToHash("0x89f4d0933bdcaf5a43266a701e081e4364e2f6d78ae8a82baea4b73e4531821e")} + stateRoots := []common.Hash{common.HexToHash("0x70ee58f4d74b706ce88307800983c06c0479f9808d38db5d751d7306f510c9b8"), common.HexToHash("0xba46d17db3364a059cc6efada4a1cc7bea472c559247aafdd920fbd017031fee"), common.HexToHash("0x7dbca3d3f5841bb8a5da985655235587c212826b0f21127e4f3470230d05b0f8"), common.HexToHash("0x551b6fdb2b0c156b104a946f815c3e2c87324be35fba14cf0ed3e4c1287d89bf")} + times := []uint64{1714427000, 1714427003, 1714427006, 1714427009} + mainnetExitRoots := []common.Hash{common.HexToHash("0x6d2478612063b2ecb19b1c75dda5add47630bbae42a2e84f7ccd33c1540db1de"), common.HexToHash("0x17aa73f0a1b0e1acd7ec05a686cfc83a746b3230480db81105d571272aff5936"), common.HexToHash("0xf1dcb7fa915388a4a7bac1da56bd37d3590524ad84cbe0ff42d22ec2be8dcb1d"), common.HexToHash("0x63ab7d9f3c87bc4bbcff42748b09ef7cf87e7e084f6d457d277fee13a4759872")} + rollupExitRoots := []common.Hash{common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0x2b911c2ea39040de58c4ddcc431c62ffde4abf63d36be05b7e5e8724056061b8"), common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0xc8500f8630165b35e61c846262a2ffa3cbe5608305115ec2c79f65bbad91d0b6")} + + for i := 0; i < 4; i++ { + err := hDB.WriteBlockBatch(uint64(i+1), 1) + assert.NoError(err) + err = hDB.WriteGlobalExitRoot(gers[i]) + assert.NoError(err) + err = hDB.WriteBlockGlobalExitRoot(uint64(i+1), gers[i]) + assert.NoError(err) + l1InforTree := &zktypes.L1InfoTreeUpdate{ + Index: uint64(i), + GER: gers[i], + MainnetExitRoot: mainnetExitRoots[i], + RollupExitRoot: rollupExitRoots[i], + ParentHash: parentHashes[i], + Timestamp: times[i], + BlockNumber: uint64(i + 1), + } + err = hDB.WriteLatestUsedGer(uint64(i+1), gers[i]) + assert.NoError(err) + err = hDB.WriteL1InfoTreeUpdateToGer(l1InforTree) + assert.NoError(err) + } + + err = hDB.WriteSequence(4, 1, common.HexToHash("0x22ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba97"), stateRoots[len(stateRoots)-1], common.HexToHash("0x0")) + assert.NoError(err) + + tx.Commit() + ger, err := zkEvmImpl.GetLatestGlobalExitRoot(ctx) + assert.NoError(err) + t.Logf("ger: %+v", ger) + assert.Equal(gers[len(gers)-1], ger) +} + +func TestGetVersionHistory(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + var l1Syncer *syncer.L1Syncer + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + ti := time.Now() + for i := 0; i < 10; i++ { + tiAux := ti + ok, err := hDB.WriteErigonVersion(fmt.Sprintf("v%d", i+1), tiAux.Add(time.Duration(1)*time.Second)) + assert.NoError(err) + assert.True(ok) + } + tx.Commit() + + rawHistory, err := zkEvmImpl.GetVersionHistory(ctx) + assert.NoError(err) + var history map[string]time.Time + err = json.Unmarshal(rawHistory, &history) + assert.NoError(err) + t.Logf("Version history: %+v", history) + for i := 0; i < 10; i++ { + tiAux := ti + timeReceived := history[fmt.Sprintf("v%d", i+1)] + assert.Equal(tiAux.Add(time.Duration(1)*time.Second).Unix(), timeReceived.Unix()) + } +} + +func TestGetExitRootTable(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, nil, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + + gers := []common.Hash{common.HexToHash("0xf010e584db63e18e207a2a2a09cfef322b8f8f185df5093ed17794ac365ef60e"), common.HexToHash("0x12021ea011bd6ebffee86ea47e2c3d08e4fe734ba7251f2ddbc9fa648af3b1e6"), common.HexToHash("0x055bbf062f8add981fd54801e5c36d404da37b8300a7babc2bd2585a54a2195a"), common.HexToHash("0x252feef2a0468f334e0efa3ec67ceb04dbe3d64204242b3774ce1850f8042760")} + parentHashes := []common.Hash{common.HexToHash("0x502b94aa765e198ecd736bcb3ec673e1fcb5985d8e610b1ba06bcf9fbdb965b2"), common.HexToHash("0x55a33ac3bf2cc61ceafdee10415448de84c2e64dc75b3f622fd61d250c1362ec"), common.HexToHash("0x88b7f001ebab21b77fe747af424320e23c039decd5e3f2bb2e074b6956079bdf"), common.HexToHash("0x89f4d0933bdcaf5a43266a701e081e4364e2f6d78ae8a82baea4b73e4531821e")} + times := []uint64{1714427000, 1714427003, 1714427006, 1714427009} + mainnetExitRoots := []common.Hash{common.HexToHash("0x6d2478612063b2ecb19b1c75dda5add47630bbae42a2e84f7ccd33c1540db1de"), common.HexToHash("0x17aa73f0a1b0e1acd7ec05a686cfc83a746b3230480db81105d571272aff5936"), common.HexToHash("0xf1dcb7fa915388a4a7bac1da56bd37d3590524ad84cbe0ff42d22ec2be8dcb1d"), common.HexToHash("0x63ab7d9f3c87bc4bbcff42748b09ef7cf87e7e084f6d457d277fee13a4759872")} + rollupExitRoots := []common.Hash{common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0x2b911c2ea39040de58c4ddcc431c62ffde4abf63d36be05b7e5e8724056061b8"), common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0xc8500f8630165b35e61c846262a2ffa3cbe5608305115ec2c79f65bbad91d0b6")} + l1InfoRoots := []common.Hash{common.HexToHash("0x52b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653d4"), common.HexToHash("0x3f911c2ea39040de58c4ddcc431c62ffde4abf63d36be05b7e5e8724056061a8"), common.HexToHash("0xaab6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653ff"), common.HexToHash("0x12500f8630165b35e61c846262a2ffa3cbe5608305115ec2c79f65bbad91d045")} + + for i := 0; i < 4; i++ { + err := hDB.WriteBlockBatch(uint64(i+1), 1) + assert.NoError(err) + err = hDB.WriteGlobalExitRoot(gers[i]) + assert.NoError(err) + err = hDB.WriteBlockGlobalExitRoot(uint64(i+1), gers[i]) + assert.NoError(err) + l1InforTree := &zktypes.L1InfoTreeUpdate{ + Index: uint64(i + 1), + GER: gers[i], + MainnetExitRoot: mainnetExitRoots[i], + RollupExitRoot: rollupExitRoots[i], + ParentHash: parentHashes[i], + Timestamp: times[i], + BlockNumber: uint64(i + 1), + } + err = hDB.WriteL1InfoTreeUpdateToGer(l1InforTree) + assert.NoError(err) + err = hDB.WriteL1InfoTreeUpdate(l1InforTree) + assert.NoError(err) + err = hDB.WriteL1InfoTreeRoot(l1InfoRoots[i], l1InforTree.Index) + assert.NoError(err) + } + tx.Commit() + + exitRootEntries, err := zkEvmImpl.GetExitRootTable(ctx) + assert.NoError(err) + t.Logf("exitRootEntries: %+v", exitRootEntries) + for i, er := range exitRootEntries { + assert.Equal(uint64(i+1), er.BlockNumber) + assert.Equal(gers[i], er.Ger) + assert.Equal(uint64(i+1), er.Index) + assert.Equal(l1InfoRoots[i], er.InfoRoot) + assert.Equal(mainnetExitRoots[i], er.MainnetExitRoot) + assert.Equal(times[i], er.MinTimestamp) + assert.Equal(parentHashes[i], er.ParentHash) + assert.Equal(rollupExitRoots[i], er.RollupExitRoot) + } +} + +var ( + testKey0, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testKey1, _ = crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80") + testKey2, _ = crypto.HexToECDSA("28b2b0318721be8c8339199172cd7cc8f5e273800a35616ec893083a4b32c02e") + testKey3, _ = crypto.HexToECDSA("59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d") +) + +func TestGetFullBlockByNumber(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, nil, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + erigonDB := erigon_db.NewErigonDb(tx) + + gers := []common.Hash{common.HexToHash("0xf010e584db63e18e207a2a2a09cfef322b8f8f185df5093ed17794ac365ef60e"), common.HexToHash("0x12021ea011bd6ebffee86ea47e2c3d08e4fe734ba7251f2ddbc9fa648af3b1e6"), common.HexToHash("0x055bbf062f8add981fd54801e5c36d404da37b8300a7babc2bd2585a54a2195a"), common.HexToHash("0x252feef2a0468f334e0efa3ec67ceb04dbe3d64204242b3774ce1850f8042760")} + parentHashes := []common.Hash{common.HexToHash("0x502b94aa765e198ecd736bcb3ec673e1fcb5985d8e610b1ba06bcf9fbdb965b2"), common.HexToHash("0x55a33ac3bf2cc61ceafdee10415448de84c2e64dc75b3f622fd61d250c1362ec"), common.HexToHash("0x88b7f001ebab21b77fe747af424320e23c039decd5e3f2bb2e074b6956079bdf"), common.HexToHash("0x89f4d0933bdcaf5a43266a701e081e4364e2f6d78ae8a82baea4b73e4531821e")} + hashes := []common.Hash{common.HexToHash("0x55a33ac3bf2cc61ceafdee10415448de84c2e64dc75b3f622fd61d250c1362ec"), common.HexToHash("0x88b7f001ebab21b77fe747af424320e23c039decd5e3f2bb2e074b6956079bdf"), common.HexToHash("0x89f4d0933bdcaf5a43266a701e081e4364e2f6d78ae8a82baea4b73e4531821e"), common.HexToHash("0x002241472c8ffeb86cd3c2bfe928cc41a47fdeffa0e98f56c1da3db6d50d29cb")} + stateRoots := []common.Hash{common.HexToHash("0x70ee58f4d74b706ce88307800983c06c0479f9808d38db5d751d7306f510c9b8"), common.HexToHash("0xba46d17db3364a059cc6efada4a1cc7bea472c559247aafdd920fbd017031fee"), common.HexToHash("0x7dbca3d3f5841bb8a5da985655235587c212826b0f21127e4f3470230d05b0f8"), common.HexToHash("0x551b6fdb2b0c156b104a946f815c3e2c87324be35fba14cf0ed3e4c1287d89bf")} + txsRoot := []common.Hash{common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")} + coinBase := common.HexToAddress("0x761d53b47334bee6612c0bd1467fb881435375b2") + times := []uint64{1714427000, 1714427003, 1714427006, 1714427009} + gasLimits := []uint64{1125899906842624, 1125899906842624, 1125899906842624, 1125899906842624} + mainnetExitRoots := []common.Hash{common.HexToHash("0x6d2478612063b2ecb19b1c75dda5add47630bbae42a2e84f7ccd33c1540db1de"), common.HexToHash("0x17aa73f0a1b0e1acd7ec05a686cfc83a746b3230480db81105d571272aff5936"), common.HexToHash("0xf1dcb7fa915388a4a7bac1da56bd37d3590524ad84cbe0ff42d22ec2be8dcb1d"), common.HexToHash("0x63ab7d9f3c87bc4bbcff42748b09ef7cf87e7e084f6d457d277fee13a4759872")} + rollupExitRoots := []common.Hash{common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0x2b911c2ea39040de58c4ddcc431c62ffde4abf63d36be05b7e5e8724056061b8"), common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0xc8500f8630165b35e61c846262a2ffa3cbe5608305115ec2c79f65bbad91d0b6")} + + signer1 := types.MakeSigner(params.TestChainConfig, 1) + signer2 := types.MakeSigner(params.TestChainConfig, 2) + signer3 := types.MakeSigner(params.TestChainConfig, 3) + signer4 := types.MakeSigner(params.TestChainConfig, 4) + + var tx0 types.Transaction = types.NewTransaction(0, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx1 types.Transaction = types.NewTransaction(1, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx2 types.Transaction = types.NewTransaction(0, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx3 types.Transaction = types.NewTransaction(1, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx4 types.Transaction = types.NewTransaction(0, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx5 types.Transaction = types.NewTransaction(1, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx6 types.Transaction = types.NewTransaction(0, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx7 types.Transaction = types.NewTransaction(1, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx8 types.Transaction = types.NewTransaction(2, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + + tx0, _ = types.SignTx(tx0, *signer1, testKey0) + tx1, _ = types.SignTx(tx1, *signer1, testKey0) + tx2, _ = types.SignTx(tx2, *signer2, testKey1) + tx3, _ = types.SignTx(tx3, *signer2, testKey1) + tx4, _ = types.SignTx(tx4, *signer3, testKey2) + tx5, _ = types.SignTx(tx5, *signer3, testKey2) + tx6, _ = types.SignTx(tx6, *signer4, testKey3) + tx7, _ = types.SignTx(tx7, *signer4, testKey3) + tx8, _ = types.SignTx(tx8, *signer4, testKey3) + + txs := [][]types.Transaction{{tx0, tx1}, {tx2, tx3}, {tx4, tx5}, {tx6, tx7, tx8}} + + for i := 0; i < 4; i++ { + err := hDB.WriteBlockBatch(uint64(i+1), 1) + assert.NoError(err) + err = hDB.WriteGlobalExitRoot(gers[i]) + assert.NoError(err) + err = hDB.WriteBlockGlobalExitRoot(uint64(i+1), gers[i]) + assert.NoError(err) + l1InforTree := &zktypes.L1InfoTreeUpdate{ + Index: uint64(i), + GER: gers[i], + MainnetExitRoot: mainnetExitRoots[i], + RollupExitRoot: rollupExitRoots[i], + ParentHash: parentHashes[i], + Timestamp: times[i], + BlockNumber: uint64(i + 1), + } + err = hDB.WriteL1InfoTreeUpdateToGer(l1InforTree) + assert.NoError(err) + } + + err = hDB.WriteSequence(4, 1, common.HexToHash("0x22ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba97"), stateRoots[len(stateRoots)-1], common.HexToHash("0x0")) + assert.NoError(err) + + for i := 0; i < 4; i++ { + _, err := erigonDB.WriteHeader(big.NewInt(int64(i+1)), hashes[i], stateRoots[i], txsRoot[i], parentHashes[i], coinBase, times[i], gasLimits[i]) + assert.NoError(err) + err = erigonDB.WriteBody(big.NewInt(int64(i+1)), hashes[i], txs[i]) + assert.NoError(err) + } + _, err = erigonDB.WriteHeader(big.NewInt(8), common.HexToHash("0x67ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a84b7ba01"), common.HexToHash("0x57ddb9a336815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7bf47"), common.HexToHash("0x67ddb9a356813c3f4c1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba57"), common.HexToHash("0x87ddb9a356812c3fac1026b6dec5df31245fbadb485c9ba5a3e3398a04b7ba68"), coinBase, 1714427021, 1125899906842624) + assert.NoError(err) + err = erigonDB.WriteBody(big.NewInt(8), common.HexToHash("0x67ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a84b7ba01"), []types.Transaction{}) + assert.NoError(err) + _, err = erigonDB.WriteHeader(big.NewInt(9), common.HexToHash("0x27ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a84b7ba81"), common.HexToHash("0x37ddb9a336815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba43"), common.HexToHash("0x87ddb9a356815c3f4c1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba56"), common.HexToHash("0x67ddb9a356815c3fac1026b6dec5df31245fbadb485c9ba5a3e3398a04b7ba48"), coinBase, 1714427024, 1125899906842624) + assert.NoError(err) + err = erigonDB.WriteBody(big.NewInt(9), common.HexToHash("0x27ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a84b7ba81"), []types.Transaction{}) + assert.NoError(err) + + tx.Commit() + + // Block 1 + block1, err := zkEvmImpl.GetFullBlockByNumber(ctx, rpc.BlockNumber(1), true) + assert.NoError(err) + t.Logf("block 1: %+v", block1) + assert.Equal(2, len(block1.Transactions)) + for i, tx := range block1.Transactions { + assert.Equal(rpctypes.ArgUint64(i), tx.Tx.Nonce) + assert.Equal(rpctypes.ArgUint64(21000), tx.Tx.Gas) + assert.Equal(common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"), tx.Tx.From) + assert.Equal(rpctypes.ArgUint64(1), *tx.Tx.BlockNumber) + } + block1, err = zkEvmImpl.GetFullBlockByNumber(ctx, rpc.BlockNumber(1), false) + assert.NoError(err) + assert.Equal(2, len(block1.Transactions)) + assert.Equal(tx0.Hash(), *block1.Transactions[0].Hash) + assert.Equal(tx1.Hash(), *block1.Transactions[1].Hash) + + // Block 2 + block2, err := zkEvmImpl.GetFullBlockByNumber(ctx, rpc.BlockNumber(2), true) + assert.NoError(err) + t.Logf("block 2: %+v", block2) + assert.Equal(2, len(block2.Transactions)) + for i, tx := range block2.Transactions { + assert.Equal(rpctypes.ArgUint64(i), tx.Tx.Nonce) + assert.Equal(rpctypes.ArgUint64(21000), tx.Tx.Gas) + assert.Equal(common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"), tx.Tx.From) + assert.Equal(rpctypes.ArgUint64(2), *tx.Tx.BlockNumber) + } + block2, err = zkEvmImpl.GetFullBlockByNumber(ctx, rpc.BlockNumber(2), false) + assert.NoError(err) + assert.Equal(2, len(block2.Transactions)) + assert.Equal(tx2.Hash(), *block2.Transactions[0].Hash) + assert.Equal(tx3.Hash(), *block2.Transactions[1].Hash) + + // Block 3 + block3, err := zkEvmImpl.GetFullBlockByNumber(ctx, rpc.BlockNumber(3), true) + assert.NoError(err) + t.Logf("block 3: %+v", block3) + assert.Equal(2, len(block3.Transactions)) + for i, tx := range block3.Transactions { + assert.Equal(rpctypes.ArgUint64(i), tx.Tx.Nonce) + assert.Equal(rpctypes.ArgUint64(21000), tx.Tx.Gas) + assert.Equal(common.HexToAddress("0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D"), tx.Tx.From) + assert.Equal(rpctypes.ArgUint64(3), *tx.Tx.BlockNumber) + } + block3, err = zkEvmImpl.GetFullBlockByNumber(ctx, rpc.BlockNumber(3), false) + assert.NoError(err) + assert.Equal(2, len(block3.Transactions)) + assert.Equal(tx4.Hash(), *block3.Transactions[0].Hash) + assert.Equal(tx5.Hash(), *block3.Transactions[1].Hash) + + // Block 4 + block4, err := zkEvmImpl.GetFullBlockByNumber(ctx, rpc.BlockNumber(4), true) + assert.NoError(err) + t.Logf("block 4: %+v", block4) + assert.Equal(3, len(block4.Transactions)) + for i, tx := range block4.Transactions { + assert.Equal(rpctypes.ArgUint64(i), tx.Tx.Nonce) + assert.Equal(rpctypes.ArgUint64(21000), tx.Tx.Gas) + assert.Equal(common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), tx.Tx.From) + assert.Equal(rpctypes.ArgUint64(4), *tx.Tx.BlockNumber) + } + block4, err = zkEvmImpl.GetFullBlockByNumber(ctx, rpc.BlockNumber(4), false) + assert.NoError(err) + assert.Equal(3, len(block4.Transactions)) + assert.Equal(tx6.Hash(), *block4.Transactions[0].Hash) + assert.Equal(tx7.Hash(), *block4.Transactions[1].Hash) + assert.Equal(tx8.Hash(), *block4.Transactions[2].Hash) +} + +func TestGetFullBlockByHash(t *testing.T) { + assert := assert.New(t) + //////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, nil, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + erigonDB := erigon_db.NewErigonDb(tx) + + gers := []common.Hash{common.HexToHash("0xf010e584db63e18e207a2a2a09cfef322b8f8f185df5093ed17794ac365ef60e"), common.HexToHash("0x12021ea011bd6ebffee86ea47e2c3d08e4fe734ba7251f2ddbc9fa648af3b1e6"), common.HexToHash("0x055bbf062f8add981fd54801e5c36d404da37b8300a7babc2bd2585a54a2195a"), common.HexToHash("0x252feef2a0468f334e0efa3ec67ceb04dbe3d64204242b3774ce1850f8042760")} + parentHashes := []common.Hash{common.HexToHash("0x502b94aa765e198ecd736bcb3ec673e1fcb5985d8e610b1ba06bcf9fbdb965b2"), common.HexToHash("0x55a33ac3bf2cc61ceafdee10415448de84c2e64dc75b3f622fd61d250c1362ec"), common.HexToHash("0x88b7f001ebab21b77fe747af424320e23c039decd5e3f2bb2e074b6956079bdf"), common.HexToHash("0x89f4d0933bdcaf5a43266a701e081e4364e2f6d78ae8a82baea4b73e4531821e")} + hashes := []common.Hash{common.HexToHash("0x55a33ac3bf2cc61ceafdee10415448de84c2e64dc75b3f622fd61d250c1362ec"), common.HexToHash("0x88b7f001ebab21b77fe747af424320e23c039decd5e3f2bb2e074b6956079bdf"), common.HexToHash("0x89f4d0933bdcaf5a43266a701e081e4364e2f6d78ae8a82baea4b73e4531821e"), common.HexToHash("0x002241472c8ffeb86cd3c2bfe928cc41a47fdeffa0e98f56c1da3db6d50d29cb")} + stateRoots := []common.Hash{common.HexToHash("0x70ee58f4d74b706ce88307800983c06c0479f9808d38db5d751d7306f510c9b8"), common.HexToHash("0xba46d17db3364a059cc6efada4a1cc7bea472c559247aafdd920fbd017031fee"), common.HexToHash("0x7dbca3d3f5841bb8a5da985655235587c212826b0f21127e4f3470230d05b0f8"), common.HexToHash("0x551b6fdb2b0c156b104a946f815c3e2c87324be35fba14cf0ed3e4c1287d89bf")} + txsRoot := []common.Hash{common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")} + coinBase := common.HexToAddress("0x761d53b47334bee6612c0bd1467fb881435375b2") + times := []uint64{1714427000, 1714427003, 1714427006, 1714427009} + gasLimits := []uint64{1125899906842624, 1125899906842624, 1125899906842624, 1125899906842624} + mainnetExitRoots := []common.Hash{common.HexToHash("0x6d2478612063b2ecb19b1c75dda5add47630bbae42a2e84f7ccd33c1540db1de"), common.HexToHash("0x17aa73f0a1b0e1acd7ec05a686cfc83a746b3230480db81105d571272aff5936"), common.HexToHash("0xf1dcb7fa915388a4a7bac1da56bd37d3590524ad84cbe0ff42d22ec2be8dcb1d"), common.HexToHash("0x63ab7d9f3c87bc4bbcff42748b09ef7cf87e7e084f6d457d277fee13a4759872")} + rollupExitRoots := []common.Hash{common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0x2b911c2ea39040de58c4ddcc431c62ffde4abf63d36be05b7e5e8724056061b8"), common.HexToHash("0x50b6637901ac94283cb4f2dcd3606c42a421444ce9643a55ecf95ec9ba5653a7"), common.HexToHash("0xc8500f8630165b35e61c846262a2ffa3cbe5608305115ec2c79f65bbad91d0b6")} + + signer1 := types.MakeSigner(params.TestChainConfig, 1) + signer2 := types.MakeSigner(params.TestChainConfig, 2) + signer3 := types.MakeSigner(params.TestChainConfig, 3) + signer4 := types.MakeSigner(params.TestChainConfig, 4) + + var tx0 types.Transaction = types.NewTransaction(0, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx1 types.Transaction = types.NewTransaction(1, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx2 types.Transaction = types.NewTransaction(0, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx3 types.Transaction = types.NewTransaction(1, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx4 types.Transaction = types.NewTransaction(0, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx5 types.Transaction = types.NewTransaction(1, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx6 types.Transaction = types.NewTransaction(0, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx7 types.Transaction = types.NewTransaction(1, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + var tx8 types.Transaction = types.NewTransaction(2, common.Address{}, uint256.NewInt(0), 21000, uint256.NewInt(0), []byte{}) + + tx0, _ = types.SignTx(tx0, *signer1, testKey0) + tx1, _ = types.SignTx(tx1, *signer1, testKey0) + tx2, _ = types.SignTx(tx2, *signer2, testKey1) + tx3, _ = types.SignTx(tx3, *signer2, testKey1) + tx4, _ = types.SignTx(tx4, *signer3, testKey2) + tx5, _ = types.SignTx(tx5, *signer3, testKey2) + tx6, _ = types.SignTx(tx6, *signer4, testKey3) + tx7, _ = types.SignTx(tx7, *signer4, testKey3) + tx8, _ = types.SignTx(tx8, *signer4, testKey3) + + txs := [][]types.Transaction{{tx0, tx1}, {tx2, tx3}, {tx4, tx5}, {tx6, tx7, tx8}} + + for i := 0; i < 4; i++ { + err := hDB.WriteBlockBatch(uint64(i+1), 1) + assert.NoError(err) + err = hDB.WriteGlobalExitRoot(gers[i]) + assert.NoError(err) + err = hDB.WriteBlockGlobalExitRoot(uint64(i+1), gers[i]) + assert.NoError(err) + l1InforTree := &zktypes.L1InfoTreeUpdate{ + Index: uint64(i), + GER: gers[i], + MainnetExitRoot: mainnetExitRoots[i], + RollupExitRoot: rollupExitRoots[i], + ParentHash: parentHashes[i], + Timestamp: times[i], + BlockNumber: uint64(i + 1), + } + err = hDB.WriteL1InfoTreeUpdateToGer(l1InforTree) + assert.NoError(err) + } + + err = hDB.WriteSequence(4, 1, common.HexToHash("0x22ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba97"), stateRoots[len(stateRoots)-1], common.HexToHash("0x0")) + assert.NoError(err) + + for i := 0; i < 4; i++ { + _, err := erigonDB.WriteHeader(big.NewInt(int64(i+1)), hashes[i], stateRoots[i], txsRoot[i], parentHashes[i], coinBase, times[i], gasLimits[i]) + assert.NoError(err) + err = erigonDB.WriteBody(big.NewInt(int64(i+1)), hashes[i], txs[i]) + assert.NoError(err) + } + _, err = erigonDB.WriteHeader(big.NewInt(8), common.HexToHash("0x67ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a84b7ba01"), common.HexToHash("0x57ddb9a336815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7bf47"), common.HexToHash("0x67ddb9a356813c3f4c1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba57"), common.HexToHash("0x87ddb9a356812c3fac1026b6dec5df31245fbadb485c9ba5a3e3398a04b7ba68"), coinBase, 1714427021, 1125899906842624) + assert.NoError(err) + err = erigonDB.WriteBody(big.NewInt(8), common.HexToHash("0x67ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a84b7ba01"), []types.Transaction{}) + assert.NoError(err) + _, err = erigonDB.WriteHeader(big.NewInt(9), common.HexToHash("0x27ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a84b7ba81"), common.HexToHash("0x37ddb9a336815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba43"), common.HexToHash("0x87ddb9a356815c3f4c1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba56"), common.HexToHash("0x67ddb9a356815c3fac1026b6dec5df31245fbadb485c9ba5a3e3398a04b7ba48"), coinBase, 1714427024, 1125899906842624) + assert.NoError(err) + err = erigonDB.WriteBody(big.NewInt(9), common.HexToHash("0x27ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a84b7ba81"), []types.Transaction{}) + assert.NoError(err) + + tx.Commit() + + // Block 1 + block1, err := zkEvmImpl.GetFullBlockByHash(ctx, hashes[0], true) + assert.NoError(err) + t.Logf("block 1: %+v", block1) + assert.Equal(2, len(block1.Transactions)) + for i, tx := range block1.Transactions { + assert.Equal(rpctypes.ArgUint64(i), tx.Tx.Nonce) + assert.Equal(rpctypes.ArgUint64(21000), tx.Tx.Gas) + assert.Equal(common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"), tx.Tx.From) + assert.Equal(rpctypes.ArgUint64(1), *tx.Tx.BlockNumber) + } + block1, err = zkEvmImpl.GetFullBlockByHash(ctx, hashes[0], false) + assert.NoError(err) + assert.Equal(2, len(block1.Transactions)) + assert.Equal(tx0.Hash(), *block1.Transactions[0].Hash) + assert.Equal(tx1.Hash(), *block1.Transactions[1].Hash) + + // Block 2 + block2, err := zkEvmImpl.GetFullBlockByHash(ctx, hashes[1], true) + assert.NoError(err) + t.Logf("block 2: %+v", block2) + assert.Equal(2, len(block2.Transactions)) + for i, tx := range block2.Transactions { + assert.Equal(rpctypes.ArgUint64(i), tx.Tx.Nonce) + assert.Equal(rpctypes.ArgUint64(21000), tx.Tx.Gas) + assert.Equal(common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"), tx.Tx.From) + assert.Equal(rpctypes.ArgUint64(2), *tx.Tx.BlockNumber) + } + block2, err = zkEvmImpl.GetFullBlockByHash(ctx, hashes[1], false) + assert.NoError(err) + assert.Equal(2, len(block2.Transactions)) + assert.Equal(tx2.Hash(), *block2.Transactions[0].Hash) + assert.Equal(tx3.Hash(), *block2.Transactions[1].Hash) + + // Block 3 + block3, err := zkEvmImpl.GetFullBlockByHash(ctx, hashes[2], true) + assert.NoError(err) + t.Logf("block 3: %+v", block3) + assert.Equal(2, len(block3.Transactions)) + for i, tx := range block3.Transactions { + assert.Equal(rpctypes.ArgUint64(i), tx.Tx.Nonce) + assert.Equal(rpctypes.ArgUint64(21000), tx.Tx.Gas) + assert.Equal(common.HexToAddress("0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D"), tx.Tx.From) + assert.Equal(rpctypes.ArgUint64(3), *tx.Tx.BlockNumber) + } + block3, err = zkEvmImpl.GetFullBlockByHash(ctx, hashes[2], false) + assert.NoError(err) + assert.Equal(2, len(block3.Transactions)) + assert.Equal(tx4.Hash(), *block3.Transactions[0].Hash) + assert.Equal(tx5.Hash(), *block3.Transactions[1].Hash) + + // Block 4 + block4, err := zkEvmImpl.GetFullBlockByHash(ctx, hashes[3], true) + assert.NoError(err) + t.Logf("block 4: %+v", block4) + assert.Equal(3, len(block4.Transactions)) + for i, tx := range block4.Transactions { + assert.Equal(rpctypes.ArgUint64(i), tx.Tx.Nonce) + assert.Equal(rpctypes.ArgUint64(21000), tx.Tx.Gas) + assert.Equal(common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), tx.Tx.From) + assert.Equal(rpctypes.ArgUint64(4), *tx.Tx.BlockNumber) + } + block4, err = zkEvmImpl.GetFullBlockByHash(ctx, hashes[3], false) + assert.NoError(err) + assert.Equal(3, len(block4.Transactions)) + assert.Equal(tx6.Hash(), *block4.Transactions[0].Hash) + assert.Equal(tx7.Hash(), *block4.Transactions[1].Hash) + assert.Equal(tx8.Hash(), *block4.Transactions[2].Hash) +} + +func TestGetForkId(t *testing.T) { + assert := assert.New(t) + + ////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + var l1Syncer *syncer.L1Syncer + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + + for i := 1; i <= 10; i++ { + err := hDB.WriteBlockBatch(uint64(i), uint64(i)) + assert.NoError(err) + err = hDB.WriteForkId(uint64(i), uint64(i)) + assert.NoError(err) + } + err = hDB.WriteSequence(4, 4, common.HexToHash("0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85"), common.HexToHash("0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0"), common.HexToHash("0x0")) + assert.NoError(err) + err = hDB.WriteSequence(7, 7, common.HexToHash("0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba86"), common.HexToHash("0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e1"), common.HexToHash("0x0")) + assert.NoError(err) + for i := 1; i <= 4; i++ { + err = stages.SaveStageProgress(tx, stages.L1VerificationsBatchNo, uint64(i)) + assert.NoError(err) + } + + tx.Commit() + forkId, err := zkEvmImpl.GetForkId(ctx) + assert.NoError(err) + assert.Equal(hexutil.Uint64(10), forkId) +} + +func TestGetForkIdByBatchNumber(t *testing.T) { + assert := assert.New(t) + + ////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + var l1Syncer *syncer.L1Syncer + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + + for i := 1; i <= 10; i++ { + err := hDB.WriteBlockBatch(uint64(i), uint64(i)) + assert.NoError(err) + err = hDB.WriteForkId(uint64(i), uint64(i)) + assert.NoError(err) + } + err = hDB.WriteSequence(4, 4, common.HexToHash("0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85"), common.HexToHash("0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0"), common.HexToHash("0x0")) + assert.NoError(err) + err = hDB.WriteSequence(7, 7, common.HexToHash("0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba86"), common.HexToHash("0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e1"), common.HexToHash("0x0")) + assert.NoError(err) + for i := 1; i <= 4; i++ { + err = stages.SaveStageProgress(tx, stages.L1VerificationsBatchNo, uint64(i)) + assert.NoError(err) + } + + tx.Commit() + forkId, err := zkEvmImpl.GetForkIdByBatchNumber(ctx, 5) + assert.NoError(err) + assert.Equal(hexutil.Uint64(5), forkId) + + forkId, err = zkEvmImpl.GetForkIdByBatchNumber(ctx, 7) + assert.NoError(err) + assert.Equal(hexutil.Uint64(7), forkId) +} + +func TestGetForkById(t *testing.T) { + assert := assert.New(t) + + ////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + var l1Syncer *syncer.L1Syncer + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + + for f := uint64(1); f <= 3; f++ { + forkId := f + blockNumber := (forkId * uint64(1000)) + err = hDB.WriteForkIdBlockOnce(forkId, blockNumber) + assert.NoError(err) + for i := uint64(1); i <= 10; i++ { + batchNumber := ((forkId - 1) * uint64(10)) + i + err = hDB.WriteForkId(batchNumber, forkId) + assert.NoError(err) + } + } + + tx.Commit() + forkInterval := rpc.ForkInterval{} + + forkIntervalJson, err := zkEvmImpl.GetForkById(ctx, 1) + assert.NoError(err) + err = json.Unmarshal(forkIntervalJson, &forkInterval) + assert.NoError(err) + assert.Equal(hexutil.Uint64(1), forkInterval.ForkId) + assert.Equal(hexutil.Uint64(1), forkInterval.FromBatchNumber) + assert.Equal(hexutil.Uint64(10), forkInterval.ToBatchNumber) + assert.Equal("", forkInterval.Version) + assert.Equal(hexutil.Uint64(1000), forkInterval.BlockNumber) + + forkIntervalJson, err = zkEvmImpl.GetForkById(ctx, 2) + assert.NoError(err) + err = json.Unmarshal(forkIntervalJson, &forkInterval) + assert.NoError(err) + assert.Equal(hexutil.Uint64(2), forkInterval.ForkId) + assert.Equal(hexutil.Uint64(11), forkInterval.FromBatchNumber) + assert.Equal(hexutil.Uint64(20), forkInterval.ToBatchNumber) + assert.Equal("", forkInterval.Version) + assert.Equal(hexutil.Uint64(2000), forkInterval.BlockNumber) + + forkIntervalJson, err = zkEvmImpl.GetForkById(ctx, 3) + assert.NoError(err) + err = json.Unmarshal(forkIntervalJson, &forkInterval) + assert.NoError(err) + assert.Equal(hexutil.Uint64(3), forkInterval.ForkId) + assert.Equal(hexutil.Uint64(21), forkInterval.FromBatchNumber) + assert.Equal(hexutil.Uint64(math.MaxUint64), forkInterval.ToBatchNumber) + assert.Equal("", forkInterval.Version) + assert.Equal(hexutil.Uint64(3000), forkInterval.BlockNumber) +} + +func TestGetForks(t *testing.T) { + assert := assert.New(t) + + ////////////// + contractBackend := backends.NewTestSimulatedBackendWithConfig(t, gspec.Alloc, gspec.Config, gspec.GasLimit) + defer contractBackend.Close() + stateCache := kvcache.New(kvcache.DefaultCoherentConfig) + contractBackend.Commit() + /////////// + + db := contractBackend.DB() + agg := contractBackend.Agg() + + baseApi := NewBaseApi(nil, stateCache, contractBackend.BlockReader(), agg, false, rpccfg.DefaultEvmCallTimeout, contractBackend.Engine(), datadir.New(t.TempDir())) + ethImpl := NewEthAPI(baseApi, db, nil, nil, nil, 5000000, 100_000, ðconfig.Defaults) + var l1Syncer *syncer.L1Syncer + zkEvmImpl := NewZkEvmAPI(ethImpl, db, 100_000, ðconfig.Defaults, l1Syncer, "") + tx, err := db.BeginRw(ctx) + assert.NoError(err) + hDB := hermez_db.NewHermezDb(tx) + + for f := uint64(1); f <= 3; f++ { + forkId := f + blockNumber := (forkId * uint64(1000)) + err = hDB.WriteForkIdBlockOnce(forkId, blockNumber) + assert.NoError(err) + for i := uint64(1); i <= 10; i++ { + batchNumber := ((forkId - 1) * uint64(10)) + i + err = hDB.WriteForkId(batchNumber, forkId) + assert.NoError(err) + } + } + + tx.Commit() + forksJson, err := zkEvmImpl.GetForks(ctx) + assert.NoError(err) + + forks := []rpc.ForkInterval{} + err = json.Unmarshal(forksJson, &forks) + assert.NoError(err) + + assert.Equal(forks[0].ForkId, hexutil.Uint64(1)) + assert.Equal(forks[0].FromBatchNumber, hexutil.Uint64(1)) + assert.Equal(forks[0].ToBatchNumber, hexutil.Uint64(10)) + assert.Equal(forks[0].Version, "") + assert.Equal(forks[0].BlockNumber, hexutil.Uint64(1000)) + + assert.Equal(forks[1].ForkId, hexutil.Uint64(2)) + assert.Equal(forks[1].FromBatchNumber, hexutil.Uint64(11)) + assert.Equal(forks[1].ToBatchNumber, hexutil.Uint64(20)) + assert.Equal(forks[1].Version, "") + assert.Equal(forks[1].BlockNumber, hexutil.Uint64(2000)) + + assert.Equal(forks[2].ForkId, hexutil.Uint64(3)) + assert.Equal(forks[2].FromBatchNumber, hexutil.Uint64(21)) + assert.Equal(forks[2].ToBatchNumber, hexutil.Uint64(math.MaxUint64)) + assert.Equal(forks[2].Version, "") + assert.Equal(forks[2].BlockNumber, hexutil.Uint64(3000)) +} diff --git a/cmd/rpcdaemon/commands/zkevm_counters.go b/cmd/rpcdaemon/commands/zkevm_counters.go index 74d57773019..e7657805bb3 100644 --- a/cmd/rpcdaemon/commands/zkevm_counters.go +++ b/cmd/rpcdaemon/commands/zkevm_counters.go @@ -28,6 +28,15 @@ import ( "github.com/ledgerwatch/erigon/zk/hermez_db" ) +const ( + //used if address not specified + defaultSenderAddress = "0x1111111111111111111111111111111111111111" + + defaultV = "0x1c" + defaultR = "0xa54492cfacf71aef702421b7fbc70636537a7b2fbe5718c5ed970a001bb7756b" + defaultS = "0x2e9fb27acc75955b898f0b12ec52aa34bf08f01db654374484b80bf12f0d841e" +) + type zkevmRPCTransaction struct { Gas hexutil.Uint64 `json:"gas"` GasPrice *hexutil.Big `json:"gasPrice,omitempty"` @@ -36,53 +45,75 @@ type zkevmRPCTransaction struct { To *common.Address `json:"to"` From *common.Address `json:"from"` Value *hexutil.Big `json:"value"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` + Data hexutility.Bytes `json:"data"` } // Tx return types.Transaction from rpcTransaction -func (tx *zkevmRPCTransaction) Tx() types.Transaction { +func (tx *zkevmRPCTransaction) Tx(sr state.StateReader) (types.Transaction, error) { if tx == nil { - return nil + return nil, nil + } + + sender := common.HexToAddress(defaultSenderAddress) + if tx.From != nil { + sender = *tx.From + } + nonce := uint64(0) + + ad, err := sr.ReadAccountData(sender) + if err != nil { + return nil, err + } + if ad != nil { + nonce = ad.Nonce + } + + if tx.Value == nil { + // set this to something non nil + tx.Value = &hexutil.Big{} } gasPrice := uint256.NewInt(1) if tx.GasPrice != nil { gasPrice = uint256.MustFromBig(tx.GasPrice.ToInt()) } + + var data []byte + if tx.Data != nil { + data = tx.Data + } else if tx.Input != nil { + data = tx.Input + } else if tx.To == nil { + return nil, fmt.Errorf("contract creation without data provided") + } + var legacy *types.LegacyTx if tx.To == nil { legacy = types.NewContractCreation( - uint64(tx.Nonce), + nonce, uint256.MustFromBig(tx.Value.ToInt()), uint64(tx.Gas), gasPrice, - tx.Input, + data, ) } else { legacy = types.NewTransaction( - uint64(tx.Nonce), + nonce, *tx.To, uint256.MustFromBig(tx.Value.ToInt()), uint64(tx.Gas), gasPrice, - tx.Input, + data, ) } - if tx.From != nil { - legacy.SetSender(*tx.From) - } + legacy.SetSender(sender) - if tx.V != nil && tx.R != nil && tx.S != nil { - // parse signature raw values V, R, S from local hex strings - legacy.V = *uint256.MustFromBig(tx.V.ToInt()) - legacy.R = *uint256.MustFromBig(tx.R.ToInt()) - legacy.S = *uint256.MustFromBig(tx.S.ToInt()) - } + legacy.V = *uint256.MustFromHex(defaultV) + legacy.R = *uint256.MustFromHex(defaultR) + legacy.S = *uint256.MustFromHex(defaultS) - return legacy + return legacy, nil } // EstimateGas implements eth_estimateGas. Returns an estimate of how much gas is necessary to allow the transaction to complete. The transaction will not be added to the blockchain. @@ -95,13 +126,6 @@ func (zkapi *ZkEvmAPIImpl) EstimateCounters(ctx context.Context, rpcTx *zkevmRPC } defer dbtx.Rollback() - if rpcTx.Value == nil { - // set this to something non nil - rpcTx.Value = &hexutil.Big{} - } - - tx := rpcTx.Tx() - chainConfig, err := api.chainConfig(dbtx) if err != nil { return nil, err @@ -139,6 +163,11 @@ func (zkapi *ZkEvmAPIImpl) EstimateCounters(ctx context.Context, rpcTx *zkevmRPC signer := types.MakeSigner(chainConfig, header.Number.Uint64()) + tx, err := rpcTx.Tx(stateReader) + if err != nil { + return nil, err + } + msg, err := tx.AsMessage(*signer, header.BaseFee, rules) if err != nil { return nil, err @@ -381,7 +410,7 @@ func (api *ZkEvmAPIImpl) TraceTransactionCounters(ctx context.Context, hash comm if config == nil { config = &tracers.TraceConfig_ZkEvm{} } - config.CounterCollector = txCounters.ExecutionCounters() + config.CounterCollector = txCounters // Trace the transaction and return return transactions.TraceTx(ctx, txEnv.Msg, txEnv.BlockContext, txEnv.TxContext, txEnv.Ibs, config, chainConfig, stream, api.ethApi.evmCallTimeout) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 991d313a311..d10edd10854 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -333,7 +333,7 @@ var ( HTTPCORSDomainFlag = cli.StringFlag{ Name: "http.corsdomain", Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", - Value: "", + Value: "*", } HTTPVirtualHostsFlag = cli.StringFlag{ Name: "http.vhosts", @@ -393,7 +393,7 @@ var ( L1CacheEnabledFlag = cli.BoolFlag{ Name: "zkevm.l1-cache-enabled", Usage: "Enable the L1 cache", - Value: true, + Value: false, } L1CachePortFlag = cli.UintFlag{ Name: "zkevm.l1-cache-port", @@ -456,6 +456,11 @@ var ( Usage: "First block to start syncing from on the L1", Value: 0, } + L1FinalizedBlockRequirementFlag = cli.Uint64Flag{ + Name: "zkevm.l1-finalized-block-requirement", + Usage: "The given block must be finalized before sequencer L1 sync continues", + Value: 0, + } L1ContractAddressCheckFlag = cli.BoolFlag{ Name: "zkevm.l1-contract-address-check", Usage: "Check the contract address on the L1", @@ -501,6 +506,21 @@ var ( Usage: "Halt the sequencer on this batch number", Value: 0, } + SequencerResequence = cli.BoolFlag{ + Name: "zkevm.sequencer-resequence", + Usage: "When enabled, the sequencer will automatically resequence unseen batches stored in data stream", + Value: false, + } + SequencerResequenceStrict = cli.BoolFlag{ + Name: "zkevm.sequencer-resequence-strict", + Usage: "Strictly resequence the rolledback batches", + Value: true, + } + SequencerResequenceReuseL1InfoIndex = cli.BoolFlag{ + Name: "zkevm.sequencer-resequence-reuse-l1-info-index", + Usage: "Reuse the L1 info index for resequencing", + Value: true, + } ExecutorUrls = cli.StringFlag{ Name: "zkevm.executor-urls", Usage: "A comma separated list of grpc addresses that host executors", @@ -647,11 +667,6 @@ var ( Usage: "Disable the virtual counters. This has an effect on on sequencer node and when external executor is not enabled.", Value: false, } - SupportGasless = cli.BoolFlag{ - Name: "zkevm.gasless", - Usage: "Support gasless transactions", - Value: false, - } ExecutorPayloadOutput = cli.StringFlag{ Name: "zkevm.executor-payload-output", Usage: "Output the payload of the executor, serialised requests stored to disk by batch number", diff --git a/consensus/misc/eip1559_zk.go b/consensus/misc/eip1559_zk.go index 1d885b18c03..97911d4e150 100644 --- a/consensus/misc/eip1559_zk.go +++ b/consensus/misc/eip1559_zk.go @@ -2,12 +2,13 @@ package misc import ( "math/big" + "github.com/ledgerwatch/erigon/chain" "github.com/ledgerwatch/erigon/core/types" ) func CalcBaseFeeZk(config *chain.Config, parent *types.Header) *big.Int { - if config.SupportGasless { + if config.AllowFreeTransactions { return big.NewInt(0) } if !config.IsLondon(parent.Number.Uint64() + 1) { diff --git a/core/blockchain_zkevm.go b/core/blockchain_zkevm.go index 26ac082e7be..bec27d6fd73 100644 --- a/core/blockchain_zkevm.go +++ b/core/blockchain_zkevm.go @@ -18,17 +18,13 @@ package core import ( + "errors" "fmt" "math/big" "time" "github.com/gateway-fm/cdk-erigon-lib/common" - "github.com/ledgerwatch/erigon/smt/pkg/blockinfo" - "github.com/ledgerwatch/erigon/chain" - - "errors" - "github.com/ledgerwatch/erigon/common/math" "github.com/ledgerwatch/erigon/consensus" "github.com/ledgerwatch/erigon/consensus/misc" @@ -36,6 +32,8 @@ import ( "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/core/vm" "github.com/ledgerwatch/erigon/core/vm/evmtypes" + "github.com/ledgerwatch/erigon/smt/pkg/blockinfo" + "github.com/ledgerwatch/erigon/zk/hermez_db" "github.com/ledgerwatch/erigon/zk/utils" ) @@ -254,13 +252,13 @@ func PrepareBlockTxExecution( /////////////////////////////////////////// //[zkevm] - get the last batch number so we can check for empty batches in between it and the new one lastBatchInserted, err := roHermezDb.GetBatchNoByL2Block(blockNum - 1) - if err != nil { + if err != nil && !errors.Is(err, hermez_db.ErrorNotStored) { return nil, nil, nil, nil, fmt.Errorf("failed to get last batch inserted: %v", err) } // write batches between last block and this if they exist currentBatch, err := roHermezDb.GetBatchNoByL2Block(blockNum) - if err != nil { + if err != nil && !errors.Is(err, hermez_db.ErrorNotStored) { return nil, nil, nil, nil, err } diff --git a/core/rawdb/accessors_chain_zkevm.go b/core/rawdb/accessors_chain_zkevm.go index 19b59b99d8c..15f3fe24bf8 100644 --- a/core/rawdb/accessors_chain_zkevm.go +++ b/core/rawdb/accessors_chain_zkevm.go @@ -1,6 +1,7 @@ package rawdb import ( + "bytes" "encoding/binary" "fmt" @@ -9,6 +10,7 @@ import ( "github.com/gateway-fm/cdk-erigon-lib/common/hexutility" "github.com/gateway-fm/cdk-erigon-lib/kv" "github.com/gateway-fm/cdk-erigon-lib/kv/kvcfg" + "github.com/ledgerwatch/erigon/common" "github.com/ledgerwatch/erigon/common/dbutils" "github.com/ledgerwatch/erigon/common/math" "github.com/ledgerwatch/erigon/core/types" @@ -84,9 +86,9 @@ func WriteBodyAndTransactions(db kv.RwTx, hash libcommon.Hash, number uint64, tx } transactionV3, _ := kvcfg.TransactionsV3.Enabled(db.(kv.Tx)) if transactionV3 { - err = WriteTransactions(db, txs, data.BaseTxId+1, &hash) + err = OverwriteTransactions(db, txs, data.BaseTxId, &hash) } else { - err = WriteTransactions(db, txs, data.BaseTxId+1, nil) + err = OverwriteTransactions(db, txs, data.BaseTxId, nil) } if err != nil { return fmt.Errorf("failed to WriteTransactions: %w", err) @@ -94,6 +96,34 @@ func WriteBodyAndTransactions(db kv.RwTx, hash libcommon.Hash, number uint64, tx return nil } +func OverwriteTransactions(db kv.RwTx, txs []types.Transaction, baseTxId uint64, blockHash *libcommon.Hash) error { + txId := baseTxId + buf := bytes.NewBuffer(nil) + for _, tx := range txs { + txIdKey := make([]byte, 8) + binary.BigEndian.PutUint64(txIdKey, txId) + txId++ + + buf.Reset() + if err := rlp.Encode(buf, tx); err != nil { + return fmt.Errorf("broken tx rlp: %w", err) + } + + // If next Append returns KeyExists error - it means you need to open transaction in App code before calling this func. Batch is also fine. + if blockHash != nil { + key := append(txIdKey, blockHash.Bytes()...) + if err := db.Put(kv.EthTxV3, key, common.CopyBytes(buf.Bytes())); err != nil { + return err + } + } else { + if err := db.Put(kv.EthTx, txIdKey, common.CopyBytes(buf.Bytes())); err != nil { + return err + } + } + } + return nil +} + func GetBodyTransactions(tx kv.RwTx, fromBlockNum, toBlockNum uint64) (*[]types.Transaction, error) { var transactions []types.Transaction if err := tx.ForEach(kv.BlockBody, hexutility.EncodeTs(fromBlockNum), func(k, v []byte) error { diff --git a/core/state/intra_block_state.go b/core/state/intra_block_state.go index 08b460f4ea2..244a9a49326 100644 --- a/core/state/intra_block_state.go +++ b/core/state/intra_block_state.go @@ -23,9 +23,9 @@ import ( "encoding/hex" - "github.com/holiman/uint256" libcommon "github.com/gateway-fm/cdk-erigon-lib/common" types2 "github.com/gateway-fm/cdk-erigon-lib/types" + "github.com/holiman/uint256" "github.com/ledgerwatch/erigon/chain" "github.com/ledgerwatch/erigon/common/u256" "github.com/ledgerwatch/erigon/core/types" @@ -362,7 +362,7 @@ func (sdb *IntraBlockState) SetCode(addr libcommon.Address, code []byte) { return } - hashedBytecode, _ := utils.HashContractBytecode(hex.EncodeToString(code)) + hashedBytecode := utils.HashContractBytecode(hex.EncodeToString(code)) stateObject.SetCode(libcommon.HexToHash(hashedBytecode), code) } } diff --git a/core/state/intra_block_state_zkevm.go b/core/state/intra_block_state_zkevm.go index f903600d1f6..fcd1f492dc7 100644 --- a/core/state/intra_block_state_zkevm.go +++ b/core/state/intra_block_state_zkevm.go @@ -34,8 +34,8 @@ type ReadOnlyHermezDb interface { GetIntermediateTxStateRoot(blockNum uint64, txhash libcommon.Hash) (libcommon.Hash, error) GetReusedL1InfoTreeIndex(blockNum uint64) (bool, error) GetSequenceByBatchNo(batchNo uint64) (*zktypes.L1BatchInfo, error) + GetHighestBlockInBatch(batchNo uint64) (uint64, bool, error) GetSequenceByBatchNoOrHighest(batchNo uint64) (*zktypes.L1BatchInfo, error) - GetHighestBlockInBatch(batchNo uint64) (uint64, error) GetLowestBlockInBatch(batchNo uint64) (uint64, bool, error) GetL2BlockNosByBatch(batchNo uint64) ([]uint64, error) GetBatchGlobalExitRoot(batchNum uint64) (*dstypes.GerUpdate, error) diff --git a/core/state/trie_db.go b/core/state/trie_db.go index 863d8c44873..ea7043502f8 100644 --- a/core/state/trie_db.go +++ b/core/state/trie_db.go @@ -912,36 +912,16 @@ func (tds *TrieDbState) ResolveSMTRetainList() (*trie.RetainList, error) { for _, addrHash := range accountTouches { addr := common.BytesToAddress(tds.preimageMap[addrHash]).String() - nonceKey, err := utils.KeyEthAddrNonce(addr) - - if err != nil { - return nil, err - } - + nonceKey := utils.KeyEthAddrNonce(addr) keys = append(keys, nonceKey.GetPath()) - balanceKey, err := utils.KeyEthAddrBalance(addr) - - if err != nil { - return nil, err - } - + balanceKey := utils.KeyEthAddrBalance(addr) keys = append(keys, balanceKey.GetPath()) - codeKey, err := utils.KeyContractCode(addr) - - if err != nil { - return nil, err - } - + codeKey := utils.KeyContractCode(addr) keys = append(keys, codeKey.GetPath()) - codeLengthKey, err := utils.KeyContractLength(addr) - - if err != nil { - return nil, err - } - + codeLengthKey := utils.KeyContractLength(addr) keys = append(keys, codeLengthKey.GetPath()) } @@ -949,11 +929,7 @@ func (tds *TrieDbState) ResolveSMTRetainList() (*trie.RetainList, error) { a := utils.ConvertHexToBigInt(ethAddr) addr := utils.ScalarToArrayBig(a) - storageKey, err := utils.KeyContractStorage(addr, key) - - if err != nil { - return nil, err - } + storageKey := utils.KeyContractStorage(addr, key) return storageKey.GetPath(), nil } diff --git a/core/vm/contracts_zkevm.go b/core/vm/contracts_zkevm.go index 50b19502bcf..780d1d102af 100644 --- a/core/vm/contracts_zkevm.go +++ b/core/vm/contracts_zkevm.go @@ -216,8 +216,9 @@ func (c *ripemd160hash_zkevm) Run(input []byte) ([]byte, error) { // data copy implemented as a native contract. type dataCopy_zkevm struct { - enabled bool - cc *CounterCollector + enabled bool + cc *CounterCollector + outLength int } func (c *dataCopy_zkevm) SetCounterCollector(cc *CounterCollector) { @@ -225,6 +226,7 @@ func (c *dataCopy_zkevm) SetCounterCollector(cc *CounterCollector) { } func (c *dataCopy_zkevm) SetOutputLength(outLength int) { + c.outLength = outLength } // RequiredGas returns the gas required to execute the pre-compiled contract. @@ -241,6 +243,11 @@ func (c *dataCopy_zkevm) Run(in []byte) ([]byte, error) { if !c.enabled { return []byte{}, ErrUnsupportedPrecompile } + + if c.cc != nil { + c.cc.preIdentity(len(in), c.outLength) + } + return in, nil } diff --git a/core/vm/instructions_zkevm.go b/core/vm/instructions_zkevm.go index 5c3e68e9faf..f17d849345e 100644 --- a/core/vm/instructions_zkevm.go +++ b/core/vm/instructions_zkevm.go @@ -477,6 +477,10 @@ func opReturn_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCo return nil, nil } +func opUndefined_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, nil +} + func opSload_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { loc := scope.Stack.Peek() interpreter.hasherBuf = loc.Bytes32() diff --git a/core/vm/interpreter_zkevm.go b/core/vm/interpreter_zkevm.go index 203d8019623..7b30435e1df 100644 --- a/core/vm/interpreter_zkevm.go +++ b/core/vm/interpreter_zkevm.go @@ -72,6 +72,8 @@ func shouldExecuteLastOpCode(op OpCode) bool { fallthrough case CREATE2: fallthrough + case SENDALL: + fallthrough case SLOAD: fallthrough case SSTORE: diff --git a/core/vm/jump_table_zkevm.go b/core/vm/jump_table_zkevm.go index 8bcf0bd6460..f544dbda99f 100644 --- a/core/vm/jump_table_zkevm.go +++ b/core/vm/jump_table_zkevm.go @@ -38,6 +38,8 @@ func newForkID4InstructionSet() JumpTable { instructionSet[EXTCODEHASH].execute = opExtCodeHash_zkevm + instructionSet[SENDALL] = instructionSet[INVALID] + // SELFDESTRUCT is replaces by SENDALL instructionSet[SELFDESTRUCT] = &operation{ execute: opSendAll_zkevm, @@ -90,6 +92,7 @@ func overrideJumpTableForLastOpcodeForkId12(jt *JumpTable) { jt[CREATE].execute = opCreate_zkevm_lastOpCode jt[RETURN].execute = opReturn_lastOpCode jt[CREATE2].execute = opCreate2_zkevm_lastOpCode + jt[SENDALL].execute = opUndefined_lastOpCode jt[SLOAD].execute = opSload_lastOpCode jt[SSTORE].execute = opSstore_lastOpCode } diff --git a/core/vm/opcodes_zkevm.go b/core/vm/opcodes_zkevm.go index ee0808e46aa..f1b22c90174 100644 --- a/core/vm/opcodes_zkevm.go +++ b/core/vm/opcodes_zkevm.go @@ -1,9 +1,12 @@ package vm -const () +const ( + SENDALL OpCode = 0xfb +) // adding extra opcodes dynamically to keep separate from the main codebase // that simplifies rebasing new versions of Erigon func init() { - + opCodeToString[SENDALL] = "SENDALL" + stringToOp["SENDALL"] = SENDALL } diff --git a/core/vm/zk_batch_counters.go b/core/vm/zk_batch_counters.go index 16dc10bb74f..4b58c0fa9d7 100644 --- a/core/vm/zk_batch_counters.go +++ b/core/vm/zk_batch_counters.go @@ -22,10 +22,6 @@ type BatchCounterCollector struct { rlpCombinedCounters Counters executionCombinedCounters Counters processingCombinedCounters Counters - - rlpCombinedCountersCache Counters - executionCombinedCountersCache Counters - processingCombinedCountersCache Counters } func NewBatchCounterCollector(smtMaxLevel int, forkId uint16, mcpReduction float64, unlimitedCounters bool, addonCounters *Counters) *BatchCounterCollector { @@ -92,6 +88,12 @@ func (bcc *BatchCounterCollector) AddNewTransactionCounters(txCounters *Transact return bcc.CheckForOverflow(false) //no need to calculate the merkle proof here } +func (bcc *BatchCounterCollector) RemovePreviousTransactionCounters() { + lastTx := bcc.transactions[len(bcc.transactions)-1] + bcc.UndoTransactionCountersCache(lastTx) + bcc.transactions = bcc.transactions[:len(bcc.transactions)-1] +} + func (bcc *BatchCounterCollector) ClearTransactionCounters() { bcc.transactions = bcc.transactions[:0] } @@ -158,7 +160,7 @@ func (bcc *BatchCounterCollector) CheckForOverflow(verifyMerkleProof bool) (bool for _, v := range combined { logText += fmt.Sprintf(" %s: initial: %v used: %v (remaining: %v)", v.name, v.initialAmount, v.used, v.remaining) } - log.Info(logText) + log.Debug(logText) } return overflow, nil @@ -221,7 +223,7 @@ func (bcc *BatchCounterCollector) CombineCollectors(verifyMerkleProof bool) (Cou } } - for k, _ := range combined { + for k := range combined { val := bcc.rlpCombinedCounters[k].used + bcc.executionCombinedCounters[k].used + bcc.processingCombinedCounters[k].used combined[k].used += val combined[k].remaining -= val @@ -254,15 +256,8 @@ func (bcc *BatchCounterCollector) CombineCollectorsNoChanges() Counters { } for _, tx := range bcc.transactions { - for k, v := range tx.rlpCounters.counters { - combined[k].used += v.used - combined[k].remaining -= v.used - } - for k, v := range tx.executionCounters.counters { - combined[k].used += v.used - combined[k].remaining -= v.used - } - for k, v := range tx.processingCounters.counters { + txCounters := tx.CombineCounters() + for k, v := range txCounters { combined[k].used += v.used combined[k].remaining -= v.used } @@ -286,3 +281,15 @@ func (bcc *BatchCounterCollector) UpdateExecutionAndProcessingCountersCache(txCo bcc.processingCombinedCounters[k].used += v.used } } + +func (bcc *BatchCounterCollector) UndoTransactionCountersCache(txCounters *TransactionCounter) { + for k, v := range txCounters.rlpCounters.counters { + bcc.rlpCombinedCounters[k].used -= v.used + } + for k, v := range txCounters.executionCounters.counters { + bcc.executionCombinedCounters[k].used -= v.used + } + for k, v := range txCounters.processingCounters.counters { + bcc.processingCombinedCounters[k].used -= v.used + } +} diff --git a/core/vm/zk_counters.go b/core/vm/zk_counters.go index 783e1b14645..0fd1aad24de 100644 --- a/core/vm/zk_counters.go +++ b/core/vm/zk_counters.go @@ -67,38 +67,53 @@ func (c *Counter) AsMap() map[string]int { } } -type Counters map[CounterKey]*Counter +type Counters []*Counter -func NewCountersFromUsedMap(used map[string]int) *Counters { +func NewCounters() Counters { + array := make(Counters, CounterTypesCount) + return array +} + +func NewCountersFromUsedArray(used []int) *Counters { res := Counters{} for k, v := range used { - res[CounterKey(k)] = &Counter{used: v} + res[k] = &Counter{used: v} } return &res } func (c Counters) UsedAsString() string { - res := fmt.Sprintf("[SHA: %v]", c[SHA].used) - res += fmt.Sprintf("[A: %v]", c[A].used) - res += fmt.Sprintf("[B: %v]", c[B].used) - res += fmt.Sprintf("[K: %v]", c[K].used) - res += fmt.Sprintf("[M: %v]", c[M].used) - res += fmt.Sprintf("[P: %v]", c[P].used) - res += fmt.Sprintf("[S: %v]", c[S].used) - res += fmt.Sprintf("[D: %v]", c[D].used) + res := fmt.Sprintf("[%s: %v]", CounterKeyNames[SHA], c[SHA].used) + res += fmt.Sprintf("[%s: %v]", CounterKeyNames[A], c[A].used) + res += fmt.Sprintf("[%s: %v]", CounterKeyNames[B], c[B].used) + res += fmt.Sprintf("[%s: %v]", CounterKeyNames[K], c[K].used) + res += fmt.Sprintf("[%s: %v]", CounterKeyNames[M], c[M].used) + res += fmt.Sprintf("[%s: %v]", CounterKeyNames[P], c[P].used) + res += fmt.Sprintf("[%s: %v]", CounterKeyNames[S], c[S].used) + res += fmt.Sprintf("[%s: %v]", CounterKeyNames[D], c[D].used) return res } +func (c Counters) UsedAsArray() []int { + array := make([]int, len(c)) + + for i, v := range c { + array[i] = v.used + } + + return array +} + func (c Counters) UsedAsMap() map[string]int { return map[string]int{ - "SHA": c[SHA].used, - "A": c[A].used, - "B": c[B].used, - "K": c[K].used, - "M": c[M].used, - "P": c[P].used, - "S": c[S].used, - "D": c[D].used, + string(CounterKeyNames[S]): c[S].used, + string(CounterKeyNames[A]): c[A].used, + string(CounterKeyNames[B]): c[B].used, + string(CounterKeyNames[M]): c[M].used, + string(CounterKeyNames[K]): c[K].used, + string(CounterKeyNames[D]): c[D].used, + string(CounterKeyNames[P]): c[P].used, + string(CounterKeyNames[SHA]): c[SHA].used, } } @@ -144,17 +159,25 @@ func (cc Counters) Clone() Counters { return clonedCounters } -type CounterKey string +type CounterKey int +type CounterName string + +const ( + S CounterKey = 0 + A CounterKey = 1 + B CounterKey = 2 + M CounterKey = 3 + K CounterKey = 4 + D CounterKey = 5 + P CounterKey = 6 + SHA CounterKey = 7 + + CounterTypesCount = 8 +) var ( - S CounterKey = "S" - A CounterKey = "A" - B CounterKey = "B" - M CounterKey = "M" - K CounterKey = "K" - D CounterKey = "D" - P CounterKey = "P" - SHA CounterKey = "SHA" + // important!!! must match the indexes of the keys + CounterKeyNames = []CounterName{"S", "A", "B", "M", "K", "D", "P", "SHA"} ) type CounterCollector struct { @@ -579,7 +602,7 @@ func (cc *CounterCollector) finishBatchProcessing() { cc.Deduct(S, 200) cc.Deduct(K, 2) cc.Deduct(P, cc.smtLevels) - cc.Deduct(B, 1) + cc.Deduct(B, 2) } func (cc *CounterCollector) isColdAddress() { @@ -709,6 +732,7 @@ func (cc *CounterCollector) setupNewBlockInfoTree() { func (cc *CounterCollector) verifyMerkleProof() { cc.Deduct(S, 250) + cc.Deduct(B, 1) cc.Deduct(K, 33) } @@ -763,9 +787,9 @@ func (cc *CounterCollector) decodeChangeL2BlockTx() { } func (cc *CounterCollector) ecAdd() { - cc.Deduct(S, 323) - cc.Deduct(B, 33) - cc.Deduct(A, 40) + cc.Deduct(S, 800) + cc.Deduct(B, 50) + cc.Deduct(A, 50) } func (cc *CounterCollector) preECMul() { @@ -778,9 +802,9 @@ func (cc *CounterCollector) preECMul() { } func (cc *CounterCollector) ecMul() { - cc.Deduct(S, 162890) - cc.Deduct(B, 16395) - cc.Deduct(A, 19161) + cc.Deduct(S, 175000) + cc.Deduct(B, 20000) + cc.Deduct(A, 20000) } func (cc *CounterCollector) preECPairing(inputsCount int) { @@ -794,9 +818,9 @@ func (cc *CounterCollector) preECPairing(inputsCount int) { } func (cc *CounterCollector) ecPairing(inputsCount int) { - cc.Deduct(S, 16+inputsCount*184017+171253) - cc.Deduct(B, inputsCount*3986+650) - cc.Deduct(A, inputsCount*13694+15411) + cc.Deduct(S, 16+inputsCount*200000+175000) + cc.Deduct(B, inputsCount*4100+750) + cc.Deduct(A, inputsCount*15000+17500) } func (cc *CounterCollector) preModExp(callDataLength, returnDataLength, bLen, mLen, eLen int, base, exponent, modulus *big.Int) { @@ -854,7 +878,7 @@ func (cc *CounterCollector) multiCall(call func(), times int) { func (cc *CounterCollector) preSha256(callDataLength int) { cc.Deduct(S, 100) cc.Deduct(B, 1) - cc.Deduct(SHA, int(math.Ceil(float64(callDataLength+1)/64))) + cc.Deduct(SHA, int(math.Ceil(float64(callDataLength+8)/64))) cc.multiCall(cc.divArith, 2) cc.mStore32() cc.mStoreX() diff --git a/core/vm/zk_counters_limits.go b/core/vm/zk_counters_limits.go index b7632f307ee..8751c1306aa 100644 --- a/core/vm/zk_counters_limits.go +++ b/core/vm/zk_counters_limits.go @@ -36,48 +36,49 @@ type counterLimits struct { } func createCountrsByLimits(c counterLimits) *Counters { - return &Counters{ - S: { - remaining: c.totalSteps, - name: "defaultTotalSteps", - initialAmount: c.totalSteps, - }, - A: { - remaining: c.arith, - name: "arith", - initialAmount: c.arith, - }, - B: { - remaining: c.binary, - name: "binary", - initialAmount: c.binary, - }, - M: { - remaining: c.memAlign, - name: "memAlign", - initialAmount: c.memAlign, - }, - K: { - remaining: c.keccaks, - name: "keccaks", - initialAmount: c.keccaks, - }, - D: { - remaining: c.padding, - name: "padding", - initialAmount: c.padding, - }, - P: { - remaining: c.poseidon, - name: "poseidon", - initialAmount: c.poseidon, - }, - SHA: { - remaining: c.sha256, - name: "sha256", - initialAmount: c.sha256, - }, + counters := NewCounters() + + counters[S] = &Counter{ + remaining: c.totalSteps, + name: "defaultTotalSteps", + initialAmount: c.totalSteps, + } + counters[A] = &Counter{ + remaining: c.arith, + name: "arith", + initialAmount: c.arith, + } + counters[B] = &Counter{ + remaining: c.binary, + name: "binary", + initialAmount: c.binary, + } + counters[M] = &Counter{ + remaining: c.memAlign, + name: "memAlign", + initialAmount: c.memAlign, + } + counters[K] = &Counter{ + remaining: c.keccaks, + name: "keccaks", + initialAmount: c.keccaks, + } + counters[D] = &Counter{ + remaining: c.padding, + name: "padding", + initialAmount: c.padding, + } + counters[P] = &Counter{ + remaining: c.poseidon, + name: "poseidon", + initialAmount: c.poseidon, + } + counters[SHA] = &Counter{ + remaining: c.sha256, + name: "sha256", + initialAmount: c.sha256, } + return &counters } // tp ne used on next forkid counters diff --git a/core/vm/zk_transaction_counters.go b/core/vm/zk_transaction_counters.go index c27388b9b83..47bc562a78b 100644 --- a/core/vm/zk_transaction_counters.go +++ b/core/vm/zk_transaction_counters.go @@ -49,6 +49,18 @@ func NewTransactionCounter(transaction types.Transaction, smtMaxLevel int, forkI return tc } +func (tc *TransactionCounter) CombineCounters() Counters { + combined := NewCounters() + for k := range tc.rlpCounters.counters { + val := tc.rlpCounters.counters[k].used + tc.executionCounters.counters[k].used + tc.processingCounters.counters[k].used + combined[k] = &Counter{ + used: val, + } + } + + return combined +} + func (tc *TransactionCounter) Clone() *TransactionCounter { var l2DataCacheCopy []byte if tc.l2DataCache != nil { diff --git a/docs/datastream/datastream.md b/docs/datastream/datastream.md index c8b022b3993..588123f5a61 100644 --- a/docs/datastream/datastream.md +++ b/docs/datastream/datastream.md @@ -70,6 +70,10 @@ There are bookmarks for Batches (type = 1) and L2Blocks (type = 2). ​​​​- Debug +### L2BlockEnd + +​​​​- Number + **Notes** * **Hash** - hash calculated by the node and used in the RPC @@ -178,9 +182,11 @@ Expected Entries Order […] [Transaction] […] +[L2Block2End] [L2BlockBookMark] [L2Block] […] +[L2Block2End] [BatchEnd] [BatchBookMark] [BatchStart] @@ -190,14 +196,9 @@ Expected Entries Order ### Fork ID names -BLUEBERRY = 4 - -DRAGONFRUIT = 5 - -INCABERRY = 6 - -ETROG = 7 - -ELDERBERRY = 8 - -ELDELBERRY_2 = 9 +- BLUEBERRY = 4 +- DRAGONFRUIT = 5 +- INCABERRY = 6 +- ETROG = 7 +- ELDERBERRY = 8 +- ELDELBERRY_2 = 9 diff --git a/eth/backend.go b/eth/backend.go index 6dbfd0c6506..d2c42810c87 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -760,9 +760,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { // entering ZK territory! cfg := backend.config - // update the chain config with the zero gas from the flags - backend.chainConfig.SupportGasless = cfg.Gasless - + backend.chainConfig.AllowFreeTransactions = cfg.AllowFreeTransactions l1Urls := strings.Split(cfg.L1RpcUrl, ",") if cfg.Zk.L1CacheEnabled { @@ -847,6 +845,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { cfg.L1HighestBlockType, ) + log.Info("Rollup ID", "rollupId", cfg.L1RollupId) + // check contract addresses in config against L1 if cfg.Zk.L1ContractAddressCheck { success, err := l1ContractAddressCheck(ctx, cfg.Zk, backend.l1Syncer) diff --git a/eth/ethconfig/config_zkevm.go b/eth/ethconfig/config_zkevm.go index 997f94610d0..32de04302b2 100644 --- a/eth/ethconfig/config_zkevm.go +++ b/eth/ethconfig/config_zkevm.go @@ -28,6 +28,7 @@ type Zk struct { L1HighestBlockType string L1MaticContractAddress common.Address L1FirstBlock uint64 + L1FinalizedBlockRequirement uint64 L1CacheEnabled bool L1CachePort uint RpcRateLimits int @@ -38,6 +39,9 @@ type Zk struct { SequencerBatchVerificationTimeout time.Duration SequencerTimeoutOnEmptyTxPool time.Duration SequencerHaltOnBatchNumber uint64 + SequencerResequence bool + SequencerResequenceStrict bool + SequencerResequenceReuseL1InfoIndex bool ExecutorUrls []string ExecutorStrictMode bool ExecutorRequestTimeout time.Duration diff --git a/eth/stagedsync/sync_zkevm.go b/eth/stagedsync/sync_zkevm.go index 974fd4f1803..960cb6b74e9 100644 --- a/eth/stagedsync/sync_zkevm.go +++ b/eth/stagedsync/sync_zkevm.go @@ -1,10 +1,11 @@ package stagedsync import ( + "fmt" + "github.com/gateway-fm/cdk-erigon-lib/kv" "github.com/ledgerwatch/erigon/zk/hermez_db" "github.com/ledgerwatch/log/v3" - "fmt" ) // UnwindToBatch is used to unwind all stages to the highest block of the batch passed in @@ -12,13 +13,13 @@ func (s *Sync) UnwindToBatch(unwindPoint uint64, tx kv.RwTx) error { // calculate block to unwind to (the node will be synced up to and including this block after the unwind completes) hdb := hermez_db.NewHermezDbReader(tx) - unwindPointBlock, err := hdb.GetHighestBlockInBatch(unwindPoint) + unwindPointBlock, found, err := hdb.GetHighestBlockInBatch(unwindPoint) if err != nil { return err } - if unwindPointBlock == 0 { - return fmt.Errorf("no batch found at block %d", unwindPoint) + if !found { + return fmt.Errorf("no block found at batch %d", unwindPoint) } log.Info("UnwindToBatch", "batchNo", unwindPoint, "blockNo", unwindPointBlock) diff --git a/eth/tracers/api_zkevm.go b/eth/tracers/api_zkevm.go index 3de153f5a9a..1550ea935a1 100644 --- a/eth/tracers/api_zkevm.go +++ b/eth/tracers/api_zkevm.go @@ -21,6 +21,6 @@ type TraceConfig_ZkEvm struct { BorTraceEnabled *bool BorTx *bool - CounterCollector *vm.CounterCollector + CounterCollector *vm.TransactionCounter SmtDepth *int } diff --git a/eth/tracers/logger/json_stream_zkevm.go b/eth/tracers/logger/json_stream_zkevm.go index 52716eafe42..4bcd41f91d9 100644 --- a/eth/tracers/logger/json_stream_zkevm.go +++ b/eth/tracers/logger/json_stream_zkevm.go @@ -33,6 +33,7 @@ type JsonStreamLogger_ZkEvm struct { counterCollector *vm.CounterCollector stateClosed bool + memSize int } // NewStructLogger returns a new logger @@ -204,36 +205,23 @@ func (l *JsonStreamLogger_ZkEvm) writeMemory(memory *vm.Memory) { if !l.cfg.DisableMemory { memData := memory.Data() - //[zkevm] don't print empty bytes in memory array after the last non-empty byte line - filteredByteLines := [][]byte{} - foundValueLine := false - for i := len(memData); i-32 >= 0; i -= 32 { - bytes := memData[i-32 : i] - - isEmpty := true - if !foundValueLine { - for _, b := range bytes { - if b != 0 { - isEmpty = false - foundValueLine = true - break - } - } - } - - if !isEmpty || foundValueLine { - filteredByteLines = append(filteredByteLines, bytes) - } + // on first occurance don't expand memory + // this is because in interpreter we expand the memory before we execute the opcode + // and the state for traced opcode should be before the execution of the opcode + if l.memSize < len(memData) { + size := len(memData) + memData = memData[:l.memSize] + l.memSize = size } l.stream.WriteMore() l.stream.WriteObjectField("memory") l.stream.WriteArrayStart() - for i := len(filteredByteLines) - 1; i >= 0; i-- { - if i != len(filteredByteLines)-1 { + for i := len(memData); i-32 >= 0; i -= 32 { + if i != len(memData) { // first 32 bytes, don't add a comma l.stream.WriteMore() } - l.stream.WriteString(string(l.hexEncodeBuf[0:hex.Encode(l.hexEncodeBuf[:], filteredByteLines[i])])) + l.stream.WriteString(string(l.hexEncodeBuf[0:hex.Encode(l.hexEncodeBuf[:], memData[i-32:i])])) } l.stream.WriteArrayEnd() diff --git a/go.mod b/go.mod index 499bc11544d..99ad830e1d9 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/emicklei/dot v1.0.0 github.com/emirpasic/gods v1.18.1 github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c - github.com/gateway-fm/cdk-erigon-lib v0.0.0-20240829075742-c69f9871e8c9 + github.com/gateway-fm/cdk-erigon-lib v0.0.0-20240905140438-511c680ed02e github.com/gateway-fm/vectorized-poseidon-gold v1.0.0 github.com/gballet/go-verkle v0.0.0-20221121182333-31427a1f2d35 github.com/go-stack/stack v1.8.1 @@ -93,6 +93,7 @@ require ( github.com/valyala/fastjson v1.6.4 github.com/vektah/gqlparser/v2 v2.5.1 github.com/xsleonard/go-merkle v1.1.0 + go.uber.org/mock v0.4.0 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.22.0 golang.org/x/exp v0.0.0-20230321023759-10a507213a29 @@ -182,7 +183,6 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/ledgerwatch/interfaces v0.0.0-20230412092010-e1c4a1a4279e // indirect github.com/lib/pq v1.10.7 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect @@ -198,7 +198,6 @@ require ( github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect - github.com/matryer/moq v0.3.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect diff --git a/go.sum b/go.sum index ccc7a5928f9..d95193e2c64 100644 --- a/go.sum +++ b/go.sum @@ -238,10 +238,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/garslo/gogen v0.0.0-20170307003452-d6ebae628c7c h1:uYNKzPntb8c6DKvP9EfrBjkLkU7pM4lM+uuHSIa8UtU= github.com/garslo/gogen v0.0.0-20170307003452-d6ebae628c7c/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= -github.com/gateway-fm/cdk-erigon-lib v0.0.0-20240819202640-e541bb90668f h1:QWDcgNitBfcFrNMZYdLMuSSSWBfA1tGpRIXd1CJuJ1g= -github.com/gateway-fm/cdk-erigon-lib v0.0.0-20240819202640-e541bb90668f/go.mod h1:Ky//z32wQOTY/drV858kr8dECBD9OBV9Xzy+aG6TxSk= -github.com/gateway-fm/cdk-erigon-lib v0.0.0-20240829075742-c69f9871e8c9 h1:GLP1WB+Mec7b7vO+4egFlE2zlpFJH9i0o4kW48WT2JI= -github.com/gateway-fm/cdk-erigon-lib v0.0.0-20240829075742-c69f9871e8c9/go.mod h1:Ky//z32wQOTY/drV858kr8dECBD9OBV9Xzy+aG6TxSk= +github.com/gateway-fm/cdk-erigon-lib v0.0.0-20240905140438-511c680ed02e h1:+UR9ZyXSpJIz0byiIukuznjDRKux9EaQO3BqqolfYXo= +github.com/gateway-fm/cdk-erigon-lib v0.0.0-20240905140438-511c680ed02e/go.mod h1:Ky//z32wQOTY/drV858kr8dECBD9OBV9Xzy+aG6TxSk= github.com/gateway-fm/vectorized-poseidon-gold v1.0.0 h1:Du0ZW+fkZhgRNGx/gAkHnMj3/Rl8uJkAEe+ZDPX3PDw= github.com/gateway-fm/vectorized-poseidon-gold v1.0.0/go.mod h1:VLGQpyjrOg8+FugH/+d8tfYd/c3z4Xqa+zbUBITygaw= github.com/gballet/go-verkle v0.0.0-20221121182333-31427a1f2d35 h1:I8QswD9gf3VEpr7bpepKKOm7ChxFITIG+oc1I5/S0no= @@ -522,8 +520,6 @@ github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7 github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/ledgerwatch/erigon-snapshot v1.1.1-0.20230404044759-5dec854ce336 h1:Yxmt4Wyd0RCLr7UJJAl0ApCP/f5qkWfvHfgPbnI8ghM= github.com/ledgerwatch/erigon-snapshot v1.1.1-0.20230404044759-5dec854ce336/go.mod h1:3AuPxZc85jkehh/HA9h8gabv5MSi3kb/ddtzBsTVJFo= -github.com/ledgerwatch/interfaces v0.0.0-20230412092010-e1c4a1a4279e h1:mT6GE/XsuUVQGTcZjrq0KoINds2fKa8VsHhGbe2PF54= -github.com/ledgerwatch/interfaces v0.0.0-20230412092010-e1c4a1a4279e/go.mod h1:ugQv1QllJzBny3cKZKxUrSnykkjkBgm27eQM6dnGAcc= github.com/ledgerwatch/log/v3 v3.7.0 h1:aFPEZdwZx4jzA3+/Pf8wNDN5tCI0cIolq/kfvgcM+og= github.com/ledgerwatch/log/v3 v3.7.0/go.mod h1:J2Jl6zV/58LeA6LTaVVnCGyf1/cYYSEOOLHY4ZN8S2A= github.com/ledgerwatch/secp256k1 v1.0.0 h1:Usvz87YoTG0uePIV8woOof5cQnLXGYa162rFf3YnwaQ= @@ -579,8 +575,6 @@ github.com/maticnetwork/crand v1.0.2 h1:Af0tAivC8zrxXDpGWNWVT/0s1fOz8w0eRbahZgUR github.com/maticnetwork/crand v1.0.2/go.mod h1:/NRNL3bj2eYdqpWmoIP5puxndTpi0XRxpj5ZKxfHjyg= github.com/maticnetwork/polyproto v0.0.2 h1:cPxuxbIDItdwGnucc3lZB58U8Zfe1mH73PWTGd15554= github.com/maticnetwork/polyproto v0.0.2/go.mod h1:e1mU2EXSwEpn5jM7GfNwu3AupsV6WAGoPFFfswXOF0o= -github.com/matryer/moq v0.3.1 h1:kLDiBJoGcusWS2BixGyTkF224aSCD8nLY24tj/NcTCs= -github.com/matryer/moq v0.3.1/go.mod h1:RJ75ZZZD71hejp39j4crZLsEDszGk6iH4v4YsWFKH4s= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -961,6 +955,8 @@ go.uber.org/fx v1.19.1/go.mod h1:bGK+AEy7XUwTBkqCsK/vDyFF0JJOA6X5KWpNC0e6qTA= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= diff --git a/migrations/counters_to_array.go b/migrations/counters_to_array.go new file mode 100644 index 00000000000..db7b8d01088 --- /dev/null +++ b/migrations/counters_to_array.go @@ -0,0 +1,72 @@ +package migrations + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/gateway-fm/cdk-erigon-lib/common/datadir" + "github.com/gateway-fm/cdk-erigon-lib/kv" + "github.com/ledgerwatch/erigon/core/vm" + "github.com/ledgerwatch/erigon/zk/hermez_db" +) + +var countersToArray = Migration{ + Name: "migrate counters from map to array", + Up: func(db kv.RwDB, dirs datadir.Dirs, progress []byte, BeforeCommit Callback) (err error) { + tx, err := db.BeginRw(context.Background()) + if err != nil { + return err + } + defer tx.Rollback() + // Migrate counters from map to array with cursor over BATCH_COUNTERS + c, err := tx.Cursor(kv.BATCH_COUNTERS) + if err != nil { + return err + } + defer c.Close() + tx.ForEach(kv.BATCH_COUNTERS, []byte{}, func(k, v []byte) error { + var countersMap map[string]int + if err := json.Unmarshal(v, &countersMap); err != nil { + // not a map, so pass on + return nil + } + + countersArray := make([]int, vm.CounterTypesCount) + for counterKey, counterCount := range countersMap { + keyName := string(counterKey) + keyIndex := -1 + for i, kn := range vm.CounterKeyNames { + if string(kn) == keyName { + keyIndex = i + break + } + } + + if keyIndex == -1 { + blockNo := hermez_db.BytesToUint64(k) + return fmt.Errorf("unknown counter key %s for block %d", keyName, blockNo) + } + countersArray[keyIndex] = counterCount + } + + countersArrayBytes, err := json.Marshal(countersArray) + if err != nil { + return err + } + newKey := make([]byte, len(k)) + copy(newKey, k) + if err := tx.Put(kv.BATCH_COUNTERS, newKey, countersArrayBytes); err != nil { + return err + } + + return nil + }) + + // This migration is no-op, but it forces the migration mechanism to apply it and thus write the DB schema version info + if err := BeforeCommit(tx, nil, true); err != nil { + return err + } + return tx.Commit() + }, +} diff --git a/migrations/counters_to_array_test.go b/migrations/counters_to_array_test.go new file mode 100644 index 00000000000..bb95ded551a --- /dev/null +++ b/migrations/counters_to_array_test.go @@ -0,0 +1,153 @@ +package migrations + +import ( + "context" + "encoding/json" + "testing" + + "github.com/gateway-fm/cdk-erigon-lib/kv" + "github.com/gateway-fm/cdk-erigon-lib/kv/memdb" + "github.com/ledgerwatch/erigon/core/vm" + "github.com/ledgerwatch/erigon/zk/hermez_db" + "github.com/stretchr/testify/require" +) + +type testCases struct { + testName string + mapCounters map[uint64]map[string]int + arrayCounters map[uint64][vm.CounterTypesCount]int + expectedCounters map[uint64][vm.CounterTypesCount]int +} + +func TestCountersToArray(t *testing.T) { + testCases := []testCases{ + { + testName: "only map entries", + mapCounters: map[uint64]map[string]int{ + 1: {string(vm.CounterKeyNames[vm.A]): 1, string(vm.CounterKeyNames[vm.B]): 2}, + 2: {string(vm.CounterKeyNames[vm.A]): 3, string(vm.CounterKeyNames[vm.B]): 4}, + }, + arrayCounters: map[uint64][vm.CounterTypesCount]int{}, + expectedCounters: map[uint64][vm.CounterTypesCount]int{ + 1: {0, 1, 2, 0, 0, 0, 0, 0}, + 2: {0, 3, 4, 0, 0, 0, 0, 0}, + }, + }, + { + testName: "only array entries", + mapCounters: map[uint64]map[string]int{}, + arrayCounters: map[uint64][vm.CounterTypesCount]int{ + 1: {0, 1, 2, 0, 0, 0, 0, 0}, + 2: {0, 3, 4, 0, 0, 0, 0, 0}, + }, + expectedCounters: map[uint64][vm.CounterTypesCount]int{ + 1: {0, 1, 2, 0, 0, 0, 0, 0}, + 2: {0, 3, 4, 0, 0, 0, 0, 0}, + }, + }, + { + testName: "arrays and maps entries", + mapCounters: map[uint64]map[string]int{ + 1: {string(vm.CounterKeyNames[vm.A]): 1, string(vm.CounterKeyNames[vm.B]): 2}, + 2: {string(vm.CounterKeyNames[vm.A]): 3, string(vm.CounterKeyNames[vm.B]): 4}, + }, + arrayCounters: map[uint64][vm.CounterTypesCount]int{ + 3: {2, 1, 2, 0, 0, 0, 0, 0}, + 4: {1, 3, 4, 0, 0, 0, 0, 0}, + }, + expectedCounters: map[uint64][vm.CounterTypesCount]int{ + 1: {0, 1, 2, 0, 0, 0, 0, 0}, + 2: {0, 3, 4, 0, 0, 0, 0, 0}, + 3: {2, 1, 2, 0, 0, 0, 0, 0}, + 4: {1, 3, 4, 0, 0, 0, 0, 0}, + }, + }, + } + + for _, tc := range testCases { + require, tmpDir, db := require.New(t), t.TempDir(), memdb.NewTestDB(t) + + err := prepareDbCounters(db, tc.mapCounters, tc.arrayCounters) + require.NoError(err) + + migrator := NewMigrator(kv.ChainDB) + + migrator.Migrations = []Migration{countersToArray} + err = migrator.Apply(db, tmpDir) + require.NoError(err) + + err = assertDbCounters(t, db, tc.testName, tc.expectedCounters) + require.NoError(err) + } +} + +func prepareDbCounters(db kv.RwDB, mapCounters map[uint64]map[string]int, arrayCounters map[uint64][vm.CounterTypesCount]int) error { + tx, err := db.BeginRw(context.Background()) + if err != nil { + return err + } + defer tx.Rollback() + + if err = tx.CreateBucket(kv.BATCH_COUNTERS); err != nil { + return err + } + + for l2BlockNo, countersMap := range mapCounters { + countersMapBytes, err := json.Marshal(countersMap) + if err != nil { + return err + } + + if err = tx.Put(kv.BATCH_COUNTERS, hermez_db.Uint64ToBytes(l2BlockNo), countersMapBytes); err != nil { + return err + } + } + + for l2BlockNo, countersArray := range arrayCounters { + countersArrayBytes, err := json.Marshal(countersArray) + if err != nil { + return err + } + + if err = tx.Put(kv.BATCH_COUNTERS, hermez_db.Uint64ToBytes(l2BlockNo), countersArrayBytes); err != nil { + return err + } + } + + return tx.Commit() +} + +func assertDbCounters(t *testing.T, db kv.RwDB, testName string, expectedCounters map[uint64][vm.CounterTypesCount]int) error { + tx, err := db.BeginRw(context.Background()) + if err != nil { + return err + } + defer tx.Rollback() + c, err := tx.Cursor(kv.BATCH_COUNTERS) + if err != nil { + return err + } + defer c.Close() + + actualCounters := map[uint64][vm.CounterTypesCount]int{} + for k, v, err := c.First(); k != nil; k, v, err = c.Next() { + if err != nil { + return err + } + + var counters []int + if err = json.Unmarshal(v, &counters); err != nil { + return err + } + + l2BlockNo := hermez_db.BytesToUint64(k) + blockCounters := [vm.CounterTypesCount]int{} + copy(blockCounters[:], counters) + + actualCounters[l2BlockNo] = blockCounters + } + + require.Equal(t, expectedCounters, actualCounters, testName) + + return tx.Commit() +} diff --git a/migrations/migrations.go b/migrations/migrations.go index 384db64974c..6ca6124d192 100644 --- a/migrations/migrations.go +++ b/migrations/migrations.go @@ -36,6 +36,8 @@ var migrations = map[kv.Label][]Migration{ txsBeginEnd, resetBlocks4, refactorTableLastRoot, + countersToArray, + resetL1Sequences, }, kv.TxPoolDB: {}, kv.SentryDB: {}, diff --git a/migrations/reset_l1sequences.go b/migrations/reset_l1sequences.go new file mode 100644 index 00000000000..c44ef81277c --- /dev/null +++ b/migrations/reset_l1sequences.go @@ -0,0 +1,33 @@ +package migrations + +import ( + "context" + "fmt" + + "github.com/gateway-fm/cdk-erigon-lib/common/datadir" + "github.com/gateway-fm/cdk-erigon-lib/kv" + "github.com/ledgerwatch/erigon/eth/stagedsync/stages" +) + +var resetL1Sequences = Migration{ + Name: "remove l1 sequences and stage_l1sync progress to download all l1 sequences anew", + Up: func(db kv.RwDB, dirs datadir.Dirs, progress []byte, BeforeCommit Callback) (err error) { + tx, err := db.BeginRw(context.Background()) + if err != nil { + return err + } + defer tx.Rollback() + tx.ClearBucket(kv.L1SEQUENCES) + + // already checked + if err := stages.SaveStageProgress(tx, stages.L1Syncer, 0); err != nil { + return fmt.Errorf("failed to get highest checked block, %w", err) + } + + // This migration is no-op, but it forces the migration mechanism to apply it and thus write the DB schema version info + if err := BeforeCommit(tx, nil, true); err != nil { + return err + } + return tx.Commit() + }, +} diff --git a/rpc/types.go b/rpc/types.go index b99383cc12d..2419893b319 100644 --- a/rpc/types.go +++ b/rpc/types.go @@ -318,3 +318,11 @@ func (ts *Timestamp) UnmarshalJSON(data []byte) error { return nil } + +type ForkInterval struct { + ForkId hexutil.Uint64 `json:"forkId"` + FromBatchNumber hexutil.Uint64 `json:"fromBatchNumber"` + ToBatchNumber hexutil.Uint64 `json:"toBatchNumber"` + Version string `json:"version"` + BlockNumber hexutil.Uint64 `json:"blockNumber"` +} diff --git a/smt/pkg/blockinfo/block_info.go b/smt/pkg/blockinfo/block_info.go index 612df1504b3..831d2c73d97 100644 --- a/smt/pkg/blockinfo/block_info.go +++ b/smt/pkg/blockinfo/block_info.go @@ -87,7 +87,8 @@ func BuildBlockInfoTree( keys = append(keys, key) vals = append(vals, val) - root, err := infoTree.smt.InsertBatch(context.Background(), "", keys, vals, nil, nil) + insertBatchCfg := smt.NewInsertBatchConfig(context.Background(), "block_info_tree", false) + root, err := infoTree.smt.InsertBatch(insertBatchCfg, keys, vals, nil, nil) if err != nil { return nil, err } @@ -324,11 +325,7 @@ func (b *BlockInfoTree) GenerateBlockTxKeysVals( logToEncode := "0x" + hex.EncodeToString(rLog.Data) + reducedTopics - hash, err := utils.HashContractBytecode(logToEncode) - if err != nil { - return nil, nil, err - } - + hash := utils.HashContractBytecode(logToEncode) logEncodedBig := utils.ConvertHexToBigInt(hash) key, val, err = generateTxLog(txIndexBig, big.NewInt(logIndex), logEncodedBig) if err != nil { diff --git a/smt/pkg/blockinfo/block_info_test.go b/smt/pkg/blockinfo/block_info_test.go index 42ddfaea093..61590900732 100644 --- a/smt/pkg/blockinfo/block_info_test.go +++ b/smt/pkg/blockinfo/block_info_test.go @@ -94,8 +94,8 @@ func TestBlockInfoHeader(t *testing.T) { if err != nil { t.Fatal(err) } - - root, err := infoTree.smt.InsertBatch(context.Background(), "", keys, vals, nil, nil) + insertBatchCfg := smt.NewInsertBatchConfig(context.Background(), "", false) + root, err := infoTree.smt.InsertBatch(insertBatchCfg, keys, vals, nil, nil) if err != nil { t.Fatal(err) } @@ -213,8 +213,8 @@ func TestSetBlockTx(t *testing.T) { if err != nil { t.Fatal(err) } - - root, err2 := infoTree.smt.InsertBatch(context.Background(), "", keys, vals, nil, nil) + insertBatchCfg := smt.NewInsertBatchConfig(context.Background(), "", false) + root, err2 := infoTree.smt.InsertBatch(insertBatchCfg, keys, vals, nil, nil) if err2 != nil { t.Fatal(err2) } diff --git a/smt/pkg/blockinfo/keys.go b/smt/pkg/blockinfo/keys.go index af9cfdef475..3f67fa4b58d 100644 --- a/smt/pkg/blockinfo/keys.go +++ b/smt/pkg/blockinfo/keys.go @@ -55,14 +55,8 @@ func KeyTxLogs(txIndex, logIndex *big.Int) (*utils.NodeKey, error) { return nil, err } - hk0, err := utils.Hash(lia.ToUintArray(), utils.BranchCapacity) - if err != nil { - return nil, err - } - hkRes, err := utils.Hash(key1.ToUintArray(), hk0) - if err != nil { - return nil, err - } + hk0 := utils.Hash(lia.ToUintArray(), utils.BranchCapacity) + hkRes := utils.Hash(key1.ToUintArray(), hk0) return &utils.NodeKey{hkRes[0], hkRes[1], hkRes[2], hkRes[3]}, nil } diff --git a/smt/pkg/db/mem-db.go b/smt/pkg/db/mem-db.go index a3d38be8626..949f267b402 100644 --- a/smt/pkg/db/mem-db.go +++ b/smt/pkg/db/mem-db.go @@ -253,11 +253,7 @@ func (m *MemDb) AddCode(code []byte) error { m.lock.Lock() // Lock for writing defer m.lock.Unlock() // Make sure to unlock when done - codeHash, err := utils.HashContractBytecode(hex.EncodeToString(code)) - if err != nil { - return err - } - + codeHash := utils.HashContractBytecode(hex.EncodeToString(code)) m.DbCode[codeHash] = code return nil } diff --git a/smt/pkg/smt/entity_storage.go b/smt/pkg/smt/entity_storage.go index ca2ce595e5c..b64969f6e60 100644 --- a/smt/pkg/smt/entity_storage.go +++ b/smt/pkg/smt/entity_storage.go @@ -15,39 +15,29 @@ import ( ) func (s *SMT) SetAccountState(ethAddr string, balance, nonce *big.Int) (*big.Int, error) { - keyBalance, err := utils.KeyEthAddrBalance(ethAddr) - if err != nil { - return nil, err - } - keyNonce, err := utils.KeyEthAddrNonce(ethAddr) - if err != nil { - return nil, err - } + keyBalance := utils.KeyEthAddrBalance(ethAddr) + keyNonce := utils.KeyEthAddrNonce(ethAddr) - _, err = s.InsertKA(keyBalance, balance) - if err != nil { + if _, err := s.InsertKA(keyBalance, balance); err != nil { return nil, err } ks := utils.EncodeKeySource(utils.KEY_BALANCE, utils.ConvertHexToAddress(ethAddr), common.Hash{}) - err = s.Db.InsertKeySource(keyBalance, ks) - if err != nil { + if err := s.Db.InsertKeySource(keyBalance, ks); err != nil { return nil, err } auxRes, err := s.InsertKA(keyNonce, nonce) - if err != nil { return nil, err } ks = utils.EncodeKeySource(utils.KEY_NONCE, utils.ConvertHexToAddress(ethAddr), common.Hash{}) - err = s.Db.InsertKeySource(keyNonce, ks) - if err != nil { + if err := s.Db.InsertKeySource(keyNonce, ks); err != nil { return nil, err } - return auxRes.NewRootScalar.ToBigInt(), err + return auxRes.NewRootScalar.ToBigInt(), nil } func (s *SMT) SetAccountStorage(addr libcommon.Address, acc *accounts.Account) error { @@ -62,14 +52,8 @@ func (s *SMT) SetAccountStorage(addr libcommon.Address, acc *accounts.Account) e } func (s *SMT) SetContractBytecode(ethAddr string, bytecode string) error { - keyContractCode, err := utils.KeyContractCode(ethAddr) - if err != nil { - return err - } - keyContractLength, err := utils.KeyContractLength(ethAddr) - if err != nil { - return err - } + keyContractCode := utils.KeyContractCode(ethAddr) + keyContractLength := utils.KeyContractLength(ethAddr) bi, bytecodeLength, err := convertBytecodeToBigInt(bytecode) if err != nil { @@ -205,6 +189,7 @@ func (s *SMT) SetContractStorage(ethAddr string, storage map[string]string, prog func (s *SMT) SetStorage(ctx context.Context, logPrefix string, accChanges map[libcommon.Address]*accounts.Account, codeChanges map[libcommon.Address]string, storageChanges map[libcommon.Address]map[string]string) ([]*utils.NodeKey, []*utils.NodeValue8, error) { var isDelete bool + var err error storageChangesInitialCapacity := 0 for _, storage := range storageChanges { @@ -222,14 +207,8 @@ func (s *SMT) SetStorage(ctx context.Context, logPrefix string, accChanges map[l default: } ethAddr := addr.String() - keyBalance, err := utils.KeyEthAddrBalance(ethAddr) - if err != nil { - return nil, nil, err - } - keyNonce, err := utils.KeyEthAddrNonce(ethAddr) - if err != nil { - return nil, nil, err - } + keyBalance := utils.KeyEthAddrBalance(ethAddr) + keyNonce := utils.KeyEthAddrNonce(ethAddr) balance := big.NewInt(0) nonce := big.NewInt(0) @@ -276,14 +255,8 @@ func (s *SMT) SetStorage(ctx context.Context, logPrefix string, accChanges map[l } ethAddr := addr.String() - keyContractCode, err := utils.KeyContractCode(ethAddr) - if err != nil { - return nil, nil, err - } - keyContractLength, err := utils.KeyContractLength(ethAddr) - if err != nil { - return nil, nil, err - } + keyContractCode := utils.KeyContractCode(ethAddr) + keyContractLength := utils.KeyContractLength(ethAddr) bi, bytecodeLength, err := convertBytecodeToBigInt(code) if err != nil { @@ -330,11 +303,7 @@ func (s *SMT) SetStorage(ctx context.Context, logPrefix string, accChanges map[l ethAddrBigIngArray := utils.ScalarToArrayBig(ethAddrBigInt) for k, v := range storage { - keyStoragePosition, err := utils.KeyContractStorage(ethAddrBigIngArray, k) - if err != nil { - return nil, nil, err - } - + keyStoragePosition := utils.KeyContractStorage(ethAddrBigIngArray, k) valueBigInt := convertStrintToBigInt(v) keysBatchStorage = append(keysBatchStorage, &keyStoragePosition) if valuesBatchStorage, isDelete, err = appendToValuesBatchStorageBigInt(valuesBatchStorage, valueBigInt); err != nil { @@ -354,8 +323,12 @@ func (s *SMT) SetStorage(ctx context.Context, logPrefix string, accChanges map[l } } - _, err := s.InsertBatch(ctx, logPrefix, keysBatchStorage, valuesBatchStorage, nil, nil) - return keysBatchStorage, valuesBatchStorage, err + insertBatchCfg := NewInsertBatchConfig(ctx, logPrefix, true) + if _, err = s.InsertBatch(insertBatchCfg, keysBatchStorage, valuesBatchStorage, nil, nil); err != nil { + return nil, nil, err + } + + return keysBatchStorage, valuesBatchStorage, nil } func (s *SMT) InsertKeySource(nodeKey *utils.NodeKey, key int, accountAddr *libcommon.Address, storagePosition *libcommon.Hash) error { @@ -376,10 +349,7 @@ func calcHashVal(v string) (*utils.NodeValue8, [4]uint64, error) { return nil, [4]uint64{}, err } - h, err := utils.Hash(value.ToUintArray(), utils.BranchCapacity) - if err != nil { - return nil, [4]uint64{}, err - } + h := utils.Hash(value.ToUintArray(), utils.BranchCapacity) return value, h, nil } @@ -404,12 +374,8 @@ func appendToValuesBatchStorageBigInt(valuesBatchStorage []*utils.NodeValue8, va } func convertBytecodeToBigInt(bytecode string) (*big.Int, int, error) { - hashedBytecode, err := utils.HashContractBytecode(bytecode) - if err != nil { - return nil, 0, err - } - var parsedBytecode string + hashedBytecode := utils.HashContractBytecode(bytecode) if strings.HasPrefix(bytecode, "0x") { parsedBytecode = bytecode[2:] diff --git a/smt/pkg/smt/entity_storage_test.go b/smt/pkg/smt/entity_storage_test.go index ca057a5674e..c45a12fc313 100644 --- a/smt/pkg/smt/entity_storage_test.go +++ b/smt/pkg/smt/entity_storage_test.go @@ -194,11 +194,7 @@ func Test_SetContractBytecode_HashBytecode(t *testing.T) { expected := "0x9257c9a31308a7cb046aba1a95679dd7e3ad695b6900e84a6470b401b1ea416e" - hashedBytecode, err := utils.HashContractBytecode(byteCode) - if err != nil { - t.Errorf("setContractBytecode failed: %v", err) - } - + hashedBytecode := utils.HashContractBytecode(byteCode) if hashedBytecode != expected { t.Errorf("setContractBytecode failed: expected %v, got %v", expected, hashedBytecode) } diff --git a/smt/pkg/smt/proof.go b/smt/pkg/smt/proof.go index 2e225be666b..d4774d2bbb0 100644 --- a/smt/pkg/smt/proof.go +++ b/smt/pkg/smt/proof.go @@ -127,11 +127,8 @@ func VerifyAndGetVal(stateRoot utils.NodeKey, proof []hexutility.Bytes, key util } path := key.GetPath() - curRoot := stateRoot - foundValue := false - for i := 0; i < len(proof); i++ { isFinalNode := len(proof[i]) == 65 @@ -147,12 +144,7 @@ func VerifyAndGetVal(stateRoot utils.NodeKey, proof []hexutility.Bytes, key util leftChildNode := [4]uint64{leftChild[0], leftChild[1], leftChild[2], leftChild[3]} rightChildNode := [4]uint64{rightChild[0], rightChild[1], rightChild[2], rightChild[3]} - h, err := utils.Hash(utils.ConcatArrays4(leftChildNode, rightChildNode), capacity) - - if err != nil { - return nil, err - } - + h := utils.Hash(utils.ConcatArrays4(leftChildNode, rightChildNode), capacity) if curRoot != h { return nil, fmt.Errorf("root mismatch at level %d, expected %d, got %d", i, curRoot, h) } @@ -193,12 +185,7 @@ func VerifyAndGetVal(stateRoot utils.NodeKey, proof []hexutility.Bytes, key util return nil, err } - h, err := utils.Hash(nodeValue.ToUintArray(), utils.BranchCapacity) - - if err != nil { - return nil, err - } - + h := utils.Hash(nodeValue.ToUintArray(), utils.BranchCapacity) if h != curRoot { return nil, fmt.Errorf("root mismatch at level %d, expected %d, got %d", len(proof)-1, curRoot, h) } diff --git a/smt/pkg/smt/proof_test.go b/smt/pkg/smt/proof_test.go index f6d92be48a1..35b14bbeb2e 100644 --- a/smt/pkg/smt/proof_test.go +++ b/smt/pkg/smt/proof_test.go @@ -73,12 +73,7 @@ func TestVerifyAndGetVal(t *testing.T) { root := utils.ScalarToRoot(smtRoot) t.Run("Value exists and proof is correct", func(t *testing.T) { - storageKey, err := utils.KeyContractStorage(address, libcommon.HexToHash("0x5").String()) - - if err != nil { - t.Fatalf("KeyContractStorage() error = %v", err) - } - + storageKey := utils.KeyContractStorage(address, libcommon.HexToHash("0x5").String()) storageProof := smt.FilterProofs(proofs, storageKey) val, err := smt.VerifyAndGetVal(root, storageProof, storageKey) @@ -100,19 +95,12 @@ func TestVerifyAndGetVal(t *testing.T) { // Fuzz with 1000 non-existent keys for i := 0; i < 1000; i++ { - nonExistentKey, err := utils.KeyContractStorage( + nonExistentKey := utils.KeyContractStorage( address, libcommon.HexToHash(fmt.Sprintf("0xdeadbeefabcd1234%d", i)).String(), ) - nonExistentKeys = append(nonExistentKeys, nonExistentKey) - - if err != nil { - t.Fatalf("KeyContractStorage() error = %v", err) - } - nonExistentKeyPath := nonExistentKey.GetPath() - keyBytes := make([]byte, 0, len(nonExistentKeyPath)) for _, v := range nonExistentKeyPath { @@ -143,7 +131,7 @@ func TestVerifyAndGetVal(t *testing.T) { t.Run("Value doesn't exist but non-existent proof is insufficient", func(t *testing.T) { nonExistentRl := trie.NewRetainList(0) - nonExistentKey, _ := utils.KeyContractStorage(address, libcommon.HexToHash("0x999").String()) + nonExistentKey := utils.KeyContractStorage(address, libcommon.HexToHash("0x999").String()) nonExistentKeyPath := nonExistentKey.GetPath() keyBytes := make([]byte, 0, len(nonExistentKeyPath)) @@ -176,7 +164,7 @@ func TestVerifyAndGetVal(t *testing.T) { }) t.Run("Value exists but proof is incorrect (first value corrupted)", func(t *testing.T) { - storageKey, _ := utils.KeyContractStorage(address, libcommon.HexToHash("0x5").String()) + storageKey := utils.KeyContractStorage(address, libcommon.HexToHash("0x5").String()) storageProof := smt.FilterProofs(proofs, storageKey) // Corrupt the proof by changing a byte @@ -194,7 +182,7 @@ func TestVerifyAndGetVal(t *testing.T) { }) t.Run("Value exists but proof is incorrect (last value corrupted)", func(t *testing.T) { - storageKey, _ := utils.KeyContractStorage(address, libcommon.HexToHash("0x5").String()) + storageKey := utils.KeyContractStorage(address, libcommon.HexToHash("0x5").String()) storageProof := smt.FilterProofs(proofs, storageKey) // Corrupt the proof by changing the last byte of the last proof element @@ -215,7 +203,7 @@ func TestVerifyAndGetVal(t *testing.T) { }) t.Run("Value exists but proof is insufficient", func(t *testing.T) { - storageKey, _ := utils.KeyContractStorage(address, libcommon.HexToHash("0x5").String()) + storageKey := utils.KeyContractStorage(address, libcommon.HexToHash("0x5").String()) storageProof := smt.FilterProofs(proofs, storageKey) // Modify the proof to claim the value doesn't exist diff --git a/smt/pkg/smt/smt.go b/smt/pkg/smt/smt.go index 8ce01187da6..4f35254d32d 100644 --- a/smt/pkg/smt/smt.go +++ b/smt/pkg/smt/smt.go @@ -170,11 +170,7 @@ func (s *SMT) InsertStorage(ethAddr string, storage *map[string]string, chm *map NewRootScalar: &or, } for k := range *storage { - keyStoragePosition, err := utils.KeyContractStorage(add, k) - if err != nil { - return nil, err - } - + keyStoragePosition := utils.KeyContractStorage(add, k) smtr, err = s.insert(keyStoragePosition, *(*chm)[k], (*vhm)[k], *smtr.NewRootScalar) if err != nil { return nil, err @@ -261,9 +257,6 @@ func (s *SMT) insert(k utils.NodeKey, v utils.NodeValue8, newValH [4]uint64, old foundVal = foundValA foundKey = utils.JoinKey(usedKey, foundRKey) - if err != nil { - return nil, err - } } else { oldRoot = utils.NodeKeyFromBigIntArray(siblings[level][keys[level]*4 : keys[level]*4+4]) usedKey = append(usedKey, keys[level]) @@ -537,47 +530,31 @@ func (s *SMT) insert(k utils.NodeKey, v utils.NodeValue8, newValH [4]uint64, old return smtResponse, nil } -func prepareHashValueForSave(in [8]uint64, capacity [4]uint64) utils.NodeValue12 { - var sl []uint64 - sl = append(sl, in[:]...) - sl = append(sl, capacity[:]...) - - v := utils.NodeValue12{} - for i, val := range sl { - b := new(big.Int) - v[i] = b.SetUint64(val) +// used only by old smt (not by smt batch/create) +func (s *SMT) hashSave(in [8]uint64, capacity, h [4]uint64) error { + if s.noSaveOnInsert { + return nil } + v := utils.ConcatArrays8AndCapacityByPointers(&in, &capacity) - return v + return s.Db.Insert(h, *v) } -func (s *SMT) hashSave(in [8]uint64, capacity, h [4]uint64) error { +func (s *SMT) hashSaveByPointers(in *[8]uint64, capacity, h *[4]uint64) error { if s.noSaveOnInsert { return nil } - v := prepareHashValueForSave(in, capacity) + v := utils.ConcatArrays8AndCapacityByPointers(in, capacity) - return s.Db.Insert(h, v) + return s.Db.Insert(*h, *v) } +// used only by old smt (not by smt batch/create) func (s *SMT) hashcalcAndSave(in [8]uint64, capacity [4]uint64) ([4]uint64, error) { - h, err := utils.Hash(in, capacity) - if err != nil { - return [4]uint64{}, err - } - + h := utils.Hash(in, capacity) return h, s.hashSave(in, capacity, h) } -func hashCalcAndPrepareForSave(in [8]uint64, capacity [4]uint64) ([4]uint64, utils.NodeValue12, error) { - h, err := utils.Hash(in, capacity) - if err != nil { - return [4]uint64{}, utils.NodeValue12{}, err - } - - return h, prepareHashValueForSave(in, capacity), nil -} - func (s *RoSMT) getLastRoot() (utils.NodeKey, error) { or, err := s.DbRo.GetLastRoot() if err != nil { diff --git a/smt/pkg/smt/smt_batch.go b/smt/pkg/smt/smt_batch.go index eb924c03a68..e7676665918 100644 --- a/smt/pkg/smt/smt_batch.go +++ b/smt/pkg/smt/smt_batch.go @@ -10,7 +10,21 @@ import ( "github.com/ledgerwatch/erigon/zk" ) -func (s *SMT) InsertBatch(ctx context.Context, logPrefix string, nodeKeys []*utils.NodeKey, nodeValues []*utils.NodeValue8, nodeValuesHashes []*[4]uint64, rootNodeHash *utils.NodeKey) (*SMTResponse, error) { +type InsertBatchConfig struct { + ctx context.Context + logPrefix string + shouldPrintProgress bool +} + +func NewInsertBatchConfig(ctx context.Context, logPrefix string, shouldPrintProgress bool) InsertBatchConfig { + return InsertBatchConfig{ + ctx: ctx, + logPrefix: logPrefix, + shouldPrintProgress: shouldPrintProgress, + } +} + +func (s *SMT) InsertBatch(cfg InsertBatchConfig, nodeKeys []*utils.NodeKey, nodeValues []*utils.NodeValue8, nodeValuesHashes []*[4]uint64, rootNodeHash *utils.NodeKey) (*SMTResponse, error) { s.clearUpMutex.Lock() defer s.clearUpMutex.Unlock() @@ -20,37 +34,61 @@ func (s *SMT) InsertBatch(ctx context.Context, logPrefix string, nodeKeys []*uti var smtBatchNodeRoot *smtBatchNode nodeHashesForDelete := make(map[uint64]map[uint64]map[uint64]map[uint64]*utils.NodeKey) - //start a progress checker - preprocessStage := uint64(size) / 10 - finalizeStage := preprocessStage - progressChan, stopProgressPrinter := zk.ProgressPrinterWithoutValues(fmt.Sprintf("[%s] SMT incremental progress", logPrefix), uint64(size)+preprocessStage+finalizeStage) - defer stopProgressPrinter() + var progressChanPre chan uint64 + var stopProgressPrinterPre func() + if cfg.shouldPrintProgress { + progressChanPre, stopProgressPrinterPre = zk.ProgressPrinter(fmt.Sprintf("[%s] SMT incremental progress (pre-process)", cfg.logPrefix), uint64(4), false) + } else { + progressChanPre = make(chan uint64, 100) + var once sync.Once + + stopProgressPrinterPre = func() { + once.Do(func() { close(progressChanPre) }) + } + } + defer stopProgressPrinterPre() if err = validateDataLengths(nodeKeys, nodeValues, &nodeValuesHashes); err != nil { return nil, err } + progressChanPre <- uint64(1) if err = removeDuplicateEntriesByKeys(&size, &nodeKeys, &nodeValues, &nodeValuesHashes); err != nil { return nil, err } + progressChanPre <- uint64(1) - if err = calculateNodeValueHashesIfMissing(s, nodeValues, &nodeValuesHashes); err != nil { + if err = calculateNodeValueHashesIfMissing(nodeValues, &nodeValuesHashes); err != nil { return nil, err } - - progressChan <- uint64(preprocessStage) + progressChanPre <- uint64(1) if err = calculateRootNodeHashIfNil(s, &rootNodeHash); err != nil { return nil, err } + progressChanPre <- uint64(1) + stopProgressPrinterPre() + var progressChan chan uint64 + var stopProgressPrinter func() + if cfg.shouldPrintProgress { + progressChan, stopProgressPrinter = zk.ProgressPrinter(fmt.Sprintf("[%s] SMT incremental progress (process)", cfg.logPrefix), uint64(size), false) + } else { + progressChan = make(chan uint64) + var once sync.Once + + stopProgressPrinter = func() { + once.Do(func() { close(progressChan) }) + } + } + defer stopProgressPrinter() for i := 0; i < size; i++ { select { - case <-ctx.Done(): - return nil, fmt.Errorf(fmt.Sprintf("[%s] Context done", logPrefix)) + case <-cfg.ctx.Done(): + return nil, fmt.Errorf(fmt.Sprintf("[%s] Context done", cfg.logPrefix)) + case progressChan <- uint64(1): default: } - progressChan <- preprocessStage + uint64(i) insertingNodeKey := nodeKeys[i] insertingNodeValue := nodeValues[i] @@ -143,10 +181,31 @@ func (s *SMT) InsertBatch(ctx context.Context, logPrefix string, nodeKeys []*uti maxInsertingNodePathLevel = insertingNodePathLevel } } + select { + case progressChan <- uint64(1): + default: + } + stopProgressPrinter() s.updateDepth(maxInsertingNodePathLevel) + totalDeleteOps := len(nodeHashesForDelete) + + var progressChanDel chan uint64 + var stopProgressPrinterDel func() + if cfg.shouldPrintProgress { + progressChanDel, stopProgressPrinterDel = zk.ProgressPrinter(fmt.Sprintf("[%s] SMT incremental progress (deletes)", cfg.logPrefix), uint64(totalDeleteOps), false) + } else { + progressChanDel = make(chan uint64, 100) + var once sync.Once + + stopProgressPrinterDel = func() { + once.Do(func() { close(progressChanDel) }) + } + } + defer stopProgressPrinterDel() for _, mapLevel0 := range nodeHashesForDelete { + progressChanDel <- uint64(1) for _, mapLevel1 := range mapLevel0 { for _, mapLevel2 := range mapLevel1 { for _, nodeHash := range mapLevel2 { @@ -156,29 +215,59 @@ func (s *SMT) InsertBatch(ctx context.Context, logPrefix string, nodeKeys []*uti } } } + stopProgressPrinterDel() + + totalFinalizeOps := len(nodeValues) + var progressChanFin chan uint64 + var stopProgressPrinterFin func() + if cfg.shouldPrintProgress { + progressChanFin, stopProgressPrinterFin = zk.ProgressPrinter(fmt.Sprintf("[%s] SMT incremental progress (finalize)", cfg.logPrefix), uint64(totalFinalizeOps), false) + } else { + progressChanFin = make(chan uint64, 100) + var once sync.Once + + stopProgressPrinterFin = func() { + once.Do(func() { close(progressChanFin) }) + } + } + defer stopProgressPrinterFin() for i, nodeValue := range nodeValues { + select { + case progressChanFin <- uint64(1): + default: + } if !nodeValue.IsZero() { - err = s.hashSave(nodeValue.ToUintArray(), utils.BranchCapacity, *nodeValuesHashes[i]) + err = s.hashSaveByPointers(nodeValue.ToUintArrayByPointer(), &utils.BranchCapacity, nodeValuesHashes[i]) if err != nil { return nil, err } } } + stopProgressPrinterFin() if smtBatchNodeRoot == nil { rootNodeHash = &utils.NodeKey{0, 0, 0, 0} } else { - if err := calculateAndSaveHashesDfs(s, smtBatchNodeRoot, make([]int, 256), 0); err != nil { - return nil, fmt.Errorf("calculating and saving hashes dfs: %w", err) + sdh := newSmtDfsHelper(s) + + go func() { + defer sdh.destroy() + + calculateAndSaveHashesDfs(sdh, smtBatchNodeRoot, make([]int, 256), 0) + rootNodeHash = (*utils.NodeKey)(smtBatchNodeRoot.hash) + }() + + if !s.noSaveOnInsert { + if err = sdh.startConsumersLoop(s); err != nil { + return nil, fmt.Errorf("saving smt hashes dfs: %w", err) + } } - rootNodeHash = (*utils.NodeKey)(smtBatchNodeRoot.hash) + sdh.wg.Wait() } if err := s.setLastRoot(*rootNodeHash); err != nil { return nil, err } - progressChan <- preprocessStage + uint64(size) + finalizeStage - return &SMTResponse{ Mode: "batch insert", NewRootScalar: rootNodeHash, @@ -235,7 +324,7 @@ func removeDuplicateEntriesByKeys(size *int, nodeKeys *[]*utils.NodeKey, nodeVal return nil } -func calculateNodeValueHashesIfMissing(s *SMT, nodeValues []*utils.NodeValue8, nodeValuesHashes *[]*[4]uint64) error { +func calculateNodeValueHashesIfMissing(nodeValues []*utils.NodeValue8, nodeValuesHashes *[]*[4]uint64) error { var globalError error size := len(nodeValues) cpuNum := parallel.DefaultNumGoroutines() @@ -275,11 +364,7 @@ func calculateNodeValueHashesIfMissingInInterval(nodeValues []*utils.NodeValue8, continue } - nodeValueHashObj, err := utils.Hash(nodeValues[i].ToUintArray(), utils.BranchCapacity) - if err != nil { - return err - } - + nodeValueHashObj := utils.Hash(nodeValues[i].ToUintArray(), utils.BranchCapacity) (*nodeValuesHashes)[i] = &nodeValueHashObj } @@ -380,59 +465,44 @@ func updateNodeHashesForDelete(nodeHashesForDelete map[uint64]map[uint64]map[uin } } -func calculateAndSaveHashesDfs(s *SMT, smtBatchNode *smtBatchNode, path []int, level int) error { +// no point to parallelize this function because db consumer is slower than this producer +func calculateAndSaveHashesDfs(sdh *smtDfsHelper, smtBatchNode *smtBatchNode, path []int, level int) { if smtBatchNode.isLeaf() { - hashObj, err := s.hashcalcAndSave(utils.ConcatArrays4(*smtBatchNode.nodeLeftHashOrRemainingKey, *smtBatchNode.nodeRightHashOrValueHash), utils.LeafCapacity) - if err != nil { - return fmt.Errorf("hashing leaf: %w", err) - } + hashObj, hashValue := utils.HashKeyAndValueByPointers(utils.ConcatArrays4ByPointers(smtBatchNode.nodeLeftHashOrRemainingKey.AsUint64Pointer(), smtBatchNode.nodeRightHashOrValueHash.AsUint64Pointer()), &utils.LeafCapacity) + smtBatchNode.hash = hashObj + if !sdh.s.noSaveOnInsert { + sdh.dataChan <- newSmtDfsHelperDataStruct(hashObj, hashValue) - smtBatchNode.hash = &hashObj - - nodeKey := utils.JoinKey(path[:level], *smtBatchNode.nodeLeftHashOrRemainingKey) - if err := s.Db.InsertHashKey(hashObj, *nodeKey); err != nil { - return fmt.Errorf("inserting hash key: %w", err) + nodeKey := utils.JoinKey(path[:level], *smtBatchNode.nodeLeftHashOrRemainingKey) + sdh.dataChan <- newSmtDfsHelperDataStruct(hashObj, nodeKey) } - - return nil + return } var totalHash utils.NodeValue8 if smtBatchNode.leftNode != nil { path[level] = 0 - if err := calculateAndSaveHashesDfs(s, smtBatchNode.leftNode, path, level+1); err != nil { - return err - } - if err := totalHash.SetHalfValue(*smtBatchNode.leftNode.hash, 0); err != nil { - return err - } + calculateAndSaveHashesDfs(sdh, smtBatchNode.leftNode, path, level+1) + totalHash.SetHalfValue(*smtBatchNode.leftNode.hash, 0) // no point to check for error because we used hardcoded 0 which ensures that no error will be returned } else { - if err := totalHash.SetHalfValue(*smtBatchNode.nodeLeftHashOrRemainingKey, 0); err != nil { - return err - } + totalHash.SetHalfValue(*smtBatchNode.nodeLeftHashOrRemainingKey, 0) // no point to check for error because we used hardcoded 0 which ensures that no error will be returned } if smtBatchNode.rightNode != nil { path[level] = 1 - if err := calculateAndSaveHashesDfs(s, smtBatchNode.rightNode, path, level+1); err != nil { - return err - } - if err := totalHash.SetHalfValue(*smtBatchNode.rightNode.hash, 1); err != nil { - return err - } + calculateAndSaveHashesDfs(sdh, smtBatchNode.rightNode, path, level+1) + totalHash.SetHalfValue(*smtBatchNode.rightNode.hash, 1) // no point to check for error because we used hardcoded 1 which ensures that no error will be returned } else { - if err := totalHash.SetHalfValue(*smtBatchNode.nodeRightHashOrValueHash, 1); err != nil { - return err - } + totalHash.SetHalfValue(*smtBatchNode.nodeRightHashOrValueHash, 1) // no point to check for error because we used hardcoded 1 which ensures that no error will be returned } - hashObj, err := s.hashcalcAndSave(totalHash.ToUintArray(), utils.BranchCapacity) - if err != nil { - return err + hashObj, hashValue := utils.HashKeyAndValueByPointers(totalHash.ToUintArrayByPointer(), &utils.BranchCapacity) + if !sdh.s.noSaveOnInsert { + sdh.dataChan <- newSmtDfsHelperDataStruct(hashObj, hashValue) } - smtBatchNode.hash = &hashObj - return nil + + smtBatchNode.hash = hashObj } type smtBatchNode struct { @@ -559,6 +629,65 @@ func (sbn *smtBatchNode) collapseLeafByRemovingTheSingleLeaf(insertingNodeKey [] return &sbn.parentNode } +type smtDfsHelperDataStruct struct { + key *[4]uint64 + value interface{} +} + +func newSmtDfsHelperDataStruct(key *[4]uint64, value interface{}) *smtDfsHelperDataStruct { + return &smtDfsHelperDataStruct{ + key: key, + value: value, + } +} + +type smtDfsHelper struct { + s *SMT + dataChan chan *smtDfsHelperDataStruct + wg *sync.WaitGroup + once *sync.Once +} + +func newSmtDfsHelper(s *SMT) *smtDfsHelper { + sdh := &smtDfsHelper{ + s: s, + dataChan: make(chan *smtDfsHelperDataStruct, 1<<16), + wg: &sync.WaitGroup{}, + once: &sync.Once{}, + } + + sdh.wg.Add(1) + + return sdh +} + +func (sdh *smtDfsHelper) destroy() { + sdh.once.Do(func() { + close(sdh.dataChan) + sdh.wg.Done() + }) +} + +func (sdh *smtDfsHelper) startConsumersLoop(s *SMT) error { + for { + dataStruct, ok := <-sdh.dataChan + if !ok { + return nil + } + + switch castedDataStruct := dataStruct.value.(type) { + case *utils.NodeKey: + if err := s.Db.InsertHashKey(*dataStruct.key, *castedDataStruct); err != nil { + return fmt.Errorf("calculating and saving hashes dfs: %w", err) + } + case *utils.NodeValue12: + if err := s.Db.Insert(*dataStruct.key, *castedDataStruct); err != nil { + return fmt.Errorf("calculating and saving hashes dfs: %w", err) + } + } + } +} + func setNodeKeyMapValue[T int | *utils.NodeKey](nodeKeyMap map[uint64]map[uint64]map[uint64]map[uint64]T, nodeKey *utils.NodeKey, value T) { mapLevel0, found := nodeKeyMap[nodeKey[0]] if !found { diff --git a/smt/pkg/smt/smt_batch_advance_test.go b/smt/pkg/smt/smt_batch_advance_test.go new file mode 100644 index 00000000000..59c09de2324 --- /dev/null +++ b/smt/pkg/smt/smt_batch_advance_test.go @@ -0,0 +1,194 @@ +package smt_test + +import ( + "context" + "fmt" + "math/big" + "testing" + + "github.com/ledgerwatch/erigon/smt/pkg/smt" + "github.com/ledgerwatch/erigon/smt/pkg/utils" + "gotest.tools/v3/assert" +) + +func TestBatchWitness(t *testing.T) { + keys := []utils.NodeKey{ + utils.NodeKey{17822804428864912231, 4683868963463720294, 2947512351908939790, 2330225637707749973}, + utils.NodeKey{15928606457751385034, 926210564408807848, 3634217732472610234, 18021748560357139965}, + utils.NodeKey{1623861826376204094, 570263533561698889, 4654109133431364496, 7281957057362652730}, + utils.NodeKey{13644513224119225920, 15807577943241006501, 9942496498562648573, 15190659753926523377}, + utils.NodeKey{9275812266666786730, 4204572028245381139, 3605834086260069958, 10007478335141208804}, + utils.NodeKey{8235907590678154663, 6691762687086189695, 15487167600723075149, 10984821506434298343}, + utils.NodeKey{16417603439618455829, 5362127645905990998, 10661203900902368419, 16076124886006448905}, + utils.NodeKey{11707747219427568787, 933117036015558858, 16439357349021750126, 14064521656451211675}, + utils.NodeKey{10768458483543229763, 12393104588695647110, 7306859896719697582, 4178785141502415085}, + utils.NodeKey{7512520260500009967, 3751662918911081259, 9113133324668552163, 12072005766952080289}, + utils.NodeKey{9944065905482556519, 8594459084728876791, 17786637052462706859, 15521772847998069525}, + utils.NodeKey{5036431633232956882, 16658186702978753823, 2870215478624537606, 11907126160741124846}, + utils.NodeKey{17938814940856978076, 13147879352039549979, 1303554763666506875, 14953772317105337015}, + utils.NodeKey{17398863357602626404, 4841219907503399295, 2992012704517273588, 16471435007473943078}, + utils.NodeKey{4763654225644445738, 5354841943603308259, 16476366216865814029, 10492509060169249179}, + utils.NodeKey{3554925909441560661, 16583852156861238748, 15693104712527552035, 8799937559790156794}, + utils.NodeKey{9617343367546549815, 6562355304138083186, 4016039301486039807, 10864657160754550133}, + utils.NodeKey{17933907347870222658, 16190350511466382228, 13330881818854499962, 1410294862891786839}, + utils.NodeKey{17260204906255015513, 15380909239227623493, 8567606678138088594, 4899143890802672405}, + utils.NodeKey{12539511585850227228, 3973200204826286539, 8108069613182344498, 11385621942985713904}, + utils.NodeKey{5984161349947667925, 7514232801604484380, 16331057190188025237, 2178913139230121631}, + utils.NodeKey{1993407781442332939, 1513605408256072860, 9533711780544200094, 4407755968940168245}, + utils.NodeKey{10660689026092155967, 7772873226204509526, 940412750970337957, 11934396459574454979}, + utils.NodeKey{13517500090161376813, 3430655983873553997, 5375259408796912397, 1582918923617071297}, + utils.NodeKey{1530581473737529386, 12702896566116465736, 5914767264290477911, 17646414071976395527}, + utils.NodeKey{16058468518382574435, 17573595348125839734, 14299084025723850432, 9173086175977268459}, + utils.NodeKey{3492167051156683621, 5113280701490269535, 3519293511105800335, 4519124618482063071}, + utils.NodeKey{18174025977752953446, 170880634573707059, 1420648486923115869, 7650935848186468717}, + utils.NodeKey{16208859541132551432, 6618660032536205153, 10385910322459208315, 8083618043937979883}, + utils.NodeKey{18055381843795531980, 13462709273291510955, 680380512647919587, 11342529403284590651}, + utils.NodeKey{14208409806025064162, 3405833321788641051, 10002545051615441056, 3286956713137532874}, + utils.NodeKey{5680425176740212736, 8706205589048866541, 1439054882559309464, 17935966873927915285}, + utils.NodeKey{110533614413158858, 1569162572987050699, 17606018854685897411, 14063722484766563720}, + utils.NodeKey{11233753640608616570, 12359586935502800882, 9900310098552340970, 2424696158120948624}, + utils.NodeKey{17470957289258137535, 89496548814733839, 13431046055752824170, 4863600257776330164}, + utils.NodeKey{12096080439449907754, 3586504186348650027, 16024032131582461863, 3698791599656620348}, + utils.NodeKey{12011265607191854676, 16995709771660398040, 10097323095148987140, 5271835541457063617}, + utils.NodeKey{13774341565485367328, 12574592232097177017, 13203533943886016969, 15689605306663468445}, + utils.NodeKey{17673889518692219847, 6954332541823247394, 954524149166700463, 10005323665613190430}, + utils.NodeKey{3390665384912132081, 273113266583762518, 15391923996500582086, 16937300536792272468}, + utils.NodeKey{3282365570547600329, 2269401659256178523, 12133143125482037239, 9431318293795439322}, + utils.NodeKey{10308056630015396434, 9302651503878791339, 1753436441509383136, 12655301298828119054}, + utils.NodeKey{4866095004323601391, 7715812469294898395, 13448442241363136994, 12560331541471347748}, + utils.NodeKey{9555357893875481640, 14044231432423634485, 2076021859364793876, 2098251167883986095}, + utils.NodeKey{13166561572768359955, 8774399027495495913, 17115924986198600732, 14679213838814779978}, + utils.NodeKey{1830856192880052688, 16817835989594317540, 6792141515706996611, 13263912888227522233}, + utils.NodeKey{8580776493878106180, 13275268150083925070, 1298114825004489111, 6818033484593972896}, + utils.NodeKey{2562799672200229655, 18444468184514201072, 17883941549041529369, 4070387813552736545}, + utils.NodeKey{9268691730026813326, 11545055880246569979, 1187823334319829775, 17259421874098825958}, + utils.NodeKey{9994578653598857505, 13890799434279521010, 6971431511534499255, 9998397274436059169}, + utils.NodeKey{18287575540870662480, 11943532407729972209, 15340299232888708073, 10838674117466297196}, + utils.NodeKey{14761821088000158583, 964796443048506502, 5721781221240658401, 13211032425907534953}, + utils.NodeKey{18144880475727242601, 4972225809077124674, 14334455111087919063, 8111397810232896953}, + utils.NodeKey{16933784929062172058, 9574268379822183272, 4944644580885359493, 3289128208877342006}, + utils.NodeKey{8619895206600224966, 15003370087833528133, 8252241585179054714, 9201580897217580981}, + utils.NodeKey{16332458695522739594, 7936008380823170261, 1848556403564669799, 17993420240804923523}, + utils.NodeKey{6515233280772008301, 4313177990083710387, 4012549955023285042, 12696650320500651942}, + utils.NodeKey{6070193153822371132, 14833198544694594099, 8041604520195724295, 569408677969141468}, + utils.NodeKey{18121124933744588643, 14019823252026845797, 497098216249706813, 14507670067050817524}, + utils.NodeKey{10768458483543229763, 12393104588695647110, 7306859896719697582, 4178785141502415085}, + utils.NodeKey{7512520260500009967, 3751662918911081259, 9113133324668552163, 12072005766952080289}, + utils.NodeKey{5911840277575969690, 14631288768946722660, 9289463458792995190, 11361263549285604206}, + utils.NodeKey{5112807231234019664, 3952289862952962911, 12826043220050158925, 4455878876833215993}, + utils.NodeKey{16417603439618455829, 5362127645905990998, 10661203900902368419, 16076124886006448905}, + utils.NodeKey{11707747219427568787, 933117036015558858, 16439357349021750126, 14064521656451211675}, + utils.NodeKey{16208859541132551432, 6618660032536205153, 10385910322459208315, 8083618043937979883}, + utils.NodeKey{18055381843795531980, 13462709273291510955, 680380512647919587, 11342529403284590651}, + utils.NodeKey{2562799672200229655, 18444468184514201072, 17883941549041529369, 4070387813552736545}, + utils.NodeKey{16339509425341743973, 7562720126843377837, 6087776866015284100, 13287333209707648581}, + utils.NodeKey{1830856192880052688, 16817835989594317540, 6792141515706996611, 13263912888227522233}, + } + + valuesTemp := [][8]uint64{ + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{2802548736, 3113182143, 10842021, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{552894464, 46566, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{4, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{8, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{92883624, 129402807, 3239216982, 1921492768, 41803744, 3662741242, 922499619, 611206845}, + [8]uint64{2149, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1220686685, 2241513088, 3059933278, 877008478, 3450374550, 2577819195, 3646855908, 1714882695}, + [8]uint64{433, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1807748760, 2873297298, 945201229, 411604167, 1063664423, 1763702642, 2637524917, 1284041408}, + [8]uint64{2112, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{2407450438, 2021315520, 3591671307, 1981785129, 893348094, 802675915, 3804752326, 2006944699}, + [8]uint64{2583, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1400751902, 190749285, 93436423, 2918498711, 3630577401, 3928294404, 1037307865, 2336717508}, + [8]uint64{10043, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{2040622618, 1654767043, 2359080366, 3993652948, 2990917507, 41202511, 3266270425, 2537679611}, + [8]uint64{2971, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{2958032465, 981708138, 2081777150, 750201226, 3046928486, 2765783602, 2851559840, 1406574120}, + [8]uint64{23683, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1741943335, 1540916232, 1327285029, 2450002482, 2695899944, 0, 0, 0}, + [8]uint64{3109587049, 2273239893, 220080300, 1823520391, 35937659, 0, 0, 0}, + [8]uint64{1677672755, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{337379899, 3225725520, 234013414, 1425864754, 2013026225, 0, 0, 0}, + [8]uint64{1031512883, 3743101878, 2828268606, 2468973124, 1081703471, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{256, 481884672, 2932392155, 111365737, 1511099657, 224351860, 164, 0}, + [8]uint64{632216695, 2300948800, 3904328458, 2148496278, 971473112, 0, 0, 0}, + [8]uint64{1031512883, 3743101878, 2828268606, 2468973124, 1081703471, 0, 0, 0}, + [8]uint64{4, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{2401446452, 1128446136, 4183588423, 3903755242, 16083787, 848717237, 2276372267, 2020002041}, + [8]uint64{2793696421, 3373683791, 3597304417, 3609426094, 2371386802, 1021540367, 828590482, 1599660962}, + [8]uint64{2793696421, 3373683791, 3597304417, 3609426094, 2371386802, 1021540367, 828590482, 1599660962}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{2793696421, 3373683791, 3597304417, 3609426094, 2371386802, 1021540367, 828590482, 1599660962}, + [8]uint64{2793696421, 3373683791, 3597304417, 3609426094, 2371386802, 1021540367, 828590482, 1599660962}, + [8]uint64{86400, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1321730048, 465661287, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{1480818688, 2647520856, 10842021, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{2407450438, 2021315520, 3591671307, 1981785129, 893348094, 802675915, 3804752326, 2006944699}, + [8]uint64{2583, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{2, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{4210873971, 1869123984, 4035019538, 1823911763, 1097145772, 827956438, 819220988, 1111695650}, + [8]uint64{20, 0, 0, 0, 0, 0, 0, 0}, + } + + values := make([]utils.NodeValue8, 0) + for _, vT := range valuesTemp { + values = append(values, utils.NodeValue8{ + big.NewInt(0).SetUint64(vT[0]), + big.NewInt(0).SetUint64(vT[1]), + big.NewInt(0).SetUint64(vT[2]), + big.NewInt(0).SetUint64(vT[3]), + big.NewInt(0).SetUint64(vT[4]), + big.NewInt(0).SetUint64(vT[5]), + big.NewInt(0).SetUint64(vT[6]), + big.NewInt(0).SetUint64(vT[7]), + }) + } + + smtIncremental := smt.NewSMT(nil, false) + smtBatch := smt.NewSMT(nil, false) + insertBatchCfg := smt.NewInsertBatchConfig(context.Background(), "", false) + for i, k := range keys { + smtIncremental.Insert(k, values[i]) + _, err := smtBatch.InsertBatch(insertBatchCfg, []*utils.NodeKey{&k}, []*utils.NodeValue8{&values[i]}, nil, nil) + assert.NilError(t, err) + + smtIncrementalRootHash, _ := smtIncremental.Db.GetLastRoot() + smtBatchRootHash, _ := smtBatch.Db.GetLastRoot() + assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) + } + + smtIncremental.DumpTree() + fmt.Println() + smtBatch.DumpTree() + fmt.Println() + + assertSmtDbStructure(t, smtBatch, false) +} diff --git a/smt/pkg/smt/smt_batch_asserts_test.go b/smt/pkg/smt/smt_batch_asserts_test.go new file mode 100644 index 00000000000..171c3af6e0f --- /dev/null +++ b/smt/pkg/smt/smt_batch_asserts_test.go @@ -0,0 +1,125 @@ +package smt_test + +import ( + "context" + "fmt" + "testing" + + "github.com/ledgerwatch/erigon/smt/pkg/db" + "github.com/ledgerwatch/erigon/smt/pkg/smt" + "github.com/ledgerwatch/erigon/smt/pkg/utils" + "gotest.tools/v3/assert" +) + +func assertSmtDbStructure(t *testing.T, s *smt.SMT, testMetadata bool) { + smtBatchRootHash, _ := s.Db.GetLastRoot() + + actualDb, ok := s.Db.(*db.MemDb) + if !ok { + return + } + + usedNodeHashesMap := make(map[string]*utils.NodeKey) + assertSmtTreeDbStructure(t, s, utils.ScalarToRoot(smtBatchRootHash), usedNodeHashesMap) + + // EXPLAIN THE LINE BELOW: db could have more values because values' hashes are not deleted + assert.Equal(t, true, len(actualDb.Db)-len(usedNodeHashesMap) >= 0) + for k := range usedNodeHashesMap { + _, found := actualDb.Db[k] + assert.Equal(t, true, found) + } + + totalLeaves := assertHashToKeyDbStrcture(t, s, utils.ScalarToRoot(smtBatchRootHash), testMetadata) + assert.Equal(t, totalLeaves, len(actualDb.DbHashKey)) + if testMetadata { + assert.Equal(t, totalLeaves, len(actualDb.DbKeySource)) + } + + assertTraverse(t, s) +} + +func assertSmtTreeDbStructure(t *testing.T, s *smt.SMT, nodeHash utils.NodeKey, usedNodeHashesMap map[string]*utils.NodeKey) { + if nodeHash.IsZero() { + return + } + + dbNodeValue, err := s.Db.Get(nodeHash) + assert.NilError(t, err) + + nodeHashHex := utils.ConvertBigIntToHex(utils.ArrayToScalar(nodeHash[:])) + usedNodeHashesMap[nodeHashHex] = &nodeHash + + if dbNodeValue.IsFinalNode() { + nodeValueHash := utils.NodeKeyFromBigIntArray(dbNodeValue[4:8]) + dbNodeValue, err = s.Db.Get(nodeValueHash) + assert.NilError(t, err) + + nodeHashHex := utils.ConvertBigIntToHex(utils.ArrayToScalar(nodeValueHash[:])) + usedNodeHashesMap[nodeHashHex] = &nodeValueHash + return + } + + assertSmtTreeDbStructure(t, s, utils.NodeKeyFromBigIntArray(dbNodeValue[0:4]), usedNodeHashesMap) + assertSmtTreeDbStructure(t, s, utils.NodeKeyFromBigIntArray(dbNodeValue[4:8]), usedNodeHashesMap) +} + +func assertHashToKeyDbStrcture(t *testing.T, smtBatch *smt.SMT, nodeHash utils.NodeKey, testMetadata bool) int { + if nodeHash.IsZero() { + return 0 + } + + dbNodeValue, err := smtBatch.Db.Get(nodeHash) + assert.NilError(t, err) + + if dbNodeValue.IsFinalNode() { + memDb := smtBatch.Db.(*db.MemDb) + + nodeKey, err := smtBatch.Db.GetHashKey(nodeHash) + assert.NilError(t, err) + + keyConc := utils.ArrayToScalar(nodeHash[:]) + k := utils.ConvertBigIntToHex(keyConc) + _, found := memDb.DbHashKey[k] + assert.Equal(t, found, true) + + if testMetadata { + keyConc = utils.ArrayToScalar(nodeKey[:]) + + _, found = memDb.DbKeySource[keyConc.String()] + assert.Equal(t, found, true) + } + return 1 + } + + return assertHashToKeyDbStrcture(t, smtBatch, utils.NodeKeyFromBigIntArray(dbNodeValue[0:4]), testMetadata) + assertHashToKeyDbStrcture(t, smtBatch, utils.NodeKeyFromBigIntArray(dbNodeValue[4:8]), testMetadata) +} + +func assertTraverse(t *testing.T, s *smt.SMT) { + smtBatchRootHash, _ := s.Db.GetLastRoot() + + ctx := context.Background() + action := func(prefix []byte, k utils.NodeKey, v utils.NodeValue12) (bool, error) { + if v.IsFinalNode() { + valHash := v.Get4to8() + v, err := s.Db.Get(*valHash) + if err != nil { + return false, err + } + + if v[0] == nil { + return false, fmt.Errorf("value is missing in the db") + } + + vInBytes := utils.ArrayBigToScalar(utils.BigIntArrayFromNodeValue8(v.GetNodeValue8())).Bytes() + if vInBytes == nil { + return false, fmt.Errorf("error in converting to bytes") + } + + return false, nil + } + + return true, nil + } + err := s.Traverse(ctx, smtBatchRootHash, action) + assert.NilError(t, err) +} diff --git a/smt/pkg/smt/smt_batch_compare_test.go b/smt/pkg/smt/smt_batch_compare_test.go new file mode 100644 index 00000000000..9b2da30d4c0 --- /dev/null +++ b/smt/pkg/smt/smt_batch_compare_test.go @@ -0,0 +1,114 @@ +package smt_test + +import ( + "context" + "os" + "testing" + "time" + + libcommon "github.com/gateway-fm/cdk-erigon-lib/common" + "github.com/ledgerwatch/erigon/core/types/accounts" + "github.com/ledgerwatch/erigon/smt/pkg/smt" + "github.com/ledgerwatch/erigon/smt/pkg/utils" + "gotest.tools/v3/assert" +) + +func TestCompareAllTreesInsertTimesAndFinalHashesUsingDiskDb(t *testing.T) { + incrementalDbPath := "/tmp/smt-incremental" + smtIncrementalDb, smtIncrementalTx, smtIncrementalSmtDb := initDb(t, incrementalDbPath) + + bulkDbPath := "/tmp/smt-bulk" + smtBulkDb, smtBulkTx, smtBulkSmtDb := initDb(t, bulkDbPath) + + batchDbPath := "/tmp/smt-batch" + smtBatchDb, smtBatchTx, smtBatchSmtDb := initDb(t, batchDbPath) + + smtIncremental := smt.NewSMT(smtIncrementalSmtDb, false) + smtBulk := smt.NewSMT(smtBulkSmtDb, false) + smtBatch := smt.NewSMT(smtBatchSmtDb, false) + + compareAllTreesInsertTimesAndFinalHashes(t, smtIncremental, smtBulk, smtBatch) + + smtIncrementalTx.Commit() + smtBulkTx.Commit() + smtBatchTx.Commit() + t.Cleanup(func() { + smtIncrementalDb.Close() + smtBulkDb.Close() + smtBatchDb.Close() + os.RemoveAll(incrementalDbPath) + os.RemoveAll(bulkDbPath) + os.RemoveAll(batchDbPath) + }) +} + +func TestCompareAllTreesInsertTimesAndFinalHashesUsingInMemoryDb(t *testing.T) { + smtIncremental := smt.NewSMT(nil, false) + smtBulk := smt.NewSMT(nil, false) + smtBatch := smt.NewSMT(nil, false) + + compareAllTreesInsertTimesAndFinalHashes(t, smtIncremental, smtBulk, smtBatch) +} + +func compareAllTreesInsertTimesAndFinalHashes(t *testing.T, smtIncremental, smtBulk, smtBatch *smt.SMT) { + batchInsertDataHolders, totalInserts := prepareData() + ctx := context.Background() + var incrementalError error + + accChanges := make(map[libcommon.Address]*accounts.Account) + codeChanges := make(map[libcommon.Address]string) + storageChanges := make(map[libcommon.Address]map[string]string) + + for _, batchInsertDataHolder := range batchInsertDataHolders { + accChanges[batchInsertDataHolder.AddressAccount] = &batchInsertDataHolder.acc + codeChanges[batchInsertDataHolder.AddressContract] = batchInsertDataHolder.Bytecode + storageChanges[batchInsertDataHolder.AddressContract] = batchInsertDataHolder.Storage + } + + startTime := time.Now() + for addr, acc := range accChanges { + if err := smtIncremental.SetAccountStorage(addr, acc); err != nil { + incrementalError = err + } + } + + for addr, code := range codeChanges { + if err := smtIncremental.SetContractBytecode(addr.String(), code); err != nil { + incrementalError = err + } + } + + for addr, storage := range storageChanges { + if _, err := smtIncremental.SetContractStorage(addr.String(), storage, nil); err != nil { + incrementalError = err + } + } + + assert.NilError(t, incrementalError) + t.Logf("Incremental insert %d values in %v\n", totalInserts, time.Since(startTime)) + + startTime = time.Now() + keyPointers, valuePointers, err := smtBatch.SetStorage(ctx, "", accChanges, codeChanges, storageChanges) + assert.NilError(t, err) + t.Logf("Batch insert %d values in %v\n", totalInserts, time.Since(startTime)) + + keys := []utils.NodeKey{} + for i, key := range keyPointers { + v := valuePointers[i] + if !v.IsZero() { + smtBulk.Db.InsertAccountValue(*key, *v) + keys = append(keys, *key) + } + } + startTime = time.Now() + smtBulk.GenerateFromKVBulk(ctx, "", keys) + t.Logf("Bulk insert %d values in %v\n", totalInserts, time.Since(startTime)) + + smtIncrementalRootHash, _ := smtIncremental.Db.GetLastRoot() + smtBatchRootHash, _ := smtBatch.Db.GetLastRoot() + smtBulkRootHash, _ := smtBulk.Db.GetLastRoot() + assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) + assert.Equal(t, utils.ConvertBigIntToHex(smtBulkRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) + + assertSmtDbStructure(t, smtBatch, true) +} diff --git a/smt/pkg/smt/smt_batch_delete_test.go b/smt/pkg/smt/smt_batch_delete_test.go new file mode 100644 index 00000000000..469d938e314 --- /dev/null +++ b/smt/pkg/smt/smt_batch_delete_test.go @@ -0,0 +1,76 @@ +package smt_test + +import ( + "context" + "fmt" + "math/big" + "testing" + + "github.com/ledgerwatch/erigon/smt/pkg/smt" + "github.com/ledgerwatch/erigon/smt/pkg/utils" + "gotest.tools/v3/assert" +) + +func TestBatchDelete(t *testing.T) { + keys := []utils.NodeKey{ + utils.NodeKey{10768458483543229763, 12393104588695647110, 7306859896719697582, 4178785141502415085}, + utils.NodeKey{7512520260500009967, 3751662918911081259, 9113133324668552163, 12072005766952080289}, + utils.NodeKey{4755722537892498409, 14621988746728905818, 15452350668109735064, 8819587610951133148}, + utils.NodeKey{6340777516277056037, 6264482673611175884, 1063722098746108599, 9062208133640346025}, + utils.NodeKey{6319287575763093444, 10809750365832475266, 6426706394050518186, 9463173325157812560}, + utils.NodeKey{15155415624738072211, 3736290188193138617, 8461047487943769832, 12188454615342744806}, + utils.NodeKey{15276670325385989216, 10944726794004460540, 9369946489424614125, 817372649097925902}, + utils.NodeKey{2562799672200229655, 18444468184514201072, 17883941549041529369, 407038781355273654}, + utils.NodeKey{10768458483543229763, 12393104588695647110, 7306859896719697582, 4178785141502415085}, + utils.NodeKey{7512520260500009967, 3751662918911081259, 9113133324668552163, 12072005766952080289}, + utils.NodeKey{4755722537892498409, 14621988746728905818, 15452350668109735064, 8819587610951133148}, + } + + valuesTemp := [][8]uint64{ + [8]uint64{0, 1, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 1, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 1, 0, 0, 0, 0, 0, 0}, + [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{103184848, 115613322, 0, 0, 0, 0, 0, 0}, + [8]uint64{2, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{3038602192, 2317586098, 794977000, 2442751483, 2309555181, 2028447238, 1023640522, 2687173865}, + [8]uint64{3100, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, + } + + values := make([]utils.NodeValue8, 0) + for _, vT := range valuesTemp { + values = append(values, utils.NodeValue8{ + big.NewInt(0).SetUint64(vT[0]), + big.NewInt(0).SetUint64(vT[1]), + big.NewInt(0).SetUint64(vT[2]), + big.NewInt(0).SetUint64(vT[3]), + big.NewInt(0).SetUint64(vT[4]), + big.NewInt(0).SetUint64(vT[5]), + big.NewInt(0).SetUint64(vT[6]), + big.NewInt(0).SetUint64(vT[7]), + }) + } + + smtIncremental := smt.NewSMT(nil, false) + smtBatch := smt.NewSMT(nil, false) + insertBatchCfg := smt.NewInsertBatchConfig(context.Background(), "", false) + for i, k := range keys { + smtIncremental.Insert(k, values[i]) + _, err := smtBatch.InsertBatch(insertBatchCfg, []*utils.NodeKey{&k}, []*utils.NodeValue8{&values[i]}, nil, nil) + assert.NilError(t, err) + + smtIncrementalRootHash, _ := smtIncremental.Db.GetLastRoot() + smtBatchRootHash, _ := smtBatch.Db.GetLastRoot() + assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) + } + + smtIncremental.DumpTree() + fmt.Println() + smtBatch.DumpTree() + fmt.Println() + + assertSmtDbStructure(t, smtBatch, false) +} diff --git a/smt/pkg/smt/smt_batch_insert_test.go b/smt/pkg/smt/smt_batch_insert_test.go new file mode 100644 index 00000000000..fc6b7b497ac --- /dev/null +++ b/smt/pkg/smt/smt_batch_insert_test.go @@ -0,0 +1,267 @@ +package smt_test + +import ( + "context" + "fmt" + "math/big" + "math/rand" + "testing" + "time" + + "github.com/ledgerwatch/erigon/smt/pkg/smt" + "github.com/ledgerwatch/erigon/smt/pkg/utils" + "gotest.tools/v3/assert" +) + +func TestBatchSimpleInsert(t *testing.T) { + keysRaw := []*big.Int{ + big.NewInt(8), + big.NewInt(8), + big.NewInt(1), + big.NewInt(31), + big.NewInt(31), + big.NewInt(0), + big.NewInt(8), + } + valuesRaw := []*big.Int{ + big.NewInt(17), + big.NewInt(18), + big.NewInt(19), + big.NewInt(20), + big.NewInt(0), + big.NewInt(0), + big.NewInt(0), + } + + keyPointers := []*utils.NodeKey{} + valuePointers := []*utils.NodeValue8{} + + smtIncremental := smt.NewSMT(nil, false) + smtBatch := smt.NewSMT(nil, false) + smtBatchNoSave := smt.NewSMT(nil, true) + + for i := range keysRaw { + k := utils.ScalarToNodeKey(keysRaw[i]) + vArray := utils.ScalarToArrayBig(valuesRaw[i]) + v, _ := utils.NodeValue8FromBigIntArray(vArray) + + keyPointers = append(keyPointers, &k) + valuePointers = append(valuePointers, v) + + smtIncremental.InsertKA(k, valuesRaw[i]) + } + + insertBatchCfg := smt.NewInsertBatchConfig(context.Background(), "", false) + _, err := smtBatch.InsertBatch(insertBatchCfg, keyPointers, valuePointers, nil, nil) + assert.NilError(t, err) + + _, err = smtBatchNoSave.InsertBatch(insertBatchCfg, keyPointers, valuePointers, nil, nil) + assert.NilError(t, err) + + smtIncremental.DumpTree() + fmt.Println() + smtBatch.DumpTree() + fmt.Println() + fmt.Println() + fmt.Println() + + smtIncrementalRootHash, _ := smtIncremental.Db.GetLastRoot() + smtBatchRootHash, _ := smtBatch.Db.GetLastRoot() + smtBatchNoSaveRootHash, _ := smtBatchNoSave.Db.GetLastRoot() + assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) + assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtBatchNoSaveRootHash)) + + assertSmtDbStructure(t, smtBatch, false) +} + +func TestBatchRawInsert(t *testing.T) { + keysForBatch := []*utils.NodeKey{} + valuesForBatch := []*utils.NodeValue8{} + + keysForIncremental := []utils.NodeKey{} + valuesForIncremental := []utils.NodeValue8{} + + smtIncremental := smt.NewSMT(nil, false) + smtBatch := smt.NewSMT(nil, false) + + rand.Seed(1) + size := 1 << 10 + for i := 0; i < size; i++ { + rawKey := big.NewInt(rand.Int63()) + rawValue := big.NewInt(rand.Int63()) + + k := utils.ScalarToNodeKey(rawKey) + vArray := utils.ScalarToArrayBig(rawValue) + v, _ := utils.NodeValue8FromBigIntArray(vArray) + + keysForBatch = append(keysForBatch, &k) + valuesForBatch = append(valuesForBatch, v) + + keysForIncremental = append(keysForIncremental, k) + valuesForIncremental = append(valuesForIncremental, *v) + + } + + startTime := time.Now() + for i := range keysForIncremental { + smtIncremental.Insert(keysForIncremental[i], valuesForIncremental[i]) + } + t.Logf("Incremental insert %d values in %v\n", len(keysForIncremental), time.Since(startTime)) + + startTime = time.Now() + + insertBatchCfg := smt.NewInsertBatchConfig(context.Background(), "", true) + _, err := smtBatch.InsertBatch(insertBatchCfg, keysForBatch, valuesForBatch, nil, nil) + assert.NilError(t, err) + t.Logf("Batch insert %d values in %v\n", len(keysForBatch), time.Since(startTime)) + + smtIncrementalRootHash, _ := smtIncremental.Db.GetLastRoot() + smtBatchRootHash, _ := smtBatch.Db.GetLastRoot() + assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) + + assertSmtDbStructure(t, smtBatch, false) + + // DELETE + keysForBatchDelete := []*utils.NodeKey{} + valuesForBatchDelete := []*utils.NodeValue8{} + + keysForIncrementalDelete := []utils.NodeKey{} + valuesForIncrementalDelete := []utils.NodeValue8{} + + sizeToDelete := 1 << 14 + for i := 0; i < sizeToDelete; i++ { + rawValue := big.NewInt(0) + vArray := utils.ScalarToArrayBig(rawValue) + v, _ := utils.NodeValue8FromBigIntArray(vArray) + + deleteIndex := rand.Intn(size) + + keyForBatchDelete := keysForBatch[deleteIndex] + keyForIncrementalDelete := keysForIncremental[deleteIndex] + + keysForBatchDelete = append(keysForBatchDelete, keyForBatchDelete) + valuesForBatchDelete = append(valuesForBatchDelete, v) + + keysForIncrementalDelete = append(keysForIncrementalDelete, keyForIncrementalDelete) + valuesForIncrementalDelete = append(valuesForIncrementalDelete, *v) + } + + startTime = time.Now() + for i := range keysForIncrementalDelete { + smtIncremental.Insert(keysForIncrementalDelete[i], valuesForIncrementalDelete[i]) + } + t.Logf("Incremental delete %d values in %v\n", len(keysForIncrementalDelete), time.Since(startTime)) + + startTime = time.Now() + + _, err = smtBatch.InsertBatch(insertBatchCfg, keysForBatchDelete, valuesForBatchDelete, nil, nil) + assert.NilError(t, err) + t.Logf("Batch delete %d values in %v\n", len(keysForBatchDelete), time.Since(startTime)) + + assertSmtDbStructure(t, smtBatch, false) +} + +func BenchmarkIncrementalInsert(b *testing.B) { + keys := []*big.Int{} + vals := []*big.Int{} + for i := 0; i < 1000; i++ { + rand.Seed(time.Now().UnixNano()) + keys = append(keys, big.NewInt(int64(rand.Intn(10000)))) + + rand.Seed(time.Now().UnixNano()) + vals = append(vals, big.NewInt(int64(rand.Intn(10000)))) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + smtIncremental := smt.NewSMT(nil, false) + incrementalInsert(smtIncremental, keys, vals) + } +} + +func BenchmarkBatchInsert(b *testing.B) { + keys := []*big.Int{} + vals := []*big.Int{} + for i := 0; i < 1000; i++ { + rand.Seed(time.Now().UnixNano()) + keys = append(keys, big.NewInt(int64(rand.Intn(10000)))) + + rand.Seed(time.Now().UnixNano()) + vals = append(vals, big.NewInt(int64(rand.Intn(10000)))) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + smtBatch := smt.NewSMT(nil, false) + batchInsert(smtBatch, keys, vals) + } +} + +func BenchmarkBatchInsertNoSave(b *testing.B) { + keys := []*big.Int{} + vals := []*big.Int{} + for i := 0; i < 1000; i++ { + rand.Seed(time.Now().UnixNano()) + keys = append(keys, big.NewInt(int64(rand.Intn(10000)))) + + rand.Seed(time.Now().UnixNano()) + vals = append(vals, big.NewInt(int64(rand.Intn(10000)))) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + smtBatch := smt.NewSMT(nil, true) + batchInsert(smtBatch, keys, vals) + } +} + +func TestBatchSimpleInsert2(t *testing.T) { + keys := []*big.Int{} + vals := []*big.Int{} + for i := 0; i < 1000; i++ { + rand.Seed(time.Now().UnixNano()) + keys = append(keys, big.NewInt(int64(rand.Intn(10000)))) + + rand.Seed(time.Now().UnixNano()) + vals = append(vals, big.NewInt(int64(rand.Intn(10000)))) + } + + smtIncremental := smt.NewSMT(nil, false) + incrementalInsert(smtIncremental, keys, vals) + + smtBatch := smt.NewSMT(nil, false) + batchInsert(smtBatch, keys, vals) + + smtBatchNoSave := smt.NewSMT(nil, false) + batchInsert(smtBatchNoSave, keys, vals) + + smtIncrementalRootHash, _ := smtIncremental.Db.GetLastRoot() + smtBatchRootHash, _ := smtBatch.Db.GetLastRoot() + smtBatchNoSaveRootHash, _ := smtBatchNoSave.Db.GetLastRoot() + + assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) + assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtBatchNoSaveRootHash)) +} + +func incrementalInsert(tree *smt.SMT, key, val []*big.Int) { + for i := range key { + k := utils.ScalarToNodeKey(key[i]) + tree.InsertKA(k, val[i]) + } +} + +func batchInsert(tree *smt.SMT, key, val []*big.Int) { + keyPointers := []*utils.NodeKey{} + valuePointers := []*utils.NodeValue8{} + + for i := range key { + k := utils.ScalarToNodeKey(key[i]) + vArray := utils.ScalarToArrayBig(val[i]) + v, _ := utils.NodeValue8FromBigIntArray(vArray) + + keyPointers = append(keyPointers, &k) + valuePointers = append(valuePointers, v) + } + insertBatchCfg := smt.NewInsertBatchConfig(context.Background(), "", false) + tree.InsertBatch(insertBatchCfg, keyPointers, valuePointers, nil, nil) +} diff --git a/smt/pkg/smt/smt_batch_test.go b/smt/pkg/smt/smt_batch_test.go deleted file mode 100644 index 59b4b7ad59c..00000000000 --- a/smt/pkg/smt/smt_batch_test.go +++ /dev/null @@ -1,810 +0,0 @@ -package smt_test - -import ( - "context" - "fmt" - "math/big" - "math/rand" - "os" - "testing" - "time" - - "github.com/c2h5oh/datasize" - libcommon "github.com/gateway-fm/cdk-erigon-lib/common" - "github.com/gateway-fm/cdk-erigon-lib/kv" - "github.com/gateway-fm/cdk-erigon-lib/kv/mdbx" - "github.com/holiman/uint256" - "github.com/ledgerwatch/erigon/core/types/accounts" - "github.com/ledgerwatch/erigon/migrations" - "github.com/ledgerwatch/erigon/smt/pkg/db" - "github.com/ledgerwatch/erigon/smt/pkg/smt" - "github.com/ledgerwatch/erigon/smt/pkg/utils" - "github.com/ledgerwatch/log/v3" - "golang.org/x/sync/semaphore" - "gotest.tools/v3/assert" -) - -type BatchInsertDataHolder struct { - acc accounts.Account - AddressAccount libcommon.Address - AddressContract libcommon.Address - Bytecode string - Storage map[string]string -} - -func TestBatchSimpleInsert(t *testing.T) { - keysRaw := []*big.Int{ - big.NewInt(8), - big.NewInt(8), - big.NewInt(1), - big.NewInt(31), - big.NewInt(31), - big.NewInt(0), - big.NewInt(8), - } - valuesRaw := []*big.Int{ - big.NewInt(17), - big.NewInt(18), - big.NewInt(19), - big.NewInt(20), - big.NewInt(0), - big.NewInt(0), - big.NewInt(0), - } - - keyPointers := []*utils.NodeKey{} - valuePointers := []*utils.NodeValue8{} - - smtIncremental := smt.NewSMT(nil, false) - smtBatch := smt.NewSMT(nil, false) - smtBatchNoSave := smt.NewSMT(nil, true) - - for i := range keysRaw { - k := utils.ScalarToNodeKey(keysRaw[i]) - vArray := utils.ScalarToArrayBig(valuesRaw[i]) - v, _ := utils.NodeValue8FromBigIntArray(vArray) - - keyPointers = append(keyPointers, &k) - valuePointers = append(valuePointers, v) - - smtIncremental.InsertKA(k, valuesRaw[i]) - } - - _, err := smtBatch.InsertBatch(context.Background(), "", keyPointers, valuePointers, nil, nil) - assert.NilError(t, err) - - _, err = smtBatchNoSave.InsertBatch(context.Background(), "", keyPointers, valuePointers, nil, nil) - assert.NilError(t, err) - - smtIncremental.DumpTree() - fmt.Println() - smtBatch.DumpTree() - fmt.Println() - fmt.Println() - fmt.Println() - - smtIncrementalRootHash, _ := smtIncremental.Db.GetLastRoot() - smtBatchRootHash, _ := smtBatch.Db.GetLastRoot() - smtBatchNoSaveRootHash, _ := smtBatchNoSave.Db.GetLastRoot() - assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) - assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtBatchNoSaveRootHash)) - - assertSmtDbStructure(t, smtBatch, false) -} - -func incrementalInsert(tree *smt.SMT, key, val []*big.Int) { - for i := range key { - k := utils.ScalarToNodeKey(key[i]) - tree.InsertKA(k, val[i]) - } -} - -func batchInsert(tree *smt.SMT, key, val []*big.Int) { - keyPointers := []*utils.NodeKey{} - valuePointers := []*utils.NodeValue8{} - - for i := range key { - k := utils.ScalarToNodeKey(key[i]) - vArray := utils.ScalarToArrayBig(val[i]) - v, _ := utils.NodeValue8FromBigIntArray(vArray) - - keyPointers = append(keyPointers, &k) - valuePointers = append(valuePointers, v) - } - tree.InsertBatch(context.Background(), "", keyPointers, valuePointers, nil, nil) -} - -func BenchmarkIncrementalInsert(b *testing.B) { - keys := []*big.Int{} - vals := []*big.Int{} - for i := 0; i < 1000; i++ { - rand.Seed(time.Now().UnixNano()) - keys = append(keys, big.NewInt(int64(rand.Intn(10000)))) - - rand.Seed(time.Now().UnixNano()) - vals = append(vals, big.NewInt(int64(rand.Intn(10000)))) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - smtIncremental := smt.NewSMT(nil, false) - incrementalInsert(smtIncremental, keys, vals) - } -} - -func BenchmarkBatchInsert(b *testing.B) { - keys := []*big.Int{} - vals := []*big.Int{} - for i := 0; i < 1000; i++ { - rand.Seed(time.Now().UnixNano()) - keys = append(keys, big.NewInt(int64(rand.Intn(10000)))) - - rand.Seed(time.Now().UnixNano()) - vals = append(vals, big.NewInt(int64(rand.Intn(10000)))) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - smtBatch := smt.NewSMT(nil, false) - batchInsert(smtBatch, keys, vals) - } -} - -func BenchmarkBatchInsertNoSave(b *testing.B) { - keys := []*big.Int{} - vals := []*big.Int{} - for i := 0; i < 1000; i++ { - rand.Seed(time.Now().UnixNano()) - keys = append(keys, big.NewInt(int64(rand.Intn(10000)))) - - rand.Seed(time.Now().UnixNano()) - vals = append(vals, big.NewInt(int64(rand.Intn(10000)))) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - smtBatch := smt.NewSMT(nil, true) - batchInsert(smtBatch, keys, vals) - } -} - -func TestBatchSimpleInsert2(t *testing.T) { - keys := []*big.Int{} - vals := []*big.Int{} - for i := 0; i < 1000; i++ { - rand.Seed(time.Now().UnixNano()) - keys = append(keys, big.NewInt(int64(rand.Intn(10000)))) - - rand.Seed(time.Now().UnixNano()) - vals = append(vals, big.NewInt(int64(rand.Intn(10000)))) - } - - smtIncremental := smt.NewSMT(nil, false) - incrementalInsert(smtIncremental, keys, vals) - - smtBatch := smt.NewSMT(nil, false) - batchInsert(smtBatch, keys, vals) - - smtBatchNoSave := smt.NewSMT(nil, false) - batchInsert(smtBatchNoSave, keys, vals) - - smtIncrementalRootHash, _ := smtIncremental.Db.GetLastRoot() - smtBatchRootHash, _ := smtBatch.Db.GetLastRoot() - smtBatchNoSaveRootHash, _ := smtBatchNoSave.Db.GetLastRoot() - - assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) - assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtBatchNoSaveRootHash)) -} - -func TestBatchWitness(t *testing.T) { - keys := []utils.NodeKey{ - utils.NodeKey{17822804428864912231, 4683868963463720294, 2947512351908939790, 2330225637707749973}, - utils.NodeKey{15928606457751385034, 926210564408807848, 3634217732472610234, 18021748560357139965}, - utils.NodeKey{1623861826376204094, 570263533561698889, 4654109133431364496, 7281957057362652730}, - utils.NodeKey{13644513224119225920, 15807577943241006501, 9942496498562648573, 15190659753926523377}, - utils.NodeKey{9275812266666786730, 4204572028245381139, 3605834086260069958, 10007478335141208804}, - utils.NodeKey{8235907590678154663, 6691762687086189695, 15487167600723075149, 10984821506434298343}, - utils.NodeKey{16417603439618455829, 5362127645905990998, 10661203900902368419, 16076124886006448905}, - utils.NodeKey{11707747219427568787, 933117036015558858, 16439357349021750126, 14064521656451211675}, - utils.NodeKey{10768458483543229763, 12393104588695647110, 7306859896719697582, 4178785141502415085}, - utils.NodeKey{7512520260500009967, 3751662918911081259, 9113133324668552163, 12072005766952080289}, - utils.NodeKey{9944065905482556519, 8594459084728876791, 17786637052462706859, 15521772847998069525}, - utils.NodeKey{5036431633232956882, 16658186702978753823, 2870215478624537606, 11907126160741124846}, - utils.NodeKey{17938814940856978076, 13147879352039549979, 1303554763666506875, 14953772317105337015}, - utils.NodeKey{17398863357602626404, 4841219907503399295, 2992012704517273588, 16471435007473943078}, - utils.NodeKey{4763654225644445738, 5354841943603308259, 16476366216865814029, 10492509060169249179}, - utils.NodeKey{3554925909441560661, 16583852156861238748, 15693104712527552035, 8799937559790156794}, - utils.NodeKey{9617343367546549815, 6562355304138083186, 4016039301486039807, 10864657160754550133}, - utils.NodeKey{17933907347870222658, 16190350511466382228, 13330881818854499962, 1410294862891786839}, - utils.NodeKey{17260204906255015513, 15380909239227623493, 8567606678138088594, 4899143890802672405}, - utils.NodeKey{12539511585850227228, 3973200204826286539, 8108069613182344498, 11385621942985713904}, - utils.NodeKey{5984161349947667925, 7514232801604484380, 16331057190188025237, 2178913139230121631}, - utils.NodeKey{1993407781442332939, 1513605408256072860, 9533711780544200094, 4407755968940168245}, - utils.NodeKey{10660689026092155967, 7772873226204509526, 940412750970337957, 11934396459574454979}, - utils.NodeKey{13517500090161376813, 3430655983873553997, 5375259408796912397, 1582918923617071297}, - utils.NodeKey{1530581473737529386, 12702896566116465736, 5914767264290477911, 17646414071976395527}, - utils.NodeKey{16058468518382574435, 17573595348125839734, 14299084025723850432, 9173086175977268459}, - utils.NodeKey{3492167051156683621, 5113280701490269535, 3519293511105800335, 4519124618482063071}, - utils.NodeKey{18174025977752953446, 170880634573707059, 1420648486923115869, 7650935848186468717}, - utils.NodeKey{16208859541132551432, 6618660032536205153, 10385910322459208315, 8083618043937979883}, - utils.NodeKey{18055381843795531980, 13462709273291510955, 680380512647919587, 11342529403284590651}, - utils.NodeKey{14208409806025064162, 3405833321788641051, 10002545051615441056, 3286956713137532874}, - utils.NodeKey{5680425176740212736, 8706205589048866541, 1439054882559309464, 17935966873927915285}, - utils.NodeKey{110533614413158858, 1569162572987050699, 17606018854685897411, 14063722484766563720}, - utils.NodeKey{11233753640608616570, 12359586935502800882, 9900310098552340970, 2424696158120948624}, - utils.NodeKey{17470957289258137535, 89496548814733839, 13431046055752824170, 4863600257776330164}, - utils.NodeKey{12096080439449907754, 3586504186348650027, 16024032131582461863, 3698791599656620348}, - utils.NodeKey{12011265607191854676, 16995709771660398040, 10097323095148987140, 5271835541457063617}, - utils.NodeKey{13774341565485367328, 12574592232097177017, 13203533943886016969, 15689605306663468445}, - utils.NodeKey{17673889518692219847, 6954332541823247394, 954524149166700463, 10005323665613190430}, - utils.NodeKey{3390665384912132081, 273113266583762518, 15391923996500582086, 16937300536792272468}, - utils.NodeKey{3282365570547600329, 2269401659256178523, 12133143125482037239, 9431318293795439322}, - utils.NodeKey{10308056630015396434, 9302651503878791339, 1753436441509383136, 12655301298828119054}, - utils.NodeKey{4866095004323601391, 7715812469294898395, 13448442241363136994, 12560331541471347748}, - utils.NodeKey{9555357893875481640, 14044231432423634485, 2076021859364793876, 2098251167883986095}, - utils.NodeKey{13166561572768359955, 8774399027495495913, 17115924986198600732, 14679213838814779978}, - utils.NodeKey{1830856192880052688, 16817835989594317540, 6792141515706996611, 13263912888227522233}, - utils.NodeKey{8580776493878106180, 13275268150083925070, 1298114825004489111, 6818033484593972896}, - utils.NodeKey{2562799672200229655, 18444468184514201072, 17883941549041529369, 4070387813552736545}, - utils.NodeKey{9268691730026813326, 11545055880246569979, 1187823334319829775, 17259421874098825958}, - utils.NodeKey{9994578653598857505, 13890799434279521010, 6971431511534499255, 9998397274436059169}, - utils.NodeKey{18287575540870662480, 11943532407729972209, 15340299232888708073, 10838674117466297196}, - utils.NodeKey{14761821088000158583, 964796443048506502, 5721781221240658401, 13211032425907534953}, - utils.NodeKey{18144880475727242601, 4972225809077124674, 14334455111087919063, 8111397810232896953}, - utils.NodeKey{16933784929062172058, 9574268379822183272, 4944644580885359493, 3289128208877342006}, - utils.NodeKey{8619895206600224966, 15003370087833528133, 8252241585179054714, 9201580897217580981}, - utils.NodeKey{16332458695522739594, 7936008380823170261, 1848556403564669799, 17993420240804923523}, - utils.NodeKey{6515233280772008301, 4313177990083710387, 4012549955023285042, 12696650320500651942}, - utils.NodeKey{6070193153822371132, 14833198544694594099, 8041604520195724295, 569408677969141468}, - utils.NodeKey{18121124933744588643, 14019823252026845797, 497098216249706813, 14507670067050817524}, - utils.NodeKey{10768458483543229763, 12393104588695647110, 7306859896719697582, 4178785141502415085}, - utils.NodeKey{7512520260500009967, 3751662918911081259, 9113133324668552163, 12072005766952080289}, - utils.NodeKey{5911840277575969690, 14631288768946722660, 9289463458792995190, 11361263549285604206}, - utils.NodeKey{5112807231234019664, 3952289862952962911, 12826043220050158925, 4455878876833215993}, - utils.NodeKey{16417603439618455829, 5362127645905990998, 10661203900902368419, 16076124886006448905}, - utils.NodeKey{11707747219427568787, 933117036015558858, 16439357349021750126, 14064521656451211675}, - utils.NodeKey{16208859541132551432, 6618660032536205153, 10385910322459208315, 8083618043937979883}, - utils.NodeKey{18055381843795531980, 13462709273291510955, 680380512647919587, 11342529403284590651}, - utils.NodeKey{2562799672200229655, 18444468184514201072, 17883941549041529369, 4070387813552736545}, - utils.NodeKey{16339509425341743973, 7562720126843377837, 6087776866015284100, 13287333209707648581}, - utils.NodeKey{1830856192880052688, 16817835989594317540, 6792141515706996611, 13263912888227522233}, - } - - valuesTemp := [][8]uint64{ - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{2802548736, 3113182143, 10842021, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{552894464, 46566, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{4, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{8, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{92883624, 129402807, 3239216982, 1921492768, 41803744, 3662741242, 922499619, 611206845}, - [8]uint64{2149, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1220686685, 2241513088, 3059933278, 877008478, 3450374550, 2577819195, 3646855908, 1714882695}, - [8]uint64{433, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1807748760, 2873297298, 945201229, 411604167, 1063664423, 1763702642, 2637524917, 1284041408}, - [8]uint64{2112, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{2407450438, 2021315520, 3591671307, 1981785129, 893348094, 802675915, 3804752326, 2006944699}, - [8]uint64{2583, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1400751902, 190749285, 93436423, 2918498711, 3630577401, 3928294404, 1037307865, 2336717508}, - [8]uint64{10043, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{2040622618, 1654767043, 2359080366, 3993652948, 2990917507, 41202511, 3266270425, 2537679611}, - [8]uint64{2971, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{2958032465, 981708138, 2081777150, 750201226, 3046928486, 2765783602, 2851559840, 1406574120}, - [8]uint64{23683, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1741943335, 1540916232, 1327285029, 2450002482, 2695899944, 0, 0, 0}, - [8]uint64{3109587049, 2273239893, 220080300, 1823520391, 35937659, 0, 0, 0}, - [8]uint64{1677672755, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{337379899, 3225725520, 234013414, 1425864754, 2013026225, 0, 0, 0}, - [8]uint64{1031512883, 3743101878, 2828268606, 2468973124, 1081703471, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{256, 481884672, 2932392155, 111365737, 1511099657, 224351860, 164, 0}, - [8]uint64{632216695, 2300948800, 3904328458, 2148496278, 971473112, 0, 0, 0}, - [8]uint64{1031512883, 3743101878, 2828268606, 2468973124, 1081703471, 0, 0, 0}, - [8]uint64{4, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{2401446452, 1128446136, 4183588423, 3903755242, 16083787, 848717237, 2276372267, 2020002041}, - [8]uint64{2793696421, 3373683791, 3597304417, 3609426094, 2371386802, 1021540367, 828590482, 1599660962}, - [8]uint64{2793696421, 3373683791, 3597304417, 3609426094, 2371386802, 1021540367, 828590482, 1599660962}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{2793696421, 3373683791, 3597304417, 3609426094, 2371386802, 1021540367, 828590482, 1599660962}, - [8]uint64{2793696421, 3373683791, 3597304417, 3609426094, 2371386802, 1021540367, 828590482, 1599660962}, - [8]uint64{86400, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1321730048, 465661287, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{1480818688, 2647520856, 10842021, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{2407450438, 2021315520, 3591671307, 1981785129, 893348094, 802675915, 3804752326, 2006944699}, - [8]uint64{2583, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{2, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{4210873971, 1869123984, 4035019538, 1823911763, 1097145772, 827956438, 819220988, 1111695650}, - [8]uint64{20, 0, 0, 0, 0, 0, 0, 0}, - } - - values := make([]utils.NodeValue8, 0) - for _, vT := range valuesTemp { - values = append(values, utils.NodeValue8{ - big.NewInt(0).SetUint64(vT[0]), - big.NewInt(0).SetUint64(vT[1]), - big.NewInt(0).SetUint64(vT[2]), - big.NewInt(0).SetUint64(vT[3]), - big.NewInt(0).SetUint64(vT[4]), - big.NewInt(0).SetUint64(vT[5]), - big.NewInt(0).SetUint64(vT[6]), - big.NewInt(0).SetUint64(vT[7]), - }) - } - - smtIncremental := smt.NewSMT(nil, false) - smtBatch := smt.NewSMT(nil, false) - - for i, k := range keys { - smtIncremental.Insert(k, values[i]) - _, err := smtBatch.InsertBatch(context.Background(), "", []*utils.NodeKey{&k}, []*utils.NodeValue8{&values[i]}, nil, nil) - assert.NilError(t, err) - - smtIncrementalRootHash, _ := smtIncremental.Db.GetLastRoot() - smtBatchRootHash, _ := smtBatch.Db.GetLastRoot() - assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) - } - - smtIncremental.DumpTree() - fmt.Println() - smtBatch.DumpTree() - fmt.Println() - - assertSmtDbStructure(t, smtBatch, false) -} - -func TestBatchDelete(t *testing.T) { - keys := []utils.NodeKey{ - utils.NodeKey{10768458483543229763, 12393104588695647110, 7306859896719697582, 4178785141502415085}, - utils.NodeKey{7512520260500009967, 3751662918911081259, 9113133324668552163, 12072005766952080289}, - utils.NodeKey{4755722537892498409, 14621988746728905818, 15452350668109735064, 8819587610951133148}, - utils.NodeKey{6340777516277056037, 6264482673611175884, 1063722098746108599, 9062208133640346025}, - utils.NodeKey{6319287575763093444, 10809750365832475266, 6426706394050518186, 9463173325157812560}, - utils.NodeKey{15155415624738072211, 3736290188193138617, 8461047487943769832, 12188454615342744806}, - utils.NodeKey{15276670325385989216, 10944726794004460540, 9369946489424614125, 817372649097925902}, - utils.NodeKey{2562799672200229655, 18444468184514201072, 17883941549041529369, 407038781355273654}, - utils.NodeKey{10768458483543229763, 12393104588695647110, 7306859896719697582, 4178785141502415085}, - utils.NodeKey{7512520260500009967, 3751662918911081259, 9113133324668552163, 12072005766952080289}, - utils.NodeKey{4755722537892498409, 14621988746728905818, 15452350668109735064, 8819587610951133148}, - } - - valuesTemp := [][8]uint64{ - [8]uint64{0, 1, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 1, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 1, 0, 0, 0, 0, 0, 0}, - [8]uint64{1, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{103184848, 115613322, 0, 0, 0, 0, 0, 0}, - [8]uint64{2, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{3038602192, 2317586098, 794977000, 2442751483, 2309555181, 2028447238, 1023640522, 2687173865}, - [8]uint64{3100, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - [8]uint64{0, 0, 0, 0, 0, 0, 0, 0}, - } - - values := make([]utils.NodeValue8, 0) - for _, vT := range valuesTemp { - values = append(values, utils.NodeValue8{ - big.NewInt(0).SetUint64(vT[0]), - big.NewInt(0).SetUint64(vT[1]), - big.NewInt(0).SetUint64(vT[2]), - big.NewInt(0).SetUint64(vT[3]), - big.NewInt(0).SetUint64(vT[4]), - big.NewInt(0).SetUint64(vT[5]), - big.NewInt(0).SetUint64(vT[6]), - big.NewInt(0).SetUint64(vT[7]), - }) - } - - smtIncremental := smt.NewSMT(nil, false) - smtBatch := smt.NewSMT(nil, false) - - for i, k := range keys { - smtIncremental.Insert(k, values[i]) - _, err := smtBatch.InsertBatch(context.Background(), "", []*utils.NodeKey{&k}, []*utils.NodeValue8{&values[i]}, nil, nil) - assert.NilError(t, err) - - smtIncrementalRootHash, _ := smtIncremental.Db.GetLastRoot() - smtBatchRootHash, _ := smtBatch.Db.GetLastRoot() - assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) - } - - smtIncremental.DumpTree() - fmt.Println() - smtBatch.DumpTree() - fmt.Println() - - assertSmtDbStructure(t, smtBatch, false) -} - -func TestBatchRawInsert(t *testing.T) { - keysForBatch := []*utils.NodeKey{} - valuesForBatch := []*utils.NodeValue8{} - - keysForIncremental := []utils.NodeKey{} - valuesForIncremental := []utils.NodeValue8{} - - smtIncremental := smt.NewSMT(nil, false) - smtBatch := smt.NewSMT(nil, false) - - rand.Seed(1) - size := 1 << 10 - for i := 0; i < size; i++ { - rawKey := big.NewInt(rand.Int63()) - rawValue := big.NewInt(rand.Int63()) - - k := utils.ScalarToNodeKey(rawKey) - vArray := utils.ScalarToArrayBig(rawValue) - v, _ := utils.NodeValue8FromBigIntArray(vArray) - - keysForBatch = append(keysForBatch, &k) - valuesForBatch = append(valuesForBatch, v) - - keysForIncremental = append(keysForIncremental, k) - valuesForIncremental = append(valuesForIncremental, *v) - - } - - startTime := time.Now() - for i := range keysForIncremental { - smtIncremental.Insert(keysForIncremental[i], valuesForIncremental[i]) - } - t.Logf("Incremental insert %d values in %v\n", len(keysForIncremental), time.Since(startTime)) - - startTime = time.Now() - _, err := smtBatch.InsertBatch(context.Background(), "", keysForBatch, valuesForBatch, nil, nil) - assert.NilError(t, err) - t.Logf("Batch insert %d values in %v\n", len(keysForBatch), time.Since(startTime)) - - smtIncrementalRootHash, _ := smtIncremental.Db.GetLastRoot() - smtBatchRootHash, _ := smtBatch.Db.GetLastRoot() - assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) - - assertSmtDbStructure(t, smtBatch, false) - - // DELETE - keysForBatchDelete := []*utils.NodeKey{} - valuesForBatchDelete := []*utils.NodeValue8{} - - keysForIncrementalDelete := []utils.NodeKey{} - valuesForIncrementalDelete := []utils.NodeValue8{} - - sizeToDelete := 1 << 14 - for i := 0; i < sizeToDelete; i++ { - rawValue := big.NewInt(0) - vArray := utils.ScalarToArrayBig(rawValue) - v, _ := utils.NodeValue8FromBigIntArray(vArray) - - deleteIndex := rand.Intn(size) - - keyForBatchDelete := keysForBatch[deleteIndex] - keyForIncrementalDelete := keysForIncremental[deleteIndex] - - keysForBatchDelete = append(keysForBatchDelete, keyForBatchDelete) - valuesForBatchDelete = append(valuesForBatchDelete, v) - - keysForIncrementalDelete = append(keysForIncrementalDelete, keyForIncrementalDelete) - valuesForIncrementalDelete = append(valuesForIncrementalDelete, *v) - } - - startTime = time.Now() - for i := range keysForIncrementalDelete { - smtIncremental.Insert(keysForIncrementalDelete[i], valuesForIncrementalDelete[i]) - } - t.Logf("Incremental delete %d values in %v\n", len(keysForIncrementalDelete), time.Since(startTime)) - - startTime = time.Now() - _, err = smtBatch.InsertBatch(context.Background(), "", keysForBatchDelete, valuesForBatchDelete, nil, nil) - assert.NilError(t, err) - t.Logf("Batch delete %d values in %v\n", len(keysForBatchDelete), time.Since(startTime)) - - assertSmtDbStructure(t, smtBatch, false) -} - -func TestCompareAllTreesInsertTimesAndFinalHashesUsingDiskDb(t *testing.T) { - incrementalDbPath := "/tmp/smt-incremental" - smtIncrementalDb, smtIncrementalTx, smtIncrementalSmtDb := initDb(t, incrementalDbPath) - - bulkDbPath := "/tmp/smt-bulk" - smtBulkDb, smtBulkTx, smtBulkSmtDb := initDb(t, bulkDbPath) - - batchDbPath := "/tmp/smt-batch" - smtBatchDb, smtBatchTx, smtBatchSmtDb := initDb(t, batchDbPath) - - smtIncremental := smt.NewSMT(smtIncrementalSmtDb, false) - smtBulk := smt.NewSMT(smtBulkSmtDb, false) - smtBatch := smt.NewSMT(smtBatchSmtDb, false) - - compareAllTreesInsertTimesAndFinalHashes(t, smtIncremental, smtBulk, smtBatch) - - smtIncrementalTx.Commit() - smtBulkTx.Commit() - smtBatchTx.Commit() - t.Cleanup(func() { - smtIncrementalDb.Close() - smtBulkDb.Close() - smtBatchDb.Close() - os.RemoveAll(incrementalDbPath) - os.RemoveAll(bulkDbPath) - os.RemoveAll(batchDbPath) - }) -} - -func TestCompareAllTreesInsertTimesAndFinalHashesUsingInMemoryDb(t *testing.T) { - smtIncremental := smt.NewSMT(nil, false) - smtBulk := smt.NewSMT(nil, false) - smtBatch := smt.NewSMT(nil, false) - - compareAllTreesInsertTimesAndFinalHashes(t, smtIncremental, smtBulk, smtBatch) -} - -func compareAllTreesInsertTimesAndFinalHashes(t *testing.T, smtIncremental, smtBulk, smtBatch *smt.SMT) { - batchInsertDataHolders, totalInserts := prepareData() - ctx := context.Background() - var incrementalError error - - accChanges := make(map[libcommon.Address]*accounts.Account) - codeChanges := make(map[libcommon.Address]string) - storageChanges := make(map[libcommon.Address]map[string]string) - - for _, batchInsertDataHolder := range batchInsertDataHolders { - accChanges[batchInsertDataHolder.AddressAccount] = &batchInsertDataHolder.acc - codeChanges[batchInsertDataHolder.AddressContract] = batchInsertDataHolder.Bytecode - storageChanges[batchInsertDataHolder.AddressContract] = batchInsertDataHolder.Storage - } - - startTime := time.Now() - for addr, acc := range accChanges { - if err := smtIncremental.SetAccountStorage(addr, acc); err != nil { - incrementalError = err - } - } - - for addr, code := range codeChanges { - if err := smtIncremental.SetContractBytecode(addr.String(), code); err != nil { - incrementalError = err - } - } - - for addr, storage := range storageChanges { - if _, err := smtIncremental.SetContractStorage(addr.String(), storage, nil); err != nil { - incrementalError = err - } - } - - assert.NilError(t, incrementalError) - t.Logf("Incremental insert %d values in %v\n", totalInserts, time.Since(startTime)) - - startTime = time.Now() - keyPointers, valuePointers, err := smtBatch.SetStorage(ctx, "", accChanges, codeChanges, storageChanges) - assert.NilError(t, err) - t.Logf("Batch insert %d values in %v\n", totalInserts, time.Since(startTime)) - - keys := []utils.NodeKey{} - for i, key := range keyPointers { - v := valuePointers[i] - if !v.IsZero() { - smtBulk.Db.InsertAccountValue(*key, *v) - keys = append(keys, *key) - } - } - startTime = time.Now() - smtBulk.GenerateFromKVBulk(ctx, "", keys) - t.Logf("Bulk insert %d values in %v\n", totalInserts, time.Since(startTime)) - - smtIncrementalRootHash, _ := smtIncremental.Db.GetLastRoot() - smtBatchRootHash, _ := smtBatch.Db.GetLastRoot() - smtBulkRootHash, _ := smtBulk.Db.GetLastRoot() - assert.Equal(t, utils.ConvertBigIntToHex(smtBatchRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) - assert.Equal(t, utils.ConvertBigIntToHex(smtBulkRootHash), utils.ConvertBigIntToHex(smtIncrementalRootHash)) - - assertSmtDbStructure(t, smtBatch, true) -} - -func initDb(t *testing.T, dbPath string) (kv.RwDB, kv.RwTx, *db.EriDb) { - ctx := context.Background() - - os.RemoveAll(dbPath) - - dbOpts := mdbx.NewMDBX(log.Root()).Path(dbPath).Label(kv.ChainDB).GrowthStep(16 * datasize.MB).RoTxsLimiter(semaphore.NewWeighted(128)) - database, err := dbOpts.Open() - if err != nil { - t.Fatalf("Cannot create db %e", err) - } - - migrator := migrations.NewMigrator(kv.ChainDB) - if err := migrator.VerifyVersion(database); err != nil { - t.Fatalf("Cannot verify db version %e", err) - } - // if err = migrator.Apply(database, dbPath); err != nil { - // t.Fatalf("Cannot migrate db %e", err) - // } - - // if err := database.Update(context.Background(), func(tx kv.RwTx) (err error) { - // return params.SetErigonVersion(tx, "test") - // }); err != nil { - // t.Fatalf("Cannot update db") - // } - - dbTransaction, err := database.BeginRw(ctx) - if err != nil { - t.Fatalf("Cannot craete db transaction") - } - - db.CreateEriDbBuckets(dbTransaction) - return database, dbTransaction, db.NewEriDb(dbTransaction) -} - -func prepareData() ([]*BatchInsertDataHolder, int) { - treeSize := 150 - storageSize := 96 - batchInsertDataHolders := make([]*BatchInsertDataHolder, 0) - rand.Seed(1) - for i := 0; i < treeSize; i++ { - storage := make(map[string]string) - addressAccountBytes := make([]byte, 20) - addressContractBytes := make([]byte, 20) - storageKeyBytes := make([]byte, 20) - storageValueBytes := make([]byte, 20) - rand.Read(addressAccountBytes) - rand.Read(addressContractBytes) - - for j := 0; j < storageSize; j++ { - rand.Read(storageKeyBytes) - rand.Read(storageValueBytes) - storage[libcommon.BytesToAddress(storageKeyBytes).Hex()] = libcommon.BytesToAddress(storageValueBytes).Hex() - } - - acc := accounts.NewAccount() - acc.Balance = *uint256.NewInt(rand.Uint64()) - acc.Nonce = rand.Uint64() - - batchInsertDataHolders = append(batchInsertDataHolders, &BatchInsertDataHolder{ - acc: acc, - AddressAccount: libcommon.BytesToAddress(addressAccountBytes), - AddressContract: libcommon.BytesToAddress(addressContractBytes), - Bytecode: "0x60806040526004361061007b5760003560e01c80639623609d1161004e5780639623609d1461012b57806399a88ec41461013e578063f2fde38b1461015e578063f3b7dead1461017e57600080fd5b8063204e1c7a14610080578063715018a6146100c95780637eff275e146100e05780638da5cb5b14610100575b600080fd5b34801561008c57600080fd5b506100a061009b366004610608565b61019e565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100d557600080fd5b506100de610255565b005b3480156100ec57600080fd5b506100de6100fb36600461062c565b610269565b34801561010c57600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166100a0565b6100de610139366004610694565b6102f7565b34801561014a57600080fd5b506100de61015936600461062c565b61038c565b34801561016a57600080fd5b506100de610179366004610608565b6103e8565b34801561018a57600080fd5b506100a0610199366004610608565b6104a4565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516101ea907f5c60da1b00000000000000000000000000000000000000000000000000000000815260040190565b600060405180830381855afa9150503d8060008114610225576040519150601f19603f3d011682016040523d82523d6000602084013e61022a565b606091505b50915091508161023957600080fd5b8080602001905181019061024d9190610788565b949350505050565b61025d6104f0565b6102676000610571565b565b6102716104f0565b6040517f8f28397000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690638f283970906024015b600060405180830381600087803b1580156102db57600080fd5b505af11580156102ef573d6000803e3d6000fd5b505050505050565b6102ff6104f0565b6040517f4f1ef28600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841690634f1ef28690349061035590869086906004016107a5565b6000604051808303818588803b15801561036e57600080fd5b505af1158015610382573d6000803e3d6000fd5b5050505050505050565b6103946104f0565b6040517f3659cfe600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690633659cfe6906024016102c1565b6103f06104f0565b73ffffffffffffffffffffffffffffffffffffffff8116610498576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6104a181610571565b50565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516101ea907ff851a44000000000000000000000000000000000000000000000000000000000815260040190565b60005473ffffffffffffffffffffffffffffffffffffffff163314610267576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161048f565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b73ffffffffffffffffffffffffffffffffffffffff811681146104a157600080fd5b60006020828403121561061a57600080fd5b8135610625816105e6565b9392505050565b6000806040838503121561063f57600080fd5b823561064a816105e6565b9150602083013561065a816105e6565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806000606084860312156106a957600080fd5b83356106b4816105e6565b925060208401356106c4816105e6565b9150604084013567ffffffffffffffff808211156106e157600080fd5b818601915086601f8301126106f557600080fd5b81358181111561070757610707610665565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561074d5761074d610665565b8160405282815289602084870101111561076657600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60006020828403121561079a57600080fd5b8151610625816105e6565b73ffffffffffffffffffffffffffffffffffffffff8316815260006020604081840152835180604085015260005b818110156107ef578581018301518582016060015282016107d3565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010192505050939250505056fea2646970667358221220372a0e10eebea1b7fa43ae4c976994e6ed01d85eedc3637b83f01d3f06be442064736f6c63430008110033", - Storage: storage, - }) - } - - return batchInsertDataHolders, treeSize*4 + treeSize*storageSize -} - -func assertSmtDbStructure(t *testing.T, s *smt.SMT, testMetadata bool) { - smtBatchRootHash, _ := s.Db.GetLastRoot() - - actualDb, ok := s.Db.(*db.MemDb) - if !ok { - return - } - - usedNodeHashesMap := make(map[string]*utils.NodeKey) - assertSmtTreeDbStructure(t, s, utils.ScalarToRoot(smtBatchRootHash), usedNodeHashesMap) - - // EXPLAIN THE LINE BELOW: db could have more values because values' hashes are not deleted - assert.Equal(t, true, len(actualDb.Db)-len(usedNodeHashesMap) >= 0) - for k := range usedNodeHashesMap { - _, found := actualDb.Db[k] - assert.Equal(t, true, found) - } - - totalLeaves := assertHashToKeyDbStrcture(t, s, utils.ScalarToRoot(smtBatchRootHash), testMetadata) - assert.Equal(t, totalLeaves, len(actualDb.DbHashKey)) - if testMetadata { - assert.Equal(t, totalLeaves, len(actualDb.DbKeySource)) - } - - assertTraverse(t, s) -} - -func assertSmtTreeDbStructure(t *testing.T, s *smt.SMT, nodeHash utils.NodeKey, usedNodeHashesMap map[string]*utils.NodeKey) { - if nodeHash.IsZero() { - return - } - - dbNodeValue, err := s.Db.Get(nodeHash) - assert.NilError(t, err) - - nodeHashHex := utils.ConvertBigIntToHex(utils.ArrayToScalar(nodeHash[:])) - usedNodeHashesMap[nodeHashHex] = &nodeHash - - if dbNodeValue.IsFinalNode() { - nodeValueHash := utils.NodeKeyFromBigIntArray(dbNodeValue[4:8]) - dbNodeValue, err = s.Db.Get(nodeValueHash) - assert.NilError(t, err) - - nodeHashHex := utils.ConvertBigIntToHex(utils.ArrayToScalar(nodeValueHash[:])) - usedNodeHashesMap[nodeHashHex] = &nodeValueHash - return - } - - assertSmtTreeDbStructure(t, s, utils.NodeKeyFromBigIntArray(dbNodeValue[0:4]), usedNodeHashesMap) - assertSmtTreeDbStructure(t, s, utils.NodeKeyFromBigIntArray(dbNodeValue[4:8]), usedNodeHashesMap) -} - -func assertHashToKeyDbStrcture(t *testing.T, smtBatch *smt.SMT, nodeHash utils.NodeKey, testMetadata bool) int { - if nodeHash.IsZero() { - return 0 - } - - dbNodeValue, err := smtBatch.Db.Get(nodeHash) - assert.NilError(t, err) - - if dbNodeValue.IsFinalNode() { - memDb := smtBatch.Db.(*db.MemDb) - - nodeKey, err := smtBatch.Db.GetHashKey(nodeHash) - assert.NilError(t, err) - - keyConc := utils.ArrayToScalar(nodeHash[:]) - k := utils.ConvertBigIntToHex(keyConc) - _, found := memDb.DbHashKey[k] - assert.Equal(t, found, true) - - if testMetadata { - keyConc = utils.ArrayToScalar(nodeKey[:]) - - _, found = memDb.DbKeySource[keyConc.String()] - assert.Equal(t, found, true) - } - return 1 - } - - return assertHashToKeyDbStrcture(t, smtBatch, utils.NodeKeyFromBigIntArray(dbNodeValue[0:4]), testMetadata) + assertHashToKeyDbStrcture(t, smtBatch, utils.NodeKeyFromBigIntArray(dbNodeValue[4:8]), testMetadata) -} - -func assertTraverse(t *testing.T, s *smt.SMT) { - smtBatchRootHash, _ := s.Db.GetLastRoot() - - ctx := context.Background() - action := func(prefix []byte, k utils.NodeKey, v utils.NodeValue12) (bool, error) { - if v.IsFinalNode() { - valHash := v.Get4to8() - v, err := s.Db.Get(*valHash) - if err != nil { - return false, err - } - - if v[0] == nil { - return false, fmt.Errorf("value is missing in the db") - } - - vInBytes := utils.ArrayBigToScalar(utils.BigIntArrayFromNodeValue8(v.GetNodeValue8())).Bytes() - if vInBytes == nil { - return false, fmt.Errorf("error in converting to bytes") - } - - return false, nil - } - - return true, nil - } - err := s.Traverse(ctx, smtBatchRootHash, action) - assert.NilError(t, err) -} diff --git a/smt/pkg/smt/smt_batch_types_test.go b/smt/pkg/smt/smt_batch_types_test.go new file mode 100644 index 00000000000..0a36b634a05 --- /dev/null +++ b/smt/pkg/smt/smt_batch_types_test.go @@ -0,0 +1,14 @@ +package smt_test + +import ( + libcommon "github.com/gateway-fm/cdk-erigon-lib/common" + "github.com/ledgerwatch/erigon/core/types/accounts" +) + +type BatchInsertDataHolder struct { + acc accounts.Account + AddressAccount libcommon.Address + AddressContract libcommon.Address + Bytecode string + Storage map[string]string +} diff --git a/smt/pkg/smt/smt_batch_utils_test.go b/smt/pkg/smt/smt_batch_utils_test.go new file mode 100644 index 00000000000..7b7e47fc391 --- /dev/null +++ b/smt/pkg/smt/smt_batch_utils_test.go @@ -0,0 +1,89 @@ +package smt_test + +import ( + "context" + "math/rand" + "os" + "testing" + + "github.com/c2h5oh/datasize" + libcommon "github.com/gateway-fm/cdk-erigon-lib/common" + "github.com/gateway-fm/cdk-erigon-lib/kv" + "github.com/gateway-fm/cdk-erigon-lib/kv/mdbx" + "github.com/holiman/uint256" + "github.com/ledgerwatch/erigon/core/types/accounts" + "github.com/ledgerwatch/erigon/migrations" + "github.com/ledgerwatch/erigon/smt/pkg/db" + "github.com/ledgerwatch/log/v3" + "golang.org/x/sync/semaphore" +) + +func initDb(t *testing.T, dbPath string) (kv.RwDB, kv.RwTx, *db.EriDb) { + ctx := context.Background() + + os.RemoveAll(dbPath) + + dbOpts := mdbx.NewMDBX(log.Root()).Path(dbPath).Label(kv.ChainDB).GrowthStep(16 * datasize.MB).RoTxsLimiter(semaphore.NewWeighted(128)) + database, err := dbOpts.Open() + if err != nil { + t.Fatalf("Cannot create db %e", err) + } + + migrator := migrations.NewMigrator(kv.ChainDB) + if err := migrator.VerifyVersion(database); err != nil { + t.Fatalf("Cannot verify db version %e", err) + } + // if err = migrator.Apply(database, dbPath); err != nil { + // t.Fatalf("Cannot migrate db %e", err) + // } + + // if err := database.Update(context.Background(), func(tx kv.RwTx) (err error) { + // return params.SetErigonVersion(tx, "test") + // }); err != nil { + // t.Fatalf("Cannot update db") + // } + + dbTransaction, err := database.BeginRw(ctx) + if err != nil { + t.Fatalf("Cannot craete db transaction") + } + + db.CreateEriDbBuckets(dbTransaction) + return database, dbTransaction, db.NewEriDb(dbTransaction) +} + +func prepareData() ([]*BatchInsertDataHolder, int) { + treeSize := 150 + storageSize := 96 + batchInsertDataHolders := make([]*BatchInsertDataHolder, 0) + rand.Seed(1) + for i := 0; i < treeSize; i++ { + storage := make(map[string]string) + addressAccountBytes := make([]byte, 20) + addressContractBytes := make([]byte, 20) + storageKeyBytes := make([]byte, 20) + storageValueBytes := make([]byte, 20) + rand.Read(addressAccountBytes) + rand.Read(addressContractBytes) + + for j := 0; j < storageSize; j++ { + rand.Read(storageKeyBytes) + rand.Read(storageValueBytes) + storage[libcommon.BytesToAddress(storageKeyBytes).Hex()] = libcommon.BytesToAddress(storageValueBytes).Hex() + } + + acc := accounts.NewAccount() + acc.Balance = *uint256.NewInt(rand.Uint64()) + acc.Nonce = rand.Uint64() + + batchInsertDataHolders = append(batchInsertDataHolders, &BatchInsertDataHolder{ + acc: acc, + AddressAccount: libcommon.BytesToAddress(addressAccountBytes), + AddressContract: libcommon.BytesToAddress(addressContractBytes), + Bytecode: "0x60806040526004361061007b5760003560e01c80639623609d1161004e5780639623609d1461012b57806399a88ec41461013e578063f2fde38b1461015e578063f3b7dead1461017e57600080fd5b8063204e1c7a14610080578063715018a6146100c95780637eff275e146100e05780638da5cb5b14610100575b600080fd5b34801561008c57600080fd5b506100a061009b366004610608565b61019e565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100d557600080fd5b506100de610255565b005b3480156100ec57600080fd5b506100de6100fb36600461062c565b610269565b34801561010c57600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166100a0565b6100de610139366004610694565b6102f7565b34801561014a57600080fd5b506100de61015936600461062c565b61038c565b34801561016a57600080fd5b506100de610179366004610608565b6103e8565b34801561018a57600080fd5b506100a0610199366004610608565b6104a4565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516101ea907f5c60da1b00000000000000000000000000000000000000000000000000000000815260040190565b600060405180830381855afa9150503d8060008114610225576040519150601f19603f3d011682016040523d82523d6000602084013e61022a565b606091505b50915091508161023957600080fd5b8080602001905181019061024d9190610788565b949350505050565b61025d6104f0565b6102676000610571565b565b6102716104f0565b6040517f8f28397000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690638f283970906024015b600060405180830381600087803b1580156102db57600080fd5b505af11580156102ef573d6000803e3d6000fd5b505050505050565b6102ff6104f0565b6040517f4f1ef28600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841690634f1ef28690349061035590869086906004016107a5565b6000604051808303818588803b15801561036e57600080fd5b505af1158015610382573d6000803e3d6000fd5b5050505050505050565b6103946104f0565b6040517f3659cfe600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690633659cfe6906024016102c1565b6103f06104f0565b73ffffffffffffffffffffffffffffffffffffffff8116610498576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6104a181610571565b50565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516101ea907ff851a44000000000000000000000000000000000000000000000000000000000815260040190565b60005473ffffffffffffffffffffffffffffffffffffffff163314610267576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161048f565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b73ffffffffffffffffffffffffffffffffffffffff811681146104a157600080fd5b60006020828403121561061a57600080fd5b8135610625816105e6565b9392505050565b6000806040838503121561063f57600080fd5b823561064a816105e6565b9150602083013561065a816105e6565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806000606084860312156106a957600080fd5b83356106b4816105e6565b925060208401356106c4816105e6565b9150604084013567ffffffffffffffff808211156106e157600080fd5b818601915086601f8301126106f557600080fd5b81358181111561070757610707610665565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561074d5761074d610665565b8160405282815289602084870101111561076657600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60006020828403121561079a57600080fd5b8151610625816105e6565b73ffffffffffffffffffffffffffffffffffffffff8316815260006020604081840152835180604085015260005b818110156107ef578581018301518582016060015282016107d3565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010192505050939250505056fea2646970667358221220372a0e10eebea1b7fa43ae4c976994e6ed01d85eedc3637b83f01d3f06be442064736f6c63430008110033", + Storage: storage, + }) + } + + return batchInsertDataHolders, treeSize*4 + treeSize*storageSize +} diff --git a/smt/pkg/smt/smt_create.go b/smt/pkg/smt/smt_create.go index f204a9df29d..325aca5e998 100644 --- a/smt/pkg/smt/smt_create.go +++ b/smt/pkg/smt/smt_create.go @@ -35,10 +35,7 @@ func (s *SMT) GenerateFromKVBulk(ctx context.Context, logPrefix string, nodeKeys defer s.clearUpMutex.Unlock() log.Info(fmt.Sprintf("[%s] Building temp binary tree started", logPrefix)) - - totalKeysCount := len(nodeKeys) - - log.Info(fmt.Sprintf("[%s] Total values to insert: %d", logPrefix, totalKeysCount)) + log.Info(fmt.Sprintf("[%s] Total values to insert: %d", logPrefix, len(nodeKeys))) log.Info(fmt.Sprintf("[%s] Sorting keys...", logPrefix)) sortStartTime := time.Now() @@ -49,6 +46,86 @@ func (s *SMT) GenerateFromKVBulk(ctx context.Context, logPrefix string, nodeKeys sortTotalTime := time.Since(sortStartTime) log.Info(fmt.Sprintf("[%s] Keys sorted in %v", logPrefix, sortTotalTime)) + wg := sync.WaitGroup{} + wg.Add(2) + + deletesWorker := utils.NewWorker(ctx, "smt_save_finished", 1<<16) + + // start a worker to delete finished parts of the tree and return values to save to the db + go func() { + defer wg.Done() + deletesWorker.DoWork() + }() + + var buildSmtLoopErr error + var rootNode *SmtNode + tempTreeBuildStart := time.Now() + leafValueMap := sync.Map{} + accountValuesReadChan := make(chan *utils.NodeValue8, 1024) + go func() { + defer wg.Done() + defer deletesWorker.Stop() + rootNode, buildSmtLoopErr = runBuildSmtLoop(s, logPrefix, nodeKeys, &leafValueMap, deletesWorker, accountValuesReadChan) + }() + + // startBuildSmtLoopDbCompanionLoop is blocking operation. It continue only when the last result is saved + if err := startBuildSmtLoopDbCompanionLoop(s, nodeKeys, deletesWorker.GetJobResultsChannel(), accountValuesReadChan); err != nil { + return [4]uint64{}, err + } + + // by the time the code goes to here the runBuildSmtLoop as finished and everything has been stored in the db + // => the .Wait here ensure that current thread will see the memory written by deleteWorkers' thread and runBuildSmtLoop's thread + wg.Wait() + + if buildSmtLoopErr != nil { + return [4]uint64{}, buildSmtLoopErr + } + + tempTreeBuildTime := time.Since(tempTreeBuildStart) + log.Info(fmt.Sprintf("[%s] Finished the temp tree build in %v, hashing and saving the result...", logPrefix, tempTreeBuildTime)) + + //special case where no values were inserted + if rootNode.isLeaf() { + return [4]uint64{}, nil + } + + //if the root node has only one branch, that branch should become the root node + var pathToDeleteFrom []int + if len(nodeKeys) == 1 { + if rootNode.node1 == nil { + rootNode = rootNode.node0 + pathToDeleteFrom = append(pathToDeleteFrom, 0) + } else if rootNode.node0 == nil && utils.IsArrayUint64Empty(rootNode.leftHash[:]) { + rootNode = rootNode.node1 + pathToDeleteFrom = append(pathToDeleteFrom, 1) + } + } + + //if the branch is a leaf, the rkey is the whole key + if rootNode.isLeaf() { + newRkey := []int{pathToDeleteFrom[0]} + pathToDeleteFrom = []int{} + newRkey = append(newRkey, rootNode.rKey...) + rootNode.rKey = newRkey + } + + finalRoot, err := rootNode.deleteTree(pathToDeleteFrom, s, &leafValueMap) + if err != nil { + return [4]uint64{}, err + } + + if err := s.setLastRoot(finalRoot); err != nil { + return [4]uint64{}, err + } + + return finalRoot, nil +} + +func runBuildSmtLoop(s *SMT, logPrefix string, nodeKeys []utils.NodeKey, leafValueMap *sync.Map, deletesWorker *utils.Worker, accountValuesReadChan <-chan *utils.NodeValue8) (*SmtNode, error) { + totalKeysCount := len(nodeKeys) + insertedKeysCount := uint64(0) + maxReachedLevel := 0 + rootNode := SmtNode{ leftHash: [4]uint64{}, node0: nil, @@ -60,32 +137,15 @@ func (s *SMT) GenerateFromKVBulk(ctx context.Context, logPrefix string, nodeKeys defer stopProgressPrinter() progressChan <- uint64(totalKeysCount) - insertedKeysCount := uint64(0) - - maxReachedLevel := 0 - - deletesWorker := utils.NewWorker(ctx, "smt_save_finished", 1000) - - // start a worker to delete finished parts of the tree and return values to save to the db - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - deletesWorker.DoWork() - wg.Done() - }() - - tempTreeBuildStart := time.Now() - leafValueMap := sync.Map{} - - var err error for _, k := range nodeKeys { // split the key keys := k.GetPath() - v, err := s.Db.GetAccountValue(k) - if err != nil { - return [4]uint64{}, err + vPointer := <-accountValuesReadChan + if vPointer == nil { + return nil, fmt.Errorf("the actual error is returned by main DB thread") } - leafValueMap.Store(k, v) + v := *vPointer + leafValueMap.Store(k, &v) // find last node siblings, level := rootNode.findLastNode(keys) @@ -136,7 +196,7 @@ func (s *SMT) GenerateFromKVBulk(ctx context.Context, logPrefix string, nodeKeys //sanity check - new leaf should be on the right side //otherwise something went wrong if leaf0.rKey[level2] != 0 || keys[level2+level] != 1 { - return [4]uint64{}, fmt.Errorf( + return nil, fmt.Errorf( "leaf insert error. new leaf should be on the right of the old, oldLeaf: %v, newLeaf: %v", append(keys[:level+1], leaf0.rKey[level2:]...), keys, @@ -159,7 +219,7 @@ func (s *SMT) GenerateFromKVBulk(ctx context.Context, logPrefix string, nodeKeys jobResult := utils.NewCalcAndPrepareJobResult(s.Db) //hash, save and delete left leaf deleteFunc := func() utils.JobResult { - leftHash, err := nodeToDelFrom.node0.deleteTreeNoSave(pathToDeleteFrom, &leafValueMap, jobResult.KvMap, jobResult.LeafsKvMap) + leftHash, err := nodeToDelFrom.node0.deleteTreeNoSave(pathToDeleteFrom, leafValueMap, jobResult.KvMap, jobResult.LeafsKvMap) if err != nil { jobResult.Err = err return jobResult @@ -198,7 +258,7 @@ func (s *SMT) GenerateFromKVBulk(ctx context.Context, logPrefix string, nodeKeys // this is case for 1 leaf inserted to the left of the root node if len(siblings) == 0 && keys[0] == 0 { if upperNode.node0 != nil { - return [4]uint64{}, fmt.Errorf("tried to override left node") + return nil, fmt.Errorf("tried to override left node") } upperNode.node0 = newNode } else { @@ -207,7 +267,7 @@ func (s *SMT) GenerateFromKVBulk(ctx context.Context, logPrefix string, nodeKeys //the new leaf should be on the right side //otherwise something went wrong if upperNode.node1 != nil || keys[level] != 1 { - return [4]uint64{}, fmt.Errorf( + return nil, fmt.Errorf( "leaf insert error. new should be on the right of the found node, foundNode: %v, newLeafKey: %v", upperNode.node1, keys, @@ -228,7 +288,7 @@ func (s *SMT) GenerateFromKVBulk(ctx context.Context, logPrefix string, nodeKeys // get all leaf keys so we can then get all needed values and pass them // this is needed because w can't read from the db in another routine deleteFunc := func() utils.JobResult { - leftHash, err := nodeToDelFrom.node0.deleteTreeNoSave(pathToDeleteFrom, &leafValueMap, jobResult.KvMap, jobResult.LeafsKvMap) + leftHash, err := nodeToDelFrom.node0.deleteTreeNoSave(pathToDeleteFrom, leafValueMap, jobResult.KvMap, jobResult.LeafsKvMap) if err != nil { jobResult.Err = err @@ -248,79 +308,47 @@ func (s *SMT) GenerateFromKVBulk(ctx context.Context, logPrefix string, nodeKeys } } - if err := runSaveLoop(deletesWorker.GetJobResultsChannel()); err != nil { - return [4]uint64{}, err - } - insertedKeysCount++ progressChan <- uint64(totalKeysCount) + insertedKeysCount } - deletesWorker.Stop() - - wg.Wait() - - // wait and save all jobs - if err := runSaveLoop(deletesWorker.GetJobResultsChannel()); err != nil { - return [4]uint64{}, err - } s.updateDepth(maxReachedLevel) - tempTreeBuildTime := time.Since(tempTreeBuildStart) - - log.Info(fmt.Sprintf("[%s] Finished the temp tree build in %v, hashing and saving the result...", logPrefix, tempTreeBuildTime)) - - //special case where no values were inserted - if rootNode.isLeaf() { - return [4]uint64{}, nil - } - - //if the root node has only one branch, that branch should become the root node - var pathToDeleteFrom []int - if len(nodeKeys) == 1 { - if rootNode.node1 == nil { - rootNode = *rootNode.node0 - pathToDeleteFrom = append(pathToDeleteFrom, 0) - } else if rootNode.node0 == nil && utils.IsArrayUint64Empty(rootNode.leftHash[:]) { - rootNode = *rootNode.node1 - pathToDeleteFrom = append(pathToDeleteFrom, 1) - } - } - - //if the branch is a leaf, the rkey is the whole key - if rootNode.isLeaf() { - newRkey := []int{pathToDeleteFrom[0]} - pathToDeleteFrom = []int{} - newRkey = append(newRkey, rootNode.rKey...) - rootNode.rKey = newRkey - } - - finalRoot, err := rootNode.deleteTree(pathToDeleteFrom, s, &leafValueMap) - if err != nil { - return [4]uint64{}, err - } - - if err := s.setLastRoot(finalRoot); err != nil { - return [4]uint64{}, err - } - - return finalRoot, nil + return &rootNode, nil } -func runSaveLoop(jobResultsChannel chan utils.JobResult) error { - for { - select { - case result := <-jobResultsChannel: - if result.GetError() != nil { - return result.GetError() - } +func startBuildSmtLoopDbCompanionLoop(s *SMT, nodeKeys []utils.NodeKey, jobResultsChannel chan utils.JobResult, accountValuesReadChan chan *utils.NodeValue8) error { + lastReadAccountValueIndex := 0 + totalKeys := len(nodeKeys) - if err := result.Save(); err != nil { + for { + accountValuesReadChanSize := len(accountValuesReadChan) + readSize := 1024 - accountValuesReadChanSize + readLimit := lastReadAccountValueIndex + readSize + if readLimit > totalKeys { + readLimit = totalKeys + } + for ; lastReadAccountValueIndex < readLimit; lastReadAccountValueIndex++ { + v, err := s.Db.GetAccountValue(nodeKeys[lastReadAccountValueIndex]) + if err != nil { + accountValuesReadChan <- nil return err } - default: + accountValuesReadChan <- &v + } + + result, ok := <-jobResultsChannel + if !ok { return nil } + + if result.GetError() != nil { + return result.GetError() + } + + if err := result.Save(); err != nil { + return err + } } } @@ -381,19 +409,16 @@ func (n *SmtNode) deleteTreeNoSave(keyPath []int, leafValueMap *sync.Map, kvMapO if !ok { return [4]uint64{}, fmt.Errorf("value not found for key %v", k) } - accoutnValue := v.(utils.NodeValue8) + accoutnValue := v.(*utils.NodeValue8) newKey := utils.RemoveKeyBits(k, len(keyPath)) //hash and save leaf - newValH, newValHV, newLeafHash, newLeafHashV, err := createNewLeafNoSave(newKey, &accoutnValue) - if err != nil { - return [4]uint64{}, err - } - kvMapOfValuesToSave[newValH] = newValHV - kvMapOfValuesToSave[newLeafHash] = newLeafHashV - kvMapOfLeafValuesToSave[newLeafHash] = k + newValH, newValHV, newLeafHash, newLeafHashV := createNewLeafNoSave(&newKey, accoutnValue) + kvMapOfValuesToSave[*newValH] = *newValHV + kvMapOfValuesToSave[*newLeafHash] = *newLeafHashV + kvMapOfLeafValuesToSave[*newLeafHash] = k - return newLeafHash, nil + return *newLeafHash, nil } var totalHash utils.NodeValue8 @@ -425,14 +450,10 @@ func (n *SmtNode) deleteTreeNoSave(keyPath []int, leafValueMap *sync.Map, kvMapO totalHash.SetHalfValue(n.leftHash, 0) - newRoot, v, err := hashCalcAndPrepareForSave(totalHash.ToUintArray(), utils.BranchCapacity) - if err != nil { - return [4]uint64{}, err - } - - kvMapOfValuesToSave[newRoot] = v + newRoot, v := utils.HashKeyAndValueByPointers(totalHash.ToUintArrayByPointer(), &utils.BranchCapacity) + kvMapOfValuesToSave[*newRoot] = *v - return newRoot, nil + return *newRoot, nil } func (n *SmtNode) deleteTree(keyPath []int, s *SMT, leafValueMap *sync.Map) (newRoot [4]uint64, err error) { @@ -447,17 +468,9 @@ func (n *SmtNode) deleteTree(keyPath []int, s *SMT, leafValueMap *sync.Map) (new return newRoot, nil } -func createNewLeafNoSave(rkey utils.NodeKey, v *utils.NodeValue8) (newValH [4]uint64, newValHV utils.NodeValue12, newLeafHash [4]uint64, newLeafHashV utils.NodeValue12, err error) { +func createNewLeafNoSave(rkey *utils.NodeKey, v *utils.NodeValue8) (newValH *[4]uint64, newValHV *utils.NodeValue12, newLeafHash *[4]uint64, newLeafHashV *utils.NodeValue12) { //hash and save leaf - newValH, newValHV, err = hashCalcAndPrepareForSave(v.ToUintArray(), utils.BranchCapacity) - if err != nil { - return [4]uint64{}, utils.NodeValue12{}, [4]uint64{}, utils.NodeValue12{}, err - } - - newLeafHash, newLeafHashV, err = hashCalcAndPrepareForSave(utils.ConcatArrays4(rkey, newValH), utils.LeafCapacity) - if err != nil { - return [4]uint64{}, utils.NodeValue12{}, [4]uint64{}, utils.NodeValue12{}, err - } - - return newValH, newValHV, newLeafHash, newLeafHashV, nil + newValH, newValHV = utils.HashKeyAndValueByPointers(v.ToUintArrayByPointer(), &utils.BranchCapacity) + newLeafHash, newLeafHashV = utils.HashKeyAndValueByPointers(utils.ConcatArrays4ByPointers(rkey.AsUint64Pointer(), newValH), &utils.LeafCapacity) + return newValH, newValHV, newLeafHash, newLeafHashV } diff --git a/smt/pkg/utils/job_queue.go b/smt/pkg/utils/job_queue.go index 4d246df4d98..606da90a915 100644 --- a/smt/pkg/utils/job_queue.go +++ b/smt/pkg/utils/job_queue.go @@ -3,7 +3,6 @@ package utils import ( "context" "sync/atomic" - "time" ) type DB interface { @@ -78,25 +77,24 @@ func (w *Worker) GetJobResultsChannel() chan JobResult { } func (w *Worker) Stop() { - w.stopped.Store(true) + close(w.jobs) } // DoWork processes jobs from the queue (jobs channel). func (w *Worker) DoWork() { -LOOP: + defer close(w.jobResults) + for { select { case <-w.ctx.Done(): - break LOOP - // if job received. - case job := <-w.jobs: + return + case job, ok := <-w.jobs: + if !ok { + return + } + jobRes := job() w.jobResults <- jobRes - default: - if w.stopped.Load() { - break LOOP - } - time.Sleep(1 * time.Millisecond) } } } diff --git a/smt/pkg/utils/utils.go b/smt/pkg/utils/utils.go index 1410cddc74f..84733ba717e 100644 --- a/smt/pkg/utils/utils.go +++ b/smt/pkg/utils/utils.go @@ -47,11 +47,19 @@ const ( var ( LeafCapacity = [4]uint64{1, 0, 0, 0} BranchCapacity = [4]uint64{0, 0, 0, 0} - hashFunc = poseidon.Hash + hashFunc = poseidon.HashWithResult ) -func Hash(in [8]uint64, capacity [4]uint64) ([4]uint64, error) { - return hashFunc(in, capacity) +func Hash(in [8]uint64, capacity [4]uint64) [4]uint64 { + var result [4]uint64 = [4]uint64{0, 0, 0, 0} + hashFunc(&in, &capacity, &result) + return result +} + +func HashByPointers(in *[8]uint64, capacity *[4]uint64) *[4]uint64 { + var result [4]uint64 = [4]uint64{0, 0, 0, 0} + hashFunc(in, capacity, &result) + return &result } func (nk *NodeKey) IsZero() bool { @@ -66,6 +74,10 @@ func (nk *NodeKey) ToBigInt() *big.Int { return ArrayToScalar(nk[:]) } +func (nk *NodeKey) AsUint64Pointer() *[4]uint64 { + return (*[4]uint64)(nk) +} + func (nv *NodeValue8) IsZero() bool { if nv == nil { return true @@ -112,6 +124,22 @@ func (nv *NodeValue8) ToUintArray() [8]uint64 { return result } +func (nv *NodeValue8) ToUintArrayByPointer() *[8]uint64 { + var result [8]uint64 + + if nv != nil { + for i := 0; i < 8; i++ { + if nv[i] != nil { + result[i] = nv[i].Uint64() + } + // if nv[i] is nil, result[i] will remain as its zero value (0) + } + } + // if nv is nil, result will be an array of 8 zeros + + return &result +} + func (nv *NodeValue12) ToBigInt() *big.Int { return ArrayToScalarBig(nv[:]) } @@ -409,18 +437,28 @@ func ConcatArrays4(a, b [4]uint64) [8]uint64 { return result } -func ConcatArrays8AndCapacity(in [8]uint64, capacity [4]uint64) NodeValue12 { - var sl []uint64 - sl = append(sl, in[:]...) - sl = append(sl, capacity[:]...) +func ConcatArrays4ByPointers(a, b *[4]uint64) *[8]uint64 { + return &[8]uint64{ + a[0], a[1], a[2], a[3], + b[0], b[1], b[2], b[3], + } +} +func ConcatArrays8AndCapacityByPointers(in *[8]uint64, capacity *[4]uint64) *NodeValue12 { v := NodeValue12{} - for i, val := range sl { - b := new(big.Int) - v[i] = b.SetUint64(val) + for i, val := range in { + v[i] = new(big.Int).SetUint64(val) + } + for i, val := range capacity { + v[i+8] = new(big.Int).SetUint64(val) } - return v + return &v +} + +func HashKeyAndValueByPointers(in *[8]uint64, capacity *[4]uint64) (*[4]uint64, *NodeValue12) { + h := HashByPointers(in, capacity) + return h, ConcatArrays8AndCapacityByPointers(in, capacity) } func RemoveKeyBits(k NodeKey, nBits int) NodeKey { @@ -568,30 +606,30 @@ func StringToH4(s string) ([4]uint64, error) { return res, nil } -func KeyEthAddrBalance(ethAddr string) (NodeKey, error) { +func KeyEthAddrBalance(ethAddr string) NodeKey { return Key(ethAddr, KEY_BALANCE) } -func KeyEthAddrNonce(ethAddr string) (NodeKey, error) { +func KeyEthAddrNonce(ethAddr string) NodeKey { return Key(ethAddr, KEY_NONCE) } -func KeyContractCode(ethAddr string) (NodeKey, error) { +func KeyContractCode(ethAddr string) NodeKey { return Key(ethAddr, SC_CODE) } -func KeyContractLength(ethAddr string) (NodeKey, error) { +func KeyContractLength(ethAddr string) NodeKey { return Key(ethAddr, SC_LENGTH) } -func Key(ethAddr string, c int) (NodeKey, error) { +func Key(ethAddr string, c int) NodeKey { a := ConvertHexToBigInt(ethAddr) add := ScalarToArrayBig(a) key1 := NodeValue8{add[0], add[1], add[2], add[3], add[4], add[5], big.NewInt(int64(c)), big.NewInt(0)} key1Capacity, err := StringToH4(HASH_POSEIDON_ALL_ZEROES) if err != nil { - return NodeKey{}, err + return NodeKey{} } return Hash(key1.ToUintArray(), key1Capacity) @@ -609,8 +647,8 @@ func KeyBig(k *big.Int, c int) (*NodeKey, error) { return nil, err } - hk0, err := Hash(key1.ToUintArray(), key1Capacity) - return &NodeKey{hk0[0], hk0[1], hk0[2], hk0[3]}, err + hk0 := Hash(key1.ToUintArray(), key1Capacity) + return &NodeKey{hk0[0], hk0[1], hk0[2], hk0[3]}, nil } func StrValToBigInt(v string) (*big.Int, bool) { @@ -621,25 +659,21 @@ func StrValToBigInt(v string) (*big.Int, bool) { return new(big.Int).SetString(v, 10) } -func KeyContractStorage(ethAddr []*big.Int, storagePosition string) (NodeKey, error) { +func KeyContractStorage(ethAddr []*big.Int, storagePosition string) NodeKey { sp, _ := StrValToBigInt(storagePosition) spArray, err := NodeValue8FromBigIntArray(ScalarToArrayBig(sp)) if err != nil { - return NodeKey{}, err + return NodeKey{} } - hk0, err := Hash(spArray.ToUintArray(), [4]uint64{0, 0, 0, 0}) - if err != nil { - return NodeKey{}, err - } + hk0 := Hash(spArray.ToUintArray(), [4]uint64{0, 0, 0, 0}) key1 := NodeValue8{ethAddr[0], ethAddr[1], ethAddr[2], ethAddr[3], ethAddr[4], ethAddr[5], big.NewInt(int64(SC_STORAGE)), big.NewInt(0)} return Hash(key1.ToUintArray(), hk0) } -func HashContractBytecode(bc string) (string, error) { - var err error +func HashContractBytecode(bc string) string { bytecode := bc if strings.HasPrefix(bc, "0x") { @@ -700,15 +734,10 @@ func HashContractBytecode(bc string) (string, error) { var capacity [4]uint64 copy(capacity[:], elementsToHash[:4]) - tmpHash, err = Hash(in, capacity) - if err != nil { - return "", err - } + tmpHash = Hash(in, capacity) } - hex := ConvertBigIntToHex(ArrayToScalar(tmpHash[:])) - - return hex, err + return ConvertBigIntToHex(ArrayToScalar(tmpHash[:])) } func ResizeHashTo32BytesByPrefixingWithZeroes(hashValue []byte) []byte { diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 00000000000..f27960b89bd --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,13 @@ +sonar.projectKey=0xPolygonHermez_cdk-erigon +sonar.organization=0xpolygonhermez + +sonar.exclusions=**/*_test.go,**/vendor/**,**/mocks/**,/test/** + +sonar.tests=./zk,./zkevm +sonar.test.inclusions=**/*_test.go +sonar.test.exclusions=**/vendor/** +sonar.issue.enforceSemantic=true + +# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. +sonar.sources=./zk,./zkevm +sonar.go.coverage.reportPaths=coverage.out diff --git a/turbo/cli/default_flags.go b/turbo/cli/default_flags.go index 9d1461ea359..6f8fabc694b 100644 --- a/turbo/cli/default_flags.go +++ b/turbo/cli/default_flags.go @@ -62,6 +62,9 @@ var DefaultFlags = []cli.Flag{ &utils.AuthRpcVirtualHostsFlag, &utils.HTTPApiFlag, &utils.WSEnabledFlag, + &utils.WSListenAddrFlag, + &utils.WSPortFlag, + &utils.WSApiFlag, &utils.WsCompressionFlag, &utils.HTTPTraceFlag, &utils.StateCacheFlag, @@ -186,6 +189,7 @@ var DefaultFlags = []cli.Flag{ &utils.L1HighestBlockTypeFlag, &utils.L1MaticContractAddressFlag, &utils.L1FirstBlockFlag, + &utils.L1FinalizedBlockRequirementFlag, &utils.L1ContractAddressCheckFlag, &utils.RpcRateLimitsFlag, &utils.RpcGetBatchWitnessConcurrencyLimitFlag, @@ -198,6 +202,9 @@ var DefaultFlags = []cli.Flag{ &utils.SequencerBatchVerificationTimeout, &utils.SequencerTimeoutOnEmptyTxPool, &utils.SequencerHaltOnBatchNumber, + &utils.SequencerResequence, + &utils.SequencerResequenceStrict, + &utils.SequencerResequenceReuseL1InfoIndex, &utils.ExecutorUrls, &utils.ExecutorStrictMode, &utils.ExecutorRequestTimeout, @@ -221,7 +228,6 @@ var DefaultFlags = []cli.Flag{ &utils.DataStreamInactivityCheckInterval, &utils.WitnessFullFlag, &utils.SyncLimit, - &utils.SupportGasless, &utils.ExecutorPayloadOutput, &utils.DebugTimers, &utils.DebugNoSync, diff --git a/turbo/cli/flags.go b/turbo/cli/flags.go index 8bb035d4306..e347c91df82 100644 --- a/turbo/cli/flags.go +++ b/turbo/cli/flags.go @@ -356,6 +356,11 @@ func setEmbeddedRpcDaemon(ctx *cli.Context, cfg *nodecfg.Config) { apis := ctx.String(utils.HTTPApiFlag.Name) log.Info("starting HTTP APIs", "APIs", apis) + wsEnabled := ctx.IsSet(utils.WSEnabledFlag.Name) + wsApis := strings.Split(ctx.String(utils.WSApiFlag.Name), ",") + if wsEnabled { + log.Info("starting WS APIs", "APIs", wsApis) + } c := &httpcfg.HttpCfg{ Enabled: ctx.Bool(utils.HTTPEnabledFlag.Name), Dirs: cfg.Dirs, @@ -387,16 +392,20 @@ func setEmbeddedRpcDaemon(ctx *cli.Context, cfg *nodecfg.Config) { }, EvmCallTimeout: ctx.Duration(EvmCallTimeoutFlag.Name), - WebsocketEnabled: ctx.IsSet(utils.WSEnabledFlag.Name), - RpcBatchConcurrency: ctx.Uint(utils.RpcBatchConcurrencyFlag.Name), - RpcStreamingDisable: ctx.Bool(utils.RpcStreamingDisableFlag.Name), - DBReadConcurrency: ctx.Int(utils.DBReadConcurrencyFlag.Name), - RpcAllowListFilePath: ctx.String(utils.RpcAccessListFlag.Name), - Gascap: ctx.Uint64(utils.RpcGasCapFlag.Name), - MaxTraces: ctx.Uint64(utils.TraceMaxtracesFlag.Name), - TraceCompatibility: ctx.Bool(utils.RpcTraceCompatFlag.Name), - BatchLimit: ctx.Int(utils.RpcBatchLimit.Name), - ReturnDataLimit: ctx.Int(utils.RpcReturnDataLimit.Name), + WebsocketEnabled: wsEnabled, + WebSocketListenAddress: ctx.String(utils.WSListenAddrFlag.Name), + WebSocketPort: ctx.Int(utils.WSPortFlag.Name), + WebsocketCORSDomain: strings.Split(ctx.String(utils.WSAllowedOriginsFlag.Name), ","), + WebSocketApi: wsApis, + RpcBatchConcurrency: ctx.Uint(utils.RpcBatchConcurrencyFlag.Name), + RpcStreamingDisable: ctx.Bool(utils.RpcStreamingDisableFlag.Name), + DBReadConcurrency: ctx.Int(utils.DBReadConcurrencyFlag.Name), + RpcAllowListFilePath: ctx.String(utils.RpcAccessListFlag.Name), + Gascap: ctx.Uint64(utils.RpcGasCapFlag.Name), + MaxTraces: ctx.Uint64(utils.TraceMaxtracesFlag.Name), + TraceCompatibility: ctx.Bool(utils.RpcTraceCompatFlag.Name), + BatchLimit: ctx.Int(utils.RpcBatchLimit.Name), + ReturnDataLimit: ctx.Int(utils.RpcReturnDataLimit.Name), TxPoolApiAddr: ctx.String(utils.TxpoolApiAddrFlag.Name), diff --git a/turbo/cli/flags_zkevm.go b/turbo/cli/flags_zkevm.go index 7ca49f3060a..39c529d5b9d 100644 --- a/turbo/cli/flags_zkevm.go +++ b/turbo/cli/flags_zkevm.go @@ -16,6 +16,10 @@ import ( "github.com/urfave/cli/v2" ) +var DeprecatedFlags = map[string]string{ + "zkevm.gasless": "zkevm.allow-free-transactions", +} + func ApplyFlagsForZkConfig(ctx *cli.Context, cfg *ethconfig.Config) { checkFlag := func(flagName string, value interface{}) { switch v := value.(type) { @@ -129,6 +133,7 @@ func ApplyFlagsForZkConfig(ctx *cli.Context, cfg *ethconfig.Config) { L1HighestBlockType: ctx.String(utils.L1HighestBlockTypeFlag.Name), L1MaticContractAddress: libcommon.HexToAddress(ctx.String(utils.L1MaticContractAddressFlag.Name)), L1FirstBlock: ctx.Uint64(utils.L1FirstBlockFlag.Name), + L1FinalizedBlockRequirement: ctx.Uint64(utils.L1FinalizedBlockRequirementFlag.Name), L1ContractAddressCheck: ctx.Bool(utils.L1ContractAddressCheckFlag.Name), RpcRateLimits: ctx.Int(utils.RpcRateLimitsFlag.Name), RpcGetBatchWitnessConcurrencyLimit: ctx.Int(utils.RpcGetBatchWitnessConcurrencyLimitFlag.Name), @@ -141,6 +146,9 @@ func ApplyFlagsForZkConfig(ctx *cli.Context, cfg *ethconfig.Config) { SequencerBatchVerificationTimeout: sequencerBatchVerificationTimeout, SequencerTimeoutOnEmptyTxPool: sequencerTimeoutOnEmptyTxPool, SequencerHaltOnBatchNumber: ctx.Uint64(utils.SequencerHaltOnBatchNumber.Name), + SequencerResequence: ctx.Bool(utils.SequencerResequence.Name), + SequencerResequenceStrict: ctx.Bool(utils.SequencerResequenceStrict.Name), + SequencerResequenceReuseL1InfoIndex: ctx.Bool(utils.SequencerResequenceReuseL1InfoIndex.Name), ExecutorUrls: strings.Split(strings.ReplaceAll(ctx.String(utils.ExecutorUrls.Name), " ", ""), ","), ExecutorStrictMode: ctx.Bool(utils.ExecutorStrictMode.Name), ExecutorRequestTimeout: ctx.Duration(utils.ExecutorRequestTimeout.Name), @@ -159,7 +167,6 @@ func ApplyFlagsForZkConfig(ctx *cli.Context, cfg *ethconfig.Config) { GasPriceFactor: ctx.Float64(utils.GasPriceFactor.Name), WitnessFull: ctx.Bool(utils.WitnessFullFlag.Name), SyncLimit: ctx.Uint64(utils.SyncLimit.Name), - Gasless: ctx.Bool(utils.SupportGasless.Name), DebugTimers: ctx.Bool(utils.DebugTimers.Name), DebugNoSync: ctx.Bool(utils.DebugNoSync.Name), DebugLimit: ctx.Uint64(utils.DebugLimit.Name), diff --git a/turbo/rpchelper/rpc_block.go b/turbo/rpchelper/rpc_block.go index 24e900250b2..df8aef896e4 100644 --- a/turbo/rpchelper/rpc_block.go +++ b/turbo/rpchelper/rpc_block.go @@ -26,6 +26,23 @@ func GetLatestBlockNumber(tx kv.Tx) (uint64, error) { } } + blockNum, err := stages.GetStageProgress(tx, stages.Execution) + if err != nil { + return 0, fmt.Errorf("getting latest block number: %w", err) + } + + return blockNum, nil +} + +func GetLatestFinishedBlockNumber(tx kv.Tx) (uint64, error) { + forkchoiceHeadHash := rawdb.ReadForkchoiceHead(tx) + if forkchoiceHeadHash != (libcommon.Hash{}) { + forkchoiceHeadNum := rawdb.ReadHeaderNumber(tx, forkchoiceHeadHash) + if forkchoiceHeadNum != nil { + return *forkchoiceHeadNum, nil + } + } + blockNum, err := stages.GetStageProgress(tx, stages.Finish) if err != nil { return 0, fmt.Errorf("getting latest block number: %w", err) @@ -43,7 +60,7 @@ func GetFinalizedBlockNumber(tx kv.Tx) (uint64, error) { hermezDb := hermez_db.NewHermezDbReader(tx) // we've got the highest batch to execute to, now get it's highest block - highestVerifiedBlock, err := hermezDb.GetHighestBlockInBatch(highestVerifiedBatchNo) + highestVerifiedBlock, _, err := hermezDb.GetHighestBlockInBatch(highestVerifiedBatchNo) if err != nil { return 0, err } diff --git a/turbo/stages/stageloop.go b/turbo/stages/stageloop.go index 4e5c6aaa6d5..29e4748cbf5 100644 --- a/turbo/stages/stageloop.go +++ b/turbo/stages/stageloop.go @@ -106,9 +106,6 @@ func StageLoop( if !errors.Is(err, zk.ErrLimboState) { log.Error("Staged Sync", "err", err) } - if recoveryErr := hd.RecoverFromDb(db); recoveryErr != nil { - log.Error("Failed to recover header sentriesClient", "err", recoveryErr) - } time.Sleep(500 * time.Millisecond) // just to avoid too much similar errors in logs continue } diff --git a/turbo/transactions/call.go b/turbo/transactions/call.go index 00647478a02..21ddf13fd4c 100644 --- a/turbo/transactions/call.go +++ b/turbo/transactions/call.go @@ -204,6 +204,7 @@ func NewReusableCaller( chainConfig *chain.Config, callTimeout time.Duration, VirtualCountersSmtReduction float64, + useCounters bool, ) (*ReusableCaller, error) { ibs := state.New(stateReader) @@ -256,15 +257,21 @@ func NewReusableCaller( msg.Data(), ) - batchCounters := vm.NewBatchCounterCollector(smtDepth, uint16(forkId), VirtualCountersSmtReduction, false, nil) - txCounters := vm.NewTransactionCounter(transaction, smtDepth, uint16(forkId), VirtualCountersSmtReduction, false) + var batchCounters *vm.BatchCounterCollector + var counterCollector *vm.CounterCollector + if useCounters { + batchCounters = vm.NewBatchCounterCollector(smtDepth, uint16(forkId), VirtualCountersSmtReduction, false, nil) + txCounters := vm.NewTransactionCounter(transaction, smtDepth, uint16(forkId), VirtualCountersSmtReduction, false) - _, err = batchCounters.AddNewTransactionCounters(txCounters) - if err != nil { - return nil, err + _, err = batchCounters.AddNewTransactionCounters(txCounters) + if err != nil { + return nil, err + } + + counterCollector = txCounters.ExecutionCounters() } - zkVmConfig := vm.ZkConfig{Config: vm.Config{NoBaseFee: true}, CounterCollector: txCounters.ExecutionCounters()} + zkVmConfig := vm.ZkConfig{Config: vm.Config{NoBaseFee: true}, CounterCollector: counterCollector} evm := vm.NewZkEVM(blockCtx, txCtx, ibs, chainConfig, zkVmConfig) return &ReusableCaller{ diff --git a/turbo/transactions/tracing.go b/turbo/transactions/tracing.go index f8f41ce3444..8f231c9863d 100644 --- a/turbo/transactions/tracing.go +++ b/turbo/transactions/tracing.go @@ -157,9 +157,13 @@ func TraceTx( ) var streaming bool - var counterCollector *vm.CounterCollector + var counterCollector *vm.TransactionCounter + var executionCounters *vm.CounterCollector if config != nil { counterCollector = config.CounterCollector + if counterCollector != nil { + executionCounters = counterCollector.ExecutionCounters() + } } switch { case config != nil && config.Tracer != nil: @@ -195,15 +199,15 @@ func TraceTx( streaming = false case config == nil: - tracer = logger.NewJsonStreamLogger_ZkEvm(nil, ctx, stream, counterCollector) + tracer = logger.NewJsonStreamLogger_ZkEvm(nil, ctx, stream, executionCounters) streaming = true default: - tracer = logger.NewJsonStreamLogger_ZkEvm(config.LogConfig, ctx, stream, counterCollector) + tracer = logger.NewJsonStreamLogger_ZkEvm(config.LogConfig, ctx, stream, executionCounters) streaming = true } - zkConfig := vm.NewZkConfig(vm.Config{Debug: true, Tracer: tracer}, counterCollector) + zkConfig := vm.NewZkConfig(vm.Config{Debug: true, Tracer: tracer}, executionCounters) // Run the transaction with tracing enabled. vmenv := vm.NewZkEVM(blockCtx, txCtx, ibs, chainConfig, zkConfig) @@ -214,9 +218,9 @@ func TraceTx( if streaming { stream.WriteObjectStart() - if config != nil && config.CounterCollector != nil { + if executionCounters != nil { stream.WriteObjectField("smtLevels") - stream.WriteInt(config.CounterCollector.GetSmtLevels()) + stream.WriteInt(executionCounters.GetSmtLevels()) stream.WriteMore() } @@ -260,6 +264,25 @@ func TraceTx( } stream.WriteObjectField("returnValue") stream.WriteString(returnVal) + + if config != nil && config.CounterCollector != nil { + differences := config.CounterCollector.CombineCounters().UsedAsMap() + + stream.WriteMore() + stream.WriteObjectField("counters") + stream.WriteObjectStart() + first := true + for key, value := range differences { + if first { + first = false + } else { + stream.WriteMore() + } + stream.WriteObjectField(key) + stream.WriteInt(value) + } + stream.WriteObjectEnd() + } stream.WriteObjectEnd() } else { if r, err1 := tracer.(tracers.Tracer).GetResult(); err1 == nil { diff --git a/turbo/trie/trie_root.go b/turbo/trie/trie_root.go index 996ef935cb4..459a8443618 100644 --- a/turbo/trie/trie_root.go +++ b/turbo/trie/trie_root.go @@ -328,21 +328,6 @@ func (r *RootHashAggregator) Receive(itemType StreamItem, hasTree bool, cutoff int, ) error { - //r.traceIf("9c3dc2561d472d125d8f87dde8f2e3758386463ade768ae1a1546d34101968bb", "00") - //if storageKey == nil { - // //if bytes.HasPrefix(accountKey, common.FromHex("08050d07")) { - // fmt.Printf("1: %d, %x, %x\n", itemType, accountKey, hash) - // //} - //} else { - // //if bytes.HasPrefix(accountKey, common.FromHex("876f5a0f54b30254d2bad26bb5a8da19cbe748fd033004095d9c96c8e667376b")) && bytes.HasPrefix(storageKey, common.FromHex("")) { - // //fmt.Printf("%x\n", storageKey) - // fmt.Printf("1: %d, %x, %x, %x\n", itemType, accountKey, storageKey, hash) - // //} - //} - // - - fmt.Printf("1: %d, %x, %x, %x\n", itemType, accountKey, storageKey, hash) - switch itemType { case StorageStreamItem: if len(r.currAccK) == 0 { diff --git a/zk/contracts/l1_abi.go b/zk/contracts/l1_abi.go index ac816a2fd15..e9b3920040c 100644 --- a/zk/contracts/l1_abi.go +++ b/zk/contracts/l1_abi.go @@ -4,11 +4,15 @@ const SequenceBatchesAbiv6_6 = "[{\"inputs\":[{\"internalType\":\"contractIPolyg const SequenceBatchesAbiv5_0 = "[{\"inputs\":[{\"internalType\":\"contractIPolygonZkEVMGlobalExitRootV2\",\"name\":\"_globalExitRootManager\",\"type\":\"address\"},{\"internalType\":\"contractIERC20Upgradeable\",\"name\":\"_pol\",\"type\":\"address\"},{\"internalType\":\"contractIPolygonZkEVMBridgeV2\",\"name\":\"_bridgeAddress\",\"type\":\"address\"},{\"internalType\":\"contractPolygonRollupManager\",\"name\":\"_rollupManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"BatchAlreadyVerified\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BatchNotSequencedOrNotSequenceEnd\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExceedMaxVerifyBatches\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FinalNumBatchBelowLastVerifiedBatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FinalNumBatchDoesNotMatchPendingState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FinalPendingStateNumInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchTimeoutNotExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchesAlreadyActive\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchesDecentralized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchesNotAllowedOnEmergencyState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchesOverflow\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForcedDataDoesNotMatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasTokenNetworkMustBeZeroOnEther\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GlobalExitRootNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"HaltTimeoutNotExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"HaltTimeoutNotExpiredAfterEmergencyState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"HugeTokenMetadataNotSupported\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InitNumBatchAboveLastVerifiedBatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InitNumBatchDoesNotMatchPendingState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitializeTransaction\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRangeBatchTimeTarget\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRangeForceBatchTimeout\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRangeMultiplierBatchFee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NewAccInputHashDoesNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NewPendingStateTimeoutMustBeLower\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NewStateRootNotInsidePrime\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NewTrustedAggregatorTimeoutMustBeLower\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughMaticAmount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughPOLAmount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OldAccInputHashDoesNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OldStateRootDoesNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPendingAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyRollupManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyTrustedAggregator\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyTrustedSequencer\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingStateDoesNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingStateInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingStateNotConsolidable\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingStateTimeoutExceedHaltAggregationTimeout\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SequenceZeroBatches\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SequencedTimestampBelowForcedTimestamp\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SequencedTimestampInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StoredRootMustBeDifferentThanNewRoot\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TransactionsLengthAboveMax\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TrustedAggregatorTimeoutExceedHaltAggregationTimeout\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TrustedAggregatorTimeoutNotExpired\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AcceptAdminRole\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"forceBatchNum\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"lastGlobalExitRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sequencer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"}],\"name\":\"ForceBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"lastGlobalExitRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sequencer\",\"type\":\"address\"}],\"name\":\"InitialSequenceBatches\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"l1InfoRoot\",\"type\":\"bytes32\"}],\"name\":\"SequenceBatches\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"}],\"name\":\"SequenceForceBatches\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newForceBatchAddress\",\"type\":\"address\"}],\"name\":\"SetForceBatchAddress\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"newforceBatchTimeout\",\"type\":\"uint64\"}],\"name\":\"SetForceBatchTimeout\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newTrustedSequencer\",\"type\":\"address\"}],\"name\":\"SetTrustedSequencer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"newTrustedSequencerURL\",\"type\":\"string\"}],\"name\":\"SetTrustedSequencerURL\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newPendingAdmin\",\"type\":\"address\"}],\"name\":\"TransferAdminRole\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"lastGlobalExitRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sequencer\",\"type\":\"address\"}],\"name\":\"UpdateEtrogSequence\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"}],\"name\":\"VerifyBatches\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"GLOBAL_EXIT_ROOT_MANAGER_L2\",\"outputs\":[{\"internalType\":\"contractIBasePolygonZkEVMGlobalExitRoot\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_BRIDGE_LIST_LEN_LEN\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_BRIDGE_PARAMS\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS_EMPTY_METADATA\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_CONSTANT_BYTES\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_CONSTANT_BYTES_EMPTY_METADATA\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_DATA_LEN_EMPTY_METADATA\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_EFFECTIVE_PERCENTAGE\",\"outputs\":[{\"internalType\":\"bytes1\",\"name\":\"\",\"type\":\"bytes1\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SET_UP_ETROG_TX\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SIGNATURE_INITIALIZE_TX_R\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SIGNATURE_INITIALIZE_TX_S\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SIGNATURE_INITIALIZE_TX_V\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptAdminRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"bridgeAddress\",\"outputs\":[{\"internalType\":\"contractIPolygonZkEVMBridgeV2\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"calculatePolPerForceBatch\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"polAmount\",\"type\":\"uint256\"}],\"name\":\"forceBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"forceBatchAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"forceBatchTimeout\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"name\":\"forcedBatches\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasTokenAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasTokenNetwork\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"networkID\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"_gasTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"_gasTokenNetwork\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"_gasTokenMetadata\",\"type\":\"bytes\"}],\"name\":\"generateInitializeTransaction\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"globalExitRootManager\",\"outputs\":[{\"internalType\":\"contractIPolygonZkEVMGlobalExitRootV2\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_admin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"sequencer\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"networkID\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"_gasTokenAddress\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"sequencerURL\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_networkName\",\"type\":\"string\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_admin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_trustedSequencer\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"_trustedSequencerURL\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_networkName\",\"type\":\"string\"},{\"internalType\":\"bytes32\",\"name\":\"_lastAccInputHash\",\"type\":\"bytes32\"}],\"name\":\"initializeUpgrade\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastAccInputHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastForceBatch\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastForceBatchSequenced\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"networkName\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"lastVerifiedBatch\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"newStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"}],\"name\":\"onVerifyBatches\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pol\",\"outputs\":[{\"internalType\":\"contractIERC20Upgradeable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rollupManager\",\"outputs\":[{\"internalType\":\"contractPolygonRollupManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"forcedGlobalExitRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"forcedTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"forcedBlockHashL1\",\"type\":\"bytes32\"}],\"internalType\":\"structPolygonRollupBaseEtrog.BatchData[]\",\"name\":\"batches\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"l2Coinbase\",\"type\":\"address\"}],\"name\":\"sequenceBatches\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"forcedGlobalExitRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"forcedTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"forcedBlockHashL1\",\"type\":\"bytes32\"}],\"internalType\":\"structPolygonRollupBaseEtrog.BatchData[]\",\"name\":\"batches\",\"type\":\"tuple[]\"}],\"name\":\"sequenceForceBatches\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newForceBatchAddress\",\"type\":\"address\"}],\"name\":\"setForceBatchAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"newforceBatchTimeout\",\"type\":\"uint64\"}],\"name\":\"setForceBatchTimeout\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newTrustedSequencer\",\"type\":\"address\"}],\"name\":\"setTrustedSequencer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"newTrustedSequencerURL\",\"type\":\"string\"}],\"name\":\"setTrustedSequencerURL\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newPendingAdmin\",\"type\":\"address\"}],\"name\":\"transferAdminRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trustedSequencer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trustedSequencerURL\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]" const SequenceBatchesValidiumAbiElderBerry = "[{\"inputs\":[{\"internalType\":\"contractIPolygonZkEVMGlobalExitRootV2\",\"name\":\"_globalExitRootManager\",\"type\":\"address\"},{\"internalType\":\"contractIERC20Upgradeable\",\"name\":\"_pol\",\"type\":\"address\"},{\"internalType\":\"contractIPolygonZkEVMBridgeV2\",\"name\":\"_bridgeAddress\",\"type\":\"address\"},{\"internalType\":\"contractPolygonRollupManager\",\"name\":\"_rollupManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"BatchAlreadyVerified\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BatchNotSequencedOrNotSequenceEnd\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExceedMaxVerifyBatches\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FinalNumBatchBelowLastVerifiedBatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FinalNumBatchDoesNotMatchPendingState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FinalPendingStateNumInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchTimeoutNotExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchesAlreadyActive\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchesDecentralized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchesNotAllowedOnEmergencyState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchesOverflow\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForcedDataDoesNotMatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasTokenNetworkMustBeZeroOnEther\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GlobalExitRootNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"HaltTimeoutNotExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"HaltTimeoutNotExpiredAfterEmergencyState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"HugeTokenMetadataNotSupported\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InitNumBatchAboveLastVerifiedBatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InitNumBatchDoesNotMatchPendingState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InitSequencedBatchDoesNotMatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitializeTransaction\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRangeBatchTimeTarget\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRangeForceBatchTimeout\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRangeMultiplierBatchFee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxTimestampSequenceInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NewAccInputHashDoesNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NewPendingStateTimeoutMustBeLower\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NewStateRootNotInsidePrime\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NewTrustedAggregatorTimeoutMustBeLower\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughMaticAmount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughPOLAmount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OldAccInputHashDoesNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OldStateRootDoesNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPendingAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyRollupManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyTrustedAggregator\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyTrustedSequencer\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingStateDoesNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingStateInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingStateNotConsolidable\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingStateTimeoutExceedHaltAggregationTimeout\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SequenceWithDataAvailabilityNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SequenceZeroBatches\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SequencedTimestampBelowForcedTimestamp\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SequencedTimestampInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StoredRootMustBeDifferentThanNewRoot\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SwitchToSameValue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TransactionsLengthAboveMax\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TrustedAggregatorTimeoutExceedHaltAggregationTimeout\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TrustedAggregatorTimeoutNotExpired\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AcceptAdminRole\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"forceBatchNum\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"lastGlobalExitRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sequencer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"}],\"name\":\"ForceBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"lastGlobalExitRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sequencer\",\"type\":\"address\"}],\"name\":\"InitialSequenceBatches\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"l1InfoRoot\",\"type\":\"bytes32\"}],\"name\":\"SequenceBatches\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"}],\"name\":\"SequenceForceBatches\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newDataAvailabilityProtocol\",\"type\":\"address\"}],\"name\":\"SetDataAvailabilityProtocol\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newForceBatchAddress\",\"type\":\"address\"}],\"name\":\"SetForceBatchAddress\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"newforceBatchTimeout\",\"type\":\"uint64\"}],\"name\":\"SetForceBatchTimeout\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newTrustedSequencer\",\"type\":\"address\"}],\"name\":\"SetTrustedSequencer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"newTrustedSequencerURL\",\"type\":\"string\"}],\"name\":\"SetTrustedSequencerURL\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"SwitchSequenceWithDataAvailability\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newPendingAdmin\",\"type\":\"address\"}],\"name\":\"TransferAdminRole\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"}],\"name\":\"VerifyBatches\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"GLOBAL_EXIT_ROOT_MANAGER_L2\",\"outputs\":[{\"internalType\":\"contractIBasePolygonZkEVMGlobalExitRoot\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_BRIDGE_LIST_LEN_LEN\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_BRIDGE_PARAMS\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_BRIDGE_PARAMS_AFTER_BRIDGE_ADDRESS_EMPTY_METADATA\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_CONSTANT_BYTES\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_CONSTANT_BYTES_EMPTY_METADATA\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_DATA_LEN_EMPTY_METADATA\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"INITIALIZE_TX_EFFECTIVE_PERCENTAGE\",\"outputs\":[{\"internalType\":\"bytes1\",\"name\":\"\",\"type\":\"bytes1\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SIGNATURE_INITIALIZE_TX_R\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SIGNATURE_INITIALIZE_TX_S\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SIGNATURE_INITIALIZE_TX_V\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"TIMESTAMP_RANGE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptAdminRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"bridgeAddress\",\"outputs\":[{\"internalType\":\"contractIPolygonZkEVMBridgeV2\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"calculatePolPerForceBatch\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dataAvailabilityProtocol\",\"outputs\":[{\"internalType\":\"contractIDataAvailabilityProtocol\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"polAmount\",\"type\":\"uint256\"}],\"name\":\"forceBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"forceBatchAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"forceBatchTimeout\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"name\":\"forcedBatches\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasTokenAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasTokenNetwork\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"networkID\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"_gasTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"_gasTokenNetwork\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"_gasTokenMetadata\",\"type\":\"bytes\"}],\"name\":\"generateInitializeTransaction\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"globalExitRootManager\",\"outputs\":[{\"internalType\":\"contractIPolygonZkEVMGlobalExitRootV2\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_admin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"sequencer\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"networkID\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"_gasTokenAddress\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"sequencerURL\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_networkName\",\"type\":\"string\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isSequenceWithDataAvailabilityAllowed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastAccInputHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastForceBatch\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastForceBatchSequenced\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"networkName\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"lastVerifiedBatch\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"newStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"}],\"name\":\"onVerifyBatches\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pol\",\"outputs\":[{\"internalType\":\"contractIERC20Upgradeable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rollupManager\",\"outputs\":[{\"internalType\":\"contractPolygonRollupManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"forcedGlobalExitRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"forcedTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"forcedBlockHashL1\",\"type\":\"bytes32\"}],\"internalType\":\"structPolygonRollupBaseEtrog.BatchData[]\",\"name\":\"batches\",\"type\":\"tuple[]\"},{\"internalType\":\"uint64\",\"name\":\"maxSequenceTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initSequencedBatch\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"l2Coinbase\",\"type\":\"address\"}],\"name\":\"sequenceBatches\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"transactionsHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"forcedGlobalExitRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"forcedTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"forcedBlockHashL1\",\"type\":\"bytes32\"}],\"internalType\":\"structPolygonValidiumEtrog.ValidiumBatchData[]\",\"name\":\"batches\",\"type\":\"tuple[]\"},{\"internalType\":\"uint64\",\"name\":\"maxSequenceTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initSequencedBatch\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"l2Coinbase\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"dataAvailabilityMessage\",\"type\":\"bytes\"}],\"name\":\"sequenceBatchesValidium\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"forcedGlobalExitRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"forcedTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"forcedBlockHashL1\",\"type\":\"bytes32\"}],\"internalType\":\"structPolygonRollupBaseEtrog.BatchData[]\",\"name\":\"batches\",\"type\":\"tuple[]\"}],\"name\":\"sequenceForceBatches\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractIDataAvailabilityProtocol\",\"name\":\"newDataAvailabilityProtocol\",\"type\":\"address\"}],\"name\":\"setDataAvailabilityProtocol\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newForceBatchAddress\",\"type\":\"address\"}],\"name\":\"setForceBatchAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"newforceBatchTimeout\",\"type\":\"uint64\"}],\"name\":\"setForceBatchTimeout\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newTrustedSequencer\",\"type\":\"address\"}],\"name\":\"setTrustedSequencer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"newTrustedSequencerURL\",\"type\":\"string\"}],\"name\":\"setTrustedSequencerURL\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"newIsSequenceWithDataAvailabilityAllowed\",\"type\":\"bool\"}],\"name\":\"switchSequenceWithDataAvailability\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newPendingAdmin\",\"type\":\"address\"}],\"name\":\"transferAdminRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trustedSequencer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trustedSequencerURL\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]" +const SequenceBatchesPreEtrogAbi = "[{\"inputs\":[{\"internalType\":\"contract IPolygonZkEVMGlobalExitRoot\",\"name\":\"_globalExitRootManager\",\"type\":\"address\"},{\"internalType\":\"contract IERC20Upgradeable\",\"name\":\"_matic\",\"type\":\"address\"},{\"internalType\":\"contract IVerifierRollup\",\"name\":\"_rollupVerifier\",\"type\":\"address\"},{\"internalType\":\"contract IPolygonZkEVMBridge\",\"name\":\"_bridgeAddress\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"_chainID\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"_forkID\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"versionBeforeUpgrade\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"BatchAlreadyVerified\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BatchNotSequencedOrNotSequenceEnd\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExceedMaxVerifyBatches\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FinalNumBatchBelowLastVerifiedBatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FinalNumBatchDoesNotMatchPendingState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FinalPendingStateNumInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchTimeoutNotExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchesAlreadyActive\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceBatchesOverflow\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForcedDataDoesNotMatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GlobalExitRootNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"HaltTimeoutNotExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InitBatchMustMatchCurrentForkID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InitNumBatchAboveLastVerifiedBatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InitNumBatchDoesNotMatchPendingState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRangeBatchTimeTarget\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRangeForceBatchTimeout\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRangeMultiplierBatchFee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NewAccInputHashDoesNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NewPendingStateTimeoutMustBeLower\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NewStateRootNotInsidePrime\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NewTrustedAggregatorTimeoutMustBeLower\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughMaticAmount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OldAccInputHashDoesNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OldStateRootDoesNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyEmergencyState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyNotEmergencyState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPendingAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyTrustedAggregator\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyTrustedSequencer\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingStateDoesNotExist\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingStateInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingStateNotConsolidable\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingStateTimeoutExceedHaltAggregationTimeout\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SequenceZeroBatches\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SequencedTimestampBelowForcedTimestamp\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SequencedTimestampInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StoredRootMustBeDifferentThanNewRoot\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TransactionsLengthAboveMax\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TrustedAggregatorTimeoutExceedHaltAggregationTimeout\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TrustedAggregatorTimeoutNotExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"VersionAlreadyUpdated\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AcceptAdminRole\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"ActivateForceBatches\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"pendingStateNum\",\"type\":\"uint64\"}],\"name\":\"ConsolidatePendingState\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"EmergencyStateActivated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"EmergencyStateDeactivated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"forceBatchNum\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"lastGlobalExitRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sequencer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"}],\"name\":\"ForceBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"}],\"name\":\"OverridePendingState\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"storedStateRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"provedStateRoot\",\"type\":\"bytes32\"}],\"name\":\"ProveNonDeterministicPendingState\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"}],\"name\":\"SequenceBatches\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"}],\"name\":\"SequenceForceBatches\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"newforceBatchTimeout\",\"type\":\"uint64\"}],\"name\":\"SetForceBatchTimeout\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newMultiplierBatchFee\",\"type\":\"uint16\"}],\"name\":\"SetMultiplierBatchFee\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"newPendingStateTimeout\",\"type\":\"uint64\"}],\"name\":\"SetPendingStateTimeout\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newTrustedAggregator\",\"type\":\"address\"}],\"name\":\"SetTrustedAggregator\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"newTrustedAggregatorTimeout\",\"type\":\"uint64\"}],\"name\":\"SetTrustedAggregatorTimeout\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newTrustedSequencer\",\"type\":\"address\"}],\"name\":\"SetTrustedSequencer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"newTrustedSequencerURL\",\"type\":\"string\"}],\"name\":\"SetTrustedSequencerURL\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"newVerifyBatchTimeTarget\",\"type\":\"uint64\"}],\"name\":\"SetVerifyBatchTimeTarget\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newPendingAdmin\",\"type\":\"address\"}],\"name\":\"TransferAdminRole\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"forkID\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"}],\"name\":\"UpdateZkEVMVersion\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"}],\"name\":\"VerifyBatches\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"numBatch\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"}],\"name\":\"VerifyBatchesTrustedAggregator\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"VERSION_BEFORE_UPGRADE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptAdminRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sequencedBatchNum\",\"type\":\"uint64\"}],\"name\":\"activateEmergencyState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"activateForceBatches\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"name\":\"batchNumToStateRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"bridgeAddress\",\"outputs\":[{\"internalType\":\"contract IPolygonZkEVMBridge\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"calculateRewardPerBatch\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"chainID\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newStateRoot\",\"type\":\"uint256\"}],\"name\":\"checkStateRootInsidePrime\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"pendingStateNum\",\"type\":\"uint64\"}],\"name\":\"consolidatePendingState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"deactivateEmergencyState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"maticAmount\",\"type\":\"uint256\"}],\"name\":\"forceBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"forceBatchTimeout\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"name\":\"forcedBatches\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"forkID\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getForcedBatchFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"initNumBatch\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"finalNewBatch\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"newLocalExitRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"oldStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"newStateRoot\",\"type\":\"bytes32\"}],\"name\":\"getInputSnarkBytes\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLastVerifiedBatch\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"globalExitRootManager\",\"outputs\":[{\"internalType\":\"contract IPolygonZkEVMGlobalExitRoot\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"trustedSequencer\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"pendingStateTimeout\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"trustedAggregator\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"trustedAggregatorTimeout\",\"type\":\"uint64\"}],\"internalType\":\"struct PolygonZkEVM.InitializePackedParameters\",\"name\":\"initializePackedParameters\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"genesisRoot\",\"type\":\"bytes32\"},{\"internalType\":\"string\",\"name\":\"_trustedSequencerURL\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_networkName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_version\",\"type\":\"string\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isEmergencyState\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isForcedBatchDisallowed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"pendingStateNum\",\"type\":\"uint64\"}],\"name\":\"isPendingStateConsolidable\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastBatchSequenced\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastForceBatch\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastForceBatchSequenced\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastPendingState\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastPendingStateConsolidated\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastTimestamp\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastVerifiedBatch\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastVerifiedBatchBeforeUpgrade\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"matic\",\"outputs\":[{\"internalType\":\"contract IERC20Upgradeable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"multiplierBatchFee\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"networkName\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"initPendingStateNum\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"finalPendingStateNum\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initNumBatch\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"finalNewBatch\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"newLocalExitRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"newStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[24]\",\"name\":\"proof\",\"type\":\"bytes32[24]\"}],\"name\":\"overridePendingState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingStateTimeout\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"pendingStateTransitions\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastVerifiedBatch\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"exitRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"initPendingStateNum\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"finalPendingStateNum\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initNumBatch\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"finalNewBatch\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"newLocalExitRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"newStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[24]\",\"name\":\"proof\",\"type\":\"bytes32[24]\"}],\"name\":\"proveNonDeterministicPendingState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rollupVerifier\",\"outputs\":[{\"internalType\":\"contract IVerifierRollup\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"globalExitRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"minForcedTimestamp\",\"type\":\"uint64\"}],\"internalType\":\"struct PolygonZkEVM.BatchData[]\",\"name\":\"batches\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"l2Coinbase\",\"type\":\"address\"}],\"name\":\"sequenceBatches\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"transactions\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"globalExitRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"minForcedTimestamp\",\"type\":\"uint64\"}],\"internalType\":\"struct PolygonZkEVM.ForcedBatchData[]\",\"name\":\"batches\",\"type\":\"tuple[]\"}],\"name\":\"sequenceForceBatches\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"name\":\"sequencedBatches\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"accInputHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sequencedTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"previousLastBatchSequenced\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"newforceBatchTimeout\",\"type\":\"uint64\"}],\"name\":\"setForceBatchTimeout\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"newMultiplierBatchFee\",\"type\":\"uint16\"}],\"name\":\"setMultiplierBatchFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"newPendingStateTimeout\",\"type\":\"uint64\"}],\"name\":\"setPendingStateTimeout\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newTrustedAggregator\",\"type\":\"address\"}],\"name\":\"setTrustedAggregator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"newTrustedAggregatorTimeout\",\"type\":\"uint64\"}],\"name\":\"setTrustedAggregatorTimeout\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newTrustedSequencer\",\"type\":\"address\"}],\"name\":\"setTrustedSequencer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"newTrustedSequencerURL\",\"type\":\"string\"}],\"name\":\"setTrustedSequencerURL\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"newVerifyBatchTimeTarget\",\"type\":\"uint64\"}],\"name\":\"setVerifyBatchTimeTarget\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newPendingAdmin\",\"type\":\"address\"}],\"name\":\"transferAdminRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trustedAggregator\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trustedAggregatorTimeout\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trustedSequencer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"trustedSequencerURL\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_versionString\",\"type\":\"string\"}],\"name\":\"updateVersion\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"verifyBatchTimeTarget\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"pendingStateNum\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initNumBatch\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"finalNewBatch\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"newLocalExitRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"newStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[24]\",\"name\":\"proof\",\"type\":\"bytes32[24]\"}],\"name\":\"verifyBatches\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"pendingStateNum\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initNumBatch\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"finalNewBatch\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"newLocalExitRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"newStateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[24]\",\"name\":\"proof\",\"type\":\"bytes32[24]\"}],\"name\":\"verifyBatchesTrustedAggregator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]" + const SequenceBatchesValidiumElderBerry = "db5b0ed7" +const SequenceBatchesPreEtrog = "5e9145c9" const SequenceBatchesIdv5_0 = "ecef3f99" const SequenceBatchesIdv6_6 = "def57e54" var SequenceBatchesMapping = map[string]string{ + SequenceBatchesPreEtrog: SequenceBatchesPreEtrogAbi, SequenceBatchesIdv5_0: SequenceBatchesAbiv5_0, SequenceBatchesIdv6_6: SequenceBatchesAbiv6_6, SequenceBatchesValidiumElderBerry: SequenceBatchesValidiumAbiElderBerry, diff --git a/zk/datastream/client/commands.go b/zk/datastream/client/commands.go index bb9cbafe825..8676a2807eb 100644 --- a/zk/datastream/client/commands.go +++ b/zk/datastream/client/commands.go @@ -23,23 +23,27 @@ func (c *StreamClient) sendHeaderCmd() error { return nil } -// sendStartBookmarkCmd sends a start command to the server, indicating -// that the client wishes to start streaming from the given bookmark -func (c *StreamClient) sendStartBookmarkCmd(bookmark []byte) error { - err := c.sendCommand(CmdStartBookmark) - if err != nil { - return err +// sendBookmarkCmd sends either CmdStartBookmark or CmdBookmark for the provided bookmark value. +// In case streaming parameter is set to true, the CmdStartBookmark is sent, otherwise the CmdBookmark. +func (c *StreamClient) sendBookmarkCmd(bookmark []byte, streaming bool) error { + // in case we want to stream the entries, CmdStartBookmark is sent, otherwise CmdBookmark command + command := CmdStartBookmark + if !streaming { + command = CmdBookmark } - // Send starting/from entry number - if err := writeFullUint32ToConn(c.conn, uint32(len(bookmark))); err != nil { + // Send the command + if err := c.sendCommand(command); err != nil { return err } - if err := writeBytesToConn(c.conn, bookmark); err != nil { + + // Send bookmark length + if err := writeFullUint32ToConn(c.conn, uint32(len(bookmark))); err != nil { return err } - return nil + // Send the bookmark to retrieve + return writeBytesToConn(c.conn, bookmark) } // sendStartCmd sends a start command to the server, indicating @@ -51,11 +55,18 @@ func (c *StreamClient) sendStartCmd(from uint64) error { } // Send starting/from entry number - if err := writeFullUint64ToConn(c.conn, from); err != nil { + return writeFullUint64ToConn(c.conn, from) +} + +// sendEntryCmd sends the get data stream entry by number command to a TCP connection +func (c *StreamClient) sendEntryCmd(entryNum uint64) error { + // Send CmdEntry command + if err := c.sendCommand(CmdEntry); err != nil { return err } - return nil + // Send entry number + return writeFullUint64ToConn(c.conn, entryNum) } // sendHeaderCmd sends the header command to the server. diff --git a/zk/datastream/client/stream_client.go b/zk/datastream/client/stream_client.go index f7362743284..461b3d9371c 100644 --- a/zk/datastream/client/stream_client.go +++ b/zk/datastream/client/stream_client.go @@ -29,15 +29,19 @@ const ( versionAddedBlockEnd = 3 // Added block end ) +var ( + // ErrFileEntryNotFound denotes error that is returned when the certain file entry is not found in the datastream + ErrFileEntryNotFound = errors.New("file entry not found") +) + type StreamClient struct { ctx context.Context server string // Server address to connect IP:port version int streamType StreamType conn net.Conn - id string // Client id - Header types.HeaderEntry // Header info received (from Header command) - checkTimeout time.Duration // time to wait for data before reporting an error + id string // Client id + checkTimeout time.Duration // time to wait for data before reporting an error // atomic lastWrittenTime atomic.Int64 @@ -59,6 +63,7 @@ const ( PtPadding = 0 PtHeader = 1 // Just for the header page PtData = 2 // Data entry + PtDataRsp = 0xfe // PtDataRsp is packet type for command response with data PtResult = 0xff // Not stored/present in file (just for client command result) ) @@ -86,6 +91,108 @@ func (c *StreamClient) IsVersion3() bool { func (c *StreamClient) GetEntryChan() chan interface{} { return c.entryChan } + +// GetL2BlockByNumber queries the data stream by sending the L2 block start bookmark for the certain block number +// and streams the changes for that block (including the transactions). +// Note that this function is intended for on demand querying and it disposes the connection after it ends. +func (c *StreamClient) GetL2BlockByNumber(blockNum uint64) (*types.FullL2Block, int, error) { + if _, err := c.EnsureConnected(); err != nil { + return nil, -1, err + } + defer c.Stop() + + var ( + l2Block *types.FullL2Block + err error + isL2Block bool + ) + + bookmark := types.NewBookmarkProto(blockNum, datastream.BookmarkType_BOOKMARK_TYPE_L2_BLOCK) + bookmarkRaw, err := bookmark.Marshal() + if err != nil { + return nil, -1, err + } + + re, err := c.initiateDownloadBookmark(bookmarkRaw) + if err != nil { + errorCode := -1 + if re != nil { + errorCode = int(re.ErrorNum) + } + return nil, errorCode, err + } + + for l2Block == nil { + select { + case <-c.ctx.Done(): + errorCode := -1 + if re != nil { + errorCode = int(re.ErrorNum) + } + return l2Block, errorCode, nil + default: + } + + parsedEntry, err := ReadParsedProto(c) + if err != nil { + return nil, -1, err + } + + l2Block, isL2Block = parsedEntry.(*types.FullL2Block) + if isL2Block { + break + } + } + + if l2Block.L2BlockNumber != blockNum { + return nil, -1, fmt.Errorf("expected block number %d but got %d", blockNum, l2Block.L2BlockNumber) + } + + return l2Block, types.CmdErrOK, nil +} + +// GetLatestL2Block queries the data stream by reading the header entry and based on total entries field, +// it retrieves the latest File entry that is of EntryTypeL2Block type. +// Note that this function is intended for on demand querying and it disposes the connection after it ends. +func (c *StreamClient) GetLatestL2Block() (l2Block *types.FullL2Block, err error) { + if _, err := c.EnsureConnected(); err != nil { + return nil, err + } + defer c.Stop() + + h, err := c.GetHeader() + if err != nil { + return nil, err + } + + latestEntryNum := h.TotalEntries - 1 + + for l2Block == nil && latestEntryNum > 0 { + if err := c.sendEntryCmdWrapper(latestEntryNum); err != nil { + return nil, err + } + + entry, err := c.NextFileEntry() + if err != nil { + return nil, err + } + + if entry.EntryType == types.EntryTypeL2Block { + if l2Block, err = types.UnmarshalL2Block(entry.Data); err != nil { + return nil, err + } + } + + latestEntryNum-- + } + + if latestEntryNum == 0 { + return nil, errors.New("failed to retrieve the latest block from the data stream") + } + + return l2Block, nil +} + func (c *StreamClient) GetLastWrittenTimeAtomic() *atomic.Int64 { return &c.lastWrittenTime } @@ -111,10 +218,14 @@ func (c *StreamClient) Start() error { } func (c *StreamClient) Stop() { + if c.conn == nil { + return + } if err := c.sendStopCmd(); err != nil { log.Warn(fmt.Sprintf("Failed to send the stop command to the data stream server: %s", err)) } c.conn.Close() + c.conn = nil close(c.entryChan) } @@ -122,45 +233,59 @@ func (c *StreamClient) Stop() { // Command header: Get status // Returns the current status of the header. // If started, terminate the connection. -func (c *StreamClient) GetHeader() error { +func (c *StreamClient) GetHeader() (*types.HeaderEntry, error) { if err := c.sendHeaderCmd(); err != nil { - return fmt.Errorf("%s send header error: %v", c.id, err) + return nil, fmt.Errorf("%s send header error: %v", c.id, err) } // Read packet packet, err := readBuffer(c.conn, 1) if err != nil { - return fmt.Errorf("%s read buffer: %v", c.id, err) + return nil, fmt.Errorf("%s read buffer: %v", c.id, err) } // Check packet type if packet[0] != PtResult { - return fmt.Errorf("%s error expecting result packet type %d and received %d", c.id, PtResult, packet[0]) + return nil, fmt.Errorf("%s error expecting result packet type %d and received %d", c.id, PtResult, packet[0]) } // Read server result entry for the command r, err := c.readResultEntry(packet) if err != nil { - return fmt.Errorf("%s read result entry error: %v", c.id, err) + return nil, fmt.Errorf("%s read result entry error: %v", c.id, err) } if err := r.GetError(); err != nil { - return fmt.Errorf("%s got Result error code %d: %v", c.id, r.ErrorNum, err) + return nil, fmt.Errorf("%s got Result error code %d: %v", c.id, r.ErrorNum, err) } // Read header entry h, err := c.readHeaderEntry() if err != nil { - return fmt.Errorf("%s read header entry error: %v", c.id, err) + return nil, fmt.Errorf("%s read header entry error: %v", c.id, err) + } + + return h, nil +} + +// sendEntryCmdWrapper sends CmdEntry command and reads packet type and decodes result entry. +func (c *StreamClient) sendEntryCmdWrapper(entryNum uint64) error { + if err := c.sendEntryCmd(entryNum); err != nil { + return err } - c.Header = *h + if re, err := c.readPacketAndDecodeResultEntry(); err != nil { + return fmt.Errorf("failed to retrieve the result entry: %w", err) + } else if err := re.GetError(); err != nil { + return err + } return nil } func (c *StreamClient) ExecutePerFile(bookmark *types.BookmarkProto, function func(file *types.FileEntry) error) error { // Get header from server - if err := c.GetHeader(); err != nil { + header, err := c.GetHeader() + if err != nil { return fmt.Errorf("%s get header error: %v", c.id, err) } @@ -169,7 +294,7 @@ func (c *StreamClient) ExecutePerFile(bookmark *types.BookmarkProto, function fu return fmt.Errorf("failed to marshal bookmark: %v", err) } - if err := c.initiateDownloadBookmark(protoBookmark); err != nil { + if _, err := c.initiateDownloadBookmark(protoBookmark); err != nil { return err } count := uint64(0) @@ -181,10 +306,10 @@ func (c *StreamClient) ExecutePerFile(bookmark *types.BookmarkProto, function fu fmt.Println("Entries read count: ", count) default: } - if c.Header.TotalEntries == count { + if header.TotalEntries == count { break } - file, err := c.readFileEntry() + file, err := c.NextFileEntry() if err != nil { return fmt.Errorf("reading file entry: %v", err) } @@ -203,7 +328,7 @@ func (c *StreamClient) EnsureConnected() (bool, error) { if err := c.tryReConnect(); err != nil { return false, fmt.Errorf("failed to reconnect the datastream client: %w", err) } - log.Info("[datastream_client] Datastream client connected.") + c.entryChan = make(chan interface{}, 100000) } return true, nil @@ -229,7 +354,7 @@ func (c *StreamClient) ReadAllEntriesToChannel() error { } // send start command - if err := c.initiateDownloadBookmark(protoBookmark); err != nil { + if _, err := c.initiateDownloadBookmark(protoBookmark); err != nil { return err } @@ -253,37 +378,31 @@ func (c *StreamClient) ReadAllEntriesToChannel() error { } // runs the prerequisites for entries download -func (c *StreamClient) initiateDownloadBookmark(bookmark []byte) error { - // send start command - if err := c.sendStartBookmarkCmd(bookmark); err != nil { - return err +func (c *StreamClient) initiateDownloadBookmark(bookmark []byte) (*types.ResultEntry, error) { + // send CmdStartBookmark command + if err := c.sendBookmarkCmd(bookmark, true); err != nil { + return nil, err } - if err := c.afterStartCommand(); err != nil { - return fmt.Errorf("after start command error: %v", err) + re, err := c.afterStartCommand() + if err != nil { + return re, fmt.Errorf("after start command error: %v", err) } - return nil + return re, nil } -func (c *StreamClient) afterStartCommand() error { - // Read packet - packet, err := readBuffer(c.conn, 1) - if err != nil { - return fmt.Errorf("read buffer error %v", err) - } - - // Read server result entry for the command - r, err := c.readResultEntry(packet) +func (c *StreamClient) afterStartCommand() (*types.ResultEntry, error) { + re, err := c.readPacketAndDecodeResultEntry() if err != nil { - return fmt.Errorf("read result entry error: %v", err) + return nil, err } - if err := r.GetError(); err != nil { - return fmt.Errorf("got Result error code %d: %v", r.ErrorNum, err) + if err := re.GetError(); err != nil { + return re, fmt.Errorf("got Result error code %d: %v", re.ErrorNum, err) } - return nil + return re, nil } // reads all entries from the server and sends them to a channel @@ -304,7 +423,7 @@ LOOP: c.conn.SetReadDeadline(time.Now().Add(c.checkTimeout)) } - parsedProto, localErr := c.readParsedProto() + parsedProto, localErr := ReadParsedProto(c) if localErr != nil { err = localErr break @@ -339,11 +458,13 @@ func (c *StreamClient) tryReConnect() error { for i := 0; i < 50; i++ { if c.conn != nil { if err := c.conn.Close(); err != nil { + log.Warn(fmt.Sprintf("[%d. iteration] failed to close the DS connection: %s", i+1, err)) return err } c.conn = nil } if err = c.Start(); err != nil { + log.Warn(fmt.Sprintf("[%d. iteration] failed to start the DS connection: %s", i+1, err)) time.Sleep(5 * time.Second) continue } @@ -353,16 +474,24 @@ func (c *StreamClient) tryReConnect() error { return err } -func (c *StreamClient) readParsedProto() ( +type FileEntryIterator interface { + NextFileEntry() (*types.FileEntry, error) +} + +func ReadParsedProto(iterator FileEntryIterator) ( parsedEntry interface{}, err error, ) { - file, err := c.readFileEntry() + file, err := iterator.NextFileEntry() if err != nil { - err = fmt.Errorf("read file entry error: %v", err) + err = fmt.Errorf("read file entry error: %w", err) return } + if file == nil { + return nil, nil + } + switch file.EntryType { case types.BookmarkEntryType: parsedEntry, err = types.UnmarshalBookmark(file.Data) @@ -384,7 +513,7 @@ func (c *StreamClient) readParsedProto() ( var l2Tx *types.L2TransactionProto LOOP: for { - if innerFile, err = c.readFileEntry(); err != nil { + if innerFile, err = iterator.NextFileEntry(); err != nil { return } @@ -428,8 +557,11 @@ func (c *StreamClient) readParsedProto() ( l2Block.L2Txs = txs parsedEntry = l2Block return + case types.EntryTypeL2BlockEnd: + log.Debug(fmt.Sprintf("retrieved EntryTypeL2BlockEnd: %+v", file)) + return case types.EntryTypeL2Tx: - err = fmt.Errorf("unexpected l2Tx out of block") + err = errors.New("unexpected L2 tx entry, found outside of block") default: err = fmt.Errorf("unexpected entry type: %d", file.EntryType) } @@ -438,15 +570,16 @@ func (c *StreamClient) readParsedProto() ( // reads file bytes from socket and tries to parse them // returns the parsed FileEntry -func (c *StreamClient) readFileEntry() (file *types.FileEntry, err error) { +func (c *StreamClient) NextFileEntry() (file *types.FileEntry, err error) { // Read packet type packet, err := readBuffer(c.conn, 1) if err != nil { return file, fmt.Errorf("failed to read packet type: %v", err) } + packetType := packet[0] // Check packet type - if packet[0] == PtResult { + if packetType == PtResult { // Read server result entry for the command r, err := c.readResultEntry(packet) if err != nil { @@ -456,8 +589,8 @@ func (c *StreamClient) readFileEntry() (file *types.FileEntry, err error) { return file, fmt.Errorf("got Result error code %d: %v", r.ErrorNum, err) } return file, nil - } else if packet[0] != PtData { - return file, fmt.Errorf("error expecting data packet type %d and received %d", PtData, packet[0]) + } else if packetType != PtData && packetType != PtDataRsp { + return file, fmt.Errorf("expected data packet type %d or %d and received %d", PtData, PtDataRsp, packetType) } // Read the rest of fixed size fields @@ -465,6 +598,10 @@ func (c *StreamClient) readFileEntry() (file *types.FileEntry, err error) { if err != nil { return file, fmt.Errorf("error reading file bytes: %v", err) } + + if packetType != PtData { + packet[0] = PtData + } buffer = append(packet, buffer...) // Read variable field (data) @@ -485,6 +622,10 @@ func (c *StreamClient) readFileEntry() (file *types.FileEntry, err error) { return file, fmt.Errorf("decode file entry error: %v", err) } + if file.EntryType == types.EntryTypeNotFound { + return file, ErrFileEntryNotFound + } + return } @@ -550,3 +691,20 @@ func (c *StreamClient) readResultEntry(packet []byte) (re *types.ResultEntry, er return re, nil } + +// readPacketAndDecodeResultEntry reads the packet from the connection and tries to decode the ResultEntry from it. +func (c *StreamClient) readPacketAndDecodeResultEntry() (*types.ResultEntry, error) { + // Read packet + packet, err := readBuffer(c.conn, 1) + if err != nil { + return nil, fmt.Errorf("read buffer error: %w", err) + } + + // Read server result entry for the command + r, err := c.readResultEntry(packet) + if err != nil { + return nil, fmt.Errorf("read result entry error: %w", err) + } + + return r, nil +} diff --git a/zk/datastream/client/stream_client_test.go b/zk/datastream/client/stream_client_test.go index 026879aa424..f8fbd917519 100644 --- a/zk/datastream/client/stream_client_test.go +++ b/zk/datastream/client/stream_client_test.go @@ -1,17 +1,28 @@ package client import ( + "bytes" "context" + "encoding/binary" + "errors" "fmt" "net" + "sync" "testing" + "time" + "github.com/gateway-fm/cdk-erigon-lib/common" + "github.com/ledgerwatch/erigon/zk/datastream/proto/github.com/0xPolygonHermez/zkevm-node/state/datastream" "github.com/ledgerwatch/erigon/zk/datastream/types" "github.com/stretchr/testify/require" "gotest.tools/v3/assert" ) -func Test_readHeaderEntry(t *testing.T) { +const ( + streamTypeFieldName = "stream type" +) + +func TestStreamClientReadHeaderEntry(t *testing.T) { type testCase struct { name string input []byte @@ -35,7 +46,7 @@ func Test_readHeaderEntry(t *testing.T) { name: "Invalid byte array length", input: []byte{20, 21, 22, 23, 24, 20}, expectedResult: nil, - expectedError: fmt.Errorf("failed to read header bytes reading from server: unexpected EOF"), + expectedError: errors.New("failed to read header bytes reading from server: unexpected EOF"), }, } @@ -59,7 +70,7 @@ func Test_readHeaderEntry(t *testing.T) { } } -func Test_readResultEntry(t *testing.T) { +func TestStreamClientReadResultEntry(t *testing.T) { type testCase struct { name string input []byte @@ -93,13 +104,13 @@ func Test_readResultEntry(t *testing.T) { name: "Invalid byte array length", input: []byte{20, 21, 22, 23, 24, 20}, expectedResult: nil, - expectedError: fmt.Errorf("failed to read main result bytes reading from server: unexpected EOF"), + expectedError: errors.New("failed to read main result bytes reading from server: unexpected EOF"), }, { name: "Invalid error length", input: []byte{0, 0, 0, 12, 0, 0, 0, 0, 20, 21}, expectedResult: nil, - expectedError: fmt.Errorf("failed to read result errStr bytes reading from server: unexpected EOF"), + expectedError: errors.New("failed to read result errStr bytes reading from server: unexpected EOF"), }, } @@ -123,7 +134,7 @@ func Test_readResultEntry(t *testing.T) { } } -func Test_readFileEntry(t *testing.T) { +func TestStreamClientReadFileEntry(t *testing.T) { type testCase struct { name string input []byte @@ -158,18 +169,18 @@ func Test_readFileEntry(t *testing.T) { name: "Invalid packet type", input: []byte{5, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 45}, expectedResult: nil, - expectedError: fmt.Errorf("error expecting data packet type 2 and received 5"), + expectedError: errors.New("expected data packet type 2 or 254 and received 5"), }, { name: "Invalid byte array length", input: []byte{2, 21, 22, 23, 24, 20}, expectedResult: nil, - expectedError: fmt.Errorf("error reading file bytes: reading from server: unexpected EOF"), + expectedError: errors.New("error reading file bytes: reading from server: unexpected EOF"), }, { name: "Invalid data length", input: []byte{2, 0, 0, 0, 31, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 64}, expectedResult: nil, - expectedError: fmt.Errorf("error reading file data bytes: reading from server: unexpected EOF"), + expectedError: errors.New("error reading file data bytes: reading from server: unexpected EOF"), }, } for _, testCase := range testCases { @@ -185,9 +196,377 @@ func Test_readFileEntry(t *testing.T) { server.Close() }() - result, err := c.readFileEntry() + result, err := c.NextFileEntry() require.Equal(t, testCase.expectedError, err) assert.DeepEqual(t, testCase.expectedResult, result) }) } } + +func TestStreamClientReadParsedProto(t *testing.T) { + c := NewClient(context.Background(), "", 0, 0, 0) + serverConn, clientConn := net.Pipe() + c.conn = clientConn + defer func() { + serverConn.Close() + clientConn.Close() + }() + + l2Block, l2Txs := createL2BlockAndTransactions(t, 3, 1) + l2BlockProto := &types.L2BlockProto{L2Block: l2Block} + l2BlockRaw, err := l2BlockProto.Marshal() + require.NoError(t, err) + + l2Tx := l2Txs[0] + l2TxProto := &types.TxProto{Transaction: l2Tx} + l2TxRaw, err := l2TxProto.Marshal() + require.NoError(t, err) + + l2BlockEnd := &types.L2BlockEndProto{Number: l2Block.GetNumber()} + l2BlockEndRaw, err := l2BlockEnd.Marshal() + require.NoError(t, err) + + var ( + errCh = make(chan error) + wg sync.WaitGroup + ) + wg.Add(1) + + go func() { + defer wg.Done() + fileEntries := []*types.FileEntry{ + createFileEntry(t, types.EntryTypeL2Block, 1, l2BlockRaw), + createFileEntry(t, types.EntryTypeL2Tx, 2, l2TxRaw), + createFileEntry(t, types.EntryTypeL2BlockEnd, 3, l2BlockEndRaw), + } + for _, fe := range fileEntries { + _, writeErr := serverConn.Write(fe.Encode()) + if writeErr != nil { + errCh <- writeErr + break + } + } + }() + + go func() { + wg.Wait() + close(errCh) + }() + + parsedEntry, err := ReadParsedProto(c) + require.NoError(t, err) + serverErr := <-errCh + require.NoError(t, serverErr) + expectedL2Tx := types.ConvertToL2TransactionProto(l2Tx) + expectedL2Block := types.ConvertToFullL2Block(l2Block) + expectedL2Block.L2Txs = append(expectedL2Block.L2Txs, *expectedL2Tx) + require.Equal(t, expectedL2Block, parsedEntry) +} + +func TestStreamClientGetLatestL2Block(t *testing.T) { + serverConn, clientConn := net.Pipe() + defer func() { + serverConn.Close() + clientConn.Close() + }() + + c := NewClient(context.Background(), "", 0, 0, 0) + c.conn = clientConn + + expectedL2Block, _ := createL2BlockAndTransactions(t, 5, 0) + l2BlockProto := &types.L2BlockProto{L2Block: expectedL2Block} + l2BlockRaw, err := l2BlockProto.Marshal() + require.NoError(t, err) + + var ( + errCh = make(chan error) + wg sync.WaitGroup + ) + wg.Add(1) + + // Prepare the server to send responses in a separate goroutine + go func() { + defer wg.Done() + + // Read the Command + if err := readAndValidateUint(t, serverConn, uint64(CmdHeader), "command"); err != nil { + errCh <- err + return + } + + // Read the StreamType + if err := readAndValidateUint(t, serverConn, uint64(StSequencer), streamTypeFieldName); err != nil { + errCh <- err + return + } + + // Write ResultEntry + re := createResultEntry(t) + _, err = serverConn.Write(re.Encode()) + if err != nil { + errCh <- fmt.Errorf("failed to write result entry to the connection: %w", err) + } + + // Write HeaderEntry + he := &types.HeaderEntry{ + PacketType: uint8(CmdHeader), + HeadLength: types.HeaderSize, + Version: 2, + SystemId: 1, + StreamType: types.StreamType(StSequencer), + TotalEntries: 4, + } + _, err = serverConn.Write(he.Encode()) + if err != nil { + errCh <- fmt.Errorf("failed to write header entry to the connection: %w", err) + } + + // Read the Command + if err := readAndValidateUint(t, serverConn, uint64(CmdEntry), "command"); err != nil { + errCh <- err + return + } + + // Read the StreamType + if err := readAndValidateUint(t, serverConn, uint64(StSequencer), streamTypeFieldName); err != nil { + errCh <- err + return + } + + // Read the EntryNumber + if err := readAndValidateUint(t, serverConn, he.TotalEntries-1, "entry number"); err != nil { + errCh <- err + return + } + + // Write the ResultEntry + _, err = serverConn.Write(re.Encode()) + if err != nil { + errCh <- fmt.Errorf("failed to write result entry to the connection: %w", err) + return + } + + // Write the FileEntry containing the L2 block information + fe := createFileEntry(t, types.EntryTypeL2Block, 1, l2BlockRaw) + _, err = serverConn.Write(fe.Encode()) + if err != nil { + errCh <- fmt.Errorf("failed to write the l2 block file entry to the connection: %w", err) + return + } + + serverConn.Close() + }() + + go func() { + wg.Wait() + close(errCh) + }() + + // ACT + l2Block, err := c.GetLatestL2Block() + require.NoError(t, err) + + // ASSERT + serverErr := <-errCh + require.NoError(t, serverErr) + + expectedFullL2Block := types.ConvertToFullL2Block(expectedL2Block) + require.Equal(t, expectedFullL2Block, l2Block) +} + +func TestStreamClientGetL2BlockByNumber(t *testing.T) { + const blockNum = uint64(5) + + serverConn, clientConn := net.Pipe() + defer func() { + serverConn.Close() + clientConn.Close() + }() + + c := NewClient(context.Background(), "", 0, 0, 0) + c.conn = clientConn + + bookmark := types.NewBookmarkProto(blockNum, datastream.BookmarkType_BOOKMARK_TYPE_L2_BLOCK) + bookmarkRaw, err := bookmark.Marshal() + require.NoError(t, err) + + expectedL2Block, l2Txs := createL2BlockAndTransactions(t, blockNum, 3) + l2BlockProto := &types.L2BlockProto{L2Block: expectedL2Block} + l2BlockRaw, err := l2BlockProto.Marshal() + require.NoError(t, err) + + l2TxsRaw := make([][]byte, len(l2Txs)) + for i, l2Tx := range l2Txs { + l2TxProto := &types.TxProto{Transaction: l2Tx} + l2TxRaw, err := l2TxProto.Marshal() + require.NoError(t, err) + l2TxsRaw[i] = l2TxRaw + } + + l2BlockEnd := &types.L2BlockEndProto{Number: expectedL2Block.GetNumber()} + l2BlockEndRaw, err := l2BlockEnd.Marshal() + require.NoError(t, err) + + errCh := make(chan error) + + createServerResponses := func(t *testing.T, serverConn net.Conn, bookmarkRaw, l2BlockRaw []byte, l2TxsRaw [][]byte, l2BlockEndRaw []byte, errCh chan error) { + defer func() { + close(errCh) + serverConn.Close() + }() + + // Read the command + if err := readAndValidateUint(t, serverConn, uint64(CmdStartBookmark), "command"); err != nil { + errCh <- err + return + } + + // Read the stream type + if err := readAndValidateUint(t, serverConn, uint64(StSequencer), streamTypeFieldName); err != nil { + errCh <- err + return + } + + // Read the bookmark length + if err := readAndValidateUint(t, serverConn, uint32(len(bookmarkRaw)), "bookmark length"); err != nil { + errCh <- err + return + } + + // Read the actual bookmark + actualBookmarkRaw, err := readBuffer(serverConn, uint32(len(bookmarkRaw))) + if err != nil { + errCh <- err + return + } + if !bytes.Equal(bookmarkRaw, actualBookmarkRaw) { + errCh <- fmt.Errorf("mismatch between expected %v and actual bookmark %v", bookmarkRaw, actualBookmarkRaw) + return + } + + // Write ResultEntry + re := createResultEntry(t) + if _, err := serverConn.Write(re.Encode()); err != nil { + errCh <- err + return + } + + // Write File entries (EntryTypeL2Block, EntryTypeL2Tx and EntryTypeL2BlockEnd) + fileEntries := make([]*types.FileEntry, 0, len(l2TxsRaw)+2) + fileEntries = append(fileEntries, createFileEntry(t, types.EntryTypeL2Block, 1, l2BlockRaw)) + entryNum := uint64(2) + for _, l2TxRaw := range l2TxsRaw { + fileEntries = append(fileEntries, createFileEntry(t, types.EntryTypeL2Tx, entryNum, l2TxRaw)) + entryNum++ + } + fileEntries = append(fileEntries, createFileEntry(t, types.EntryTypeL2BlockEnd, entryNum, l2BlockEndRaw)) + + for _, fe := range fileEntries { + if _, err := serverConn.Write(fe.Encode()); err != nil { + errCh <- err + return + } + } + + } + + go createServerResponses(t, serverConn, bookmarkRaw, l2BlockRaw, l2TxsRaw, l2BlockEndRaw, errCh) + + l2Block, errCode, err := c.GetL2BlockByNumber(blockNum) + require.NoError(t, err) + require.Equal(t, types.CmdErrOK, errCode) + + serverErr := <-errCh + require.NoError(t, serverErr) + + l2TxsProto := make([]types.L2TransactionProto, len(l2Txs)) + for i, tx := range l2Txs { + l2TxProto := types.ConvertToL2TransactionProto(tx) + l2TxsProto[i] = *l2TxProto + } + expectedFullL2Block := types.ConvertToFullL2Block(expectedL2Block) + expectedFullL2Block.L2Txs = l2TxsProto + require.Equal(t, expectedFullL2Block, l2Block) +} + +// readAndValidateUint reads the uint value and validates it against expected value from the connection in order to unblock future write operations +func readAndValidateUint(t *testing.T, conn net.Conn, expected interface{}, paramName string) error { + t.Helper() + + var length uint32 + switch expected.(type) { + case uint64: + length = 8 + case uint32: + length = 4 + default: + return fmt.Errorf("unsupported expected type for %s: %T", paramName, expected) + } + + valueRaw, err := readBuffer(conn, length) + if err != nil { + return fmt.Errorf("failed to read %s parameter: %w", paramName, err) + } + + switch expectedValue := expected.(type) { + case uint64: + value := binary.BigEndian.Uint64(valueRaw) + if value != expectedValue { + return fmt.Errorf("%s parameter value mismatch between expected %d and actual %d", paramName, expectedValue, value) + } + case uint32: + value := binary.BigEndian.Uint32(valueRaw) + if value != expectedValue { + return fmt.Errorf("%s parameter value mismatch between expected %d and actual %d", paramName, expectedValue, value) + } + } + + return nil +} + +// createFileEntry is a helper function that creates FileEntry +func createFileEntry(t *testing.T, entryType types.EntryType, num uint64, data []byte) *types.FileEntry { + t.Helper() + return &types.FileEntry{ + PacketType: PtData, + Length: types.FileEntryMinSize + uint32(len(data)), + EntryType: entryType, + EntryNum: num, + Data: data, + } +} + +func createResultEntry(t *testing.T) *types.ResultEntry { + t.Helper() + return &types.ResultEntry{ + PacketType: PtResult, + ErrorNum: types.CmdErrOK, + Length: types.ResultEntryMinSize, + ErrorStr: nil, + } +} + +// createL2BlockAndTransactions creates a single L2 block with the transactions +func createL2BlockAndTransactions(t *testing.T, blockNum uint64, txnCount int) (*datastream.L2Block, []*datastream.Transaction) { + t.Helper() + txns := make([]*datastream.Transaction, 0, txnCount) + l2Block := &datastream.L2Block{ + Number: blockNum, + BatchNumber: 1, + Timestamp: uint64(time.Now().UnixMilli()), + Hash: common.HexToHash("0x123456987654321").Bytes(), + BlockGasLimit: 1000000000, + } + + for i := 0; i < txnCount; i++ { + txns = append(txns, + &datastream.Transaction{ + L2BlockNumber: l2Block.GetNumber(), + Index: uint64(i), + IsValid: true, + Debug: &datastream.Debug{Message: fmt.Sprintf("Hello %d. transaction!", i+1)}, + }) + } + + return l2Block, txns +} diff --git a/zk/datastream/server/data_stream_server.go b/zk/datastream/server/data_stream_server.go index bddd671e9b9..5968c5e19cb 100644 --- a/zk/datastream/server/data_stream_server.go +++ b/zk/datastream/server/data_stream_server.go @@ -6,11 +6,13 @@ import ( "github.com/0xPolygonHermez/zkevm-data-streamer/datastreamer" zktypes "github.com/ledgerwatch/erigon/zk/types" "github.com/ledgerwatch/erigon/zk/utils" + "github.com/ledgerwatch/log/v3" libcommon "github.com/gateway-fm/cdk-erigon-lib/common" "github.com/gateway-fm/cdk-erigon-lib/kv" "github.com/ledgerwatch/erigon/core/rawdb" eritypes "github.com/ledgerwatch/erigon/core/types" + "github.com/ledgerwatch/erigon/zk/datastream/client" "github.com/ledgerwatch/erigon/zk/datastream/proto/github.com/0xPolygonHermez/zkevm-node/state/datastream" "github.com/ledgerwatch/erigon/zk/datastream/types" ) @@ -26,10 +28,12 @@ type DbReader interface { GetBlockInfoRoot(blockNumber uint64) (libcommon.Hash, error) GetIntermediateTxStateRoot(blockNumber uint64, txHash libcommon.Hash) (libcommon.Hash, error) GetEffectiveGasPricePercentage(txHash libcommon.Hash) (uint8, error) - GetHighestBlockInBatch(batchNumber uint64) (uint64, error) + GetHighestBlockInBatch(batchNumber uint64) (uint64, bool, error) GetInvalidBatch(batchNumber uint64) (bool, error) GetBatchNoByL2Block(blockNumber uint64) (uint64, error) CheckBatchNoByL2Block(l2BlockNo uint64) (uint64, bool, error) + GetPreviousIndexBlock(blockNumber uint64) (uint64, uint64, bool, error) + GetBatchEnd(l2BlockNo uint64) (bool, error) } type BookmarkType byte @@ -176,6 +180,7 @@ func createBlockWithBatchCheckStreamEntriesProto( chainId, forkId uint64, shouldSkipBatchEndEntry bool, + checkBatchEnd bool, ) (*DataStreamEntries, error) { var err error var endEntriesProto []DataStreamEntryProto @@ -193,7 +198,7 @@ func createBlockWithBatchCheckStreamEntriesProto( } // the genesis we insert fully, so we would have to skip closing it if !shouldSkipBatchEndEntry { - localExitRoot, err := utils.GetBatchLocalExitRootFromSCStorageForLatestBlock(batchNumber, reader, tx) + localExitRoot, err := utils.GetBatchLocalExitRootFromSCStorageForLatestBlock(lastBatchNumber, reader, tx) if err != nil { return nil, err } @@ -208,21 +213,12 @@ func createBlockWithBatchCheckStreamEntriesProto( } } - blockNum := block.NumberU64() - - l1InfoTreeMinTimestamps := make(map[uint64]uint64) - deltaTimestamp := block.Time() - lastBlock.Time() - if blockNum == 1 { - deltaTimestamp = block.Time() - l1InfoTreeMinTimestamps[0] = 0 - } - - if blockEntries, err = createFullBlockStreamEntriesProto(reader, tx, block, block.Transactions(), forkId, deltaTimestamp, batchNumber, l1InfoTreeMinTimestamps); err != nil { + if blockEntries, err = createFullBlockStreamEntriesProto(reader, tx, block, lastBlock, block.Transactions(), forkId, batchNumber, make(map[uint64]uint64)); err != nil { return nil, err } if blockEntries.Size() == 0 { - return nil, fmt.Errorf("didn't create any entries for block %d", blockNum) + return nil, fmt.Errorf("didn't create any entries for block %d", block.NumberU64()) } entries := NewDataStreamEntries(len(endEntriesProto) + startEntriesProto.Size() + blockEntries.Size()) @@ -230,21 +226,56 @@ func createBlockWithBatchCheckStreamEntriesProto( entries.AddMany(startEntriesProto.Entries()) entries.AddMany(blockEntries.Entries()) + // if we're at the latest block known to the stream we need to check if it is a batch end + // and write the end entry. This scenario occurs when the sequencer is running with a stop + // height and so never moves to the next batch, and we need to close it off + if checkBatchEnd { + isEnd, err := reader.GetBatchEnd(block.NumberU64()) + if err != nil { + return nil, err + } + if isEnd { + gers, err := reader.GetBatchGlobalExitRootsProto(lastBatchNumber, batchNumber) + if err != nil { + return nil, err + } + localExitRoot, err := utils.GetBatchLocalExitRootFromSCStorageForLatestBlock(batchNumber, reader, tx) + if err != nil { + return nil, err + } + lastBlockRoot := block.Root() + finalEndEntries, err := addBatchEndEntriesProto(batchNumber, &lastBlockRoot, gers, &localExitRoot) + if err != nil { + return nil, err + } + newEntries := NewDataStreamEntries(entries.Size() + len(finalEndEntries)) + newEntries.AddMany(entries.Entries()) + newEntries.AddMany(finalEndEntries) + entries = newEntries + } + } + return entries, nil } func createFullBlockStreamEntriesProto( reader DbReader, tx kv.Tx, - block *eritypes.Block, + block, + lastBlock *eritypes.Block, filteredTransactions eritypes.Transactions, forkId, - deltaTimestamp, batchNumber uint64, l1InfoTreeMinTimestamps map[uint64]uint64, ) (*DataStreamEntries, error) { - entries := NewDataStreamEntries(len(filteredTransactions) + 3) // block bookmark + block + block end blockNum := block.NumberU64() + deltaTimestamp := block.Time() - lastBlock.Time() + if blockNum == 1 { + deltaTimestamp = block.Time() + l1InfoTreeMinTimestamps[0] = 0 + } + + entries := NewDataStreamEntries(len(filteredTransactions) + 3) // block bookmark + block + block end // L2 BLOCK BOOKMARK entries.Add(newL2BlockBookmarkEntryProto(blockNum)) @@ -263,6 +294,14 @@ func createFullBlockStreamEntriesProto( } if l1InfoIndex > 0 { + prevIndexBlock, prevIndex, found, err := reader.GetPreviousIndexBlock(blockNum) + if err != nil { + return nil, err + } + if found && prevIndex >= l1InfoIndex { + log.Warn("1 info index not bigger than previous index", "prevIndex", prevIndex, "prevBlock", prevIndexBlock, "l1InfoIndex", l1InfoIndex, "currentBlock", blockNum) + } + // get the l1 info data, so we can add the min timestamp to the map l1Info, err := reader.GetL1InfoTreeUpdate(l1InfoIndex) if err != nil { @@ -361,18 +400,12 @@ func BuildWholeBatchStreamEntriesProto( for _, block := range blocks { blockNum := block.NumberU64() - deltaTimestamp := block.Time() - lastBlock.Time() - if blockNum == 1 { - deltaTimestamp = block.Time() - l1InfoTreeMinTimestamps[0] = 0 - } - txForBlock, found := txsPerBlock[blockNum] if !found { return nil, fmt.Errorf("no transactions array found for block %d", blockNum) } - blockEntries, err := createFullBlockStreamEntriesProto(reader, tx, &block, txForBlock, forkId, deltaTimestamp, batchNumber, l1InfoTreeMinTimestamps) + blockEntries, err := createFullBlockStreamEntriesProto(reader, tx, &block, &lastBlock, txForBlock, forkId, batchNumber, l1InfoTreeMinTimestamps) if err != nil { return nil, err } @@ -567,3 +600,88 @@ func (srv *DataStreamServer) getLastEntryOfType(entryType datastreamer.EntryType return emtryEntry, false, nil } + +type dataStreamServerIterator struct { + stream *datastreamer.StreamServer + curEntryNum uint64 + header uint64 +} + +func newDataStreamServerIterator(stream *datastreamer.StreamServer, start uint64) *dataStreamServerIterator { + return &dataStreamServerIterator{ + stream: stream, + curEntryNum: start, + header: stream.GetHeader().TotalEntries - 1, + } +} + +func (it *dataStreamServerIterator) NextFileEntry() (entry *types.FileEntry, err error) { + if it.curEntryNum > it.header { + return nil, nil + } + + var fileEntry datastreamer.FileEntry + fileEntry, err = it.stream.GetEntry(it.curEntryNum) + if err != nil { + return nil, err + } + + it.curEntryNum += 1 + + return &types.FileEntry{ + PacketType: uint8(fileEntry.Type), + Length: fileEntry.Length, + EntryType: types.EntryType(fileEntry.Type), + EntryNum: fileEntry.Number, + Data: fileEntry.Data, + }, nil +} + +func (srv *DataStreamServer) ReadBatches(start uint64, end uint64) ([][]*types.FullL2Block, error) { + bookmark := types.NewBookmarkProto(start, datastream.BookmarkType_BOOKMARK_TYPE_BATCH) + marshalled, err := bookmark.Marshal() + if err != nil { + return nil, err + } + + entryNum, err := srv.stream.GetBookmark(marshalled) + + if err != nil { + return nil, err + } + + iterator := newDataStreamServerIterator(srv.stream, entryNum) + + return ReadBatches(iterator, start, end) +} + +func ReadBatches(iterator client.FileEntryIterator, start uint64, end uint64) ([][]*types.FullL2Block, error) { + batches := make([][]*types.FullL2Block, end-start+1) + +LOOP_ENTRIES: + for { + parsedProto, err := client.ReadParsedProto(iterator) + if err != nil { + return nil, err + } + + if parsedProto == nil { + break + } + + switch parsedProto := parsedProto.(type) { + case *types.BatchStart: + batches[parsedProto.Number-start] = []*types.FullL2Block{} + case *types.BatchEnd: + if parsedProto.Number == end { + break LOOP_ENTRIES + } + case *types.FullL2Block: + batches[parsedProto.BatchNumber-start] = append(batches[parsedProto.BatchNumber-start], parsedProto) + default: + continue + } + } + + return batches, nil +} diff --git a/zk/datastream/server/data_stream_server_utils.go b/zk/datastream/server/data_stream_server_utils.go index b25550ace7f..68e989f4564 100644 --- a/zk/datastream/server/data_stream_server_utils.go +++ b/zk/datastream/server/data_stream_server_utils.go @@ -226,8 +226,26 @@ func getBatchTypeAndFork(batchNumber uint64, reader DbReader) (datastream.BatchT } else { batchType = datastream.BatchType_BATCH_TYPE_REGULAR } + fork, err := reader.GetForkId(batchNumber) - return batchType, fork, err + if err != nil { + return datastream.BatchType_BATCH_TYPE_UNSPECIFIED, 0, err + } + + if fork == 0 && batchNumber > 1 { + // iterate backwards, this only happens for empty batches pre etrog + for batchNumber > 1 { + batchNumber-- + fork, err = reader.GetForkId(batchNumber) + if err != nil { + return datastream.BatchType_BATCH_TYPE_UNSPECIFIED, 0, err + } + if fork != 0 { + break + } + } + } + return batchType, fork, nil } func addBatchStartEntries(reader DbReader, batchNum, chainId uint64) ([]DataStreamEntryProto, error) { diff --git a/zk/datastream/server/datastream_populate.go b/zk/datastream/server/datastream_populate.go index 157c750e7bd..89bafa9beb5 100644 --- a/zk/datastream/server/datastream_populate.go +++ b/zk/datastream/server/datastream_populate.go @@ -2,6 +2,7 @@ package server import ( "context" + "errors" "fmt" "time" @@ -13,6 +14,7 @@ import ( "github.com/ledgerwatch/erigon/zk/hermez_db" "github.com/ledgerwatch/erigon/zk/utils" "github.com/ledgerwatch/log/v3" + "github.com/ledgerwatch/erigon/eth/stagedsync/stages" ) const ( @@ -107,12 +109,12 @@ func (srv *DataStreamServer) WriteBlocksToStreamConsecutively( ////////// latestbatchNum, err := reader.GetBatchNoByL2Block(from - 1) - if err != nil { + if err != nil && !errors.Is(err, hermez_db.ErrorNotStored) { return err } batchNum, err := reader.GetBatchNoByL2Block(from) - if err != nil { + if err != nil && !errors.Is(err, hermez_db.ErrorNotStored) { return err } @@ -142,6 +144,11 @@ func (srv *DataStreamServer) WriteBlocksToStreamConsecutively( entries := make([]DataStreamEntryProto, 0, insertEntryCount) var forkId uint64 + + batchesProgress, err := stages.GetStageProgress(tx, stages.Batches) + if err != nil { + return err + } LOOP: for currentBlockNumber := from; currentBlockNumber <= to; currentBlockNumber++ { select { @@ -160,7 +167,7 @@ LOOP: } batchNum, err := reader.GetBatchNoByL2Block(currentBlockNumber) - if err != nil { + if err != nil && !errors.Is(err, hermez_db.ErrorNotStored) { return err } @@ -172,7 +179,9 @@ LOOP: } } - blockEntries, err := createBlockWithBatchCheckStreamEntriesProto(reader, tx, block, lastBlock, batchNum, latestbatchNum, srv.chainId, forkId, islastEntrybatchEnd) + checkBatchEnd := currentBlockNumber == batchesProgress + + blockEntries, err := createBlockWithBatchCheckStreamEntriesProto(reader, tx, block, lastBlock, batchNum, latestbatchNum, srv.chainId, forkId, islastEntrybatchEnd, checkBatchEnd) if err != nil { return err } @@ -250,14 +259,7 @@ func (srv *DataStreamServer) WriteBlockWithBatchStartToStream( } } - l1InfoTreeMinTimestamps := make(map[uint64]uint64) - deltaTimestamp := block.Time() - prevBlock.Time() - if blockNum == 1 { - deltaTimestamp = block.Time() - l1InfoTreeMinTimestamps[0] = 0 - } - - blockEntries, err := createFullBlockStreamEntriesProto(reader, tx, &block, block.Transactions(), forkId, deltaTimestamp, batchNum, make(map[uint64]uint64)) + blockEntries, err := createFullBlockStreamEntriesProto(reader, tx, &block, &prevBlock, block.Transactions(), forkId, batchNum, make(map[uint64]uint64)) if err != nil { return err } @@ -297,7 +299,7 @@ func (srv *DataStreamServer) UnwindIfNecessary(logPrefix string, reader DbReader log.Warn(fmt.Sprintf("[%s] Datastream must unwind to batch", logPrefix), "prevBlockBatchNum", prevBlockBatchNum, "batchNum", batchNum) //get latest block in prev batch - lastBlockInPrevbatch, err := reader.GetHighestBlockInBatch(prevBlockBatchNum) + lastBlockInPrevbatch, _, err := reader.GetHighestBlockInBatch(prevBlockBatchNum) if err != nil { return err } @@ -365,7 +367,7 @@ func (srv *DataStreamServer) WriteGenesisToStream( tx kv.Tx, ) error { batchNo, err := reader.GetBatchNoByL2Block(0) - if err != nil { + if err != nil && !errors.Is(err, hermez_db.ErrorNotStored) { return err } @@ -384,6 +386,7 @@ func (srv *DataStreamServer) WriteGenesisToStream( l2BlockBookmark := newL2BlockBookmarkEntryProto(genesis.NumberU64()) l2Block := newL2BlockProto(genesis, genesis.Hash().Bytes(), batchNo, ger, 0, 0, common.Hash{}, 0, common.Hash{}) + l2BlockEnd := newL2BlockEndProto(0) batchStart := newBatchStartProto(batchNo, srv.chainId, GenesisForkId, datastream.BatchType_BATCH_TYPE_REGULAR) ler, err := utils.GetBatchLocalExitRootFromSCStorageForLatestBlock(0, reader, tx) @@ -392,7 +395,7 @@ func (srv *DataStreamServer) WriteGenesisToStream( } batchEnd := newBatchEndProto(ler, genesis.Root(), 0) - if err = srv.commitEntriesToStreamProto([]DataStreamEntryProto{batchBookmark, batchStart, l2BlockBookmark, l2Block, batchEnd}); err != nil { + if err = srv.commitEntriesToStreamProto([]DataStreamEntryProto{batchBookmark, batchStart, l2BlockBookmark, l2Block, l2BlockEnd, batchEnd}); err != nil { return err } diff --git a/zk/datastream/types/entry_type.go b/zk/datastream/types/entry_type.go index 827aabfb15c..49a3909b67b 100644 --- a/zk/datastream/types/entry_type.go +++ b/zk/datastream/types/entry_type.go @@ -1,5 +1,7 @@ package types +import "math" + type EntryType uint32 var ( @@ -11,4 +13,5 @@ var ( EntryTypeGerUpdate EntryType = 5 EntryTypeL2BlockEnd EntryType = 6 BookmarkEntryType EntryType = 176 + EntryTypeNotFound EntryType = math.MaxUint32 ) diff --git a/zk/datastream/types/file.go b/zk/datastream/types/file.go index 41c043caa22..20417460dfe 100644 --- a/zk/datastream/types/file.go +++ b/zk/datastream/types/file.go @@ -34,7 +34,7 @@ func (f *FileEntry) IsBookmarkBlock() bool { } func (f *FileEntry) IsL2BlockEnd() bool { - return uint32(f.EntryType) == uint32(6) //TODO: fix once it is added in the lib + return uint32(f.EntryType) == uint32(datastream.EntryType_ENTRY_TYPE_L2_BLOCK_END) } func (f *FileEntry) IsL2Block() bool { return uint32(f.EntryType) == uint32(datastream.EntryType_ENTRY_TYPE_L2_BLOCK) @@ -62,6 +62,17 @@ func (f *FileEntry) IsGerUpdate() bool { return f.EntryType == EntryTypeGerUpdate } +// Encode encodes file entry to the binary format +func (f *FileEntry) Encode() []byte { + be := make([]byte, 1) + be[0] = f.PacketType + be = binary.BigEndian.AppendUint32(be, f.Length) + be = binary.BigEndian.AppendUint32(be, uint32(f.EntryType)) + be = binary.BigEndian.AppendUint64(be, f.EntryNum) + be = append(be, f.Data...) //nolint:makezero + return be +} + // Decode/convert from binary bytes slice to FileEntry type func DecodeFileEntry(b []byte) (*FileEntry, error) { if uint32(len(b)) < FileEntryMinSize { diff --git a/zk/datastream/types/header.go b/zk/datastream/types/header.go index 5b393a17794..9af3d368e36 100644 --- a/zk/datastream/types/header.go +++ b/zk/datastream/types/header.go @@ -5,14 +5,16 @@ import ( "fmt" ) -const HeaderSize = 38 -const HeaderSizePreEtrog = 29 +const ( + HeaderSize = 38 + HeaderSizePreEtrog = 29 +) type StreamType uint64 type HeaderEntry struct { PacketType uint8 // 1:Header - HeadLength uint32 // 38 oe 29 + HeadLength uint32 // 38 or 29 Version uint8 SystemId uint64 StreamType StreamType // 1:Sequencer @@ -20,6 +22,19 @@ type HeaderEntry struct { TotalEntries uint64 // Total number of data entries (entry type 2) } +// Encode encodes given HeaderEntry into a binary format +func (e *HeaderEntry) Encode() []byte { + be := make([]byte, 1) + be[0] = e.PacketType + be = binary.BigEndian.AppendUint32(be, e.HeadLength) + be = append(be, e.Version) //nolint:makezero + be = binary.BigEndian.AppendUint64(be, e.SystemId) + be = binary.BigEndian.AppendUint64(be, uint64(e.StreamType)) + be = binary.BigEndian.AppendUint64(be, e.TotalLength) + be = binary.BigEndian.AppendUint64(be, e.TotalEntries) + return be +} + // Decode/convert from binary bytes slice to a header entry type func DecodeHeaderEntryPreEtrog(b []byte) (*HeaderEntry, error) { return &HeaderEntry{ diff --git a/zk/datastream/types/l2block_proto.go b/zk/datastream/types/l2block_proto.go index a36bf542166..c3cb4f3be89 100644 --- a/zk/datastream/types/l2block_proto.go +++ b/zk/datastream/types/l2block_proto.go @@ -73,21 +73,24 @@ func UnmarshalL2Block(data []byte) (*FullL2Block, error) { return nil, err } - l2Block := &FullL2Block{ - BatchNumber: block.BatchNumber, - L2BlockNumber: block.Number, - Timestamp: int64(block.Timestamp), - DeltaTimestamp: block.DeltaTimestamp, - L1InfoTreeIndex: block.L1InfotreeIndex, - GlobalExitRoot: libcommon.BytesToHash(block.GlobalExitRoot), - Coinbase: libcommon.BytesToAddress(block.Coinbase), - L1BlockHash: libcommon.BytesToHash(block.L1Blockhash), - L2Blockhash: libcommon.BytesToHash(block.Hash), - StateRoot: libcommon.BytesToHash(block.StateRoot), - BlockGasLimit: block.BlockGasLimit, - BlockInfoRoot: libcommon.BytesToHash(block.BlockInfoRoot), - Debug: ProcessDebug(block.Debug), - } + return ConvertToFullL2Block(&block), nil +} - return l2Block, nil +// ConvertToFullL2Block converts the datastream.L2Block to types.FullL2Block +func ConvertToFullL2Block(block *datastream.L2Block) *FullL2Block { + return &FullL2Block{ + BatchNumber: block.GetBatchNumber(), + L2BlockNumber: block.GetNumber(), + Timestamp: int64(block.GetTimestamp()), + DeltaTimestamp: block.GetDeltaTimestamp(), + L1InfoTreeIndex: block.GetL1InfotreeIndex(), + GlobalExitRoot: libcommon.BytesToHash(block.GetGlobalExitRoot()), + Coinbase: libcommon.BytesToAddress(block.GetCoinbase()), + L1BlockHash: libcommon.BytesToHash(block.GetL1Blockhash()), + L2Blockhash: libcommon.BytesToHash(block.GetHash()), + StateRoot: libcommon.BytesToHash(block.GetStateRoot()), + BlockGasLimit: block.GetBlockGasLimit(), + BlockInfoRoot: libcommon.BytesToHash(block.GetBlockInfoRoot()), + Debug: ProcessDebug(block.GetDebug()), + } } diff --git a/zk/datastream/types/result.go b/zk/datastream/types/result.go index 1db1061c0e2..1e6652cbb9d 100644 --- a/zk/datastream/types/result.go +++ b/zk/datastream/types/result.go @@ -12,11 +12,12 @@ const ( ResultEntryMinSize = uint32(9) // Command errors - CmdErrOK = 0 - CmdErrAlreadyStarted = 1 - CmdErrAlreadyStopped = 2 - CmdErrBadFromEntry = 3 - CmdErrInvalidCommand = 9 + CmdErrOK = 0 // CmdErrOK for no error + CmdErrAlreadyStarted = 1 // CmdErrAlreadyStarted for client already started error + CmdErrAlreadyStopped = 2 // CmdErrAlreadyStopped for client already stopped error + CmdErrBadFromEntry = 3 // CmdErrBadFromEntry for invalid starting entry number + CmdErrBadFromBookmark = 4 // CmdErrBadFromBookmark for invalid starting bookmark + CmdErrInvalidCommand = 9 // CmdErrInvalidCommand for invalid/unknown command error ) type ResultEntry struct { @@ -41,9 +42,18 @@ func (r *ResultEntry) GetError() error { return errors.New(string(r.ErrorStr)) } +// Encode encodes result entry to the binary format +func (r *ResultEntry) Encode() []byte { + be := make([]byte, 1) + be[0] = r.PacketType + be = binary.BigEndian.AppendUint32(be, r.Length) + be = binary.BigEndian.AppendUint32(be, r.ErrorNum) + be = append(be, r.ErrorStr...) //nolint:makezero + return be +} + // Decode/convert from binary bytes slice to an entry type func DecodeResultEntry(b []byte) (*ResultEntry, error) { - if uint32(len(b)) < ResultEntryMinSize { return &ResultEntry{}, fmt.Errorf("invalid result entry binary size. Expected: >=%d, got: %d", ResultEntryMinSize, len(b)) } diff --git a/zk/datastream/types/result_test.go b/zk/datastream/types/result_test.go index 20a1f6cbabc..bdbbf40c7d6 100644 --- a/zk/datastream/types/result_test.go +++ b/zk/datastream/types/result_test.go @@ -59,3 +59,17 @@ func TestResultDecode(t *testing.T) { }) } } + +func TestEncodeDecodeResult(t *testing.T) { + expectedResult := &ResultEntry{ + PacketType: 1, + Length: 19, + ErrorNum: 5, + ErrorStr: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + } + + resultRaw := expectedResult.Encode() + actualResult, err := DecodeResultEntry(resultRaw) + require.NoError(t, err) + require.Equal(t, expectedResult, actualResult) +} diff --git a/zk/datastream/types/tx_proto.go b/zk/datastream/types/tx_proto.go index c89145a2ab0..5d8c333ed77 100644 --- a/zk/datastream/types/tx_proto.go +++ b/zk/datastream/types/tx_proto.go @@ -1,9 +1,9 @@ package types import ( + libcommon "github.com/gateway-fm/cdk-erigon-lib/common" "github.com/ledgerwatch/erigon/zk/datastream/proto/github.com/0xPolygonHermez/zkevm-node/state/datastream" "google.golang.org/protobuf/proto" - libcommon "github.com/gateway-fm/cdk-erigon-lib/common" ) type TxProto struct { @@ -35,15 +35,18 @@ func UnmarshalTx(data []byte) (*L2TransactionProto, error) { return nil, err } - l2Tx := &L2TransactionProto{ - L2BlockNumber: tx.L2BlockNumber, - Index: tx.Index, - IsValid: tx.IsValid, - Encoded: tx.Encoded, - EffectiveGasPricePercentage: uint8(tx.EffectiveGasPricePercentage), - IntermediateStateRoot: libcommon.BytesToHash(tx.ImStateRoot), - Debug: ProcessDebug(tx.Debug), - } + return ConvertToL2TransactionProto(&tx), nil +} - return l2Tx, nil +// ConvertToL2TransactionProto converts transaction object from datastream.Transaction to types.L2TransactionProto +func ConvertToL2TransactionProto(tx *datastream.Transaction) *L2TransactionProto { + return &L2TransactionProto{ + L2BlockNumber: tx.GetL2BlockNumber(), + Index: tx.GetIndex(), + IsValid: tx.GetIsValid(), + Encoded: tx.GetEncoded(), + EffectiveGasPricePercentage: uint8(tx.GetEffectiveGasPricePercentage()), + IntermediateStateRoot: libcommon.BytesToHash(tx.GetImStateRoot()), + Debug: ProcessDebug(tx.GetDebug()), + } } diff --git a/zk/debug_tools/mdbx-data-browser/dbdata_retriever.go b/zk/debug_tools/mdbx-data-browser/dbdata_retriever.go index 41fed2fc9d3..d8d87e9bafa 100644 --- a/zk/debug_tools/mdbx-data-browser/dbdata_retriever.go +++ b/zk/debug_tools/mdbx-data-browser/dbdata_retriever.go @@ -154,11 +154,13 @@ func (d *DbDataRetriever) getHighestBlockInBatch(batchNum uint64) (*coreTypes.Bl return nil, nil } - blockNum, err := d.dbReader.GetHighestBlockInBatch(batchNum) + blockNum, found, err := d.dbReader.GetHighestBlockInBatch(batchNum) if err != nil { return nil, err } - + if !found { + return nil, fmt.Errorf("block not found in batch %d", batchNum) + } blockHash, err := rawdb.ReadCanonicalHash(d.tx, blockNum) if err != nil { return nil, err @@ -296,7 +298,9 @@ func (d *DbDataRetriever) GetBatchAffiliation(blocks []uint64) ([]*BatchAffiliat batchInfoMap := make(map[uint64]*BatchAffiliationInfo) for _, blockNum := range blocks { batchNum, err := d.dbReader.GetBatchNoByL2Block(blockNum) - if err != nil { + if errors.Is(err, hermez_db.ErrorNotStored) && !(blockNum == 0 && batchNum == 0) { + return nil, fmt.Errorf("batch is not found for block num %d", blockNum) + } else if err != nil { return nil, err } diff --git a/zk/debug_tools/test-contracts/contracts/Creates.sol b/zk/debug_tools/test-contracts/contracts/Creates.sol new file mode 100644 index 00000000000..cdd7c517bae --- /dev/null +++ b/zk/debug_tools/test-contracts/contracts/Creates.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.7.0 <0.9.0; + +contract Creates { + function opCreate(bytes memory bytecode, uint length) public returns(address) { + address addr; + assembly { + addr := create(0, 0xa0, length) + sstore(0x0, addr) + } + return addr; + } + + function opCreate2(bytes memory bytecode, uint length) public returns(address) { + address addr; + assembly { + addr := create2(0, 0xa0, length, 0x2) + sstore(0x0, addr) + } + return addr; + } + + function opCreate2Complex(bytes memory bytecode, uint length) public returns(address, uint256) { + uint256 number = add(1, 2); + + address addr; + assembly { + addr := create2(0, add(bytecode, 0x20), length, 0x2) + sstore(0x0, addr) + } + + number = add(2, 4); + + return (addr, number); + } + + function add(uint256 a, uint256 b) public pure returns(uint256) { + return a + b; + } + + function sendValue() public payable { + uint bal; + assembly{ + bal := add(bal,callvalue()) + sstore(0x1, bal) + } + } + + function opCreateValue(bytes memory bytecode, uint length) public payable returns(address) { + address addr; + assembly { + addr := create(500, 0xa0, length) + sstore(0x0, addr) + } + return addr; + } + + function opCreate2Value(bytes memory bytecode, uint length) public payable returns(address) { + address addr; + assembly { + addr := create2(300, 0xa0, length, 0x55555) + sstore(0x0, addr) + } + return addr; + } +} \ No newline at end of file diff --git a/zk/debug_tools/test-contracts/package.json b/zk/debug_tools/test-contracts/package.json index 25a3edd0745..06514e69be1 100644 --- a/zk/debug_tools/test-contracts/package.json +++ b/zk/debug_tools/test-contracts/package.json @@ -10,6 +10,7 @@ "counter:bali": "npx hardhat compile && npx hardhat run scripts/counter.js --network bali", "counter:cardona": "npx hardhat compile && npx hardhat run scripts/counter.js --network cardona", "counter:mainnet": "npx hardhat compile && npx hardhat run scripts/counter.js --network mainnet", + "emitlog:local": "npx hardhat compile && npx hardhat run scripts/emitlog.js --network local", "emitlog:bali": "npx hardhat compile && npx hardhat run scripts/emitlog.js --network bali", "emitlog:cardona": "npx hardhat compile && npx hardhat run scripts/emitlog.js --network cardona", "emitlog:mainnet": "npx hardhat compile && npx hardhat run scripts/emitlog.js --network mainnet", @@ -18,7 +19,8 @@ "erc20Revert:local": "npx hardhat compile && npx hardhat run scripts/ERC20-revert.js --network local", "erc20Revert:sepolia": "npx hardhat compile && npx hardhat run scripts/ERC20-revert.js --network sepolia", "chainCall:local": "npx hardhat compile && npx hardhat run scripts/chain-call.js --network local", - "chainCall:sepolia": "npx hardhat compile && npx hardhat run scripts/chain-call.js --network sepolia" + "chainCall:sepolia": "npx hardhat compile && npx hardhat run scripts/chain-call.js --network sepolia", + "create:local": "npx hardhat compile && npx hardhat run scripts/create.js --network local" }, "keywords": [], "author": "", diff --git a/zk/debug_tools/test-contracts/scripts/create.js b/zk/debug_tools/test-contracts/scripts/create.js new file mode 100644 index 00000000000..291e7ac0c45 --- /dev/null +++ b/zk/debug_tools/test-contracts/scripts/create.js @@ -0,0 +1,30 @@ + +// deploys contracts and calls a method to produce delegate call + +async function main() { + const deployableBytecode = "608060405234801561000f575f80fd5b506101778061001d5f395ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c806306661abd14610043578063a87d942c14610061578063d09de08a1461007f575b5f80fd5b61004b610089565b60405161005891906100c8565b60405180910390f35b61006961008e565b60405161007691906100c8565b60405180910390f35b610087610096565b005b5f5481565b5f8054905090565b60015f808282546100a7919061010e565b92505081905550565b5f819050919050565b6100c2816100b0565b82525050565b5f6020820190506100db5f8301846100b9565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610118826100b0565b9150610123836100b0565b925082820190508082111561013b5761013a6100e1565b5b9291505056fea2646970667358221220137ae5cf0fcdf694f11fbe24952b202d62e7154851f6232b7b897dbf37a2d18164736f6c63430008140033" + try { + const Creates = await hre.ethers.getContractFactory("Creates"); + + // Deploy the contracts + const createsContract = await Creates.deploy(); + + // Wait for the deployment transactions to be mined + await createsContract.waitForDeployment(); + + console.log(`DelegateCalled deployed to: ${await createsContract.getAddress()}`); + + const opCreate = await createsContract.opCreate(hre.ethers.toUtf8Bytes(deployableBytecode), deployableBytecode.length); + console.log('opCreate method call transaction: ', opCreate.hash); + } catch (error) { + console.error(error.toString()); + process.exit(1); + } + } + + main() + .then(() => process.exit(0)) + .catch(error => { + console.error(error); + process.exit(1); + }); \ No newline at end of file diff --git a/zk/erigon_db/db.go b/zk/erigon_db/db.go index c66bea2e0e6..a944408e22d 100644 --- a/zk/erigon_db/db.go +++ b/zk/erigon_db/db.go @@ -13,6 +13,12 @@ import ( var sha3UncleHash = common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") +type ReadOnlyErigonDb interface { + GetBodyTransactions(fromBlockNo, toBlockNo uint64) (*[]ethTypes.Transaction, error) + ReadCanonicalHash(blockNo uint64) (common.Hash, error) + GetHeader(blockNo uint64) (*ethTypes.Header, error) +} + type ErigonDb struct { tx kv.RwTx } diff --git a/zk/hermez_db/db.go b/zk/hermez_db/db.go index b3e6319a9d4..429ba5e72c5 100644 --- a/zk/hermez_db/db.go +++ b/zk/hermez_db/db.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "math" + "sort" "github.com/gateway-fm/cdk-erigon-lib/common" "github.com/gateway-fm/cdk-erigon-lib/kv" @@ -50,6 +51,7 @@ const FORK_HISTORY = "fork_history" // index const JUST_UNWOUND = "just_unwound" // batch number -> true const PLAIN_STATE_VERSION = "plain_state_version" // batch number -> true const ERIGON_VERSIONS = "erigon_versions" // erigon version -> timestamp of startup +const BATCH_ENDS = "batch_ends" // var HermezDbTables = []string{ L1VERIFICATIONS, @@ -85,6 +87,7 @@ var HermezDbTables = []string{ JUST_UNWOUND, PLAIN_STATE_VERSION, ERIGON_VERSIONS, + BATCH_ENDS, } type HermezDb struct { @@ -131,7 +134,7 @@ func (db *HermezDbReader) GetBatchNoByL2Block(l2BlockNo uint64) (uint64, error) } if k == nil { - return 0, nil + return 0, ErrorNotStored } if BytesToUint64(k) != l2BlockNo { @@ -203,10 +206,20 @@ func (db *HermezDbReader) GetLatestDownloadedBatchNo() (uint64, error) { return BytesToUint64(v), nil } -func (db *HermezDbReader) GetHighestBlockInBatch(batchNo uint64) (uint64, error) { +// returns 0 and true for batch 0 (custom case) even thou no block in the db for taht batch +// returns 0 and false if no blocks found in the DB for that batch +func (db *HermezDbReader) GetHighestBlockInBatch(batchNo uint64) (uint64, bool, error) { + // custom case for batch 0 + if batchNo == 0 { + return 0, true, nil + } blocks, err := db.GetL2BlockNosByBatch(batchNo) if err != nil { - return 0, err + return 0, false, err + } + + if len(blocks) == 0 { + return 0, false, nil } max := uint64(0) @@ -216,10 +229,17 @@ func (db *HermezDbReader) GetHighestBlockInBatch(batchNo uint64) (uint64, error) } } - return max, nil + return max, true, nil } +// returns 0 and true for batch 0 (custom case) even thou no block in the db for taht batch +// returns 0 and false if no blocks found in the DB for that batch func (db *HermezDbReader) GetLowestBlockInBatch(batchNo uint64) (blockNo uint64, found bool, err error) { + // custom case for batch 0 + if batchNo == 0 { + return 0, true, nil + } + blocks, err := db.GetL2BlockNosByBatch(batchNo) if err != nil { return 0, false, err @@ -249,7 +269,7 @@ func (db *HermezDbReader) GetHighestVerifiedBlockNo() (uint64, error) { return 0, nil } - blockNo, err := db.GetHighestBlockInBatch(v.BatchNo) + blockNo, _, err := db.GetHighestBlockInBatch(v.BatchNo) if err != nil { return 0, err } @@ -275,6 +295,10 @@ func (db *HermezDbReader) GetSequenceByBatchNo(batchNo uint64) (*types.L1BatchIn return db.getByBatchNo(L1SEQUENCES, batchNo) } +func (db *HermezDbReader) GetRangeSequencesByBatch(batchNo uint64) (*types.L1BatchInfo, *types.L1BatchInfo, error) { + return db.getPrevAndCurrentForBatch(L1SEQUENCES, batchNo) +} + func (db *HermezDbReader) GetSequenceByBatchNoOrHighest(batchNo uint64) (*types.L1BatchInfo, error) { seq, err := db.GetSequenceByBatchNo(batchNo) if err != nil { @@ -304,25 +328,29 @@ func (db *HermezDbReader) GetSequenceByBatchNoOrHighest(batchNo uint64) (*types. } if batch > batchNo { - if len(v) != 64 { - return nil, fmt.Errorf("invalid hash length") - } - - l1TxHash := common.BytesToHash(v[:32]) - stateRoot := common.BytesToHash(v[32:64]) - - return &types.L1BatchInfo{ - BatchNo: batch, - L1BlockNo: l1Block, - StateRoot: stateRoot, - L1TxHash: l1TxHash, - }, nil + return parseL1BatchInfo(l1Block, batch, v) } } return nil, nil } +func parseL1BatchInfo(l1BlockN, batchN uint64, v []byte) (*types.L1BatchInfo, error) { + if len(v) != 96 && len(v) != 64 { + return nil, fmt.Errorf("invalid hash length") + } + + l1TxHash := common.BytesToHash(v[:32]) + stateRoot := common.BytesToHash(v[32:64]) + l1InfoRoot := common.BytesToHash(v[64:]) + return &types.L1BatchInfo{ + BatchNo: batchN, + L1BlockNo: l1BlockN, + StateRoot: stateRoot, + L1TxHash: l1TxHash, + L1InfoRoot: l1InfoRoot, + }, nil +} func (db *HermezDbReader) GetVerificationByL1Block(l1BlockNo uint64) (*types.L1BatchInfo, error) { return db.getByL1Block(L1VERIFICATIONS, l1BlockNo) } @@ -403,23 +431,62 @@ func (db *HermezDbReader) getByL1Block(table string, l1BlockNo uint64) (*types.L } if l1Block == l1BlockNo { - if len(v) != 96 && len(v) != 64 { - return nil, fmt.Errorf("invalid hash length") - } + return parseL1BatchInfo(l1Block, batchNo, v) + } + } - l1TxHash := common.BytesToHash(v[:32]) - stateRoot := common.BytesToHash(v[32:64]) + return nil, nil +} - return &types.L1BatchInfo{ - BatchNo: batchNo, - L1BlockNo: l1Block, - StateRoot: stateRoot, - L1TxHash: l1TxHash, - }, nil +func (db *HermezDbReader) getPrevAndCurrentForBatch(table string, batchNo uint64) (prev *types.L1BatchInfo, current *types.L1BatchInfo, err error) { + c, err := db.tx.Cursor(table) + if err != nil { + return + } + defer c.Close() + + var k, v []byte + for k, v, err = c.First(); k != nil; k, v, err = c.Next() { + if err != nil { + return + } + + l1Block, batch, err1 := SplitKey(k) + if err1 != nil { + err = err1 + return + } + + // found the current one + if batch >= batchNo { + current, err = parseL1BatchInfo(l1Block, batch, v) + if err != nil { + return + } + break } } - return nil, nil + k, v, err = c.Prev() + if err != nil { + return + } + if len(v) == 0 { + prev = &types.L1BatchInfo{} + return + } + + l1Block, prevBatch, err := SplitKey(k) + if err != nil { + return + } + + prev, err = parseL1BatchInfo(l1Block, prevBatch, v) + if err != nil { + return + } + + return } func (db *HermezDbReader) getByBatchNo(table string, batchNo uint64) (*types.L1BatchInfo, error) { @@ -441,24 +508,7 @@ func (db *HermezDbReader) getByBatchNo(table string, batchNo uint64) (*types.L1B } if batch == batchNo { - if len(v) != 96 && len(v) != 64 { - return nil, fmt.Errorf("invalid hash length") - } - - l1TxHash := common.BytesToHash(v[:32]) - stateRoot := common.BytesToHash(v[32:64]) - var l1InfoRoot common.Hash - if len(v) > 64 { - l1InfoRoot = common.BytesToHash(v[64:]) - } - - return &types.L1BatchInfo{ - BatchNo: batchNo, - L1BlockNo: l1Block, - StateRoot: stateRoot, - L1TxHash: l1TxHash, - L1InfoRoot: l1InfoRoot, - }, nil + return parseL1BatchInfo(l1Block, batch, v) } } @@ -502,29 +552,14 @@ func (db *HermezDbReader) getLatest(table string) (*types.L1BatchInfo, error) { if len(value) == 0 { return nil, nil } - - if len(value) != 96 && len(value) != 64 { - return nil, fmt.Errorf("invalid hash length") - } - - l1TxHash := common.BytesToHash(value[:32]) - stateRoot := common.BytesToHash(value[32:64]) - var l1InfoRoot common.Hash - if len(value) > 64 { - l1InfoRoot = common.BytesToHash(value[64:]) - } - - return &types.L1BatchInfo{ - BatchNo: batchNo, - L1BlockNo: l1BlockNo, - L1TxHash: l1TxHash, - StateRoot: stateRoot, - L1InfoRoot: l1InfoRoot, - }, nil + return parseL1BatchInfo(l1BlockNo, batchNo, value) } -func (db *HermezDb) WriteSequence(l1BlockNo, batchNo uint64, l1TxHash, stateRoot common.Hash) error { - val := append(l1TxHash.Bytes(), stateRoot.Bytes()...) +func (db *HermezDb) WriteSequence(l1BlockNo, batchNo uint64, l1TxHash, stateRoot, l1InfoRoot common.Hash) error { + val := make([]byte, 0, 96) + val = append(val, l1TxHash.Bytes()...) + val = append(val, stateRoot.Bytes()...) + val = append(val, l1InfoRoot.Bytes()...) return db.tx.Put(L1SEQUENCES, ConcatKey(l1BlockNo, batchNo), val) } @@ -540,8 +575,7 @@ func (db *HermezDb) RollbackSequences(batchNo uint64) error { break } - err = db.tx.Delete(L1SEQUENCES, ConcatKey(latestSequence.L1BlockNo, latestSequence.BatchNo)) - if err != nil { + if err = db.tx.Delete(L1SEQUENCES, ConcatKey(latestSequence.L1BlockNo, latestSequence.BatchNo)); err != nil { return err } } @@ -551,7 +585,7 @@ func (db *HermezDb) RollbackSequences(batchNo uint64) error { func (db *HermezDb) TruncateSequences(l2BlockNo uint64) error { batchNo, err := db.GetBatchNoByL2Block(l2BlockNo) - if err != nil { + if err != nil && !errors.Is(err, ErrorNotStored) { return err } if batchNo == 0 { @@ -580,8 +614,7 @@ func (db *HermezDb) TruncateSequences(l2BlockNo uint64) error { continue } // delete seq - err = db.tx.Delete(L1SEQUENCES, ConcatKey(seq.L1BlockNo, seq.BatchNo)) - if err != nil { + if err = db.tx.Delete(L1SEQUENCES, ConcatKey(seq.L1BlockNo, seq.BatchNo)); err != nil { return err } } @@ -595,7 +628,7 @@ func (db *HermezDb) WriteVerification(l1BlockNo, batchNo uint64, l1TxHash common func (db *HermezDb) TruncateVerifications(l2BlockNo uint64) error { batchNo, err := db.GetBatchNoByL2Block(l2BlockNo) - if err != nil { + if err != nil && !errors.Is(err, ErrorNotStored) { return err } if batchNo == 0 { @@ -946,7 +979,7 @@ func (db *HermezDb) DeleteBlockBatches(fromBlockNum, toBlockNum uint64) error { // find all the batches involved for i := fromBlockNum; i <= toBlockNum; i++ { batch, err := db.GetBatchNoByL2Block(i) - if err != nil { + if err != nil && !errors.Is(err, ErrorNotStored) { return err } batchNumbersMap[batch] = struct{}{} @@ -997,6 +1030,9 @@ func (db *HermezDb) deleteFromBucketWithUintKeysRange(bucket string, fromBlockNu } func (db *HermezDbReader) GetForkId(batchNo uint64) (uint64, error) { + if batchNo == 0 { + batchNo = 1 + } v, err := db.tx.GetOne(FORKIDS, Uint64ToBytes(batchNo)) if err != nil { return 0, err @@ -1256,6 +1292,32 @@ func (db *HermezDbReader) GetBlockL1InfoTreeIndex(blockNumber uint64) (uint64, e return BytesToUint64(v), nil } +// gets the previous saved index and block for that index +// uses current inex block as parameter +func (db *HermezDbReader) GetPreviousIndexBlock(currentIndexBlockNumber uint64) (blockNum uint64, index uint64, found bool, err error) { + c, err := db.tx.Cursor(BLOCK_L1_INFO_TREE_INDEX) + if err != nil { + return + } + defer c.Close() + + k, _, err := c.SeekExact(Uint64ToBytes(currentIndexBlockNumber)) + if err != nil || k == nil { + return + } + + k, v, err := c.Prev() + if err != nil || k == nil { + return + } + + blockNum = BytesToUint64(k) + index = BytesToUint64(v) + found = true + + return +} + func (db *HermezDb) WriteBlockL1InfoTreeIndexProgress(blockNumber uint64, l1Index uint64) error { latestBlockNumber, latestL1Index, err := db.GetLatestBlockL1InfoTreeIndexProgress() if err != nil { @@ -1360,7 +1422,7 @@ func (db *HermezDbReader) GetWitness(batchNumber uint64) ([]byte, error) { return v, nil } -func (db *HermezDb) WriteBatchCounters(blockNumber uint64, counters map[string]int) error { +func (db *HermezDb) WriteBatchCounters(blockNumber uint64, counters []int) error { countersJson, err := json.Marshal(counters) if err != nil { return err @@ -1368,7 +1430,7 @@ func (db *HermezDb) WriteBatchCounters(blockNumber uint64, counters map[string]i return db.tx.Put(BATCH_COUNTERS, Uint64ToBytes(blockNumber), countersJson) } -func (db *HermezDbReader) GetLatestBatchCounters(batchNumber uint64) (countersMap map[string]int, found bool, err error) { +func (db *HermezDbReader) GetLatestBatchCounters(batchNumber uint64) (countersArray []int, found bool, err error) { batchBlockNumbers, err := db.GetL2BlockNosByBatch(batchNumber) if err != nil { return nil, false, err @@ -1381,12 +1443,12 @@ func (db *HermezDbReader) GetLatestBatchCounters(batchNumber uint64) (countersMa found = len(v) > 0 if found { - if err = json.Unmarshal(v, &countersMap); err != nil { + if err = json.Unmarshal(v, &countersArray); err != nil { return nil, false, err } } - return countersMap, found, nil + return countersArray, found, nil } func (db *HermezDb) DeleteBatchCounters(fromBlockNum, toBlockNum uint64) error { @@ -1701,3 +1763,107 @@ func (db *HermezDb) WriteErigonVersion(version string, timestamp time.Time) (boo // write new version return true, db.tx.Put(ERIGON_VERSIONS, []byte(version), Uint64ToBytes(uint64(timestamp.Unix()))) } + +func (db *HermezDb) WriteBatchEnd(blockNo uint64) error { + key := Uint64ToBytes(blockNo) + return db.tx.Put(BATCH_ENDS, key, []byte{1}) +} + +func (db *HermezDbReader) GetBatchEnd(blockNo uint64) (bool, error) { + v, err := db.tx.GetOne(BATCH_ENDS, Uint64ToBytes(blockNo)) + if err != nil { + return false, err + } + return len(v) > 0, nil +} + +func (db *HermezDb) DeleteBatchEnds(from, to uint64) error { + return db.deleteFromBucketWithUintKeysRange(BATCH_ENDS, from, to) +} + +func (db *HermezDbReader) GetAllForkIntervals() ([]types.ForkInterval, error) { + return db.getForkIntervals(nil) +} + +func (db *HermezDbReader) GetForkInterval(forkID uint64) (*types.ForkInterval, bool, error) { + forkIntervals, err := db.getForkIntervals(&forkID) + if err != nil { + return nil, false, err + } + + if len(forkIntervals) == 0 { + return nil, false, err + } + + forkInterval := forkIntervals[0] + return &forkInterval, true, nil +} + +func (db *HermezDbReader) getForkIntervals(forkIdFilter *uint64) ([]types.ForkInterval, error) { + mapForkIntervals := map[uint64]types.ForkInterval{} + + c, err := db.tx.Cursor(FORKIDS) + if err != nil { + return nil, err + } + defer c.Close() + + lastForkId := uint64(0) + for k, v, err := c.First(); k != nil; k, v, err = c.Next() { + if err != nil { + return nil, err + } + + batchNumber := BytesToUint64(k) + forkID := BytesToUint64(v) + + if forkID > lastForkId { + lastForkId = forkID + } + + if forkIdFilter != nil && *forkIdFilter != forkID { + continue + } + + mapInterval, found := mapForkIntervals[forkID] + if !found { + mapInterval = types.ForkInterval{ + ForkID: forkID, + FromBatchNumber: batchNumber, + ToBatchNumber: batchNumber, + } + } + + if batchNumber < mapInterval.FromBatchNumber { + mapInterval.FromBatchNumber = batchNumber + } + + if batchNumber > mapInterval.ToBatchNumber { + mapInterval.ToBatchNumber = batchNumber + } + + mapForkIntervals[forkID] = mapInterval + } + + forkIntervals := make([]types.ForkInterval, 0, len(mapForkIntervals)) + for forkId, forkInterval := range mapForkIntervals { + blockNumber, found, err := db.GetForkIdBlock(forkInterval.ForkID) + if err != nil { + return nil, err + } else if found { + forkInterval.BlockNumber = blockNumber + } + + if forkId == lastForkId { + forkInterval.ToBatchNumber = math.MaxUint64 + } + + forkIntervals = append(forkIntervals, forkInterval) + } + + sort.Slice(forkIntervals, func(i, j int) bool { + return forkIntervals[i].FromBatchNumber < forkIntervals[j].FromBatchNumber + }) + + return forkIntervals, nil +} diff --git a/zk/hermez_db/db_test.go b/zk/hermez_db/db_test.go index 0e6e6891676..bb631dfe58a 100644 --- a/zk/hermez_db/db_test.go +++ b/zk/hermez_db/db_test.go @@ -3,6 +3,7 @@ package hermez_db import ( "context" "fmt" + "math" "testing" "github.com/gateway-fm/cdk-erigon-lib/common" @@ -13,7 +14,7 @@ import ( ) type IHermezDb interface { - WriteSequence(uint64, uint64, common.Hash, common.Hash) error + WriteSequence(uint64, uint64, common.Hash, common.Hash, common.Hash) error WriteVerification(uint64, uint64, common.Hash, common.Hash) error } @@ -50,8 +51,8 @@ func TestGetSequenceByL1Block(t *testing.T) { defer cleanup() db := NewHermezDb(tx) - require.NoError(t, db.WriteSequence(1, 1001, common.HexToHash("0xabc"), common.HexToHash("0xabc"))) - require.NoError(t, db.WriteSequence(2, 1002, common.HexToHash("0xdef"), common.HexToHash("0xdef"))) + require.NoError(t, db.WriteSequence(1, 1001, common.HexToHash("0xabc"), common.HexToHash("0xabc"), common.HexToHash("0x0"))) + require.NoError(t, db.WriteSequence(2, 1002, common.HexToHash("0xdef"), common.HexToHash("0xdef"), common.HexToHash("0x0"))) info, err := db.GetSequenceByL1Block(1) require.NoError(t, err) @@ -71,8 +72,8 @@ func TestGetSequenceByBatchNo(t *testing.T) { defer cleanup() db := NewHermezDb(tx) - require.NoError(t, db.WriteSequence(1, 1001, common.HexToHash("0xabc"), common.HexToHash("0xabcd"))) - require.NoError(t, db.WriteSequence(2, 1002, common.HexToHash("0xdef"), common.HexToHash("0xdefg"))) + require.NoError(t, db.WriteSequence(1, 1001, common.HexToHash("0xabc"), common.HexToHash("0xabcd"), common.HexToHash("0x0"))) + require.NoError(t, db.WriteSequence(2, 1002, common.HexToHash("0xdef"), common.HexToHash("0xdefg"), common.HexToHash("0x0"))) info, err := db.GetSequenceByBatchNo(1001) require.NoError(t, err) @@ -117,7 +118,7 @@ func TestGetAndSetLatest(t *testing.T) { testCases := []struct { desc string table string - writeSequenceMethod func(IHermezDb, uint64, uint64, common.Hash, common.Hash) error + writeSequenceMethod func(IHermezDb, uint64, uint64, common.Hash, common.Hash, common.Hash) error writeVerificationMethod func(IHermezDb, uint64, uint64, common.Hash, common.Hash) error l1BlockNo uint64 batchNo uint64 @@ -137,7 +138,7 @@ func TestGetAndSetLatest(t *testing.T) { db := NewHermezDb(tx) var err error if tc.table == L1SEQUENCES { - err = tc.writeSequenceMethod(db, tc.l1BlockNo, tc.batchNo, tc.l1TxHashBytes, tc.stateRoot) + err = tc.writeSequenceMethod(db, tc.l1BlockNo, tc.batchNo, tc.l1TxHashBytes, tc.stateRoot, common.HexToHash("0x0")) } else { err = tc.writeVerificationMethod(db, tc.l1BlockNo, tc.batchNo, tc.l1TxHashBytes, tc.stateRoot) } @@ -191,36 +192,52 @@ func TestGetAndSetLatestUnordered(t *testing.T) { } func TestGetAndSetForkId(t *testing.T) { + tx, cleanup := GetDbTx() + defer cleanup() + db := NewHermezDb(tx) - testCases := []struct { - batchNo uint64 - forkId uint64 + forkIntervals := []struct { + ForkId uint64 + FromBatchNumber uint64 + ToBatchNumber uint64 }{ - {9, 0}, // batchNo < 10 -> forkId = 0 - {10, 1}, // batchNo = 10 -> forkId = 1 - {11, 1}, // batchNo > 10 -> forkId = 1 - {99, 1}, // batchNo < 100 -> forkId = 1 - {100, 2}, // batchNo >= 100 -> forkId = 2 - {1000, 2}, // batchNo > 100 -> forkId = 2 + {ForkId: 1, FromBatchNumber: 1, ToBatchNumber: 10}, + {ForkId: 2, FromBatchNumber: 11, ToBatchNumber: 100}, + {ForkId: 3, FromBatchNumber: 101, ToBatchNumber: 1000}, } - for _, tc := range testCases { - t.Run(fmt.Sprintf("BatchNo: %d ForkId: %d", tc.batchNo, tc.forkId), func(t *testing.T) { - tx, cleanup := GetDbTx() - db := NewHermezDb(tx) - - err := db.WriteForkId(10, 1) - require.NoError(t, err, "Failed to write ForkId") - err = db.WriteForkId(tc.batchNo, tc.forkId) - require.NoError(t, err, "Failed to write ForkId") - err = db.WriteForkId(100, 2) + for _, forkInterval := range forkIntervals { + for b := forkInterval.FromBatchNumber; b <= forkInterval.ToBatchNumber; b++ { + err := db.WriteForkId(b, forkInterval.ForkId) require.NoError(t, err, "Failed to write ForkId") + } + } - fetchedForkId, err := db.GetForkId(tc.batchNo) - require.NoError(t, err, "Failed to get ForkId") - assert.Equal(t, tc.forkId, fetchedForkId, "Fetched ForkId doesn't match expected") - cleanup() - }) + testCases := []struct { + batchNo uint64 + expectedForkId uint64 + }{ + {0, 1}, // batch 0 = forkID, special case, batch 0 has the same forkId as batch 1 + + {1, 1}, // batch 1 = forkId 1, first batch for forkId 1 + {5, 1}, // batch 5 = forkId 1, a batch between first and last for forkId 1 + {10, 1}, // batch 10 = forkId 1, last batch for forkId 1 + + {11, 2}, // batch 11 = forkId 1, first batch for forkId 2 + {50, 2}, // batch 50 = forkId 1, a batch between first and last for forkId 2 + {100, 2}, // batch 100 = forkId 1, last batch for forkId 2 + + {101, 3}, // batch 101 = forkId 1, first batch for forkId 3 + {500, 3}, // batch 500 = forkId 1, a batch between first and last for forkId 3 + {1000, 3}, // batch 1000 = forkId 1, last batch for forkId 3 + + {1001, 0}, // batch 1001 = a batch out of the range of the known forks + } + + for _, tc := range testCases { + fetchedForkId, err := db.GetForkId(tc.batchNo) + assert.NoError(t, err) + assert.Equal(t, tc.expectedForkId, fetchedForkId, "invalid expected fork id when getting fork id by batch number") } } @@ -300,7 +317,7 @@ func TestTruncateSequences(t *testing.T) { db := NewHermezDb(tx) for i := 0; i < 1000; i++ { - err := db.WriteSequence(uint64(i), uint64(i), common.HexToHash("0xabc"), common.HexToHash("0xabc")) + err := db.WriteSequence(uint64(i), uint64(i), common.HexToHash("0xabc"), common.HexToHash("0xabc"), common.HexToHash("0x0")) require.NoError(t, err) err = db.WriteBlockBatch(uint64(i), uint64(i)) require.NoError(t, err) @@ -352,7 +369,7 @@ func TestTruncateBlockBatches(t *testing.T) { for i := l2BlockNo + 1; i <= 1000; i++ { _, err := db.GetBatchNoByL2Block(i) - require.NoError(t, err) + require.Error(t, err) } for i := uint64(1); i <= l2BlockNo; i++ { @@ -372,7 +389,7 @@ func BenchmarkWriteSequence(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - err := db.WriteSequence(uint64(i), uint64(i+1000), common.HexToHash("0xabc"), common.HexToHash("0xabc")) + err := db.WriteSequence(uint64(i), uint64(i+1000), common.HexToHash("0xabc"), common.HexToHash("0xabc"), common.HexToHash("0x0")) if err != nil { b.Fatal(err) } @@ -400,7 +417,7 @@ func BenchmarkGetSequenceByL1Block(b *testing.B) { db := NewHermezDb(tx) for i := 0; i < 1000; i++ { - err := db.WriteSequence(uint64(i), uint64(i+1000), common.HexToHash("0xabc"), common.HexToHash("0xabc")) + err := db.WriteSequence(uint64(i), uint64(i+1000), common.HexToHash("0xabc"), common.HexToHash("0xabc"), common.HexToHash("0x0")) if err != nil { b.Fatal(err) } @@ -444,7 +461,7 @@ func BenchmarkGetSequenceByBatchNo(b *testing.B) { db := NewHermezDb(tx) for i := 0; i < 1000; i++ { - err := db.WriteSequence(uint64(i), uint64(i+1000), common.HexToHash("0xabc"), common.HexToHash("0xabc")) + err := db.WriteSequence(uint64(i), uint64(i+1000), common.HexToHash("0xabc"), common.HexToHash("0xabc"), common.HexToHash("0x0")) if err != nil { b.Fatal(err) } @@ -503,3 +520,105 @@ func TestBatchBlocks(t *testing.T) { t.Fatal("Expected 1000 blocks") } } + +func TestDeleteForkId(t *testing.T) { + type forkInterval struct { + ForkId uint64 + FromBatchNumber uint64 + ToBatchNumber uint64 + } + forkIntervals := []forkInterval{ + {1, 1, 10}, + {2, 11, 20}, + {3, 21, 30}, + {4, 31, 40}, + {5, 41, 50}, + {6, 51, 60}, + {7, 61, 70}, + } + + testCases := []struct { + name string + fromBatchToDelete uint64 + toBatchToDelete uint64 + expectedDeletedForksIds []uint64 + expectedRemainingForkIntervals []forkInterval + }{ + {"delete fork id only for the last batch", 70, 70, nil, []forkInterval{ + {1, 1, 10}, + {2, 11, 20}, + {3, 21, 30}, + {4, 31, 40}, + {5, 41, 50}, + {6, 51, 60}, + {7, 61, math.MaxUint64}, + }}, + {"delete fork id for batches that don't exist", 80, 90, nil, []forkInterval{ + {1, 1, 10}, + {2, 11, 20}, + {3, 21, 30}, + {4, 31, 40}, + {5, 41, 50}, + {6, 51, 60}, + {7, 61, math.MaxUint64}, + }}, + {"delete fork id for batches that cross multiple forks from some point until the last one - unwind", 27, 70, []uint64{4, 5, 6, 7}, []forkInterval{ + {1, 1, 10}, + {2, 11, 20}, + {3, 21, math.MaxUint64}, + }}, + {"delete fork id for batches that cross multiple forks from zero to some point - prune", 0, 36, []uint64{1, 2, 3}, []forkInterval{ + {4, 37, 40}, + {5, 41, 50}, + {6, 51, 60}, + {7, 61, math.MaxUint64}, + }}, + {"delete fork id for batches that cross multiple forks from some point after the beginning to some point before the end - hole", 23, 42, []uint64{4}, []forkInterval{ + {1, 1, 10}, + {2, 11, 20}, + {3, 21, 22}, + {5, 43, 50}, + {6, 51, 60}, + {7, 61, math.MaxUint64}, + }}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + tx, cleanup := GetDbTx() + defer cleanup() + db := NewHermezDb(tx) + + for _, forkInterval := range forkIntervals { + for b := forkInterval.FromBatchNumber; b <= forkInterval.ToBatchNumber; b++ { + err := db.WriteForkId(b, forkInterval.ForkId) + require.NoError(t, err, "Failed to write ForkId") + } + } + + err := db.DeleteForkIds(tc.fromBatchToDelete, tc.toBatchToDelete) + require.NoError(t, err) + + for batchNum := tc.fromBatchToDelete; batchNum <= tc.toBatchToDelete; batchNum++ { + forkId, err := db.GetForkId(batchNum) + require.NoError(t, err) + assert.Equal(t, uint64(0), forkId) + } + + for _, forkId := range tc.expectedDeletedForksIds { + forkInterval, found, err := db.GetForkInterval(forkId) + require.NoError(t, err) + assert.False(t, found) + assert.Nil(t, forkInterval) + } + + for _, remainingForkInterval := range tc.expectedRemainingForkIntervals { + forkInterval, found, err := db.GetForkInterval(remainingForkInterval.ForkId) + require.NoError(t, err) + assert.True(t, found) + assert.Equal(t, remainingForkInterval.FromBatchNumber, forkInterval.FromBatchNumber) + assert.Equal(t, remainingForkInterval.ToBatchNumber, forkInterval.ToBatchNumber) + } + }) + } +} diff --git a/zk/hermez_db/errors.go b/zk/hermez_db/errors.go new file mode 100644 index 00000000000..1b00794996d --- /dev/null +++ b/zk/hermez_db/errors.go @@ -0,0 +1,7 @@ +package hermez_db + +import "errors" + +var ( + ErrorNotStored = errors.New("not stored") +) diff --git a/zk/l1infotree/tree.go b/zk/l1infotree/tree.go index 77b05d400da..a213aa00b3f 100644 --- a/zk/l1infotree/tree.go +++ b/zk/l1infotree/tree.go @@ -36,8 +36,8 @@ func NewL1InfoTree(height uint8, initialLeaves [][32]byte) (*L1InfoTree, error) mt.allLeaves[leaf] = struct{}{} } - log.Debug("Initial count: ", mt.count) - log.Debug("Initial root: ", mt.currentRoot) + log.Debug(fmt.Sprintf("Initial count: %d", mt.count)) + log.Debug(fmt.Sprintf("Initial root: %s", mt.currentRoot)) return mt, nil } diff --git a/zk/legacy_executor_verifier/legacy_executor_verifier.go b/zk/legacy_executor_verifier/legacy_executor_verifier.go index c2717fd9860..9b081b0c01e 100644 --- a/zk/legacy_executor_verifier/legacy_executor_verifier.go +++ b/zk/legacy_executor_verifier/legacy_executor_verifier.go @@ -61,6 +61,10 @@ func (vr *VerifierRequest) IsOverdue() bool { return time.Since(vr.creationTime) > vr.timeout } +func (vr *VerifierRequest) GetFirstBlockNumber() uint64 { + return vr.BlockNumbers[0] +} + func (vr *VerifierRequest) GetLastBlockNumber() uint64 { return vr.BlockNumbers[len(vr.BlockNumbers)-1] } @@ -126,6 +130,7 @@ func NewLegacyExecutorVerifier( } func (v *LegacyExecutorVerifier) StartAsyncVerification( + logPrefix string, forkId uint64, batchNumber uint64, stateRoot common.Hash, @@ -138,21 +143,23 @@ func (v *LegacyExecutorVerifier) StartAsyncVerification( request := NewVerifierRequestWithTimeout(forkId, batchNumber, blockNumbers, stateRoot, counters, requestTimeout) if useRemoteExecutor { - promise = v.VerifyAsync(request, blockNumbers) + promise = v.VerifyAsync(request) } else { - promise = v.VerifyWithoutExecutor(request, blockNumbers) + promise = v.VerifyWithoutExecutor(request) } - v.appendPromise(promise) + size := v.appendPromise(promise) + log.Info(fmt.Sprintf("[%s] Starting verification request", logPrefix), "batch-number", batchNumber, "blocks-range", fmt.Sprintf("[%d;%d]", request.GetFirstBlockNumber(), request.GetLastBlockNumber()), "pending-requests", size) } -func (v *LegacyExecutorVerifier) appendPromise(promise *Promise[*VerifierBundle]) { +func (v *LegacyExecutorVerifier) appendPromise(promise *Promise[*VerifierBundle]) int { v.mtxPromises.Lock() defer v.mtxPromises.Unlock() v.promises = append(v.promises, promise) + return len(v.promises) } -func (v *LegacyExecutorVerifier) VerifySync(tx kv.Tx, request *VerifierRequest, witness, streamBytes []byte, timestampLimit, firstBlockNumber uint64, l1InfoTreeMinTimestamps map[uint64]uint64) error { +func (v *LegacyExecutorVerifier) VerifySync(tx kv.Tx, request *VerifierRequest, witness, streamBytes []byte, timestampLimit uint64, l1InfoTreeMinTimestamps map[uint64]uint64) error { oldAccInputHash := common.HexToHash("0x0") payload := &Payload{ Witness: witness, @@ -177,7 +184,7 @@ func (v *LegacyExecutorVerifier) VerifySync(tx kv.Tx, request *VerifierRequest, e.AquireAccess() defer e.ReleaseAccess() - previousBlock, err := rawdb.ReadBlockByNumber(tx, firstBlockNumber-1) + previousBlock, err := rawdb.ReadBlockByNumber(tx, request.GetFirstBlockNumber()-1) if err != nil { return err } @@ -189,11 +196,12 @@ func (v *LegacyExecutorVerifier) VerifySync(tx kv.Tx, request *VerifierRequest, return executorErr } -func (v *LegacyExecutorVerifier) VerifyAsync(request *VerifierRequest, blockNumbers []uint64) *Promise[*VerifierBundle] { +func (v *LegacyExecutorVerifier) VerifyAsync(request *VerifierRequest) *Promise[*VerifierBundle] { // eager promise will do the work as soon as called in a goroutine, then we can retrieve the result later // ProcessResultsSequentiallyUnsafe relies on the fact that this function returns ALWAYS non-verifierBundle and error. The only exception is the case when verifications has been canceled. Only then the verifierBundle can be nil return NewPromise[*VerifierBundle](func() (*VerifierBundle, error) { verifierBundle := NewVerifierBundle(request, nil) + blockNumbers := verifierBundle.Request.BlockNumbers e := v.GetNextOnlineAvailableExecutor() if e == nil { @@ -291,11 +299,9 @@ func (v *LegacyExecutorVerifier) VerifyAsync(request *VerifierRequest, blockNumb }) } -func (v *LegacyExecutorVerifier) VerifyWithoutExecutor(request *VerifierRequest, blockNumbers []uint64) *Promise[*VerifierBundle] { +func (v *LegacyExecutorVerifier) VerifyWithoutExecutor(request *VerifierRequest) *Promise[*VerifierBundle] { promise := NewPromise[*VerifierBundle](func() (*VerifierBundle, error) { response := &VerifierResponse{ - // BatchNumber: request.BatchNumber, - // BlockNumber: request.BlockNumber, Valid: true, OriginalCounters: request.Counters, Witness: nil, @@ -309,7 +315,14 @@ func (v *LegacyExecutorVerifier) VerifyWithoutExecutor(request *VerifierRequest, return promise } -func (v *LegacyExecutorVerifier) ProcessResultsSequentially() ([]*VerifierBundle, error) { +func (v *LegacyExecutorVerifier) HasPendingVerifications() bool { + v.mtxPromises.Lock() + defer v.mtxPromises.Unlock() + + return len(v.promises) > 0 +} + +func (v *LegacyExecutorVerifier) ProcessResultsSequentially(logPrefix string) ([]*VerifierBundle, error) { v.mtxPromises.Lock() defer v.mtxPromises.Unlock() @@ -345,6 +358,7 @@ func (v *LegacyExecutorVerifier) ProcessResultsSequentially() ([]*VerifierBundle break } + log.Info(fmt.Sprintf("[%s] Finished verification request", logPrefix), "batch-number", verifierBundle.Request.BatchNumber, "blocks-range", fmt.Sprintf("[%d;%d]", verifierBundle.Request.GetFirstBlockNumber(), verifierBundle.Request.GetLastBlockNumber()), "is-valid", verifierBundle.Response.Valid, "pending-requests", len(v.promises)-1-idx) verifierResponse = append(verifierResponse, verifierBundle) } @@ -436,7 +450,7 @@ func (v *LegacyExecutorVerifier) GetWholeBatchStreamBytes( txsPerBlock[blockNumber] = filteredTransactions } - entries, err := server.BuildWholeBatchStreamEntriesProto(tx, hermezDb, v.streamServer.GetChainId(), batchNumber, previousBatch, blocks, txsPerBlock, l1InfoTreeMinTimestamps) + entries, err := server.BuildWholeBatchStreamEntriesProto(tx, hermezDb, v.streamServer.GetChainId(), previousBatch, batchNumber, blocks, txsPerBlock, l1InfoTreeMinTimestamps) if err != nil { return nil, err } diff --git a/zk/rpcdaemon/types_zkevm.go b/zk/rpcdaemon/types_zkevm.go index d8ca2bfb62c..617d31ae60e 100644 --- a/zk/rpcdaemon/types_zkevm.go +++ b/zk/rpcdaemon/types_zkevm.go @@ -25,9 +25,9 @@ type Batch struct { } type BatchDataSlim struct { - Number uint64 `json:"number"` - BatchL2Data ArgBytes `json:"batchL2Data,omitempty"` - Empty bool `json:"empty"` + Number ArgUint64 `json:"number"` + BatchL2Data ArgBytes `json:"batchL2Data,omitempty"` + Empty bool `json:"empty"` } type BlockWithInfoRootAndGer struct { diff --git a/zk/stages/stage_batches.go b/zk/stages/stage_batches.go index 5b0fd305dbe..97a0ed739c5 100644 --- a/zk/stages/stage_batches.go +++ b/zk/stages/stage_batches.go @@ -23,7 +23,9 @@ import ( txtype "github.com/ledgerwatch/erigon/zk/tx" "github.com/ledgerwatch/erigon/core/rawdb" + "github.com/ledgerwatch/erigon/core/state" "github.com/ledgerwatch/erigon/eth/ethconfig" + "github.com/ledgerwatch/erigon/zk/datastream/client" "github.com/ledgerwatch/erigon/zk/utils" "github.com/ledgerwatch/log/v3" ) @@ -33,6 +35,11 @@ const ( STAGE_PROGRESS_SAVE = 3000000 ) +var ( + // ErrFailedToFindCommonAncestor denotes error suggesting that the common ancestor is not found in the database + ErrFailedToFindCommonAncestor = errors.New("failed to find common ancestor block in the db") +) + type ErigonDb interface { WriteHeader(batchNo *big.Int, blockHash common.Hash, stateRoot, txHash, parentHash common.Hash, coinbase common.Address, ts, gasLimit uint64) (*ethTypes.Header, error) WriteBody(batchNo *big.Int, headerHash common.Hash, txs []ethTypes.Transaction) error @@ -70,26 +77,48 @@ type HermezDb interface { type DatastreamClient interface { ReadAllEntriesToChannel() error GetEntryChan() chan interface{} + GetL2BlockByNumber(blockNum uint64) (*types.FullL2Block, int, error) + GetLatestL2Block() (*types.FullL2Block, error) GetLastWrittenTimeAtomic() *atomic.Int64 GetStreamingAtomic() *atomic.Bool GetProgressAtomic() *atomic.Uint64 EnsureConnected() (bool, error) + Start() error + Stop() } +type dsClientCreatorHandler func(context.Context, *ethconfig.Zk, uint64) (DatastreamClient, error) + type BatchesCfg struct { - db kv.RwDB - blockRoutineStarted bool - dsClient DatastreamClient - zkCfg *ethconfig.Zk + db kv.RwDB + blockRoutineStarted bool + dsClient DatastreamClient + dsQueryClientCreator dsClientCreatorHandler + zkCfg *ethconfig.Zk } -func StageBatchesCfg(db kv.RwDB, dsClient DatastreamClient, zkCfg *ethconfig.Zk) BatchesCfg { - return BatchesCfg{ +func StageBatchesCfg(db kv.RwDB, dsClient DatastreamClient, zkCfg *ethconfig.Zk, options ...Option) BatchesCfg { + cfg := BatchesCfg{ db: db, blockRoutineStarted: false, dsClient: dsClient, zkCfg: zkCfg, } + + for _, opt := range options { + opt(&cfg) + } + + return cfg +} + +type Option func(*BatchesCfg) + +// WithDSClientCreator is a functional option to set the datastream client creator callback. +func WithDSClientCreator(handler dsClientCreatorHandler) Option { + return func(c *BatchesCfg) { + c.dsQueryClientCreator = handler + } } var emptyHash = common.Hash{0} @@ -130,23 +159,48 @@ func SpawnStageBatches( return fmt.Errorf("save stage progress error: %v", err) } - // get batch for batches progress - stageProgressBatchNo, err := hermezDb.GetBatchNoByL2Block(stageProgressBlockNo) - if err != nil { - return fmt.Errorf("get batch no by l2 block error: %v", err) - } - //// BISECT //// if cfg.zkCfg.DebugLimit > 0 && stageProgressBlockNo > cfg.zkCfg.DebugLimit { return nil } + // get batch for batches progress + stageProgressBatchNo, err := hermezDb.GetBatchNoByL2Block(stageProgressBlockNo) + if err != nil && !errors.Is(err, hermez_db.ErrorNotStored) { + return fmt.Errorf("get batch no by l2 block error: %v", err) + } + highestVerifiedBatch, err := stages.GetStageProgress(tx, stages.L1VerificationsBatchNo) if err != nil { return errors.New("could not retrieve l1 verifications batch no progress") } startSyncTime := time.Now() + + latestForkId, err := stages.GetStageProgress(tx, stages.ForkId) + if err != nil { + return err + } + + dsQueryClient, err := newStreamClient(ctx, cfg, latestForkId) + if err != nil { + log.Warn(fmt.Sprintf("[%s] %s", logPrefix, err)) + return err + } + defer dsQueryClient.Stop() + + highestDSL2Block, err := dsQueryClient.GetLatestL2Block() + if err != nil { + return fmt.Errorf("failed to retrieve the latest datastream l2 block: %w", err) + } + + if highestDSL2Block.L2BlockNumber < stageProgressBlockNo { + stageProgressBlockNo = highestDSL2Block.L2BlockNumber + } + + log.Debug(fmt.Sprintf("[%s] Highest block in datastream", logPrefix), "block", highestDSL2Block.L2BlockNumber) + log.Debug(fmt.Sprintf("[%s] Highest block in db", logPrefix), "block", stageProgressBlockNo) + dsClientProgress := cfg.dsClient.GetProgressAtomic() dsClientProgress.Store(stageProgressBlockNo) // start routine to download blocks and push them in a channel @@ -160,7 +214,7 @@ func SpawnStageBatches( for i := 0; i < 5; i++ { connected, err = cfg.dsClient.EnsureConnected() if err != nil { - log.Error("[datastream_client] Error connecting to datastream", "error", err) + log.Error(fmt.Sprintf("[%s] Error connecting to datastream", logPrefix), "error", err) continue } if connected { @@ -174,7 +228,7 @@ func SpawnStageBatches( if connected { if err := cfg.dsClient.ReadAllEntriesToChannel(); err != nil { - log.Error("[datastream_client] Error downloading blocks from datastream", "error", err) + log.Error(fmt.Sprintf("[%s] Error downloading blocks from datastream", logPrefix), "error", err) } } }() @@ -248,9 +302,14 @@ LOOP: } case *types.BatchEnd: if entry.StateRoot != lastBlockRoot { - log.Warn(fmt.Sprintf("[%s] batch end state root mismatches last block's: %x, expected: %x", logPrefix, entry.StateRoot, lastBlockRoot)) + log.Debug(fmt.Sprintf("[%s] batch end state root mismatches last block's: %x, expected: %x", logPrefix, entry.StateRoot, lastBlockRoot)) + } + // keep a record of the last block processed when we receive the batch end + if err = hermezDb.WriteBatchEnd(lastBlockHeight); err != nil { + return err } case *types.FullL2Block: + log.Debug(fmt.Sprintf("[%s] Retrieved %d (%s) block from stream", logPrefix, entry.L2BlockNumber, entry.L2Blockhash.String())) if cfg.zkCfg.SyncLimit > 0 && entry.L2BlockNumber >= cfg.zkCfg.SyncLimit { // stop the node going into a crazy loop time.Sleep(2 * time.Second) @@ -288,9 +347,57 @@ LOOP: // when the stage is fired up for the first time log.Warn(fmt.Sprintf("[%s] Skipping block %d, already processed", logPrefix, entry.L2BlockNumber)) } + + dbBatchNum, err := hermezDb.GetBatchNoByL2Block(entry.L2BlockNumber) + if err != nil { + return err + } + + if entry.BatchNumber != dbBatchNum { + // if the bath number mismatches, it means that we need to trigger an unwinding of blocks + log.Warn(fmt.Sprintf("[%s] Batch number mismatch detected. Triggering unwind...", logPrefix), + "block", entry.L2BlockNumber, "ds batch", entry.BatchNumber, "db batch", dbBatchNum) + if err := rollback(logPrefix, eriDb, hermezDb, dsQueryClient, entry.L2BlockNumber, tx, u); err != nil { + return err + } + cfg.dsClient.Stop() + return nil + } continue } + var dbParentBlockHash common.Hash + if entry.L2BlockNumber > 0 { + dbParentBlockHash, err = eriDb.ReadCanonicalHash(entry.L2BlockNumber - 1) + if err != nil { + return fmt.Errorf("failed to retrieve parent block hash for datastream block %d: %w", + entry.L2BlockNumber, err) + } + } + + dsParentBlockHash := lastHash + if dsParentBlockHash == emptyHash { + parentBlockDS, _, err := dsQueryClient.GetL2BlockByNumber(entry.L2BlockNumber - 1) + if err != nil { + return err + } + + if parentBlockDS != nil { + dsParentBlockHash = parentBlockDS.L2Blockhash + } + } + + if dbParentBlockHash != dsParentBlockHash { + // unwind/rollback blocks until the latest common ancestor block + log.Warn(fmt.Sprintf("[%s] Parent block hashes mismatch on block %d. Triggering unwind...", logPrefix, entry.L2BlockNumber), + "db parent block hash", dbParentBlockHash, "ds parent block hash", dsParentBlockHash) + if err := rollback(logPrefix, eriDb, hermezDb, dsQueryClient, entry.L2BlockNumber, tx, u); err != nil { + return err + } + cfg.dsClient.Stop() + return nil + } + // skip if we already have this block if entry.L2BlockNumber < lastBlockHeight+1 { log.Warn(fmt.Sprintf("[%s] Unwinding to block %d", logPrefix, entry.L2BlockNumber)) @@ -299,6 +406,7 @@ LOOP: return fmt.Errorf("failed to get bad block: %v", err) } u.UnwindTo(entry.L2BlockNumber, badBlock) + return nil } // check for sequential block numbers @@ -422,16 +530,18 @@ LOOP: return err } - if err := tx.Commit(); err != nil { - return fmt.Errorf("failed to commit tx, %w", err) - } + if freshTx { + if err := tx.Commit(); err != nil { + return fmt.Errorf("failed to commit tx, %w", err) + } - tx, err = cfg.db.BeginRw(ctx) - if err != nil { - return fmt.Errorf("failed to open tx, %w", err) + tx, err = cfg.db.BeginRw(ctx) + if err != nil { + return fmt.Errorf("failed to open tx, %w", err) + } + hermezDb = hermez_db.NewHermezDb(tx) + eriDb = erigon_db.NewErigonDb(tx) } - hermezDb = hermez_db.NewHermezDb(tx) - eriDb = erigon_db.NewErigonDb(tx) prevAmountBlocksWritten = blocksWritten } @@ -523,15 +633,15 @@ func UnwindBatchesStage(u *stagedsync.UnwindState, tx kv.RwTx, cfg BatchesCfg, c } fromBatchPrev, err := hermezDb.GetBatchNoByL2Block(fromBlock - 1) - if err != nil { + if err != nil && !errors.Is(err, hermez_db.ErrorNotStored) { return fmt.Errorf("get batch no by l2 block error: %v", err) } fromBatch, err := hermezDb.GetBatchNoByL2Block(fromBlock) - if err != nil { + if err != nil && !errors.Is(err, hermez_db.ErrorNotStored) { return fmt.Errorf("get fromBatch no by l2 block error: %v", err) } toBatch, err := hermezDb.GetBatchNoByL2Block(toBlock) - if err != nil { + if err != nil && !errors.Is(err, hermez_db.ErrorNotStored) { return fmt.Errorf("get toBatch no by l2 block error: %v", err) } @@ -606,6 +716,10 @@ func UnwindBatchesStage(u *stagedsync.UnwindState, tx kv.RwTx, cfg BatchesCfg, c if err = hermezDb.DeleteReusedL1InfoTreeIndexes(fromBlock, toBlock); err != nil { return fmt.Errorf("write reused l1 info tree index error: %w", err) } + + if err = hermezDb.DeleteBatchEnds(fromBlock, toBlock); err != nil { + return fmt.Errorf("delete batch ends error: %v", err) + } /////////////////////////////////////////////////////// log.Info(fmt.Sprintf("[%s] Deleted headers, bodies, forkIds and blockBatches.", logPrefix)) @@ -625,7 +739,7 @@ func UnwindBatchesStage(u *stagedsync.UnwindState, tx kv.RwTx, cfg BatchesCfg, c ///////////////////////////////////////////// // iterate until a block with lower batch number is found // this is the last block of the previous batch and the highest hashable block for verifications - lastBatchHighestBlock, err := hermezDb.GetHighestBlockInBatch(fromBatchPrev - 1) + lastBatchHighestBlock, _, err := hermezDb.GetHighestBlockInBatch(fromBatchPrev - 1) if err != nil { return fmt.Errorf("get batch highest block error: %w", err) } @@ -698,7 +812,7 @@ func PruneBatchesStage(s *stagedsync.PruneState, tx kv.RwTx, cfg BatchesCfg, ctx defer tx.Rollback() } - log.Info(fmt.Sprintf("[%s] Pruning barches...", logPrefix)) + log.Info(fmt.Sprintf("[%s] Pruning batches...", logPrefix)) defer log.Info(fmt.Sprintf("[%s] Unwinding batches complete", logPrefix)) hermezDb := hermez_db.NewHermezDb(tx) @@ -842,3 +956,137 @@ func writeL2Block(eriDb ErigonDb, hermezDb HermezDb, l2Block *types.FullL2Block, return nil } + +// rollback performs the unwinding of blocks: +// 1. queries the latest common ancestor for datastream and db, +// 2. resolves the unwind block (as the latest block in the previous batch, comparing to the found ancestor block) +// 3. triggers the unwinding +func rollback(logPrefix string, eriDb *erigon_db.ErigonDb, hermezDb *hermez_db.HermezDb, + dsQueryClient DatastreamClient, latestDSBlockNum uint64, tx kv.RwTx, u stagedsync.Unwinder) error { + ancestorBlockNum, ancestorBlockHash, err := findCommonAncestor(eriDb, hermezDb, dsQueryClient, latestDSBlockNum) + if err != nil { + return err + } + log.Debug(fmt.Sprintf("[%s] The common ancestor for datastream and db is block %d (%s)", logPrefix, ancestorBlockNum, ancestorBlockHash)) + + unwindBlockNum, unwindBlockHash, batchNum, err := getUnwindPoint(eriDb, hermezDb, ancestorBlockNum, ancestorBlockHash) + if err != nil { + return err + } + + if err = stages.SaveStageProgress(tx, stages.HighestSeenBatchNumber, batchNum-1); err != nil { + return err + } + log.Warn(fmt.Sprintf("[%s] Unwinding to block %d (%s)", logPrefix, unwindBlockNum, unwindBlockHash)) + u.UnwindTo(unwindBlockNum, unwindBlockHash) + return nil +} + +// findCommonAncestor searches the latest common ancestor block number and hash between the data stream and the local db. +// The common ancestor block is the one that matches both l2 block hash and batch number. +func findCommonAncestor( + db erigon_db.ReadOnlyErigonDb, + hermezDb state.ReadOnlyHermezDb, + dsClient DatastreamClient, + latestBlockNum uint64) (uint64, common.Hash, error) { + var ( + startBlockNum = uint64(0) + endBlockNum = latestBlockNum + blockNumber *uint64 + blockHash common.Hash + ) + + if latestBlockNum == 0 { + return 0, emptyHash, ErrFailedToFindCommonAncestor + } + + for startBlockNum <= endBlockNum { + if endBlockNum == 0 { + return 0, emptyHash, ErrFailedToFindCommonAncestor + } + + midBlockNum := (startBlockNum + endBlockNum) / 2 + midBlockDataStream, errCode, err := dsClient.GetL2BlockByNumber(midBlockNum) + if err != nil && + // the required block might not be in the data stream, so ignore that error + errCode != types.CmdErrBadFromBookmark { + return 0, emptyHash, err + } + + midBlockDbHash, err := db.ReadCanonicalHash(midBlockNum) + if err != nil { + return 0, emptyHash, err + } + + dbBatchNum, err := hermezDb.GetBatchNoByL2Block(midBlockNum) + if err != nil { + return 0, emptyHash, err + } + + if midBlockDataStream != nil && + midBlockDataStream.L2Blockhash == midBlockDbHash && + midBlockDataStream.BatchNumber == dbBatchNum { + startBlockNum = midBlockNum + 1 + + blockNumber = &midBlockNum + blockHash = midBlockDbHash + } else { + endBlockNum = midBlockNum - 1 + } + } + + if blockNumber == nil { + return 0, emptyHash, ErrFailedToFindCommonAncestor + } + + return *blockNumber, blockHash, nil +} + +// getUnwindPoint resolves the unwind block as the latest block in the previous batch, relative to the provided block. +func getUnwindPoint(eriDb erigon_db.ReadOnlyErigonDb, hermezDb state.ReadOnlyHermezDb, blockNum uint64, blockHash common.Hash) (uint64, common.Hash, uint64, error) { + batchNum, err := hermezDb.GetBatchNoByL2Block(blockNum) + if err != nil { + return 0, emptyHash, 0, err + } + + if batchNum == 0 { + return 0, emptyHash, 0, + fmt.Errorf("failed to find batch number for the block %d (%s)", blockNum, blockHash) + } + + unwindBlockNum, _, err := hermezDb.GetHighestBlockInBatch(batchNum - 1) + if err != nil { + return 0, emptyHash, 0, err + } + + unwindBlockHash, err := eriDb.ReadCanonicalHash(unwindBlockNum) + if err != nil { + return 0, emptyHash, 0, err + } + + return unwindBlockNum, unwindBlockHash, batchNum, nil +} + +// newStreamClient instantiates new datastreamer client and starts it. +func newStreamClient(ctx context.Context, cfg BatchesCfg, latestForkId uint64) (DatastreamClient, error) { + var ( + dsClient DatastreamClient + err error + ) + + if cfg.dsQueryClientCreator != nil { + dsClient, err = cfg.dsQueryClientCreator(ctx, cfg.zkCfg, latestForkId) + if err != nil { + return nil, fmt.Errorf("failed to create a datastream client. Reason: %w", err) + } + } else { + zkCfg := cfg.zkCfg + dsClient = client.NewClient(ctx, zkCfg.L2DataStreamerUrl, zkCfg.DatastreamVersion, zkCfg.L2DataStreamerTimeout, uint16(latestForkId)) + } + + if err := dsClient.Start(); err != nil { + return nil, fmt.Errorf("failed to start a datastream client. Reason: %w", err) + } + + return dsClient, nil +} diff --git a/zk/stages/stage_batches_test.go b/zk/stages/stage_batches_test.go index 58221da1c8b..1bc39ca895f 100644 --- a/zk/stages/stage_batches_test.go +++ b/zk/stages/stage_batches_test.go @@ -3,15 +3,18 @@ package stages import ( "context" "encoding/hex" + "strings" "testing" "github.com/gateway-fm/cdk-erigon-lib/common" "github.com/gateway-fm/cdk-erigon-lib/kv" "github.com/gateway-fm/cdk-erigon-lib/kv/memdb" + "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/eth/stagedsync" "github.com/ledgerwatch/erigon/eth/stagedsync/stages" "github.com/ledgerwatch/erigon/smt/pkg/db" "github.com/ledgerwatch/erigon/zk/datastream/types" + "github.com/ledgerwatch/erigon/zk/erigon_db" "github.com/ledgerwatch/erigon/zk/hermez_db" "github.com/ledgerwatch/erigon/eth/ethconfig" @@ -19,36 +22,9 @@ import ( ) func TestUnwindBatches(t *testing.T) { - fullL2Blocks := []types.FullL2Block{} - post155 := "0xf86780843b9aca00826163941275fbb540c8efc58b812ba83b0d0b8b9917ae98808464fbb77c1ba0b7d2a666860f3c6b8f5ef96f86c7ec5562e97fd04c2e10f3755ff3a0456f9feba0246df95217bf9082f84f9e40adb0049c6664a5bb4c9cbe34ab1a73e77bab26ed" - post155Bytes, err := hex.DecodeString(post155[2:]) currentBlockNumber := 10 + fullL2Blocks := createTestL2Blocks(t, currentBlockNumber) - require.NoError(t, err) - for i := 1; i <= currentBlockNumber; i++ { - fullL2Blocks = append(fullL2Blocks, types.FullL2Block{ - BatchNumber: 1 + uint64(i/2), - L2BlockNumber: uint64(i), - Timestamp: int64(i) * 10000, - DeltaTimestamp: uint32(i) * 10, - L1InfoTreeIndex: uint32(i) + 20, - GlobalExitRoot: common.Hash{byte(i)}, - Coinbase: common.Address{byte(i)}, - ForkId: 1 + uint64(i)/3, - L1BlockHash: common.Hash{byte(i)}, - L2Blockhash: common.Hash{byte(i)}, - StateRoot: common.Hash{byte(i)}, - L2Txs: []types.L2TransactionProto{ - { - EffectiveGasPricePercentage: 255, - IsValid: true, - IntermediateStateRoot: common.Hash{byte(i + 1)}, - Encoded: post155Bytes, - }, - }, - ParentHash: common.Hash{byte(i - 1)}, - }) - } gerUpdates := []types.GerUpdate{} for i := currentBlockNumber + 1; i <= currentBlockNumber+5; i++ { gerUpdates = append(gerUpdates, types.GerUpdate{ @@ -64,18 +40,25 @@ func TestUnwindBatches(t *testing.T) { ctx, db1 := context.Background(), memdb.NewTestDB(t) tx := memdb.BeginRw(t, db1) - err = hermez_db.CreateHermezBuckets(tx) + err := hermez_db.CreateHermezBuckets(tx) require.NoError(t, err) err = db.CreateEriDbBuckets(tx) require.NoError(t, err) dsClient := NewTestDatastreamClient(fullL2Blocks, gerUpdates) - cfg := StageBatchesCfg(db1, dsClient, ðconfig.Zk{}) + + tmpDSClientCreator := func(_ context.Context, _ *ethconfig.Zk, _ uint64) (DatastreamClient, error) { + return NewTestDatastreamClient(fullL2Blocks, gerUpdates), nil + } + cfg := StageBatchesCfg(db1, dsClient, ðconfig.Zk{}, WithDSClientCreator(tmpDSClientCreator)) s := &stagedsync.StageState{ID: stages.Batches, BlockNumber: 0} u := &stagedsync.Sync{} us := &stagedsync.UnwindState{ID: stages.Batches, UnwindPoint: 0, CurrentBlockNumber: uint64(currentBlockNumber)} + hDB := hermez_db.NewHermezDb(tx) + err = hDB.WriteBlockBatch(0, 0) + require.NoError(t, err) err = stages.SaveStageProgress(tx, stages.L1VerificationsBatchNo, 20) require.NoError(t, err) @@ -125,6 +108,134 @@ func TestUnwindBatches(t *testing.T) { } size, err := tx3.BucketSize(bucket) require.NoError(t, err) - require.Equal(t, bucketSized[bucket], size, "butcket %s is not empty", bucket) + require.Equal(t, bucketSized[bucket], size, "bucket %s is not empty", bucket) + } +} + +func TestFindCommonAncestor(t *testing.T) { + blocksCount := 40 + l2Blocks := createTestL2Blocks(t, blocksCount) + + testCases := []struct { + name string + dbBlocksCount int + dsBlocksCount int + latestBlockNum uint64 + divergentBlockHistory bool + expectedBlockNum uint64 + expectedHash common.Hash + expectedError error + }{ + { + name: "Successful search (db lagging behind the data stream)", + dbBlocksCount: 5, + dsBlocksCount: 10, + latestBlockNum: 5, + expectedBlockNum: 5, + expectedHash: common.Hash{byte(5)}, + expectedError: nil, + }, + { + name: "Successful search (db leading the data stream)", + dbBlocksCount: 20, + dsBlocksCount: 10, + latestBlockNum: 10, + expectedBlockNum: 10, + expectedHash: common.Hash{byte(10)}, + expectedError: nil, + }, + { + name: "Failed to find common ancestor block (latest block number is 0)", + dbBlocksCount: 10, + dsBlocksCount: 10, + latestBlockNum: 0, + expectedError: ErrFailedToFindCommonAncestor, + }, + { + name: "Failed to find common ancestor block (different blocks in the data stream and db)", + dbBlocksCount: 10, + dsBlocksCount: 10, + divergentBlockHistory: true, + latestBlockNum: 20, + expectedError: ErrFailedToFindCommonAncestor, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // ARRANGE + testDb, tx := memdb.NewTestTx(t) + defer testDb.Close() + defer tx.Rollback() + err := hermez_db.CreateHermezBuckets(tx) + require.NoError(t, err) + + err = db.CreateEriDbBuckets(tx) + require.NoError(t, err) + + hermezDb := hermez_db.NewHermezDb(tx) + erigonDb := erigon_db.NewErigonDb(tx) + + dsBlocks := l2Blocks[:tc.dsBlocksCount] + dbBlocks := l2Blocks[:tc.dbBlocksCount] + if tc.divergentBlockHistory { + dbBlocks = l2Blocks[tc.dsBlocksCount : tc.dbBlocksCount+tc.dsBlocksCount] + } + + dsClient := NewTestDatastreamClient(dsBlocks, nil) + for _, l2Block := range dbBlocks { + require.NoError(t, hermezDb.WriteBlockBatch(l2Block.L2BlockNumber, l2Block.BatchNumber)) + require.NoError(t, rawdb.WriteCanonicalHash(tx, l2Block.L2Blockhash, l2Block.L2BlockNumber)) + } + + // ACT + ancestorNum, ancestorHash, err := findCommonAncestor(erigonDb, hermezDb, dsClient, tc.latestBlockNum) + + // ASSERT + if tc.expectedError != nil { + require.Error(t, err) + require.Equal(t, tc.expectedError.Error(), err.Error()) + require.Equal(t, uint64(0), ancestorNum) + require.Equal(t, emptyHash, ancestorHash) + } else { + require.NoError(t, err) + require.Equal(t, tc.expectedBlockNum, ancestorNum) + require.Equal(t, tc.expectedHash, ancestorHash) + } + }) + } +} + +func createTestL2Blocks(t *testing.T, blocksCount int) []types.FullL2Block { + post155 := "0xf86780843b9aca00826163941275fbb540c8efc58b812ba83b0d0b8b9917ae98808464fbb77c1ba0b7d2a666860f3c6b8f5ef96f86c7ec5562e97fd04c2e10f3755ff3a0456f9feba0246df95217bf9082f84f9e40adb0049c6664a5bb4c9cbe34ab1a73e77bab26ed" + post155Bytes, err := hex.DecodeString(strings.TrimPrefix(post155, "0x")) + require.NoError(t, err) + + l2Blocks := make([]types.FullL2Block, 0, blocksCount) + for i := 1; i <= blocksCount; i++ { + l2Blocks = append(l2Blocks, types.FullL2Block{ + BatchNumber: 1 + uint64(i/2), + L2BlockNumber: uint64(i), + Timestamp: int64(i) * 10000, + DeltaTimestamp: uint32(i) * 10, + L1InfoTreeIndex: uint32(i) + 20, + GlobalExitRoot: common.Hash{byte(i)}, + Coinbase: common.Address{byte(i)}, + ForkId: 1 + uint64(i)/3, + L1BlockHash: common.Hash{byte(i)}, + L2Blockhash: common.Hash{byte(i)}, + StateRoot: common.Hash{byte(i)}, + L2Txs: []types.L2TransactionProto{ + { + EffectiveGasPricePercentage: 255, + IsValid: true, + IntermediateStateRoot: common.Hash{byte(i + 1)}, + Encoded: post155Bytes, + }, + }, + ParentHash: common.Hash{byte(i - 1)}, + }) } + + return l2Blocks } diff --git a/zk/stages/stage_dataStreamCatchup.go b/zk/stages/stage_dataStreamCatchup.go index 199fe4d69d4..ec22c414585 100644 --- a/zk/stages/stage_dataStreamCatchup.go +++ b/zk/stages/stage_dataStreamCatchup.go @@ -11,8 +11,8 @@ import ( "github.com/ledgerwatch/erigon/eth/stagedsync/stages" "github.com/ledgerwatch/erigon/zk/datastream/server" "github.com/ledgerwatch/erigon/zk/hermez_db" - "github.com/ledgerwatch/log/v3" "github.com/ledgerwatch/erigon/zk/sequencer" + "github.com/ledgerwatch/log/v3" ) type DataStreamCatchupCfg struct { @@ -91,6 +91,15 @@ func CatchupDatastream(ctx context.Context, logPrefix string, tx kv.RwTx, stream if err != nil { return 0, err } + + // this handles a case where the node was synced in RPC mode without a datastream + // in this case the datastream progress will be 0 but it still should catch up + if finalBlockNumber == 0 { + finalBlockNumber, err = stages.GetStageProgress(tx, stages.Execution) + if err != nil { + return 0, err + } + } } else { finalBlockNumber, err = stages.GetStageProgress(tx, stages.Execution) if err != nil { diff --git a/zk/stages/stage_interhashes.go b/zk/stages/stage_interhashes.go index a20e42e6d00..fe0ab0dbf5d 100644 --- a/zk/stages/stage_interhashes.go +++ b/zk/stages/stage_interhashes.go @@ -405,9 +405,6 @@ func zkIncrementIntermediateHashes(ctx context.Context, logPrefix string, s *sta if len(ach) > 0 { hexcc := "0x" + ach codeChanges[addr] = hexcc - if err != nil { - return trie.EmptyRoot, err - } } } @@ -684,20 +681,9 @@ func processAccount(db smt.DB, a *accounts.Account, as map[string]string, inc ui } func insertContractBytecodeToKV(db smt.DB, keys []utils.NodeKey, ethAddr string, bytecode string) ([]utils.NodeKey, error) { - keyContractCode, err := utils.KeyContractCode(ethAddr) - if err != nil { - return []utils.NodeKey{}, err - } - - keyContractLength, err := utils.KeyContractLength(ethAddr) - if err != nil { - return []utils.NodeKey{}, err - } - - hashedBytecode, err := utils.HashContractBytecode(bytecode) - if err != nil { - return []utils.NodeKey{}, err - } + keyContractCode := utils.KeyContractCode(ethAddr) + keyContractLength := utils.KeyContractLength(ethAddr) + hashedBytecode := utils.HashContractBytecode(bytecode) parsedBytecode := strings.TrimPrefix(bytecode, "0x") if len(parsedBytecode)%2 != 0 { @@ -746,10 +732,7 @@ func insertContractStorageToKV(db smt.DB, keys []utils.NodeKey, ethAddr string, continue } - keyStoragePosition, err := utils.KeyContractStorage(add, k) - if err != nil { - return []utils.NodeKey{}, err - } + keyStoragePosition := utils.KeyContractStorage(add, k) base := 10 if strings.HasPrefix(v, "0x") { @@ -779,14 +762,8 @@ func insertContractStorageToKV(db smt.DB, keys []utils.NodeKey, ethAddr string, } func insertAccountStateToKV(db smt.DB, keys []utils.NodeKey, ethAddr string, balance, nonce *big.Int) ([]utils.NodeKey, error) { - keyBalance, err := utils.KeyEthAddrBalance(ethAddr) - if err != nil { - return []utils.NodeKey{}, err - } - keyNonce, err := utils.KeyEthAddrNonce(ethAddr) - if err != nil { - return []utils.NodeKey{}, err - } + keyBalance := utils.KeyEthAddrBalance(ethAddr) + keyNonce := utils.KeyEthAddrNonce(ethAddr) x := utils.ScalarToArrayBig(balance) valueBalance, err := utils.NodeValue8FromBigIntArray(x) diff --git a/zk/stages/stage_l1_info_tree.go b/zk/stages/stage_l1_info_tree.go index b16c3982bf4..d944ffb8c63 100644 --- a/zk/stages/stage_l1_info_tree.go +++ b/zk/stages/stage_l1_info_tree.go @@ -39,13 +39,14 @@ func SpawnL1InfoTreeStage( cfg L1InfoTreeCfg, ctx context.Context, quiet bool, -) (err error) { +) (funcErr error) { logPrefix := s.LogPrefix() log.Info(fmt.Sprintf("[%s] Starting L1 Info Tree stage", logPrefix)) defer log.Info(fmt.Sprintf("[%s] Finished L1 Info Tree stage", logPrefix)) freshTx := tx == nil if freshTx { + var err error tx, err = cfg.db.BeginRw(ctx) if err != nil { return err @@ -69,7 +70,14 @@ func SpawnL1InfoTreeStage( } if !cfg.syncer.IsSyncStarted() { - cfg.syncer.Run(progress) + cfg.syncer.RunQueryBlocks(progress) + defer func() { + if funcErr != nil { + cfg.syncer.StopQueryBlocks() + cfg.syncer.ConsumeQueryBlocks() + cfg.syncer.WaitQueryBlocksToFinish() + } + }() } logChan := cfg.syncer.GetLogsChan() @@ -115,7 +123,8 @@ LOOP: tree, err := initialiseL1InfoTree(hermezDb) if err != nil { - return err + funcErr = err + return funcErr } // process the logs in chunks @@ -128,7 +137,8 @@ LOOP: headersMap, err := cfg.syncer.L1QueryHeaders(chunk) if err != nil { - return err + funcErr = err + return funcErr } for _, l := range chunk { @@ -136,15 +146,16 @@ LOOP: case contracts.UpdateL1InfoTreeTopic: header := headersMap[l.BlockNumber] if header == nil { - header, err = cfg.syncer.GetHeader(l.BlockNumber) - if err != nil { - return err + header, funcErr = cfg.syncer.GetHeader(l.BlockNumber) + if funcErr != nil { + return funcErr } } tmpUpdate, err := CreateL1InfoTreeUpdate(l, header) if err != nil { - return err + funcErr = err + return funcErr } leafHash := l1infotree.HashLeafData(tmpUpdate.GER, tmpUpdate.ParentHash, tmpUpdate.Timestamp) @@ -160,7 +171,8 @@ LOOP: newRoot, err := tree.AddLeaf(uint32(latestUpdate.Index), leafHash) if err != nil { - return err + funcErr = err + return funcErr } log.Debug("New L1 Index", "index", latestUpdate.Index, @@ -171,14 +183,14 @@ LOOP: "parent", latestUpdate.ParentHash.String(), ) - if err = HandleL1InfoTreeUpdate(hermezDb, latestUpdate); err != nil { - return err + if funcErr = HandleL1InfoTreeUpdate(hermezDb, latestUpdate); funcErr != nil { + return funcErr } - if err = hermezDb.WriteL1InfoTreeLeaf(latestUpdate.Index, leafHash); err != nil { - return err + if funcErr = hermezDb.WriteL1InfoTreeLeaf(latestUpdate.Index, leafHash); funcErr != nil { + return funcErr } - if err = hermezDb.WriteL1InfoTreeRoot(common.BytesToHash(newRoot[:]), latestUpdate.Index); err != nil { - return err + if funcErr = hermezDb.WriteL1InfoTreeRoot(common.BytesToHash(newRoot[:]), latestUpdate.Index); funcErr != nil { + return funcErr } processed++ @@ -192,15 +204,15 @@ LOOP: if len(allLogs) > 0 { progress = allLogs[len(allLogs)-1].BlockNumber + 1 } - if err := stages.SaveStageProgress(tx, stages.L1InfoTree, progress); err != nil { - return err + if funcErr = stages.SaveStageProgress(tx, stages.L1InfoTree, progress); funcErr != nil { + return funcErr } log.Info(fmt.Sprintf("[%s] Info tree updates", logPrefix), "count", len(allLogs)) if freshTx { - if err := tx.Commit(); err != nil { - return err + if funcErr = tx.Commit(); funcErr != nil { + return funcErr } } diff --git a/zk/stages/stage_l1_sequencer_sync.go b/zk/stages/stage_l1_sequencer_sync.go index 49339e8d277..2d8e7a99e28 100644 --- a/zk/stages/stage_l1_sequencer_sync.go +++ b/zk/stages/stage_l1_sequencer_sync.go @@ -42,13 +42,14 @@ func SpawnL1SequencerSyncStage( cfg L1SequencerSyncCfg, ctx context.Context, quiet bool, -) (err error) { +) (funcErr error) { logPrefix := s.LogPrefix() log.Info(fmt.Sprintf("[%s] Starting L1 Sequencer sync stage", logPrefix)) defer log.Info(fmt.Sprintf("[%s] Finished L1 Sequencer sync stage", logPrefix)) freshTx := tx == nil if freshTx { + var err error tx, err = cfg.db.BeginRw(ctx) if err != nil { return err @@ -66,12 +67,37 @@ func SpawnL1SequencerSyncStage( } if progress == 0 { progress = cfg.zkCfg.L1FirstBlock - 1 + + } + + // if the flag is set - wait for that block to be finalized on L1 before continuing + if progress <= cfg.zkCfg.L1FinalizedBlockRequirement && cfg.zkCfg.L1FinalizedBlockRequirement > 0 { + for { + finalized, finalizedBn, err := cfg.syncer.CheckL1BlockFinalized(cfg.zkCfg.L1FinalizedBlockRequirement) + if err != nil { + // we shouldn't just throw the error, because it could be a timeout, or "too many requests" error and we could jsut retry + log.Error(fmt.Sprintf("[%s] Error checking if L1 block %v is finalized: %v", logPrefix, cfg.zkCfg.L1FinalizedBlockRequirement, err)) + } + + if finalized { + break + } + log.Info(fmt.Sprintf("[%s] Waiting for L1 block %v to be correctly checked for \"finalized\" before continuing. Current finalized is %d", logPrefix, cfg.zkCfg.L1FinalizedBlockRequirement, finalizedBn)) + time.Sleep(1 * time.Minute) // sleep could be even bigger since finalization takes more than 10 minutes + } } hermezDb := hermez_db.NewHermezDb(tx) if !cfg.syncer.IsSyncStarted() { - cfg.syncer.Run(progress) + cfg.syncer.RunQueryBlocks(progress) + defer func() { + if funcErr != nil { + cfg.syncer.StopQueryBlocks() + cfg.syncer.ConsumeQueryBlocks() + cfg.syncer.WaitQueryBlocksToFinish() + } + }() } logChan := cfg.syncer.GetLogsChan() @@ -83,22 +109,23 @@ Loop: case logs := <-logChan: headersMap, err := cfg.syncer.L1QueryHeaders(logs) if err != nil { - return err + funcErr = err + return funcErr } for _, l := range logs { header := headersMap[l.BlockNumber] switch l.Topics[0] { case contracts.InitialSequenceBatchesTopic: - if err := HandleInitialSequenceBatches(cfg.syncer, hermezDb, l, header); err != nil { - return err + if funcErr = HandleInitialSequenceBatches(cfg.syncer, hermezDb, l, header); funcErr != nil { + return funcErr } case contracts.AddNewRollupTypeTopic: rollupType := l.Topics[1].Big().Uint64() forkIdBytes := l.Data[64:96] // 3rd positioned item in the log data forkId := new(big.Int).SetBytes(forkIdBytes).Uint64() - if err := hermezDb.WriteRollupType(rollupType, forkId); err != nil { - return err + if funcErr = hermezDb.WriteRollupType(rollupType, forkId); funcErr != nil { + return funcErr } case contracts.CreateNewRollupTopic: rollupId := l.Topics[1].Big().Uint64() @@ -109,13 +136,14 @@ Loop: rollupType := new(big.Int).SetBytes(rollupTypeBytes).Uint64() fork, err := hermezDb.GetForkFromRollupType(rollupType) if err != nil { - return err + funcErr = err + return funcErr } if fork == 0 { log.Error("received CreateNewRollupTopic for unknown rollup type", "rollupType", rollupType) } - if err := hermezDb.WriteNewForkHistory(fork, 0); err != nil { - return err + if funcErr = hermezDb.WriteNewForkHistory(fork, 0); funcErr != nil { + return funcErr } case contracts.UpdateRollupTopic: rollupId := l.Topics[1].Big().Uint64() @@ -126,15 +154,17 @@ Loop: newRollup := new(big.Int).SetBytes(newRollupBytes).Uint64() fork, err := hermezDb.GetForkFromRollupType(newRollup) if err != nil { - return err + funcErr = err + return funcErr } if fork == 0 { - return fmt.Errorf("received UpdateRollupTopic for unknown rollup type: %v", newRollup) + funcErr = fmt.Errorf("received UpdateRollupTopic for unknown rollup type: %v", newRollup) + return funcErr } latestVerifiedBytes := l.Data[32:64] latestVerified := new(big.Int).SetBytes(latestVerifiedBytes).Uint64() - if err := hermezDb.WriteNewForkHistory(fork, latestVerified); err != nil { - return err + if funcErr = hermezDb.WriteNewForkHistory(fork, latestVerified); funcErr != nil { + return funcErr } default: log.Warn("received unexpected topic from l1 sequencer sync stage", "topic", l.Topics[0]) @@ -150,21 +180,19 @@ Loop: } } - cfg.syncer.Stop() - progress = cfg.syncer.GetLastCheckedL1Block() if progress >= cfg.zkCfg.L1FirstBlock { // do not save progress if progress less than L1FirstBlock - if err = stages.SaveStageProgress(tx, stages.L1SequencerSync, progress); err != nil { - return err + if funcErr = stages.SaveStageProgress(tx, stages.L1SequencerSync, progress); funcErr != nil { + return funcErr } } log.Info(fmt.Sprintf("[%s] L1 Sequencer sync finished", logPrefix)) if freshTx { - if err = tx.Commit(); err != nil { - return err + if funcErr = tx.Commit(); funcErr != nil { + return funcErr } } diff --git a/zk/stages/stage_l1syncer.go b/zk/stages/stage_l1syncer.go index d7edde1d5ac..82876015db4 100644 --- a/zk/stages/stage_l1syncer.go +++ b/zk/stages/stage_l1syncer.go @@ -38,8 +38,11 @@ type IL1Syncer interface { L1QueryHeaders(logs []ethTypes.Log) (map[uint64]*ethTypes.Header, error) GetBlock(number uint64) (*ethTypes.Block, error) GetHeader(number uint64) (*ethTypes.Header, error) - Run(lastCheckedBlock uint64) - Stop() + RunQueryBlocks(lastCheckedBlock uint64) + StopQueryBlocks() + ConsumeQueryBlocks() + WaitQueryBlocksToFinish() + CheckL1BlockFinalized(blockNo uint64) (bool, uint64, error) } var ( @@ -70,8 +73,7 @@ func SpawnStageL1Syncer( tx kv.RwTx, cfg L1SyncerCfg, quiet bool, -) error { - +) (funcErr error) { ///// DEBUG BISECT ///// if cfg.zkCfg.DebugLimit > 0 { return nil @@ -114,7 +116,14 @@ func SpawnStageL1Syncer( } // start the syncer - cfg.syncer.Run(l1BlockProgress) + cfg.syncer.RunQueryBlocks(l1BlockProgress) + defer func() { + if funcErr != nil { + cfg.syncer.StopQueryBlocks() + cfg.syncer.ConsumeQueryBlocks() + cfg.syncer.WaitQueryBlocksToFinish() + } + }() } logsChan := cfg.syncer.GetLogsChan() @@ -128,22 +137,20 @@ Loop: for { select { case logs := <-logsChan: - infos := make([]*types.L1BatchInfo, 0, len(logs)) - batchLogTypes := make([]BatchLogType, 0, len(logs)) for _, l := range logs { l := l info, batchLogType := parseLogType(cfg.zkCfg.L1RollupId, &l) - infos = append(infos, &info) - batchLogTypes = append(batchLogTypes, batchLogType) - } - - for i, l := range logs { - info := *infos[i] - batchLogType := batchLogTypes[i] switch batchLogType { case logSequence: - if err := hermezDb.WriteSequence(info.L1BlockNo, info.BatchNo, info.L1TxHash, info.StateRoot); err != nil { - return fmt.Errorf("failed to write batch info, %w", err) + fallthrough + case logSequenceEtrog: + // prevent storing pre-etrog sequences for etrog rollups + if batchLogType == logSequence && cfg.zkCfg.L1RollupId > 1 { + continue + } + if err := hermezDb.WriteSequence(info.L1BlockNo, info.BatchNo, info.L1TxHash, info.StateRoot, info.L1InfoRoot); err != nil { + funcErr = fmt.Errorf("failed to write batch info, %w", err) + return funcErr } if info.L1BlockNo > highestWrittenL1BlockNo { highestWrittenL1BlockNo = info.L1BlockNo @@ -151,17 +158,25 @@ Loop: newSequencesCount++ case logRollbackBatches: if err := hermezDb.RollbackSequences(info.BatchNo); err != nil { - return fmt.Errorf("failed to write rollback sequence, %w", err) + funcErr = fmt.Errorf("failed to write rollback sequence, %w", err) + return funcErr } if info.L1BlockNo > highestWrittenL1BlockNo { highestWrittenL1BlockNo = info.L1BlockNo } case logVerify: + fallthrough + case logVerifyEtrog: + // prevent storing pre-etrog verifications for etrog rollups + if batchLogType == logVerify && cfg.zkCfg.L1RollupId > 1 { + continue + } if info.BatchNo > highestVerification.BatchNo { highestVerification = info } if err := hermezDb.WriteVerification(info.L1BlockNo, info.BatchNo, info.L1TxHash, info.StateRoot); err != nil { - return fmt.Errorf("failed to write verification for block %d, %w", info.L1BlockNo, err) + funcErr = fmt.Errorf("failed to write verification for block %d, %w", info.L1BlockNo, err) + return funcErr } if info.L1BlockNo > highestWrittenL1BlockNo { highestWrittenL1BlockNo = info.L1BlockNo @@ -191,7 +206,8 @@ Loop: log.Info(fmt.Sprintf("[%s] Saving L1 syncer progress", logPrefix), "latestCheckedBlock", latestCheckedBlock, "newVerificationsCount", newVerificationsCount, "newSequencesCount", newSequencesCount, "highestWrittenL1BlockNo", highestWrittenL1BlockNo) if err := stages.SaveStageProgress(tx, stages.L1Syncer, highestWrittenL1BlockNo); err != nil { - return fmt.Errorf("failed to save stage progress, %w", err) + funcErr = fmt.Errorf("failed to save stage progress, %w", err) + return funcErr } if highestVerification.BatchNo > 0 { log.Info(fmt.Sprintf("[%s]", logPrefix), "highestVerificationBatchNo", highestVerification.BatchNo) @@ -215,7 +231,8 @@ Loop: if internalTxOpened { log.Debug("l1 sync: first cycle, committing tx") if err := tx.Commit(); err != nil { - return fmt.Errorf("failed to commit tx, %w", err) + funcErr = fmt.Errorf("failed to commit tx, %w", err) + return funcErr } } @@ -227,17 +244,16 @@ type BatchLogType byte var ( logUnknown BatchLogType = 0 logSequence BatchLogType = 1 - logVerify BatchLogType = 2 - logL1InfoTreeUpdate BatchLogType = 4 - logRollbackBatches BatchLogType = 5 + logSequenceEtrog BatchLogType = 2 + logVerify BatchLogType = 3 + logVerifyEtrog BatchLogType = 4 + logL1InfoTreeUpdate BatchLogType = 5 + logRollbackBatches BatchLogType = 6 logIncompatible BatchLogType = 100 ) func parseLogType(l1RollupId uint64, log *ethTypes.Log) (l1BatchInfo types.L1BatchInfo, batchLogType BatchLogType) { - bigRollupId := new(big.Int).SetUint64(l1RollupId) - isRollupIdMatching := log.Topics[1] == common.BigToHash(bigRollupId) - var ( batchNum uint64 stateRoot, l1InfoRoot common.Hash @@ -248,7 +264,7 @@ func parseLogType(l1RollupId uint64, log *ethTypes.Log) (l1BatchInfo types.L1Bat batchLogType = logSequence batchNum = new(big.Int).SetBytes(log.Topics[1].Bytes()).Uint64() case contracts.SequencedBatchTopicEtrog: - batchLogType = logSequence + batchLogType = logSequenceEtrog batchNum = new(big.Int).SetBytes(log.Topics[1].Bytes()).Uint64() l1InfoRoot = common.BytesToHash(log.Data[:32]) case contracts.VerificationTopicPreEtrog: @@ -256,16 +272,20 @@ func parseLogType(l1RollupId uint64, log *ethTypes.Log) (l1BatchInfo types.L1Bat batchNum = new(big.Int).SetBytes(log.Topics[1].Bytes()).Uint64() stateRoot = common.BytesToHash(log.Data[:32]) case contracts.VerificationValidiumTopicEtrog: + bigRollupId := new(big.Int).SetUint64(l1RollupId) + isRollupIdMatching := log.Topics[1] == common.BigToHash(bigRollupId) if isRollupIdMatching { - batchLogType = logVerify + batchLogType = logVerifyEtrog batchNum = new(big.Int).SetBytes(log.Topics[1].Bytes()).Uint64() stateRoot = common.BytesToHash(log.Data[:32]) } else { batchLogType = logIncompatible } case contracts.VerificationTopicEtrog: + bigRollupId := new(big.Int).SetUint64(l1RollupId) + isRollupIdMatching := log.Topics[1] == common.BigToHash(bigRollupId) if isRollupIdMatching { - batchLogType = logVerify + batchLogType = logVerifyEtrog batchNum = common.BytesToHash(log.Data[:32]).Big().Uint64() stateRoot = common.BytesToHash(log.Data[32:64]) } else { @@ -335,7 +355,7 @@ func verifyAgainstLocalBlocks(tx kv.RwTx, hermezDb *hermez_db.HermezDb, logPrefi // in this case we need to find the blocknumber that is highest for the last batch // get the batch of the last hashed block hashedBatch, err := hermezDb.GetBatchNoByL2Block(hashedBlockNo) - if err != nil { + if err != nil && !errors.Is(err, hermez_db.ErrorNotStored) { return err } diff --git a/zk/stages/stage_sequence_execute.go b/zk/stages/stage_sequence_execute.go index 7a757a1a7ec..bad1c5efb65 100644 --- a/zk/stages/stage_sequence_execute.go +++ b/zk/stages/stage_sequence_execute.go @@ -15,9 +15,13 @@ import ( "github.com/ledgerwatch/erigon/eth/stagedsync" "github.com/ledgerwatch/erigon/eth/stagedsync/stages" "github.com/ledgerwatch/erigon/zk" + zktx "github.com/ledgerwatch/erigon/zk/tx" "github.com/ledgerwatch/erigon/zk/utils" ) +// we must perform execution and datastream alignment only during first run of this stage +var shouldCheckForExecutionAndDataStreamAlighmentOnNodeStart = true + func SpawnSequencingStage( s *stagedsync.StageState, u stagedsync.Unwinder, @@ -25,6 +29,78 @@ func SpawnSequencingStage( cfg SequenceBlockCfg, historyCfg stagedsync.HistoryCfg, quiet bool, +) (err error) { + roTx, err := cfg.db.BeginRo(ctx) + if err != nil { + return err + } + defer roTx.Rollback() + + lastBatch, err := stages.GetStageProgress(roTx, stages.HighestSeenBatchNumber) + if err != nil { + return err + } + + highestBatchInDS, err := cfg.datastreamServer.GetHighestBatchNumber() + if err != nil { + return err + } + + if !cfg.zk.SequencerResequence || lastBatch >= highestBatchInDS { + if cfg.zk.SequencerResequence { + log.Info(fmt.Sprintf("[%s] Resequencing completed. Please restart sequencer without resequence flag.", s.LogPrefix())) + time.Sleep(10 * time.Second) + return nil + } + + err = sequencingStageStep(s, u, ctx, cfg, historyCfg, quiet, nil) + if err != nil { + return err + } + } else { + log.Info(fmt.Sprintf("[%s] Last batch %d is lower than highest batch in datastream %d, resequencing...", s.LogPrefix(), lastBatch, highestBatchInDS)) + + batches, err := cfg.datastreamServer.ReadBatches(lastBatch+1, highestBatchInDS) + if err != nil { + return err + } + + err = cfg.datastreamServer.UnwindToBatchStart(lastBatch + 1) + if err != nil { + return err + } + + log.Info(fmt.Sprintf("[%s] Resequence from batch %d to %d in data stream", s.LogPrefix(), lastBatch+1, highestBatchInDS)) + + for _, batch := range batches { + batchJob := NewResequenceBatchJob(batch) + subBatchCount := 0 + for batchJob.HasMoreBlockToProcess() { + if err = sequencingStageStep(s, u, ctx, cfg, historyCfg, quiet, batchJob); err != nil { + return err + } + + subBatchCount += 1 + } + + log.Info(fmt.Sprintf("[%s] Resequenced original batch %d with %d batches", s.LogPrefix(), batchJob.batchToProcess[0].BatchNumber, subBatchCount)) + if cfg.zk.SequencerResequenceStrict && subBatchCount != 1 { + return fmt.Errorf("strict mode enabled, but resequenced batch %d has %d sub-batches", batchJob.batchToProcess[0].BatchNumber, subBatchCount) + } + } + } + + return nil +} + +func sequencingStageStep( + s *stagedsync.StageState, + u stagedsync.Unwinder, + ctx context.Context, + cfg SequenceBlockCfg, + historyCfg stagedsync.HistoryCfg, + quiet bool, + resequenceBatchJob *ResequenceBatchJob, ) (err error) { logPrefix := s.LogPrefix() log.Info(fmt.Sprintf("[%s] Starting sequencing stage", logPrefix)) @@ -66,9 +142,9 @@ func SpawnSequencingStage( var block *types.Block runLoopBlocks := true batchContext := newBatchContext(ctx, &cfg, &historyCfg, s, sdb) - batchState := newBatchState(forkId, batchNumberForStateInitialization, cfg.zk.HasExecutors(), cfg.zk.L1SyncStartBlock > 0, cfg.txPool) + batchState := newBatchState(forkId, batchNumberForStateInitialization, executionAt+1, cfg.zk.HasExecutors(), cfg.zk.L1SyncStartBlock > 0, cfg.txPool, resequenceBatchJob) blockDataSizeChecker := newBlockDataChecker() - streamWriter := newSequencerBatchStreamWriter(batchContext, batchState, lastBatch) // using lastBatch (rather than batchState.batchNumber) is not mistake + streamWriter := newSequencerBatchStreamWriter(batchContext, batchState) // injected batch if executionAt == 0 { @@ -86,20 +162,30 @@ func SpawnSequencingStage( return sdb.tx.Commit() } - // handle cases where the last batch wasn't committed to the data stream. - // this could occur because we're migrating from an RPC node to a sequencer - // or because the sequencer was restarted and not all processes completed (like waiting from remote executor) - // we consider the data stream as verified by the executor so treat it as "safe" and unwind blocks beyond there - // if we identify any. During normal operation this function will simply check and move on without performing - // any action. - if !batchState.isAnyRecovery() { - isUnwinding, err := alignExecutionToDatastream(batchContext, batchState, executionAt, u) - if err != nil { - return err - } - if isUnwinding { - return sdb.tx.Commit() + if shouldCheckForExecutionAndDataStreamAlighmentOnNodeStart { + // handle cases where the last batch wasn't committed to the data stream. + // this could occur because we're migrating from an RPC node to a sequencer + // or because the sequencer was restarted and not all processes completed (like waiting from remote executor) + // we consider the data stream as verified by the executor so treat it as "safe" and unwind blocks beyond there + // if we identify any. During normal operation this function will simply check and move on without performing + // any action. + if !batchState.isAnyRecovery() { + isUnwinding, err := alignExecutionToDatastream(batchContext, batchState, executionAt, u) + if err != nil { + // do not set shouldCheckForExecutionAndDataStreamAlighmentOnNodeStart=false because of the error + return err + } + if isUnwinding { + err = sdb.tx.Commit() + if err != nil { + // do not set shouldCheckForExecutionAndDataStreamAlighmentOnNodeStart=false because of the error + return err + } + shouldCheckForExecutionAndDataStreamAlighmentOnNodeStart = false + return nil + } } + shouldCheckForExecutionAndDataStreamAlighmentOnNodeStart = false } tryHaltSequencer(batchContext, batchState.batchNumber) @@ -108,7 +194,7 @@ func SpawnSequencingStage( return err } - batchCounters, err := prepareBatchCounters(batchContext, batchState, nil) + batchCounters, err := prepareBatchCounters(batchContext, batchState) if err != nil { return err } @@ -163,6 +249,18 @@ func SpawnSequencingStage( } } + if batchState.isResequence() { + if !batchState.resequenceBatchJob.HasMoreBlockToProcess() { + for streamWriter.legacyVerifier.HasPendingVerifications() { + streamWriter.CommitNewUpdates() + time.Sleep(1 * time.Second) + } + + runLoopBlocks = false + break + } + } + header, parentBlock, err := prepareHeader(sdb.tx, blockNumber-1, batchState.blockState.getDeltaTimestamp(), batchState.getBlockHeaderForcedTimestamp(), batchState.forkId, batchState.getCoinbase(&cfg)) if err != nil { return err @@ -176,7 +274,7 @@ func SpawnSequencingStage( // timer: evm + smt t := utils.StartTimer("stage_sequence_execute", "evm", "smt") - infoTreeIndexProgress, l1TreeUpdate, l1TreeUpdateIndex, l1BlockHash, ger, shouldWriteGerToContract, err := prepareL1AndInfoTreeRelatedStuff(sdb, batchState, header.Time) + infoTreeIndexProgress, l1TreeUpdate, l1TreeUpdateIndex, l1BlockHash, ger, shouldWriteGerToContract, err := prepareL1AndInfoTreeRelatedStuff(sdb, batchState, header.Time, cfg.zk.SequencerResequenceReuseL1InfoIndex) if err != nil { return err } @@ -185,7 +283,7 @@ func SpawnSequencingStage( if err != nil { return err } - if !batchState.isAnyRecovery() && overflowOnNewBlock { + if (!batchState.isAnyRecovery() || batchState.isResequence()) && overflowOnNewBlock { break } @@ -227,7 +325,7 @@ func SpawnSequencingStage( if err != nil { return err } - } else if !batchState.isL1Recovery() { + } else if !batchState.isL1Recovery() && !batchState.isResequence() { var allConditionsOK bool batchState.blockState.transactionsForInclusion, allConditionsOK, err = getNextPoolTransactions(ctx, cfg, executionAt, batchState.forkId, batchState.yieldedTransactions) if err != nil { @@ -243,6 +341,17 @@ func SpawnSequencingStage( } else { log.Trace(fmt.Sprintf("[%s] Yielded transactions from the pool", logPrefix), "txCount", len(batchState.blockState.transactionsForInclusion)) } + } else if batchState.isResequence() { + batchState.blockState.transactionsForInclusion, err = batchState.resequenceBatchJob.YieldNextBlockTransactions(zktx.DecodeTx) + if err != nil { + return err + } + } + + if len(batchState.blockState.transactionsForInclusion) == 0 { + time.Sleep(batchContext.cfg.zk.SequencerTimeoutOnEmptyTxPool) + } else { + log.Trace(fmt.Sprintf("[%s] Yielded transactions from the pool", logPrefix), "txCount", len(batchState.blockState.transactionsForInclusion)) } for i, transaction := range batchState.blockState.transactionsForInclusion { @@ -257,6 +366,18 @@ func SpawnSequencingStage( panic("limbo transaction has already been executed once so they must not fail while re-executing") } + if batchState.isResequence() { + if cfg.zk.SequencerResequenceStrict { + return fmt.Errorf("strict mode enabled, but resequenced batch %d failed to add transaction %s: %v", batchState.batchNumber, txHash, err) + } else { + log.Warn(fmt.Sprintf("[%s] error adding transaction to batch during resequence: %v", logPrefix, err), + "hash", txHash, + "to", transaction.GetTo(), + ) + continue + } + } + // if we are in recovery just log the error as a warning. If the data is on the L1 then we should consider it as confirmed. // The executor/prover would simply skip a TX with an invalid nonce for example so we don't need to worry about that here. if batchState.isL1Recovery() { @@ -272,38 +393,84 @@ func SpawnSequencingStage( // Each transaction in yielded will be reevaluated at the end of each batch } - if anyOverflow { + switch anyOverflow { + case overflowCounters: + // remove the last attempted counters as we may want to continue processing this batch with other transactions + batchCounters.RemovePreviousTransactionCounters() + if batchState.isLimboRecovery() { panic("limbo transaction has already been executed once so they must not overflow counters while re-executing") } if !batchState.isL1Recovery() { - log.Info(fmt.Sprintf("[%s] overflowed adding transaction to batch", logPrefix), "batch", batchState.batchNumber, "tx-hash", txHash, "has-any-transactions-in-this-batch", batchState.hasAnyTransactionsInThisBatch) /* There are two cases when overflow could occur. - 1. The block DOES not contains any transactions. + 1. The block DOES not contain any transactions. In this case it means that a single tx overflow entire zk-counters. In this case we mark it so. Once marked it will be discarded from the tx-pool async (once the tx-pool process the creation of a new batch) + Block production then continues as normal looking for more suitable transactions NB: The tx SHOULD not be removed from yielded set, because if removed, it will be picked again on next block. That's why there is i++. It ensures that removing from yielded will start after the problematic tx 2. The block contains transactions. - In this case, we just have to remove the transaction that overflowed the zk-counters and all transactions after it, from the yielded set. - This removal will ensure that these transaction could be added in the next block(s) + In this case we make note that we have had a transaction that overflowed and continue attempting to process transactions + Once we reach the cap for these attempts we will stop producing blocks and consider the batch done */ if !batchState.hasAnyTransactionsInThisBatch { + // mark the transaction to be removed from the pool cfg.txPool.MarkForDiscardFromPendingBest(txHash) - log.Info(fmt.Sprintf("single transaction %s overflow counters", txHash)) + log.Info(fmt.Sprintf("[%s] single transaction %s cannot fit into batch", logPrefix, txHash)) + } else { + batchState.newOverflowTransaction() + log.Info(fmt.Sprintf("[%s] transaction %s overflow counters", logPrefix, txHash), "count", batchState.overflowTransactions) + if batchState.reachedOverflowTransactionLimit() { + log.Info(fmt.Sprintf("[%s] closing batch due to counters", logPrefix), "count", batchState.overflowTransactions) + runLoopBlocks = false + break LOOP_TRANSACTIONS + } } - runLoopBlocks = false - break LOOP_TRANSACTIONS + // continue on processing other transactions and skip this one + continue } + if batchState.isResequence() && cfg.zk.SequencerResequenceStrict { + return fmt.Errorf("strict mode enabled, but resequenced batch %d overflowed counters on block %d", batchState.batchNumber, blockNumber) + } + case overflowGas: + if batchState.isAnyRecovery() { + panic(fmt.Sprintf("block gas limit overflow in recovery block: %d", blockNumber)) + } + log.Info(fmt.Sprintf("[%s] gas overflowed adding transaction to block", logPrefix), "block", blockNumber, "tx-hash", txHash) + runLoopBlocks = false + break LOOP_TRANSACTIONS + case overflowNone: } if err == nil { blockDataSizeChecker = &backupDataSizeChecker batchState.onAddedTransaction(transaction, receipt, execResult, effectiveGas) } + + // We will only update the processed index in resequence job if there isn't overflow + if batchState.isResequence() { + batchState.resequenceBatchJob.UpdateLastProcessedTx(txHash) + } + } + + if batchState.isResequence() { + if len(batchState.blockState.transactionsForInclusion) == 0 { + // We need to jump to the next block here if there are no transactions in current block + batchState.resequenceBatchJob.UpdateLastProcessedTx(batchState.resequenceBatchJob.CurrentBlock().L2Blockhash) + break LOOP_TRANSACTIONS + } + + if batchState.resequenceBatchJob.AtNewBlockBoundary() { + // We need to jump to the next block here if we are at the end of the current block + break LOOP_TRANSACTIONS + } else { + if cfg.zk.SequencerResequenceStrict { + return fmt.Errorf("strict mode enabled, but resequenced batch %d has transactions that overflowed counters or failed transactions", batchState.batchNumber) + } + } } if batchState.isL1Recovery() { @@ -341,9 +508,9 @@ func SpawnSequencingStage( } if gasPerSecond != 0 { - log.Info(fmt.Sprintf("[%s] Finish block %d with %d transactions... (%d gas/s)", logPrefix, blockNumber, len(batchState.blockState.builtBlockElements.transactions), int(gasPerSecond))) + log.Info(fmt.Sprintf("[%s] Finish block %d with %d transactions... (%d gas/s)", logPrefix, blockNumber, len(batchState.blockState.builtBlockElements.transactions), int(gasPerSecond)), "info-tree-index", infoTreeIndexProgress) } else { - log.Info(fmt.Sprintf("[%s] Finish block %d with %d transactions...", logPrefix, blockNumber, len(batchState.blockState.builtBlockElements.transactions))) + log.Info(fmt.Sprintf("[%s] Finish block %d with %d transactions...", logPrefix, blockNumber, len(batchState.blockState.builtBlockElements.transactions)), "info-tree-index", infoTreeIndexProgress) } // add a check to the verifier and also check for responses @@ -364,7 +531,7 @@ func SpawnSequencingStage( if err != nil { return err } - cfg.legacyVerifier.StartAsyncVerification(batchState.forkId, batchState.batchNumber, block.Root(), counters.UsedAsMap(), batchState.builtBlocks, useExecutorForVerification, batchContext.cfg.zk.SequencerBatchVerificationTimeout) + cfg.legacyVerifier.StartAsyncVerification(batchContext.s.LogPrefix(), batchState.forkId, batchState.batchNumber, block.Root(), counters.UsedAsMap(), batchState.builtBlocks, useExecutorForVerification, batchContext.cfg.zk.SequencerBatchVerificationTimeout) // check for new responses from the verifier needsUnwind, err := updateStreamAndCheckRollback(batchContext, batchState, streamWriter, u) @@ -384,12 +551,6 @@ func SpawnSequencingStage( } } - cfg.legacyVerifier.Wait() - needsUnwind, err := updateStreamAndCheckRollback(batchContext, batchState, streamWriter, u) - if err != nil || needsUnwind { - return err - } - /* if adding something below that line we must ensure - it is also handled property in processInjectedInitialBatch @@ -398,10 +559,6 @@ func SpawnSequencingStage( - it is unwound correctly */ - if err := finalizeLastBatchInDatastream(batchContext, batchState.batchNumber, block.NumberU64()); err != nil { - return err - } - // TODO: It is 99% sure that there is no need to write this in any of processInjectedInitialBatch, alignExecutionToDatastream, doCheckForBadBatch but it is worth double checknig // the unwind of this value is handed by UnwindExecutionStageDbWrites if _, err := rawdb.IncrementStateVersionByBlockNumberIfNeeded(batchContext.sdb.tx, block.NumberU64()); err != nil { diff --git a/zk/stages/stage_sequence_execute_batch.go b/zk/stages/stage_sequence_execute_batch.go index 483dd276d77..80e2c7351b5 100644 --- a/zk/stages/stage_sequence_execute_batch.go +++ b/zk/stages/stage_sequence_execute_batch.go @@ -24,16 +24,24 @@ func prepareBatchNumber(sdb *stageDb, forkId, lastBatch uint64, isL1Recovery boo return 0, err } - if len(blockNumbersInBatchSoFar) < len(recoveredBatchData.DecodedData) { - return lastBatch, nil + if len(blockNumbersInBatchSoFar) < len(recoveredBatchData.DecodedData) { // check if there are more blocks to process + isLastBatchBad, err := sdb.hermezDb.GetInvalidBatch(lastBatch) + if err != nil { + return 0, err + } + + // if last batch is not bad then continue buildingin it, otherwise return lastBatch+1 (at the end of the function) + if !isLastBatchBad { + return lastBatch, nil + } } } return lastBatch + 1, nil } -func prepareBatchCounters(batchContext *BatchContext, batchState *BatchState, intermediateUsedCounters *vm.Counters) (*vm.BatchCounterCollector, error) { - return vm.NewBatchCounterCollector(batchContext.sdb.smt.GetDepth(), uint16(batchState.forkId), batchContext.cfg.zk.VirtualCountersSmtReduction, batchContext.cfg.zk.ShouldCountersBeUnlimited(batchState.isL1Recovery()), intermediateUsedCounters), nil +func prepareBatchCounters(batchContext *BatchContext, batchState *BatchState) (*vm.BatchCounterCollector, error) { + return vm.NewBatchCounterCollector(batchContext.sdb.smt.GetDepth(), uint16(batchState.forkId), batchContext.cfg.zk.VirtualCountersSmtReduction, batchContext.cfg.zk.ShouldCountersBeUnlimited(batchState.isL1Recovery()), nil), nil } func doCheckForBadBatch(batchContext *BatchContext, batchState *BatchState, thisBlock uint64) (bool, error) { @@ -62,7 +70,7 @@ func doCheckForBadBatch(batchContext *BatchContext, batchState *BatchState, this if err = batchContext.sdb.hermezDb.WriteInvalidBatch(batchState.batchNumber); err != nil { return false, err } - if err = batchContext.sdb.hermezDb.WriteBatchCounters(currentBlock.NumberU64(), map[string]int{}); err != nil { + if err = batchContext.sdb.hermezDb.WriteBatchCounters(currentBlock.NumberU64(), []int{}); err != nil { return false, err } if err = stages.SaveStageProgress(batchContext.sdb.tx, stages.HighestSeenBatchNumber, batchState.batchNumber); err != nil { diff --git a/zk/stages/stage_sequence_execute_blocks.go b/zk/stages/stage_sequence_execute_blocks.go index f80aa918bee..997e77c02af 100644 --- a/zk/stages/stage_sequence_execute_blocks.go +++ b/zk/stages/stage_sequence_execute_blocks.go @@ -242,7 +242,7 @@ func finaliseBlock( } // write batch counters - err = batchContext.sdb.hermezDb.WriteBatchCounters(newNum.Uint64(), batchCounters.CombineCollectorsNoChanges().UsedAsMap()) + err = batchContext.sdb.hermezDb.WriteBatchCounters(newNum.Uint64(), batchCounters.CombineCollectorsNoChanges().UsedAsArray()) if err != nil { return nil, err } diff --git a/zk/stages/stage_sequence_execute_data_stream.go b/zk/stages/stage_sequence_execute_data_stream.go index 20b59890797..a1e3606cb09 100644 --- a/zk/stages/stage_sequence_execute_data_stream.go +++ b/zk/stages/stage_sequence_execute_data_stream.go @@ -14,29 +14,31 @@ import ( ) type SequencerBatchStreamWriter struct { + batchContext *BatchContext + batchState *BatchState ctx context.Context logPrefix string legacyVerifier *verifier.LegacyExecutorVerifier sdb *stageDb streamServer *server.DataStreamServer hasExecutors bool - lastBatch uint64 } -func newSequencerBatchStreamWriter(batchContext *BatchContext, batchState *BatchState, lastBatch uint64) *SequencerBatchStreamWriter { +func newSequencerBatchStreamWriter(batchContext *BatchContext, batchState *BatchState) *SequencerBatchStreamWriter { return &SequencerBatchStreamWriter{ + batchContext: batchContext, + batchState: batchState, ctx: batchContext.ctx, logPrefix: batchContext.s.LogPrefix(), legacyVerifier: batchContext.cfg.legacyVerifier, sdb: batchContext.sdb, streamServer: batchContext.cfg.datastreamServer, hasExecutors: batchState.hasExecutorForThisBatch, - lastBatch: lastBatch, } } func (sbc *SequencerBatchStreamWriter) CommitNewUpdates() ([]*verifier.VerifierBundle, error) { - verifierBundles, err := sbc.legacyVerifier.ProcessResultsSequentially() + verifierBundles, err := sbc.legacyVerifier.ProcessResultsSequentially(sbc.logPrefix) if err != nil { return nil, err } @@ -46,12 +48,29 @@ func (sbc *SequencerBatchStreamWriter) CommitNewUpdates() ([]*verifier.VerifierB func (sbc *SequencerBatchStreamWriter) writeBlockDetailsToDatastream(verifiedBundles []*verifier.VerifierBundle) ([]*verifier.VerifierBundle, error) { var checkedVerifierBundles []*verifier.VerifierBundle = make([]*verifier.VerifierBundle, 0, len(verifiedBundles)) + for _, bundle := range verifiedBundles { request := bundle.Request response := bundle.Response if response.Valid { - parentBlock, err := rawdb.ReadBlockByNumber(sbc.sdb.tx, request.GetLastBlockNumber()-1) + highestClosedBatch, err := sbc.streamServer.GetHighestClosedBatch() + if err != nil { + return checkedVerifierBundles, err + } + highestStartedBatch, err := sbc.streamServer.GetHighestBatchNumber() + if err != nil { + return checkedVerifierBundles, err + } + isCurrentBatchHigherThanLastInDatastream := request.BatchNumber > highestStartedBatch + isLastBatchInDatastremClosed := highestClosedBatch == highestStartedBatch + if isCurrentBatchHigherThanLastInDatastream && !isLastBatchInDatastremClosed { + if err := finalizeLastBatchInDatastream(sbc.batchContext, highestStartedBatch, request.GetFirstBlockNumber()-1); err != nil { + return checkedVerifierBundles, err + } + } + + previousBlock, err := rawdb.ReadBlockByNumber(sbc.sdb.tx, request.GetLastBlockNumber()-1) if err != nil { return checkedVerifierBundles, err } @@ -59,18 +78,24 @@ func (sbc *SequencerBatchStreamWriter) writeBlockDetailsToDatastream(verifiedBun if err != nil { return checkedVerifierBundles, err } + // all blocks in a request has identical batch number + // we need only to check the previous block's batch number for i == 0 + previousBlockBatchNumber := request.BatchNumber + if len(request.BlockNumbers) == 1 { + var found bool + previousBlockBatchNumber, found, err = sbc.sdb.hermezDb.HermezDbReader.CheckBatchNoByL2Block(previousBlock.NumberU64()) + if !found || err != nil { + return checkedVerifierBundles, err + } + } - if err := sbc.streamServer.WriteBlockWithBatchStartToStream(sbc.logPrefix, sbc.sdb.tx, sbc.sdb.hermezDb, request.ForkId, request.BatchNumber, sbc.lastBatch, *parentBlock, *block); err != nil { + if err := sbc.streamServer.WriteBlockWithBatchStartToStream(sbc.logPrefix, sbc.sdb.tx, sbc.sdb.hermezDb, request.ForkId, request.BatchNumber, previousBlockBatchNumber, *previousBlock, *block); err != nil { return checkedVerifierBundles, err } if err = stages.SaveStageProgress(sbc.sdb.tx, stages.DataStream, block.NumberU64()); err != nil { return checkedVerifierBundles, err } - - // once we have handled the very first block we can update the last batch to be the current batch safely so that - // we don't keep adding batch bookmarks in between blocks - sbc.lastBatch = request.BatchNumber } checkedVerifierBundles = append(checkedVerifierBundles, bundle) @@ -85,9 +110,12 @@ func (sbc *SequencerBatchStreamWriter) writeBlockDetailsToDatastream(verifiedBun } func alignExecutionToDatastream(batchContext *BatchContext, batchState *BatchState, lastExecutedBlock uint64, u stagedsync.Unwinder) (bool, error) { - lastExecutedBatch := batchState.batchNumber - 1 + lastStartedDatastreamBatch, err := batchContext.cfg.datastreamServer.GetHighestBatchNumber() + if err != nil { + return false, err + } - lastDatastreamBatch, err := batchContext.cfg.datastreamServer.GetHighestBatchNumber() + lastClosedDatastreamBatch, err := batchContext.cfg.datastreamServer.GetHighestClosedBatch() if err != nil { return false, err } @@ -97,22 +125,24 @@ func alignExecutionToDatastream(batchContext *BatchContext, batchState *BatchSta return false, err } - if lastExecutedBatch == lastDatastreamBatch && lastExecutedBlock == lastDatastreamBlock { - return false, nil + if lastStartedDatastreamBatch != lastClosedDatastreamBatch { + if err := finalizeLastBatchInDatastreamIfNotFinalized(batchContext, lastStartedDatastreamBatch, lastDatastreamBlock); err != nil { + return false, err + } } - if err := finalizeLastBatchInDatastreamIfNotFinalized(batchContext, lastDatastreamBatch, lastDatastreamBlock); err != nil { - return false, err - } + if lastExecutedBlock != lastDatastreamBlock { + block, err := rawdb.ReadBlockByNumber(batchContext.sdb.tx, lastDatastreamBlock) + if err != nil { + return false, err + } - block, err := rawdb.ReadBlockByNumber(batchContext.sdb.tx, lastDatastreamBlock) - if err != nil { - return false, err + log.Warn(fmt.Sprintf("[%s] Unwinding due to a datastream gap", batchContext.s.LogPrefix()), "streamHeight", lastDatastreamBlock, "sequencerHeight", lastExecutedBlock) + u.UnwindTo(lastDatastreamBlock, block.Hash()) + return true, nil } - log.Warn(fmt.Sprintf("[%s] Unwinding due to a datastream gap", batchContext.s.LogPrefix()), "streamHeight", lastDatastreamBlock, "sequencerHeight", lastExecutedBlock) - u.UnwindTo(lastDatastreamBlock, block.Hash()) - return true, nil + return false, nil } func finalizeLastBatchInDatastreamIfNotFinalized(batchContext *BatchContext, batchToClose, blockToCloseAt uint64) error { diff --git a/zk/stages/stage_sequence_execute_limbo.go b/zk/stages/stage_sequence_execute_limbo.go index 64a9b6ae6e1..1df43995cec 100644 --- a/zk/stages/stage_sequence_execute_limbo.go +++ b/zk/stages/stage_sequence_execute_limbo.go @@ -3,7 +3,6 @@ package stages import ( "bytes" "fmt" - "math" "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/core/types" @@ -12,92 +11,39 @@ import ( "github.com/ledgerwatch/log/v3" ) -type limboStreamBytesGroup struct { - blockNumber uint64 - transactionsIndicesInBlock []int -} - -func newLimboStreamBytesGroup(blockNumber uint64) *limboStreamBytesGroup { - return &limboStreamBytesGroup{ - blockNumber: blockNumber, - transactionsIndicesInBlock: make([]int, 0, 1), - } -} - -type limboStreamBytesBuilderHelper struct { - sendersToGroupMap map[string][]*limboStreamBytesGroup -} - -func newLimboStreamBytesBuilderHelper() *limboStreamBytesBuilderHelper { - return &limboStreamBytesBuilderHelper{ - sendersToGroupMap: make(map[string][]*limboStreamBytesGroup), - } -} - -func (_this *limboStreamBytesBuilderHelper) append(senderMapKey string, blockNumber uint64, transactionIndex int) ([]uint64, [][]int) { - limboStreamBytesGroups := _this.add(senderMapKey, blockNumber, transactionIndex) - - size := len(limboStreamBytesGroups) - resultBlocks := make([]uint64, size) - resultTransactionsSet := make([][]int, size) - - for i := 0; i < size; i++ { - group := limboStreamBytesGroups[i] - resultBlocks[i] = group.blockNumber - resultTransactionsSet[i] = group.transactionsIndicesInBlock - } - - return resultBlocks, resultTransactionsSet -} - -func (_this *limboStreamBytesBuilderHelper) add(senderMapKey string, blockNumber uint64, transactionIndex int) []*limboStreamBytesGroup { - limboStreamBytesGroups, ok := _this.sendersToGroupMap[senderMapKey] - if !ok { - limboStreamBytesGroups = []*limboStreamBytesGroup{newLimboStreamBytesGroup(blockNumber)} - _this.sendersToGroupMap[senderMapKey] = limboStreamBytesGroups - } - group := limboStreamBytesGroups[len(limboStreamBytesGroups)-1] - if group.blockNumber != blockNumber { - group = newLimboStreamBytesGroup(blockNumber) - limboStreamBytesGroups = append(limboStreamBytesGroups, group) - _this.sendersToGroupMap[senderMapKey] = limboStreamBytesGroups - } - group.transactionsIndicesInBlock = append(group.transactionsIndicesInBlock, transactionIndex) - - return limboStreamBytesGroups -} - func handleLimbo(batchContext *BatchContext, batchState *BatchState, verifierBundle *legacy_executor_verifier.VerifierBundle) error { - request := verifierBundle.Request legacyVerifier := batchContext.cfg.legacyVerifier + request := verifierBundle.Request + blockNumber := request.GetLastBlockNumber() + blockNumbers := []uint64{blockNumber} log.Info(fmt.Sprintf("[%s] identified an invalid batch, entering limbo", batchContext.s.LogPrefix()), "batch", request.BatchNumber) l1InfoTreeMinTimestamps := make(map[uint64]uint64) - if _, err := legacyVerifier.GetWholeBatchStreamBytes(request.BatchNumber, batchContext.sdb.tx, []uint64{request.GetLastBlockNumber()}, batchContext.sdb.hermezDb.HermezDbReader, l1InfoTreeMinTimestamps, nil); err != nil { + if _, err := legacyVerifier.GetWholeBatchStreamBytes(request.BatchNumber, batchContext.sdb.tx, blockNumbers, batchContext.sdb.hermezDb.HermezDbReader, l1InfoTreeMinTimestamps, nil); err != nil { return err } - blockNumber := request.GetLastBlockNumber() witness, err := legacyVerifier.WitnessGenerator.GetWitnessByBlockRange(batchContext.sdb.tx, batchContext.ctx, blockNumber, blockNumber, false, batchContext.cfg.zk.WitnessFull) if err != nil { return err } - limboSendersToPreviousTxMap := make(map[string]uint32) - limboStreamBytesBuilderHelper := newLimboStreamBytesBuilderHelper() - - limboDetails := txpool.NewLimboBatchDetails() - limboDetails.Witness = witness - limboDetails.L1InfoTreeMinTimestamps = l1InfoTreeMinTimestamps - limboDetails.BatchNumber = request.BatchNumber - limboDetails.ForkId = request.ForkId + limboBlock := txpool.NewLimboBlockDetails() + limboBlock.Witness = witness + limboBlock.L1InfoTreeMinTimestamps = l1InfoTreeMinTimestamps + limboBlock.BlockNumber = blockNumber + limboBlock.BatchNumber = request.BatchNumber + limboBlock.ForkId = request.ForkId block, err := rawdb.ReadBlockByNumber(batchContext.sdb.tx, blockNumber) if err != nil { return err } + var transactionsToIncludeByIndex [][]int = [][]int{ + make([]int, 0, len(block.Transactions())), + } for i, transaction := range block.Transactions() { var b []byte buffer := bytes.NewBuffer(b) @@ -111,28 +57,20 @@ func handleLimbo(batchContext *BatchContext, batchState *BatchState, verifierBun if err != nil { return err } - senderMapKey := sender.Hex() - blocksForStreamBytes, transactionsToIncludeByIndex := limboStreamBytesBuilderHelper.append(senderMapKey, blockNumber, i) - streamBytes, err := legacyVerifier.GetWholeBatchStreamBytes(request.BatchNumber, batchContext.sdb.tx, blocksForStreamBytes, batchContext.sdb.hermezDb.HermezDbReader, l1InfoTreeMinTimestamps, transactionsToIncludeByIndex) + transactionsToIncludeByIndex[0] = append(transactionsToIncludeByIndex[0], i) + streamBytes, err := legacyVerifier.GetWholeBatchStreamBytes(request.BatchNumber, batchContext.sdb.tx, blockNumbers, batchContext.sdb.hermezDb.HermezDbReader, l1InfoTreeMinTimestamps, transactionsToIncludeByIndex) if err != nil { return err } - previousTxIndex, ok := limboSendersToPreviousTxMap[senderMapKey] - if !ok { - previousTxIndex = math.MaxUint32 - } - hash := transaction.Hash() - limboTxCount := limboDetails.AppendTransaction(buffer.Bytes(), streamBytes, hash, sender, previousTxIndex) - limboSendersToPreviousTxMap[senderMapKey] = limboTxCount - 1 + limboBlock.AppendTransaction(buffer.Bytes(), streamBytes, hash, sender) log.Info(fmt.Sprintf("[%s] adding transaction to limbo", batchContext.s.LogPrefix()), "hash", hash) } - limboDetails.TimestampLimit = block.Time() - limboDetails.FirstBlockNumber = block.NumberU64() - batchContext.cfg.txPool.ProcessLimboBatchDetails(limboDetails) + limboBlock.BlockTimestamp = block.Time() + batchContext.cfg.txPool.ProcessUncheckedLimboBlockDetails(limboBlock) return nil } diff --git a/zk/stages/stage_sequence_execute_state.go b/zk/stages/stage_sequence_execute_state.go index 20245564fdd..7e8af94a78c 100644 --- a/zk/stages/stage_sequence_execute_state.go +++ b/zk/stages/stage_sequence_execute_state.go @@ -15,6 +15,8 @@ import ( "github.com/ledgerwatch/erigon/zk/txpool" ) +const maximumOverflowTransactionAttempts = 5 + type BatchContext struct { ctx context.Context cfg *SequenceBlockCfg @@ -44,9 +46,11 @@ type BatchState struct { blockState *BlockState batchL1RecoveryData *BatchL1RecoveryData limboRecoveryData *LimboRecoveryData + resequenceBatchJob *ResequenceBatchJob + overflowTransactions int } -func newBatchState(forkId, batchNumber uint64, hasExecutorForThisBatch, l1Recovery bool, txPool *txpool.TxPool) *BatchState { +func newBatchState(forkId, batchNumber, blockNumber uint64, hasExecutorForThisBatch, l1Recovery bool, txPool *txpool.TxPool, resequenceBatchJob *ResequenceBatchJob) *BatchState { batchState := &BatchState{ forkId: forkId, batchNumber: batchNumber, @@ -57,15 +61,32 @@ func newBatchState(forkId, batchNumber uint64, hasExecutorForThisBatch, l1Recove blockState: newBlockState(), batchL1RecoveryData: nil, limboRecoveryData: nil, + resequenceBatchJob: resequenceBatchJob, } - if l1Recovery { - batchState.batchL1RecoveryData = newBatchL1RecoveryData(batchState) - } - - limboHeaderTimestamp, limboTxHash := txPool.GetLimboTxHash(batchState.batchNumber) - if limboTxHash != nil { - batchState.limboRecoveryData = newLimboRecoveryData(limboHeaderTimestamp, limboTxHash) + if batchNumber != injectedBatchBatchNumber { // process injected batch regularly, no matter if it is in any recovery + if l1Recovery { + batchState.batchL1RecoveryData = newBatchL1RecoveryData(batchState) + } + + limboBlock, limboTxHash := txPool.GetLimboDetailsForRecovery(blockNumber) + if limboTxHash != nil { + // batchNumber == limboBlock.BatchNumber then we've unwound to the very beginning of the batch. 'limboBlock.BlockNumber' is the 1st block of 'batchNumber' batch. Everything is fine. + + // batchNumber - 1 == limboBlock.BatchNumber then we've unwound to the middle of a batch. We must set in 'batchState' that we're going to resume a batch build rather than starting a new one. Everything is fine. + if batchNumber-1 == limboBlock.BatchNumber { + batchState.batchNumber = limboBlock.BatchNumber + } else if batchNumber != limboBlock.BatchNumber { + // in any other configuration rather than (batchNumber or batchNumber - 1) == limboBlock.BatchNumber we can only panic + panic(fmt.Errorf("requested batch %d while the network is already on %d", limboBlock.BatchNumber, batchNumber)) + } + + batchState.limboRecoveryData = newLimboRecoveryData(limboBlock.BlockTimestamp, limboTxHash) + } + + if batchState.isL1Recovery() && batchState.isLimboRecovery() { + panic("Both recoveries cannot be active simultaneously") + } } return batchState @@ -79,8 +100,12 @@ func (bs *BatchState) isLimboRecovery() bool { return bs.limboRecoveryData != nil } +func (bs *BatchState) isResequence() bool { + return bs.resequenceBatchJob != nil +} + func (bs *BatchState) isAnyRecovery() bool { - return bs.isL1Recovery() || bs.isLimboRecovery() + return bs.isL1Recovery() || bs.isLimboRecovery() || bs.isResequence() } func (bs *BatchState) isThereAnyTransactionsToRecover() bool { @@ -103,6 +128,10 @@ func (bs *BatchState) getBlockHeaderForcedTimestamp() uint64 { return bs.limboRecoveryData.limboHeaderTimestamp } + if bs.isResequence() { + return uint64(bs.resequenceBatchJob.CurrentBlock().Timestamp) + } + return math.MaxUint64 } @@ -123,6 +152,14 @@ func (bs *BatchState) onBuiltBlock(blockNumber uint64) { bs.builtBlocks = append(bs.builtBlocks, blockNumber) } +func (bs *BatchState) newOverflowTransaction() { + bs.overflowTransactions++ +} + +func (bs *BatchState) reachedOverflowTransactionLimit() bool { + return bs.overflowTransactions >= maximumOverflowTransactionAttempts +} + // TYPE BATCH L1 RECOVERY DATA type BatchL1RecoveryData struct { recoveredBatchDataSize int diff --git a/zk/stages/stage_sequence_execute_transactions.go b/zk/stages/stage_sequence_execute_transactions.go index 42ff1285b2e..2ee8244a5fa 100644 --- a/zk/stages/stage_sequence_execute_transactions.go +++ b/zk/stages/stage_sequence_execute_transactions.go @@ -98,6 +98,14 @@ func extractTransactionsFromSlot(slot *types2.TxsRlp) ([]types.Transaction, erro return transactions, nil } +type overflowType uint8 + +const ( + overflowNone overflowType = iota + overflowCounters + overflowGas +) + func attemptAddTransaction( cfg SequenceBlockCfg, sdb *stageDb, @@ -110,7 +118,7 @@ func attemptAddTransaction( l1Recovery bool, forkId, l1InfoIndex uint64, blockDataSizeChecker *BlockDataChecker, -) (*types.Receipt, *core.ExecutionResult, bool, error) { +) (*types.Receipt, *core.ExecutionResult, overflowType, error) { var batchDataOverflow, overflow bool var err error @@ -121,7 +129,7 @@ func attemptAddTransaction( if blockDataSizeChecker != nil { txL2Data, err := txCounters.GetL2DataCache() if err != nil { - return nil, nil, false, err + return nil, nil, overflowNone, err } batchDataOverflow = blockDataSizeChecker.AddTransactionData(txL2Data) if batchDataOverflow { @@ -129,11 +137,12 @@ func attemptAddTransaction( } } if err != nil { - return nil, nil, false, err + return nil, nil, overflowNone, err } anyOverflow := overflow || batchDataOverflow if anyOverflow && !l1Recovery { - return nil, nil, true, nil + log.Debug("Transaction preexecute overflow detected", "txHash", transaction.Hash(), "coutners", batchCounters.CombineCollectorsNoChanges().UsedAsString()) + return nil, nil, overflowCounters, nil } gasPool := new(core.GasPool).AddGas(transactionGasLimit) @@ -164,23 +173,31 @@ func attemptAddTransaction( ) if err != nil { - return nil, nil, false, err + return nil, nil, overflowNone, err } if err = txCounters.ProcessTx(ibs, execResult.ReturnData); err != nil { - return nil, nil, false, err + return nil, nil, overflowNone, err } batchCounters.UpdateExecutionAndProcessingCountersCache(txCounters) // now that we have executed we can check again for an overflow if overflow, err = batchCounters.CheckForOverflow(l1InfoIndex != 0); err != nil { - return nil, nil, false, err + return nil, nil, overflowNone, err } + counters := batchCounters.CombineCollectorsNoChanges().UsedAsString() if overflow { + log.Debug("Transaction overflow detected", "txHash", transaction.Hash(), "coutners", counters) + ibs.RevertToSnapshot(snapshot) + return nil, nil, overflowCounters, nil + } + if gasUsed > header.GasLimit { + log.Debug("Transaction overflows block gas limit", "txHash", transaction.Hash(), "txGas", receipt.GasUsed, "blockGasUsed", header.GasUsed) ibs.RevertToSnapshot(snapshot) - return nil, nil, true, nil + return nil, nil, overflowGas, nil } + log.Debug("Transaction added", "txHash", transaction.Hash(), "coutners", counters) // add the gas only if not reverted. This should not be moved above the overflow check header.GasUsed = gasUsed @@ -188,10 +205,10 @@ func attemptAddTransaction( // we need to keep hold of the effective percentage used // todo [zkevm] for now we're hard coding to the max value but we need to calc this properly if err = sdb.hermezDb.WriteEffectiveGasPricePercentage(transaction.Hash(), effectiveGasPrice); err != nil { - return nil, nil, false, err + return nil, nil, overflowNone, err } ibs.FinalizeTx(evm.ChainRules(), noop) - return receipt, execResult, false, nil + return receipt, execResult, overflowNone, nil } diff --git a/zk/stages/stage_sequence_execute_unwind.go b/zk/stages/stage_sequence_execute_unwind.go index b8918aa33d7..5f7f9229cee 100644 --- a/zk/stages/stage_sequence_execute_unwind.go +++ b/zk/stages/stage_sequence_execute_unwind.go @@ -2,6 +2,7 @@ package stages import ( "context" + "errors" "fmt" "github.com/gateway-fm/cdk-erigon-lib/common/hexutility" @@ -44,7 +45,7 @@ func UnwindSequenceExecutionStage(u *stagedsync.UnwindState, s *stagedsync.Stage func unwindSequenceExecutionStage(u *stagedsync.UnwindState, s *stagedsync.StageState, tx kv.RwTx, ctx context.Context, cfg SequenceBlockCfg, initialCycle bool) error { hermezDb := hermez_db.NewHermezDb(tx) fromBatch, err := hermezDb.GetBatchNoByL2Block(u.UnwindPoint) - if err != nil { + if err != nil && !errors.Is(err, hermez_db.ErrorNotStored){ return err } diff --git a/zk/stages/stage_sequence_execute_utils.go b/zk/stages/stage_sequence_execute_utils.go index 83c77c52098..7f7e89d0a01 100644 --- a/zk/stages/stage_sequence_execute_utils.go +++ b/zk/stages/stage_sequence_execute_utils.go @@ -29,6 +29,7 @@ import ( "github.com/ledgerwatch/erigon/turbo/shards" "github.com/ledgerwatch/erigon/turbo/stages/headerdownload" "github.com/ledgerwatch/erigon/zk/datastream/server" + dsTypes "github.com/ledgerwatch/erigon/zk/datastream/types" "github.com/ledgerwatch/erigon/zk/hermez_db" verifier "github.com/ledgerwatch/erigon/zk/legacy_executor_verifier" "github.com/ledgerwatch/erigon/zk/tx" @@ -240,7 +241,12 @@ func prepareHeader(tx kv.RwTx, previousBlockNumber, deltaTimestamp, forcedTimest }, parentBlock, nil } -func prepareL1AndInfoTreeRelatedStuff(sdb *stageDb, batchState *BatchState, proposedTimestamp uint64) ( +func prepareL1AndInfoTreeRelatedStuff( + sdb *stageDb, + batchState *BatchState, + proposedTimestamp uint64, + reuseL1InfoIndex bool, +) ( infoTreeIndexProgress uint64, l1TreeUpdate *zktypes.L1InfoTreeUpdate, l1TreeUpdateIndex uint64, @@ -258,8 +264,17 @@ func prepareL1AndInfoTreeRelatedStuff(sdb *stageDb, batchState *BatchState, prop return } - if batchState.isL1Recovery() { - l1TreeUpdateIndex = uint64(batchState.blockState.blockL1RecoveryData.L1InfoTreeIndex) + if batchState.isL1Recovery() || (batchState.isResequence() && reuseL1InfoIndex) { + if batchState.isL1Recovery() { + l1TreeUpdateIndex = uint64(batchState.blockState.blockL1RecoveryData.L1InfoTreeIndex) + } else { + // Resequence mode: + // If we are resequencing at the beginning (AtNewBlockBoundary->true) of a rolledback block, we need to reuse the l1TreeUpdateIndex from the block. + // If we are in the middle of a block (AtNewBlockBoundary -> false), it means the original block will be requenced into multiple blocks, so we will leave l1TreeUpdateIndex as 0 for the rest of blocks. + if batchState.resequenceBatchJob.AtNewBlockBoundary() { + l1TreeUpdateIndex = uint64(batchState.resequenceBatchJob.CurrentBlock().L1InfoTreeIndex) + } + } if l1TreeUpdate, err = sdb.hermezDb.GetL1InfoTreeUpdate(l1TreeUpdateIndex); err != nil { return } @@ -270,9 +285,10 @@ func prepareL1AndInfoTreeRelatedStuff(sdb *stageDb, batchState *BatchState, prop if l1TreeUpdateIndex, l1TreeUpdate, err = calculateNextL1TreeUpdateToUse(infoTreeIndexProgress, sdb.hermezDb, proposedTimestamp); err != nil { return } - if l1TreeUpdateIndex > 0 { - infoTreeIndexProgress = l1TreeUpdateIndex - } + } + + if l1TreeUpdateIndex > 0 { + infoTreeIndexProgress = l1TreeUpdateIndex } // we only want GER and l1 block hash for indexes above 0 - 0 is a special case @@ -488,3 +504,78 @@ func (bdc *BlockDataChecker) AddTransactionData(txL2Data []byte) bool { return false } + +type txMatadata struct { + blockNum int + txIndex int +} + +type ResequenceBatchJob struct { + batchToProcess []*dsTypes.FullL2Block + StartBlockIndex int + StartTxIndex int + txIndexMap map[common.Hash]txMatadata +} + +func NewResequenceBatchJob(batch []*dsTypes.FullL2Block) *ResequenceBatchJob { + return &ResequenceBatchJob{ + batchToProcess: batch, + StartBlockIndex: 0, + StartTxIndex: 0, + txIndexMap: make(map[common.Hash]txMatadata), + } +} + +func (r *ResequenceBatchJob) HasMoreBlockToProcess() bool { + return r.StartBlockIndex < len(r.batchToProcess) +} + +func (r *ResequenceBatchJob) AtNewBlockBoundary() bool { + return r.StartTxIndex == 0 +} + +func (r *ResequenceBatchJob) CurrentBlock() *dsTypes.FullL2Block { + if r.HasMoreBlockToProcess() { + return r.batchToProcess[r.StartBlockIndex] + } + return nil +} + +func (r *ResequenceBatchJob) YieldNextBlockTransactions(decoder zktx.TxDecoder) ([]types.Transaction, error) { + blockTransactions := make([]types.Transaction, 0) + if r.HasMoreBlockToProcess() { + block := r.CurrentBlock() + r.txIndexMap[block.L2Blockhash] = txMatadata{r.StartBlockIndex, 0} + + for i := r.StartTxIndex; i < len(block.L2Txs); i++ { + transaction := block.L2Txs[i] + tx, _, err := decoder(transaction.Encoded, transaction.EffectiveGasPricePercentage, block.ForkId) + if err != nil { + return nil, fmt.Errorf("decode tx error: %v", err) + } + r.txIndexMap[tx.Hash()] = txMatadata{r.StartBlockIndex, i} + blockTransactions = append(blockTransactions, tx) + } + } + + return blockTransactions, nil +} + +func (r *ResequenceBatchJob) UpdateLastProcessedTx(h common.Hash) { + if idx, ok := r.txIndexMap[h]; ok { + block := r.batchToProcess[idx.blockNum] + + if idx.txIndex >= len(block.L2Txs)-1 { + // we've processed all the transactions in this block + // move to the next block + r.StartBlockIndex = idx.blockNum + 1 + r.StartTxIndex = 0 + } else { + // move to the next transaction in the block + r.StartBlockIndex = idx.blockNum + r.StartTxIndex = idx.txIndex + 1 + } + } else { + log.Warn("tx hash not found in tx index map", "hash", h) + } +} diff --git a/zk/stages/stage_sequence_execute_utils_test.go b/zk/stages/stage_sequence_execute_utils_test.go index 3ff72032840..f5fe9d0eb50 100644 --- a/zk/stages/stage_sequence_execute_utils_test.go +++ b/zk/stages/stage_sequence_execute_utils_test.go @@ -1,8 +1,13 @@ package stages import ( + "reflect" "testing" + "github.com/gateway-fm/cdk-erigon-lib/common" + "github.com/holiman/uint256" + "github.com/ledgerwatch/erigon/core/types" + dsTypes "github.com/ledgerwatch/erigon/zk/datastream/types" zktx "github.com/ledgerwatch/erigon/zk/tx" zktypes "github.com/ledgerwatch/erigon/zk/types" ) @@ -207,3 +212,252 @@ func Test_PrepareForkId_DuringRecovery(t *testing.T) { }) } } + +// Mock implementation of zktx.DecodeTx for testing purposes +func mockDecodeTx(encoded []byte, effectiveGasPricePercentage byte, forkId uint64) (types.Transaction, uint8, error) { + return types.NewTransaction(0, common.Address{}, uint256.NewInt(0), 0, uint256.NewInt(0), encoded), 0, nil +} + +func TestResequenceBatchJob_HasMoreToProcess(t *testing.T) { + tests := []struct { + name string + job ResequenceBatchJob + expected bool + }{ + { + name: "Has more blocks to process", + job: ResequenceBatchJob{ + batchToProcess: []*dsTypes.FullL2Block{{}, {}}, + StartBlockIndex: 1, + StartTxIndex: 0, + }, + expected: true, + }, + { + name: "Has more transactions to process", + job: ResequenceBatchJob{ + batchToProcess: []*dsTypes.FullL2Block{{L2Txs: []dsTypes.L2TransactionProto{{}, {}}}}, + StartBlockIndex: 0, + StartTxIndex: 0, + }, + expected: true, + }, + { + name: "No more to process", + job: ResequenceBatchJob{ + batchToProcess: []*dsTypes.FullL2Block{{}}, + StartBlockIndex: 1, + StartTxIndex: 0, + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.job.HasMoreBlockToProcess(); got != tt.expected { + t.Errorf("ResequenceBatchJob.HasMoreBlockToProcess() = %v, want %v", got, tt.expected) + } + }) + } +} + +func TestResequenceBatchJob_CurrentBlock(t *testing.T) { + tests := []struct { + name string + job ResequenceBatchJob + expected *dsTypes.FullL2Block + }{ + { + name: "Has current block", + job: ResequenceBatchJob{ + batchToProcess: []*dsTypes.FullL2Block{{L2BlockNumber: 1}, {L2BlockNumber: 2}}, + StartBlockIndex: 0, + StartTxIndex: 0, + }, + expected: &dsTypes.FullL2Block{L2BlockNumber: 1}, + }, + { + name: "No current block", + job: ResequenceBatchJob{ + batchToProcess: []*dsTypes.FullL2Block{{L2BlockNumber: 1}}, + StartBlockIndex: 1, + StartTxIndex: 0, + }, + expected: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.job.CurrentBlock() + if (got == nil && tt.expected != nil) || (got != nil && tt.expected == nil) { + t.Errorf("ResequenceBatchJob.CurrentBlock() = %v, want %v", got, tt.expected) + } + if got != nil && tt.expected != nil && got.L2BlockNumber != tt.expected.L2BlockNumber { + t.Errorf("ResequenceBatchJob.CurrentBlock().L2BlockNumber = %v, want %v", got.L2BlockNumber, tt.expected.L2BlockNumber) + } + }) + } +} + +func TestResequenceBatchJob_YieldNextBlockTransactions(t *testing.T) { + // Replace the actual zktx.DecodeTx with our mock function for testing + + tests := []struct { + name string + job ResequenceBatchJob + expectedTxCount int + expectedError bool + }{ + { + name: "Yield transactions", + job: ResequenceBatchJob{ + batchToProcess: []*dsTypes.FullL2Block{ + { + L2Txs: []dsTypes.L2TransactionProto{{}, {}}, + ForkId: 1, + }, + }, + StartBlockIndex: 0, + StartTxIndex: 0, + txIndexMap: make(map[common.Hash]txMatadata), + }, + expectedTxCount: 2, + expectedError: false, + }, + { + name: "No transactions to yield", + job: ResequenceBatchJob{ + batchToProcess: []*dsTypes.FullL2Block{{}}, + StartBlockIndex: 1, + StartTxIndex: 0, + txIndexMap: make(map[common.Hash]txMatadata), + }, + expectedTxCount: 0, + expectedError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + txs, err := tt.job.YieldNextBlockTransactions(mockDecodeTx) + if (err != nil) != tt.expectedError { + t.Errorf("ResequenceBatchJob.YieldNextBlockTransactions() error = %v, expectedError %v", err, tt.expectedError) + return + } + if len(txs) != tt.expectedTxCount { + t.Errorf("ResequenceBatchJob.YieldNextBlockTransactions() returned %d transactions, expected %d", len(txs), tt.expectedTxCount) + } + }) + } +} + +func TestResequenceBatchJob_YieldAndUpdate(t *testing.T) { + // Setup the batch + batch := []*dsTypes.FullL2Block{ + {L2Txs: []dsTypes.L2TransactionProto{{Encoded: []byte("1")}, {Encoded: []byte("2")}}, L2Blockhash: common.HexToHash("0")}, + {L2Txs: []dsTypes.L2TransactionProto{}, L2Blockhash: common.HexToHash("1")}, + {L2Txs: []dsTypes.L2TransactionProto{}, L2Blockhash: common.HexToHash("2")}, + {L2Txs: []dsTypes.L2TransactionProto{{Encoded: []byte("3")}, {Encoded: []byte("4")}}, L2Blockhash: common.HexToHash("3")}, + } + + job := ResequenceBatchJob{ + batchToProcess: batch, + StartBlockIndex: 0, + StartTxIndex: 1, // Start at block 0, index 1 + txIndexMap: make(map[common.Hash]txMatadata), + } + + processTransactions := func(txs []types.Transaction) { + for _, tx := range txs { + job.UpdateLastProcessedTx(tx.Hash()) + } + } + + // First call - should yield transaction 2 from block 0 + txs, err := job.YieldNextBlockTransactions(mockDecodeTx) + if err != nil { + t.Fatalf("First call: Unexpected error: %v", err) + } + if len(txs) != 1 || string(txs[0].GetData()) != "2" { + t.Errorf("Expected 1 transaction with data '2', got %d transactions with data '%s'", len(txs), string(txs[0].GetData())) + } + processTransactions(txs) + tx2 := txs[0] + + // Second call - should yield empty block (block 1) + txs, err = job.YieldNextBlockTransactions(mockDecodeTx) + if err != nil { + t.Fatalf("Second call: Unexpected error: %v", err) + } + if len(txs) != 0 { + t.Errorf("Expected 0 transactions, got %d", len(txs)) + } + job.UpdateLastProcessedTx(job.CurrentBlock().L2Blockhash) + + // Third call - should yield empty block (block 2) + txs, err = job.YieldNextBlockTransactions(mockDecodeTx) + if err != nil { + t.Fatalf("Third call: Unexpected error: %v", err) + } + if len(txs) != 0 { + t.Errorf("Expected 0 transactions, got %d", len(txs)) + } + job.UpdateLastProcessedTx(job.CurrentBlock().L2Blockhash) + + // Fourth call - should yield transactions 3 and 4, but we'll only process 3 + txs, err = job.YieldNextBlockTransactions(mockDecodeTx) + if err != nil { + t.Fatalf("Fourth call: Unexpected error: %v", err) + } + if len(txs) != 2 || string(txs[0].GetData()) != "3" || string(txs[1].GetData()) != "4" { + t.Errorf("Expected 2 transactions with data '3' and '4', got %d transactions", len(txs)) + } + processTransactions(txs[:1]) // Only process the first transaction (3) + tx3 := txs[0] + tx4 := txs[1] + + // Check final state + if job.StartBlockIndex != 3 { + t.Errorf("Expected StartBlockIndex to be 3, got %d", job.StartBlockIndex) + } + + if job.StartTxIndex != 1 { + t.Errorf("Expected StartTxIndex to be 1, got %d", job.StartTxIndex) + } + + // Final call - should yield transaction 4 + txs, err = job.YieldNextBlockTransactions(mockDecodeTx) + if err != nil { + t.Fatalf("Final call: Unexpected error: %v", err) + } + if len(txs) != 1 || string(txs[0].GetData()) != "4" { + t.Errorf("Expected 1 transaction with data '4', got %d transactions", len(txs)) + } + + processTransactions(txs) + + if job.HasMoreBlockToProcess() { + t.Errorf("Expected no more blocks to process") + } + + // Verify txIndexMap + expectedTxIndexMap := map[common.Hash]txMatadata{ + common.HexToHash("0"): {0, 0}, + common.HexToHash("1"): {1, 0}, + common.HexToHash("2"): {2, 0}, + common.HexToHash("3"): {3, 0}, + tx2.Hash(): {0, 1}, // Transaction 2 + tx3.Hash(): {3, 0}, // Transaction 3 + tx4.Hash(): {3, 1}, // Transaction 4 + } + + for hash, index := range expectedTxIndexMap { + if actualIndex, exists := job.txIndexMap[hash]; !exists { + t.Errorf("Expected hash %s to exist in txIndexMap", hash.Hex()) + } else if !reflect.DeepEqual(actualIndex, index) { + t.Errorf("For hash %s, expected index %v, got %v", hash.Hex(), index, actualIndex) + } + } +} diff --git a/zk/stages/stage_sequencer_l1_block_sync.go b/zk/stages/stage_sequencer_l1_block_sync.go index df8a2ffaf7c..bc2bc2bc150 100644 --- a/zk/stages/stage_sequencer_l1_block_sync.go +++ b/zk/stages/stage_sequencer_l1_block_sync.go @@ -44,7 +44,7 @@ func SpawnSequencerL1BlockSyncStage( tx kv.RwTx, cfg SequencerL1BlockSyncCfg, quiet bool, -) error { +) (funcErr error) { logPrefix := s.LogPrefix() log.Info(fmt.Sprintf("[%s] Starting L1 block sync stage", logPrefix)) defer log.Info(fmt.Sprintf("[%s] Finished L1 block sync stage", logPrefix)) @@ -114,7 +114,14 @@ func SpawnSequencerL1BlockSyncStage( } if !cfg.syncer.IsSyncStarted() { - cfg.syncer.Run(l1BlockHeight) + cfg.syncer.RunQueryBlocks(l1BlockHeight) + defer func() { + if funcErr != nil { + cfg.syncer.StopQueryBlocks() + cfg.syncer.ConsumeQueryBlocks() + cfg.syncer.WaitQueryBlocksToFinish() + } + }() } logChan := cfg.syncer.GetLogsChan() @@ -137,14 +144,14 @@ LOOP: var transaction types.Transaction attempts := 0 for { - transaction, _, err = cfg.syncer.GetTransaction(l.TxHash) - if err == nil { + transaction, _, funcErr = cfg.syncer.GetTransaction(l.TxHash) + if funcErr == nil { break } else { log.Warn("Error getting transaction, attempting again", "hash", l.TxHash.String(), "err", err) attempts++ if attempts > 50 { - return err + return funcErr } time.Sleep(500 * time.Millisecond) } @@ -156,12 +163,14 @@ LOOP: l1InfoRoot := l.Data if len(l1InfoRoot) != 32 { log.Error(fmt.Sprintf("[%s] L1 info root is not 32 bytes", logPrefix), "tx-hash", l.TxHash.String()) - return errors.New("l1 info root is not 32 bytes") + funcErr = errors.New("l1 info root is not 32 bytes") + return funcErr } batches, coinbase, limitTimestamp, err := l1_data.DecodeL1BatchData(transaction.GetData(), cfg.zkCfg.DAUrl) if err != nil { - return err + funcErr = err + return funcErr } limitTimestampBytes := make([]byte, 8) @@ -187,8 +196,8 @@ LOOP: copy(data[52:], limitTimestampBytes) copy(data[60:], batch) - if err := hermezDb.WriteL1BatchData(b, data); err != nil { - return err + if funcErr = hermezDb.WriteL1BatchData(b, data); funcErr != nil { + return funcErr } // check if we need to stop here based on config @@ -217,14 +226,14 @@ LOOP: lastCheckedBlock := cfg.syncer.GetLastCheckedL1Block() if lastCheckedBlock > l1BlockHeight { log.Info(fmt.Sprintf("[%s] Saving L1 block sync progress", logPrefix), "lastChecked", lastCheckedBlock) - if err := stages.SaveStageProgress(tx, stages.L1BlockSync, lastCheckedBlock); err != nil { - return err + if funcErr = stages.SaveStageProgress(tx, stages.L1BlockSync, lastCheckedBlock); funcErr != nil { + return funcErr } } if freshTx { - if err := tx.Commit(); err != nil { - return err + if funcErr = tx.Commit(); funcErr != nil { + return funcErr } } diff --git a/zk/stages/test_utils.go b/zk/stages/test_utils.go index 62b130a9fa0..df250ebf717 100644 --- a/zk/stages/test_utils.go +++ b/zk/stages/test_utils.go @@ -14,6 +14,7 @@ type TestDatastreamClient struct { progress atomic.Uint64 entriesChan chan interface{} errChan chan error + isStarted bool } func NewTestDatastreamClient(fullL2Blocks []types.FullL2Block, gerUpdates []types.GerUpdate) *TestDatastreamClient { @@ -33,11 +34,12 @@ func (c *TestDatastreamClient) EnsureConnected() (bool, error) { func (c *TestDatastreamClient) ReadAllEntriesToChannel() error { c.streamingAtomic.Store(true) + defer c.streamingAtomic.Swap(false) - for i, _ := range c.fullL2Blocks { + for i := range c.fullL2Blocks { c.entriesChan <- &c.fullL2Blocks[i] } - for i, _ := range c.gerUpdates { + for i := range c.gerUpdates { c.entriesChan <- &c.gerUpdates[i] } @@ -52,12 +54,44 @@ func (c *TestDatastreamClient) GetErrChan() chan error { return c.errChan } +func (c *TestDatastreamClient) GetL2BlockByNumber(blockNum uint64) (*types.FullL2Block, int, error) { + for _, l2Block := range c.fullL2Blocks { + if l2Block.L2BlockNumber == blockNum { + return &l2Block, types.CmdErrOK, nil + } + } + + return nil, -1, nil +} + +func (c *TestDatastreamClient) GetLatestL2Block() (*types.FullL2Block, error) { + if len(c.fullL2Blocks) == 0 { + return nil, nil + } + return &c.fullL2Blocks[len(c.fullL2Blocks)-1], nil +} + func (c *TestDatastreamClient) GetLastWrittenTimeAtomic() *atomic.Int64 { return &c.lastWrittenTimeAtomic } + func (c *TestDatastreamClient) GetStreamingAtomic() *atomic.Bool { return &c.streamingAtomic } + func (c *TestDatastreamClient) GetProgressAtomic() *atomic.Uint64 { return &c.progress } + +func (c *TestDatastreamClient) ReadBatches(start uint64, end uint64) ([][]*types.FullL2Block, error) { + return nil, nil +} + +func (c *TestDatastreamClient) Start() error { + c.isStarted = true + return nil +} + +func (c *TestDatastreamClient) Stop() { + c.isStarted = false +} diff --git a/zk/syncer/decodeEtrogSequenceBatchesCallData_testdata.go b/zk/syncer/decodeEtrogSequenceBatchesCallData_testdata.go new file mode 100644 index 00000000000..56fd118b09c --- /dev/null +++ b/zk/syncer/decodeEtrogSequenceBatchesCallData_testdata.go @@ -0,0 +1,94 @@ +package syncer + +import ( + "github.com/gateway-fm/cdk-erigon-lib/common" +) + +type decodeElderberrySequencesTestCase struct { + Input string + Expected SequenceBatchesCalldataElderberry +} + +// DecodeEtrogSequenceBatchesCallDataTestCases - test cases for the DecodeEtrogSequenceBatchesCallData function +var decodeElderberrySequenceBatchesCallDataTestCases = []decodeElderberrySequencesTestCase{ + { + Input: "0xdef57e5400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000066e141dd0000000000000000000000000000000000000000000000000000000000204a05000000000000000000000000148ee7daf16574cd020afa34cc658f8f3fbd280000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000050800000000000000000000000000000000000000000000000000000000000009e00000000000000000000000000000000000000000000000000000000000000cfc0000000000000000000000000000000000000000000000000000000000000fb400000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000018020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030b80b0000000300000000f9044e82674884017bf1a0830bcfc4948ff6508bdd71b535df073920c423e9eaee0b97af80b90424ec0ab6a70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000e0434ddf5273f71250a99c6fbcabfeca301de5f000000000000000000000000f6ad3ccf71abb3e12becf6b3d2a74c963859adcd00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000001a45ae401dc0000000000000000000000000000000000000000000000000000000066e13be200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000000000000000000000000000000000000000001f40000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af00000000000000000000000000000000000000000000000000000000000652e10000000000000000000000000000000000000000000000000163adcee8b87f200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000003d5320821bfca19fb0b5428f2c79d63bd5246f890000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e13be20000000000000000000000000000000000000000000000000163adcee8b87f2000000000000000000000000000000000000000000000002e2103547d3748000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080f1f06050464bbe1899db8975029243860aae9dbc153f3d0bde4cbf423605c0f36b575d04708f9a517c2cfe3c6af0d379c30bca1097b252776609f3c2ef706d681bff0b0000000400000000f901ae82662284017bf1a08308fc58948ff6508bdd71b535df073920c423e9eaee0b97af80b90184b61d27f6000000000000000000000000f6ad3ccf71abb3e12becf6b3d2a74c963859adcd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000003d5320821bfca19fb0b5428f2c79d63bd5246f890000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e13be200000000000000000000000000000000000000000000000001634a3fb8d2af5000000000000000000000000000000000000000000000002e14026ffdab72000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d80800fb8261fa1f177e41f88e7978b3cc48fe720a22ce153ad4bd88febf30f2bf8db3354cbb1d8a684aa733a0c9872a02c28dc350015d9ad47916a5e8d97b365ad8b1cff0b00000003000000000b0000000300000000f28231da84ee6b280082627094d20595ed0ed59d85d9359de5164b320a5c40fa6e875f9a48fae655c8840333e6b882044d80806ad649fc21c968300d2abebc5e256eafba5e069316dd680a6bb4feec0290e85b7d9831647c8d457a29b6efa64a88add3d0ab985436c7155238ed86435f30dc261bff0b00000003000000000b00000003000000000b0000000300000000f9010d821ed48401821fa88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d200000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009814ae9030c504000000000000000000000000000000000000000000000000000000000000000082044d8080f2c8fb37b8417e9a3d7245c8f428d932e87f43877e75b346c3d575a80700110b28ff4f9ac9d7882c8b61cf9929d2a20a8cb62d32aea89cbca06e3021eeaac3fd1cff0b00000003000000000b0000000300000000f903f23b8401620100830343f1941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f52e9f9451b39e2171a782aec26677586f35adc72ac984d0921cc7b9512c744e75000000000000000000000000000000000000000000000000000044364c5bb00000000000000000000000000081f6b887657b679d9e23153ffcd703e1110102400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5867796855613343726552507448414d4c75314a6f47796d6d734b6a66676252464e4b5943644172364541570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004137eaeccfc1a481ec514bda2863333d34977c77f56e4033ea74cef4c22a748db0717b9d2961adbeab964a3963b682bcab163f2cec97eefd02c5df5b66a4e71b521b0000000000000000000000000000000000000000000000000000000000000082044d8080ea7294305dca446fc2918900c201f24486d75b33cafe6e2e8bf369218f229293230e8b67c0bd2b47182ab77ffbaed1d7763fc5705e306d776b0f8f8e86efd88d1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ed5840166e3008307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d3e0000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982adc32888842000000000000000000000000000000000000000000000000000000000000000082044d808067fffeed9f7553babaf6570c8a11233f004d0758170e5d23484c095da5ea282b08682443b3ea253139b3ac01977c0a758f081fd6cd614a24205c1d67cbd9afbf1bff0b00000003000000000b0000000300000000f902ee826181840189ad40830804df945523985926aa12ba58dc5ad00ddca99678d7227e80b902c484d61c970000000000000000000000000000000000000000000000000000000000000060000000000000000000000000292fc50e4eb66c3f6514b9e402dbc25961824d62000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000001a4c23a4c88000000000000000000000000000000000000000000000000000110d9316ec0009bdafe8da6cde01e6040b2d2187f6b67a297205ae12cbfe9485196bfa1b3a8374f9dd0dda9066451633b471b04af8ef8204abfe2693c8bf8c450bc20dcec28d300000000000000000000000063e99e508a9f2b6ed180811f80db90ce7417426c000000000000000000000000000000000000000000000000045f2a42a47981960000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000f02bbc9de6e443efdf3fc41851529c2c3b9e5e0c0000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000447647691d000000000000000000000000000000000000000000000000045e1f2d2e761b0b00000000000000000000000063e99e508a9f2b6ed180811f80db90ce7417426c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000411e413bf834084915b547cc0d72d37e10a33d19426323e626c254fe8663b6984d4cc14841f43d8ca4842e10ab2fe3995cab429c30a54301522ea3a5a5272a91871b0000000000000000000000000000000000000000000000000000000000000082044d80804f5051f8a0057498cd6d2af0d1628a32348d13945f6bbdfd570b3544c7a08f8875aedee2be658959e23184a59bf6b92b43ee0fe5934f93f28b93ce4288de1f6a1cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903ce82264a8405f5e1008307a120943348053b13e22b2717c742cd4a60f5040564a53080b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300010100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000d23aaa8116edf941e1aba78b113d7a670002ce4a0207020601080400030509000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000023c2b0000000000000000000000000000000000000000000000000000000000023c316f00000000000000000000000000000000000000000000000000000000023c316f00000000000000000000000000000000000000000000000000000000023c5b7000000000000000000000000000000000000000000000000000000000023c7e8900000000000000000000000000000000000000000000000000000000023c828000000000000000000000000000000000000000000000000000000000023c954000000000000000000000000000000000000000000000000000000000023ca99000000000000000000000000000000000000000000000000000000000023f57ef00000000000000000000000000000000000000000000000000000000023f59fd0000000000000000000000000000000000000000000000000000000000000004650755b20d11b5cde1a5e1f721e10c74f5f078fac6800341d15be87da2e74a3c5acb65047658a523d0c57d88d94ca1d78f66ccd21b9b203f2df55700919e11bd2fbf684aea0d5dc6ef5cd78dc496afc655c627eb2e2da675143859fb439860329edfbc8fc5731ef33d3d50efcd525f27181b84a44fcf726ca066ebe622c15866000000000000000000000000000000000000000000000000000000000000000421bfca8453ac26e096cd1e78f31336665f46d385c6333d63a813a1bee82a0d97248c4d7624257e93bfbb42e7a8b901568660bea31df9cccf80f064263ac1977e495fb904f2b8477e6114f459116561463c2fb93122fffff08adf8c85aa5850f657c7fed085522823081dda81d1bd94235fc42fd1e70f70b38737760887e1ae0c82044d8080da940144b03a145e88a116e125b1a24eac784a85138a43a7bf4529d1e5fa33751fc270dd6380301802a94c1c6f1d398f5684f56ffd1d974231bcbabb1fe9ad791bff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ed684017eeb588307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d5c0000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009824b00e503048000000000000000000000000000000000000000000000000000000000000000082044d80805a0f08064fc456e60e242cee4f20ecde976a51c7e1c1b1245922d15696c32cba65dbc28c8984d233552830250b4be646f523d65f1e686aa09b038769469d40ec1cff0b0000000300000000f8b12f840238e8a08304ac1194222228060e7efbb1d78bb5d454581910e392222286352f5cb5673bb88432accbb900000000000000000000000000000000000000000000000000000000000000af00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000271bb7b9b0000000000000000000000000000d4ebba85fbd618d08c6911e6bea0e514280144682044d808080e4ceead86b43aca84d560e925df839a58fc08296edf87dc44985d38e1312113a39d3f053ece00a9741ef979d9b0457a11dc7b65ad04dc117ad9a49c52987901bff0b0000000300000000f13d84016caf608304443a94ee1727f5074e747716637e1776b7f7c7133f16b18732a5492d0cfb90841249c58b82044d80801633bcf1a8f14729079a42e6e7d86f2e304924ea015e3f1ddadefe9e31869263170df9e3efeb2ce79a7a320bcbcd31acf77e4d0a33fd4945f0d6724d688b88241cff0b000000040000ae44e94d8402faf08082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080317339510effaa798bc532fa32187f09b40c9b5cd12ccbeb7f7ab3fd3c157e680721490b51e44c59435ea2cd2143d71ad2b6316ae19d4055cfcea37d7a2cb9eb1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9018f830151d184018715308302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000010000000000000000000000003e0103ff5b7f4b338a8f977b35846aa7c01ed379000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000001635044eda5700000000000000000000000000000000000000000000000000000000000000014760dfebac75f07fa46b8e87d9151b673b8e33d4710293497a4a5c9524aedc8882044d8080a3566b6ff106e89842dda24f38fbc6ee0a105588e63240914f04986798835bf030b2f4d65e86d43514d0c03ee84e5c46a84fc637ac927233674c4cf7effe31101cff0b00000003000000000b0000000300000000f9010d821ed78401754e688307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d7a0000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982405427dd941000000000000000000000000000000000000000000000000000000000000000082044d80808cdf22284f15949ea3fbb6eeff81ee87a198722c1f8f827b1ce98ca30138b756444a20bc97269e363da23b33ee1badcfe012964ab8e73ee9c70f6f4af5ae99351bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f902ac258402625a00830494ba94b89a6778d1efe7a5b7096757a21b810cc2886fa180b902843593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e13c4b0000000000000000000000000000000000000000000000000000000000000002000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000376f2930000000000000000000000000000000000000000000000000057e3e6c76eb74600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000968980f6c8b41abeeadab481578614ad405f58890000000000000000000000000000000000000000000000000057e3e6c76eb74682044d8080388b027ca9400e25178e592726a62b4d39a6172d956bf11474ce49f65b0fa8a426f0e323ac7e8de7a4daa322ca42fa3c2cf66081047319341febcf2a8859af3b1cfff9028c208402625a008304932b94b89a6778d1efe7a5b7096757a21b810cc2886fa180b9026424856bc3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000039bac84000000000000000000000000000000000000000000000000005b8731fffd99f800000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000006ded4acd9fd87eec14f18b22e9046f000be0685a000000000000000000000000000000000000000000000000005b8731fffd99f882044d8080b53c40cb340c035c10a519f25f2de3fca0cd253015c28e7049b9d485ea98b1a5021c6938342e403283007f2ea6a9152a9089428f3fdee38461164b0fb89d690d1cfff9028c2e8402625a008305581d94b89a6778d1efe7a5b7096757a21b810cc2886fa180b9026424856bc3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000004710b280000000000000000000000000000000000000000000000000070a92a16ee367400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000e3119ac2c91cf8e2ca81b54d757c166c53647a2b0000000000000000000000000000000000000000000000000070a92a16ee367482044d808008fd8af756bc9385919f74d9a9573a3581faf800c7999d51e834423a5daa4000067f947784576968c91ae28a653c76771a15035f088241705df3780d3cb9df1c1cfff9028c1e8402625a008305581d94b89a6778d1efe7a5b7096757a21b810cc2886fa180b9026424856bc3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000047054780000000000000000000000000000000000000000000000000070971273bec4a100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000018c4dce3c0172a5322aefc459d69e85957d387890000000000000000000000000000000000000000000000000070971273bec4a182044d8080ba9906ac2e50febe56442a09ea73b14880b4f418e30f891402dedfc36e18e57b3b7ef3466f11fcd657b0d99da14c32249962f8f505656ccf48f0a5fa85946c2d1cff0b0000000300000000e97b8402d112408255f094e10add2ad591a7ac3ca46788a06290de017b9fb48084632a9a5282044d8080e2aeb5078a6bdcb30be8415f3bf338183d2b725380bb85cc21596c775e414b103b7de8fceeeb38b5089d6af9f7f4862f805c332651b87bd2c444f16180db50ad1cff0b0000000300000000f8b6028402c9ee9683024b46943a23f943181408eac424116af7b7790c94cb97a58701c6bf52634000b88800000182ad69fa4f0000000000000000000000000000000000000000000000000001c6bf52634000000000000000000000000000b675ed9441dd8ac6d3a206ab61357bcd47384c6c000000000000000000000000000000000000000000000000000000000000e70800000000000000000000000000000000000000000000000000000000000000cd82044d80808493c0e815b42ccfaf9a06a6ccf16f1e445a0412aab8f360dd19929adec226d2431b3cb925d2fc60b325d59f97e35b7b52ea4ef006860596f1b9d79afcf1d0181bfff9010d821ed884016548d88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d980000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009815ae8ed77f3c000000000000000000000000000000000000000000000000000000000000000082044d80808cfb084863d1694fdd8775bf4fac4410b1b9cf98c1201ba0741106879be8d41f36ca97ecaa1eb058f23208921fcfdde1cf82299c336c2e9303c15865d9a036311bff0b0000000300000000ee8308be5e8403fcd02082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080a3271eeb3e1a9ca9fda4117e8abb72638a128fdce0cb2efb9ef2f870f60b2f672741f52279c993542e2b9d52d8961b2ce486f5498db46deb246b5916a379f8b61babf9014f8301154b84019853408305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13b4200000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000007331f3647a294a90a1ca61c0000000000000000000000000000000000000000adf85b68ca582b83dd4bebe00000082044d8080b8cb2d6ac52d01ef955c17584d1abf7462b3014aedde6b0beeac237e3ff3bc104ae81fd1a2adbc78ec0681dfaf235f3ae18164c3af60a6f02dc9f6100818119f1bfff901ce82989884015445608305ea4c948ff6508bdd71b535df073920c423e9eaee0b97af80b901a4b61d27f60000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f40000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e13c6d00000000000000000000000000000000000000000000000001634ada69799d20000000000000000000000000000000000000000000000000000000000df1806400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080768c51f79db3186f54b0754da1b78ab5a79b6fdf23109910fd4cd6cd9d7f3c8c22e2c8aa620d0d71b4b842da4b0742a4e67da2a0df0aaa08b3cb5d4d1a5955931cfff9012f83018d5e840154456083069df4941b81d678ffb9c0263b24a97847620c99d213eb1480b90104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f400000000000000000000000023abcb82f468e3422a7212d4ad47cc2dd39162a30000000000000000000000000000000000000000000000000000000066e13c6e00000000000000000000000000000000000000000000000001634985c6047d20000000000000000000000000000000000000000000000000000000000df17315000000000000000000000000000000000000000000000000000000000000000082044d8080e56167524cec085263787a173ce728e393d5f361abcfcb92098b68de4fb3a7bc72ae91803810eaafb610b3c66b581c91c6474b137de853eacc2c8d7b73f5b8da1bff000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d7a0b0000000300000000f86b825b2a8401764c50830d57b69498d7c628086262ea7bd83a23ccbc72f8cd10cd8480b842d43b9dcbb61e6ccfbcfef9f21e1bb5064f1cb33f0503849c0ae884bfdc14dddeb7cae95494f3684148550102d6efe114c9b6058a20aab759e064f50544590914050382044d8080eed135980b8038da4e871a8e95e5644a40cb85fc3f97c42a3b8c9aeac897dad368eff35e0c6aa77b54885fbe3528e9b9a9e5193d3142d67226825f6e2b5fd8251bfff906ae8216d4840176a36c831396f094063bac84221b5b02b92ceb740ac129db0edfedc780b90684f740f3280000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000005e4e7d7ed2800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000003000000000000000000000000d63b7691dd98fa89a2ea5e1604700489c585aa7b000000000000000000000000d63b7691dd98fa89a2ea5e1604700489c585aa7b000000000000000000000000d63b7691dd98fa89a2ea5e1604700489c585aa7b000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb140000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb1400000000000000000000000098299ead1cd04fbf1659799b14a559206f3ed165000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000066ea75c3000000000000000000000000000000000000000000000000000000000000002b4f9a0e7fd2bf6067db6994cf12e4495df938e6e90001f4a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000066ea75c3000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500006437eaa0ef3549a5bb7d431be78a3d99bd360d19e500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000066ea75c3000000000000000000000000000000000000000000000000000000000000002b37eaa0ef3549a5bb7d431be78a3d99bd360d19e50001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080746ae602ceaec3387a089510db6ba15a6a45a6b60aeb564d2af783a2249308d935f550046db0988101d4d533b7d390b764e1bc7afc641fdd957f1c421e5c50491cff0b00000003000000000b0000000300000000ee8308be5f840158d94082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080fb504691bbf5c90ff09420f32385c4df07792dd1f0017568c765f1a180386bd54529e1b0819171dbced80e60b965c78a1afd887ad60450c0851fc4472a1aed711baaf8ee83019e5584019dd18083035f30945eb6b3db915d29fc624b8a0e42ac029e36a1d86b80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000ca0000000000000000000000000000000000000000000000012fb937557964d13c00000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000000001082044d8080bff5978438909bd3d0bf26d170678ba5ab948afebb9ed51824a16fca21bc2196068165970f68315889bcd05a63d9f7405722b2f0fdaceda25eb59c93b897e6d41cff0b0000000300000000f9028d818f840158d9408303a82b94678aa4bf4e210cf2166753e054d5b7c31cc7fa8680b902645ae401dc00000000000000000000000000000000000000000000000000000191dfe1d94000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104b858183f0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000004786e420000000000000000000000000000000000000000000000000070ec0f4c9ee97e000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004449404b7c0000000000000000000000000000000000000000000000000070ec0f4c9ee97e0000000000000000000000004abf4710e38fd914212ec0d30026d0fa9d51d40d0000000000000000000000000000000000000000000000000000000082044d8080d6fefcfe30a6a3903219dbcf5937bfd3ee6ee34564e643242a790393324905217ad5bb7e3950286ec8f627314e6e18d8715ac80379c8f7d36eda1672d0bb69481cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ed9840156dd708307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13db60000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c8260d3d200c7d000000000000000000000000000000000000000000000000000000000000000082044d8080098be2bc098d407c1a9d7cadf5840c2c250e338538dc1ae0f5df4ed073f8a85414e47ece3ccc969f95ce9251738051c03ae40575075b9b4b4309210dcfbe44411cff0b00000003000000000b0000000300000000ee821a198401793280825be09482ce01bba0c29bf9b61dc9cfa7968b1e0891f5678702f89f754a380d8082044d8080b2c54612390fc975b7d024f6e8eed9942a73c0d2101ebb04df518e3744db33b4009c3b2e21252b7c462b71a2c34f8a40ca9f84bb30ab6fac7b692840b383dc481cab0b00000003000000000b0000000300000000f9070e8201138402d33c58831d10fc946b2c0c7be2048daa9b5527982c29f48062b34d5880b906e4b80c2f0900000000000000000000000000000000000000000000000000313282fe1a7140000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e5000000000000000000000000000000000000000000000000000000000025b4a900000000000000000000000000000000000000000000000000000000002548190000000000000000000000000000000000000000000000000000000066e1497b0000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000006c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000025b4a9000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a500000000000000000000000000000000000000000000000000000000000000018000000000000000000027109591b8a30c3a52256ea93e98da49ee43afa136a80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a5000000000000000000000000000000000000000000000000000000000000000100000000000000000000271007a42e9f31c1fb7c2056b040e55e35c2ea6744770000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e5000000000000000000000000000000000000000000000000000000000000000082044d808057853a22fce3f85fcdd486d18e841d491de09b35e2151168a3af8bd9352e5e616a70f07e3aad14a6760f82ccb45077001a395a1c9a5556c0d33241225108ab081bff0b00000003000000000b00000004000000000b00000003000000000b00000003000000000b0000000300000000f9010d821eda8401607a608307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13dd40000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000981eaf19eb97c6000000000000000000000000000000000000000000000000000000000000000082044d8080bbaae2e5f840a60b75b397d51ffd3f6c648b7077d6d6091491237e9efb69d8c91620f7793c4dddb315cf997662a77b2c48cbbf881985c642d3cb63c4528a02b11bff0b00000003000000000b0000000300000000f02f8402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109875543df729c0000840333e82082044d8080e79b7308819654ce4d40697f7c1f044a00ec7796c73596957de663e9274d072a76e03b8eb9732d95e673501ed9cad4a63a32d33aa256caafc4f1867fb13eacc41cfff0268402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109873c6568f12e8000840333e82482044d80802cdb77771d166fa9140bd3ea0d17bdc5eeac3836d05d6144612c0434af52b5db2aeac6d0a8bdead3238ee12124dd9f387388ab5b52ab1100a1dadd6a8e9b740d1cfff01f8402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109874a9b6384488000840333e82382044d80806cd1e42d4d065497ce59e511d21e6d5819bf91ea9fa250786b757e0705f356eb5c43ae1da48a134429a5584590f1e4580d103310c3806cfde466a9c5231e35eb1cfff0218402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109873ff2e795f50000840333e82282044d808084e8d0f689fe1d5c190a4247b452a33fd6735de9d5f9f5003d8601d5b0c707d631c33cc82b2690e5df6efa0c2f44d68f62d47cd0922a94f802cdbedb115ee30a1cfff9018f830151d284018c1e408302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa0000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c201fab6ebde597669e39a9a69d724998e50b2f8000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000001630804d32c10000000000000000000000000000000000000000000000000000000000000001b75f9deb5f8d72d9cd3ea0037c11ac2401d310730bc692f37ec19861105e08c582044d808002793ca3270d2bdb8a4efb0eea532dec733de8f35e5a1c3f90bf185ed69c73a973cc8df40252607608de8728eb43d5e9fccbdabc4fce80c5d4fc858a36e928ad1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ea81808402625a0082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d80808805469c3fb84413a35485f506012ad1a6820b80738b3bf685b1d6175b9dfc79397f9b692337a0f633040da8401f8a5fc53852106d916911af513ff271e4aa261bff0b00000003000000000b0000000300000000f9010d821edb84017a1ce08307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13df20000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c82f0c5cacca38000000000000000000000000000000000000000000000000000000000000000082044d8080baa772220471b8149d55e5a5ac2bfbdd12c03eaeec654e08d9c74ec43b63d12a469bb9de8bf8ff2cd2edd8926daf89b887df334862e623a68cdc3b90bf7ba7391cff0b0000000300000000f84a318401681b808312c61094ee1727f5074e747716637e1776b7f7c7133f16b180a4db006a750000000000000000000000000000000000000000000000000000000008f58afa82044d80809489b2c6a4c5dc17ec3ec50d299e487c996839ed4d26bcc4c07f910dc21a53c04ff08798dea40e018cbd296ffa623776cc5382be8e8964a7383b1c8b9717ba2b1bcf00000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004cc20b00000001000000000b0000000300000000f86a228401681b8082b4e5940d1e753a25ebda689453309112904807625befbe80b844095ea7b30000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b500000000000000000000000000000000000000000000000026e055297acfc7bb82044d80802d542b44996d63d0e2ee4f8063c5c21b00ed851ab3f63911b2e0d4f9422287497b5085cca5dab841c5406c9bca6745b50a28a543431c355ee63d7e469e7b72971bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903ce821bc68405f5e1008307a12094d83729a90474d6ae09502ee3c58485cef40ac87280b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300010001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000093136a0f83405fd54b230fa9778a13300002cdfe0409010405000602070308000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000038408d1790000000000000000000000000000000000000000000000000000000384669fe00000000000000000000000000000000000000000000000000000000384669fe00000000000000000000000000000000000000000000000000000000384941d490000000000000000000000000000000000000000000000000000000384f1bda900000000000000000000000000000000000000000000000000000003850842c800000000000000000000000000000000000000000000000000000003851ac5b600000000000000000000000000000000000000000000000000000003851ac5b600000000000000000000000000000000000000000000000000000003853adfd00000000000000000000000000000000000000000000000000000000385578f900000000000000000000000000000000000000000000000000000000000000004208140e5e3dac54f5eb8cd8211adb203b85cc8f48b5d7d5768ecae9622df24a03866b2325c2c74955907e637e920627241ee7d4220e6aa6fe4b474fa0a1467c4ed42c8afac6f30478534bfd8a1b58ede8bfbef3d1908c0288a743fb6aeb7c12ae89fd84ca1a67ee72ea3d922c892a66e7caa0994879cace58eaf8e30ec0b641c000000000000000000000000000000000000000000000000000000000000000469aea40a060cd08cdc062fcb2e3a8b1b45faa0ff08b58607ff6941f18a2561184328d4b3414cda5fc8a80dcd28dee321564a1c1eaffabd2601892446321625b1324fff5e231ca6e5a020a57e4b2c8b83eac3067b76fcdfe8f990509b8bd6ba882f431699ec517fa2f99ea179d76f3f872438f6d787d9e6704c52145bc16b31b082044d808039cd808d19202040e8c498c2ab13bcdf3af0b148bac34a026b1925ccf02cdd89763a910a26e0d8c91449b9d39d62aa35f116b0f303de9ddbe0adb211226be4bf1bfff9010d821edc84015d46108307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13e100000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c7fc160522df2f000000000000000000000000000000000000000000000000000000000000000082044d8080bc426dda601756b4851b479be56dca7cbdcaca215314342d4e5cbefc7caeaa3b2115f87f94ec752953f052e46ffb2dea9db847ebabf21a239845e387e5d24fad1bfff86c82011484014ca44082ebf49437eaa0ef3549a5bb7d431be78a3d99bd360d19e580b844095ea7b30000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae0000000000000000000000000000000000000000000000000000000000f0719a82044d8080511a80bfb3d3116410992606d87913954e8731ed90a5131f21de84e035939b8273d0b786dc03ce855c47f5af2ca93567b0db3ed5fddb3f6cf5c19f8930e7f9d11cfff903ce8222f28405f5e1008307a120942e8932d5662ad426fe335162b233680a524395ee80b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000086e0a443f1c658191e265ab8658a37b300000a6c0405010609000708030402000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000023b4b9200000000000000000000000000000000000000000000000000000000023b749c00000000000000000000000000000000000000000000000000000000023b749c00000000000000000000000000000000000000000000000000000000023b749c00000000000000000000000000000000000000000000000000000000023b8a3900000000000000000000000000000000000000000000000000000000023bcb6800000000000000000000000000000000000000000000000000000000023cc1da00000000000000000000000000000000000000000000000000000000023db84b00000000000000000000000000000000000000000000000000000000023db84b00000000000000000000000000000000000000000000000000000000023f6bdf00000000000000000000000000000000000000000000000000000000000000043ff63dc58a98b2caba46ba7a58cfa6b0378efd7ffd05ca878dcb191d92a850679b626656e08c2977addd9799a357347e31d4965d125bbe06a7557899cbe5a7206c636624e5636f4f3d669960a863e778fc8a1d29779990238c08bc3a3f5df2908936ae2660fed387a6272501cd4e7f16a25ef0a5c05cce014666dacfb6e85cb0000000000000000000000000000000000000000000000000000000000000000456cd47dcbb6b4c21b5c5978aa6f26fdda9fc62555429ef0a40b98205fbd86617648c9b624b5c64efe94ed0c11d54cd14a1ef04c3bb18f4d0945a8e88ec84fd62470136dc0befd1267388d059cd270f7f0a706eded9d9d4c6f4578fa869525ef45073a43bb30b1cbaa3f4affad5b05288b4e0a8f0724deba4e6024f235cd711f182044d8080bf100f793640914b3157343b839411911773ed9a2a463b79f1ae0d6295f64f3f105d96335e24c7519de149fb1767d2c21c25f7ff0718c9ac0acd017449d799141bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f870058401a17b0082bd6194555a64968e4803e27669d64e349ef3d18fca089586b1aca8f532dab844e56461ad000000000000000000000000000000000000000000000000000000000000a4b10000000000000000000000006587b69f97af1c9e91455401cd3ea5594590bbf982044d8080fccdafcb3644954a5a889c5a673c6180b646da17f832cd853a74a6c2413cd51169f1cfabe17e29cdaebbcfd961d06fb56bb2da7b4b11f7cbaf137e84ec327a9f1cfff03284015be680828d9794a06e1351e2fd2d45b5d35633ca7ecf328684a109878a6f0ad96bd73e840333e8c682044d8080f16b6fa1af75307cdea0f6fa7170d8c353bbdc1af5099bd4b4b69f1ee48a4aba2fecbab3e4eeda03128b8308f6becfd6fddee743bde4b24bc7fe32fc0c9fee2a1bff0b00000003000000000b0000000300000000f903ce8222f38405f5e1008307a120940e7197e7200a706e4b8e0ecfd39ef8c45cff142280b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000b62c61f914e58e40747a3fd731525dd20002ce3b0308090106020403070005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000003e5e21b4000000000000000000000000000000000000000000000000000000003e5e21b4000000000000000000000000000000000000000000000000000000003e5e33a8000000000000000000000000000000000000000000000000000000003e5e6e40000000000000000000000000000000000000000000000000000000003e5f4130000000000000000000000000000000000000000000000000000000003e5f4130000000000000000000000000000000000000000000000000000000003e5f5904000000000000000000000000000000000000000000000000000000003e5f5904000000000000000000000000000000000000000000000000000000003e60a0c0000000000000000000000000000000000000000000000000000000003e60a0c0000000000000000000000000000000000000000000000000000000000000000427c6181796e378ecce0eb4756eb5cb3cb22c2f5c31ec76afbb61cdfc9f96c2999ac5dc98c1619fa6401ffa39d5343d7d5b3d32c7c84f294e8caea56fbd7912240fd00d9938f0a0f896ad6a9aaaad9ec782ae47546019d0b2bd0be88287821fe235539a12cc61ddf8f127b192a2724d095088635a3acaded33c14f154ca65ead300000000000000000000000000000000000000000000000000000000000000040217a78ca7b9fc8eed7813085e982f6e8d699f8d548699c4c401f9b74f2bce452d0d3f0f47a6220d36ac59f3c8cddaf9a270e932a234e473bb8507e1a7bd5b6b33cce711f84eaeda3afd58b99e2149fd24ec6aa607bae119a4b358c204fd90b6414015a5b4d6efe234bfb3d0e3b2e1042364cce80bf3286ea367d06ad7f0e27282044d808072d67c06cd88db7a351633d59042a3909e3564f6d707240357850526ee6c561a60ce10685dec1ad08cde99aaa47a3bcbd8f7bc647e08375d873f7a2584478b391cff0b0000000400000000ee8308be6084044f35e082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080813ec45766e4b4c8838d0f69507e663a3ae1280bc1021a063d71381ecd29f84302e1bcc769c6d76488c0346c2069ad8d142fcbdeb8a15ec4f729a2a14c0515141caa0b0000000300000000f9064e82011584016fbca0831143e7941231deb6f5749ef6ce6943a275a1d3e7486f4eae80b90624ca360ae0000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000631cd561b0c80000000000000000000000000000000000000000000000000017769505dbc5db0000000000000000000000000000000000000000000000000000000066e5304f0000000000000000000000000000000000000000000000000017769505dbc5db0000000000000000000000000000000000000000000000000000000066e5304f000000000000000000000000bd72882120508518fcba2ae58e134ecead18d979000000000000000000000000710bda329b2a6224e4b44833de30f38e7f81d5640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039400c73445066cf0991b56490031d9a8241645cd8c8532c84dccfc1a95f295600000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002fa5fcc93ac9b373ef0065708330f7cac493fea70000000000000000000000000000000000000000000000000017dfac08f2f2400000000000000000000000000000000000000000000000000000000000002105000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003686f700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6a756d7065722e65786368616e676500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000057bffa72db682f7eb6c132dae03ff36bbeb0c45900000000000000000000000057bffa72db682f7eb6c132dae03ff36bbeb0c45900000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0719a00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001a42646478b00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000000000000000000000000000000000000000f0719a000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000017dfac08f2f2400000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000b20237eaa0ef3549a5bb7d431be78a3d99bd360d19e501ffff01849c0ae884bfdc14dddeb7cae95494f3684148550157bffa72db682f7eb6c132dae03ff36bbeb0c45901a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03501ffff019f37552b87b68e7f169c442d595c1be7a0f03b920057bffa72db682f7eb6c132dae03ff36bbeb0c459014f9a0e7fd2bf6067db6994cf12e4495df938e6e901ffff02001231deb6f5749ef6ce6943a275a1d3e7486f4eae00000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d80802787b973f4d54062c810118dd824524f22139a2f95fc0c1847abfa0251e073ab71313f805daf772a06c1873d8390356030408d7cfaef18b7200e9f246c2012e41bff0b0000000300000000f9028e82393b843b9aca00830667f8943a464f746d23ab22155710f44db16dca53e0775e80b902647c39d130000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000012700000010000000100000001000000127000000000000000000000010000001170000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76c46702575bb24a61a3c6674a33b1c536418d418d7a2a5454760d0425f62a69690005804c0f4171353a8d70c267c85f228b5795422bb97e8701f57060b3afcacbda96828451185401ac923c55ed67ced54306dd4b6391546b221f111a5a62b01bc3c144011bb991df3e06a3f746ab38f98556f8e11b36c05ceb94aad204dffa5c664e9bd463791cf2b3fe4687153754968af9bcd9a70b8d25d31afef5e268e9aabb50aadf7d1ce0391fbf3eb6e6cf69572dd19bf6fc5081a462f752638d846b9d0862a6cc1dd706aadce7a16fa076ac160a7a7e59233f0659153b2efe2c60c5f302eb7cddaa4a1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d030005804c000827500000000000000000000000003ddc189d48a792d2c6262d20aa0f9f78e88a9d470000044d0000000000000000000000006ae540f1c3a96a8628d52b416ec1bf72932f83fe0000000000000000000000000000000000000000000000000000000001c9c409000000000000000000000000d2799cd96798074f709423369bf2fea60392852a0000000000000000000000000000000000000082044d80805b9b901758bfbabee0b9f3477ea901ad9b36199c881c5fbcb1fa4d241495f353045c800c11c133aab3e1fe1dd8de12d11392a42fffef25507c70ded109ad73891bffee8308be6184014b1da082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080406d26cc0725e8e4ec416f42fd563433cafed607432c1bf19783e749e2a8ad8939ea1ad189a766c6759c4a5210377b7cf68cff953b4447bf715010dba204d3dd1cab0b00000003000000000b00000003000000000b0000000300000000f9024f830151d384016c3a308305cae894555a64968e4803e27669d64e349ef3d18fca089580b902240ddedd8400000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000001ee4c000000000000000000000000000000000000000000000000005d423c655aa0000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000662d6dda64cfc7fc21d2b1005f406e984a178d4b0000000000000000000000009478dcdb5e5c190fb37f70358585430e1bb1168e000000000000000000000000199bee3bc56add7f1a6ad1995002730de45886d60000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000015b9a6cdab510000000000000000000000000000000000000000000000000000168458403b1f00000000000000000000000000000000000000000000000000001377a57a748600000000000000000000000000000000000000000000000000000000000000039dfb46e76740a84405448ea66a1d95618c9528481cd2730100d191ce78a360a487e98381ed3d5acc9ec5f7f31382ed1c794017648b3c7d25e8a8818173a65b8e690d56cc7f2e91fb3994a585e6553c4ce62cda42398be3ba59f1bd342f43dc4f82044d8080ea85b1543ba847d93e70fb47b0750c5197ebdf75f4efa28b37da97e41e68c12a57b1de35882e372f52bf5a16179a4ac2e453025d3b4731b212be3c2ddb3354b71cfff908cc2384016fbca083053197946131b5fae19ea4f9d964eac0408e4408b66337b580b908a4e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000006200000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000d1e753a25ebda689453309112904807625befbe000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000f91f8300f8200f43eecf89e7229f257f085ecad50000000000000000000000000000000000000000000000000000000066e1407c00000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004063407a490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca300000000000000000000000058684788c718d0cfec837ff65adda6c8721fe1e90000000000000000000000000d1e753a25ebda689453309112904807625befbe0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000000000000000000000000000026e055297acfc7bb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060a4587899c58e7cc2e54303188f56acecaea4779400000000000000001388000100000000000000000000000076ae48a40000000000000000000712ecdbf54b4d00000000000000000000000048b8419b2bc0fb63ee96e3a370e30b200cc2e6720000000000000000000000000d1e753a25ebda689453309112904807625befbe000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f91f8300f8200f43eecf89e7229f257f085ecad500000000000000000000000000000000000000000000000026e055297acfc7bb00000000000000000000000000000000000000000000000000070f4daa5dc3780000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000026e055297acfc7bb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022e7b22536f75726365223a22446566694c6c616d61222c22416d6f756e74496e555344223a22342e353935383138333735373437393135222c22416d6f756e744f7574555344223a22342e363531333730393136353932343332222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a2231393931313333333438383432333137222c2254696d657374616d70223a313732363033363934302c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a224968587873792f325642344f4a6366357074564f4a524472566676336c7941426173386e4e3151446e537770426b384765546b6d79637532347a4d4f58535a6b5059672b4d53394a6374336c79587745766b3679382b572b6b303643332b6b4369753337654d49484b47436c6136742f7377416b72625773337177322f474378536639325973353038754f764e6446624f5877705a6c3374794d30745833574a724c616551426c4d42466b77664f5632445168596c32716e41494e344563655a574c4a456a555865417545694a62685a39534844312b76746a4b457452355056796a4472727563476b54634d35745739523549755653554f754e6434632f39766c537269343569464234456f37316e424931624d2b446e78495837563137792b41634d6d585235336d7a685645476b59647848745a6e706f336b776f683841356751786148433678362f4a4d6e56686a5049716877673d3d227d7d00000000000000000000000000000000000082044d80802ca8348995a238faba4d03fc97638947ea65044c08547e8bb931231fb982886b1e4851515149a0b4f1e5131cf5f123de352c7172839852eef079aeb26bdcba361cff0b0000000300000000f86a0b84014b1da082b4cb94c5015b9d9161dca7e18e32f6f25c4ad850731fd480b844095ea7b30000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae00000000000000000000000000000000000000000000000098dbf0792f215d3882044d8080e1a347db720494ebbc892aefb0db8be1c1a4f61b3310d9528abde1ae2ee1cdde24efc0ecb27d55991eb644ba770a91c52ab8e69616ae78fdd3557d4c7122fec91cfff8b10784014b1da0830151b3942a66b9c88caebe9aefc4ecd7b5e7e60881f7e15c860d18c2e28000b884056b01ce00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000007ce66c50e2840000000000000000000000000000000000000000000000000000000000000000000a63727970746f72616e6b0000000000000000000000000000000000000000000082044d8080cf67643e57fe0e3d537c2c8fe40440a97de31e6606b6968a34ce382900b587c128946d1e7505038bf50427502088c8de36e7ebe911171ce1288c5bec04d91f521cff0b00000003000000000b0000000300000000ea10840c49018d830135909468c89f28d223be23967480c06057d22b3400293580845646c6b982044d808002b9b94c0a76cc3e16e09a8b9e9506f4d3f57ab9e8932717c80659bcc9837d0e631d462ca21abb98fd5a0c63877e2d59a8d332c10d30569ced47e5924f2100ef1cff0b00000003000000000b0000000300000000f9068c0c8401e915ba8307f1fc941231deb6f5749ef6ce6943a275a1d3e7486f4eae80b906642c57e88435f1a3a9536b95a72ff7601f7df30a5b8da95a0580587d62f17457034d663d6400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000002ac8900f549d90f11aa593db90a0fb57e753b805000000000000000000000000000000000000000000000000001087372423cc6f000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000086d746f7073776170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863346639646134323361626336393237393937383338313533656131386631616266396435613938000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000b49ead76fe09967d7ca0dbcef3c3a06eb3aa0cb4000000000000000000000000b49ead76fe09967d7ca0dbcef3c3a06eb3aa0cb4000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd400000000000000000000000000000000000000000000000098dbf0792f215d3800000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000084eedd56e1000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000000000000000000000000000004e438d8deb1321000000000000000000000000000000000000000000000000001390e3637ac4c8000000000000000000000000c4f9da423abc6927997838153ea18f1abf9d5a980000000000000000000000000000000000000000000000000000000000000000000000000000000057bffa72db682f7eb6c132dae03ff36bbeb0c45900000000000000000000000057bffa72db682f7eb6c132dae03ff36bbeb0c459000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000987a1c083dbb854f00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e42646478b000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000000000000000000000000000987a1c083dbb854f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000000001087372423cc6f0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000f402c5015b9d9161dca7e18e32f6f25c4ad850731fd401ffff019bc342259cceda0487e70b71a3161f002a95f0e80057bffa72db682f7eb6c132dae03ff36bbeb0c45901a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03501ffff01ca06375be938a2d6ef311dfafab7e326d55d23cc0057bffa72db682f7eb6c132dae03ff36bbeb0c459011e4a5963abfd975d8c9021ce480b42188849d41d01ffff01337af062ce32bb423010415196e315c4154d36c30157bffa72db682f7eb6c132dae03ff36bbeb0c459014f9a0e7fd2bf6067db6994cf12e4495df938e6e901ffff02001231deb6f5749ef6ce6943a275a1d3e7486f4eae0000000000000000000000000000000000000000000000000000000000000000000000000000000082044d808097c0b85dae5e497dc2def38b73fa7db167176b675e1ca0b9b5fb7acdaed7d33b3b01365195f76cdb02373c4d9553f9e6093d77ef79fb4980b74383688d7a06951bfff903f22f8401c9c38083048020941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5ab9c89ffcc1119514c1b92d64eb03aa97ed8fd52866dcd07383c2c7d4af347a5000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000009e47fbb2a2a27b3b02e4a63b3ef5a3dc863c02230000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d52696a487341633572727a65414e584d735334725a68555a6b576e35386151384a6776523832786a4351374d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000418824be9a966bd0478025bd02a796118c2fb8a23756cbbc2f82d61314f9c6d9d3341db0d02c63f9530b2ecc581ecec5acc10a6ff8a641d52071a0ff8421ce59701b0000000000000000000000000000000000000000000000000000000000000082044d80807034d54692cf1b649157e81c0bdf66632633f6485cdf329ceefb5d55042d38760b6da7e01b7a770f453bec69609332f517ac66c5e4b7798e75abef875a3791651cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f902d3138402625a008305aaaa947481c16e7782608ccba70029c0fd41d78aa6b56e87027ca57357c000b902a43593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e13d1100000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000027ca57357c0000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000027ca57357c000000000000000000000000000000000000000000000000000000000000018d0f800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000424f9a0e7fd2bf6067db6994cf12e4495df938e6e9000bb81e4a5963abfd975d8c9021ce480b42188849d41d000064a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000082044d80803baf44841d975b6bc565ace7c443e7ae7d9ddac53580a8a85ae9ed88c26907ee1e81aed1a48561a8238e7476616c026622e9e207b4a337dff4e87be321a5c4f41cff0b00000003000000000b0000000300000000f903f38190840189ad4083034cca941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f53796b8092eeb16d8bd97e1af7a200666a05419e70bad6c72d92f0e2af24f0d5c000000000000000000000000000000000000000000000000000044364c5bb00000000000000000000000000013988cef5721a77e9a873b9c988559abd698516a0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065a6572696f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5441676266396a785343475176785a476e6576435437394c334d7258794d6d35715957687a68476d3646586b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f95331b37d73776f15d46c38bdbe349eccc9f1f7928fb41f54b8fb9fd5228843643976c760da92db0f41c7b1506e1fc66bbb01f8b2dc70e5a96ce3cd0502b5351c0000000000000000000000000000000000000000000000000000000000000082044d808081ad4be405410a00887985f909b52ceefbf5b7414500559946678f167ec95b646c37fff789b69860bb5850da757344ac27d1c45c9b8c4374d7eafbe2d3d8b9141bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ec39840175d72082520894e4edb277e41dc89ab076a1f049f4a3efa700bce88705692911fe533b8082044d808000315fc52bc3d2f4aea5fd2de0229fae82cd273efe66b698c4ea990e20b60fde5ad8c57f6f5748d6b2709cc620626f50e34a577e7b30bbc2fbfbe8342793af081caa0b00000004000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ea2c840d42700b83013590948418962b8e0c95f386f89415d99883cd52226f808084fec0921482044d8080783253c33c7b79ecb24aa939f976a21069d7837a666da00eb8a2eacb5298d4457566c70d92178084f27c8538eab68e46df3d914512fdb1032ae5933071f699541bff0b0000000300000000f9014f8301154c8401b021008305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13c3100000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000733f79f5a91ea2abe3e50940000000000000000000000000000000000000000ae01687505aa45399c5393c00000082044d8080da4584b7d6fc09c1e15ad4cdc966ebede2301b221fe74a64f3b4b077c6b7d2bf2d38c74317a7901d41676c66859e0e26b28a4ce29ac544f67d8d2cb8b503e0481bff0b00000003000000000b00000003000000000b0000000300000000f903f381978401681b8083030015941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5e88a8ba0ef0f81c929520cdcdfaabfaa6b722c4048cec8c493456ad0ab0c325d000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000007df220262a4c9ca1fa9b7224bc6f7f4572a892cc0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008496e6a65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d506f336736676d4a72386d6b6467384a57764859576b4659486d4b47485777436d425175473276416a44315300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041bdf0d198c5f6ad13e9fb6c891a05b67ccced70655831525ee9febd3c21cb33595c536eb39f577fae252fb903477529ee1d4a5d15ae9731d06543a56bfe1fecdd1b0000000000000000000000000000000000000000000000000000000000000082044d80809de5af8deb33a1a58f32c6dcdac7c610ff048603e566c8cd0cb4fd54b65a38934d59fdd44897d94a3be46fca792bd7a75e88e363289921a8c1096967b30b62441cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ea818b8402faf08082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080612819e135e75602473c7c42e3a2fe293cf9e99ce22373be8ea63724f3381f6f746b65a1f24f8f3abf355f4c8dacdb2b96b48befa479cc96881163cc827e14841bff0b0000000300000000f903f2348401745080830343f1941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f545b5e3028d7045b7f9dea45daaa86de008257fba39d272d868c9d8edaf813fb4000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000005f9c7447129fa10af4cb51e24be323abff1759e50000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5138456f61454658787954393331636d42684653426447486168444d684d353473484b4a745a417072507168000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414b2ae38ceaeb1af116941ed4ec6eb66a86116d4d08a4920ac07d1feaa8ee607d6d5b635040118c5c37fa9f05b6c8a4cc07c3619a8c0def6821827ee9539530e81c0000000000000000000000000000000000000000000000000000000000000082044d80805aaeda75850432083fdb91bd4b910a0850c50f29c6a81e7f5661c0a0e19c2b282a15c71aeaa646bec001c6421af6b380d7321cdec496258e1a893529de2fa4621cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be62840418476082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080b7a139b28d8c5192cab1a5c55a8efcc93cebddfcdb1a0376188144df587e57b1302f1ec53be25bc03bf9fd84b1bc298006b6d0a3b4cb296342bcabd8254f9a4c1bab0b0000000300000000f8f108840174508083034de3942a66b9c88caebe9aefc4ecd7b5e7e60881f7e15c86a0285cf72142b8c4d559ccbb00000000000000000000000000000000000000000000000000000000000009dd000000000000000000000000422f746a9d5b73e25d41dcc18fc1ea09da88ee2a0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000a63727970746f72616e6b0000000000000000000000000000000000000000000082044d8080463a0e84cc018410c62c0f6cb7a9055556b94cf37fb5363e004e2952831ee3a109837118f4ea90e6753e6f16dc192a72053fc655a789d15767496bcdd45e5b961cff0b0000000300000000ee8308be63840169a22082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080dcada0e0a24e607958874f9206c0a0540fa170176d694c6c1d0546eb6d1b787b4b63a62905b6a8b302168d6a12fdec6a5c89b10ddadabd6ccf3d0f099ac835271babf28231db84ee6b280082627094580b1a559eca8c875381251f291f96777ef3af89871715c995317000840333e95a82044d8080f5a183cd5ecb9ccfb91a8c22d2a14191a32c2325742e13b48b8ab6d2b1ac8af65a2dda29b70368485ae17017f0c8570850b37600fc668c6547aa69cde7e2f6721cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000e90284015752a08255f094e10add2ad591a7ac3ca46788a06290de017b9fb48084632a9a5282044d8080aeb52a12273e7d18d4d61292b972b9cc460236c359d0b290d7440e6a9891e8f37ef89ab59bdaed7e90a33fd03dce86ee855f91a6afe8515009b4dc2542fc315d1bff0b00000004000000000b00000003000000000b00000003000000000b0000000300000000f9010d821edd84016bb1788307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13ee20000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c8390bcde7c066000000000000000000000000000000000000000000000000000000000000000082044d808030d0dce5dce8bd5dac0a2de99f3a997bc9b2720b37e0c916d521614aa96b2b295d3ed3ff7c6be137f9e895f4a0736de165dc14c87878a1d8b01222026689675c1cff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031150b0000000200000000f9010d821ede84016ee5c88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13e4c0000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c805935ba60ef3000000000000000000000000000000000000000000000000000000000000000082044d8080e6523db0d8d9f0fc01b8d28d354646bb37ee3a01d94299baf62a3f0513c1cd5b4706f80c68a13689d843d784ccbad2a69c049b87cf72f7a244bab58b25d3f2ad1bfff9010d821edf8401721a188307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13e6a0000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c7f7976b336093000000000000000000000000000000000000000000000000000000000000000082044d80805f7ba49687fc0feb05f0fb1b46b605610f94161628bb0d78a593085bb2d6328d67d03a13c5f7b0d080fc8585f1dc01eb567f4ec6dc22fe372d0dad7b2057b3aa1bff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000eb82013a8402625a0082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080a1e4294a95f81c0195243e271861ea2cda2b25132a4e4866cb58deb993128035280b82205bf5f93e6cd481ea9ce1b4408428898fc5813bd8bf58df3199de169f1cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000e9028402d301b68255f094e10add2ad591a7ac3ca46788a06290de017b9fb48084632a9a5282044d80803516937610ade172676e6ab42573bbde2ba8b143b82c019640b2c41168bc7e445254048ff8978b561f0a95d2d71f4da3d8c67c7f05cc90ed9521cd01e6da0c2a1bff0b0000000400000000f903ce8226f38405f5e1008307a1209467d0809ada627ab5bd623640332a2f370b2c906f80b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000084049d6baf6f3253bbdf284da6b5d1370002ce300301000706050902030408000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000005f5b5a40000000000000000000000000000000000000000000000000000000005f5b6080000000000000000000000000000000000000000000000000000000005f5b6080000000000000000000000000000000000000000000000000000000005f5cb840000000000000000000000000000000000000000000000000000000005f5ce0f0000000000000000000000000000000000000000000000000000000005f5dcbb0000000000000000000000000000000000000000000000000000000005f5dd180000000000000000000000000000000000000000000000000000000005f5dd180000000000000000000000000000000000000000000000000000000005f5dd180000000000000000000000000000000000000000000000000000000005f5dd18000000000000000000000000000000000000000000000000000000000000000420f5d5e5132ac16831c0c420b2e4348553e2835cf6b7764eefb1908997cc8501dfcf180d7ad72bf3313f016313fe7aa3a134b06b53f4f2317a74b5455878011a986cc2fd0b3eb2e32ac671e13fe59db59688a1f49fd71fc5ea1de3162975a6cd243ab73d9dfb19ed57b626487e0e8d4192de45e5084083cd1a03aeb6b004776b00000000000000000000000000000000000000000000000000000000000000046545e8567be21ffb54df1c55b30908e62cae7ab7acbd840698e425e4b4999ea36d28dd45cd30a8ed96f05762d723d83130ace27284c9bc868bb8f1ccae6726f731b09fe696a4b676a0cfdce2a8d0d58fd9d4aeb116110fe1918a8b4f403db20b35b9625924384aa070bbc25c7c24c2f2f39577af4e2bbbd2699560ee129687ad82044d8080270674afad2582de39abcfe1cb19d8243b9f6d31ac6df02257ee13e2d558e4961656fa5e782a66d6f59ff6881448d38b61fd630bffc81442ce9575081c9ea1be1cff0b00000003000000000b0000000300000000f901ef830151d4840154ba908304aaf894555a64968e4803e27669d64e349ef3d18fca089580b901c40ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000002557c000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000020000000000000000000000001757ee1ca5541182d242027a734cefb3d6a745fa0000000000000000000000008cb1d12040c282ce84f851763e2befc48a0810b10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000015705520846800000000000000000000000000000000000000000000000000001563c0e8ed6a00000000000000000000000000000000000000000000000000000000000000022bbc5cbbf99c0d37f3d2aae6daf5a4d7dfa26f8ad4ea658ceeacd1d16bea82961cb86c8ae18cd27416d0782aff31adf25117d75b85cac5c410df5b8de615c70a82044d808017f2d6241834bf9550f7264c42b27a3d05a2a8fb5315b9d82791edbaac56107a5c926d96702e5c401424c34f351eb84109cafe915de7d7d26636ea9806a814801bff0b00000003000000000b00000003000000000b0000000300000000f86b208408a4f1f8830156219468286607a1d43602d880d349187c3c48c0fd05e680b844095ea7b3000000000000000000000000c8a21fcd5a100c3ecc037c97e2f9c53a8d3a02a100000000000000000000000000000000000000000000000585333fe46a1a1a6c82044d80806b533a403af42468742ae5f3efdc12daf9bd38dfa3059924cd35e9d2d2a377444acf354174f9ca1f414ccedd2e434e0b433ff872f60a20da007ed0f80458d5851bff0b0000000300000000f8b12784021c58208304ac1194222228060e7efbb1d78bb5d454581910e392222286119763fd5872b88432accbb9000000000000000000000000000000000000000000000000000000000000007e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cbba106e000000000000000000000000000f78c3faf2046813f12fe6652ff952b2ba6ba845a82044d80808f22f2de07105da8d5d9dc8edb193fd108c8fd3c4858dc20a23e09acb3995eec3dbd05a55530b38f6b4fd7a1416914f04f13fa6c25bc4b1397a784abe13b15f71cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000eb82013b8402625a00828d4594b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080a54a20ef6241a24e66579a016490ddaa20ac5237cc3920240bea379f56c5f16519284d19c65b19253e95a448bc53b8e8674d4904eca64bfa9d6804a0b21462ec1cff0b00000003000000000b0000000300000000f903f26a840174508083030015941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5445b43df8087dc5aea1da71e16e6be5db16a5a065bb35fc5a7561c4fba8bf78d000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000009cc2cb595f85c9d1c527996555e21327fb5e47440000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008496e6a65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d51715667374e78715a51573550667737417932774a76486a445748577937624a6b567659667974754b47774400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cf056d549f465af9b6e55e1dc4762e9790374b2bb1e47bf465dff9a977521b291acfbd54361ebf15ca703a29f98ed7de9fcc1739d17123b634f88b24e1e3f5751b0000000000000000000000000000000000000000000000000000000000000082044d8080a67e87448e21458f2e0daa72470c7b3aba821cd5b07d4128054d5ea267941bf06cd1939cada4bb72f53fdbf3f479a9eada26885bb585c42ea28031f4c04c4a2e1cff0b00000003000000000b00000003000000000b0000000300000000f903f2258402d6b0f38309105c941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f57434eed86a2209cb61008d35514b33b5b56d69aa8d6051dcbf2adda360fd7abb000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000132239919653064455f78e683c365bdcefa8bb3d0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4f4b582057616c6c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d52663166534d787a51314b54654d5a4866657635706d736e61724a76725359784a6b7667393973334e7a4c6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004192bbfc4d495eb5a1784f3ff5109e78c2be43fed9874388b78bfa25c3224866d51712a636189146f18ec716c2ddcde6c64df64fe8f2936fc5da46ca656cff2aec1b0000000000000000000000000000000000000000000000000000000000000082044d808084ffe2036a8fa6ea7499f4346870a19b84fc74158e7aa047eb67cc820563e9df5eceae53db779c5ed39afa1177f3a62d9bec678fc3e4bf5ad4bca0a53d6fb9ee1bff0b00000003000000000b00000003000000000b0000000300000000ee81a68402625a00827b0c94009905bf008cca637185eeafe8f51bb56dd2aca7880429d069189e00008082044d80801b0200bc64259973edb845427dee669bba37b97e3ce801142133f133aa7a77e46719187d4f3d66ad9e37983725da1a3061178b2360912a0616e94c51a34bb7161caa0b0000000300000000f9010d821ee084018bbc988307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13f3c0000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009832b42064d1b7000000000000000000000000000000000000000000000000000000000000000082044d8080dcd2ef6ffc64607c3e8228e4a11c5ed700b6b617bb7a7622c9a6577c7e2aff874f66acc28f4e6a4ab30d106a7a2286af17a28eb5d2402c5552d0c9520d20d6901cfff9010d821ee184018553f88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13ea60000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c8198f0b7fc618000000000000000000000000000000000000000000000000000000000000000082044d8080c12153878eadcd2d15c857ef2792e9fc2db5d3bd6c27ae891ab4f16a25ba7294600e03e412be6aa0a2c4f040d26eb1242d254843ccea87ed5f696ce1699258941bfff9010d821ee284016ee5c88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13ec40000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c82e8c66cbf1a6000000000000000000000000000000000000000000000000000000000000000082044d8080fddf91e72906765982df34a767d163cbbd0c0b347658ab56a0a3a04cc1f14e124cc53b90924519309db15d1dd1fd2b1e575beb4bcc8cea65e68b054f9296ba4f1cfff9014f8301154d8401c445408305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13ce600000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000731958d2d429ea51c49f3b00000000000000000000000000000000000000000adf463a3704377c3ee8d91680000082044d8080545c76198bc384464dea22db921db86fb5648d1e859861a5d0eae70a2e46b75132b3c7d6c8988a27fd2b269cf097d4199ff2d2ee0dd71d759c097df85d7c47e21cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be64840413b38082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80801b1380301ae8db0eb3f94243c29ebe663ed4dad76f4affbbd516cef3379933ee0924b271cc7c68abbe2a716e18c485712d126d9e0897e17bc99739b86a98f9601bab0b00000003000000000b0000000300000000ee8308be6584015be68082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80808912439f3a0301bb9fe59b96f6977ef1bb83487fa42628a7d614d58718ad148b07c60f18aaaf59d36f2a560b9f13057a3bb48a50a31f7772d4281bcb302fdb0a1bab0b00000004000000000b00000003000000000b00000003000000000b0000000300000000f86a0784017bf1a082b48f941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b300000000000000000000000031c2f6fcff4f8759b3bd5bf0e1084a055615c7680000000000000000000000000000000000000000000000000000000000194b4782044d8080d06b79bc4df74c4a6ba59e8bc1e57e9c3194700dcbf8df2fc3c9fa2ef555844a0dbdcb8e23298a3dff13aecf21fc2287dbfa68df1cce01614f6659f7729ba8c51bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9044c0884017d78408304578d94b89a6778d1efe7a5b7096757a21b810cc2886fa180b904243593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e13e4000000000000000000000000000000000000000000000000000000000000000030a000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006708ca1f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b89a6778d1efe7a5b7096757a21b810cc2886fa10000000000000000000000000000000000000000000000000000000066e1442700000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000041d1a054cf52782e83d7b7111c466fa04e03472a9c2098b9a0dccdefb17745170c3fc27f88fa426fbdce2343c90a3662dc441e75c6ba37988ae18160ca98fa7f141c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000194b470000000000000000000000000000000000000000000000000002820037b6d0c600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002b1e4a5963abfd975d8c9021ce480b42188849d41d0000644f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000019581a7c83f846f74fc66fbefef1299521c65e390000000000000000000000000000000000000000000000000002820037b6d0c682044d80801efd30d23b23d0b0fbfe93446a107c35aff67967f83b03a5a93799323fc98f5568d4c4c7bef6650609c9d1b067eedacac74d49b715b587ec4d61bc4d275fb94f1cff0b00000003000000000b0000000300000000f9011381b684017d78408307249c94f6ad3ccf71abb3e12becf6b3d2a74c963859adcd8723a0f814eb3f9db8e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000b1db6eec8d8c629f9f7e28bf2680e3e264e929880000000000000000000000000000000000000000000000000000000066e141bb0000000000000000000000000000000000000000000000000023a0f814eb3f9d0000000000000000000000000000000000000000000000000000000001633e7d000000000000000000000000000000000000000000000000000000000000000082044d8080f3a405df3bdb20a750ee93584f05ab47603187da5842f402d2c9d766f50560717e471e18c695cc989d88de0aa59c4c67ca94b1093b05004db29a50d5f5751ae31bff0b0000000300000000f903ac0484017d784083077b6f94d8e1e7009802c914b0d39b31fc1759a865b727b180b90384ac9650d80000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000a40c49ccbe0000000000000000000000000000000000000000000000000000000000004fe0000000000000000000000000000000000000000000000000000003a2cd4f5c670000000000000000000000000000000000000000000000000000000001805e6400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066e141b1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084fc6f78650000000000000000000000000000000000000000000000000000000000004fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004469bc35b20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf65cc37241d426970b91720e8255140ade00f05000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064df2ab5bb0000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000001805e64000000000000000000000000cf65cc37241d426970b91720e8255140ade00f050000000000000000000000000000000000000000000000000000000082044d8080a3d508557b59df3d77b1fa113f2223b67f82fde7ad9d9cf6f8e2b3bdd9b52a672a2f437e010cd99922806b351f71ab6896a97723f26cbd7b5781c0305b64f81a1cff0b00000003000000000b00000003000000000b0000000300000000f9010d821ee384019bc2288307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13f960000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000981883f28b083b000000000000000000000000000000000000000000000000000000000000000082044d8080b5e4d0db289d0e6f61d922545bd7579bf20015b29bee82a2eb83bc9bbda09ea04b33b1f4c5f3e5aba7f5821bfa1a9f464373d9afbc99e4dcc58297a52ff062f61bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000e93a8402cdccb48255f094e10add2ad591a7ac3ca46788a06290de017b9fb48084632a9a5282044d8080be4c664f330b9151f51b9471b46aaffe652d5a410fa66eeb8d1f84d72f80cbdc3cc90a5514e45f2f576ec352449eb0313c6ea9a15769a11eb2ea11d86dc6f9ca1bff0b00000004000000000b0000000300000000f9010d821ee484019bc2288307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13fb40000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009834b4eb369439000000000000000000000000000000000000000000000000000000000000000082044d808075de06ec5466daebd6df45e07e32c8c4727338a259fdb82acb5feb48d3652fc838947db58b0d09a037a303b3c74274196df3c5de1142bcd2f88aabaebb2323881cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9018f830151d58401a6f9408302c78894555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c788000000000000000000000000000000000000000000000000005d423c655aa0000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000796b7bf65e570f59e08ac136dcc32e8f642f683e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000016442530c6a80000000000000000000000000000000000000000000000000000000000000001e093a1bf79cf96024c9b7b7ed3a8e26c709e92dbbed7918174b100d95d49e55682044d80800977d0591c4d658fa5bd677158b390bf73e8ae5d7218f7bc4614f3eed5126eed6226f3e972b5c62ec523064f6af4787e049c34f96e41dd9d77d4d25f0d2e76161cff0b0000000300000000f9010d821ee5840193bf608307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13fd20000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000098370b3e46923d000000000000000000000000000000000000000000000000000000000000000082044d80807df0dc6ae14e4160030559716f36cf546839515e3a14e78546cbab05e2107d09760212c06296915c5c1a92d990722bd3e26afeb0cf29bcc462985f9331f117981cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f901f30a840175d7208303465694678aa4bf4e210cf2166753e054d5b7c31cc7fa8687079fbc2a49e180b901c45ae401dc00000000000000000000000000000000000000000000000000000191dfea9b9b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000104b858183f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000af09364718f745b6855df11778d9b523eda1e4bc00000000000000000000000000000000000000000000000000079fbc2a49e180000000000000000000000000000000000000000000000000b55d73b977dd5698000000000000000000000000000000000000000000000000000000000000002b4f9a0e7fd2bf6067db6994cf12e4495df938e6e90009c4a2036f0538221a77a3937f1379699f44945018d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080438d26a5ab0788935cfeadd2658b8a1447d9448fba3f8bbcf8def6c0b74f77cc77c03cba29fcb4d3fdc1b913bcef92e36e10cbd8bd913da805760e2d9c5242ec1bff000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ad40b00000001000000000b00000003000000000b00000004000000000b00000003000000000b0000000300000000ee8308be66840433bea082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80806f7f1dc42b8cc34aeb9ee8206e47765be4cfdb5536272d3250950648aa4e3d5907cfece316b171d676cb399a7a75c4b11ed9ed5a3f27dd4122d8c909d4dc51a81bab0b0000000300000000f86c81b784016694e083010ec4941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b3000000000000000000000000331f3a300b7115a45ba31e3428ac002267bb6d7700000000000000000000000000000000000000000000000000000000000f424082044d80805fa04ce2bc7596aa78bff65f39212d4f8c6325cbf35deb9fe4fe916b9b2858e33756e78b77457b8c39c573cd729120935a76c6a1d6bc73459a16514fa1054b5e1cff0b0000000300000000ee8308be678401945ba082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d808090c0419282e01e7ccd1b49b104e6e722c161127a5197edd0bfef9447d4600a2a3e17a07b6523f4aea15c603004537da2e38cc7aec7ba9801099d516b53fbff0a1cab0b0000000300000000f8ee83019e568401e53ac083035f30945eb6b3db915d29fc624b8a0e42ac029e36a1d86b80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a1000000000000000000000000000000000000000000000000000ac720b1f97be20000000000000000000000000000000000000000000000000000000239000f7e000000000000000000000000000000000000000000000000000000000000001082044d8080443dbf96a2e3983c0cbb6e79cdd2a18777b63fa692b19ccfed0bd0b2ed0a6e667db137169578347abbb112cfbf718d3795e48e569f79b357a042c5f1b4837aac1bff0b00000003000000000b0000000300000000f9010d821ee68401a893688307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13fff0000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009834b4eb369439000000000000000000000000000000000000000000000000000000000000000082044d808049b78766cf06b54ad4071fdf9f0182f50a767cae6a31f64f2379b7ab9b6ec2e65fd26b7b443ef10cffc30988b7bbc73132033d1d6e64351a47baf21fc12bee9a1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ee78401b230588307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140180000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982a06a2b59803000000000000000000000000000000000000000000000000000000000000000082044d808086d7452f41889b2e6811591fc74bae161b77d3712a76ab034c6d7634f3e0041667e2f1c34dbe96c8c6486cebbaef1f7b3cff036f05e72662317089557121c1d41cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9014f8301154e8401c61a00830596399473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13dd600000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000731d4964ad65b18a4655c340000000000000000000000000000000000000000adf476e7c1ddbf9c2658d5180000082044d8080ee854267d400eb90253e3ed78b0ae5ec32fe0ba653de34d80f5ce044cc0fff77498cfa898a2ca54b3b84abab13ba9fd6658f4e0f644d940d33f269a9e6d7cba31cff0b0000000300000000f9018f830151d68401d0ef708302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000070637b4d8b21c764d0d9c08dd1d28b279bb2823500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000151c1d3bbe7200000000000000000000000000000000000000000000000000000000000000012a0a7392d80a1be757cdebf95b665fe0544e091c1b8fdb0ea7470aa9e0f6492082044d80809b8ab0a9bb0496a9099c6018c73ff2acaada0e299dcdae23bc20fb29fad715822cec0db8716a0edbff285acecc08b662b89a0412b629ba866e4c2abb80aab2f91bff0b0000000300000000ea4484018cba808302066e943df9e9ba39dc3ee6b6fb8556bccdfa87ee64fba680841249c58b82044d80807943273fee9a25c1549219629bd8844eaa6e9e327a7935d203e2f9d6d238a4837e3fbe0944dd0d07ccf2cdeec4e465bd25322949b697bb0efcaafe2c3c5316611bff0b0000000300000000f9010d821ee88401bbcd488307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140360000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982d87a827433e000000000000000000000000000000000000000000000000000000000000000082044d8080d03b301c79a78d1584e3c7eedaf2272a56f24adef479a8c3e045ce46ff16065d7cd5f8bd5528c8a634951d7e3f11c7c97c9d57cae43640818c7135a1a9284ae01cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ea81898402625a00827b5a9400cd1fb066573ad4215aa699e580af9ef4b3a4168084372500ab82044d80801c0e2075230c09e5f074cac36419c3eda1ded5447ad6470d15caf956eeb3f4d5305e11deed13c3947e01beafb0054fa692ee7b708525bd6438bb1283d9f5633f1bff0b00000004000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ee98401a22ac88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140540000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000983735fbc7f641000000000000000000000000000000000000000000000000000000000000000082044d8080590354f8346245d29a2193f306d6473bf0786bdbafbe91df5a84e8642e3570eb3a6da19a7f13a0e834f5884633dadd4d2e432c2c2bfa3324429be9b7ad04290a1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f86b2e840bf5ba4783015de6944dd70bec5984171a75003a6186219cb99ed79cbe80b844771602f70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000482044d808026293839e8593c6b85003aa27b0a4797069ba043803164bd5209b940d39c2bad7bfc0e2c652552a2606eb57f0dc6f7426805edf84ca2ad6fdbfcd9136ff73bca1cff0b00000003000000000b0000000300000000f86c81b884016b28c083010ed6941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b3000000000000000000000000c8a21fcd5a100c3ecc037c97e2f9c53a8d3a02a10000000000000000000000000000000000000000000000000000000000b3671882044d8080ee2364f3d622e422ec2a90a92137fc5d8a51510bfe0605b11ce2474538a7e5d90afc93bd57016193b3c0a416f3a471bbe24bf08eb385bfb9635425e3f84acc7c1bff0b0000000300000000f9010d821eea84017d51308307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140720000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982a3158e2273e000000000000000000000000000000000000000000000000000000000000000082044d80801bb6d2b5470d77ae8986b29496a0565ee37380f17c51fb34da2d9b4aa9b78ab66d72906a4d0f10e0663f4560942d246c895187c4287b43f9d26bfe128b4abb671cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000e94684015d6d2082a01994b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d808085262abae3391ea17e9032bf33e93d793771d42e14defe690719b53803d7085915ff9bd53ce52fb50282712974ff93950c0161b19a492c040c0606e855207f821cff0b00000003000000000b0000000300000000ee8308be6884040f1fa082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080bcbd6ba1c6cca5649a36a7e14c75a5561bac294fc4ea5cbd50e4c83a04b0039c5ab9e91f0f06c0c028f7a6aa146fa901ac23abdf57072083cb2113e5d53e7da81caa0b0000000300000000f9010c81c18402625a008303233f942a3dd3eb832af982ec71669e178424b10dca2ede80b8e4cd58657900000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e602c1920443f01cb100a57a7f894df8eb42f6600000000000000000000000000000000000000000000000098a7d9b8314c0000000000000000000000000000a2036f0538221a77a3937f1379699f44945018d0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000082044d808085cf2d27dfbfd35f2e62054951ea381ca212755366c3b51fff597c6ffdff170e4bfec23bd116bc191684d77da0f186d5202128c4a7cc68187ae9746b5c8f412c1cff0b0000000300000000ee8308be6984015a5fe082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d808020bdf52ff36fb31db16395c7eaa2bbff670a9dd200ea46062836d24f1b20e6e03cf5779a839573d635f939669f6d36f345a0bd444217df07950ad766e4c4efd41caa0b00000003000000000b0000000300000000f9010d821eeb84016bb1788307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140900000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982cb210d1b77d000000000000000000000000000000000000000000000000000000000000000082044d80805c32534154b68c162e322a19dcd13dd8fd1fd1183396f6b4c1e6c43609149f4566b01aed437e1992606c98b53406eae40a6b630dea0e7cda6a124ac4eef068121bfff9050d81b984015a5fe0830a10a994b83b554730d29ce4cb55bb42206c3e2c03e4a40a80b904e454e3f31b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000b2829a0000000000000000000000000000000000000000000000000011b27c73a01b7d0000000000000000000000000000000000000000000000000011c9409d5f4dcd00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000440000000000000000000000000b1db6eec8d8c629f9f7e28bf2680e3e264e929880000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000066e19297afc94fb216be4efcb60b0969a606731d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f6ad3ccf71abb3e12becf6b3d2a74c963859adcd000000000000000000000000b83b554730d29ce4cb55bb42206c3e2c03e4a40a0000000000000000000000000000000000000000000000000000000000000148c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000b83b554730d29ce4cb55bb42206c3e2c03e4a40a0000000000000000000000000000000000000000000000000000000066ea78b70000000000000000000000000000000000000000000000000000000000b2829a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000281e4a5963abfd975d8c9021ce480b42188849d41d4f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000e1829cfe0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001240000000000000000000000000000000000000000000000000000000000000148000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080c4b8a950b6fb2fd40ef9292e2e21f16ce93551a97b61f65dbc361e1b97c7e4fe353efe7bbaa446443a57106a013415b016a5de387499d032c14384b3b85e5a4d1bff0b0000000300000000f9018f830151d7840179a7b08302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000010000000000000000000000001e09278590cc10fb2bd69235d2f4372fd0e1ac44000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000002bd26ff6604000000000000000000000000000000000000000000000000000000000000000018bce34245eaa4dc895284ff7b987382cd12c8b40d2f55ace41c054e6b667f19082044d8080185025c2a18ff67f6619138b44a5262ba2181151be1526f23b3787bef28350337867a681c56458160a1ee95dec4ad4c9a3c47f1dfb9a39f9b39c67ad3c6f62551cff0b00000003000000000b0000000400000000ec0e84015752a082520894e4edb277e41dc89ab076a1f049f4a3efa700bce88708d8a44d7dc33d8082044d8080e5f393baeec28606eb9e5a973614ff7a3bb17e7df7d56de1c8dc34d71ae0b50d766c5c5d0a02d04a7b4140cef33d896eba5ae9c796e18f047b7b3bb7c0e7af981babe94584015752a082a01994b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d808064019d123c2bc2e40c10f63cef99518b130d358e122504f0028f2f14085f24204f3d2f2de773ce5bb4be2eb02d7978421bdaef8a98f8d2407d3ba53e0ef406061bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821eec8401754e688307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140ae0000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982cb210d1b77d000000000000000000000000000000000000000000000000000000000000000082044d8080f8fc4cd3cfd4f5669ef452ef2ad8c5082f1ce1bf2f0523a52e12a916b9ad8c6c12a08dc48c171e434cc75852b7c8161b0741f0465821e8bda5fc10ffaeebdd841cff0b00000003000000000b00000003000000000b0000000300000000f12a8402625a008307489e94dfb6baa334712cbbeb26b7537f62b81c2a87b1e8871a84698758700d84e9ed3d3082044d8080c9df68b81de01a7ddf57cd4037afa1df1f031e10810bc3c23c88347bba337b20343c98d810b700b8e27ca6243277b28771d484477cbd58366d02b57f3b8aeb731bfff86a0584016387a082b49b941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b3000000000000000000000000ad41c77d99e282267c1492cdefe528d7d50442530000000000000000000000000000000000000000000000000000000001805e6482044d8080bc02d6c00e37eecd89d84e5439fe5186d2b55f91e68bc952ae7803f7252afc0438bf92877c67810409bb9b40ea6d08b272f760015563aa9e052bb46ccd5e245f1bff0b00000003000000000b0000000300000000f8b133840225dda08304ac1194222228060e7efbb1d78bb5d454581910e39222228614edc04e0e0ab88432accbb900000000000000000000000000000000000000000000000000000000000000c6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b48eb57e000000000000000000000000000860db06d8c758c594143686d0ef9cb52543bae0382044d80803c9ebdd5f72aed2cfa0d36b5311cba9fd9a211cf7c9260502b5b3975751b24603678ebaa584a22aefcc118b5f8983f25e0201d6152c9b7d0d9dd6c7249760a211bff0b0000000300000000f86c81ba8401607a6083010ed6941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b3000000000000000000000000b5f43c2206e3cafecd62651f5fce9091a020748800000000000000000000000000000000000000000000000000000000001e848082044d8080f97d0cf4ce6c85eb1609a85f2c71839e2bb12896b7968547c085d447c99f23ac4f3af1959ed38d3fd76c379dbee00fe520c52c8ec791afe021b974d7442e9c261cff0b00000003000000000b0000000300000000f86c81bb84014b1da083010de8944f9a0e7fd2bf6067db6994cf12e4495df938e6e980b844095ea7b3000000000000000000000000b5f43c2206e3cafecd62651f5fce9091a0207488000000000000000000000000000000000000000000000000000739ecb738d98f82044d8080a78e3716c0eb3802c51e41bd1c7ca64c9a638221f07c7c06af9f6c94300b21d37ac0c2374cfce5539fe7d9c180e153b2fa10b1ccae8b727a8d4734dbd4b9287f1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f281bc840175d720830107e5944f9a0e7fd2bf6067db6994cf12e4495df938e6e9870739ecb738d98f84d0e30db082044d80801049e7b2658e6542463bbe11e60a7198f23cc761a536410d5383e00aa6f6cc68440573478137a9ce3486ff41a903bfb738c44469e56c7d1dc667ee4c18750dbd1cff0b0000000300000000f90432018402625a0083053b1f941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b90404d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000a26ed90933f9410c967efc64452681f501477ca7c7bb0efe1a8e16b8063c3ad420b4781cf348b8a0a4851fc3c2ff2553000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000807edc9cca4620e59b6919cf51b699ec6a2bde550000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008496e6a65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d544869314d7366795965714573576178696f4e445734344a504d476d346b6e734e486567705473675668514a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000023c5fc38090643b410c8c06c4c0f3246059e98ee0000000000000000000000000000000000000000000000000000000000000ce40000000000000000000000000000000000000000000000000000000000000041ecd952920c1aff799bb35cc6b1306371afbc5f683cdefb2ee2e31ae21bd48a8270797e9889904a574fa1918a3bb13fa2b28d83ed3ab567b3aceea912b8d04f7f1b0000000000000000000000000000000000000000000000000000000000000082044d8080feec2c92b7ecf47d4dbf93f7041e0082d822ea2bedafe3c6ae11d8426678fb643c29bab890f68593a4f8421415f5a68481621730b71dac5b16a41b7c31ef762c1cfff84a06840174508083074ad694ad41c77d99e282267c1492cdefe528d7d504425380a4a0712d6800000000000000000000000000000000000000000000000000000000016e360082044d808067b5300ca47a28ce2521475a6224c2fe27e4b28da3a2cab92231440d3938603724b3ee0a7fc6bca38ad52b7ca90b534e67e8d3a55ca472e3eba0ac51bd5fbc7e1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9018f830151d884018c1e408302042094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000020420000000000000000000000000000000000000000000000000005d423c655aa0000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000da930060b712589736018c6d891ec57b0399db7800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000763bfbd2200000000000000000000000000000000000000000000000000000000000000000017f0ea037712c6b94793ed1db0d924ab44381912765b13d871e7eb101ac652d3982044d8080ec0c9e107850e44db65fd11d35d41ee8ddadfd9a2e989dbc619a60c52adcd2860092874af3ca89dfdd56ce708439cc6cc538ee10b7fb9023d4125f7abca62f0c1cff0b00000004000000000b00000003000000000b00000003000000000b0000000300000000e92b8401499700829f8094b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d80805b33a4bea9274b6838fc8c299e7e1d463d8a9866847ed34961dfa742b1e09e7960336c7a436d7c78f8c055607e77072b8f1ddcdeea4757c9236b8dbfea38a1181bff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e080b00000000000000000b00000003000000000b00000003000000000b0000000300000000f84a0784016e36008305dc8f9468d9baa40394da2e2c1ca05d30bf33f52823ee7b80a4c5ebeaec000000000000000000000000000000000000000000000000000000000112a88082044d8080b0b78bcb15ccad6e34965bb75310d919de9c98653338a4e621bb68d4e1158bc071db0cd0eee106b2a15820242d3a30d78cab313ce696b38609712e9b97b4f2211cf6f00c840a85ad1c82fd67944f9a0e7fd2bf6067db6994cf12e4495df938e6e987131d14dce4400084d0e30db082044d808061485ee80be12c1cf4118d79c8240621c4d61342aa6e21313bcb338e0c4d3f371f1589ae7a144279671a249bf3d2514d9f39c850081cfe35070f9df8b5bc52b71cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be6a840433bea082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080ae7ad2d365c97fd210a6f105189d8f8845c7607a5304f61a75e3f95b30d0278654bd6071ea8dc8eb258293e33b8662588af561e361eeec88f2c222dab85904a21caaf9014f8301154f8401aaa2c08305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13ec600000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000007322d2c4642c149072472c80000000000000000000000000000000000000000ade71ffb3551101896108d800000082044d80802d5cadf3e11640c1e52b9bceb4f465b5be3247fc0ec042d85c44575e2e71c3ac0542b94b31e12bdecca3fbfa292da254413c91cff46b7a09a6a264a4b58fa1941bff0b0000000300000000f84a0d840b405b068301519d944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d00000000000000000000000000000000000000000000000000131d14dce4400082044d808005ff907a16f3f1614d5f81b3fb224f805038d423f46f7a3201533568f87cf5032028fb37a272fa727550e1dce4af167b67b51157e24de676306f97faecf6ceac1bff0b0000000300000000ef808402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a10986246139ca8000840333f38b82044d8080e1a97f847a204044e3f9e326c84f4d5369fc9bdb762c0aa38122fd4f9e5321d0660038ec1b8f5c2273ccfe875ffc274d7b809cfb2ab742631683613108c7e8501cffee8308be6b84016387a082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d808086767ef08deb78094341e575335c29fa45b39ae1dfa57003b2f1b8e0f29dd57f13cd26cdd53e10c6c35df7eeecf0369bc77a701d0732dbeea119688fa09f7d021caa0b00000003000000000b00000003000000000b0000000300000000f00e840764acfe82c6ee944f9a0e7fd2bf6067db6994cf12e4495df938e6e98712dc81e31cd00084d0e30db082044d8080cbfcaa3444f14e64cb0fa9e0259f2fd1210b5425a8c23842150c77d1c9e8578d79aea37c19621cdc2d60eb47fdef1ddfb63a0dcf97dd37f639b862a80d81aab31cff0b0000000300000000f903f20a8402b900fb8309e24a941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f53ae48ab8aba0990d601236bfbbeb5f26e910d68867594ac5def0ee45b27ce819000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000ec90f6634a907165e5d18a14c1dc51e32b9de8b60000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4f4b582057616c6c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d6252327345444c614761345a33664a4278386d6b65636e59646b6b323451586443556a737253466a62745161000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417ee5aac8e9e510dbe7b12d5757762ea0b0eed32189dce8ceb044ba9bd16daac61415af82e6bdac2e90d2227bfe8336c383ac24ce03af40f159412e86b49e02f71c0000000000000000000000000000000000000000000000000000000000000082044d808054c2fcd741aff604618f9331edd8de2062dc15c3ca37a0ae759881f21547c1dd491a44cf2cc85c20f72575718f39a8026b3989a1f07f4c9de324b8fbf62d03801bff0b00000003000000000b0000000300000000f84a0f840842bf86830127cf944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000012dc81e31cd00082044d80803d7108665030a686303828a8590515a325439a383cd915a8510b1ead08fdbe64726b280ca7a73a844d6ff801f53ad44480c39059472080c96b4207f2b41eeb141bff0b00000003000000000b00000004000000000b00000003000000000b0000000300000000f010840cd2e41082ec87944f9a0e7fd2bf6067db6994cf12e4495df938e6e987141476cc45400084d0e30db082044d8080bb1a5745409e78f126e0f3d301d1bbb8e829404909bda7bdba47b38a8e5cc5022975deb7d293bdb69210a4a50cb606efce11040df82ec34008b179a51447d3a41bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f84a11840b840af383014099944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d00000000000000000000000000000000000000000000000000141476cc45400082044d80800f0a3bb81e483365b2f6196c60d0d97882e9d2816fbfb78689cd3850a33f7d9e0f6732a7d860fea63d323bdd924b39043a592e79fc5cb42593cf5a3722b854de1cff0b00000003000000000b0000000300000000f86a0884017d784082b49b94a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03580b844095ea7b300000000000000000000000046a15b0b27311cedf172ab29e4f4766fbe7f4364000000000000000000000000000000000000000000000000000000000112a88082044d8080325b6bc0360425e4a14b8d91cd8855d8649bf946352c4ad927a31e56d22c004731500e69af69ce8b80b06946735af113f7ea67749f57af617d5deae89eb1f3d61bff0b0000000300000000f9018f830151d98401a1f0308302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000010000000000000000000000007f35a6e27c542fe3bb6bdda638abc8bc65c3c0960000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000015c7ff2eadb60000000000000000000000000000000000000000000000000000000000000001a93b38316983d650e0fdfc2642ca16b840420a6bd6962cfbf417bf5a790d681b82044d8080738091a8459376230f139c30ca51cebeb6deeab09492d46a6009f33a090953b219d8f52e737caa178042069504eb99b0554f51963c9943204724b283f347c10c1cff0b0000000300000000f00484018519608287f19465a4b8a0927c7fd899aed24356bf83810f7b9a3f871c6bf52634000084db6b524682044d808050f3c9ba5b616396e065717c29365860afd49c5e9aa275eff0f37446d3eb5e3611e8626c0be00efd62ed5d31b51cc334dd8793c6bc8c62808616faebc97cb7b71bff0b0000000300000000f112840cd321b68301312c944f9a0e7fd2bf6067db6994cf12e4495df938e6e9871382f1e51b400084d0e30db082044d80809585c9b5b7689c9abd1823e19e0f8d592f57f419f94cca0ae20ed021b68aaeb556273263752bc1e78c8508d5af41a05d2b886e07b78330d63df42177f02110621cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f84a13840891001683010317944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d000000000000000000000000000000000000000000000000001382f1e51b400082044d80802eeb9e8ba20f90a05d7584a87870c63edc5a0952fd52d1299d54a19098af421408f9882e7e62d42d0afdff81a06c4614ada1367758b701289f3aac12f5ec13e71bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f114840be4d1b6830126d3944f9a0e7fd2bf6067db6994cf12e4495df938e6e987145b3905a2500084d0e30db082044d8080ddfa4814082f9ca8947b3a9263cb97c0957bed97d3032e8fa38df43eaf7669743b15fc3cce18b60300063396898de729594c2af72e147d8dafbf7dbb3e9e745d1cff0b00000003000000000b00000003000000000b0000000300000000f84a158409de70b683013343944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d00000000000000000000000000000000000000000000000000145b3905a2500082044d80800c288f5c663955ad276d8e8bed2233997f07bcaeade6992c98b05f99151c6c2a27d0a04fb3e07473e11b88176db0f5baa46dec2c48eb704e4d6bcd78467b25fe1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f1168409d2a29e830141d0944f9a0e7fd2bf6067db6994cf12e4495df938e6e98713a7531ee5c00084d0e30db082044d8080f22f024c3df0f0582d53d27586bc0b534636d4bbbdecb6cba76d31d9ec26c24a2fc8e613aa6d783ef7541ca0463908b696f7a3cac12710986820bd17613a05ee1cff0b00000004000000000b00000003000000000b000000030000ae450b0000000300000000f84a17840843742f83014a3d944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000013a7531ee5c00082044d808093acdfe33b2bf0475202506781b23511c98ddf648158adc2480626821c8462f765d5b53638bf280519dca4c3c6d1b8e62068dfab0aa3ee45702e69dd44d70e881bff0b00000003000000000b0000000300000000f9010d8263618401810e38831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d00000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e141af000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000000000000000042a6800000000000000000000000000000000000000000000000000000000000000000082044d8080787ff95ee62900d9971c92e31e4a72c46ccb514a1f429be155b8bf471369fc0610e0132cde21bd011d3efc70d70cdf470af7ad9b5aaf86170803e31f29a94bd21bfff9012e8263628401810e38831e8480941b81d678ffb9c0263b24a97847620c99d213eb1480b90104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f400000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e141af000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000000000000000042b949a000000000000000000000000000000000000000000000000000000000000000082044d8080812562f17bf5daeaa60ed73ad30eac5996a784b57451663ad68d2a3a1a4ceffd0e98775ae6ade38d54ffb4a21778eb2a047d280b8408451096b13019394cc5eb1cfff9010d8263638401747b78831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e13bf40000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000980faec989d805000000000000000000000000000000000000000000000000000000000000000082044d8080d6ae2347276fa3cfd245d9379684454bb7f0bcc3cca6d642d827809ff5d836922ec796bffe17a15a93e05d6d62d524588b2e083a3baff8736a9ba633ae1f7cf51bfff9010d8263648401875798831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e13c180000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009819595140ba78000000000000000000000000000000000000000000000000000000000000000082044d808008a9fd6bf5a08b08d9611be3a3243fdc5809a0e51aec5629bf321cf09436fc67600bd37403aa4f6155045256cd67620aa2bc7e7118c6b2dcd5f72db9c30a433e1bfff9010d8263658401600d00831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e13c3c0000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000981383eb6618c6000000000000000000000000000000000000000000000000000000000000000082044d80809be365822aadb974a913e75d18dd5280b322409c861bfb10d545c17c72979d847b52b9ca8a4f49f5dba9016e98d2e55536a8ce32fdf06b6a13320aab4a6869821bfff9012e82636684016331b0831e8480941b81d678ffb9c0263b24a97847620c99d213eb1480b90104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f400000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e13d9d000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000000000000000042ebcea000000000000000000000000000000000000000000000000000000000000000082044d8080cbbc34a2b94e3d2e56260b5e0034028c2f6bc1c7306fe9a9f1956b670c027dfd70a38bd41a95f94774d84e470ea2a25a42c499e1e4b971dc91a426139f61a09e1bff0b0000000300000000ee8308be6c840453c9c082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80809fa5bd47d6b5cc939b9b27f080a9ee8477b2baf9fc9ac7b51c023ba5eab004ed22ec4afd14c073c5eb9f9a2802eac4f24ac04c34b7ef33de4ecba895e8749e141baaf901ce826cd284017143408306dcda948ff6508bdd71b535df073920c423e9eaee0b97af80b901a4b61d27f60000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f40000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e14087000000000000000000000000000000000000000000000000016348a452209ad0000000000000000000000000000000000000000000000000000000000de701c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080aaa0ea5c36cb0e4fd0bce18e23632f9c8ea2b06b5351c2c69a2adfec23fff4a43f84b301c33c51f2744abdfab346fc91d47c8dbe1b2c91849163ea7d0dc871531cff0b0000000300000000f9010d826b8984017143408308e02294f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000098f0f120de21a90f220b0027a9c70029df9bbde40000000000000000000000000000000000000000000000000000000066e14088000000000000000000000000000000000000000000000000016348961fbb5800000000000000000000000000000000000000000000000000000000000de6fe26000000000000000000000000000000000000000000000000000000000000000082044d80809c6fef4b141e8e4a07c9cbb2511753153fcabf3a831eec7216bb4af90abb7b7d6fb5002e74953de4a87a5aada51b25c9b1b05a961546421ecdda070420f081621cff0b0000000300000000f9010e83018d5f84017143408308d82094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000023abcb82f468e3422a7212d4ad47cc2dd39162a30000000000000000000000000000000000000000000000000000000066e1408a000000000000000000000000000000000000000000000000016349e13e663f90000000000000000000000000000000000000000000000000000000000de70b11000000000000000000000000000000000000000000000000000000000000000082044d8080637884a6f0f95a88b0fee2f9bb436161c0032965d91166bea89124fe6513d31c5527771f11b4ed3cb90e8b757c9f4c9fd74c6bcdc82af2e68521ffd5e0875cea1cffee8308be6d840171434082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080c501252774527c8bd4e9fd808914868352ec182c325575824ae9e83523ba33ce46a6b82b2ebb32e4194110443e5c67ad08df9d8db9bc6fa8aea72192c1192e221baa0b0000000300000000f018840a88762382e3fa944f9a0e7fd2bf6067db6994cf12e4495df938e6e98714191a3bf5780084d0e30db082044d808076d4330b8513208b9814415391c70d0cfec79655b397fd2632d5e82c57962cb87956a8c4b93809d7904baadce5b66e8e720ea8f6ffc5c9765226b0952f7dafd01bfff901ae826749840169a22083091f30948ff6508bdd71b535df073920c423e9eaee0b97af80b90184b61d27f6000000000000000000000000f6ad3ccf71abb3e12becf6b3d2a74c963859adcd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e14090000000000000000000000000000000000000000000000000016348d202444f70000000000000000000000000000000000000000000000000000000000de6ff7800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d80807838e9e5f7ffa45112082937cf4c13ac9a4b34d2d89c0f88a6d1e5b8fbbabe68450963e324bff7087380a48b63fd683577784f3eb7c926b9a9ec08acc1cce13f1bff0b00000003000000000b00000003000000000b0000000300000000f903f215840169a220830343e5941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5552350c0f4036746139ed62c05a8199a5364297d41a85bbdf9d08e25c0b01711000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000c2f02570540a1e49f72b83365db2aed22ee6cbad0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075068616e746f6d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d526e4b355959546b517155556361316f505241637157456f4878737847576658517670364e4c43435647705600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041303e333a0a05a77d8b21e3acd4fee5daaf7544b8b4d685934b1343ccc154afe811166033a30ae22ed0dfb9c4ccdec95eed70ac788e04d58b361f0a5f38260a011b0000000000000000000000000000000000000000000000000000000000000082044d808057a8cf819dd9e7deefa39df29b556f3298ecf2343570849e33ca211713427ed3666812d6fc31f2b1269f4d272d30c4c4d6fe42bcfd0ace89e156199a62e72e771cff0b00000003000000000b0000000300000000f90bae82244c8401650e408303e4a494d46f8e428a06789b5884df54e029e738277388d180b90b84cdd1b25d00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000008e000000000000000000000000000000000000000000000000000000000000000740a14de1e598b81620773454588b85d6b5d4eec32573e12145ec8cd4881eba87279f5f243eb89ea9383e677c61a144f9a0e7fd2bf6067db6994cf12e4495df938e6e922072671ce2724c2d62888ce0330cd083a20623f6e1a2edccbb959c2ee656b3497c5155af15a0ceaa8b0cd940c4fe9792d12000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000004138c5d370f4c6b199ed42c2e346cef963a8fdf9512270c9518698cac23166fccd4145a7fe09b7b53828ce325cacb2368e5917ccf999f0454697e0edb2333ef67c1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414815d2c0438852df0d6e1f6e079682e1cdc5f56410cf9260224db24c1c2ff2ea25ea8b65b7966110962eebc47a78f4d77a8b1dd0a3855d73ca97da7e89cccfa31b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410f45357656c66529a2f7c6fca35dfc1f13f7db5cd546cc958503ecba7d9d55ca51b1682a289b9ae1daaf82ec950e0005e71c01b0bff347c68ca80999802cf0fe1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004166d8d1d855e266c5688f8a45503d9448366fea91b50d4c8ba12ebda394ef76a84d003f8bf256cafc0791d521dc3fd681710fc1e0dc49d373fe1a3be3ef9645ea1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f2d31bb75fc66cbc2f75af278c95a028b6b764f0d056d65f3f50bd9d8ad857c53c229fd9cc844192595b6e8c892edd4566e32f17add3ec25102f06edeac4de4a1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f1d037092dba92c2b718f8cc6d1d0274087cc7e033e6aeffe8ef39246188a2e34ef3761e29604612724fc57bd129d9ca0b1ca246c3f7c18113cc3b8acfdfd4b91b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041aaecb7b28127e84f580d3944c59e7de10d46f2cb121f0b56364ce9d843c1fd783e28f41e1e33378f071dacd4788a44db8314174eca4757a19470cce0ef98fd5a1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000419a9ea0bf5e36016e3380ee472c85ac21cd072153f7265ac450f9c2853782cf715a8105e3826f08d47139e61146f7768af4b55b3ab485772ed6332c66150d80961b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000241a100333eefa2efc389ec836a6ff619fc1c644000000000000000000000000273035e10f106499eface385dba07135e7cc8e5400000000000000000000000055f4a1bfc655cf55ed325f2338a1dee84f754df200000000000000000000000057c96a00f9ff7b25cb5cf964f1a191be9321b8c8000000000000000000000000870cf8dd5d9c8eb1403dfd6e6a4753f4d617a53800000000000000000000000095016e36adb4e0151735ced3992a7fa54e16bd08000000000000000000000000954adc74481634b4d278c459853b4e6cc17ae8d200000000000000000000000098e9d288743839e96a8005a6b51c770bbf7788c00000000000000000000000009a66644084108a1bc23a9ccd50d6d63e53098db60000000000000000000000009a8cfacf513fb3d5e39f5952c8608e985b3dc6ef0000000000000000000000009ac5279013edfec74c5c2976fc831ad0527402e00000000000000000000000009cd5006e1bff785dad5869efd81a2c42545c9d9b000000000000000000000000a73b339c3fae27bedf7cb72d9d000b08fc899609000000000000000000000000bfa2f68bf9ad60dc3cfb1cef04730eb7fa251424000000000000000000000000c74acab8c0a340f585d008cb521d64d2554171a8000000000000000000000000cf12dd34d7597d06ff98f85d2b9483d9d5f7d952000000000000000000000000d10c833f4305e1053a64bc738c550381f48104ca000000000000000000000000f4151eebfa1b9c87dd92c8243a18b1baef8c1813000000000000000000000000f5ad7f3782e8a67bffa297684e27cf9fcc781be1000000000000000000000000f6e93eb288658de5e2e982f99d2b378b22959d1500000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000002f447e9b1e8adca8df0000000000000000000000000000000000000000000000440370c65f337c6835c0000000000000000000000000000000000000000000002c9067d217c36159ff0000000000000000000000000000000000000000000000530db71af8da0bfebd000000000000000000000000000000000000000000000051e62edada099ff54000000000000000000000000000000000000000000000001999bb4b4f342a0dbd00000000000000000000000000000000000000000000002c08f0c7601de23e6a0000000000000000000000000000000000000000000000ec1deea13ae957d41000000000000000000000000000000000000000000000011be0842d86f7e8e736e6c50000000000000000000000000000000000000000003e4b325079a76d1d368000000000000000000000000000000000000000000000394b6fd351bc3090d200000000000000000000000000000000000000000000010792503324e9fba39d000000000000000000000000000000000000000000000059fd816a8470289cecac00000000000000000000000000000000000000000000d2faebf995c3095183ab000000000000000000000000000000000000000000009158ca2eac127d67ba00000000000000000000000000000000000000000000003bda588740b8d2e76f00000000000000000000000000000000000000000000008ac2e56a51bc65072900000000000000000000000000000000000000000000004383436ecf64559db0000000000000000000000000000000000000000000000031c05fae36f04651b4000000000000000000000000000000000000000000000010250608f677cbded0000082044d80805d36d9762307593a13e91e19fb81e6c0a17830d4834e18d66c52d6411dc7b22009da393426cdda33d3b5d7385fae3ee7b625f0febf8d72c3785dc733aada5e7f1bff0b00000003000000000b0000000300000000f84a19840af6f75283015497944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000014191a3bf5780082044d8080021f1ce93bb9218374d2ea45dbd2bc8d97f6fa5aad023474b71e12f2ccc583e03ab6cf9edaf16b142da8d07b44b1811aff429a4bdf6b0af997d0707f98f295061cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f01a840b695c7082e6e8944f9a0e7fd2bf6067db6994cf12e4495df938e6e987145b679690200084d0e30db082044d80804b2eb02436c30f483e54e8af28f2f1c59db7f5d56a24c83d0b3d2c4f47065ecf68f4895fb2a2e27e0af1b416d34da70f50affc5291e02a2a13f29ae09f92ab1a1cff0b00000003000000000b00000003000000000b0000000400000000f903f3818684015d6d20830399fa941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5b38fb2ebaa1e7167ed74018ce74a588d28794f3dbe74ab6185c751c3f31a2d81000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000007e4c51597dba086fd1a07751db43836a3acd20bf0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4269746765742057616c6c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d545050343333525078526b70515170476b5831365176573558694c6e334d46643833577a37524334764a647000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041aaddceedfb48b401c31abb8555f78cf18080778613ebbcace5332467725ef3096d0aff50b740abfb1372f7511caa6e46683774e7697c2c3fb360621b0aed16761b0000000000000000000000000000000000000000000000000000000000000082044d80804858cae0f925fe74ffa06631cc0e9bb9955a839c9eaa30bdd8b11a0b7b8de9371e19d31182948a1e2d730e3f9cab2f67d1a5af4e2a62f386d8951d34f94367f41bff0b00000003000000000b00000003000000000b0000000300000000f84a1b84086ce4f483012ee7944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d00000000000000000000000000000000000000000000000000145b679690200082044d80803ce9647fac84541c0f88f2635be838c489ac64d29edc0759905e1677a53a996c4ba90cf64b959dfe09642aa7a67fdaf495c1cd9cbee52efca91f26932b9506401bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f11c8409ef8bf783015963944f9a0e7fd2bf6067db6994cf12e4495df938e6e98713c0ca60f3800084d0e30db082044d80803e3309c130e2bbd0bc32bd9282681387467a68ef88003d71ef2fcba5fecce4c41cf843eb96e923daf4dddad4176d421115cf36e97ca5b794b7bb1330900ff1681bff0b0000000300000000f904351a8401d1168083040eb680870130ae43fff000b9041a60806040526040516103ba3803806103ba83398101604081905261002291610133565b813410156100765760405162461bcd60e51b815260206004820152601b60248201527f496e73756666696369656e74206465706c6f796d656e74206665650000000000604482015260640160405180910390fd5b600080546001600160a01b0319163317815560028290556040516001600160a01b0385169134919081818185875af1925050503d80600081146100d5576040519150601f19603f3d011682016040523d82523d6000602084013e6100da565b606091505b50506040805134815260208101849052428183015290513092506001600160a01b0386169133917f21e7cd7f82cd180cc2efc8f6d34988374ee142862fb2f9a8f60192e9b3cba1f99181900360600190a4505050610174565b600080600060608486031215610147578283fd5b83516001600160a01b038116811461015d578384fd5b602085015160409095015190969495509392505050565b610237806101836000396000f3fe60806040526004361061003f5760003560e01c8063b0910a5814610044578063c0129d431461006d578063efdee94f14610077578063f7b98453146100af575b600080fd5b34801561005057600080fd5b5061005a60025481565b6040519081526020015b60405180910390f35b6100756100dc565b005b34801561008357600080fd5b50600054610097906001600160a01b031681565b6040516001600160a01b039091168152602001610064565b3480156100bb57600080fd5b5061005a6100ca3660046101d3565b60016020526000908152604090205481565b6002543410156101285760405162461bcd60e51b8152602060048201526013602482015272496e73756666696369656e7420676d2066656560681b604482015260640160405180910390fd5b33600090815260016020526040808220429055815490516001600160a01b039091169134919081818185875af1925050503d8060008114610185576040519150601f19603f3d011682016040523d82523d6000602084013e61018a565b606091505b505060005460405142815230925033916001600160a01b0316907ffa30e9e1a69333c24ba36197dd28ca3e0c5f35e57ddd18903b4fd4ea7d4912339060200160405180910390a4565b6000602082840312156101e4578081fd5b81356001600160a01b03811681146101fa578182fd5b939250505056fea26469706673582212205084495e17d7e67c22e09899255168827489804e496d7d6f983ad2752ca58a2764736f6c63430008040033000000000000000000000000c451b0191351ce308fdfd779d73814c910fc5ecb000000000000000000000000000000000000000000000000000130ae43fff00000000000000000000000000000000000000000000000000000001e77d399980082044d8080e753eab3daad33e5406b6e9ccf7fdd0268c3eea1b2799d1052a1a16fbc83a6e317be62f32646d8c452718d299d38228c41dd5a1dc67961dbcd542bb551b391f41bff0b00000003000000000b0000000300000000f903f23584016cb4338303977b941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5a5b86aa0e7b0c70701b8db6d1e5e8a7ee65e36fb3fac08e68c65eaee771351f0000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000006158a9ffe6404eef6a2bdf331feba141f17235870000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007696d546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5579436d583974784a4545776f7756365646633566386e76576936717a414b46424d64767a6835535a334a340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004133474e9c238d06b566181d591c630a71d9e10eeb72ca063a40dadadcd0c0f6fd4d6b0326c4943466f3b886510d4a14429e394fc1a8abc8a0e2794ef07b91ca561c0000000000000000000000000000000000000000000000000000000000000082044d808030e5d2dbbab99819c571447b15a332129be20c6d875c480cad06e720a32b11d84bf57d2fc8d9eea3820202f47268b26a94e700afdb728edb396bd7312b8d8d621bff0b0000000300000000f90139058402625a00830284788080b9012560806040526000805461ffff1916905534801561001b57600080fd5b5060fb8061002a6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80630c55699c146037578063b49004e914605b575b600080fd5b60005460449061ffff1681565b60405161ffff909116815260200160405180910390f35b60616063565b005b60008054600191908190607a90849061ffff166096565b92506101000a81548161ffff021916908361ffff160217905550565b61ffff81811683821601908082111560be57634e487b7160e01b600052601160045260246000fd5b509291505056fea2646970667358221220666c87ec501268817295a4ca1fc6e3859faf241f38dd688f145135970920009264736f6c6343000812003382044d808044a18dbe91f5ed18370200a54bfa8738d390e49046e22faf1b6320fa25a8b23450dd1c97b91e2cac7fe4618bebc997644786d82b3432cb304df14213b6a782a11bfff9010c81b88404c4b40083032174942a3dd3eb832af982ec71669e178424b10dca2ede80b8e4cd586579000000000000000000000000000000000000000000000000000000000000000000000000000000000000000092de600f86669d5bc23fd33c4b93689d49c55aae000000000000000000000000000000000000000000000000a688906bd8b00000000000000000000000000000a2036f0538221a77a3937f1379699f44945018d0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000082044d8080689c42d280e7d7a501e3b8a03727d170815bb230e407c1adbe02c056943267013fe64d7ddd8e1dd996ec63c86a17964c9982076c6f51dbe46034e05b8cf2068f1bfff8491d840bdac5d982e3d2944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000013c0ca60f3800082044d8080f0658ba891342b29cb36b03d7c7f4ef697ab9453400c9bbc07e6689f2442a27d283e986cbe0569d209a00ce4a0bb790909b20727d04e17df540cad993cffaf681cff0b0000000300000000f9018c09840178e4608308474c9446a15b0b27311cedf172ab29e4f4766fbe7f436480b90164883164560000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000000000000000000000000000000000000000000064fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcc905fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcd6740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000112a8800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000112a880000000000000000000000000cf65cc37241d426970b91720e8255140ade00f050000000000000000000000000000000000000000000000000000000066e140ce82044d8080cce7a5d5d6aadae5e27baa53001df17380db45b57e5be0ace2a6cf6b062e722e32fadbd8ab50149b54214c9542ae78c7defd5928ab2f23fde10078dfb00c3e9f1cfff9014f830115508401b59f408305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13fb500000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e1000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000072f21faa37999cc861c82500000000000000000000000000000000000000000add83f3e7c6394511984a9080000082044d8080262b264f0820837db4a0fffcfe5309152b391bf1a80d105d564051c6bb5c5c644dd1e64849aadc9a6650a6d15dd17f1a0dfea656b7f190de92b7c3867d4c46b11cff0b0000000300000000f903f2038402625a008304e632941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5f1f3689562d1c0dab2e8a2cc0cb879b692ec71495e8cda15ae8d2ce622c7c6ea000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000000f9bccf01d2a27b0b6f7060c238a3c499814c7290000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c52616262792057616c6c657400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d597346367139725173466b42344d51453766334a35333843594748316f61696539766b5643616b573875697100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041547595cdab235f61e0e57fce4721337a52a434ab432daa2749642b01f769f773340113e8ed17a36f42e4fb82c5e8a9947fd69f29b12d76902d8b6091d7589e831c0000000000000000000000000000000000000000000000000000000000000082044d80801dafcb03a4ae839365fa5edd4e370763a965b1956859fbc950e985137707683f5cb0463918a45f865a13728c1d2785953a8820ae39ed5c5cce11c3d9f798155e1cff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000357f0b00000003000000000b00000003000000000b0000000300000000f01e8409868dda82c553944f9a0e7fd2bf6067db6994cf12e4495df938e6e98714138df7a0300084d0e30db082044d8080c3e8e282b8d2318bb3eacfb610c28212435b08a9a9a7957ccf3b7db78ccf6362259552814b9d6fadf088245eedf9894d2715ea943553730ee3a0e37de0d2bd8e1bff0b00000003000000000b0000000300000000e9068402625a0082ff7294e38fac030c30a806f1a18348cb35b1f9796b89bd8084b49004e982044d8080d4af995d1407374e10081bc81f21a015911814ec43220eb6a75ac5d347da563104c194b2dad08167c9b8018db7cb67cbd4730f7ae1ef58f508e1df803ca7738b1bff0b00000003000000000b00000003000000000b0000000300000000f8491f8407b3caf482ec15944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000014138df7a0300082044d80807a2a304783534f2b6627a011dcd5655a14f601f08f8ed1a5ceb48cfea15d246a1aa2513734725e55a1aebf47662192fb6b51f3ee33f5885df867aa9251519b951bff0b00000003000000000b0000000300000000ea1684015a5fe0830275e094c790f82bf6f8709aa4a56dc11afad7af7c2a986780843d18b91282044d80808d203c5fc7ac06efeaa4989ea373b55b32440f50b4969ed35eed578101407e2e485fbf235e553ff03b12aaa183b8ba39eefd649cba4a64d01ff298b6fbcc67331bff0b00000003000000000b0000000300000000f020840c0ac30582c5cc944f9a0e7fd2bf6067db6994cf12e4495df938e6e9871301cbf18c600084d0e30db082044d8080b2da0efc52a37be0b0abb72e2c878e2d894135e5903aa4b7510215dfc06644434894408779c15dc2e31b3c26bc9eedd420ba0b8cc62c91a40a79c93bc211b4781bffe9078402625a00829b4094e38fac030c30a806f1a18348cb35b1f9796b89bd8084b49004e982044d8080a96eb803ae5fa5e9bd6330e833372d275f55c50f2abd9a783c76011c171bb9a854f7ba60b09c1d684ed56f94254f87f6a3f064e1a1d542dbd09b67a36ea583611bff0b00000003000000000b00000003000000000b00000004000000000b0000000300000000f86c820582840175d720829d91946e6b8f03c407cfce46ba9847231fec40dce9308380b84440c10f1900000000000000000000000092db6815e62f6a8ef196306502c0283297f73b75000000000000000000000000000000000000000000000001f399b1438a10000082044d808097103c7391adfca69c78cbba1820b0bd440608827f76f970ebabee9770d106a325407087010a8ecceaf780e93d7db2140e934a94ff35ec90a2eca49a466f852e1bff0b0000000300000000f84921840aa60ab382e3b8944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d000000000000000000000000000000000000000000000000001301cbf18c600082044d8080a93e5a2fcd9fc6e8431efecde6b5119e2ed7bdfbce535819481e7c4a8afe7afa48bb9219bd7bf591c3b4c1677320ef5961415c7192e2e564945cb87cdceb51661cff0b00000003000000000b0000000300000000ee8308be6e840175d72082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d808024468cb79bb0bdcd83559601af894fea6b58b2c042c87a274da30b03304085ad289f7d82b0e6df87cfdf16b69a1671915fb2fb2779831748932db2ef39172c301caa0b00000003000000000b0000000300000000f86c82058384017d7840829d91946e6b8f03c407cfce46ba9847231fec40dce9308380b84440c10f1900000000000000000000000092db6815e62f6a8ef196306502c0283297f73b75000000000000000000000000000000000000000000000003afb087b87690000082044d8080079b0b01fc85f171207c07f0864e86783059697bffc98962dc0573ae40c413786a55a9c8e8cb9e58d24936de95f05fa3f28cac23efb6de2f65f455652c45cd631bfff909ae82129684017d7840830993579489e830972c7b453d4715ba1d3894aa096f219f0280b9098457ffc8610000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000006ac983efafa917bf04342e3684a863c9212e1fe9642eba19e81b0eab36c223645f2b84aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000000000000000000000000000012659eafec31743a8a1b09826b378842d4b42910da64ae7c0be9774b3490711b80e76dc2d6e18b40e24de42f1bb55971bd666fdeb1a4a463c0a4154a072796517000000000000000000000000000000000000000000000000000000000000094000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000004e70004b01987b4fd7dacab348f0b39e9ae261100000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000bf4f119c5bbfe0000000000000000000000000000000000000000000000000000000000000066e13ff10000000000000000000000000000000000000000000000000000000000000002afa96d133fb7ca1e775405c5ec53a83986cc81317bfa06200d34061675b76adced276e8201ff7defde2bac4a9e387413b3da71f8d1de09ebac9a5490ed0bc189000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004e70004b01987b4fd7dacab348f0b39e9ae26110000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000007e6125df4bd46800000000000000000000000000000000000000000000000000000000000066e13ff1000000000000000000000000000000000000000000000000000000000000000283ca785cb646b1762da8d7c98b01f56e69f4ffe07463f3ece57a61331ca1817bed276e8201ff7defde2bac4a9e387413b3da71f8d1de09ebac9a5490ed0bc189000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000004e70004b01987b4fd7dacab348f0b39e9ae2611000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000ef4ebe4c98404800000000000000000000000000000000000000000000000000000000066e13ff100000000000000000000000000000000000000000000000000000000000000026957de8f52722139f85b1fc83e50f75e52000cfd5e2f6a1814eaac236579b00bfce0e08ce4fc8588953f31682cb0cbddf9b74ab8f00f2e8ff6837ebeaa953f31000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000040000000000000000000000004e70004b01987b4fd7dacab348f0b39e9ae26110000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000f3cd9dd0f7b2b000000000000000000000000000000000000000000000000000000000066e13ff1000000000000000000000000000000000000000000000000000000000000000228f70f83d2d2c2012ccc8e6d043c4421144c676e19ad77b3f5d06a55532ea2d9fce0e08ce4fc8588953f31682cb0cbddf9b74ab8f00f2e8ff6837ebeaa953f310000000000000000000000000000000000000000000000000000000000000002ff0100000000000000000000000000000000000000000000000000000000000082044d8080a928627bca2a83841e6a08cac57dc3f01cce3d139d5371895f14a66f284721a3598e5b04b9282a7b1b5abd1c4b80e125256206fa48c41c6203b40defbcfbd8e01cff0b00000004000000000b0000000300000000f122840a1b74f88301535c944f9a0e7fd2bf6067db6994cf12e4495df938e6e98712a07b108eb00084d0e30db082044d8080b672c58ef0e2fb2431dfc2e188bdaccc549afd9453f907295a9b78242cc392e20797400e2cadd16e89f6490992082c84dd9200eaf4135c3af743c8eed63212d01bffea81cd8402625a0082ef4094b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080cf5c96744631e4660952c51533ff1331d8f92e6f5dfe5fc50e8ae59af5d654ba0992777ecad55c9fa7751777c9674d850059586adf1e25263c674a6affd8fbc51bff0b00000003000000000b00000003000000000b0000000300000000f86c82058484017d7840829d91946e6b8f03c407cfce46ba9847231fec40dce9308380b84440c10f1900000000000000000000000092db6815e62f6a8ef196306502c0283297f73b75000000000000000000000000000000000000000000000001ae361fc1451c000082044d80806d429321175f5602fd6772119c4ca54dc2ebf6837a3d2c7da3cf16e92cbee35f13d4a4f4d2a454164430f73b7c646d1a9ba2589463c8c4a01fbfb57190756a461bff0b00000003000000000b0000000300000000f86c820585840186a000829d91946e6b8f03c407cfce46ba9847231fec40dce9308380b84440c10f1900000000000000000000000092db6815e62f6a8ef196306502c0283297f73b75000000000000000000000000000000000000000000000006d499ec6c6338000082044d8080e7af27a209e255c5d8dac95f112ba887fae9d1e14a2e4bd2ab28c0db0405add0789ea99836c9f7006838a5c8a343c7910fa04fe62359c5840021a1e8ecacb5cb1bff0b0000000300000000f84a23840929944583010d17944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000012a07b108eb00082044d808070235f900cc55b78e1e6928ef4ce66827bf7fe71c143477be736d589379811ac253eaa21eeb61a16813aa811386885afe8290602dd5690383627722ba9d7e2211cfff86b188402c37cf0830243db941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b300000000000000000000000057df6092665eb6058de53939612413ff4b09114effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d8080c40b5b896d81653b619a8913d90eee62c5d619b4962ecdee41ea9832aa95020349132258d75204c62c8fc76d9f8283b1326d4ee913a0b723657c54fbfb1bb2f71bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9048c198402c7c8ea83135048946b2c0c7be2048daa9b5527982c29f48062b34d5880b90464b80c2f0900000000000000000000000000000000000000000000000000313295693672400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000a16c02000000000000000000000000000000000000000000000000000feb937a2911570000000000000000000000000000000000000000000000000000000066e14e3200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000a16c02000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a500000000000000000000000000000000000000000000000000000000000000010000000000000000000027104412c7152c658967a3360f0a1472e701bdbeca9e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000082044d8080b3e971ff6fd58ceb07da1d98787e1144e20bbb3a4bc4898427f95bb1883b209b79f23b51651aa2197d379428541ad7ab7f2baed6ea46c94ee62fe302d16eb4c41cff0b00000003000000000b00000003000000000b0000000300000000ee8308be6f8401820c2082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80805c8c929f5ee6b9d6d3ee54c1745ed9009dac82133aa764d8b66f952ce4af2e0072573744d41b291cc6384aa9c9b5057a4a1d979fdab2c590ea251fcdb75fc49d1bac0b00000003000000000b0000000300000000f9018f830151da8401a8a6f08302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000050b52227f968abe8cba85ec45a4bda8f65d1d1670000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000015b392fb030c00000000000000000000000000000000000000000000000000000000000000018da49930ea8edc4bf2ba83646893e29e20703a5b2534c8e97ce446e41bc6ffe682044d8080c1a1b89b29b44083c19bc4b74e77b2a9190047f3ed5ce33198f142333eb479ab19eb434a8c761191c6fe9f02162be2d0bb5e163eb2b8528d493c44413fc512671cfff86d82010584018e41208304f8d394dfb6baa334712cbbeb26b7537f62b81c2a87b1e880b8446e4bb3b5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000182044d8080fc53deb3a49af4a7f3bdbe490ec121cf4b137ee3e28bce917494eabf24b148b91aeaf809a6a552b1e7bcebbb955ed6a54d004ce4fa883b0d53c9faac21cc542b1bff0b00000003000000000b00000003000000000b00000004000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903f220840186a00083030009941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5e9924268a07bf7d21c823217668de1706f45cf6eedfaf6528e79c479eb926f00000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000cf6ea21f3928460e36e30150815f212fca2f9a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5166436f4d73464e784d7274756a31507054614c444261554c586551694453626b5a63524533687839516b68000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000418550cca2cf3bfcec208c53cdd1cc838b0ffb5c5256bba0d4166875e47c099b175f181a3a95b5c0706963513659d182ba900782a379d0eb74715719e59a6f58c11c0000000000000000000000000000000000000000000000000000000000000082044d8080e58acba608a369d18c6d73f43366584f723671662e22cd7bb1807671b3d2f71f03a1f7a8ef7d198a54345cbb4f226888cc8867e55b6b20e231e0228d71d79cb71cfff8b12b84025317c08304ac1194222228060e7efbb1d78bb5d454581910e392222286043d112b3c31b88432accbb900000000000000000000000000000000000000000000000000000000000000e6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b48eb57e000000000000000000000000000ea6e4548bb97b9237fd08bd32ad9a6f8e59eb10b82044d80808c2c47c45393f93b84a8a00f3e965a8a3a532e4d03d0da39a78f6ed20f91faf017d9d1405441a4a8b6d81421ff34ce60c607ff5843b029907fbeb58ee371f34e1cff0b0000000300000000f903f22184028dd3a78309e223941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f53d6aea38052471c5a2ab59bb49640603e17c99abdaa8a597c19480f99e27963d000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000000673962924875bd4cbd2e0b22ed63c85cecac2e40000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4f4b582057616c6c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d55316142655a5a6139787756357746507855734a37416d6276714a38574a784375656f4452666567595a42790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004159b0d36df5531ae9fad1f1382129122fb7cdf909728e403412134e87d1c1542a4d2a58cc08a4005b8f8dfa3a594103b3176813122f947790cd52a2fe8df5f5c41c0000000000000000000000000000000000000000000000000000000000000082044d80805c95ef7a6c28a8e2e82f28d6d894b5b9ee5171b03a1c22968ce432462eede80b24163ce4e126a85062e602cd6e83bc9fa4855353b979476e4e8a6d495d56d5f21bff0b00000003000000000b0000000300000000ea819d8401a39de0827181943f0a9f960341dc53747d43d27e4e1a2678f7983e8084753899e982044d80803e62d33d344773f096582827acedb468e005027390ecd7e427d3bfee95cc734e4932744bfea8adcf4dabaebb2115f1d742b8f346984fc496f7e7a795cb71d6f31bff0b00000003000000000b00000003000000000b0000000300000000f86e8301c9918401aa54a0830186a09465a4b8a0927c7fd899aed24356bf83810f7b9a3f80b844535b355c000000000000000000000000a703654c31dd3bc26dff3c6ec524540c602380390000000000000000000000000000000000000000000000000002659289813c0082044d80804de874ddea4656d09ad455f64143e57454f56e7191364e43bbbceabefc4ca03002f35052b4e2b6d4be7b637a6dcb4c3b37d6e536f7a5dcc64b147469ade0037c1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f8cb0c840b7f6358830a623f94c28a4ec9f09e4071e3707eaaca5c3754fa4f5faa80b8a48fb8b6c780b56aeeab2dc46113b09dd8069578d16c679cf639475dd6e0b8e60b7a3b21d9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000082044d808014cb2bfb965493c5dca083a997eec444184bd9f3f79aa4c555bae2f5fe5d567058f2dc8d1eb1aa371a97d86c65095e572232021dc7a3d453c2a2ec11c955092d1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903f2248402625a0083048056941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5f51a6c4d02c18d30b29fa792091edfdeeb4a51d55a18c953784a33e54d7fe50a000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000f65a8d44c6ec9823ff7be42b3cac3b457ae8d5200000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c52616262792057616c6c657400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d64696179665a6f6f7a676a64625374764a657a48467664325a39384e656669517736594a4144456a56554e4100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b37d89a9353bd6b5183ab63e9080c97cb5a24fc814b5fadf0d531acd6272dcdf00a568d77e7e5ef3b1821820166377891930eddcfd50fde885affcc1320f61d31b0000000000000000000000000000000000000000000000000000000000000082044d808076fb7660fdc1ef605bc860a4d1b98537f6bb3c18c507dffec61c663c14950ee06415ecdb58bfe14b8642ce2d909378beda578c5353356a559a6ae1f176eb204f1cffec0184028e2965825208944570f01acc78003c270b68217b819d783cc892f487179d7b0b84e6678082044d8080829f6ee74f6fcf2ad05e10267d898068704c651ec8c3af9bc1b06d48b721e8ed5d928a25a8942496fa4b557e61bf6e6552e5d95c53ae3d1ec9bcd843379689381baa0b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be708404c63aa082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080331d3409eb28ac0f3aa116833654fa85ebc0ce15d7d6045703ea5cabed548b2e15c7a437215b8e74d70939efc3842b0dd571788b3abd34449555b16d18c6b6c11caa0b00000003000000000b0000000400000000ee8308be7184019768e082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80804993bb9121b4238b115f0591a929482dd9ff7c2e6974e85901a475fef18636417feb9d1551cfb7d465738a2d001cb5f7ba47e65fd4c1ab2e6d67117fa835a3361caa0b00000003000000000b00000003000000000b0000000300000000f9018f830151db8401b466c08302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000010000000000000000000000003e5cb35901d40efb3c6d38aacfb0f4b0cd5258650000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000045cb32e45f28000000000000000000000000000000000000000000000000000000000000000142b576c33464f5ecbf09fa2b1575d07772c814bcebababe6c099862cafb4fccb82044d80805dd0484cc33cf12d031a9a675192d937ebc6aa79432bdedb9190f4fe84410b6029c8935f77a6f8cc99964f47afdeb4c41790eeeb636babf8e3f034742f8c98b81cff0b00000003000000000b0000000300000000f00e840172c9e0830139f594b58f5110855fbef7a715d325d60543e7d4c1814386a758d6a38000841249c58b82044d808045d3b812f522cf8f1b5a670f65c1fe24a0806c563eb3dcd9ad2bffb3e2aceeaa0884dea66e76f89a5047243e18d3bacc1a849ec983f640a958ee410c804916741bff0b0000000300000000f382010684019768e08305576d94dfb6baa334712cbbeb26b7537f62b81c2a87b1e887254db1c224400084e9ed3d3082044d80800b1d6a122d7289631fdfb4b72e4a6a86c293afa79c1f3337c9a188badcf82dbb2efba7d16402ebaa03b0059aa68f7f3a445d2fd3425cab4179d50a9d06df0aca1bffec81a78402faf0808252089433fd1acc271574f6e5c2eb4a0ea1264251ba2119860abb620a3c008082044d8080c9a1e1b5d798f7af1ffbd19e89c74fa39119c8b261e9f84fa39d9af2c67200d43ee954e07bf11fb21588f5d62e6c5c7dd8da23de14658664a4389b4fefd13f6d1cab0b0000000300000000f903f21f8402625a0083048056941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f522f0b9754d4d2585628043451776bc2eb65ac80a097992cdd46586be7aad1739000000000000000000000000000000000000000000000000000044364c5bb00000000000000000000000000033fd1acc271574f6e5c2eb4a0ea1264251ba21190000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c52616262792057616c6c657400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5374533974704d3479584e42543463474437554b4a77616a4b67443363444b7971347a71764e45657248434d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414ed93ff5c116db4089def64910db6449c30e8196e78031d25816d7e4f92b119f62e583f8b2003c9b042427ace41ef3a6a1619130f2fcc00fee151d0acbc804191b0000000000000000000000000000000000000000000000000000000000000082044d80805341f33dd6e64568431b0846bbc656fdc618ce107b9812dd2bf3f2e91c44f0240c3281cd38136e751fee0902452bca959da52d667a092d2fbc70ef493af7cef81bff00000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040110b00000000000000000b0000000300000000f9014f830115518401bcf240830596219473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e140a500000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000731412d863de818aedc2ac00000000000000000000000000000000000000000ae12c46db9b6fc047c306c500000082044d80801d46c877c9b34d2ab10700459c548bbdd97b4c71ddaaae59eb4aa9829063a3456742ada3207659a4d8499dbb6600f91b9e7ecef2807fb739d2a63af6538555161cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f901b30f84019bfcc083039dd894b58f5110855fbef7a715d325d60543e7d4c18143870b7f8bdfda5884b901845190563600000000000000000000000090321b082c1da51d0ec59ec7d02f5b65d1c70ed3000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000b7951c00000000000000000000000090321b082c1da51d0ec59ec7d02f5b65d1c70ed300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001490321b082c1da51d0ec59ec7d02f5b65d1c70ed3000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200010000000000000000000000000000000000000000000000000000000000061a8000000000000000000000000000000000000000000000000000000000000082044d8080fc542a4b3b5caadfd5c04117a3f8512c467f8a59866aaea320aa3b5bd891f92b068e84eeb6846cea45c443f478e7c48968d14a2034cf6ea9005facf652f51ea91bff0b00000003000000000b0000000400000000f86c81d08402625a0083011108940d1e753a25ebda689453309112904807625befbe80b844095ea7b300000000000000000000000031c2f6fcff4f8759b3bd5bf0e1084a055615c768ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d8080a984967c135177e8261c40769322acd476a9a79a07d7266cf441b0329b3b009533e8693879324ede3e8028f2baa8c51ea3aa1212ad5bf5c94dc49f14a2831ff61bffef808402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109863691d6afc000840333f63e82044d80803f154443bd814efa2e144586740cbb0024d5f3ea553b60c131823aa61e2be4e73adab50e89b656b3c7e4c9e2f511754dc45d14ee7894de88498f2cd23e825a501bff0b00000003000000000b00000003000000000b000000030000ae460b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9044d81d18402625a0083044f4e94b89a6778d1efe7a5b7096757a21b810cc2886fa180b904243593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e141e700000000000000000000000000000000000000000000000000000000000000030a080c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000d1e753a25ebda689453309112904807625befbe000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006708cdc50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b89a6778d1efe7a5b7096757a21b810cc2886fa10000000000000000000000000000000000000000000000000000000066e147cd00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000041ce9c062459de7685af53cae3d9c022ce609093f04ce875111314f8aa1366bfd579a369788bb5ca6b5cf908cff33ffda44e1afa0c4165caab252665bb02ece9861b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000007355b876be771900000000000000000000000000000000000000000000000000001541e01138ed100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000d1e753a25ebda689453309112904807625befbe0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ab07bd0ac2502a7fd0a5005b420b96f94104fcf80000000000000000000000000000000000000000000000000001541e01138ed182044d808002d7ed117ceaf8a71c9325711c8ebe173ffb02a537a631170f2bbb140ad8532b2e3f3dcec0e5c85384fbb643e3508cce4625696ffedfb0f5d16c517a11ea1bdd1bff0b00000003000000000b0000000300000000f86a808402d8fce182c16f94652a38fa87f60a122aef360eeefcaf6258eddf6a80b844095ea7b30000000000000000000000000a6b1904369fe59e002ad0713ae89d4e3df5a7cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d808096c77cebc8ba97dbcb33a46b05001e3f26206f91c497007900507d8297919a5e042728715790ea437a06e937ffb322b65031db0ba633dcbe0351a1ca78a70c991cff0b00000003000000000b0000000300000000f86b1a8402d06b49830243db941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b300000000000000000000000057df6092665eb6058de53939612413ff4b09114effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d8080005ee956dfbd7ecdf8ca2c5fa9b5a21aedb2a425d4d1568032332b5ec3e689fa1988b83c9fc83a17142953dfcfd62d2e79c15f761b013b042537a77602933ddf1cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f90a6e8204ae84019a762083023cc494709944a48caf83535e43471680fda4905fb3920a80b90a44437b91160000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000074000000000000000000000000000000000000000000000000000000000000008e000000000000000000000000000000000000000000000000000000000000001641a0a0b3e000000000000000000000000c52eea00154b4ff1ebbf8ba39fde37f1ac3b9fd46575f2c29a636fa55eaa8aa86706b6d3c33a21e0e79e1647259718dac070efc40000000000000000000000000000000000000000000000000000000066e140ab00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000937596f639e540000000000000000000000000000000000000000000000000000000000000004172b3eb5cd3fc78267d5672b9ad5317286aa8f3d66e55d866dcdbc9e5d1255c6d26535ad3d1b26b4b8fbc6bfe8cc543d3dd612138909a4aeb47117f908bbb30321c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001641a0a0b3e000000000000000000000000bc6471e88d8afe936a45beb8bd20a210ebef68221998d1c5e381c1edec0feec35e8bc3c9d7cb39ace96d9279b6f9a07a7ea454240000000000000000000000000000000000000000000000000000000066e140a800000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000009463846edd3200000000000000000000000000000000000000000000000000000000000000004168362b70473277d046be65bc395f0b0c8b98173b46adb4192c6f4127df5d1867494705d3ceac8368afe2ece88a7545794f4e51fd431667febdddfd66a2b152aa1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001641a0a0b3e000000000000000000000000a924847354c551c79bae7e75529364ba0449e51a39001761c965b7719e6b39822f09c891d103009d2703a4989cf7bc2e007582fc0000000000000000000000000000000000000000000000000000000066e140ac00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000945b55a7e0000000000000000000000000000000000000000000000000000000000000000000413dc56655400801a9c210ecdecebbc3da45c92de696f13b97bf5e73b6044e27366fa1e651e74e5f57d6a610791ec51a8eb5f2e8c482a5b5e22f939c0f0b1eeb391c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001641a0a0b3e000000000000000000000000c9b494d3c6ea3fd42779df9a136db10374c98d80c7dd05baff9d2948c0f215ffa84c4de82694e959f2fa7b70f2f2a1f398381e900000000000000000000000000000000000000000000000000000000066e140af00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000009455be8a47413a000000000000000000000000000000000000000000000000000000000000004152452f2785fe8cc8876e4d7c70e6656fa7d2fd5d48d1ba751829ec73a2ac5a607e300939c863047c82dc39ad2e28db75546263bf6c8fa3aa3ae7009607e8db191c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001641a0a0b3e0000000000000000000000006b56e47dccfbc82d63df3da417d26e8b1b877f0f5d7cab00186de4dd836b0e3f89da0565a5a68597e1772792cd3c63dc4505d9bb0000000000000000000000000000000000000000000000000000000066e140a700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000093a95d5afc400000000000000000000000000000000000000000000000000000000000000000418ac1031836570d4ca35d6a2cbfc9e7cc8ee5da912831a8769c87b4b5df1b74604428cea4bc23413a82b246008ae57b93c1f917ab594b6c63c60b009db44cfe871c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e400aae33f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005750fe3cc44fb71eb6dcda12a40ba96a779ff3292e4ee0b83e480607fa18a253c25dc66d59c09b65362270bd4ba318502886dd51323d42cc672bb118a2f377d937701240fd1a97e184401c372d22d78050175399403b5fde50faf03bab1c7573ac1c918bd24675fed06d7c1ca44882492b3ae0462c3b0c6e7cae84702c2d9708be6812ce7d565ac54474e619953d3b2588ad818c5b7328ec16de6304fa43badee0000000000000000000000000000000000000000000000000000000082044d8080aa51d9561dc2f3f50bc646119dc5d2288be28526b01facb218bfe12777bb470b73d45a28e9f367be9c9ab1e9823735251e7c58ab8b527dd8864c32531d9ef4431cff0b0000000300000000f8723c8401a524808301d9779454c3ef8ce719ed72c2050ee42a05aca9e9aa1e1f8701c6bf52634000b8441d7df19100000000000000000000000030280ba7411118147b835ea31029481b01284175000000000000000000000000000000000000000000000000000000000000000182044d8080c4a02d3c640f35c2628bf24777bcb8157adac99f25a33a66aa0833e12e0692ba77820fa9aa795375809f045555ba5cc8167c7891ed03506dc2c67b64ee7112be1bff0b0000000300000000f86c81d28402625a00830110e194a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03580b844095ea7b300000000000000000000000031c2f6fcff4f8759b3bd5bf0e1084a055615c768ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d80804660394e29ceeb7d28d161134a042cb966b23c6ed12f2bf645022ada6f27fc3143c01a84374564d949e10dc0df1c9c103f78651b831bd12482d8e3442f024a881cff0b0000000300000000f9048c1b8402c9f9b583138a26946b2c0c7be2048daa9b5527982c29f48062b34d5880b90464b80c2f090000000000000000000000000000000000000000000000000031329898bf72400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000990e21000000000000000000000000000000000000000000000000000f1b8b1e74b2040000000000000000000000000000000000000000000000000000000066e14f0200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000990e21000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a500000000000000000000000000000000000000000000000000000000000000010000000000000000000027104412c7152c658967a3360f0a1472e701bdbeca9e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000082044d8080c52fcc136946cb5b9089d62a52d13d661c9588fb367523e6404b34932fc651fd108b70a62f080f0a280357b74a7eabc52b5b807bee6d4ee85f85bdffdce996441bff0b0000000300000000f8cb018402c9f9b58302724d940a6b1904369fe59e002ad0713ae89d4e3df5a7cf80b8a491695586000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003148e44ea107a00000000000000000000000000000000000000000000000000031167b0f044b80000000000000000000000000000000000000000000000000000000066ea7b6882044d8080c54a7d2f5e3aaa2f9e932ea0b5c6fbdb5166c3bb4226725e8cf0c5acbb677a637ce0e52990ef54755786e983d449c7c6adc824f57fe7a50f4960fba85ec8d44c1cfff86b198402c4c487830243db941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b300000000000000000000000057df6092665eb6058de53939612413ff4b09114effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d80805a91813eb1f40d2394eee6ad6410d547b8189e6be17bf23c90dbc1d01ba6edff103a22f55e0986304b0d36b367e905b3a5ecbf94755bb2670b563e25b6d174921cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9048c1a8402b3d0168313849b946b2c0c7be2048daa9b5527982c29f48062b34d5880b90464b80c2f0900000000000000000000000000000000000000000000000000313298f36971400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000991c85000000000000000000000000000000000000000000000000000f1cf6bcc4e4c20000000000000000000000000000000000000000000000000000000066e14f1a00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000991c85000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a500000000000000000000000000000000000000000000000000000000000000010000000000000000000027104412c7152c658967a3360f0a1472e701bdbeca9e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000082044d8080f24aed3d22f15fbcc57fafe44d53a1a9e60f4ba51291fa4f22b53e20d79ddfea5553060fbced61fca5aa4d1d333d067b1c3904ecae9d9c71c47d5693a257e8cc1bff0b00000003000000000b00000003000000000b00000003000000000b0000000400000000f9044d81d38402faf08083052a5194b89a6778d1efe7a5b7096757a21b810cc2886fa180b904243593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e1422900000000000000000000000000000000000000000000000000000000000000030a000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc035000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006708cdfc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b89a6778d1efe7a5b7096757a21b810cc2886fa10000000000000000000000000000000000000000000000000000000066e1480400000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000004159610a05dec35ea87031acdfdc775d81b7d21424be26f4198d7963c054b6486102f7bb552f766607a24314a4d4674ec5a8b6cae07000ecc918b6ef1b92b1dad51c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000002a055b00000000000000000000000000000000000000000000000000042ae8f761201200000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ab07bd0ac2502a7fd0a5005b420b96f94104fcf800000000000000000000000000000000000000000000000000042ae8f761201282044d8080fa6c7ff44fb469090f7f796a4dd27d3dee25540259f1b8307ce80915f329281f1643255ab2b3498b2827900ca99996c2cf60c4e2b058cab244d2783263fc07c31bff0b00000003000000000b0000000300000000ee8308be728401a6ab2082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80808cae305056836f1b109e76a38a29a95e699ca239c636e820c5221cc0bfc1ae8e08b630842cda6c38fb50cbb4676c4a61e00b01f55b68f27f51123de8cffd652e1baae96a8402faf08082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080d0a9dbdd118b85ea2dd4a860354598e348085c2c448ffe77b9b9ebfc87bbd4b3416de323e24a7e3ff04007cad827b524e6054943ac7f7d51942392f1c1b9199f1bff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903f2028402faf0808304e5ea941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5fb2f110de3cb09ff94ef99619912ce386a0f42d2f466e7d3e480d7129bacc121000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000a703654c31dd3bc26dff3c6ec524540c602380390000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008496e6a65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d55656151777944734138474a4858594c794248344333565564504d6f5142474c457a3648476d423152696f5600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f2eb6584e3d4bfdae352806a3a5f344ad70fe77bacc930a7a40b925f409ffe4f4c8a88c6e07314b8a109757251949b0932d806ef8a608b54f8dee0f447ba37381c0000000000000000000000000000000000000000000000000000000000000082044d8080f2eed7742d047224403c36decbbc5c7a6f1bf7f67057cb078633cf30c96449ac7d2a665aba4252c99bba96742960a7228167514b354f24d9a97b6f5777197bb11cff0b00000003000000000b00000003000000000b0000000300000000e93a8402faf08082ef4094b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080f512c7a6222205683b9012666c5455c998de80ca017dcae80b4b8353f2d9c6e7444e694afe71445e1f73011c9416982fcd2013de37f5a6356b62d4786b2e01dd1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be738401acc5a082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80800d61afecc903851650d8243af5e3ec20654db98d9c0fe14f7cdb86d9e50f2fd7188796e923b771c47a7674636d3a9b4e14fce759226a2445a470836e8aeb1fc31babf9014f830115528401f78a408305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e1415a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e1000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000072fa529d46831fa4ea1365c0000000000000000000000000000000000000000ae065f41c16c30440f13ba800000082044d80806bfcacaee014344f6c6bf79fa3af76f7baf85705a22c6b6022aa3d2275044f515e385563fc9f791dd33b51658d81b061b0e582896fd33f745f00b4208500200a1cff0b00000003000000000b0000000300000000ec038402faf080827b0c945e809a85aa182a9921edd10a4163745bb3e362848704adcdf52b50068082044d808072e2f7ccdfbf0c9967dc8d2983a80a846f075f22068fc67764df88c72dfc3f206fa80b91470d803b7f94b181278d72809f042b83b4eb3b242998ac89f12491ee1caa0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000400000000e9768402faf08082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080f7c406958a3b283f040ba8e3b99e77961e478a6849858a513f9950fe2474271660a78cdff6ac241cd222b97a27685de4da616036e123ebe6947bc531590760df1bff0b0000000300000000f86c81d48402faf080830163f89437eaa0ef3549a5bb7d431be78a3d99bd360d19e580b844095ea7b300000000000000000000000031c2f6fcff4f8759b3bd5bf0e1084a055615c76800000000000000000000000000000000000000000000000000000000000927c082044d8080e3b16d5bfd529eaefa4d8ad988900a3ac14748b9b787645fe42498619e4b85001f4ec92e43b5ccdd3e61780fee963ddf0fbaa9b6b7bde93e6e6bbda3abed8d811cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d8263678401ab7d80831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc651188000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e143f80000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000098176ab6ed58bc000000000000000000000000000000000000000000000000000000000000000082044d8080f44f214824c6406324f3caf06ca6aad692bfbce83fb69685723885047c1978a0009f698a2561f6fd301af75d83ba928ae9718dd2d175bee9e2a70260a91980151cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be748404cace8082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80802c732f67940b07b16d4ff7c2ed509c81df86e1aa948e04a97f4c1cc90de679a07a50c2c1df215261e32b211a422ec0cc62994adcc02962b201a058db5485e0661bab0b00000003000000000b0000000300000000ee8308be75840198ef8082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080365e902ec6677c927098c294ccd1808009bd6baacd6cc18272b1f8e4a3a88a940c88b52ac0c9568948d082d6298474169873f7fa0dc09bd1db8b6be09ec6016d1cab0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f86a1384018e412082b4cb94c5015b9d9161dca7e18e32f6f25c4ad850731fd480b844095ea7b30000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae0000000000000000000000000000000000000000000000003ddd14cefbe04ae482044d808090ed685bc17c39f23da886bec59a15265f557d06b5388c26f489d199e2c9dbbd4e5964f35d8272171d695b20f1aece1e56bad6a0f93b5992ff69f774527ac2751cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9088c1484026ccc90830c45a3941231deb6f5749ef6ce6943a275a1d3e7486f4eae80b908642c57e884ba378f2fce937d7762985fbb68cb4ae55a699ae984949e2401b561a7327abe8f00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000003f2825e0ffd694adee76c9581fe707606074aa900000000000000000000000000000000000000000000000000006b01f2257e4a2000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000086d746f7073776170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863346639646134323361626336393237393937383338313533656131386631616266396435613938000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000b49ead76fe09967d7ca0dbcef3c3a06eb3aa0cb4000000000000000000000000b49ead76fe09967d7ca0dbcef3c3a06eb3aa0cb4000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd40000000000000000000000000000000000000000000000003ddd14cefbe04ae400000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000084eedd56e1000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000000000000000000000000000001fac95eaffefc40000000000000000000000000000000000000000000000000007eb257abffbf1000000000000000000000000c4f9da423abc6927997838153ea18f1abf9d5a98000000000000000000000000000000000000000000000000000000000000000000000000000000006a000f20005980200259b80c51020030400010680000000000000000000000006a000f20005980200259b80c5102003040001068000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003db57d1396205f2f00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e4e3ead59e0000000000000000000000005f0000d4780a00d2dce0a00004000800cb0e5041000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000003db57d1396205f2f0000000000000000000000000000000000000000000000000006b01f2257e4a20000000000000000000000000000000000000000000000000006c16a70441813d770acd5d9f04dc897d42ce2f295f18700000000000000000000000000f2a81500000000000000000000000000000000000000000000000000000000000000008c208b7b5625d78deb49240ef28126cbe2738098100000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000240f6ad3ccf71abb3e12becf6b3d2a74c963859adcd00000140008401000000000b00000000000000000000000000000000000000000000000000000000c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000005f0000d4780a00d2dce0a00004000800cb0e50410000000000000000000000000000000000000000000000000000000066ea7c4c0000000000000000000000000000000000000000000000003db57d1396205f2f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003cc5015b9d9161dca7e18e32f6f25c4ad850731fd4a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0354f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000004000040000ff000007000000000000000000000000000000000000000000000000000000002e1a7d4d0000000000000000000000000000000000000000000000000006c16a704418136a000f20005980200259b80c51020030400010680000002000000000ff04000900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080d8991732646a94259a6feeec42e5a4fc65fd2a806c109c58b6da6c6b9aa175a024d45fb5eb3d774b1b9a50a6ee7dd40b8104d1caec3bdbe82885a18aea9308621bff0b0000000300000000f8b12e840263c1608304ac1194222228060e7efbb1d78bb5d454581910e39222228614edc409dd73b88432accbb900000000000000000000000000000000000000000000000000000000000000c600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000246139ca80000000000000000000000000000532b4600bc0667326bbec62c1f55a4e962b21a782044d8080dd3a56e7bb40299dab4d821c8860cde90383a218003cf93ada19e6ac7762fbb2248a75809c92a48152c303d8197289a58956d0318ae6d816ba67054d954ad5181cff0b0000000300000000000000000000000000000000000000", + Expected: SequenceBatchesCalldataElderberry{ + Batches: []SequencedBatchElderberry{ + { + Transactions: common.FromHex("0x0b0000000300000000f9044e82674884017bf1a0830bcfc4948ff6508bdd71b535df073920c423e9eaee0b97af80b90424ec0ab6a70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000e0434ddf5273f71250a99c6fbcabfeca301de5f000000000000000000000000f6ad3ccf71abb3e12becf6b3d2a74c963859adcd00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000001a45ae401dc0000000000000000000000000000000000000000000000000000000066e13be200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e404e45aaf000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000000000000000000000000000000000000000001f40000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af00000000000000000000000000000000000000000000000000000000000652e10000000000000000000000000000000000000000000000000163adcee8b87f200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000003d5320821bfca19fb0b5428f2c79d63bd5246f890000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e13be20000000000000000000000000000000000000000000000000163adcee8b87f2000000000000000000000000000000000000000000000002e2103547d3748000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080f1f06050464bbe1899db8975029243860aae9dbc153f3d0bde4cbf423605c0f36b575d04708f9a517c2cfe3c6af0d379c30bca1097b252776609f3c2ef706d681bff0b0000000400000000f901ae82662284017bf1a08308fc58948ff6508bdd71b535df073920c423e9eaee0b97af80b90184b61d27f6000000000000000000000000f6ad3ccf71abb3e12becf6b3d2a74c963859adcd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000003d5320821bfca19fb0b5428f2c79d63bd5246f890000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e13be200000000000000000000000000000000000000000000000001634a3fb8d2af5000000000000000000000000000000000000000000000002e14026ffdab72000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d80800fb8261fa1f177e41f88e7978b3cc48fe720a22ce153ad4bd88febf30f2bf8db3354cbb1d8a684aa733a0c9872a02c28dc350015d9ad47916a5e8d97b365ad8b1cff0b00000003000000000b0000000300000000f28231da84ee6b280082627094d20595ed0ed59d85d9359de5164b320a5c40fa6e875f9a48fae655c8840333e6b882044d80806ad649fc21c968300d2abebc5e256eafba5e069316dd680a6bb4feec0290e85b7d9831647c8d457a29b6efa64a88add3d0ab985436c7155238ed86435f30dc261bff0b00000003000000000b00000003000000000b0000000300000000f9010d821ed48401821fa88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d200000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009814ae9030c504000000000000000000000000000000000000000000000000000000000000000082044d8080f2c8fb37b8417e9a3d7245c8f428d932e87f43877e75b346c3d575a80700110b28ff4f9ac9d7882c8b61cf9929d2a20a8cb62d32aea89cbca06e3021eeaac3fd1cff0b00000003000000000b0000000300000000f903f23b8401620100830343f1941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f52e9f9451b39e2171a782aec26677586f35adc72ac984d0921cc7b9512c744e75000000000000000000000000000000000000000000000000000044364c5bb00000000000000000000000000081f6b887657b679d9e23153ffcd703e1110102400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5867796855613343726552507448414d4c75314a6f47796d6d734b6a66676252464e4b5943644172364541570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004137eaeccfc1a481ec514bda2863333d34977c77f56e4033ea74cef4c22a748db0717b9d2961adbeab964a3963b682bcab163f2cec97eefd02c5df5b66a4e71b521b0000000000000000000000000000000000000000000000000000000000000082044d8080ea7294305dca446fc2918900c201f24486d75b33cafe6e2e8bf369218f229293230e8b67c0bd2b47182ab77ffbaed1d7763fc5705e306d776b0f8f8e86efd88d1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ed5840166e3008307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d3e0000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982adc32888842000000000000000000000000000000000000000000000000000000000000000082044d808067fffeed9f7553babaf6570c8a11233f004d0758170e5d23484c095da5ea282b08682443b3ea253139b3ac01977c0a758f081fd6cd614a24205c1d67cbd9afbf1bff0b00000003000000000b0000000300000000f902ee826181840189ad40830804df945523985926aa12ba58dc5ad00ddca99678d7227e80b902c484d61c970000000000000000000000000000000000000000000000000000000000000060000000000000000000000000292fc50e4eb66c3f6514b9e402dbc25961824d62000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000001a4c23a4c88000000000000000000000000000000000000000000000000000110d9316ec0009bdafe8da6cde01e6040b2d2187f6b67a297205ae12cbfe9485196bfa1b3a8374f9dd0dda9066451633b471b04af8ef8204abfe2693c8bf8c450bc20dcec28d300000000000000000000000063e99e508a9f2b6ed180811f80db90ce7417426c000000000000000000000000000000000000000000000000045f2a42a47981960000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000f02bbc9de6e443efdf3fc41851529c2c3b9e5e0c0000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000447647691d000000000000000000000000000000000000000000000000045e1f2d2e761b0b00000000000000000000000063e99e508a9f2b6ed180811f80db90ce7417426c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000411e413bf834084915b547cc0d72d37e10a33d19426323e626c254fe8663b6984d4cc14841f43d8ca4842e10ab2fe3995cab429c30a54301522ea3a5a5272a91871b0000000000000000000000000000000000000000000000000000000000000082044d80804f5051f8a0057498cd6d2af0d1628a32348d13945f6bbdfd570b3544c7a08f8875aedee2be658959e23184a59bf6b92b43ee0fe5934f93f28b93ce4288de1f6a1cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903ce82264a8405f5e1008307a120943348053b13e22b2717c742cd4a60f5040564a53080b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300010100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000d23aaa8116edf941e1aba78b113d7a670002ce4a0207020601080400030509000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000023c2b0000000000000000000000000000000000000000000000000000000000023c316f00000000000000000000000000000000000000000000000000000000023c316f00000000000000000000000000000000000000000000000000000000023c5b7000000000000000000000000000000000000000000000000000000000023c7e8900000000000000000000000000000000000000000000000000000000023c828000000000000000000000000000000000000000000000000000000000023c954000000000000000000000000000000000000000000000000000000000023ca99000000000000000000000000000000000000000000000000000000000023f57ef00000000000000000000000000000000000000000000000000000000023f59fd0000000000000000000000000000000000000000000000000000000000000004650755b20d11b5cde1a5e1f721e10c74f5f078fac6800341d15be87da2e74a3c5acb65047658a523d0c57d88d94ca1d78f66ccd21b9b203f2df55700919e11bd2fbf684aea0d5dc6ef5cd78dc496afc655c627eb2e2da675143859fb439860329edfbc8fc5731ef33d3d50efcd525f27181b84a44fcf726ca066ebe622c15866000000000000000000000000000000000000000000000000000000000000000421bfca8453ac26e096cd1e78f31336665f46d385c6333d63a813a1bee82a0d97248c4d7624257e93bfbb42e7a8b901568660bea31df9cccf80f064263ac1977e495fb904f2b8477e6114f459116561463c2fb93122fffff08adf8c85aa5850f657c7fed085522823081dda81d1bd94235fc42fd1e70f70b38737760887e1ae0c82044d8080da940144b03a145e88a116e125b1a24eac784a85138a43a7bf4529d1e5fa33751fc270dd6380301802a94c1c6f1d398f5684f56ffd1d974231bcbabb1fe9ad791bff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ed684017eeb588307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d5c0000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009824b00e503048000000000000000000000000000000000000000000000000000000000000000082044d80805a0f08064fc456e60e242cee4f20ecde976a51c7e1c1b1245922d15696c32cba65dbc28c8984d233552830250b4be646f523d65f1e686aa09b038769469d40ec1cff0b0000000300000000f8b12f840238e8a08304ac1194222228060e7efbb1d78bb5d454581910e392222286352f5cb5673bb88432accbb900000000000000000000000000000000000000000000000000000000000000af00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000271bb7b9b0000000000000000000000000000d4ebba85fbd618d08c6911e6bea0e514280144682044d808080e4ceead86b43aca84d560e925df839a58fc08296edf87dc44985d38e1312113a39d3f053ece00a9741ef979d9b0457a11dc7b65ad04dc117ad9a49c52987901bff0b0000000300000000f13d84016caf608304443a94ee1727f5074e747716637e1776b7f7c7133f16b18732a5492d0cfb90841249c58b82044d80801633bcf1a8f14729079a42e6e7d86f2e304924ea015e3f1ddadefe9e31869263170df9e3efeb2ce79a7a320bcbcd31acf77e4d0a33fd4945f0d6724d688b88241cff0b000000040000ae44e94d8402faf08082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080317339510effaa798bc532fa32187f09b40c9b5cd12ccbeb7f7ab3fd3c157e680721490b51e44c59435ea2cd2143d71ad2b6316ae19d4055cfcea37d7a2cb9eb1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9018f830151d184018715308302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000010000000000000000000000003e0103ff5b7f4b338a8f977b35846aa7c01ed379000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000001635044eda5700000000000000000000000000000000000000000000000000000000000000014760dfebac75f07fa46b8e87d9151b673b8e33d4710293497a4a5c9524aedc8882044d8080a3566b6ff106e89842dda24f38fbc6ee0a105588e63240914f04986798835bf030b2f4d65e86d43514d0c03ee84e5c46a84fc637ac927233674c4cf7effe31101cff0b00000003000000000b0000000300000000f9010d821ed78401754e688307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d7a0000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982405427dd941000000000000000000000000000000000000000000000000000000000000000082044d80808cdf22284f15949ea3fbb6eeff81ee87a198722c1f8f827b1ce98ca30138b756444a20bc97269e363da23b33ee1badcfe012964ab8e73ee9c70f6f4af5ae99351bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f902ac258402625a00830494ba94b89a6778d1efe7a5b7096757a21b810cc2886fa180b902843593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e13c4b0000000000000000000000000000000000000000000000000000000000000002000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000376f2930000000000000000000000000000000000000000000000000057e3e6c76eb74600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000968980f6c8b41abeeadab481578614ad405f58890000000000000000000000000000000000000000000000000057e3e6c76eb74682044d8080388b027ca9400e25178e592726a62b4d39a6172d956bf11474ce49f65b0fa8a426f0e323ac7e8de7a4daa322ca42fa3c2cf66081047319341febcf2a8859af3b1cfff9028c208402625a008304932b94b89a6778d1efe7a5b7096757a21b810cc2886fa180b9026424856bc3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000039bac84000000000000000000000000000000000000000000000000005b8731fffd99f800000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000006ded4acd9fd87eec14f18b22e9046f000be0685a000000000000000000000000000000000000000000000000005b8731fffd99f882044d8080b53c40cb340c035c10a519f25f2de3fca0cd253015c28e7049b9d485ea98b1a5021c6938342e403283007f2ea6a9152a9089428f3fdee38461164b0fb89d690d1cfff9028c2e8402625a008305581d94b89a6778d1efe7a5b7096757a21b810cc2886fa180b9026424856bc3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000004710b280000000000000000000000000000000000000000000000000070a92a16ee367400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000e3119ac2c91cf8e2ca81b54d757c166c53647a2b0000000000000000000000000000000000000000000000000070a92a16ee367482044d808008fd8af756bc9385919f74d9a9573a3581faf800c7999d51e834423a5daa4000067f947784576968c91ae28a653c76771a15035f088241705df3780d3cb9df1c1cfff9028c1e8402625a008305581d94b89a6778d1efe7a5b7096757a21b810cc2886fa180b9026424856bc3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000047054780000000000000000000000000000000000000000000000000070971273bec4a100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000018c4dce3c0172a5322aefc459d69e85957d387890000000000000000000000000000000000000000000000000070971273bec4a182044d8080ba9906ac2e50febe56442a09ea73b14880b4f418e30f891402dedfc36e18e57b3b7ef3466f11fcd657b0d99da14c32249962f8f505656ccf48f0a5fa85946c2d1cff0b0000000300000000e97b8402d112408255f094e10add2ad591a7ac3ca46788a06290de017b9fb48084632a9a5282044d8080e2aeb5078a6bdcb30be8415f3bf338183d2b725380bb85cc21596c775e414b103b7de8fceeeb38b5089d6af9f7f4862f805c332651b87bd2c444f16180db50ad1cff0b0000000300000000f8b6028402c9ee9683024b46943a23f943181408eac424116af7b7790c94cb97a58701c6bf52634000b88800000182ad69fa4f0000000000000000000000000000000000000000000000000001c6bf52634000000000000000000000000000b675ed9441dd8ac6d3a206ab61357bcd47384c6c000000000000000000000000000000000000000000000000000000000000e70800000000000000000000000000000000000000000000000000000000000000cd82044d80808493c0e815b42ccfaf9a06a6ccf16f1e445a0412aab8f360dd19929adec226d2431b3cb925d2fc60b325d59f97e35b7b52ea4ef006860596f1b9d79afcf1d0181bfff9010d821ed884016548d88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13d980000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009815ae8ed77f3c000000000000000000000000000000000000000000000000000000000000000082044d80808cfb084863d1694fdd8775bf4fac4410b1b9cf98c1201ba0741106879be8d41f36ca97ecaa1eb058f23208921fcfdde1cf82299c336c2e9303c15865d9a036311bff0b0000000300000000ee8308be5e8403fcd02082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080a3271eeb3e1a9ca9fda4117e8abb72638a128fdce0cb2efb9ef2f870f60b2f672741f52279c993542e2b9d52d8961b2ce486f5498db46deb246b5916a379f8b61babf9014f8301154b84019853408305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13b4200000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000007331f3647a294a90a1ca61c0000000000000000000000000000000000000000adf85b68ca582b83dd4bebe00000082044d8080b8cb2d6ac52d01ef955c17584d1abf7462b3014aedde6b0beeac237e3ff3bc104ae81fd1a2adbc78ec0681dfaf235f3ae18164c3af60a6f02dc9f6100818119f1bfff901ce82989884015445608305ea4c948ff6508bdd71b535df073920c423e9eaee0b97af80b901a4b61d27f60000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f40000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e13c6d00000000000000000000000000000000000000000000000001634ada69799d20000000000000000000000000000000000000000000000000000000000df1806400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080768c51f79db3186f54b0754da1b78ab5a79b6fdf23109910fd4cd6cd9d7f3c8c22e2c8aa620d0d71b4b842da4b0742a4e67da2a0df0aaa08b3cb5d4d1a5955931cfff9012f83018d5e840154456083069df4941b81d678ffb9c0263b24a97847620c99d213eb1480b90104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f400000000000000000000000023abcb82f468e3422a7212d4ad47cc2dd39162a30000000000000000000000000000000000000000000000000000000066e13c6e00000000000000000000000000000000000000000000000001634985c6047d20000000000000000000000000000000000000000000000000000000000df17315000000000000000000000000000000000000000000000000000000000000000082044d8080e56167524cec085263787a173ce728e393d5f361abcfcb92098b68de4fb3a7bc72ae91803810eaafb610b3c66b581c91c6474b137de853eacc2c8d7b73f5b8da1bff"), + ForcedGlobalExitRoot: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + ForcedTimestamp: 0, + ForcedBlockHashL1: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + }, + { + Transactions: common.FromHex("0x0b0000000300000000f86b825b2a8401764c50830d57b69498d7c628086262ea7bd83a23ccbc72f8cd10cd8480b842d43b9dcbb61e6ccfbcfef9f21e1bb5064f1cb33f0503849c0ae884bfdc14dddeb7cae95494f3684148550102d6efe114c9b6058a20aab759e064f50544590914050382044d8080eed135980b8038da4e871a8e95e5644a40cb85fc3f97c42a3b8c9aeac897dad368eff35e0c6aa77b54885fbe3528e9b9a9e5193d3142d67226825f6e2b5fd8251bfff906ae8216d4840176a36c831396f094063bac84221b5b02b92ceb740ac129db0edfedc780b90684f740f3280000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000005e4e7d7ed2800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000003000000000000000000000000d63b7691dd98fa89a2ea5e1604700489c585aa7b000000000000000000000000d63b7691dd98fa89a2ea5e1604700489c585aa7b000000000000000000000000d63b7691dd98fa89a2ea5e1604700489c585aa7b000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb140000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb1400000000000000000000000098299ead1cd04fbf1659799b14a559206f3ed165000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000066ea75c3000000000000000000000000000000000000000000000000000000000000002b4f9a0e7fd2bf6067db6994cf12e4495df938e6e90001f4a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000066ea75c3000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500006437eaa0ef3549a5bb7d431be78a3d99bd360d19e500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000066ea75c3000000000000000000000000000000000000000000000000000000000000002b37eaa0ef3549a5bb7d431be78a3d99bd360d19e50001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080746ae602ceaec3387a089510db6ba15a6a45a6b60aeb564d2af783a2249308d935f550046db0988101d4d533b7d390b764e1bc7afc641fdd957f1c421e5c50491cff0b00000003000000000b0000000300000000ee8308be5f840158d94082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080fb504691bbf5c90ff09420f32385c4df07792dd1f0017568c765f1a180386bd54529e1b0819171dbced80e60b965c78a1afd887ad60450c0851fc4472a1aed711baaf8ee83019e5584019dd18083035f30945eb6b3db915d29fc624b8a0e42ac029e36a1d86b80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000ca0000000000000000000000000000000000000000000000012fb937557964d13c00000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000000001082044d8080bff5978438909bd3d0bf26d170678ba5ab948afebb9ed51824a16fca21bc2196068165970f68315889bcd05a63d9f7405722b2f0fdaceda25eb59c93b897e6d41cff0b0000000300000000f9028d818f840158d9408303a82b94678aa4bf4e210cf2166753e054d5b7c31cc7fa8680b902645ae401dc00000000000000000000000000000000000000000000000000000191dfe1d94000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000104b858183f0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000004786e420000000000000000000000000000000000000000000000000070ec0f4c9ee97e000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004449404b7c0000000000000000000000000000000000000000000000000070ec0f4c9ee97e0000000000000000000000004abf4710e38fd914212ec0d30026d0fa9d51d40d0000000000000000000000000000000000000000000000000000000082044d8080d6fefcfe30a6a3903219dbcf5937bfd3ee6ee34564e643242a790393324905217ad5bb7e3950286ec8f627314e6e18d8715ac80379c8f7d36eda1672d0bb69481cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ed9840156dd708307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13db60000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c8260d3d200c7d000000000000000000000000000000000000000000000000000000000000000082044d8080098be2bc098d407c1a9d7cadf5840c2c250e338538dc1ae0f5df4ed073f8a85414e47ece3ccc969f95ce9251738051c03ae40575075b9b4b4309210dcfbe44411cff0b00000003000000000b0000000300000000ee821a198401793280825be09482ce01bba0c29bf9b61dc9cfa7968b1e0891f5678702f89f754a380d8082044d8080b2c54612390fc975b7d024f6e8eed9942a73c0d2101ebb04df518e3744db33b4009c3b2e21252b7c462b71a2c34f8a40ca9f84bb30ab6fac7b692840b383dc481cab0b00000003000000000b0000000300000000f9070e8201138402d33c58831d10fc946b2c0c7be2048daa9b5527982c29f48062b34d5880b906e4b80c2f0900000000000000000000000000000000000000000000000000313282fe1a7140000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e5000000000000000000000000000000000000000000000000000000000025b4a900000000000000000000000000000000000000000000000000000000002548190000000000000000000000000000000000000000000000000000000066e1497b0000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000006c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000025b4a9000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a500000000000000000000000000000000000000000000000000000000000000018000000000000000000027109591b8a30c3a52256ea93e98da49ee43afa136a80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a5000000000000000000000000000000000000000000000000000000000000000100000000000000000000271007a42e9f31c1fb7c2056b040e55e35c2ea6744770000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e5000000000000000000000000000000000000000000000000000000000000000082044d808057853a22fce3f85fcdd486d18e841d491de09b35e2151168a3af8bd9352e5e616a70f07e3aad14a6760f82ccb45077001a395a1c9a5556c0d33241225108ab081bff0b00000003000000000b00000004000000000b00000003000000000b00000003000000000b0000000300000000f9010d821eda8401607a608307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13dd40000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000981eaf19eb97c6000000000000000000000000000000000000000000000000000000000000000082044d8080bbaae2e5f840a60b75b397d51ffd3f6c648b7077d6d6091491237e9efb69d8c91620f7793c4dddb315cf997662a77b2c48cbbf881985c642d3cb63c4528a02b11bff0b00000003000000000b0000000300000000f02f8402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109875543df729c0000840333e82082044d8080e79b7308819654ce4d40697f7c1f044a00ec7796c73596957de663e9274d072a76e03b8eb9732d95e673501ed9cad4a63a32d33aa256caafc4f1867fb13eacc41cfff0268402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109873c6568f12e8000840333e82482044d80802cdb77771d166fa9140bd3ea0d17bdc5eeac3836d05d6144612c0434af52b5db2aeac6d0a8bdead3238ee12124dd9f387388ab5b52ab1100a1dadd6a8e9b740d1cfff01f8402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109874a9b6384488000840333e82382044d80806cd1e42d4d065497ce59e511d21e6d5819bf91ea9fa250786b757e0705f356eb5c43ae1da48a134429a5584590f1e4580d103310c3806cfde466a9c5231e35eb1cfff0218402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109873ff2e795f50000840333e82282044d808084e8d0f689fe1d5c190a4247b452a33fd6735de9d5f9f5003d8601d5b0c707d631c33cc82b2690e5df6efa0c2f44d68f62d47cd0922a94f802cdbedb115ee30a1cfff9018f830151d284018c1e408302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa0000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c201fab6ebde597669e39a9a69d724998e50b2f8000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000001630804d32c10000000000000000000000000000000000000000000000000000000000000001b75f9deb5f8d72d9cd3ea0037c11ac2401d310730bc692f37ec19861105e08c582044d808002793ca3270d2bdb8a4efb0eea532dec733de8f35e5a1c3f90bf185ed69c73a973cc8df40252607608de8728eb43d5e9fccbdabc4fce80c5d4fc858a36e928ad1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ea81808402625a0082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d80808805469c3fb84413a35485f506012ad1a6820b80738b3bf685b1d6175b9dfc79397f9b692337a0f633040da8401f8a5fc53852106d916911af513ff271e4aa261bff0b00000003000000000b0000000300000000f9010d821edb84017a1ce08307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13df20000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c82f0c5cacca38000000000000000000000000000000000000000000000000000000000000000082044d8080baa772220471b8149d55e5a5ac2bfbdd12c03eaeec654e08d9c74ec43b63d12a469bb9de8bf8ff2cd2edd8926daf89b887df334862e623a68cdc3b90bf7ba7391cff0b0000000300000000f84a318401681b808312c61094ee1727f5074e747716637e1776b7f7c7133f16b180a4db006a750000000000000000000000000000000000000000000000000000000008f58afa82044d80809489b2c6a4c5dc17ec3ec50d299e487c996839ed4d26bcc4c07f910dc21a53c04ff08798dea40e018cbd296ffa623776cc5382be8e8964a7383b1c8b9717ba2b1bcf"), + ForcedGlobalExitRoot: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + ForcedTimestamp: 0, + ForcedBlockHashL1: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + }, + { + Transactions: common.FromHex("0x0b00000001000000000b0000000300000000f86a228401681b8082b4e5940d1e753a25ebda689453309112904807625befbe80b844095ea7b30000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b500000000000000000000000000000000000000000000000026e055297acfc7bb82044d80802d542b44996d63d0e2ee4f8063c5c21b00ed851ab3f63911b2e0d4f9422287497b5085cca5dab841c5406c9bca6745b50a28a543431c355ee63d7e469e7b72971bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903ce821bc68405f5e1008307a12094d83729a90474d6ae09502ee3c58485cef40ac87280b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300010001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000093136a0f83405fd54b230fa9778a13300002cdfe0409010405000602070308000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000038408d1790000000000000000000000000000000000000000000000000000000384669fe00000000000000000000000000000000000000000000000000000000384669fe00000000000000000000000000000000000000000000000000000000384941d490000000000000000000000000000000000000000000000000000000384f1bda900000000000000000000000000000000000000000000000000000003850842c800000000000000000000000000000000000000000000000000000003851ac5b600000000000000000000000000000000000000000000000000000003851ac5b600000000000000000000000000000000000000000000000000000003853adfd00000000000000000000000000000000000000000000000000000000385578f900000000000000000000000000000000000000000000000000000000000000004208140e5e3dac54f5eb8cd8211adb203b85cc8f48b5d7d5768ecae9622df24a03866b2325c2c74955907e637e920627241ee7d4220e6aa6fe4b474fa0a1467c4ed42c8afac6f30478534bfd8a1b58ede8bfbef3d1908c0288a743fb6aeb7c12ae89fd84ca1a67ee72ea3d922c892a66e7caa0994879cace58eaf8e30ec0b641c000000000000000000000000000000000000000000000000000000000000000469aea40a060cd08cdc062fcb2e3a8b1b45faa0ff08b58607ff6941f18a2561184328d4b3414cda5fc8a80dcd28dee321564a1c1eaffabd2601892446321625b1324fff5e231ca6e5a020a57e4b2c8b83eac3067b76fcdfe8f990509b8bd6ba882f431699ec517fa2f99ea179d76f3f872438f6d787d9e6704c52145bc16b31b082044d808039cd808d19202040e8c498c2ab13bcdf3af0b148bac34a026b1925ccf02cdd89763a910a26e0d8c91449b9d39d62aa35f116b0f303de9ddbe0adb211226be4bf1bfff9010d821edc84015d46108307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13e100000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c7fc160522df2f000000000000000000000000000000000000000000000000000000000000000082044d8080bc426dda601756b4851b479be56dca7cbdcaca215314342d4e5cbefc7caeaa3b2115f87f94ec752953f052e46ffb2dea9db847ebabf21a239845e387e5d24fad1bfff86c82011484014ca44082ebf49437eaa0ef3549a5bb7d431be78a3d99bd360d19e580b844095ea7b30000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae0000000000000000000000000000000000000000000000000000000000f0719a82044d8080511a80bfb3d3116410992606d87913954e8731ed90a5131f21de84e035939b8273d0b786dc03ce855c47f5af2ca93567b0db3ed5fddb3f6cf5c19f8930e7f9d11cfff903ce8222f28405f5e1008307a120942e8932d5662ad426fe335162b233680a524395ee80b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000086e0a443f1c658191e265ab8658a37b300000a6c0405010609000708030402000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000023b4b9200000000000000000000000000000000000000000000000000000000023b749c00000000000000000000000000000000000000000000000000000000023b749c00000000000000000000000000000000000000000000000000000000023b749c00000000000000000000000000000000000000000000000000000000023b8a3900000000000000000000000000000000000000000000000000000000023bcb6800000000000000000000000000000000000000000000000000000000023cc1da00000000000000000000000000000000000000000000000000000000023db84b00000000000000000000000000000000000000000000000000000000023db84b00000000000000000000000000000000000000000000000000000000023f6bdf00000000000000000000000000000000000000000000000000000000000000043ff63dc58a98b2caba46ba7a58cfa6b0378efd7ffd05ca878dcb191d92a850679b626656e08c2977addd9799a357347e31d4965d125bbe06a7557899cbe5a7206c636624e5636f4f3d669960a863e778fc8a1d29779990238c08bc3a3f5df2908936ae2660fed387a6272501cd4e7f16a25ef0a5c05cce014666dacfb6e85cb0000000000000000000000000000000000000000000000000000000000000000456cd47dcbb6b4c21b5c5978aa6f26fdda9fc62555429ef0a40b98205fbd86617648c9b624b5c64efe94ed0c11d54cd14a1ef04c3bb18f4d0945a8e88ec84fd62470136dc0befd1267388d059cd270f7f0a706eded9d9d4c6f4578fa869525ef45073a43bb30b1cbaa3f4affad5b05288b4e0a8f0724deba4e6024f235cd711f182044d8080bf100f793640914b3157343b839411911773ed9a2a463b79f1ae0d6295f64f3f105d96335e24c7519de149fb1767d2c21c25f7ff0718c9ac0acd017449d799141bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f870058401a17b0082bd6194555a64968e4803e27669d64e349ef3d18fca089586b1aca8f532dab844e56461ad000000000000000000000000000000000000000000000000000000000000a4b10000000000000000000000006587b69f97af1c9e91455401cd3ea5594590bbf982044d8080fccdafcb3644954a5a889c5a673c6180b646da17f832cd853a74a6c2413cd51169f1cfabe17e29cdaebbcfd961d06fb56bb2da7b4b11f7cbaf137e84ec327a9f1cfff03284015be680828d9794a06e1351e2fd2d45b5d35633ca7ecf328684a109878a6f0ad96bd73e840333e8c682044d8080f16b6fa1af75307cdea0f6fa7170d8c353bbdc1af5099bd4b4b69f1ee48a4aba2fecbab3e4eeda03128b8308f6becfd6fddee743bde4b24bc7fe32fc0c9fee2a1bff0b00000003000000000b0000000300000000f903ce8222f38405f5e1008307a120940e7197e7200a706e4b8e0ecfd39ef8c45cff142280b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000b62c61f914e58e40747a3fd731525dd20002ce3b0308090106020403070005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000003e5e21b4000000000000000000000000000000000000000000000000000000003e5e21b4000000000000000000000000000000000000000000000000000000003e5e33a8000000000000000000000000000000000000000000000000000000003e5e6e40000000000000000000000000000000000000000000000000000000003e5f4130000000000000000000000000000000000000000000000000000000003e5f4130000000000000000000000000000000000000000000000000000000003e5f5904000000000000000000000000000000000000000000000000000000003e5f5904000000000000000000000000000000000000000000000000000000003e60a0c0000000000000000000000000000000000000000000000000000000003e60a0c0000000000000000000000000000000000000000000000000000000000000000427c6181796e378ecce0eb4756eb5cb3cb22c2f5c31ec76afbb61cdfc9f96c2999ac5dc98c1619fa6401ffa39d5343d7d5b3d32c7c84f294e8caea56fbd7912240fd00d9938f0a0f896ad6a9aaaad9ec782ae47546019d0b2bd0be88287821fe235539a12cc61ddf8f127b192a2724d095088635a3acaded33c14f154ca65ead300000000000000000000000000000000000000000000000000000000000000040217a78ca7b9fc8eed7813085e982f6e8d699f8d548699c4c401f9b74f2bce452d0d3f0f47a6220d36ac59f3c8cddaf9a270e932a234e473bb8507e1a7bd5b6b33cce711f84eaeda3afd58b99e2149fd24ec6aa607bae119a4b358c204fd90b6414015a5b4d6efe234bfb3d0e3b2e1042364cce80bf3286ea367d06ad7f0e27282044d808072d67c06cd88db7a351633d59042a3909e3564f6d707240357850526ee6c561a60ce10685dec1ad08cde99aaa47a3bcbd8f7bc647e08375d873f7a2584478b391cff0b0000000400000000ee8308be6084044f35e082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080813ec45766e4b4c8838d0f69507e663a3ae1280bc1021a063d71381ecd29f84302e1bcc769c6d76488c0346c2069ad8d142fcbdeb8a15ec4f729a2a14c0515141caa0b0000000300000000f9064e82011584016fbca0831143e7941231deb6f5749ef6ce6943a275a1d3e7486f4eae80b90624ca360ae0000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000631cd561b0c80000000000000000000000000000000000000000000000000017769505dbc5db0000000000000000000000000000000000000000000000000000000066e5304f0000000000000000000000000000000000000000000000000017769505dbc5db0000000000000000000000000000000000000000000000000000000066e5304f000000000000000000000000bd72882120508518fcba2ae58e134ecead18d979000000000000000000000000710bda329b2a6224e4b44833de30f38e7f81d5640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039400c73445066cf0991b56490031d9a8241645cd8c8532c84dccfc1a95f295600000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002fa5fcc93ac9b373ef0065708330f7cac493fea70000000000000000000000000000000000000000000000000017dfac08f2f2400000000000000000000000000000000000000000000000000000000000002105000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003686f700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6a756d7065722e65786368616e676500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000057bffa72db682f7eb6c132dae03ff36bbeb0c45900000000000000000000000057bffa72db682f7eb6c132dae03ff36bbeb0c45900000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0719a00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001a42646478b00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000000000000000000000000000000000000000f0719a000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000017dfac08f2f2400000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000b20237eaa0ef3549a5bb7d431be78a3d99bd360d19e501ffff01849c0ae884bfdc14dddeb7cae95494f3684148550157bffa72db682f7eb6c132dae03ff36bbeb0c45901a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03501ffff019f37552b87b68e7f169c442d595c1be7a0f03b920057bffa72db682f7eb6c132dae03ff36bbeb0c459014f9a0e7fd2bf6067db6994cf12e4495df938e6e901ffff02001231deb6f5749ef6ce6943a275a1d3e7486f4eae00000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d80802787b973f4d54062c810118dd824524f22139a2f95fc0c1847abfa0251e073ab71313f805daf772a06c1873d8390356030408d7cfaef18b7200e9f246c2012e41bff0b0000000300000000f9028e82393b843b9aca00830667f8943a464f746d23ab22155710f44db16dca53e0775e80b902647c39d130000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000012700000010000000100000001000000127000000000000000000000010000001170000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76c46702575bb24a61a3c6674a33b1c536418d418d7a2a5454760d0425f62a69690005804c0f4171353a8d70c267c85f228b5795422bb97e8701f57060b3afcacbda96828451185401ac923c55ed67ced54306dd4b6391546b221f111a5a62b01bc3c144011bb991df3e06a3f746ab38f98556f8e11b36c05ceb94aad204dffa5c664e9bd463791cf2b3fe4687153754968af9bcd9a70b8d25d31afef5e268e9aabb50aadf7d1ce0391fbf3eb6e6cf69572dd19bf6fc5081a462f752638d846b9d0862a6cc1dd706aadce7a16fa076ac160a7a7e59233f0659153b2efe2c60c5f302eb7cddaa4a1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d030005804c000827500000000000000000000000003ddc189d48a792d2c6262d20aa0f9f78e88a9d470000044d0000000000000000000000006ae540f1c3a96a8628d52b416ec1bf72932f83fe0000000000000000000000000000000000000000000000000000000001c9c409000000000000000000000000d2799cd96798074f709423369bf2fea60392852a0000000000000000000000000000000000000082044d80805b9b901758bfbabee0b9f3477ea901ad9b36199c881c5fbcb1fa4d241495f353045c800c11c133aab3e1fe1dd8de12d11392a42fffef25507c70ded109ad73891bffee8308be6184014b1da082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080406d26cc0725e8e4ec416f42fd563433cafed607432c1bf19783e749e2a8ad8939ea1ad189a766c6759c4a5210377b7cf68cff953b4447bf715010dba204d3dd1cab0b00000003000000000b00000003000000000b0000000300000000f9024f830151d384016c3a308305cae894555a64968e4803e27669d64e349ef3d18fca089580b902240ddedd8400000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000001ee4c000000000000000000000000000000000000000000000000005d423c655aa0000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000662d6dda64cfc7fc21d2b1005f406e984a178d4b0000000000000000000000009478dcdb5e5c190fb37f70358585430e1bb1168e000000000000000000000000199bee3bc56add7f1a6ad1995002730de45886d60000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000015b9a6cdab510000000000000000000000000000000000000000000000000000168458403b1f00000000000000000000000000000000000000000000000000001377a57a748600000000000000000000000000000000000000000000000000000000000000039dfb46e76740a84405448ea66a1d95618c9528481cd2730100d191ce78a360a487e98381ed3d5acc9ec5f7f31382ed1c794017648b3c7d25e8a8818173a65b8e690d56cc7f2e91fb3994a585e6553c4ce62cda42398be3ba59f1bd342f43dc4f82044d8080ea85b1543ba847d93e70fb47b0750c5197ebdf75f4efa28b37da97e41e68c12a57b1de35882e372f52bf5a16179a4ac2e453025d3b4731b212be3c2ddb3354b71cfff908cc2384016fbca083053197946131b5fae19ea4f9d964eac0408e4408b66337b580b908a4e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000006200000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000d1e753a25ebda689453309112904807625befbe000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000f91f8300f8200f43eecf89e7229f257f085ecad50000000000000000000000000000000000000000000000000000000066e1407c00000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004063407a490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca300000000000000000000000058684788c718d0cfec837ff65adda6c8721fe1e90000000000000000000000000d1e753a25ebda689453309112904807625befbe0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000000000000000000000000000026e055297acfc7bb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060a4587899c58e7cc2e54303188f56acecaea4779400000000000000001388000100000000000000000000000076ae48a40000000000000000000712ecdbf54b4d00000000000000000000000048b8419b2bc0fb63ee96e3a370e30b200cc2e6720000000000000000000000000d1e753a25ebda689453309112904807625befbe000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f91f8300f8200f43eecf89e7229f257f085ecad500000000000000000000000000000000000000000000000026e055297acfc7bb00000000000000000000000000000000000000000000000000070f4daa5dc3780000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000026e055297acfc7bb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022e7b22536f75726365223a22446566694c6c616d61222c22416d6f756e74496e555344223a22342e353935383138333735373437393135222c22416d6f756e744f7574555344223a22342e363531333730393136353932343332222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a2231393931313333333438383432333137222c2254696d657374616d70223a313732363033363934302c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a224968587873792f325642344f4a6366357074564f4a524472566676336c7941426173386e4e3151446e537770426b384765546b6d79637532347a4d4f58535a6b5059672b4d53394a6374336c79587745766b3679382b572b6b303643332b6b4369753337654d49484b47436c6136742f7377416b72625773337177322f474378536639325973353038754f764e6446624f5877705a6c3374794d30745833574a724c616551426c4d42466b77664f5632445168596c32716e41494e344563655a574c4a456a555865417545694a62685a39534844312b76746a4b457452355056796a4472727563476b54634d35745739523549755653554f754e6434632f39766c537269343569464234456f37316e424931624d2b446e78495837563137792b41634d6d585235336d7a685645476b59647848745a6e706f336b776f683841356751786148433678362f4a4d6e56686a5049716877673d3d227d7d00000000000000000000000000000000000082044d80802ca8348995a238faba4d03fc97638947ea65044c08547e8bb931231fb982886b1e4851515149a0b4f1e5131cf5f123de352c7172839852eef079aeb26bdcba361cff0b0000000300000000f86a0b84014b1da082b4cb94c5015b9d9161dca7e18e32f6f25c4ad850731fd480b844095ea7b30000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae00000000000000000000000000000000000000000000000098dbf0792f215d3882044d8080e1a347db720494ebbc892aefb0db8be1c1a4f61b3310d9528abde1ae2ee1cdde24efc0ecb27d55991eb644ba770a91c52ab8e69616ae78fdd3557d4c7122fec91cfff8b10784014b1da0830151b3942a66b9c88caebe9aefc4ecd7b5e7e60881f7e15c860d18c2e28000b884056b01ce00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000007ce66c50e2840000000000000000000000000000000000000000000000000000000000000000000a63727970746f72616e6b0000000000000000000000000000000000000000000082044d8080cf67643e57fe0e3d537c2c8fe40440a97de31e6606b6968a34ce382900b587c128946d1e7505038bf50427502088c8de36e7ebe911171ce1288c5bec04d91f521cff0b00000003000000000b0000000300000000ea10840c49018d830135909468c89f28d223be23967480c06057d22b3400293580845646c6b982044d808002b9b94c0a76cc3e16e09a8b9e9506f4d3f57ab9e8932717c80659bcc9837d0e631d462ca21abb98fd5a0c63877e2d59a8d332c10d30569ced47e5924f2100ef1cff0b00000003000000000b0000000300000000f9068c0c8401e915ba8307f1fc941231deb6f5749ef6ce6943a275a1d3e7486f4eae80b906642c57e88435f1a3a9536b95a72ff7601f7df30a5b8da95a0580587d62f17457034d663d6400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000002ac8900f549d90f11aa593db90a0fb57e753b805000000000000000000000000000000000000000000000000001087372423cc6f000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000086d746f7073776170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863346639646134323361626336393237393937383338313533656131386631616266396435613938000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000b49ead76fe09967d7ca0dbcef3c3a06eb3aa0cb4000000000000000000000000b49ead76fe09967d7ca0dbcef3c3a06eb3aa0cb4000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd400000000000000000000000000000000000000000000000098dbf0792f215d3800000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000084eedd56e1000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000000000000000000000000000004e438d8deb1321000000000000000000000000000000000000000000000000001390e3637ac4c8000000000000000000000000c4f9da423abc6927997838153ea18f1abf9d5a980000000000000000000000000000000000000000000000000000000000000000000000000000000057bffa72db682f7eb6c132dae03ff36bbeb0c45900000000000000000000000057bffa72db682f7eb6c132dae03ff36bbeb0c459000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000987a1c083dbb854f00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e42646478b000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000000000000000000000000000987a1c083dbb854f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000000001087372423cc6f0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000f402c5015b9d9161dca7e18e32f6f25c4ad850731fd401ffff019bc342259cceda0487e70b71a3161f002a95f0e80057bffa72db682f7eb6c132dae03ff36bbeb0c45901a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03501ffff01ca06375be938a2d6ef311dfafab7e326d55d23cc0057bffa72db682f7eb6c132dae03ff36bbeb0c459011e4a5963abfd975d8c9021ce480b42188849d41d01ffff01337af062ce32bb423010415196e315c4154d36c30157bffa72db682f7eb6c132dae03ff36bbeb0c459014f9a0e7fd2bf6067db6994cf12e4495df938e6e901ffff02001231deb6f5749ef6ce6943a275a1d3e7486f4eae0000000000000000000000000000000000000000000000000000000000000000000000000000000082044d808097c0b85dae5e497dc2def38b73fa7db167176b675e1ca0b9b5fb7acdaed7d33b3b01365195f76cdb02373c4d9553f9e6093d77ef79fb4980b74383688d7a06951bfff903f22f8401c9c38083048020941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5ab9c89ffcc1119514c1b92d64eb03aa97ed8fd52866dcd07383c2c7d4af347a5000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000009e47fbb2a2a27b3b02e4a63b3ef5a3dc863c02230000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d52696a487341633572727a65414e584d735334725a68555a6b576e35386151384a6776523832786a4351374d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000418824be9a966bd0478025bd02a796118c2fb8a23756cbbc2f82d61314f9c6d9d3341db0d02c63f9530b2ecc581ecec5acc10a6ff8a641d52071a0ff8421ce59701b0000000000000000000000000000000000000000000000000000000000000082044d80807034d54692cf1b649157e81c0bdf66632633f6485cdf329ceefb5d55042d38760b6da7e01b7a770f453bec69609332f517ac66c5e4b7798e75abef875a3791651cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f902d3138402625a008305aaaa947481c16e7782608ccba70029c0fd41d78aa6b56e87027ca57357c000b902a43593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e13d1100000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000027ca57357c0000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000027ca57357c000000000000000000000000000000000000000000000000000000000000018d0f800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000424f9a0e7fd2bf6067db6994cf12e4495df938e6e9000bb81e4a5963abfd975d8c9021ce480b42188849d41d000064a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000082044d80803baf44841d975b6bc565ace7c443e7ae7d9ddac53580a8a85ae9ed88c26907ee1e81aed1a48561a8238e7476616c026622e9e207b4a337dff4e87be321a5c4f41cff0b00000003000000000b0000000300000000f903f38190840189ad4083034cca941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f53796b8092eeb16d8bd97e1af7a200666a05419e70bad6c72d92f0e2af24f0d5c000000000000000000000000000000000000000000000000000044364c5bb00000000000000000000000000013988cef5721a77e9a873b9c988559abd698516a0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065a6572696f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5441676266396a785343475176785a476e6576435437394c334d7258794d6d35715957687a68476d3646586b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f95331b37d73776f15d46c38bdbe349eccc9f1f7928fb41f54b8fb9fd5228843643976c760da92db0f41c7b1506e1fc66bbb01f8b2dc70e5a96ce3cd0502b5351c0000000000000000000000000000000000000000000000000000000000000082044d808081ad4be405410a00887985f909b52ceefbf5b7414500559946678f167ec95b646c37fff789b69860bb5850da757344ac27d1c45c9b8c4374d7eafbe2d3d8b9141bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ec39840175d72082520894e4edb277e41dc89ab076a1f049f4a3efa700bce88705692911fe533b8082044d808000315fc52bc3d2f4aea5fd2de0229fae82cd273efe66b698c4ea990e20b60fde5ad8c57f6f5748d6b2709cc620626f50e34a577e7b30bbc2fbfbe8342793af081caa0b00000004000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ea2c840d42700b83013590948418962b8e0c95f386f89415d99883cd52226f808084fec0921482044d8080783253c33c7b79ecb24aa939f976a21069d7837a666da00eb8a2eacb5298d4457566c70d92178084f27c8538eab68e46df3d914512fdb1032ae5933071f699541bff0b0000000300000000f9014f8301154c8401b021008305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13c3100000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000733f79f5a91ea2abe3e50940000000000000000000000000000000000000000ae01687505aa45399c5393c00000082044d8080da4584b7d6fc09c1e15ad4cdc966ebede2301b221fe74a64f3b4b077c6b7d2bf2d38c74317a7901d41676c66859e0e26b28a4ce29ac544f67d8d2cb8b503e0481bff0b00000003000000000b00000003000000000b0000000300000000f903f381978401681b8083030015941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5e88a8ba0ef0f81c929520cdcdfaabfaa6b722c4048cec8c493456ad0ab0c325d000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000007df220262a4c9ca1fa9b7224bc6f7f4572a892cc0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008496e6a65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d506f336736676d4a72386d6b6467384a57764859576b4659486d4b47485777436d425175473276416a44315300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041bdf0d198c5f6ad13e9fb6c891a05b67ccced70655831525ee9febd3c21cb33595c536eb39f577fae252fb903477529ee1d4a5d15ae9731d06543a56bfe1fecdd1b0000000000000000000000000000000000000000000000000000000000000082044d80809de5af8deb33a1a58f32c6dcdac7c610ff048603e566c8cd0cb4fd54b65a38934d59fdd44897d94a3be46fca792bd7a75e88e363289921a8c1096967b30b62441cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ea818b8402faf08082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080612819e135e75602473c7c42e3a2fe293cf9e99ce22373be8ea63724f3381f6f746b65a1f24f8f3abf355f4c8dacdb2b96b48befa479cc96881163cc827e14841bff0b0000000300000000f903f2348401745080830343f1941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f545b5e3028d7045b7f9dea45daaa86de008257fba39d272d868c9d8edaf813fb4000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000005f9c7447129fa10af4cb51e24be323abff1759e50000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5138456f61454658787954393331636d42684653426447486168444d684d353473484b4a745a417072507168000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414b2ae38ceaeb1af116941ed4ec6eb66a86116d4d08a4920ac07d1feaa8ee607d6d5b635040118c5c37fa9f05b6c8a4cc07c3619a8c0def6821827ee9539530e81c0000000000000000000000000000000000000000000000000000000000000082044d80805aaeda75850432083fdb91bd4b910a0850c50f29c6a81e7f5661c0a0e19c2b282a15c71aeaa646bec001c6421af6b380d7321cdec496258e1a893529de2fa4621cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be62840418476082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080b7a139b28d8c5192cab1a5c55a8efcc93cebddfcdb1a0376188144df587e57b1302f1ec53be25bc03bf9fd84b1bc298006b6d0a3b4cb296342bcabd8254f9a4c1bab0b0000000300000000f8f108840174508083034de3942a66b9c88caebe9aefc4ecd7b5e7e60881f7e15c86a0285cf72142b8c4d559ccbb00000000000000000000000000000000000000000000000000000000000009dd000000000000000000000000422f746a9d5b73e25d41dcc18fc1ea09da88ee2a0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000a63727970746f72616e6b0000000000000000000000000000000000000000000082044d8080463a0e84cc018410c62c0f6cb7a9055556b94cf37fb5363e004e2952831ee3a109837118f4ea90e6753e6f16dc192a72053fc655a789d15767496bcdd45e5b961cff0b0000000300000000ee8308be63840169a22082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080dcada0e0a24e607958874f9206c0a0540fa170176d694c6c1d0546eb6d1b787b4b63a62905b6a8b302168d6a12fdec6a5c89b10ddadabd6ccf3d0f099ac835271babf28231db84ee6b280082627094580b1a559eca8c875381251f291f96777ef3af89871715c995317000840333e95a82044d8080f5a183cd5ecb9ccfb91a8c22d2a14191a32c2325742e13b48b8ab6d2b1ac8af65a2dda29b70368485ae17017f0c8570850b37600fc668c6547aa69cde7e2f6721cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000e90284015752a08255f094e10add2ad591a7ac3ca46788a06290de017b9fb48084632a9a5282044d8080aeb52a12273e7d18d4d61292b972b9cc460236c359d0b290d7440e6a9891e8f37ef89ab59bdaed7e90a33fd03dce86ee855f91a6afe8515009b4dc2542fc315d1bff0b00000004000000000b00000003000000000b00000003000000000b0000000300000000f9010d821edd84016bb1788307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13ee20000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c8390bcde7c066000000000000000000000000000000000000000000000000000000000000000082044d808030d0dce5dce8bd5dac0a2de99f3a997bc9b2720b37e0c916d521614aa96b2b295d3ed3ff7c6be137f9e895f4a0736de165dc14c87878a1d8b01222026689675c1cff"), + ForcedGlobalExitRoot: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + ForcedTimestamp: 0, + ForcedBlockHashL1: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + }, + { + Transactions: common.FromHex("0x0b0000000200000000f9010d821ede84016ee5c88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13e4c0000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c805935ba60ef3000000000000000000000000000000000000000000000000000000000000000082044d8080e6523db0d8d9f0fc01b8d28d354646bb37ee3a01d94299baf62a3f0513c1cd5b4706f80c68a13689d843d784ccbad2a69c049b87cf72f7a244bab58b25d3f2ad1bfff9010d821edf8401721a188307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13e6a0000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c7f7976b336093000000000000000000000000000000000000000000000000000000000000000082044d80805f7ba49687fc0feb05f0fb1b46b605610f94161628bb0d78a593085bb2d6328d67d03a13c5f7b0d080fc8585f1dc01eb567f4ec6dc22fe372d0dad7b2057b3aa1bff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000eb82013a8402625a0082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080a1e4294a95f81c0195243e271861ea2cda2b25132a4e4866cb58deb993128035280b82205bf5f93e6cd481ea9ce1b4408428898fc5813bd8bf58df3199de169f1cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000e9028402d301b68255f094e10add2ad591a7ac3ca46788a06290de017b9fb48084632a9a5282044d80803516937610ade172676e6ab42573bbde2ba8b143b82c019640b2c41168bc7e445254048ff8978b561f0a95d2d71f4da3d8c67c7f05cc90ed9521cd01e6da0c2a1bff0b0000000400000000f903ce8226f38405f5e1008307a1209467d0809ada627ab5bd623640332a2f370b2c906f80b903a4c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000300010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000084049d6baf6f3253bbdf284da6b5d1370002ce300301000706050902030408000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000005f5b5a40000000000000000000000000000000000000000000000000000000005f5b6080000000000000000000000000000000000000000000000000000000005f5b6080000000000000000000000000000000000000000000000000000000005f5cb840000000000000000000000000000000000000000000000000000000005f5ce0f0000000000000000000000000000000000000000000000000000000005f5dcbb0000000000000000000000000000000000000000000000000000000005f5dd180000000000000000000000000000000000000000000000000000000005f5dd180000000000000000000000000000000000000000000000000000000005f5dd180000000000000000000000000000000000000000000000000000000005f5dd18000000000000000000000000000000000000000000000000000000000000000420f5d5e5132ac16831c0c420b2e4348553e2835cf6b7764eefb1908997cc8501dfcf180d7ad72bf3313f016313fe7aa3a134b06b53f4f2317a74b5455878011a986cc2fd0b3eb2e32ac671e13fe59db59688a1f49fd71fc5ea1de3162975a6cd243ab73d9dfb19ed57b626487e0e8d4192de45e5084083cd1a03aeb6b004776b00000000000000000000000000000000000000000000000000000000000000046545e8567be21ffb54df1c55b30908e62cae7ab7acbd840698e425e4b4999ea36d28dd45cd30a8ed96f05762d723d83130ace27284c9bc868bb8f1ccae6726f731b09fe696a4b676a0cfdce2a8d0d58fd9d4aeb116110fe1918a8b4f403db20b35b9625924384aa070bbc25c7c24c2f2f39577af4e2bbbd2699560ee129687ad82044d8080270674afad2582de39abcfe1cb19d8243b9f6d31ac6df02257ee13e2d558e4961656fa5e782a66d6f59ff6881448d38b61fd630bffc81442ce9575081c9ea1be1cff0b00000003000000000b0000000300000000f901ef830151d4840154ba908304aaf894555a64968e4803e27669d64e349ef3d18fca089580b901c40ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000002557c000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000020000000000000000000000001757ee1ca5541182d242027a734cefb3d6a745fa0000000000000000000000008cb1d12040c282ce84f851763e2befc48a0810b10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000015705520846800000000000000000000000000000000000000000000000000001563c0e8ed6a00000000000000000000000000000000000000000000000000000000000000022bbc5cbbf99c0d37f3d2aae6daf5a4d7dfa26f8ad4ea658ceeacd1d16bea82961cb86c8ae18cd27416d0782aff31adf25117d75b85cac5c410df5b8de615c70a82044d808017f2d6241834bf9550f7264c42b27a3d05a2a8fb5315b9d82791edbaac56107a5c926d96702e5c401424c34f351eb84109cafe915de7d7d26636ea9806a814801bff0b00000003000000000b00000003000000000b0000000300000000f86b208408a4f1f8830156219468286607a1d43602d880d349187c3c48c0fd05e680b844095ea7b3000000000000000000000000c8a21fcd5a100c3ecc037c97e2f9c53a8d3a02a100000000000000000000000000000000000000000000000585333fe46a1a1a6c82044d80806b533a403af42468742ae5f3efdc12daf9bd38dfa3059924cd35e9d2d2a377444acf354174f9ca1f414ccedd2e434e0b433ff872f60a20da007ed0f80458d5851bff0b0000000300000000f8b12784021c58208304ac1194222228060e7efbb1d78bb5d454581910e392222286119763fd5872b88432accbb9000000000000000000000000000000000000000000000000000000000000007e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cbba106e000000000000000000000000000f78c3faf2046813f12fe6652ff952b2ba6ba845a82044d80808f22f2de07105da8d5d9dc8edb193fd108c8fd3c4858dc20a23e09acb3995eec3dbd05a55530b38f6b4fd7a1416914f04f13fa6c25bc4b1397a784abe13b15f71cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000eb82013b8402625a00828d4594b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080a54a20ef6241a24e66579a016490ddaa20ac5237cc3920240bea379f56c5f16519284d19c65b19253e95a448bc53b8e8674d4904eca64bfa9d6804a0b21462ec1cff0b00000003000000000b0000000300000000f903f26a840174508083030015941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5445b43df8087dc5aea1da71e16e6be5db16a5a065bb35fc5a7561c4fba8bf78d000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000009cc2cb595f85c9d1c527996555e21327fb5e47440000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008496e6a65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d51715667374e78715a51573550667737417932774a76486a445748577937624a6b567659667974754b47774400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041cf056d549f465af9b6e55e1dc4762e9790374b2bb1e47bf465dff9a977521b291acfbd54361ebf15ca703a29f98ed7de9fcc1739d17123b634f88b24e1e3f5751b0000000000000000000000000000000000000000000000000000000000000082044d8080a67e87448e21458f2e0daa72470c7b3aba821cd5b07d4128054d5ea267941bf06cd1939cada4bb72f53fdbf3f479a9eada26885bb585c42ea28031f4c04c4a2e1cff0b00000003000000000b00000003000000000b0000000300000000f903f2258402d6b0f38309105c941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f57434eed86a2209cb61008d35514b33b5b56d69aa8d6051dcbf2adda360fd7abb000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000132239919653064455f78e683c365bdcefa8bb3d0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4f4b582057616c6c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d52663166534d787a51314b54654d5a4866657635706d736e61724a76725359784a6b7667393973334e7a4c6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004192bbfc4d495eb5a1784f3ff5109e78c2be43fed9874388b78bfa25c3224866d51712a636189146f18ec716c2ddcde6c64df64fe8f2936fc5da46ca656cff2aec1b0000000000000000000000000000000000000000000000000000000000000082044d808084ffe2036a8fa6ea7499f4346870a19b84fc74158e7aa047eb67cc820563e9df5eceae53db779c5ed39afa1177f3a62d9bec678fc3e4bf5ad4bca0a53d6fb9ee1bff0b00000003000000000b00000003000000000b0000000300000000ee81a68402625a00827b0c94009905bf008cca637185eeafe8f51bb56dd2aca7880429d069189e00008082044d80801b0200bc64259973edb845427dee669bba37b97e3ce801142133f133aa7a77e46719187d4f3d66ad9e37983725da1a3061178b2360912a0616e94c51a34bb7161caa0b0000000300000000f9010d821ee084018bbc988307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13f3c0000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009832b42064d1b7000000000000000000000000000000000000000000000000000000000000000082044d8080dcd2ef6ffc64607c3e8228e4a11c5ed700b6b617bb7a7622c9a6577c7e2aff874f66acc28f4e6a4ab30d106a7a2286af17a28eb5d2402c5552d0c9520d20d6901cfff9010d821ee184018553f88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13ea60000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c8198f0b7fc618000000000000000000000000000000000000000000000000000000000000000082044d8080c12153878eadcd2d15c857ef2792e9fc2db5d3bd6c27ae891ab4f16a25ba7294600e03e412be6aa0a2c4f040d26eb1242d254843ccea87ed5f696ce1699258941bfff9010d821ee284016ee5c88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13ec40000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000001c82e8c66cbf1a6000000000000000000000000000000000000000000000000000000000000000082044d8080fddf91e72906765982df34a767d163cbbd0c0b347658ab56a0a3a04cc1f14e124cc53b90924519309db15d1dd1fd2b1e575beb4bcc8cea65e68b054f9296ba4f1cfff9014f8301154d8401c445408305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13ce600000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000731958d2d429ea51c49f3b00000000000000000000000000000000000000000adf463a3704377c3ee8d91680000082044d8080545c76198bc384464dea22db921db86fb5648d1e859861a5d0eae70a2e46b75132b3c7d6c8988a27fd2b269cf097d4199ff2d2ee0dd71d759c097df85d7c47e21cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be64840413b38082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80801b1380301ae8db0eb3f94243c29ebe663ed4dad76f4affbbd516cef3379933ee0924b271cc7c68abbe2a716e18c485712d126d9e0897e17bc99739b86a98f9601bab0b00000003000000000b0000000300000000ee8308be6584015be68082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80808912439f3a0301bb9fe59b96f6977ef1bb83487fa42628a7d614d58718ad148b07c60f18aaaf59d36f2a560b9f13057a3bb48a50a31f7772d4281bcb302fdb0a1bab0b00000004000000000b00000003000000000b00000003000000000b0000000300000000f86a0784017bf1a082b48f941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b300000000000000000000000031c2f6fcff4f8759b3bd5bf0e1084a055615c7680000000000000000000000000000000000000000000000000000000000194b4782044d8080d06b79bc4df74c4a6ba59e8bc1e57e9c3194700dcbf8df2fc3c9fa2ef555844a0dbdcb8e23298a3dff13aecf21fc2287dbfa68df1cce01614f6659f7729ba8c51bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9044c0884017d78408304578d94b89a6778d1efe7a5b7096757a21b810cc2886fa180b904243593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e13e4000000000000000000000000000000000000000000000000000000000000000030a000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006708ca1f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b89a6778d1efe7a5b7096757a21b810cc2886fa10000000000000000000000000000000000000000000000000000000066e1442700000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000041d1a054cf52782e83d7b7111c466fa04e03472a9c2098b9a0dccdefb17745170c3fc27f88fa426fbdce2343c90a3662dc441e75c6ba37988ae18160ca98fa7f141c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000194b470000000000000000000000000000000000000000000000000002820037b6d0c600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002b1e4a5963abfd975d8c9021ce480b42188849d41d0000644f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000019581a7c83f846f74fc66fbefef1299521c65e390000000000000000000000000000000000000000000000000002820037b6d0c682044d80801efd30d23b23d0b0fbfe93446a107c35aff67967f83b03a5a93799323fc98f5568d4c4c7bef6650609c9d1b067eedacac74d49b715b587ec4d61bc4d275fb94f1cff0b00000003000000000b0000000300000000f9011381b684017d78408307249c94f6ad3ccf71abb3e12becf6b3d2a74c963859adcd8723a0f814eb3f9db8e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000b1db6eec8d8c629f9f7e28bf2680e3e264e929880000000000000000000000000000000000000000000000000000000066e141bb0000000000000000000000000000000000000000000000000023a0f814eb3f9d0000000000000000000000000000000000000000000000000000000001633e7d000000000000000000000000000000000000000000000000000000000000000082044d8080f3a405df3bdb20a750ee93584f05ab47603187da5842f402d2c9d766f50560717e471e18c695cc989d88de0aa59c4c67ca94b1093b05004db29a50d5f5751ae31bff0b0000000300000000f903ac0484017d784083077b6f94d8e1e7009802c914b0d39b31fc1759a865b727b180b90384ac9650d80000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000a40c49ccbe0000000000000000000000000000000000000000000000000000000000004fe0000000000000000000000000000000000000000000000000000003a2cd4f5c670000000000000000000000000000000000000000000000000000000001805e6400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066e141b1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084fc6f78650000000000000000000000000000000000000000000000000000000000004fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004469bc35b20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf65cc37241d426970b91720e8255140ade00f05000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064df2ab5bb0000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000001805e64000000000000000000000000cf65cc37241d426970b91720e8255140ade00f050000000000000000000000000000000000000000000000000000000082044d8080a3d508557b59df3d77b1fa113f2223b67f82fde7ad9d9cf6f8e2b3bdd9b52a672a2f437e010cd99922806b351f71ab6896a97723f26cbd7b5781c0305b64f81a1cff0b00000003000000000b00000003000000000b0000000300000000f9010d821ee384019bc2288307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13f960000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000981883f28b083b000000000000000000000000000000000000000000000000000000000000000082044d8080b5e4d0db289d0e6f61d922545bd7579bf20015b29bee82a2eb83bc9bbda09ea04b33b1f4c5f3e5aba7f5821bfa1a9f464373d9afbc99e4dcc58297a52ff062f61bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000e93a8402cdccb48255f094e10add2ad591a7ac3ca46788a06290de017b9fb48084632a9a5282044d8080be4c664f330b9151f51b9471b46aaffe652d5a410fa66eeb8d1f84d72f80cbdc3cc90a5514e45f2f576ec352449eb0313c6ea9a15769a11eb2ea11d86dc6f9ca1bff0b00000004000000000b0000000300000000f9010d821ee484019bc2288307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13fb40000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009834b4eb369439000000000000000000000000000000000000000000000000000000000000000082044d808075de06ec5466daebd6df45e07e32c8c4727338a259fdb82acb5feb48d3652fc838947db58b0d09a037a303b3c74274196df3c5de1142bcd2f88aabaebb2323881cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9018f830151d58401a6f9408302c78894555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c788000000000000000000000000000000000000000000000000005d423c655aa0000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000796b7bf65e570f59e08ac136dcc32e8f642f683e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000016442530c6a80000000000000000000000000000000000000000000000000000000000000001e093a1bf79cf96024c9b7b7ed3a8e26c709e92dbbed7918174b100d95d49e55682044d80800977d0591c4d658fa5bd677158b390bf73e8ae5d7218f7bc4614f3eed5126eed6226f3e972b5c62ec523064f6af4787e049c34f96e41dd9d77d4d25f0d2e76161cff0b0000000300000000f9010d821ee5840193bf608307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13fd20000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000098370b3e46923d000000000000000000000000000000000000000000000000000000000000000082044d80807df0dc6ae14e4160030559716f36cf546839515e3a14e78546cbab05e2107d09760212c06296915c5c1a92d990722bd3e26afeb0cf29bcc462985f9331f117981cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f901f30a840175d7208303465694678aa4bf4e210cf2166753e054d5b7c31cc7fa8687079fbc2a49e180b901c45ae401dc00000000000000000000000000000000000000000000000000000191dfea9b9b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000104b858183f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000af09364718f745b6855df11778d9b523eda1e4bc00000000000000000000000000000000000000000000000000079fbc2a49e180000000000000000000000000000000000000000000000000b55d73b977dd5698000000000000000000000000000000000000000000000000000000000000002b4f9a0e7fd2bf6067db6994cf12e4495df938e6e90009c4a2036f0538221a77a3937f1379699f44945018d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080438d26a5ab0788935cfeadd2658b8a1447d9448fba3f8bbcf8def6c0b74f77cc77c03cba29fcb4d3fdc1b913bcef92e36e10cbd8bd913da805760e2d9c5242ec1bff"), + ForcedGlobalExitRoot: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + ForcedTimestamp: 0, + ForcedBlockHashL1: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + }, + { + Transactions: common.FromHex("0x0b00000001000000000b00000003000000000b00000004000000000b00000003000000000b0000000300000000ee8308be66840433bea082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80806f7f1dc42b8cc34aeb9ee8206e47765be4cfdb5536272d3250950648aa4e3d5907cfece316b171d676cb399a7a75c4b11ed9ed5a3f27dd4122d8c909d4dc51a81bab0b0000000300000000f86c81b784016694e083010ec4941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b3000000000000000000000000331f3a300b7115a45ba31e3428ac002267bb6d7700000000000000000000000000000000000000000000000000000000000f424082044d80805fa04ce2bc7596aa78bff65f39212d4f8c6325cbf35deb9fe4fe916b9b2858e33756e78b77457b8c39c573cd729120935a76c6a1d6bc73459a16514fa1054b5e1cff0b0000000300000000ee8308be678401945ba082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d808090c0419282e01e7ccd1b49b104e6e722c161127a5197edd0bfef9447d4600a2a3e17a07b6523f4aea15c603004537da2e38cc7aec7ba9801099d516b53fbff0a1cab0b0000000300000000f8ee83019e568401e53ac083035f30945eb6b3db915d29fc624b8a0e42ac029e36a1d86b80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a1000000000000000000000000000000000000000000000000000ac720b1f97be20000000000000000000000000000000000000000000000000000000239000f7e000000000000000000000000000000000000000000000000000000000000001082044d8080443dbf96a2e3983c0cbb6e79cdd2a18777b63fa692b19ccfed0bd0b2ed0a6e667db137169578347abbb112cfbf718d3795e48e569f79b357a042c5f1b4837aac1bff0b00000003000000000b0000000300000000f9010d821ee68401a893688307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e13fff0000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009834b4eb369439000000000000000000000000000000000000000000000000000000000000000082044d808049b78766cf06b54ad4071fdf9f0182f50a767cae6a31f64f2379b7ab9b6ec2e65fd26b7b443ef10cffc30988b7bbc73132033d1d6e64351a47baf21fc12bee9a1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ee78401b230588307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140180000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982a06a2b59803000000000000000000000000000000000000000000000000000000000000000082044d808086d7452f41889b2e6811591fc74bae161b77d3712a76ab034c6d7634f3e0041667e2f1c34dbe96c8c6486cebbaef1f7b3cff036f05e72662317089557121c1d41cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9014f8301154e8401c61a00830596399473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13dd600000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000731d4964ad65b18a4655c340000000000000000000000000000000000000000adf476e7c1ddbf9c2658d5180000082044d8080ee854267d400eb90253e3ed78b0ae5ec32fe0ba653de34d80f5ce044cc0fff77498cfa898a2ca54b3b84abab13ba9fd6658f4e0f644d940d33f269a9e6d7cba31cff0b0000000300000000f9018f830151d68401d0ef708302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000070637b4d8b21c764d0d9c08dd1d28b279bb2823500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000151c1d3bbe7200000000000000000000000000000000000000000000000000000000000000012a0a7392d80a1be757cdebf95b665fe0544e091c1b8fdb0ea7470aa9e0f6492082044d80809b8ab0a9bb0496a9099c6018c73ff2acaada0e299dcdae23bc20fb29fad715822cec0db8716a0edbff285acecc08b662b89a0412b629ba866e4c2abb80aab2f91bff0b0000000300000000ea4484018cba808302066e943df9e9ba39dc3ee6b6fb8556bccdfa87ee64fba680841249c58b82044d80807943273fee9a25c1549219629bd8844eaa6e9e327a7935d203e2f9d6d238a4837e3fbe0944dd0d07ccf2cdeec4e465bd25322949b697bb0efcaafe2c3c5316611bff0b0000000300000000f9010d821ee88401bbcd488307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140360000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982d87a827433e000000000000000000000000000000000000000000000000000000000000000082044d8080d03b301c79a78d1584e3c7eedaf2272a56f24adef479a8c3e045ce46ff16065d7cd5f8bd5528c8a634951d7e3f11c7c97c9d57cae43640818c7135a1a9284ae01cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ea81898402625a00827b5a9400cd1fb066573ad4215aa699e580af9ef4b3a4168084372500ab82044d80801c0e2075230c09e5f074cac36419c3eda1ded5447ad6470d15caf956eeb3f4d5305e11deed13c3947e01beafb0054fa692ee7b708525bd6438bb1283d9f5633f1bff0b00000004000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821ee98401a22ac88307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140540000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000983735fbc7f641000000000000000000000000000000000000000000000000000000000000000082044d8080590354f8346245d29a2193f306d6473bf0786bdbafbe91df5a84e8642e3570eb3a6da19a7f13a0e834f5884633dadd4d2e432c2c2bfa3324429be9b7ad04290a1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f86b2e840bf5ba4783015de6944dd70bec5984171a75003a6186219cb99ed79cbe80b844771602f70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000482044d808026293839e8593c6b85003aa27b0a4797069ba043803164bd5209b940d39c2bad7bfc0e2c652552a2606eb57f0dc6f7426805edf84ca2ad6fdbfcd9136ff73bca1cff0b00000003000000000b0000000300000000f86c81b884016b28c083010ed6941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b3000000000000000000000000c8a21fcd5a100c3ecc037c97e2f9c53a8d3a02a10000000000000000000000000000000000000000000000000000000000b3671882044d8080ee2364f3d622e422ec2a90a92137fc5d8a51510bfe0605b11ce2474538a7e5d90afc93bd57016193b3c0a416f3a471bbe24bf08eb385bfb9635425e3f84acc7c1bff0b0000000300000000f9010d821eea84017d51308307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140720000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982a3158e2273e000000000000000000000000000000000000000000000000000000000000000082044d80801bb6d2b5470d77ae8986b29496a0565ee37380f17c51fb34da2d9b4aa9b78ab66d72906a4d0f10e0663f4560942d246c895187c4287b43f9d26bfe128b4abb671cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000e94684015d6d2082a01994b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d808085262abae3391ea17e9032bf33e93d793771d42e14defe690719b53803d7085915ff9bd53ce52fb50282712974ff93950c0161b19a492c040c0606e855207f821cff0b00000003000000000b0000000300000000ee8308be6884040f1fa082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080bcbd6ba1c6cca5649a36a7e14c75a5561bac294fc4ea5cbd50e4c83a04b0039c5ab9e91f0f06c0c028f7a6aa146fa901ac23abdf57072083cb2113e5d53e7da81caa0b0000000300000000f9010c81c18402625a008303233f942a3dd3eb832af982ec71669e178424b10dca2ede80b8e4cd58657900000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e602c1920443f01cb100a57a7f894df8eb42f6600000000000000000000000000000000000000000000000098a7d9b8314c0000000000000000000000000000a2036f0538221a77a3937f1379699f44945018d0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000082044d808085cf2d27dfbfd35f2e62054951ea381ca212755366c3b51fff597c6ffdff170e4bfec23bd116bc191684d77da0f186d5202128c4a7cc68187ae9746b5c8f412c1cff0b0000000300000000ee8308be6984015a5fe082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d808020bdf52ff36fb31db16395c7eaa2bbff670a9dd200ea46062836d24f1b20e6e03cf5779a839573d635f939669f6d36f345a0bd444217df07950ad766e4c4efd41caa0b00000003000000000b0000000300000000f9010d821eeb84016bb1788307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140900000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982cb210d1b77d000000000000000000000000000000000000000000000000000000000000000082044d80805c32534154b68c162e322a19dcd13dd8fd1fd1183396f6b4c1e6c43609149f4566b01aed437e1992606c98b53406eae40a6b630dea0e7cda6a124ac4eef068121bfff9050d81b984015a5fe0830a10a994b83b554730d29ce4cb55bb42206c3e2c03e4a40a80b904e454e3f31b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000b2829a0000000000000000000000000000000000000000000000000011b27c73a01b7d0000000000000000000000000000000000000000000000000011c9409d5f4dcd00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000440000000000000000000000000b1db6eec8d8c629f9f7e28bf2680e3e264e929880000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000004a00000000000000000000000000000000000000000000000000000000066e19297afc94fb216be4efcb60b0969a606731d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f6ad3ccf71abb3e12becf6b3d2a74c963859adcd000000000000000000000000b83b554730d29ce4cb55bb42206c3e2c03e4a40a0000000000000000000000000000000000000000000000000000000000000148c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000b83b554730d29ce4cb55bb42206c3e2c03e4a40a0000000000000000000000000000000000000000000000000000000066ea78b70000000000000000000000000000000000000000000000000000000000b2829a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000281e4a5963abfd975d8c9021ce480b42188849d41d4f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000e1829cfe0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001240000000000000000000000000000000000000000000000000000000000000148000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080c4b8a950b6fb2fd40ef9292e2e21f16ce93551a97b61f65dbc361e1b97c7e4fe353efe7bbaa446443a57106a013415b016a5de387499d032c14384b3b85e5a4d1bff0b0000000300000000f9018f830151d7840179a7b08302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000010000000000000000000000001e09278590cc10fb2bd69235d2f4372fd0e1ac44000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000002bd26ff6604000000000000000000000000000000000000000000000000000000000000000018bce34245eaa4dc895284ff7b987382cd12c8b40d2f55ace41c054e6b667f19082044d8080185025c2a18ff67f6619138b44a5262ba2181151be1526f23b3787bef28350337867a681c56458160a1ee95dec4ad4c9a3c47f1dfb9a39f9b39c67ad3c6f62551cff0b00000003000000000b0000000400000000ec0e84015752a082520894e4edb277e41dc89ab076a1f049f4a3efa700bce88708d8a44d7dc33d8082044d8080e5f393baeec28606eb9e5a973614ff7a3bb17e7df7d56de1c8dc34d71ae0b50d766c5c5d0a02d04a7b4140cef33d896eba5ae9c796e18f047b7b3bb7c0e7af981babe94584015752a082a01994b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d808064019d123c2bc2e40c10f63cef99518b130d358e122504f0028f2f14085f24204f3d2f2de773ce5bb4be2eb02d7978421bdaef8a98f8d2407d3ba53e0ef406061bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d821eec8401754e688307a12094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc65118800000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e50000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000002aa6845f7e84b2cc1619c823bf4f6b04ec733f2c0000000000000000000000000000000000000000000000000000000066e140ae0000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000982cb210d1b77d000000000000000000000000000000000000000000000000000000000000000082044d8080f8fc4cd3cfd4f5669ef452ef2ad8c5082f1ce1bf2f0523a52e12a916b9ad8c6c12a08dc48c171e434cc75852b7c8161b0741f0465821e8bda5fc10ffaeebdd841cff0b00000003000000000b00000003000000000b0000000300000000f12a8402625a008307489e94dfb6baa334712cbbeb26b7537f62b81c2a87b1e8871a84698758700d84e9ed3d3082044d8080c9df68b81de01a7ddf57cd4037afa1df1f031e10810bc3c23c88347bba337b20343c98d810b700b8e27ca6243277b28771d484477cbd58366d02b57f3b8aeb731bfff86a0584016387a082b49b941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b3000000000000000000000000ad41c77d99e282267c1492cdefe528d7d50442530000000000000000000000000000000000000000000000000000000001805e6482044d8080bc02d6c00e37eecd89d84e5439fe5186d2b55f91e68bc952ae7803f7252afc0438bf92877c67810409bb9b40ea6d08b272f760015563aa9e052bb46ccd5e245f1bff0b00000003000000000b0000000300000000f8b133840225dda08304ac1194222228060e7efbb1d78bb5d454581910e39222228614edc04e0e0ab88432accbb900000000000000000000000000000000000000000000000000000000000000c6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b48eb57e000000000000000000000000000860db06d8c758c594143686d0ef9cb52543bae0382044d80803c9ebdd5f72aed2cfa0d36b5311cba9fd9a211cf7c9260502b5b3975751b24603678ebaa584a22aefcc118b5f8983f25e0201d6152c9b7d0d9dd6c7249760a211bff0b0000000300000000f86c81ba8401607a6083010ed6941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b3000000000000000000000000b5f43c2206e3cafecd62651f5fce9091a020748800000000000000000000000000000000000000000000000000000000001e848082044d8080f97d0cf4ce6c85eb1609a85f2c71839e2bb12896b7968547c085d447c99f23ac4f3af1959ed38d3fd76c379dbee00fe520c52c8ec791afe021b974d7442e9c261cff0b00000003000000000b0000000300000000f86c81bb84014b1da083010de8944f9a0e7fd2bf6067db6994cf12e4495df938e6e980b844095ea7b3000000000000000000000000b5f43c2206e3cafecd62651f5fce9091a0207488000000000000000000000000000000000000000000000000000739ecb738d98f82044d8080a78e3716c0eb3802c51e41bd1c7ca64c9a638221f07c7c06af9f6c94300b21d37ac0c2374cfce5539fe7d9c180e153b2fa10b1ccae8b727a8d4734dbd4b9287f1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f281bc840175d720830107e5944f9a0e7fd2bf6067db6994cf12e4495df938e6e9870739ecb738d98f84d0e30db082044d80801049e7b2658e6542463bbe11e60a7198f23cc761a536410d5383e00aa6f6cc68440573478137a9ce3486ff41a903bfb738c44469e56c7d1dc667ee4c18750dbd1cff0b0000000300000000f90432018402625a0083053b1f941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b90404d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000a26ed90933f9410c967efc64452681f501477ca7c7bb0efe1a8e16b8063c3ad420b4781cf348b8a0a4851fc3c2ff2553000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000807edc9cca4620e59b6919cf51b699ec6a2bde550000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008496e6a65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d544869314d7366795965714573576178696f4e445734344a504d476d346b6e734e486567705473675668514a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000023c5fc38090643b410c8c06c4c0f3246059e98ee0000000000000000000000000000000000000000000000000000000000000ce40000000000000000000000000000000000000000000000000000000000000041ecd952920c1aff799bb35cc6b1306371afbc5f683cdefb2ee2e31ae21bd48a8270797e9889904a574fa1918a3bb13fa2b28d83ed3ab567b3aceea912b8d04f7f1b0000000000000000000000000000000000000000000000000000000000000082044d8080feec2c92b7ecf47d4dbf93f7041e0082d822ea2bedafe3c6ae11d8426678fb643c29bab890f68593a4f8421415f5a68481621730b71dac5b16a41b7c31ef762c1cfff84a06840174508083074ad694ad41c77d99e282267c1492cdefe528d7d504425380a4a0712d6800000000000000000000000000000000000000000000000000000000016e360082044d808067b5300ca47a28ce2521475a6224c2fe27e4b28da3a2cab92231440d3938603724b3ee0a7fc6bca38ad52b7ca90b534e67e8d3a55ca472e3eba0ac51bd5fbc7e1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9018f830151d884018c1e408302042094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000020420000000000000000000000000000000000000000000000000005d423c655aa0000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000da930060b712589736018c6d891ec57b0399db7800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000763bfbd2200000000000000000000000000000000000000000000000000000000000000000017f0ea037712c6b94793ed1db0d924ab44381912765b13d871e7eb101ac652d3982044d8080ec0c9e107850e44db65fd11d35d41ee8ddadfd9a2e989dbc619a60c52adcd2860092874af3ca89dfdd56ce708439cc6cc538ee10b7fb9023d4125f7abca62f0c1cff0b00000004000000000b00000003000000000b00000003000000000b0000000300000000e92b8401499700829f8094b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d80805b33a4bea9274b6838fc8c299e7e1d463d8a9866847ed34961dfa742b1e09e7960336c7a436d7c78f8c055607e77072b8f1ddcdeea4757c9236b8dbfea38a1181bff"), + ForcedGlobalExitRoot: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + ForcedTimestamp: 0, + ForcedBlockHashL1: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + }, + { + Transactions: common.FromHex("0x0b00000000000000000b00000003000000000b00000003000000000b0000000300000000f84a0784016e36008305dc8f9468d9baa40394da2e2c1ca05d30bf33f52823ee7b80a4c5ebeaec000000000000000000000000000000000000000000000000000000000112a88082044d8080b0b78bcb15ccad6e34965bb75310d919de9c98653338a4e621bb68d4e1158bc071db0cd0eee106b2a15820242d3a30d78cab313ce696b38609712e9b97b4f2211cf6f00c840a85ad1c82fd67944f9a0e7fd2bf6067db6994cf12e4495df938e6e987131d14dce4400084d0e30db082044d808061485ee80be12c1cf4118d79c8240621c4d61342aa6e21313bcb338e0c4d3f371f1589ae7a144279671a249bf3d2514d9f39c850081cfe35070f9df8b5bc52b71cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be6a840433bea082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080ae7ad2d365c97fd210a6f105189d8f8845c7607a5304f61a75e3f95b30d0278654bd6071ea8dc8eb258293e33b8662588af561e361eeec88f2c222dab85904a21caaf9014f8301154f8401aaa2c08305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13ec600000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000007322d2c4642c149072472c80000000000000000000000000000000000000000ade71ffb3551101896108d800000082044d80802d5cadf3e11640c1e52b9bceb4f465b5be3247fc0ec042d85c44575e2e71c3ac0542b94b31e12bdecca3fbfa292da254413c91cff46b7a09a6a264a4b58fa1941bff0b0000000300000000f84a0d840b405b068301519d944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d00000000000000000000000000000000000000000000000000131d14dce4400082044d808005ff907a16f3f1614d5f81b3fb224f805038d423f46f7a3201533568f87cf5032028fb37a272fa727550e1dce4af167b67b51157e24de676306f97faecf6ceac1bff0b0000000300000000ef808402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a10986246139ca8000840333f38b82044d8080e1a97f847a204044e3f9e326c84f4d5369fc9bdb762c0aa38122fd4f9e5321d0660038ec1b8f5c2273ccfe875ffc274d7b809cfb2ab742631683613108c7e8501cffee8308be6b84016387a082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d808086767ef08deb78094341e575335c29fa45b39ae1dfa57003b2f1b8e0f29dd57f13cd26cdd53e10c6c35df7eeecf0369bc77a701d0732dbeea119688fa09f7d021caa0b00000003000000000b00000003000000000b0000000300000000f00e840764acfe82c6ee944f9a0e7fd2bf6067db6994cf12e4495df938e6e98712dc81e31cd00084d0e30db082044d8080cbfcaa3444f14e64cb0fa9e0259f2fd1210b5425a8c23842150c77d1c9e8578d79aea37c19621cdc2d60eb47fdef1ddfb63a0dcf97dd37f639b862a80d81aab31cff0b0000000300000000f903f20a8402b900fb8309e24a941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f53ae48ab8aba0990d601236bfbbeb5f26e910d68867594ac5def0ee45b27ce819000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000ec90f6634a907165e5d18a14c1dc51e32b9de8b60000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4f4b582057616c6c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d6252327345444c614761345a33664a4278386d6b65636e59646b6b323451586443556a737253466a62745161000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417ee5aac8e9e510dbe7b12d5757762ea0b0eed32189dce8ceb044ba9bd16daac61415af82e6bdac2e90d2227bfe8336c383ac24ce03af40f159412e86b49e02f71c0000000000000000000000000000000000000000000000000000000000000082044d808054c2fcd741aff604618f9331edd8de2062dc15c3ca37a0ae759881f21547c1dd491a44cf2cc85c20f72575718f39a8026b3989a1f07f4c9de324b8fbf62d03801bff0b00000003000000000b0000000300000000f84a0f840842bf86830127cf944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000012dc81e31cd00082044d80803d7108665030a686303828a8590515a325439a383cd915a8510b1ead08fdbe64726b280ca7a73a844d6ff801f53ad44480c39059472080c96b4207f2b41eeb141bff0b00000003000000000b00000004000000000b00000003000000000b0000000300000000f010840cd2e41082ec87944f9a0e7fd2bf6067db6994cf12e4495df938e6e987141476cc45400084d0e30db082044d8080bb1a5745409e78f126e0f3d301d1bbb8e829404909bda7bdba47b38a8e5cc5022975deb7d293bdb69210a4a50cb606efce11040df82ec34008b179a51447d3a41bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f84a11840b840af383014099944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d00000000000000000000000000000000000000000000000000141476cc45400082044d80800f0a3bb81e483365b2f6196c60d0d97882e9d2816fbfb78689cd3850a33f7d9e0f6732a7d860fea63d323bdd924b39043a592e79fc5cb42593cf5a3722b854de1cff0b00000003000000000b0000000300000000f86a0884017d784082b49b94a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03580b844095ea7b300000000000000000000000046a15b0b27311cedf172ab29e4f4766fbe7f4364000000000000000000000000000000000000000000000000000000000112a88082044d8080325b6bc0360425e4a14b8d91cd8855d8649bf946352c4ad927a31e56d22c004731500e69af69ce8b80b06946735af113f7ea67749f57af617d5deae89eb1f3d61bff0b0000000300000000f9018f830151d98401a1f0308302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000010000000000000000000000007f35a6e27c542fe3bb6bdda638abc8bc65c3c0960000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000015c7ff2eadb60000000000000000000000000000000000000000000000000000000000000001a93b38316983d650e0fdfc2642ca16b840420a6bd6962cfbf417bf5a790d681b82044d8080738091a8459376230f139c30ca51cebeb6deeab09492d46a6009f33a090953b219d8f52e737caa178042069504eb99b0554f51963c9943204724b283f347c10c1cff0b0000000300000000f00484018519608287f19465a4b8a0927c7fd899aed24356bf83810f7b9a3f871c6bf52634000084db6b524682044d808050f3c9ba5b616396e065717c29365860afd49c5e9aa275eff0f37446d3eb5e3611e8626c0be00efd62ed5d31b51cc334dd8793c6bc8c62808616faebc97cb7b71bff0b0000000300000000f112840cd321b68301312c944f9a0e7fd2bf6067db6994cf12e4495df938e6e9871382f1e51b400084d0e30db082044d80809585c9b5b7689c9abd1823e19e0f8d592f57f419f94cca0ae20ed021b68aaeb556273263752bc1e78c8508d5af41a05d2b886e07b78330d63df42177f02110621cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f84a13840891001683010317944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d000000000000000000000000000000000000000000000000001382f1e51b400082044d80802eeb9e8ba20f90a05d7584a87870c63edc5a0952fd52d1299d54a19098af421408f9882e7e62d42d0afdff81a06c4614ada1367758b701289f3aac12f5ec13e71bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f114840be4d1b6830126d3944f9a0e7fd2bf6067db6994cf12e4495df938e6e987145b3905a2500084d0e30db082044d8080ddfa4814082f9ca8947b3a9263cb97c0957bed97d3032e8fa38df43eaf7669743b15fc3cce18b60300063396898de729594c2af72e147d8dafbf7dbb3e9e745d1cff0b00000003000000000b00000003000000000b0000000300000000f84a158409de70b683013343944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d00000000000000000000000000000000000000000000000000145b3905a2500082044d80800c288f5c663955ad276d8e8bed2233997f07bcaeade6992c98b05f99151c6c2a27d0a04fb3e07473e11b88176db0f5baa46dec2c48eb704e4d6bcd78467b25fe1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f1168409d2a29e830141d0944f9a0e7fd2bf6067db6994cf12e4495df938e6e98713a7531ee5c00084d0e30db082044d8080f22f024c3df0f0582d53d27586bc0b534636d4bbbdecb6cba76d31d9ec26c24a2fc8e613aa6d783ef7541ca0463908b696f7a3cac12710986820bd17613a05ee1cff0b00000004000000000b00000003000000000b000000030000ae450b0000000300000000f84a17840843742f83014a3d944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000013a7531ee5c00082044d808093acdfe33b2bf0475202506781b23511c98ddf648158adc2480626821c8462f765d5b53638bf280519dca4c3c6d1b8e62068dfab0aa3ee45702e69dd44d70e881bff0b00000003000000000b0000000300000000f9010d8263618401810e38831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d00000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e141af000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000000000000000042a6800000000000000000000000000000000000000000000000000000000000000000082044d8080787ff95ee62900d9971c92e31e4a72c46ccb514a1f429be155b8bf471369fc0610e0132cde21bd011d3efc70d70cdf470af7ad9b5aaf86170803e31f29a94bd21bfff9012e8263628401810e38831e8480941b81d678ffb9c0263b24a97847620c99d213eb1480b90104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f400000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e141af000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000000000000000042b949a000000000000000000000000000000000000000000000000000000000000000082044d8080812562f17bf5daeaa60ed73ad30eac5996a784b57451663ad68d2a3a1a4ceffd0e98775ae6ade38d54ffb4a21778eb2a047d280b8408451096b13019394cc5eb1cfff9010d8263638401747b78831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e13bf40000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000980faec989d805000000000000000000000000000000000000000000000000000000000000000082044d8080d6ae2347276fa3cfd245d9379684454bb7f0bcc3cca6d642d827809ff5d836922ec796bffe17a15a93e05d6d62d524588b2e083a3baff8736a9ba633ae1f7cf51bfff9010d8263648401875798831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e13c180000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000009819595140ba78000000000000000000000000000000000000000000000000000000000000000082044d808008a9fd6bf5a08b08d9611be3a3243fdc5809a0e51aec5629bf321cf09436fc67600bd37403aa4f6155045256cd67620aa2bc7e7118c6b2dcd5f72db9c30a433e1bfff9010d8263658401600d00831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e13c3c0000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000981383eb6618c6000000000000000000000000000000000000000000000000000000000000000082044d80809be365822aadb974a913e75d18dd5280b322409c861bfb10d545c17c72979d847b52b9ca8a4f49f5dba9016e98d2e55536a8ce32fdf06b6a13320aab4a6869821bfff9012e82636684016331b0831e8480941b81d678ffb9c0263b24a97847620c99d213eb1480b90104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f400000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e13d9d000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000000000000000042ebcea000000000000000000000000000000000000000000000000000000000000000082044d8080cbbc34a2b94e3d2e56260b5e0034028c2f6bc1c7306fe9a9f1956b670c027dfd70a38bd41a95f94774d84e470ea2a25a42c499e1e4b971dc91a426139f61a09e1bff0b0000000300000000ee8308be6c840453c9c082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80809fa5bd47d6b5cc939b9b27f080a9ee8477b2baf9fc9ac7b51c023ba5eab004ed22ec4afd14c073c5eb9f9a2802eac4f24ac04c34b7ef33de4ecba895e8749e141baaf901ce826cd284017143408306dcda948ff6508bdd71b535df073920c423e9eaee0b97af80b901a4b61d27f60000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000000000000000000000000000000000000000001f40000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e14087000000000000000000000000000000000000000000000000016348a452209ad0000000000000000000000000000000000000000000000000000000000de701c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080aaa0ea5c36cb0e4fd0bce18e23632f9c8ea2b06b5351c2c69a2adfec23fff4a43f84b301c33c51f2744abdfab346fc91d47c8dbe1b2c91849163ea7d0dc871531cff0b0000000300000000f9010d826b8984017143408308e02294f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000098f0f120de21a90f220b0027a9c70029df9bbde40000000000000000000000000000000000000000000000000000000066e14088000000000000000000000000000000000000000000000000016348961fbb5800000000000000000000000000000000000000000000000000000000000de6fe26000000000000000000000000000000000000000000000000000000000000000082044d80809c6fef4b141e8e4a07c9cbb2511753153fcabf3a831eec7216bb4af90abb7b7d6fb5002e74953de4a87a5aada51b25c9b1b05a961546421ecdda070420f081621cff0b0000000300000000f9010e83018d5f84017143408308d82094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03500000000000000000000000023abcb82f468e3422a7212d4ad47cc2dd39162a30000000000000000000000000000000000000000000000000000000066e1408a000000000000000000000000000000000000000000000000016349e13e663f90000000000000000000000000000000000000000000000000000000000de70b11000000000000000000000000000000000000000000000000000000000000000082044d8080637884a6f0f95a88b0fee2f9bb436161c0032965d91166bea89124fe6513d31c5527771f11b4ed3cb90e8b757c9f4c9fd74c6bcdc82af2e68521ffd5e0875cea1cffee8308be6d840171434082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080c501252774527c8bd4e9fd808914868352ec182c325575824ae9e83523ba33ce46a6b82b2ebb32e4194110443e5c67ad08df9d8db9bc6fa8aea72192c1192e221baa0b0000000300000000f018840a88762382e3fa944f9a0e7fd2bf6067db6994cf12e4495df938e6e98714191a3bf5780084d0e30db082044d808076d4330b8513208b9814415391c70d0cfec79655b397fd2632d5e82c57962cb87956a8c4b93809d7904baadce5b66e8e720ea8f6ffc5c9765226b0952f7dafd01bfff901ae826749840169a22083091f30948ff6508bdd71b535df073920c423e9eaee0b97af80b90184b61d27f6000000000000000000000000f6ad3ccf71abb3e12becf6b3d2a74c963859adcd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4bc6511880000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000008ff6508bdd71b535df073920c423e9eaee0b97af0000000000000000000000000000000000000000000000000000000066e14090000000000000000000000000000000000000000000000000016348d202444f70000000000000000000000000000000000000000000000000000000000de6ff7800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d80807838e9e5f7ffa45112082937cf4c13ac9a4b34d2d89c0f88a6d1e5b8fbbabe68450963e324bff7087380a48b63fd683577784f3eb7c926b9a9ec08acc1cce13f1bff0b00000003000000000b00000003000000000b0000000300000000f903f215840169a220830343e5941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5552350c0f4036746139ed62c05a8199a5364297d41a85bbdf9d08e25c0b01711000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000c2f02570540a1e49f72b83365db2aed22ee6cbad0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075068616e746f6d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d526e4b355959546b517155556361316f505241637157456f4878737847576658517670364e4c43435647705600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041303e333a0a05a77d8b21e3acd4fee5daaf7544b8b4d685934b1343ccc154afe811166033a30ae22ed0dfb9c4ccdec95eed70ac788e04d58b361f0a5f38260a011b0000000000000000000000000000000000000000000000000000000000000082044d808057a8cf819dd9e7deefa39df29b556f3298ecf2343570849e33ca211713427ed3666812d6fc31f2b1269f4d272d30c4c4d6fe42bcfd0ace89e156199a62e72e771cff0b00000003000000000b0000000300000000f90bae82244c8401650e408303e4a494d46f8e428a06789b5884df54e029e738277388d180b90b84cdd1b25d00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000008e000000000000000000000000000000000000000000000000000000000000000740a14de1e598b81620773454588b85d6b5d4eec32573e12145ec8cd4881eba87279f5f243eb89ea9383e677c61a144f9a0e7fd2bf6067db6994cf12e4495df938e6e922072671ce2724c2d62888ce0330cd083a20623f6e1a2edccbb959c2ee656b3497c5155af15a0ceaa8b0cd940c4fe9792d12000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000004138c5d370f4c6b199ed42c2e346cef963a8fdf9512270c9518698cac23166fccd4145a7fe09b7b53828ce325cacb2368e5917ccf999f0454697e0edb2333ef67c1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414815d2c0438852df0d6e1f6e079682e1cdc5f56410cf9260224db24c1c2ff2ea25ea8b65b7966110962eebc47a78f4d77a8b1dd0a3855d73ca97da7e89cccfa31b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410f45357656c66529a2f7c6fca35dfc1f13f7db5cd546cc958503ecba7d9d55ca51b1682a289b9ae1daaf82ec950e0005e71c01b0bff347c68ca80999802cf0fe1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004166d8d1d855e266c5688f8a45503d9448366fea91b50d4c8ba12ebda394ef76a84d003f8bf256cafc0791d521dc3fd681710fc1e0dc49d373fe1a3be3ef9645ea1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f2d31bb75fc66cbc2f75af278c95a028b6b764f0d056d65f3f50bd9d8ad857c53c229fd9cc844192595b6e8c892edd4566e32f17add3ec25102f06edeac4de4a1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f1d037092dba92c2b718f8cc6d1d0274087cc7e033e6aeffe8ef39246188a2e34ef3761e29604612724fc57bd129d9ca0b1ca246c3f7c18113cc3b8acfdfd4b91b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041aaecb7b28127e84f580d3944c59e7de10d46f2cb121f0b56364ce9d843c1fd783e28f41e1e33378f071dacd4788a44db8314174eca4757a19470cce0ef98fd5a1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000419a9ea0bf5e36016e3380ee472c85ac21cd072153f7265ac450f9c2853782cf715a8105e3826f08d47139e61146f7768af4b55b3ab485772ed6332c66150d80961b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000241a100333eefa2efc389ec836a6ff619fc1c644000000000000000000000000273035e10f106499eface385dba07135e7cc8e5400000000000000000000000055f4a1bfc655cf55ed325f2338a1dee84f754df200000000000000000000000057c96a00f9ff7b25cb5cf964f1a191be9321b8c8000000000000000000000000870cf8dd5d9c8eb1403dfd6e6a4753f4d617a53800000000000000000000000095016e36adb4e0151735ced3992a7fa54e16bd08000000000000000000000000954adc74481634b4d278c459853b4e6cc17ae8d200000000000000000000000098e9d288743839e96a8005a6b51c770bbf7788c00000000000000000000000009a66644084108a1bc23a9ccd50d6d63e53098db60000000000000000000000009a8cfacf513fb3d5e39f5952c8608e985b3dc6ef0000000000000000000000009ac5279013edfec74c5c2976fc831ad0527402e00000000000000000000000009cd5006e1bff785dad5869efd81a2c42545c9d9b000000000000000000000000a73b339c3fae27bedf7cb72d9d000b08fc899609000000000000000000000000bfa2f68bf9ad60dc3cfb1cef04730eb7fa251424000000000000000000000000c74acab8c0a340f585d008cb521d64d2554171a8000000000000000000000000cf12dd34d7597d06ff98f85d2b9483d9d5f7d952000000000000000000000000d10c833f4305e1053a64bc738c550381f48104ca000000000000000000000000f4151eebfa1b9c87dd92c8243a18b1baef8c1813000000000000000000000000f5ad7f3782e8a67bffa297684e27cf9fcc781be1000000000000000000000000f6e93eb288658de5e2e982f99d2b378b22959d1500000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000002f447e9b1e8adca8df0000000000000000000000000000000000000000000000440370c65f337c6835c0000000000000000000000000000000000000000000002c9067d217c36159ff0000000000000000000000000000000000000000000000530db71af8da0bfebd000000000000000000000000000000000000000000000051e62edada099ff54000000000000000000000000000000000000000000000001999bb4b4f342a0dbd00000000000000000000000000000000000000000000002c08f0c7601de23e6a0000000000000000000000000000000000000000000000ec1deea13ae957d41000000000000000000000000000000000000000000000011be0842d86f7e8e736e6c50000000000000000000000000000000000000000003e4b325079a76d1d368000000000000000000000000000000000000000000000394b6fd351bc3090d200000000000000000000000000000000000000000000010792503324e9fba39d000000000000000000000000000000000000000000000059fd816a8470289cecac00000000000000000000000000000000000000000000d2faebf995c3095183ab000000000000000000000000000000000000000000009158ca2eac127d67ba00000000000000000000000000000000000000000000003bda588740b8d2e76f00000000000000000000000000000000000000000000008ac2e56a51bc65072900000000000000000000000000000000000000000000004383436ecf64559db0000000000000000000000000000000000000000000000031c05fae36f04651b4000000000000000000000000000000000000000000000010250608f677cbded0000082044d80805d36d9762307593a13e91e19fb81e6c0a17830d4834e18d66c52d6411dc7b22009da393426cdda33d3b5d7385fae3ee7b625f0febf8d72c3785dc733aada5e7f1bff0b00000003000000000b0000000300000000f84a19840af6f75283015497944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000014191a3bf5780082044d8080021f1ce93bb9218374d2ea45dbd2bc8d97f6fa5aad023474b71e12f2ccc583e03ab6cf9edaf16b142da8d07b44b1811aff429a4bdf6b0af997d0707f98f295061cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f01a840b695c7082e6e8944f9a0e7fd2bf6067db6994cf12e4495df938e6e987145b679690200084d0e30db082044d80804b2eb02436c30f483e54e8af28f2f1c59db7f5d56a24c83d0b3d2c4f47065ecf68f4895fb2a2e27e0af1b416d34da70f50affc5291e02a2a13f29ae09f92ab1a1cff0b00000003000000000b00000003000000000b0000000400000000f903f3818684015d6d20830399fa941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5b38fb2ebaa1e7167ed74018ce74a588d28794f3dbe74ab6185c751c3f31a2d81000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000007e4c51597dba086fd1a07751db43836a3acd20bf0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4269746765742057616c6c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d545050343333525078526b70515170476b5831365176573558694c6e334d46643833577a37524334764a647000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041aaddceedfb48b401c31abb8555f78cf18080778613ebbcace5332467725ef3096d0aff50b740abfb1372f7511caa6e46683774e7697c2c3fb360621b0aed16761b0000000000000000000000000000000000000000000000000000000000000082044d80804858cae0f925fe74ffa06631cc0e9bb9955a839c9eaa30bdd8b11a0b7b8de9371e19d31182948a1e2d730e3f9cab2f67d1a5af4e2a62f386d8951d34f94367f41bff0b00000003000000000b00000003000000000b0000000300000000f84a1b84086ce4f483012ee7944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d00000000000000000000000000000000000000000000000000145b679690200082044d80803ce9647fac84541c0f88f2635be838c489ac64d29edc0759905e1677a53a996c4ba90cf64b959dfe09642aa7a67fdaf495c1cd9cbee52efca91f26932b9506401bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f11c8409ef8bf783015963944f9a0e7fd2bf6067db6994cf12e4495df938e6e98713c0ca60f3800084d0e30db082044d80803e3309c130e2bbd0bc32bd9282681387467a68ef88003d71ef2fcba5fecce4c41cf843eb96e923daf4dddad4176d421115cf36e97ca5b794b7bb1330900ff1681bff0b0000000300000000f904351a8401d1168083040eb680870130ae43fff000b9041a60806040526040516103ba3803806103ba83398101604081905261002291610133565b813410156100765760405162461bcd60e51b815260206004820152601b60248201527f496e73756666696369656e74206465706c6f796d656e74206665650000000000604482015260640160405180910390fd5b600080546001600160a01b0319163317815560028290556040516001600160a01b0385169134919081818185875af1925050503d80600081146100d5576040519150601f19603f3d011682016040523d82523d6000602084013e6100da565b606091505b50506040805134815260208101849052428183015290513092506001600160a01b0386169133917f21e7cd7f82cd180cc2efc8f6d34988374ee142862fb2f9a8f60192e9b3cba1f99181900360600190a4505050610174565b600080600060608486031215610147578283fd5b83516001600160a01b038116811461015d578384fd5b602085015160409095015190969495509392505050565b610237806101836000396000f3fe60806040526004361061003f5760003560e01c8063b0910a5814610044578063c0129d431461006d578063efdee94f14610077578063f7b98453146100af575b600080fd5b34801561005057600080fd5b5061005a60025481565b6040519081526020015b60405180910390f35b6100756100dc565b005b34801561008357600080fd5b50600054610097906001600160a01b031681565b6040516001600160a01b039091168152602001610064565b3480156100bb57600080fd5b5061005a6100ca3660046101d3565b60016020526000908152604090205481565b6002543410156101285760405162461bcd60e51b8152602060048201526013602482015272496e73756666696369656e7420676d2066656560681b604482015260640160405180910390fd5b33600090815260016020526040808220429055815490516001600160a01b039091169134919081818185875af1925050503d8060008114610185576040519150601f19603f3d011682016040523d82523d6000602084013e61018a565b606091505b505060005460405142815230925033916001600160a01b0316907ffa30e9e1a69333c24ba36197dd28ca3e0c5f35e57ddd18903b4fd4ea7d4912339060200160405180910390a4565b6000602082840312156101e4578081fd5b81356001600160a01b03811681146101fa578182fd5b939250505056fea26469706673582212205084495e17d7e67c22e09899255168827489804e496d7d6f983ad2752ca58a2764736f6c63430008040033000000000000000000000000c451b0191351ce308fdfd779d73814c910fc5ecb000000000000000000000000000000000000000000000000000130ae43fff00000000000000000000000000000000000000000000000000000001e77d399980082044d8080e753eab3daad33e5406b6e9ccf7fdd0268c3eea1b2799d1052a1a16fbc83a6e317be62f32646d8c452718d299d38228c41dd5a1dc67961dbcd542bb551b391f41bff0b00000003000000000b0000000300000000f903f23584016cb4338303977b941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5a5b86aa0e7b0c70701b8db6d1e5e8a7ee65e36fb3fac08e68c65eaee771351f0000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000006158a9ffe6404eef6a2bdf331feba141f17235870000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007696d546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5579436d583974784a4545776f7756365646633566386e76576936717a414b46424d64767a6835535a334a340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004133474e9c238d06b566181d591c630a71d9e10eeb72ca063a40dadadcd0c0f6fd4d6b0326c4943466f3b886510d4a14429e394fc1a8abc8a0e2794ef07b91ca561c0000000000000000000000000000000000000000000000000000000000000082044d808030e5d2dbbab99819c571447b15a332129be20c6d875c480cad06e720a32b11d84bf57d2fc8d9eea3820202f47268b26a94e700afdb728edb396bd7312b8d8d621bff0b0000000300000000f90139058402625a00830284788080b9012560806040526000805461ffff1916905534801561001b57600080fd5b5060fb8061002a6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80630c55699c146037578063b49004e914605b575b600080fd5b60005460449061ffff1681565b60405161ffff909116815260200160405180910390f35b60616063565b005b60008054600191908190607a90849061ffff166096565b92506101000a81548161ffff021916908361ffff160217905550565b61ffff81811683821601908082111560be57634e487b7160e01b600052601160045260246000fd5b509291505056fea2646970667358221220666c87ec501268817295a4ca1fc6e3859faf241f38dd688f145135970920009264736f6c6343000812003382044d808044a18dbe91f5ed18370200a54bfa8738d390e49046e22faf1b6320fa25a8b23450dd1c97b91e2cac7fe4618bebc997644786d82b3432cb304df14213b6a782a11bfff9010c81b88404c4b40083032174942a3dd3eb832af982ec71669e178424b10dca2ede80b8e4cd586579000000000000000000000000000000000000000000000000000000000000000000000000000000000000000092de600f86669d5bc23fd33c4b93689d49c55aae000000000000000000000000000000000000000000000000a688906bd8b00000000000000000000000000000a2036f0538221a77a3937f1379699f44945018d0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000082044d8080689c42d280e7d7a501e3b8a03727d170815bb230e407c1adbe02c056943267013fe64d7ddd8e1dd996ec63c86a17964c9982076c6f51dbe46034e05b8cf2068f1bfff8491d840bdac5d982e3d2944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000013c0ca60f3800082044d8080f0658ba891342b29cb36b03d7c7f4ef697ab9453400c9bbc07e6689f2442a27d283e986cbe0569d209a00ce4a0bb790909b20727d04e17df540cad993cffaf681cff0b0000000300000000f9018c09840178e4608308474c9446a15b0b27311cedf172ab29e4f4766fbe7f436480b90164883164560000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000000000000000000000000000000000000000000064fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcc905fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcd6740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000112a8800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000112a880000000000000000000000000cf65cc37241d426970b91720e8255140ade00f050000000000000000000000000000000000000000000000000000000066e140ce82044d8080cce7a5d5d6aadae5e27baa53001df17380db45b57e5be0ace2a6cf6b062e722e32fadbd8ab50149b54214c9542ae78c7defd5928ab2f23fde10078dfb00c3e9f1cfff9014f830115508401b59f408305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e13fb500000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e1000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000072f21faa37999cc861c82500000000000000000000000000000000000000000add83f3e7c6394511984a9080000082044d8080262b264f0820837db4a0fffcfe5309152b391bf1a80d105d564051c6bb5c5c644dd1e64849aadc9a6650a6d15dd17f1a0dfea656b7f190de92b7c3867d4c46b11cff0b0000000300000000f903f2038402625a008304e632941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5f1f3689562d1c0dab2e8a2cc0cb879b692ec71495e8cda15ae8d2ce622c7c6ea000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000000f9bccf01d2a27b0b6f7060c238a3c499814c7290000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c52616262792057616c6c657400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d597346367139725173466b42344d51453766334a35333843594748316f61696539766b5643616b573875697100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041547595cdab235f61e0e57fce4721337a52a434ab432daa2749642b01f769f773340113e8ed17a36f42e4fb82c5e8a9947fd69f29b12d76902d8b6091d7589e831c0000000000000000000000000000000000000000000000000000000000000082044d80801dafcb03a4ae839365fa5edd4e370763a965b1956859fbc950e985137707683f5cb0463918a45f865a13728c1d2785953a8820ae39ed5c5cce11c3d9f798155e1cff"), + ForcedGlobalExitRoot: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + ForcedTimestamp: 0, + ForcedBlockHashL1: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + }, + { + Transactions: common.FromHex("0x0b00000003000000000b00000003000000000b0000000300000000f01e8409868dda82c553944f9a0e7fd2bf6067db6994cf12e4495df938e6e98714138df7a0300084d0e30db082044d8080c3e8e282b8d2318bb3eacfb610c28212435b08a9a9a7957ccf3b7db78ccf6362259552814b9d6fadf088245eedf9894d2715ea943553730ee3a0e37de0d2bd8e1bff0b00000003000000000b0000000300000000e9068402625a0082ff7294e38fac030c30a806f1a18348cb35b1f9796b89bd8084b49004e982044d8080d4af995d1407374e10081bc81f21a015911814ec43220eb6a75ac5d347da563104c194b2dad08167c9b8018db7cb67cbd4730f7ae1ef58f508e1df803ca7738b1bff0b00000003000000000b00000003000000000b0000000300000000f8491f8407b3caf482ec15944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000014138df7a0300082044d80807a2a304783534f2b6627a011dcd5655a14f601f08f8ed1a5ceb48cfea15d246a1aa2513734725e55a1aebf47662192fb6b51f3ee33f5885df867aa9251519b951bff0b00000003000000000b0000000300000000ea1684015a5fe0830275e094c790f82bf6f8709aa4a56dc11afad7af7c2a986780843d18b91282044d80808d203c5fc7ac06efeaa4989ea373b55b32440f50b4969ed35eed578101407e2e485fbf235e553ff03b12aaa183b8ba39eefd649cba4a64d01ff298b6fbcc67331bff0b00000003000000000b0000000300000000f020840c0ac30582c5cc944f9a0e7fd2bf6067db6994cf12e4495df938e6e9871301cbf18c600084d0e30db082044d8080b2da0efc52a37be0b0abb72e2c878e2d894135e5903aa4b7510215dfc06644434894408779c15dc2e31b3c26bc9eedd420ba0b8cc62c91a40a79c93bc211b4781bffe9078402625a00829b4094e38fac030c30a806f1a18348cb35b1f9796b89bd8084b49004e982044d8080a96eb803ae5fa5e9bd6330e833372d275f55c50f2abd9a783c76011c171bb9a854f7ba60b09c1d684ed56f94254f87f6a3f064e1a1d542dbd09b67a36ea583611bff0b00000003000000000b00000003000000000b00000004000000000b0000000300000000f86c820582840175d720829d91946e6b8f03c407cfce46ba9847231fec40dce9308380b84440c10f1900000000000000000000000092db6815e62f6a8ef196306502c0283297f73b75000000000000000000000000000000000000000000000001f399b1438a10000082044d808097103c7391adfca69c78cbba1820b0bd440608827f76f970ebabee9770d106a325407087010a8ecceaf780e93d7db2140e934a94ff35ec90a2eca49a466f852e1bff0b0000000300000000f84921840aa60ab382e3b8944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d000000000000000000000000000000000000000000000000001301cbf18c600082044d8080a93e5a2fcd9fc6e8431efecde6b5119e2ed7bdfbce535819481e7c4a8afe7afa48bb9219bd7bf591c3b4c1677320ef5961415c7192e2e564945cb87cdceb51661cff0b00000003000000000b0000000300000000ee8308be6e840175d72082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d808024468cb79bb0bdcd83559601af894fea6b58b2c042c87a274da30b03304085ad289f7d82b0e6df87cfdf16b69a1671915fb2fb2779831748932db2ef39172c301caa0b00000003000000000b0000000300000000f86c82058384017d7840829d91946e6b8f03c407cfce46ba9847231fec40dce9308380b84440c10f1900000000000000000000000092db6815e62f6a8ef196306502c0283297f73b75000000000000000000000000000000000000000000000003afb087b87690000082044d8080079b0b01fc85f171207c07f0864e86783059697bffc98962dc0573ae40c413786a55a9c8e8cb9e58d24936de95f05fa3f28cac23efb6de2f65f455652c45cd631bfff909ae82129684017d7840830993579489e830972c7b453d4715ba1d3894aa096f219f0280b9098457ffc8610000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000006ac983efafa917bf04342e3684a863c9212e1fe9642eba19e81b0eab36c223645f2b84aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000000000000000000000000000012659eafec31743a8a1b09826b378842d4b42910da64ae7c0be9774b3490711b80e76dc2d6e18b40e24de42f1bb55971bd666fdeb1a4a463c0a4154a072796517000000000000000000000000000000000000000000000000000000000000094000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000004e70004b01987b4fd7dacab348f0b39e9ae261100000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000bf4f119c5bbfe0000000000000000000000000000000000000000000000000000000000000066e13ff10000000000000000000000000000000000000000000000000000000000000002afa96d133fb7ca1e775405c5ec53a83986cc81317bfa06200d34061675b76adced276e8201ff7defde2bac4a9e387413b3da71f8d1de09ebac9a5490ed0bc189000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004e70004b01987b4fd7dacab348f0b39e9ae26110000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000007e6125df4bd46800000000000000000000000000000000000000000000000000000000000066e13ff1000000000000000000000000000000000000000000000000000000000000000283ca785cb646b1762da8d7c98b01f56e69f4ffe07463f3ece57a61331ca1817bed276e8201ff7defde2bac4a9e387413b3da71f8d1de09ebac9a5490ed0bc189000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000004e70004b01987b4fd7dacab348f0b39e9ae2611000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000ef4ebe4c98404800000000000000000000000000000000000000000000000000000000066e13ff100000000000000000000000000000000000000000000000000000000000000026957de8f52722139f85b1fc83e50f75e52000cfd5e2f6a1814eaac236579b00bfce0e08ce4fc8588953f31682cb0cbddf9b74ab8f00f2e8ff6837ebeaa953f31000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000040000000000000000000000004e70004b01987b4fd7dacab348f0b39e9ae26110000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000f3cd9dd0f7b2b000000000000000000000000000000000000000000000000000000000066e13ff1000000000000000000000000000000000000000000000000000000000000000228f70f83d2d2c2012ccc8e6d043c4421144c676e19ad77b3f5d06a55532ea2d9fce0e08ce4fc8588953f31682cb0cbddf9b74ab8f00f2e8ff6837ebeaa953f310000000000000000000000000000000000000000000000000000000000000002ff0100000000000000000000000000000000000000000000000000000000000082044d8080a928627bca2a83841e6a08cac57dc3f01cce3d139d5371895f14a66f284721a3598e5b04b9282a7b1b5abd1c4b80e125256206fa48c41c6203b40defbcfbd8e01cff0b00000004000000000b0000000300000000f122840a1b74f88301535c944f9a0e7fd2bf6067db6994cf12e4495df938e6e98712a07b108eb00084d0e30db082044d8080b672c58ef0e2fb2431dfc2e188bdaccc549afd9453f907295a9b78242cc392e20797400e2cadd16e89f6490992082c84dd9200eaf4135c3af743c8eed63212d01bffea81cd8402625a0082ef4094b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080cf5c96744631e4660952c51533ff1331d8f92e6f5dfe5fc50e8ae59af5d654ba0992777ecad55c9fa7751777c9674d850059586adf1e25263c674a6affd8fbc51bff0b00000003000000000b00000003000000000b0000000300000000f86c82058484017d7840829d91946e6b8f03c407cfce46ba9847231fec40dce9308380b84440c10f1900000000000000000000000092db6815e62f6a8ef196306502c0283297f73b75000000000000000000000000000000000000000000000001ae361fc1451c000082044d80806d429321175f5602fd6772119c4ca54dc2ebf6837a3d2c7da3cf16e92cbee35f13d4a4f4d2a454164430f73b7c646d1a9ba2589463c8c4a01fbfb57190756a461bff0b00000003000000000b0000000300000000f86c820585840186a000829d91946e6b8f03c407cfce46ba9847231fec40dce9308380b84440c10f1900000000000000000000000092db6815e62f6a8ef196306502c0283297f73b75000000000000000000000000000000000000000000000006d499ec6c6338000082044d8080e7af27a209e255c5d8dac95f112ba887fae9d1e14a2e4bd2ab28c0db0405add0789ea99836c9f7006838a5c8a343c7910fa04fe62359c5840021a1e8ecacb5cb1bff0b0000000300000000f84a23840929944583010d17944f9a0e7fd2bf6067db6994cf12e4495df938e6e980a42e1a7d4d0000000000000000000000000000000000000000000000000012a07b108eb00082044d808070235f900cc55b78e1e6928ef4ce66827bf7fe71c143477be736d589379811ac253eaa21eeb61a16813aa811386885afe8290602dd5690383627722ba9d7e2211cfff86b188402c37cf0830243db941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b300000000000000000000000057df6092665eb6058de53939612413ff4b09114effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d8080c40b5b896d81653b619a8913d90eee62c5d619b4962ecdee41ea9832aa95020349132258d75204c62c8fc76d9f8283b1326d4ee913a0b723657c54fbfb1bb2f71bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9048c198402c7c8ea83135048946b2c0c7be2048daa9b5527982c29f48062b34d5880b90464b80c2f0900000000000000000000000000000000000000000000000000313295693672400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000a16c02000000000000000000000000000000000000000000000000000feb937a2911570000000000000000000000000000000000000000000000000000000066e14e3200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000a16c02000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a500000000000000000000000000000000000000000000000000000000000000010000000000000000000027104412c7152c658967a3360f0a1472e701bdbeca9e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000082044d8080b3e971ff6fd58ceb07da1d98787e1144e20bbb3a4bc4898427f95bb1883b209b79f23b51651aa2197d379428541ad7ab7f2baed6ea46c94ee62fe302d16eb4c41cff0b00000003000000000b00000003000000000b0000000300000000ee8308be6f8401820c2082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80805c8c929f5ee6b9d6d3ee54c1745ed9009dac82133aa764d8b66f952ce4af2e0072573744d41b291cc6384aa9c9b5057a4a1d979fdab2c590ea251fcdb75fc49d1bac0b00000003000000000b0000000300000000f9018f830151da8401a8a6f08302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000050b52227f968abe8cba85ec45a4bda8f65d1d1670000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000015b392fb030c00000000000000000000000000000000000000000000000000000000000000018da49930ea8edc4bf2ba83646893e29e20703a5b2534c8e97ce446e41bc6ffe682044d8080c1a1b89b29b44083c19bc4b74e77b2a9190047f3ed5ce33198f142333eb479ab19eb434a8c761191c6fe9f02162be2d0bb5e163eb2b8528d493c44413fc512671cfff86d82010584018e41208304f8d394dfb6baa334712cbbeb26b7537f62b81c2a87b1e880b8446e4bb3b5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000182044d8080fc53deb3a49af4a7f3bdbe490ec121cf4b137ee3e28bce917494eabf24b148b91aeaf809a6a552b1e7bcebbb955ed6a54d004ce4fa883b0d53c9faac21cc542b1bff0b00000003000000000b00000003000000000b00000004000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903f220840186a00083030009941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5e9924268a07bf7d21c823217668de1706f45cf6eedfaf6528e79c479eb926f00000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000cf6ea21f3928460e36e30150815f212fca2f9a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5166436f4d73464e784d7274756a31507054614c444261554c586551694453626b5a63524533687839516b68000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000418550cca2cf3bfcec208c53cdd1cc838b0ffb5c5256bba0d4166875e47c099b175f181a3a95b5c0706963513659d182ba900782a379d0eb74715719e59a6f58c11c0000000000000000000000000000000000000000000000000000000000000082044d8080e58acba608a369d18c6d73f43366584f723671662e22cd7bb1807671b3d2f71f03a1f7a8ef7d198a54345cbb4f226888cc8867e55b6b20e231e0228d71d79cb71cfff8b12b84025317c08304ac1194222228060e7efbb1d78bb5d454581910e392222286043d112b3c31b88432accbb900000000000000000000000000000000000000000000000000000000000000e6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b48eb57e000000000000000000000000000ea6e4548bb97b9237fd08bd32ad9a6f8e59eb10b82044d80808c2c47c45393f93b84a8a00f3e965a8a3a532e4d03d0da39a78f6ed20f91faf017d9d1405441a4a8b6d81421ff34ce60c607ff5843b029907fbeb58ee371f34e1cff0b0000000300000000f903f22184028dd3a78309e223941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f53d6aea38052471c5a2ab59bb49640603e17c99abdaa8a597c19480f99e27963d000000000000000000000000000000000000000000000000000044364c5bb0000000000000000000000000000673962924875bd4cbd2e0b22ed63c85cecac2e40000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4f4b582057616c6c6574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d55316142655a5a6139787756357746507855734a37416d6276714a38574a784375656f4452666567595a42790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004159b0d36df5531ae9fad1f1382129122fb7cdf909728e403412134e87d1c1542a4d2a58cc08a4005b8f8dfa3a594103b3176813122f947790cd52a2fe8df5f5c41c0000000000000000000000000000000000000000000000000000000000000082044d80805c95ef7a6c28a8e2e82f28d6d894b5b9ee5171b03a1c22968ce432462eede80b24163ce4e126a85062e602cd6e83bc9fa4855353b979476e4e8a6d495d56d5f21bff0b00000003000000000b0000000300000000ea819d8401a39de0827181943f0a9f960341dc53747d43d27e4e1a2678f7983e8084753899e982044d80803e62d33d344773f096582827acedb468e005027390ecd7e427d3bfee95cc734e4932744bfea8adcf4dabaebb2115f1d742b8f346984fc496f7e7a795cb71d6f31bff0b00000003000000000b00000003000000000b0000000300000000f86e8301c9918401aa54a0830186a09465a4b8a0927c7fd899aed24356bf83810f7b9a3f80b844535b355c000000000000000000000000a703654c31dd3bc26dff3c6ec524540c602380390000000000000000000000000000000000000000000000000002659289813c0082044d80804de874ddea4656d09ad455f64143e57454f56e7191364e43bbbceabefc4ca03002f35052b4e2b6d4be7b637a6dcb4c3b37d6e536f7a5dcc64b147469ade0037c1cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f8cb0c840b7f6358830a623f94c28a4ec9f09e4071e3707eaaca5c3754fa4f5faa80b8a48fb8b6c780b56aeeab2dc46113b09dd8069578d16c679cf639475dd6e0b8e60b7a3b21d9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000082044d808014cb2bfb965493c5dca083a997eec444184bd9f3f79aa4c555bae2f5fe5d567058f2dc8d1eb1aa371a97d86c65095e572232021dc7a3d453c2a2ec11c955092d1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903f2248402625a0083048056941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5f51a6c4d02c18d30b29fa792091edfdeeb4a51d55a18c953784a33e54d7fe50a000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000f65a8d44c6ec9823ff7be42b3cac3b457ae8d5200000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c52616262792057616c6c657400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d64696179665a6f6f7a676a64625374764a657a48467664325a39384e656669517736594a4144456a56554e4100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b37d89a9353bd6b5183ab63e9080c97cb5a24fc814b5fadf0d531acd6272dcdf00a568d77e7e5ef3b1821820166377891930eddcfd50fde885affcc1320f61d31b0000000000000000000000000000000000000000000000000000000000000082044d808076fb7660fdc1ef605bc860a4d1b98537f6bb3c18c507dffec61c663c14950ee06415ecdb58bfe14b8642ce2d909378beda578c5353356a559a6ae1f176eb204f1cffec0184028e2965825208944570f01acc78003c270b68217b819d783cc892f487179d7b0b84e6678082044d8080829f6ee74f6fcf2ad05e10267d898068704c651ec8c3af9bc1b06d48b721e8ed5d928a25a8942496fa4b557e61bf6e6552e5d95c53ae3d1ec9bcd843379689381baa0b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be708404c63aa082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080331d3409eb28ac0f3aa116833654fa85ebc0ce15d7d6045703ea5cabed548b2e15c7a437215b8e74d70939efc3842b0dd571788b3abd34449555b16d18c6b6c11caa0b00000003000000000b0000000400000000ee8308be7184019768e082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80804993bb9121b4238b115f0591a929482dd9ff7c2e6974e85901a475fef18636417feb9d1551cfb7d465738a2d001cb5f7ba47e65fd4c1ab2e6d67117fa835a3361caa0b00000003000000000b00000003000000000b0000000300000000f9018f830151db8401b466c08302c7a094555a64968e4803e27669d64e349ef3d18fca089580b901640ddedd8400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000002c7a0000000000000000000000000000000000000000000000000005d423c655aa00000000000000000000000000000000000000000000000000000000000000000010000000000000000000000003e5cb35901d40efb3c6d38aacfb0f4b0cd5258650000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000045cb32e45f28000000000000000000000000000000000000000000000000000000000000000142b576c33464f5ecbf09fa2b1575d07772c814bcebababe6c099862cafb4fccb82044d80805dd0484cc33cf12d031a9a675192d937ebc6aa79432bdedb9190f4fe84410b6029c8935f77a6f8cc99964f47afdeb4c41790eeeb636babf8e3f034742f8c98b81cff0b00000003000000000b0000000300000000f00e840172c9e0830139f594b58f5110855fbef7a715d325d60543e7d4c1814386a758d6a38000841249c58b82044d808045d3b812f522cf8f1b5a670f65c1fe24a0806c563eb3dcd9ad2bffb3e2aceeaa0884dea66e76f89a5047243e18d3bacc1a849ec983f640a958ee410c804916741bff0b0000000300000000f382010684019768e08305576d94dfb6baa334712cbbeb26b7537f62b81c2a87b1e887254db1c224400084e9ed3d3082044d80800b1d6a122d7289631fdfb4b72e4a6a86c293afa79c1f3337c9a188badcf82dbb2efba7d16402ebaa03b0059aa68f7f3a445d2fd3425cab4179d50a9d06df0aca1bffec81a78402faf0808252089433fd1acc271574f6e5c2eb4a0ea1264251ba2119860abb620a3c008082044d8080c9a1e1b5d798f7af1ffbd19e89c74fa39119c8b261e9f84fa39d9af2c67200d43ee954e07bf11fb21588f5d62e6c5c7dd8da23de14658664a4389b4fefd13f6d1cab0b0000000300000000f903f21f8402625a0083048056941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f522f0b9754d4d2585628043451776bc2eb65ac80a097992cdd46586be7aad1739000000000000000000000000000000000000000000000000000044364c5bb00000000000000000000000000033fd1acc271574f6e5c2eb4a0ea1264251ba21190000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c52616262792057616c6c657400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d5374533974704d3479584e42543463474437554b4a77616a4b67443363444b7971347a71764e45657248434d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414ed93ff5c116db4089def64910db6449c30e8196e78031d25816d7e4f92b119f62e583f8b2003c9b042427ace41ef3a6a1619130f2fcc00fee151d0acbc804191b0000000000000000000000000000000000000000000000000000000000000082044d80805341f33dd6e64568431b0846bbc656fdc618ce107b9812dd2bf3f2e91c44f0240c3281cd38136e751fee0902452bca959da52d667a092d2fbc70ef493af7cef81bff"), + ForcedGlobalExitRoot: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + ForcedTimestamp: 0, + ForcedBlockHashL1: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + }, + { + Transactions: common.FromHex("0x0b00000000000000000b0000000300000000f9014f830115518401bcf240830596219473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e140a500000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e10000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000731412d863de818aedc2ac00000000000000000000000000000000000000000ae12c46db9b6fc047c306c500000082044d80801d46c877c9b34d2ab10700459c548bbdd97b4c71ddaaae59eb4aa9829063a3456742ada3207659a4d8499dbb6600f91b9e7ecef2807fb739d2a63af6538555161cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f901b30f84019bfcc083039dd894b58f5110855fbef7a715d325d60543e7d4c18143870b7f8bdfda5884b901845190563600000000000000000000000090321b082c1da51d0ec59ec7d02f5b65d1c70ed3000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000b7951c00000000000000000000000090321b082c1da51d0ec59ec7d02f5b65d1c70ed300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000001490321b082c1da51d0ec59ec7d02f5b65d1c70ed3000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200010000000000000000000000000000000000000000000000000000000000061a8000000000000000000000000000000000000000000000000000000000000082044d8080fc542a4b3b5caadfd5c04117a3f8512c467f8a59866aaea320aa3b5bd891f92b068e84eeb6846cea45c443f478e7c48968d14a2034cf6ea9005facf652f51ea91bff0b00000003000000000b0000000400000000f86c81d08402625a0083011108940d1e753a25ebda689453309112904807625befbe80b844095ea7b300000000000000000000000031c2f6fcff4f8759b3bd5bf0e1084a055615c768ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d8080a984967c135177e8261c40769322acd476a9a79a07d7266cf441b0329b3b009533e8693879324ede3e8028f2baa8c51ea3aa1212ad5bf5c94dc49f14a2831ff61bffef808402625a0082c07194a06e1351e2fd2d45b5d35633ca7ecf328684a109863691d6afc000840333f63e82044d80803f154443bd814efa2e144586740cbb0024d5f3ea553b60c131823aa61e2be4e73adab50e89b656b3c7e4c9e2f511754dc45d14ee7894de88498f2cd23e825a501bff0b00000003000000000b00000003000000000b000000030000ae460b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9044d81d18402625a0083044f4e94b89a6778d1efe7a5b7096757a21b810cc2886fa180b904243593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e141e700000000000000000000000000000000000000000000000000000000000000030a080c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000d1e753a25ebda689453309112904807625befbe000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006708cdc50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b89a6778d1efe7a5b7096757a21b810cc2886fa10000000000000000000000000000000000000000000000000000000066e147cd00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000041ce9c062459de7685af53cae3d9c022ce609093f04ce875111314f8aa1366bfd579a369788bb5ca6b5cf908cff33ffda44e1afa0c4165caab252665bb02ece9861b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000007355b876be771900000000000000000000000000000000000000000000000000001541e01138ed100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000d1e753a25ebda689453309112904807625befbe0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ab07bd0ac2502a7fd0a5005b420b96f94104fcf80000000000000000000000000000000000000000000000000001541e01138ed182044d808002d7ed117ceaf8a71c9325711c8ebe173ffb02a537a631170f2bbb140ad8532b2e3f3dcec0e5c85384fbb643e3508cce4625696ffedfb0f5d16c517a11ea1bdd1bff0b00000003000000000b0000000300000000f86a808402d8fce182c16f94652a38fa87f60a122aef360eeefcaf6258eddf6a80b844095ea7b30000000000000000000000000a6b1904369fe59e002ad0713ae89d4e3df5a7cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d808096c77cebc8ba97dbcb33a46b05001e3f26206f91c497007900507d8297919a5e042728715790ea437a06e937ffb322b65031db0ba633dcbe0351a1ca78a70c991cff0b00000003000000000b0000000300000000f86b1a8402d06b49830243db941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b300000000000000000000000057df6092665eb6058de53939612413ff4b09114effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d8080005ee956dfbd7ecdf8ca2c5fa9b5a21aedb2a425d4d1568032332b5ec3e689fa1988b83c9fc83a17142953dfcfd62d2e79c15f761b013b042537a77602933ddf1cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f90a6e8204ae84019a762083023cc494709944a48caf83535e43471680fda4905fb3920a80b90a44437b91160000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000074000000000000000000000000000000000000000000000000000000000000008e000000000000000000000000000000000000000000000000000000000000001641a0a0b3e000000000000000000000000c52eea00154b4ff1ebbf8ba39fde37f1ac3b9fd46575f2c29a636fa55eaa8aa86706b6d3c33a21e0e79e1647259718dac070efc40000000000000000000000000000000000000000000000000000000066e140ab00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000937596f639e540000000000000000000000000000000000000000000000000000000000000004172b3eb5cd3fc78267d5672b9ad5317286aa8f3d66e55d866dcdbc9e5d1255c6d26535ad3d1b26b4b8fbc6bfe8cc543d3dd612138909a4aeb47117f908bbb30321c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001641a0a0b3e000000000000000000000000bc6471e88d8afe936a45beb8bd20a210ebef68221998d1c5e381c1edec0feec35e8bc3c9d7cb39ace96d9279b6f9a07a7ea454240000000000000000000000000000000000000000000000000000000066e140a800000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000009463846edd3200000000000000000000000000000000000000000000000000000000000000004168362b70473277d046be65bc395f0b0c8b98173b46adb4192c6f4127df5d1867494705d3ceac8368afe2ece88a7545794f4e51fd431667febdddfd66a2b152aa1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001641a0a0b3e000000000000000000000000a924847354c551c79bae7e75529364ba0449e51a39001761c965b7719e6b39822f09c891d103009d2703a4989cf7bc2e007582fc0000000000000000000000000000000000000000000000000000000066e140ac00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000945b55a7e0000000000000000000000000000000000000000000000000000000000000000000413dc56655400801a9c210ecdecebbc3da45c92de696f13b97bf5e73b6044e27366fa1e651e74e5f57d6a610791ec51a8eb5f2e8c482a5b5e22f939c0f0b1eeb391c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001641a0a0b3e000000000000000000000000c9b494d3c6ea3fd42779df9a136db10374c98d80c7dd05baff9d2948c0f215ffa84c4de82694e959f2fa7b70f2f2a1f398381e900000000000000000000000000000000000000000000000000000000066e140af00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000009455be8a47413a000000000000000000000000000000000000000000000000000000000000004152452f2785fe8cc8876e4d7c70e6656fa7d2fd5d48d1ba751829ec73a2ac5a607e300939c863047c82dc39ad2e28db75546263bf6c8fa3aa3ae7009607e8db191c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001641a0a0b3e0000000000000000000000006b56e47dccfbc82d63df3da417d26e8b1b877f0f5d7cab00186de4dd836b0e3f89da0565a5a68597e1772792cd3c63dc4505d9bb0000000000000000000000000000000000000000000000000000000066e140a700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000093a95d5afc400000000000000000000000000000000000000000000000000000000000000000418ac1031836570d4ca35d6a2cbfc9e7cc8ee5da912831a8769c87b4b5df1b74604428cea4bc23413a82b246008ae57b93c1f917ab594b6c63c60b009db44cfe871c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e400aae33f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005750fe3cc44fb71eb6dcda12a40ba96a779ff3292e4ee0b83e480607fa18a253c25dc66d59c09b65362270bd4ba318502886dd51323d42cc672bb118a2f377d937701240fd1a97e184401c372d22d78050175399403b5fde50faf03bab1c7573ac1c918bd24675fed06d7c1ca44882492b3ae0462c3b0c6e7cae84702c2d9708be6812ce7d565ac54474e619953d3b2588ad818c5b7328ec16de6304fa43badee0000000000000000000000000000000000000000000000000000000082044d8080aa51d9561dc2f3f50bc646119dc5d2288be28526b01facb218bfe12777bb470b73d45a28e9f367be9c9ab1e9823735251e7c58ab8b527dd8864c32531d9ef4431cff0b0000000300000000f8723c8401a524808301d9779454c3ef8ce719ed72c2050ee42a05aca9e9aa1e1f8701c6bf52634000b8441d7df19100000000000000000000000030280ba7411118147b835ea31029481b01284175000000000000000000000000000000000000000000000000000000000000000182044d8080c4a02d3c640f35c2628bf24777bcb8157adac99f25a33a66aa0833e12e0692ba77820fa9aa795375809f045555ba5cc8167c7891ed03506dc2c67b64ee7112be1bff0b0000000300000000f86c81d28402625a00830110e194a8ce8aee21bc2a48a5ef670afcc9274c7bbbc03580b844095ea7b300000000000000000000000031c2f6fcff4f8759b3bd5bf0e1084a055615c768ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d80804660394e29ceeb7d28d161134a042cb966b23c6ed12f2bf645022ada6f27fc3143c01a84374564d949e10dc0df1c9c103f78651b831bd12482d8e3442f024a881cff0b0000000300000000f9048c1b8402c9f9b583138a26946b2c0c7be2048daa9b5527982c29f48062b34d5880b90464b80c2f090000000000000000000000000000000000000000000000000031329898bf72400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000990e21000000000000000000000000000000000000000000000000000f1b8b1e74b2040000000000000000000000000000000000000000000000000000000066e14f0200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000990e21000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a500000000000000000000000000000000000000000000000000000000000000010000000000000000000027104412c7152c658967a3360f0a1472e701bdbeca9e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000082044d8080c52fcc136946cb5b9089d62a52d13d661c9588fb367523e6404b34932fc651fd108b70a62f080f0a280357b74a7eabc52b5b807bee6d4ee85f85bdffdce996441bff0b0000000300000000f8cb018402c9f9b58302724d940a6b1904369fe59e002ad0713ae89d4e3df5a7cf80b8a491695586000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003148e44ea107a00000000000000000000000000000000000000000000000000031167b0f044b80000000000000000000000000000000000000000000000000000000066ea7b6882044d8080c54a7d2f5e3aaa2f9e932ea0b5c6fbdb5166c3bb4226725e8cf0c5acbb677a637ce0e52990ef54755786e983d449c7c6adc824f57fe7a50f4960fba85ec8d44c1cfff86b198402c4c487830243db941e4a5963abfd975d8c9021ce480b42188849d41d80b844095ea7b300000000000000000000000057df6092665eb6058de53939612413ff4b09114effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82044d80805a91813eb1f40d2394eee6ad6410d547b8189e6be17bf23c90dbc1d01ba6edff103a22f55e0986304b0d36b367e905b3a5ecbf94755bb2670b563e25b6d174921cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9048c1a8402b3d0168313849b946b2c0c7be2048daa9b5527982c29f48062b34d5880b90464b80c2f0900000000000000000000000000000000000000000000000000313298f36971400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000991c85000000000000000000000000000000000000000000000000000f1cf6bcc4e4c20000000000000000000000000000000000000000000000000000000066e14f1a00000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000991c85000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f304c7323d9101cd90c05696c97a4cf984a9f6a500000000000000000000000000000000000000000000000000000000000000010000000000000000000027104412c7152c658967a3360f0a1472e701bdbeca9e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000001e4a5963abfd975d8c9021ce480b42188849d41d0000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000000000000000000000000000000000000000000082044d8080f24aed3d22f15fbcc57fafe44d53a1a9e60f4ba51291fa4f22b53e20d79ddfea5553060fbced61fca5aa4d1d333d067b1c3904ecae9d9c71c47d5693a257e8cc1bff0b00000003000000000b00000003000000000b00000003000000000b0000000400000000f9044d81d38402faf08083052a5194b89a6778d1efe7a5b7096757a21b810cc2886fa180b904243593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000066e1422900000000000000000000000000000000000000000000000000000000000000030a000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc035000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006708cdfc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b89a6778d1efe7a5b7096757a21b810cc2886fa10000000000000000000000000000000000000000000000000000000066e1480400000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000004159610a05dec35ea87031acdfdc775d81b7d21424be26f4198d7963c054b6486102f7bb552f766607a24314a4d4674ec5a8b6cae07000ecc918b6ef1b92b1dad51c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000002a055b00000000000000000000000000000000000000000000000000042ae8f761201200000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350001f44f9a0e7fd2bf6067db6994cf12e4495df938e6e90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ab07bd0ac2502a7fd0a5005b420b96f94104fcf800000000000000000000000000000000000000000000000000042ae8f761201282044d8080fa6c7ff44fb469090f7f796a4dd27d3dee25540259f1b8307ce80915f329281f1643255ab2b3498b2827900ca99996c2cf60c4e2b058cab244d2783263fc07c31bff0b00000003000000000b0000000300000000ee8308be728401a6ab2082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80808cae305056836f1b109e76a38a29a95e699ca239c636e820c5221cc0bfc1ae8e08b630842cda6c38fb50cbb4676c4a61e00b01f55b68f27f51123de8cffd652e1baae96a8402faf08082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080d0a9dbdd118b85ea2dd4a860354598e348085c2c448ffe77b9b9ebfc87bbd4b3416de323e24a7e3ff04007cad827b524e6054943ac7f7d51942392f1c1b9199f1bff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f903f2028402faf0808304e5ea941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b903c4d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000a26ed90933f9410c967efc64452681f5fb2f110de3cb09ff94ef99619912ce386a0f42d2f466e7d3e480d7129bacc121000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000a703654c31dd3bc26dff3c6ec524540c602380390000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008496e6a65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d55656151777944734138474a4858594c794248344333565564504d6f5142474c457a3648476d423152696f5600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f2eb6584e3d4bfdae352806a3a5f344ad70fe77bacc930a7a40b925f409ffe4f4c8a88c6e07314b8a109757251949b0932d806ef8a608b54f8dee0f447ba37381c0000000000000000000000000000000000000000000000000000000000000082044d8080f2eed7742d047224403c36decbbc5c7a6f1bf7f67057cb078633cf30c96449ac7d2a665aba4252c99bba96742960a7228167514b354f24d9a97b6f5777197bb11cff0b00000003000000000b00000003000000000b0000000300000000e93a8402faf08082ef4094b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080f512c7a6222205683b9012666c5455c998de80ca017dcae80b4b8353f2d9c6e7444e694afe71445e1f73011c9416982fcd2013de37f5a6356b62d4786b2e01dd1bff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be738401acc5a082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80800d61afecc903851650d8243af5e3ec20654db98d9c0fe14f7cdb86d9e50f2fd7188796e923b771c47a7674636d3a9b4e14fce759226a2445a470836e8aeb1fc31babf9014f830115528401f78a408305962d9473903fec691a80ec47bc830bf3f0bad127a06e3080b90124782661bc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000066e1415a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000000000000000000000ea034fb02eb1808c2cc3adbc15f447b93cbe08e1000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000072fa529d46831fa4ea1365c0000000000000000000000000000000000000000ae065f41c16c30440f13ba800000082044d80806bfcacaee014344f6c6bf79fa3af76f7baf85705a22c6b6022aa3d2275044f515e385563fc9f791dd33b51658d81b061b0e582896fd33f745f00b4208500200a1cff0b00000003000000000b0000000300000000ec038402faf080827b0c945e809a85aa182a9921edd10a4163745bb3e362848704adcdf52b50068082044d808072e2f7ccdfbf0c9967dc8d2983a80a846f075f22068fc67764df88c72dfc3f206fa80b91470d803b7f94b181278d72809f042b83b4eb3b242998ac89f12491ee1caa0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000400000000e9768402faf08082f02694b8af4fa4feabaa02a09d146e4f871ea4a0a41c048084a66f42c082044d8080f7c406958a3b283f040ba8e3b99e77961e478a6849858a513f9950fe2474271660a78cdff6ac241cd222b97a27685de4da616036e123ebe6947bc531590760df1bff0b0000000300000000f86c81d48402faf080830163f89437eaa0ef3549a5bb7d431be78a3d99bd360d19e580b844095ea7b300000000000000000000000031c2f6fcff4f8759b3bd5bf0e1084a055615c76800000000000000000000000000000000000000000000000000000000000927c082044d8080e3b16d5bfd529eaefa4d8ad988900a3ac14748b9b787645fe42498619e4b85001f4ec92e43b5ccdd3e61780fee963ddf0fbaa9b6b7bde93e6e6bbda3abed8d811cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9010d8263678401ab7d80831e848094f6ad3ccf71abb3e12becf6b3d2a74c963859adcd80b8e4bc651188000000000000000000000000a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0350000000000000000000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e900000000000000000000000033128fa08f5e0545f4714434b53bdb5e98f624740000000000000000000000000000000000000000000000000000000066e143f80000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000098176ab6ed58bc000000000000000000000000000000000000000000000000000000000000000082044d8080f44f214824c6406324f3caf06ca6aad692bfbce83fb69685723885047c1978a0009f698a2561f6fd301af75d83ba928ae9718dd2d175bee9e2a70260a91980151cff0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000ee8308be748404cace8082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d80802c732f67940b07b16d4ff7c2ed509c81df86e1aa948e04a97f4c1cc90de679a07a50c2c1df215261e32b211a422ec0cc62994adcc02962b201a058db5485e0661bab0b00000003000000000b0000000300000000ee8308be75840198ef8082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a0008082044d8080365e902ec6677c927098c294ccd1808009bd6baacd6cc18272b1f8e4a3a88a940c88b52ac0c9568948d082d6298474169873f7fa0dc09bd1db8b6be09ec6016d1cab0b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b00000003000000000b0000000300000000f86a1384018e412082b4cb94c5015b9d9161dca7e18e32f6f25c4ad850731fd480b844095ea7b30000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae0000000000000000000000000000000000000000000000003ddd14cefbe04ae482044d808090ed685bc17c39f23da886bec59a15265f557d06b5388c26f489d199e2c9dbbd4e5964f35d8272171d695b20f1aece1e56bad6a0f93b5992ff69f774527ac2751cff0b00000003000000000b00000003000000000b00000003000000000b0000000300000000f9088c1484026ccc90830c45a3941231deb6f5749ef6ce6943a275a1d3e7486f4eae80b908642c57e884ba378f2fce937d7762985fbb68cb4ae55a699ae984949e2401b561a7327abe8f00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000003f2825e0ffd694adee76c9581fe707606074aa900000000000000000000000000000000000000000000000000006b01f2257e4a2000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000086d746f7073776170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863346639646134323361626336393237393937383338313533656131386631616266396435613938000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000b49ead76fe09967d7ca0dbcef3c3a06eb3aa0cb4000000000000000000000000b49ead76fe09967d7ca0dbcef3c3a06eb3aa0cb4000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd40000000000000000000000000000000000000000000000003ddd14cefbe04ae400000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000084eedd56e1000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000000000000000000000000000001fac95eaffefc40000000000000000000000000000000000000000000000000007eb257abffbf1000000000000000000000000c4f9da423abc6927997838153ea18f1abf9d5a98000000000000000000000000000000000000000000000000000000000000000000000000000000006a000f20005980200259b80c51020030400010680000000000000000000000006a000f20005980200259b80c5102003040001068000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003db57d1396205f2f00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e4e3ead59e0000000000000000000000005f0000d4780a00d2dce0a00004000800cb0e5041000000000000000000000000c5015b9d9161dca7e18e32f6f25c4ad850731fd4000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000003db57d1396205f2f0000000000000000000000000000000000000000000000000006b01f2257e4a20000000000000000000000000000000000000000000000000006c16a70441813d770acd5d9f04dc897d42ce2f295f18700000000000000000000000000f2a81500000000000000000000000000000000000000000000000000000000000000008c208b7b5625d78deb49240ef28126cbe2738098100000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000240f6ad3ccf71abb3e12becf6b3d2a74c963859adcd00000140008401000000000b00000000000000000000000000000000000000000000000000000000c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000005f0000d4780a00d2dce0a00004000800cb0e50410000000000000000000000000000000000000000000000000000000066ea7c4c0000000000000000000000000000000000000000000000003db57d1396205f2f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003cc5015b9d9161dca7e18e32f6f25c4ad850731fd4a8ce8aee21bc2a48a5ef670afcc9274c7bbbc0354f9a0e7fd2bf6067db6994cf12e4495df938e6e9000000004f9a0e7fd2bf6067db6994cf12e4495df938e6e90000004000040000ff000007000000000000000000000000000000000000000000000000000000002e1a7d4d0000000000000000000000000000000000000000000000000006c16a704418136a000f20005980200259b80c51020030400010680000002000000000ff04000900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082044d8080d8991732646a94259a6feeec42e5a4fc65fd2a806c109c58b6da6c6b9aa175a024d45fb5eb3d774b1b9a50a6ee7dd40b8104d1caec3bdbe82885a18aea9308621bff0b0000000300000000f8b12e840263c1608304ac1194222228060e7efbb1d78bb5d454581910e39222228614edc409dd73b88432accbb900000000000000000000000000000000000000000000000000000000000000c600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000246139ca80000000000000000000000000000532b4600bc0667326bbec62c1f55a4e962b21a782044d8080dd3a56e7bb40299dab4d821c8860cde90383a218003cf93ada19e6ac7762fbb2248a75809c92a48152c303d8197289a58956d0318ae6d816ba67054d954ad5181cff0b0000000300000000"), + ForcedGlobalExitRoot: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + ForcedTimestamp: 0, + ForcedBlockHashL1: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + }, + }, + InitSequencedBatch: 2116101, + L2Coinbase: common.HexToAddress("0x148Ee7dAF16574cD020aFa34CC658f8F3fbd2800"), + MaxSequenceTimestamp: 1726038493, + }, + }, +} + +type decodePreEtrogSequencesTestCase struct { + Input string + Expected SequenceBatchesCalldataPreEtrog +} + +var decodePreEtrogSequenceBatchesCallDataTestCases = []decodePreEtrogSequencesTestCase{ + { + Input: "0x5e9145c90000000000000000000000000000000000000000000000000000000000000040000000000000000000000000148ee7daf16574cd020afa34cc658f8f3fbd28000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000800399065677ecf314502940629335726a30609193556a589f6c185648c3d528be00000000000000000000000000000000000000000000000000000000641dde270000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000087bf9056880808316e360942a3dd3eb832af982ec71669e178424b10dca2ede80b905442cffd02e0000000000000000000000000000000000000000000000000000000000000000ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d3021ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85e58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a193440eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968ffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f839867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756afcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0f9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5f8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf8923490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99cc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8beccda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d22733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981fe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0b46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0c65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2f4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd95a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e3774df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652cdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618db8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d0838c5655cb21c6cb83313b5a631175dff4963772cce9108188b34ac87c81c41e662ee4dd2dd7b2bc707961b1e646c4047669dcb6584f0d8d770daf5d7e7deb2e388ab20e2573d171a88108e79d820e98f26c0b84aa8b2f4aa4968dbb818ea32293237c50ba75ee485f4c22adf2f741400bdf8d6a9cc7df7ecae576221665d7358448818bb4ae4562849e949e17ac16e0be16688e156b5cf15e098c627c0056a90000000000000000000000000000000000000000000000000000000000000000927e6ceecb5b20935d26fbfc57002a59298b6e82640e8d652809e06854d7a81f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000001dba1131000664b884a1ba238464159892252d3a000000000000000000000000000000000000000000000000016345785d8a00000000000000000000000000000000000000000000000000000000000000000520000000000000000000000000000000000000000000000000000000000000000082044d8080e3a5f558b3cf098afea2afb2d3366fe13d26bdb22732a91e2a0c10c7f97a6fbb1e6debddee91c6ce4fe8d0ba6f17b2e336080e5b6b70bd5d88bb5dca5121966d1cf7018501d2b4518082940e941dba1131000664b884a1ba238464159892252d3a8091457468657265756d207363616c6573212182044d8080954863b2ae756eb3dfbbce5acaa53009dfa5d05c2f2fedaeefb8b386924d78c864b0db49d3626fc3676a5cb91bd00cdeb12b73ada1bad8695d8a35d4667ad3f51bf90185028501dea013808301f91f8080b90170608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063d46300fd1461003b578063ee919d5014610059575b600080fd5b610043610075565b60405161005091906100a1565b60405180910390f35b610073600480360381019061006e91906100ed565b61007e565b005b60008054905090565b8060008190555050565b6000819050919050565b61009b81610088565b82525050565b60006020820190506100b66000830184610092565b92915050565b600080fd5b6100ca81610088565b81146100d557600080fd5b50565b6000813590506100e7816100c1565b92915050565b600060208284031215610103576101026100bc565b5b6000610111848285016100d8565b9150509291505056fea2646970667358221220f5cae3e6573396dec6ce732ba576ffffc34b0f6b0a10df2f0dc326c69796bf1c64736f6c6343000812003382044d808095271051bfa721a7b6b0c2180aafbaf905f85e1398badb471718c376b695d5ef60fa923564459177637555b43253309231b6f8e10e3ecbac37ac5854da04934e1bf84a038501b44e560082b91494b33dcb71fc49969473ded72bc361f6be8ee149d180a4ee919d50000000000000000000000000000000000000000000000000000000000000003282044d80803458433f99b626a532cc9aebaae875ecd606cbb978da7ddace53b817fe8e758972adbadb7933a580acda21b790bf5c2d04264ad56dc4b453316b0a6fa8fa17b71c0000000000", + Expected: SequenceBatchesCalldataPreEtrog{ + Batches: []SequencedBatchPreEtrog{ + { + Transactions: common.FromHex("0xf9056880808316e360942a3dd3eb832af982ec71669e178424b10dca2ede80b905442cffd02e0000000000000000000000000000000000000000000000000000000000000000ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d3021ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85e58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a193440eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968ffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f839867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756afcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0f9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5f8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf8923490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99cc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8beccda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d22733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981fe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0b46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0c65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2f4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd95a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e3774df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652cdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618db8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d0838c5655cb21c6cb83313b5a631175dff4963772cce9108188b34ac87c81c41e662ee4dd2dd7b2bc707961b1e646c4047669dcb6584f0d8d770daf5d7e7deb2e388ab20e2573d171a88108e79d820e98f26c0b84aa8b2f4aa4968dbb818ea32293237c50ba75ee485f4c22adf2f741400bdf8d6a9cc7df7ecae576221665d7358448818bb4ae4562849e949e17ac16e0be16688e156b5cf15e098c627c0056a90000000000000000000000000000000000000000000000000000000000000000927e6ceecb5b20935d26fbfc57002a59298b6e82640e8d652809e06854d7a81f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000001dba1131000664b884a1ba238464159892252d3a000000000000000000000000000000000000000000000000016345785d8a00000000000000000000000000000000000000000000000000000000000000000520000000000000000000000000000000000000000000000000000000000000000082044d8080e3a5f558b3cf098afea2afb2d3366fe13d26bdb22732a91e2a0c10c7f97a6fbb1e6debddee91c6ce4fe8d0ba6f17b2e336080e5b6b70bd5d88bb5dca5121966d1cf7018501d2b4518082940e941dba1131000664b884a1ba238464159892252d3a8091457468657265756d207363616c6573212182044d8080954863b2ae756eb3dfbbce5acaa53009dfa5d05c2f2fedaeefb8b386924d78c864b0db49d3626fc3676a5cb91bd00cdeb12b73ada1bad8695d8a35d4667ad3f51bf90185028501dea013808301f91f8080b90170608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063d46300fd1461003b578063ee919d5014610059575b600080fd5b610043610075565b60405161005091906100a1565b60405180910390f35b610073600480360381019061006e91906100ed565b61007e565b005b60008054905090565b8060008190555050565b6000819050919050565b61009b81610088565b82525050565b60006020820190506100b66000830184610092565b92915050565b600080fd5b6100ca81610088565b81146100d557600080fd5b50565b6000813590506100e7816100c1565b92915050565b600060208284031215610103576101026100bc565b5b6000610111848285016100d8565b9150509291505056fea2646970667358221220f5cae3e6573396dec6ce732ba576ffffc34b0f6b0a10df2f0dc326c69796bf1c64736f6c6343000812003382044d808095271051bfa721a7b6b0c2180aafbaf905f85e1398badb471718c376b695d5ef60fa923564459177637555b43253309231b6f8e10e3ecbac37ac5854da04934e1bf84a038501b44e560082b91494b33dcb71fc49969473ded72bc361f6be8ee149d180a4ee919d50000000000000000000000000000000000000000000000000000000000000003282044d80803458433f99b626a532cc9aebaae875ecd606cbb978da7ddace53b817fe8e758972adbadb7933a580acda21b790bf5c2d04264ad56dc4b453316b0a6fa8fa17b71c"), + GlobalExitRoot: common.HexToHash("0x0399065677ecf314502940629335726a30609193556a589f6c185648c3d528be"), + Timestamp: 1679679015, + MinForcedTimestamp: 0, + }, + }, + L2Coinbase: common.HexToAddress("0x148Ee7dAF16574cD020aFa34CC658f8F3fbd2800"), + }, + }, +} diff --git a/zk/syncer/l1_syncer.go b/zk/syncer/l1_syncer.go index 2a75781b722..b0b9a022c0d 100644 --- a/zk/syncer/l1_syncer.go +++ b/zk/syncer/l1_syncer.go @@ -9,6 +9,7 @@ import ( "time" "github.com/gateway-fm/cdk-erigon-lib/common" + "github.com/iden3/go-iden3-crypto/keccak256" ethereum "github.com/ledgerwatch/erigon" "github.com/ledgerwatch/log/v3" @@ -25,11 +26,14 @@ var ( var errorShortResponseLT32 = fmt.Errorf("response too short to contain hash data") var errorShortResponseLT96 = fmt.Errorf("response too short to contain last batch number data") -const rollupSequencedBatchesSignature = "0x25280169" // hardcoded abi signature -const globalExitRootManager = "0xd02103ca" -const rollupManager = "0x49b7b802" -const admin = "0xf851a440" -const trustedSequencer = "0xcfa8ed47" +const ( + rollupSequencedBatchesSignature = "0x25280169" // hardcoded abi signature + globalExitRootManager = "0xd02103ca" + rollupManager = "0x49b7b802" + admin = "0xf851a440" + trustedSequencer = "0xcfa8ed47" + sequencedBatchesMapSignature = "0xb4d63f58" +) type IEtherman interface { HeaderByNumber(ctx context.Context, blockNumber *big.Int) (*ethTypes.Header, error) @@ -38,6 +42,7 @@ type IEtherman interface { CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) TransactionByHash(ctx context.Context, hash common.Hash) (ethTypes.Transaction, bool, error) TransactionReceipt(ctx context.Context, txHash common.Hash) (*ethTypes.Receipt, error) + StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) } type fetchJob struct { @@ -67,11 +72,12 @@ type L1Syncer struct { isSyncStarted atomic.Bool isDownloading atomic.Bool lastCheckedL1Block atomic.Uint64 + wgRunLoopDone sync.WaitGroup + flagStop atomic.Bool // Channels - logsChan chan []ethTypes.Log - progressMessageChan chan string - quit chan struct{} + logsChan chan []ethTypes.Log + logsChanProgress chan string highestBlockType string // finalized, latest, safe } @@ -86,9 +92,8 @@ func NewL1Syncer(ctx context.Context, etherMans []IEtherman, l1ContractAddresses topics: topics, blockRange: blockRange, queryDelay: queryDelay, - progressMessageChan: make(chan string), logsChan: make(chan []ethTypes.Log), - quit: make(chan struct{}), + logsChanProgress: make(chan string), highestBlockType: highestBlockType, } } @@ -119,8 +124,26 @@ func (s *L1Syncer) GetLastCheckedL1Block() uint64 { return s.lastCheckedL1Block.Load() } -func (s *L1Syncer) Stop() { - s.quit <- struct{}{} +func (s *L1Syncer) StopQueryBlocks() { + s.flagStop.Store(true) +} + +func (s *L1Syncer) ConsumeQueryBlocks() { + for { + select { + case <-s.logsChan: + case <-s.logsChanProgress: + default: + if !s.isSyncStarted.Load() { + return + } + time.Sleep(time.Second) + } + } +} + +func (s *L1Syncer) WaitQueryBlocksToFinish() { + s.wgRunLoopDone.Wait() } // Channels @@ -129,32 +152,35 @@ func (s *L1Syncer) GetLogsChan() chan []ethTypes.Log { } func (s *L1Syncer) GetProgressMessageChan() chan string { - return s.progressMessageChan + return s.logsChanProgress } -func (s *L1Syncer) Run(lastCheckedBlock uint64) { +func (s *L1Syncer) RunQueryBlocks(lastCheckedBlock uint64) { //if already started, don't start another thread if s.isSyncStarted.Load() { return } + s.isSyncStarted.Store(true) + // set it to true to catch the first cycle run case where the check can pass before the latest block is checked s.isDownloading.Store(true) s.lastCheckedL1Block.Store(lastCheckedBlock) + s.wgRunLoopDone.Add(1) + s.flagStop.Store(false) + //start a thread to cheack for new l1 block in interval go func() { - s.isSyncStarted.Store(true) defer s.isSyncStarted.Store(false) + defer s.wgRunLoopDone.Done() log.Info("Starting L1 syncer thread") defer log.Info("Stopping L1 syncer thread") for { - select { - case <-s.quit: + if s.flagStop.Load() { return - default: } latestL1Block, err := s.getLatestL1Block() @@ -192,7 +218,18 @@ func (s *L1Syncer) GetTransaction(hash common.Hash) (ethTypes.Transaction, bool, return em.TransactionByHash(context.Background(), hash) } -func (s *L1Syncer) GetOldAccInputHash(ctx context.Context, addr *common.Address, rollupId, batchNum uint64) (common.Hash, error) { +func (s *L1Syncer) GetPreElderberryAccInputHash(ctx context.Context, addr *common.Address, batchNum uint64) (common.Hash, error) { + h, err := s.callSequencedBatchesMap(ctx, addr, batchNum) + if err != nil { + return common.Hash{}, err + } + + return h, nil +} + +// returns accInputHash only if the batch matches the last batch in sequence +// on Etrrof the rollup contract was changed so data is taken differently +func (s *L1Syncer) GetElderberryAccInputHash(ctx context.Context, addr *common.Address, rollupId, batchNum uint64) (common.Hash, error) { h, _, err := s.callGetRollupSequencedBatches(ctx, addr, rollupId, batchNum) if err != nil { return common.Hash{}, err @@ -323,12 +360,15 @@ func (s *L1Syncer) queryBlocks() error { low += s.blockRange + 1 } + wg := sync.WaitGroup{} stop := make(chan bool) jobs := make(chan fetchJob, len(fetches)) results := make(chan jobResult, len(fetches)) + defer close(results) + wg.Add(batchWorkers) for i := 0; i < batchWorkers; i++ { - go s.getSequencedLogs(jobs, results, stop) + go s.getSequencedLogs(jobs, results, stop, &wg) } for _, fetch := range fetches { @@ -337,20 +377,27 @@ func (s *L1Syncer) queryBlocks() error { close(jobs) ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + + var err error var progress uint64 = 0 + aimingFor := s.latestL1Block - startBlock complete := 0 loop: for { select { case <-s.ctx.Done(): - close(stop) break loop case res := <-results: + if s.flagStop.Load() { + break loop + } + complete++ if res.Error != nil { - close(stop) - return res.Error + err = res.Error + break loop } progress += res.Size if len(res.Logs) > 0 { @@ -359,21 +406,25 @@ loop: if complete == len(fetches) { // we've got all the results we need - close(stop) break loop } case <-ticker.C: if aimingFor == 0 { continue } - s.progressMessageChan <- fmt.Sprintf("L1 Blocks processed progress (amounts): %d/%d (%d%%)", progress, aimingFor, (progress*100)/aimingFor) + s.logsChanProgress <- fmt.Sprintf("L1 Blocks processed progress (amounts): %d/%d (%d%%)", progress, aimingFor, (progress*100)/aimingFor) } } - return nil + close(stop) + wg.Wait() + + return err } -func (s *L1Syncer) getSequencedLogs(jobs <-chan fetchJob, results chan jobResult, stop chan bool) { +func (s *L1Syncer) getSequencedLogs(jobs <-chan fetchJob, results chan jobResult, stop chan bool, wg *sync.WaitGroup) { + defer wg.Done() + for { select { case <-stop: @@ -410,7 +461,6 @@ func (s *L1Syncer) getSequencedLogs(jobs <-chan fetchJob, results chan jobResult } break } - results <- jobResult{ Size: j.To - j.From, Error: nil, @@ -420,6 +470,34 @@ func (s *L1Syncer) getSequencedLogs(jobs <-chan fetchJob, results chan jobResult } } +// calls the old rollup contract to get the accInputHash for a certain batch +// returns the accInputHash and lastBatchNumber +func (s *L1Syncer) callSequencedBatchesMap(ctx context.Context, addr *common.Address, batchNum uint64) (accInputHash common.Hash, err error) { + mapKeyHex := fmt.Sprintf("%064x%064x", batchNum, 114 /* _legacySequencedBatches slot*/) + mapKey := keccak256.Hash(common.FromHex(mapKeyHex)) + mkh := common.BytesToHash(mapKey) + + em := s.getNextEtherman() + + resp, err := em.StorageAt(ctx, *addr, mkh, nil) + if err != nil { + return + } + + if err != nil { + return + } + + if len(resp) < 32 { + return + } + accInputHash = common.BytesToHash(resp[:32]) + + return +} + +// calls the rollup contract to get the accInputHash for a certain batch +// returns the accInputHash and lastBatchNumber func (s *L1Syncer) callGetRollupSequencedBatches(ctx context.Context, addr *common.Address, rollupId, batchNum uint64) (common.Hash, uint64, error) { rollupID := fmt.Sprintf("%064x", rollupId) batchNumber := fmt.Sprintf("%064x", batchNum) @@ -480,3 +558,13 @@ func (s *L1Syncer) callGetAddress(ctx context.Context, addr *common.Address, dat return common.BytesToAddress(resp[len(resp)-20:]), nil } + +func (s *L1Syncer) CheckL1BlockFinalized(blockNo uint64) (finalized bool, finalizedBn uint64, err error) { + em := s.getNextEtherman() + block, err := em.BlockByNumber(s.ctx, big.NewInt(rpc.FinalizedBlockNumber.Int64())) + if err != nil { + return false, 0, err + } + + return block.NumberU64() >= blockNo, block.NumberU64(), nil +} diff --git a/zk/syncer/utils.go b/zk/syncer/utils.go new file mode 100644 index 00000000000..1d1d17b2bc2 --- /dev/null +++ b/zk/syncer/utils.go @@ -0,0 +1,188 @@ +package syncer + +import ( + "encoding/hex" + "fmt" + "strings" + + "github.com/gateway-fm/cdk-erigon-lib/common" + "github.com/ledgerwatch/erigon/accounts/abi" + "github.com/ledgerwatch/erigon/zk/contracts" + "github.com/ledgerwatch/erigon/zk/utils" +) + +func GetAccInputDataCalcFunction(l1InfoRoot common.Hash, decodedSequenceInteerface interface{}) (accInputHashCalcFn func(prevAccInputHash common.Hash, index int) *common.Hash, totalSequenceBatches int, err error) { + switch decodedSequence := decodedSequenceInteerface.(type) { + case *SequenceBatchesCalldataPreEtrog: + accInputHashCalcFn = func(prevAccInputHash common.Hash, index int) *common.Hash { + return utils.CalculatePreEtrogAccInputHash(prevAccInputHash, decodedSequence.Batches[index].Transactions, decodedSequence.Batches[index].GlobalExitRoot, decodedSequence.Batches[index].Timestamp, decodedSequence.L2Coinbase) + } + return accInputHashCalcFn, len(decodedSequence.Batches), nil + case *SequenceBatchesCalldataEtrog: + accInputHashCalcFn = func(prevAccInputHash common.Hash, index int) *common.Hash { + return utils.CalculateEtrogAccInputHash(prevAccInputHash, decodedSequence.Batches[index].Transactions, l1InfoRoot, decodedSequence.Batches[index].ForcedTimestamp, decodedSequence.L2Coinbase, decodedSequence.Batches[index].ForcedBlockHashL1) + } + return accInputHashCalcFn, len(decodedSequence.Batches), nil + case *SequenceBatchesCalldataElderberry: + accInputHashCalcFn = func(prevAccInputHash common.Hash, index int) *common.Hash { + return utils.CalculateEtrogAccInputHash(prevAccInputHash, decodedSequence.Batches[index].Transactions, l1InfoRoot, decodedSequence.MaxSequenceTimestamp, decodedSequence.L2Coinbase, decodedSequence.Batches[index].ForcedBlockHashL1) + } + return accInputHashCalcFn, len(decodedSequence.Batches), nil + default: + return nil, 0, fmt.Errorf("unexpected type of decoded sequence calldata: %T", decodedSequenceInteerface) + } +} + +func DecodeSequenceBatchesCalldata(data []byte) (calldata interface{}, err error) { + methodSig := hex.EncodeToString(data[:4]) + abiString := contracts.SequenceBatchesMapping[methodSig] + if abiString == "" { + return nil, fmt.Errorf("no abi found for method signature: %s", methodSig) + } + abi, err := abi.JSON(strings.NewReader(abiString)) + if err != nil { + return nil, fmt.Errorf("error parsing etrogPolygonZkEvmAbi to json: %v", err) + } + + // recover Method from signature and ABI + method, err := abi.MethodById(data) + if err != nil { + return nil, fmt.Errorf("error recovering method from signature: %v", err) + } + + //sanitycheck + if method.Name != "sequenceBatches" { + return nil, fmt.Errorf("method name is not sequenceBatches, got: %s", method.Name) + } + + unpackedCalldata := make(map[string]interface{}) + if err := method.Inputs.UnpackIntoMap(unpackedCalldata, data[4:] /*first 4 bytes are method signature and not needed */); err != nil { + return nil, fmt.Errorf("error unpacking data: %v", err) + } + + switch methodSig { + case contracts.SequenceBatchesPreEtrog: + return decodePreEtrogSequenceBatchesCallData(unpackedCalldata), nil + case contracts.SequenceBatchesIdv5_0: + return decodeEtrogSequenceBatchesCallData(unpackedCalldata), nil + case contracts.SequenceBatchesIdv6_6: + return decodeElderberryBatchesCallData(unpackedCalldata), nil + default: + return nil, fmt.Errorf("no decoder found for method signature: %s", methodSig) + } +} + +type SequencedBatchElderberry struct { + Transactions []byte + ForcedGlobalExitRoot common.Hash + ForcedTimestamp uint64 + ForcedBlockHashL1 common.Hash +} + +type SequenceBatchesCalldataElderberry struct { + Batches []SequencedBatchElderberry + InitSequencedBatch uint64 + L2Coinbase common.Address + MaxSequenceTimestamp uint64 +} + +func decodeElderberryBatchesCallData(unpackedCalldata map[string]interface{}) *SequenceBatchesCalldataElderberry { + unpackedbatches := unpackedCalldata["batches"].([]struct { + Transactions []uint8 `json:"transactions"` + ForcedGlobalExitRoot [32]uint8 `json:"forcedGlobalExitRoot"` + ForcedTimestamp uint64 `json:"forcedTimestamp"` + ForcedBlockHashL1 [32]uint8 `json:"forcedBlockHashL1"` + }) + + calldata := &SequenceBatchesCalldataElderberry{ + Batches: make([]SequencedBatchElderberry, len(unpackedbatches)), + InitSequencedBatch: unpackedCalldata["initSequencedBatch"].(uint64), + L2Coinbase: unpackedCalldata["l2Coinbase"].(common.Address), + MaxSequenceTimestamp: unpackedCalldata["maxSequenceTimestamp"].(uint64), + } + + for i, batch := range unpackedbatches { + calldata.Batches[i] = SequencedBatchElderberry{ + Transactions: batch.Transactions, + ForcedGlobalExitRoot: common.BytesToHash(batch.ForcedGlobalExitRoot[:]), + ForcedTimestamp: batch.ForcedTimestamp, + ForcedBlockHashL1: common.BytesToHash(batch.ForcedBlockHashL1[:]), + } + } + + return calldata +} + +type SequencedBatchEtrog struct { + Transactions []uint8 + ForcedGlobalExitRoot common.Hash + ForcedTimestamp uint64 + ForcedBlockHashL1 common.Hash +} + +type SequenceBatchesCalldataEtrog struct { + Batches []SequencedBatchEtrog + L2Coinbase common.Address +} + +func decodeEtrogSequenceBatchesCallData(unpackedCalldata map[string]interface{}) *SequenceBatchesCalldataEtrog { + unpackedbatches := unpackedCalldata["batches"].([]struct { + Transactions []uint8 `json:"transactions"` + ForcedGlobalExitRoot [32]uint8 `json:"forcedGlobalExitRoot"` + ForcedTimestamp uint64 `json:"forcedTimestamp"` + ForcedBlockHashL1 [32]uint8 `json:"forcedBlockHashL1"` + }) + + calldata := &SequenceBatchesCalldataEtrog{ + Batches: make([]SequencedBatchEtrog, len(unpackedbatches)), + L2Coinbase: unpackedCalldata["l2Coinbase"].(common.Address), + } + + for i, batch := range unpackedbatches { + calldata.Batches[i] = SequencedBatchEtrog{ + Transactions: batch.Transactions, + ForcedGlobalExitRoot: common.BytesToHash(batch.ForcedGlobalExitRoot[:]), + ForcedTimestamp: batch.ForcedTimestamp, + ForcedBlockHashL1: batch.ForcedBlockHashL1, + } + } + + return calldata +} + +type SequencedBatchPreEtrog struct { + Transactions []uint8 + GlobalExitRoot common.Hash + Timestamp uint64 + MinForcedTimestamp uint64 +} + +type SequenceBatchesCalldataPreEtrog struct { + Batches []SequencedBatchPreEtrog + L2Coinbase common.Address +} + +func decodePreEtrogSequenceBatchesCallData(unpackedCalldata map[string]interface{}) *SequenceBatchesCalldataPreEtrog { + unpackedbatches := unpackedCalldata["batches"].([]struct { + Transactions []uint8 `json:"transactions"` + GlobalExitRoot [32]uint8 `json:"globalExitRoot"` + Timestamp uint64 `json:"timestamp"` + MinForcedTimestamp uint64 `json:"minForcedTimestamp"` + }) + + calldata := &SequenceBatchesCalldataPreEtrog{ + Batches: make([]SequencedBatchPreEtrog, len(unpackedbatches)), + L2Coinbase: unpackedCalldata["l2Coinbase"].(common.Address), + } + + for i, batch := range unpackedbatches { + calldata.Batches[i] = SequencedBatchPreEtrog{ + Transactions: batch.Transactions, + GlobalExitRoot: common.BytesToHash(batch.GlobalExitRoot[:]), + Timestamp: batch.Timestamp, + MinForcedTimestamp: batch.MinForcedTimestamp, + } + } + + return calldata +} diff --git a/zk/syncer/utils_test.go b/zk/syncer/utils_test.go new file mode 100644 index 00000000000..3e6cec231b7 --- /dev/null +++ b/zk/syncer/utils_test.go @@ -0,0 +1,36 @@ +package syncer + +import ( + "testing" + + "github.com/ledgerwatch/erigon/common" + "github.com/stretchr/testify/require" +) + +func Test_decodeEtrogSequenceBatchesCallData(t *testing.T) { + input := decodeElderberrySequenceBatchesCallDataTestCases + + for _, tc := range input { + data := common.FromHex(tc.Input) + calldata, err := DecodeSequenceBatchesCalldata(data) + if err != nil { + t.Fatal(err) + } + + require.Equal(t, &tc.Expected, calldata) + } +} + +func Test_decodePreEtrogSequenceBatchesCallData(t *testing.T) { + input := decodePreEtrogSequenceBatchesCallDataTestCases + + for _, tc := range input { + data := common.FromHex(tc.Input) + calldata, err := DecodeSequenceBatchesCalldata(data) + if err != nil { + t.Fatal(err) + } + + require.Equal(t, &tc.Expected, calldata) + } +} diff --git a/zk/tests/testdata/benchmark-keccaks.json b/zk/tests/testdata/benchmark-keccaks.json index ecf4dc4f170..f8b75bffa87 100644 --- a/zk/tests/testdata/benchmark-keccaks.json +++ b/zk/tests/testdata/benchmark-keccaks.json @@ -126,7 +126,7 @@ "virtualCounters": { "steps": 48968, "arith": 949, - "binary": 2173, + "binary": 2174, "memAlign": 117, "keccaks": 20, "padding": 23, diff --git a/zk/tests/testdata/block-info.json b/zk/tests/testdata/block-info.json index 51e14cd8407..b72a94e943f 100644 --- a/zk/tests/testdata/block-info.json +++ b/zk/tests/testdata/block-info.json @@ -216,7 +216,7 @@ "virtualCounters": { "steps": 71310, "arith": 3529, - "binary": 4577, + "binary": 4578, "memAlign": 12, "keccaks": 27, "padding": 186, diff --git a/zk/tests/testdata/geth-ecmul.json b/zk/tests/testdata/geth-ecmul.json index e61991f6ba4..6b0fe4ff125 100644 --- a/zk/tests/testdata/geth-ecmul.json +++ b/zk/tests/testdata/geth-ecmul.json @@ -165,9 +165,9 @@ "newStateRoot": "0x19275c111a31bc53f0a46468b0bf82bd111dcca67ee933282c300ffdd4c827bd", "newBatchAccInputHash": "0x60accc3c72a5a592c5a944e24a65e401962fee136f18633b9e962b4805282f0b", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -341,9 +341,9 @@ "newStateRoot": "0x7eb4bf447a41e7e58ae64d889a6de29ea5dd57d1bba255a4a3687c2ebedea216", "newBatchAccInputHash": "0x75679d17666ca566c9abfd7f266914d5d608e9ed4d324a174abf647fff555a78", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -517,9 +517,9 @@ "newStateRoot": "0xeb035702d0b2f1b0666d2101a8c4190a69f909a4dad47f9130a4b0fcfcfc6d34", "newBatchAccInputHash": "0x24d3685d6be67c07a68ccb5af2f5d423bb562630c4ddc9aaad5ac3236fd544d8", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -693,9 +693,9 @@ "newStateRoot": "0x72d36f85e07d0cd9fc75567db9ca40e81410921ea9e16c76172a301ddcb97e0e", "newBatchAccInputHash": "0xcea11061c18bac08bd0efc720cec0141969806a34895a6078cbdc4fcf17a1fa3", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -869,9 +869,9 @@ "newStateRoot": "0xcfda0a0766179f81ee5cdde1e970615ee6c352fa86ae5c3f0ca201c9ad98ea15", "newBatchAccInputHash": "0x08804820a1108785df381d2539fbcd3ae0b9c7f7a5f4afc9631a0db9044e1308", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -1045,9 +1045,9 @@ "newStateRoot": "0x4bf1b7a9f4dc36551c9af0e12d3cd2b15884ea383cfd6d20bf87175f7be9fb11", "newBatchAccInputHash": "0x7605f7c0ba88b2ee44f1e9b5244cd2e4ac315b2adcf4ea7fa47bdb84da50058d", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -1221,9 +1221,9 @@ "newStateRoot": "0xec812a35dc3eae58733f43913924bac38f1d41bce187bbf6bf9eb1710c3a5a89", "newBatchAccInputHash": "0xece597accb62dbd0e2cec24c3d29023e3bd646cdab95bd9173c4b02001b21705", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -1397,9 +1397,9 @@ "newStateRoot": "0x4f7ebd78085f0544ba41fde7167d22a01c76fdff44dd92dc3b6a0f71d5b5bc97", "newBatchAccInputHash": "0xf715916ed5e8fe4167933a0893e2fff4fed47e429aca4ca637a8a13082a39598", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -1573,9 +1573,9 @@ "newStateRoot": "0xc1c5eeedbca6ce7758e6a4afab23e8b008e58a57671d5b25041e3bea780d0c27", "newBatchAccInputHash": "0x36dd195c5b543b0485f3a5c1e64054ce8484273255c9fd6ed3cc5237299e2fcd", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -1749,9 +1749,9 @@ "newStateRoot": "0x3acd19a9cbab6f27f23adbc4cb34feb0b08cf22bfbe1ef9fa3cf97868c4b826d", "newBatchAccInputHash": "0x3c4f0b506be46c76b65c8549454c240db519b77dd9a186afeff33edec7f786e3", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -1925,9 +1925,9 @@ "newStateRoot": "0x56b04b47f151a7b01880b88a03eefadd5562d6d800b58915e86c92e080894101", "newBatchAccInputHash": "0x84dbf28820faf2cfeefc75b0ce625cbc412967d6347203ad374118e4945200aa", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -2101,9 +2101,9 @@ "newStateRoot": "0x099ac25ab395105db567b5442b2a23ce53ce0e22decb88b5f5c1bba5c8816d6d", "newBatchAccInputHash": "0xad436c99b82c476061884aa379eb10692e66faa4487a60797d582ccc5256e5ab", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -2277,9 +2277,9 @@ "newStateRoot": "0x5fffb9f377c61ab2f8c1497a25823b7ee862b4e9b33cadb02959603bf9f29b5a", "newBatchAccInputHash": "0x9a307b322eaa819f9592647f7ad3a3c7059116cbd53fa1faf6ef9a2a31943631", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -2453,9 +2453,9 @@ "newStateRoot": "0x4d0e05d3aef1a4a24b9cfc6441612d7eb720dcfa91376d098d755f07b59c24ab", "newBatchAccInputHash": "0x80739e894bf6a6c653609dbcba5704fac4b586dfbdf9f531fb5a07a9ef3217d8", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -2629,9 +2629,9 @@ "newStateRoot": "0x18d66de5837a7f87c8b5211acba446dcddadc039beff8821b34c5846fbcfb9c6", "newBatchAccInputHash": "0x47f92ebb85fb8cfda9f05bb99d3461e7b70e91829a0c99340309097dfbbee930", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -2805,9 +2805,9 @@ "newStateRoot": "0x11ac1a6fa4cfd545452c53afb5cda0ee7460b8a5da98dcf414d9910bfcba77a2", "newBatchAccInputHash": "0xd4512654fb8603f8b93ee90a5e65e1dc4de888ffd6a81a97c7901df1b8da4cce", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -2981,9 +2981,9 @@ "newStateRoot": "0x49b3ff95ee987dff042df49425fb49ed4555357ed855804f405494b4e6590b9b", "newBatchAccInputHash": "0x4605af08fbde2988726036f8875561ae90195cdc822f0d1899bac2f797ee987a", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -3157,9 +3157,9 @@ "newStateRoot": "0x8003d17426b22713f38b45b770d7aa2b9585d33e3d660f89f3821fdb94148933", "newBatchAccInputHash": "0x458ffbb0a296fcb2fafa7b35b53c268281ccf0d0ced9c654b8bca15839bbacc8", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -3331,9 +3331,9 @@ "newStateRoot": "0x0b2b389aaecd03e2b0d13bd60e5177b594bced4b9569056c63245729f9714dbe", "newBatchAccInputHash": "0xff480d0771914b57e1f822af49449a5e9ce118ebaaa42fef331249dca3f9a69f", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, @@ -3507,9 +3507,9 @@ "newStateRoot": "0x97b5fd0b875f2091cfe97ae69c42ccca3f50a15ea43d08db8573250b277779d6", "newBatchAccInputHash": "0x4d444f626a5da967a18a58c52faec97c38a17edfecd10351282528b33f2f86a9", "virtualCounters": { - "steps": 296341, - "arith": 20887, - "binary": 21563, + "steps": 308451, + "arith": 21726, + "binary": 25169, "memAlign": 127, "keccaks": 11, "padding": 41, diff --git a/zk/tests/testdata/selfdestruct.json b/zk/tests/testdata/selfdestruct.json index 78892520c4e..1390470455a 100644 --- a/zk/tests/testdata/selfdestruct.json +++ b/zk/tests/testdata/selfdestruct.json @@ -178,7 +178,7 @@ "virtualCounters": { "steps": 44342, "arith": 1892, - "binary": 2681, + "binary": 2682, "memAlign": 7, "keccaks": 17, "padding": 68, diff --git a/zk/tests/testdata/state-transition-e2e.json b/zk/tests/testdata/state-transition-e2e.json index cddff2452e9..3ccf0478f05 100644 --- a/zk/tests/testdata/state-transition-e2e.json +++ b/zk/tests/testdata/state-transition-e2e.json @@ -369,7 +369,7 @@ "virtualCounters": { "steps": 846409, "arith": 13062, - "binary": 41032, + "binary": 41034, "memAlign": 1273, "keccaks": 172, "padding": 1108, diff --git a/zk/tests/testdata/state-transition-processor.json b/zk/tests/testdata/state-transition-processor.json index 76837e0b830..30e8ed7e0ab 100644 --- a/zk/tests/testdata/state-transition-processor.json +++ b/zk/tests/testdata/state-transition-processor.json @@ -87,7 +87,7 @@ "virtualCounters": { "steps": 10312, "arith": 581, - "binary": 726, + "binary": 727, "memAlign": 0, "keccaks": 8, "padding": 13, @@ -275,7 +275,7 @@ "virtualCounters": { "steps": 46917, "arith": 2880, - "binary": 3395, + "binary": 3396, "memAlign": 0, "keccaks": 21, "padding": 43, @@ -368,7 +368,7 @@ "virtualCounters": { "steps": 9661, "arith": 578, - "binary": 693, + "binary": 694, "memAlign": 0, "keccaks": 7, "padding": 6, @@ -496,7 +496,7 @@ "virtualCounters": { "steps": 18519, "arith": 1149, - "binary": 1343, + "binary": 1344, "memAlign": 0, "keccaks": 10, "padding": 12, @@ -593,7 +593,7 @@ "virtualCounters": { "steps": 10312, "arith": 581, - "binary": 726, + "binary": 727, "memAlign": 0, "keccaks": 8, "padding": 13, @@ -729,7 +729,7 @@ "virtualCounters": { "steps": 37147, "arith": 2300, - "binary": 2694, + "binary": 2695, "memAlign": 0, "keccaks": 17, "padding": 30, @@ -826,7 +826,7 @@ "virtualCounters": { "steps": 10312, "arith": 581, - "binary": 726, + "binary": 727, "memAlign": 0, "keccaks": 8, "padding": 13, @@ -936,7 +936,7 @@ "virtualCounters": { "steps": 20256, "arith": 1165, - "binary": 1439, + "binary": 1440, "memAlign": 0, "keccaks": 12, "padding": 26, @@ -1072,7 +1072,7 @@ "virtualCounters": { "steps": 39622, "arith": 2321, - "binary": 2829, + "binary": 2830, "memAlign": 0, "keccaks": 20, "padding": 52, @@ -1210,7 +1210,7 @@ "virtualCounters": { "steps": 27010, "arith": 1231, - "binary": 1682, + "binary": 1683, "memAlign": 3, "keccaks": 13, "padding": 37, @@ -1343,7 +1343,7 @@ "virtualCounters": { "steps": 40186, "arith": 1451, - "binary": 2383, + "binary": 2384, "memAlign": 7, "keccaks": 14, "padding": 47, @@ -1487,7 +1487,7 @@ "virtualCounters": { "steps": 29339, "arith": 1247, - "binary": 1764, + "binary": 1765, "memAlign": 6, "keccaks": 13, "padding": 48, @@ -1631,7 +1631,7 @@ "virtualCounters": { "steps": 252402, "arith": 6185, - "binary": 15022, + "binary": 15023, "memAlign": 248, "keccaks": 29, "padding": 95, @@ -1754,7 +1754,7 @@ "virtualCounters": { "steps": 29852, "arith": 1741, - "binary": 2128, + "binary": 2129, "memAlign": 0, "keccaks": 16, "padding": 39, diff --git a/zk/tests/testdata/state-transition-zkevmdb.json b/zk/tests/testdata/state-transition-zkevmdb.json index b4257c5b5b0..f8cf07714b7 100644 --- a/zk/tests/testdata/state-transition-zkevmdb.json +++ b/zk/tests/testdata/state-transition-zkevmdb.json @@ -69,7 +69,7 @@ "virtualCounters": { "steps": 10312, "arith": 581, - "binary": 726, + "binary": 727, "memAlign": 0, "keccaks": 8, "padding": 13, diff --git a/zk/tx/tx.go b/zk/tx/tx.go index 9b0dfe072ba..e6f9debfc17 100644 --- a/zk/tx/tx.go +++ b/zk/tx/tx.go @@ -189,6 +189,8 @@ func DecodeBatchL2Blocks(txsData []byte, forkID uint64) ([]DecodedBatchL2Data, e return result, nil } +type TxDecoder func(encodedTx []byte, gasPricePercentage uint8, forkID uint64) (types.Transaction, uint8, error) + func DecodeTx(encodedTx []byte, efficiencyPercentage byte, forkId uint64) (types.Transaction, uint8, error) { // efficiencyPercentage := uint8(0) if forkId >= uint64(constants.ForkID5Dragonfruit) { @@ -383,9 +385,11 @@ func GetDecodedV(tx types.Transaction, v *uint256.Int) *uint256.Int { } func GenerateBlockBatchL2Data(forkId uint16, deltaTimestamp uint32, l1InfoTreeIndex uint32, transactions []types.Transaction, egTx map[common.Hash]uint8) ([]byte, error) { - // add in the changeL2Block transaction - result := GenerateStartBlockBatchL2Data(deltaTimestamp, l1InfoTreeIndex) - + result := make([]byte, 0) + // add in the changeL2Block transaction if after forkId 7 + if forkId >= uint16(constants.ForkID7Etrog) { + result = GenerateStartBlockBatchL2Data(deltaTimestamp, l1InfoTreeIndex) + } for _, transaction := range transactions { encoded, err := TransactionToL2Data(transaction, forkId, egTx[transaction.Hash()]) if err != nil { @@ -496,11 +500,7 @@ func ComputeL2TxHash( } hash += fromPart - hashed, err := utils.HashContractBytecode(hash) - if err != nil { - return common.Hash{}, err - } - + hashed := utils.HashContractBytecode(hash) return common.HexToHash(hashed), nil } diff --git a/zk/txpool/policy.go b/zk/txpool/policy.go index 0a5c0d33fac..b6459d9a9be 100644 --- a/zk/txpool/policy.go +++ b/zk/txpool/policy.go @@ -53,14 +53,23 @@ func containsPolicy(policies []byte, policy Policy) bool { return bytes.Contains(policies, policy.ToByteArray()) } -// CheckPolicy checks if the given address has the given policy for the online ACL mode -func CheckPolicy(ctx context.Context, aclDB kv.RwDB, addr common.Address, policy Policy) (bool, error) { +// DoesAccountHavePolicy checks if the given account has the given policy for the online ACL mode +func DoesAccountHavePolicy(ctx context.Context, aclDB kv.RwDB, addr common.Address, policy Policy) (bool, error) { + hasPolicy, _, err := checkIfAccountHasPolicy(ctx, aclDB, addr, policy) + return hasPolicy, err +} + +func checkIfAccountHasPolicy(ctx context.Context, aclDB kv.RwDB, addr common.Address, policy Policy) (bool, ACLMode, error) { if !IsSupportedPolicy(policy) { - return false, errUnknownPolicy + return false, DisabledMode, errUnknownPolicy } // Retrieve the mode configuration - var hasPolicy bool + var ( + hasPolicy bool + mode ACLMode = DisabledMode + ) + err := aclDB.View(ctx, func(tx kv.Tx) error { value, err := tx.GetOne(Config, []byte("mode")) if err != nil { @@ -72,7 +81,7 @@ func CheckPolicy(ctx context.Context, aclDB kv.RwDB, addr common.Address, policy return nil } - mode := string(value) + mode = ACLMode(value) table := BlockList if mode == AllowlistMode { @@ -87,18 +96,18 @@ func CheckPolicy(ctx context.Context, aclDB kv.RwDB, addr common.Address, policy policyBytes = value if policyBytes != nil && containsPolicy(policyBytes, policy) { - // If address is in the whitelist and has the policy, return true - // If address is in the blacklist and has the policy, return false + // If address is in the allowlist and has the policy, return true + // If address is in the blocklist and has the policy, return false hasPolicy = true } return nil }) if err != nil { - return false, err + return false, mode, err } - return hasPolicy, nil + return hasPolicy, mode, nil } // UpdatePolicies sets a policy for an address @@ -251,7 +260,19 @@ func resolvePolicy(txn *types.TxSlot) Policy { return SendTx } -// create a method checkpolicy to check an address according to passed policy in the method -func (p *TxPool) checkPolicy(ctx context.Context, addr common.Address, policy Policy) (bool, error) { - return CheckPolicy(ctx, p.aclDB, addr, policy) +// isActionAllowed checks if the given action is allowed for the given address +func (p *TxPool) isActionAllowed(ctx context.Context, addr common.Address, policy Policy) (bool, error) { + hasPolicy, mode, err := checkIfAccountHasPolicy(ctx, p.aclDB, addr, policy) + if err != nil { + return false, err + } + + switch mode { + case BlocklistMode: + // If the mode is blocklist, and address has a certain policy, then invert the result + // because, for example, if it has sendTx policy, it means it is not allowed to sendTx + return !hasPolicy, nil + default: + return hasPolicy, nil + } } diff --git a/zk/txpool/policy_test.go b/zk/txpool/policy_test.go index 0a1a22fd26d..6c31f1d8f47 100644 --- a/zk/txpool/policy_test.go +++ b/zk/txpool/policy_test.go @@ -135,7 +135,7 @@ func TestRemovePolicy(t *testing.T) { require.NoError(t, err) // Check if the policy is removed from the ACL - hasPolicy, err := CheckPolicy(ctx, db, addr, policy) + hasPolicy, err := DoesAccountHavePolicy(ctx, db, addr, policy) require.NoError(t, err) require.False(t, hasPolicy) }) @@ -155,7 +155,7 @@ func TestRemovePolicy(t *testing.T) { require.NoError(t, err) // Check if the policy is still not present in the ACL - hasPolicy, err := CheckPolicy(ctx, db, addr, policy) + hasPolicy, err := DoesAccountHavePolicy(ctx, db, addr, policy) require.NoError(t, err) require.False(t, hasPolicy) }) @@ -172,7 +172,7 @@ func TestRemovePolicy(t *testing.T) { require.NoError(t, err) // Check if the policy is still not present in the ACL - hasPolicy, err := CheckPolicy(ctx, db, addr, policy) + hasPolicy, err := DoesAccountHavePolicy(ctx, db, addr, policy) require.NoError(t, err) require.False(t, hasPolicy) }) @@ -208,7 +208,7 @@ func TestAddPolicy(t *testing.T) { require.NoError(t, err) // Check if the policy exists in the ACL - hasPolicy, err := CheckPolicy(ctx, db, addr, policy) + hasPolicy, err := DoesAccountHavePolicy(ctx, db, addr, policy) require.NoError(t, err) require.True(t, hasPolicy) }) @@ -228,7 +228,7 @@ func TestAddPolicy(t *testing.T) { require.NoError(t, err) // Check if the policy still exists in the ACL - hasPolicy, err := CheckPolicy(ctx, db, addr, policy) + hasPolicy, err := DoesAccountHavePolicy(ctx, db, addr, policy) require.NoError(t, err) require.True(t, hasPolicy) }) @@ -279,15 +279,15 @@ func TestUpdatePolicies(t *testing.T) { require.NoError(t, err) // Check if the policies are added correctly - hasPolicy, err := CheckPolicy(ctx, db, addr1, SendTx) + hasPolicy, err := DoesAccountHavePolicy(ctx, db, addr1, SendTx) require.NoError(t, err) require.True(t, hasPolicy) - hasPolicy, err = CheckPolicy(ctx, db, addr1, Deploy) + hasPolicy, err = DoesAccountHavePolicy(ctx, db, addr1, Deploy) require.NoError(t, err) require.True(t, hasPolicy) - hasPolicy, err = CheckPolicy(ctx, db, addr2, SendTx) + hasPolicy, err = DoesAccountHavePolicy(ctx, db, addr2, SendTx) require.NoError(t, err) require.True(t, hasPolicy) }) @@ -307,15 +307,15 @@ func TestUpdatePolicies(t *testing.T) { require.NoError(t, err) // Check if the policies are removed correctly - hasPolicy, err := CheckPolicy(ctx, db, addr1, SendTx) + hasPolicy, err := DoesAccountHavePolicy(ctx, db, addr1, SendTx) require.NoError(t, err) require.False(t, hasPolicy) - hasPolicy, err = CheckPolicy(ctx, db, addr1, Deploy) + hasPolicy, err = DoesAccountHavePolicy(ctx, db, addr1, Deploy) require.NoError(t, err) require.False(t, hasPolicy) - hasPolicy, err = CheckPolicy(ctx, db, addr2, SendTx) + hasPolicy, err = DoesAccountHavePolicy(ctx, db, addr2, SendTx) require.NoError(t, err) require.True(t, hasPolicy) }) @@ -335,15 +335,15 @@ func TestUpdatePolicies(t *testing.T) { require.NoError(t, err) // Check if the policies are removed correctly - hasPolicy, err := CheckPolicy(ctx, db, addr1, SendTx) + hasPolicy, err := DoesAccountHavePolicy(ctx, db, addr1, SendTx) require.NoError(t, err) require.False(t, hasPolicy) - hasPolicy, err = CheckPolicy(ctx, db, addr1, Deploy) + hasPolicy, err = DoesAccountHavePolicy(ctx, db, addr1, Deploy) require.NoError(t, err) require.False(t, hasPolicy) - hasPolicy, err = CheckPolicy(ctx, db, addr2, SendTx) + hasPolicy, err = DoesAccountHavePolicy(ctx, db, addr2, SendTx) require.NoError(t, err) require.False(t, hasPolicy) }) @@ -363,3 +363,81 @@ func TestUpdatePolicies(t *testing.T) { require.ErrorIs(t, err, errUnsupportedACLType) }) } + +func TestIsActionAllowed(t *testing.T) { + db := newTestACLDB(t, "") + ctx := context.Background() + + txPool := &TxPool{aclDB: db} + + t.Run("isActionAllowed - BlocklistMode - Policy Exists", func(t *testing.T) { + SetMode(ctx, db, BlocklistMode) + + // Create a test address and policy + addr := common.HexToAddress("0x1234567890abcdef") + policy := SendTx + + // Add the policy to the ACL + require.NoError(t, AddPolicy(ctx, db, "blocklist", addr, policy)) + + // Check if the action is allowed + allowed, err := txPool.isActionAllowed(ctx, addr, policy) + require.NoError(t, err) + require.False(t, allowed) // In blocklist mode, having the policy means the action is not allowed + }) + + t.Run("isActionAllowed - BlocklistMode - Policy Does Not Exist", func(t *testing.T) { + SetMode(ctx, db, BlocklistMode) + + // Create a test address and policy + addr := common.HexToAddress("0x1234567890abcdef") + policy := Deploy + + // Check if the action is allowed + allowed, err := txPool.isActionAllowed(ctx, addr, policy) + require.NoError(t, err) + require.True(t, allowed) // In blocklist mode, not having the policy means the action is allowed + }) + + t.Run("isActionAllowed - AllowlistMode - Policy Exists", func(t *testing.T) { + SetMode(ctx, db, AllowlistMode) + + // Create a test address and policy + addr := common.HexToAddress("0x1234567890abcdef") + policy := SendTx + + // Add the policy to the ACL + require.NoError(t, AddPolicy(ctx, db, "allowlist", addr, policy)) + + // Check if the action is allowed + allowed, err := txPool.isActionAllowed(ctx, addr, policy) + require.NoError(t, err) + require.True(t, allowed) // In allowlist mode, having the policy means the action is allowed + }) + + t.Run("isActionAllowed - AllowlistMode - Policy Does Not Exist", func(t *testing.T) { + SetMode(ctx, db, AllowlistMode) + + // Create a test address and policy + addr := common.HexToAddress("0x1234567890abcdef") + policy := Deploy + + // Check if the action is allowed + allowed, err := txPool.isActionAllowed(ctx, addr, policy) + require.NoError(t, err) + require.False(t, allowed) // In allowlist mode, not having the policy means the action is not allowed + }) + + t.Run("isActionAllowed - DisabledMode", func(t *testing.T) { + SetMode(ctx, db, DisabledMode) + + // Create a test address and policy + addr := common.HexToAddress("0x1234567890abcdef") + policy := SendTx + + // Check if the action is allowed + allowed, err := txPool.isActionAllowed(ctx, addr, policy) + require.NoError(t, err) + require.True(t, allowed) // In disabled mode, all actions are allowed + }) +} diff --git a/zk/txpool/pool.go b/zk/txpool/pool.go index 3b7e36ddbca..2a472f4bbec 100644 --- a/zk/txpool/pool.go +++ b/zk/txpool/pool.go @@ -146,6 +146,7 @@ const ( SenderDisallowedDeploy DiscardReason = 26 // sender is not allowed to deploy contracts by ACL policy DiscardByLimbo DiscardReason = 27 SmartContractDeploymentDisabled DiscardReason = 28 // to == null not allowed, config set to block smart contract deployment + GasLimitTooHigh DiscardReason = 29 // gas limit is too high ) func (r DiscardReason) String() string { @@ -208,6 +209,8 @@ func (r DiscardReason) String() string { return "limbo error" case SmartContractDeploymentDisabled: return "smart contract deployment disabled" + case GasLimitTooHigh: + return fmt.Sprintf("gas limit too high. Max: %d", transactionGasLimit) default: panic(fmt.Sprintf("discard reason: %d", r)) } @@ -743,6 +746,13 @@ func (p *TxPool) validateTx(txn *types.TxSlot, isLocal bool, stateCache kvcache. } return IntrinsicGas } + if txn.Gas > transactionGasLimit { + if txn.Traced { + log.Info(fmt.Sprintf("TX TRACING: validateTx gas limit too high idHash=%x gas=%d, limit=%d", txn.IDHash, txn.Gas, transactionGasLimit)) + } + return GasLimitTooHigh + } + if !isLocal && uint64(p.all.count(txn.SenderID)) > p.cfg.AccountSlots { if txn.Traced { log.Info(fmt.Sprintf("TX TRACING: validateTx marked as spamming idHash=%x slots=%d, limit=%d", txn.IDHash, p.all.count(txn.SenderID), p.cfg.AccountSlots)) @@ -772,7 +782,7 @@ func (p *TxPool) validateTx(txn *types.TxSlot, isLocal bool, stateCache kvcache. switch resolvePolicy(txn) { case SendTx: var allow bool - allow, err := p.checkPolicy(context.TODO(), from, SendTx) + allow, err := p.isActionAllowed(context.TODO(), from, SendTx) if err != nil { panic(err) } @@ -782,7 +792,7 @@ func (p *TxPool) validateTx(txn *types.TxSlot, isLocal bool, stateCache kvcache. case Deploy: var allow bool // check that sender may deploy contracts - allow, err := p.checkPolicy(context.TODO(), from, Deploy) + allow, err := p.isActionAllowed(context.TODO(), from, Deploy) if err != nil { panic(err) } @@ -1042,12 +1052,7 @@ func (p *TxPool) addTxs(blockNum uint64, cacheView kvcache.CacheView, senders *s sendersWithChangedState[mt.Tx.SenderID] = struct{}{} } - for _, mt := range p.overflowZkCounters { - pending.Remove(mt) - discard(mt, OverflowZkCounters) - sendersWithChangedState[mt.Tx.SenderID] = struct{}{} - } - p.overflowZkCounters = p.overflowZkCounters[:0] + p.discardOverflowZkCountersFromPending(pending, discard, sendersWithChangedState) for senderID := range sendersWithChangedState { nonce, balance, err := senders.info(cacheView, senderID) @@ -1137,12 +1142,7 @@ func (p *TxPool) addTxsOnNewBlock( } } - for _, mt := range p.overflowZkCounters { - pending.Remove(mt) - discard(mt, OverflowZkCounters) - sendersWithChangedState[mt.Tx.SenderID] = struct{}{} - } - p.overflowZkCounters = p.overflowZkCounters[:0] + p.discardOverflowZkCountersFromPending(pending, discard, sendersWithChangedState) for senderID := range sendersWithChangedState { nonce, balance, err := senders.info(cacheView, senderID) diff --git a/zk/txpool/pool_zk.go b/zk/txpool/pool_zk.go index f53c7965c68..6a653159c2d 100644 --- a/zk/txpool/pool_zk.go +++ b/zk/txpool/pool_zk.go @@ -123,7 +123,7 @@ func (p *TxPool) onSenderStateChange(senderID uint64, senderNonce uint64, sender mt.subPool &^= NotTooMuchGas // zk: here we don't care about block limits any more and care about only the transaction gas limit in ZK - if mt.Tx.Gas < transactionGasLimit { + if mt.Tx.Gas <= transactionGasLimit { mt.subPool |= NotTooMuchGas } @@ -192,7 +192,7 @@ func (p *TxPool) best(n uint16, txs *types.TxsRlp, tx kv.Tx, onTopOf, availableG continue } - if mt.Tx.Gas >= transactionGasLimit { + if mt.Tx.Gas > transactionGasLimit { // Skip transactions with very large gas limit, these shouldn't enter the pool at all log.Debug("found a transaction in the pending pool with too high gas for tx - clear the tx pool") continue @@ -259,6 +259,22 @@ func (p *TxPool) MarkForDiscardFromPendingBest(txHash common.Hash) { } } +// discards the transactions that are in overflowZkCoutners from pending +// executes the discard function on them +// deletes the tx from the sendersWithChangedState map +// deletes the discarded txs from the overflowZkCounters +func (p *TxPool) discardOverflowZkCountersFromPending(pending *PendingPool, discard func(*metaTx, DiscardReason), sendersWithChangedState map[uint64]struct{}) { + for _, mt := range p.overflowZkCounters { + log.Info("[tx_pool] Removing TX from pending due to counter overflow", "tx", mt.Tx.IDHash) + pending.Remove(mt) + discard(mt, OverflowZkCounters) + sendersWithChangedState[mt.Tx.SenderID] = struct{}{} + // do not hold on to the discard reason for an OOC issue + p.discardReasonsLRU.Remove(string(mt.Tx.IDHash[:])) + } + p.overflowZkCounters = p.overflowZkCounters[:0] +} + func (p *TxPool) StartIfNotStarted(ctx context.Context, txPoolDb kv.RoDB, coreTx kv.Tx) error { p.lock.Lock() defer p.lock.Unlock() diff --git a/zk/txpool/pool_zk_limbo.go b/zk/txpool/pool_zk_limbo.go index f79e827fe3c..2fef5802b4e 100644 --- a/zk/txpool/pool_zk_limbo.go +++ b/zk/txpool/pool_zk_limbo.go @@ -1,17 +1,13 @@ package txpool import ( - "context" - "encoding/binary" "fmt" "math" "sync/atomic" "github.com/gateway-fm/cdk-erigon-lib/common" "github.com/gateway-fm/cdk-erigon-lib/kv" - "github.com/gateway-fm/cdk-erigon-lib/kv/kvcache" "github.com/gateway-fm/cdk-erigon-lib/types" - "github.com/ledgerwatch/log/v3" "github.com/status-im/keycard-go/hexutils" ) @@ -19,22 +15,20 @@ const ( TablePoolLimbo = "PoolLimbo" DbKeyInvalidTxPrefix = uint8(1) DbKeySlotsPrefix = uint8(2) - DbKeyBatchesPrefix = uint8(3) - DbKeyAwaitingBlockHandlingPrefix = uint8(4) - - DbKeyBatchesWitnessPrefix = uint8(5) - DbKeyBatchesL1InfoTreePrefix = uint8(6) - DbKeyBatchesTimestampLimitPrefix = uint8(7) - DbKeyBatchesFirstBlockNumberPrefix = uint8(8) - DbKeyBatchesBatchNumberPrefix = uint8(9) - DbKeyBatchesForkIdPrefix = uint8(10) - - DbKeyTxRlpPrefix = uint8(11) - DbKeyTxStreamBytesPrefix = uint8(12) - DbKeyTxRootPrefix = uint8(13) - DbKeyTxHashPrefix = uint8(14) - DbKeyTxSenderPrefix = uint8(15) - DbKeyTxPreviousTxPrefix = uint8(16) + DbKeyAwaitingBlockHandlingPrefix = uint8(3) + + DbKeyBlockWitnessPrefix = uint8(16) + DbKeyBlockL1InfoTreePrefix = uint8(17) + DbKeyBlockBlockTimestampPrefix = uint8(18) + DbKeyBlockBlockNumberPrefix = uint8(19) + DbKeyBlockBatchNumberPrefix = uint8(20) + DbKeyBlockForkIdPrefix = uint8(21) + + DbKeyTxRlpPrefix = uint8(48) + DbKeyTxStreamBytesPrefix = uint8(49) + DbKeyTxRootPrefix = uint8(50) + DbKeyTxHashPrefix = uint8(51) + DbKeyTxSenderPrefix = uint8(52) ) var emptyHash = common.Hash{} @@ -66,34 +60,33 @@ func (_this *LimboSendersWithChangedState) decrement(senderId uint64) { } -type LimboBatchTransactionDetails struct { +type LimboBlockTransactionDetails struct { Rlp []byte StreamBytes []byte Root common.Hash Hash common.Hash Sender common.Address - PreviousTx uint32 } -func newLimboBatchTransactionDetails(rlp, streamBytes []byte, hash common.Hash, sender common.Address, previousTx uint32) *LimboBatchTransactionDetails { - return &LimboBatchTransactionDetails{ +func newLimboBlockTransactionDetails(rlp, streamBytes []byte, hash common.Hash, sender common.Address) *LimboBlockTransactionDetails { + return &LimboBlockTransactionDetails{ Rlp: rlp, StreamBytes: streamBytes, Root: common.Hash{}, Hash: hash, Sender: sender, - PreviousTx: previousTx, } } -func (_this *LimboBatchTransactionDetails) hasRoot() bool { +func (_this *LimboBlockTransactionDetails) hasRoot() bool { return _this.Root != emptyHash } type Limbo struct { - invalidTxsMap map[string]uint8 //invalid tx: hash -> handled - limboSlots *types.TxSlots - limboBatches []*LimboBatchDetails + invalidTxsMap map[string]uint8 //invalid tx: hash -> handled + limboSlots *types.TxSlots + uncheckedLimboBlocks []*LimboBlockDetails + invalidLimboBlocks []*LimboBlockDetails // used to denote some process has made the pool aware that an unwind is about to occur and to wait // until the unwind has been processed before allowing yielding of transactions again @@ -104,29 +97,50 @@ func newLimbo() *Limbo { return &Limbo{ invalidTxsMap: make(map[string]uint8), limboSlots: &types.TxSlots{}, - limboBatches: make([]*LimboBatchDetails, 0), + uncheckedLimboBlocks: make([]*LimboBlockDetails, 0), + invalidLimboBlocks: make([]*LimboBlockDetails, 0), awaitingBlockHandling: atomic.Bool{}, } } -func (_this *Limbo) resizeBatches(newSize int) { - for i := len(_this.limboBatches); i < newSize; i++ { - _this.limboBatches = append(_this.limboBatches, NewLimboBatchDetails()) +func (_this *Limbo) resizeUncheckedBlocks(blockIndex, txIndex int) { + if blockIndex == -1 { + return } + + size := blockIndex + 1 + for i := len(_this.uncheckedLimboBlocks); i < size; i++ { + _this.uncheckedLimboBlocks = append(_this.uncheckedLimboBlocks, NewLimboBlockDetails()) + } + + _this.uncheckedLimboBlocks[blockIndex].resizeTransactions(txIndex) +} + +func (_this *Limbo) resizeInvalidBlocks(blockIndex, txIndex int) { + if blockIndex == -1 { + return + } + + size := blockIndex + 1 + for i := len(_this.invalidLimboBlocks); i < size; i++ { + _this.invalidLimboBlocks = append(_this.invalidLimboBlocks, NewLimboBlockDetails()) + } + + _this.invalidLimboBlocks[blockIndex].resizeTransactions(txIndex) } -func (_this *Limbo) getFirstTxWithoutRootByBatch(batchNumber uint64) (*LimboBatchDetails, *LimboBatchTransactionDetails) { - for _, limboBatch := range _this.limboBatches { - for _, limboTx := range limboBatch.Transactions { +func (_this *Limbo) getFirstTxWithoutRootByBlockNumber(blockNumber uint64) (*LimboBlockDetails, *LimboBlockTransactionDetails) { + for _, limboBlock := range _this.uncheckedLimboBlocks { + for _, limboTx := range limboBlock.Transactions { if !limboTx.hasRoot() { - if batchNumber < limboBatch.BatchNumber { + if blockNumber < limboBlock.BlockNumber { return nil, nil } - if batchNumber > limboBatch.BatchNumber { - panic(fmt.Errorf("requested batch %d while the network is already on %d", limboBatch.BatchNumber, batchNumber)) + if blockNumber > limboBlock.BlockNumber { + panic(fmt.Errorf("requested block %d while the network is already on %d", limboBlock.BlockNumber, blockNumber)) } - return limboBatch, limboTx + return limboBlock, limboTx } } } @@ -134,46 +148,51 @@ func (_this *Limbo) getFirstTxWithoutRootByBatch(batchNumber uint64) (*LimboBatc return nil, nil } -func (_this *Limbo) getLimboTxDetailsByTxHash(txHash *common.Hash) (*LimboBatchDetails, *LimboBatchTransactionDetails, uint32) { - for _, limboBatch := range _this.limboBatches { - limboTx, i := limboBatch.getTxDetailsByHash(txHash) +func (_this *Limbo) getTxDetailsByHash(txHash *common.Hash) (*LimboBlockDetails, *LimboBlockTransactionDetails, uint32, uint32) { + for i, limboBlock := range _this.uncheckedLimboBlocks { + limboTx, j := limboBlock.getTxDetailsByHash(txHash) if limboTx != nil { - return limboBatch, limboTx, i + return limboBlock, limboTx, uint32(i), j } } - return nil, nil, math.MaxUint32 + return nil, nil, math.MaxUint32, math.MaxUint32 } -type LimboBatchDetails struct { +type LimboBlockDetails struct { Witness []byte L1InfoTreeMinTimestamps map[uint64]uint64 - TimestampLimit uint64 - FirstBlockNumber uint64 + BlockTimestamp uint64 + BlockNumber uint64 BatchNumber uint64 ForkId uint64 - Transactions []*LimboBatchTransactionDetails + Transactions []*LimboBlockTransactionDetails } -func NewLimboBatchDetails() *LimboBatchDetails { - return &LimboBatchDetails{ +func NewLimboBlockDetails() *LimboBlockDetails { + return &LimboBlockDetails{ L1InfoTreeMinTimestamps: make(map[uint64]uint64), - Transactions: make([]*LimboBatchTransactionDetails, 0), + Transactions: make([]*LimboBlockTransactionDetails, 0), } } -func (_this *LimboBatchDetails) resizeTransactions(newSize int) { - for i := len(_this.Transactions); i < newSize; i++ { - _this.Transactions = append(_this.Transactions, &LimboBatchTransactionDetails{}) +func (_this *LimboBlockDetails) resizeTransactions(txIndex int) { + if txIndex == -1 { + return + } + size := txIndex + 1 + + for i := len(_this.Transactions); i < size; i++ { + _this.Transactions = append(_this.Transactions, &LimboBlockTransactionDetails{}) } } -func (_this *LimboBatchDetails) AppendTransaction(rlp, streamBytes []byte, hash common.Hash, sender common.Address, previousTx uint32) uint32 { - _this.Transactions = append(_this.Transactions, newLimboBatchTransactionDetails(rlp, streamBytes, hash, sender, previousTx)) +func (_this *LimboBlockDetails) AppendTransaction(rlp, streamBytes []byte, hash common.Hash, sender common.Address) uint32 { + _this.Transactions = append(_this.Transactions, newLimboBlockTransactionDetails(rlp, streamBytes, hash, sender)) return uint32(len(_this.Transactions)) } -func (_this *LimboBatchDetails) getTxDetailsByHash(txHash *common.Hash) (*LimboBatchTransactionDetails, uint32) { +func (_this *LimboBlockDetails) getTxDetailsByHash(txHash *common.Hash) (*LimboBlockTransactionDetails, uint32) { for i, limboTx := range _this.Transactions { if limboTx.Hash == *txHash { return limboTx, uint32(i) @@ -183,48 +202,35 @@ func (_this *LimboBatchDetails) getTxDetailsByHash(txHash *common.Hash) (*LimboB return nil, math.MaxUint32 } -func (p *TxPool) GetLimboTxHash(batchNumber uint64) (uint64, *common.Hash) { +func (p *TxPool) GetLimboDetailsForRecovery(blockNumber uint64) (*LimboBlockDetails, *common.Hash) { p.lock.Lock() defer p.lock.Unlock() - limboBatch, limboTx := p.limbo.getFirstTxWithoutRootByBatch(batchNumber) - if limboBatch == nil { - return 0, nil + limboBlock, limboTx := p.limbo.getFirstTxWithoutRootByBlockNumber(blockNumber) + if limboBlock == nil { + return nil, nil } - return limboBatch.TimestampLimit, &limboTx.Hash + return limboBlock, &limboTx.Hash } func (p *TxPool) GetLimboTxRplsByHash(tx kv.Tx, txHash *common.Hash) (*types.TxsRlp, error) { p.lock.Lock() defer p.lock.Unlock() - limboBatch, _, txIndex := p.limbo.getLimboTxDetailsByTxHash(txHash) - if limboBatch == nil { + limboBlock, _, _, txIndex := p.limbo.getTxDetailsByHash(txHash) + if limboBlock == nil { return nil, fmt.Errorf("missing transaction") } - maxSize := len(limboBatch.Transactions) - txIndices := make([]uint32, 0, maxSize) - - for { - if txIndex == math.MaxUint32 { - break - } - - txIndices = append(txIndices, txIndex) - txIndex = limboBatch.Transactions[txIndex].PreviousTx - } + txSize := txIndex + 1 txsRlps := &types.TxsRlp{} - txsRlps.Resize(uint(len(txIndices))) - - txIndicesSize := len(txIndices) - for i, txIndex := range txIndices { - reverseIndex := txIndicesSize - 1 - i - limboTx := limboBatch.Transactions[txIndex] - txsRlps.Txs[reverseIndex] = limboTx.Rlp - copy(txsRlps.Senders.At(reverseIndex), limboTx.Sender[:]) - txsRlps.IsLocal[reverseIndex] = true // all limbo tx are considered local //TODO: explain better about local + txsRlps.Resize(uint(txSize)) + for i := uint32(0); i < txSize; i++ { + limboTx := limboBlock.Transactions[i] + txsRlps.Txs[i] = limboTx.Rlp + copy(txsRlps.Senders.At(int(i)), limboTx.Sender[:]) + txsRlps.IsLocal[i] = true // all limbo tx are considered local //TODO: explain better about local } return txsRlps, nil @@ -234,14 +240,14 @@ func (p *TxPool) UpdateLimboRootByTxHash(txHash *common.Hash, stateRoot *common. p.lock.Lock() defer p.lock.Unlock() - _, limboTx, _ := p.limbo.getLimboTxDetailsByTxHash(txHash) + _, limboTx, _, _ := p.limbo.getTxDetailsByHash(txHash) limboTx.Root = *stateRoot } -func (p *TxPool) ProcessLimboBatchDetails(details *LimboBatchDetails) { +func (p *TxPool) ProcessUncheckedLimboBlockDetails(limboBlock *LimboBlockDetails) { p.lock.Lock() defer p.lock.Unlock() - p.limbo.limboBatches = append(p.limbo.limboBatches, details) + p.limbo.uncheckedLimboBlocks = append(p.limbo.uncheckedLimboBlocks, limboBlock) /* as we know we're about to enter an unwind we need to ensure that all the transactions have been @@ -254,22 +260,22 @@ func (p *TxPool) ProcessLimboBatchDetails(details *LimboBatchDetails) { p.denyYieldingTransactions() } -func (p *TxPool) GetLimboDetails() []*LimboBatchDetails { +func (p *TxPool) GetInvalidLimboBlocksDetails() []*LimboBlockDetails { p.lock.Lock() defer p.lock.Unlock() - return p.limbo.limboBatches + return p.limbo.invalidLimboBlocks } -func (p *TxPool) GetLimboDetailsCloned() []*LimboBatchDetails { +func (p *TxPool) GetUncheckedLimboBlocksDetailsClonedWeak() []*LimboBlockDetails { p.lock.Lock() defer p.lock.Unlock() - limboBatchesClone := make([]*LimboBatchDetails, len(p.limbo.limboBatches)) - copy(limboBatchesClone, p.limbo.limboBatches) - return limboBatchesClone + limboBlocksClone := make([]*LimboBlockDetails, len(p.limbo.uncheckedLimboBlocks)) + copy(limboBlocksClone, p.limbo.uncheckedLimboBlocks) + return limboBlocksClone } -func (p *TxPool) MarkProcessedLimboDetails(size int, invalidTxs []*string) { +func (p *TxPool) MarkProcessedLimboDetails(size int, invalidBatchesIndices []int, invalidTxs []*string) { p.lock.Lock() defer p.lock.Unlock() @@ -277,7 +283,10 @@ func (p *TxPool) MarkProcessedLimboDetails(size int, invalidTxs []*string) { p.limbo.invalidTxsMap[*idHash] = 0 } - p.limbo.limboBatches = p.limbo.limboBatches[size:] + for _, invalidBatchesIndex := range invalidBatchesIndices { + p.limbo.invalidLimboBlocks = append(p.limbo.invalidLimboBlocks, p.limbo.uncheckedLimboBlocks[invalidBatchesIndex]) + } + p.limbo.uncheckedLimboBlocks = p.limbo.uncheckedLimboBlocks[size:] } // should be called from within a locked context from the pool @@ -333,7 +342,7 @@ func (p *TxPool) finalizeLimboOnNewBlock(limboTxs *types.TxSlots) { // should be called from within a locked context from the pool func (p *TxPool) isTxKnownToLimbo(hash common.Hash) bool { - for _, limbo := range p.limbo.limboBatches { + for _, limbo := range p.limbo.uncheckedLimboBlocks { for _, limboTx := range limbo.Transactions { if limboTx.Hash == hash { return true @@ -355,244 +364,6 @@ func (p *TxPool) allowYieldingTransactions() { p.limbo.awaitingBlockHandling.Store(false) } -func (p *TxPool) flushLockedLimbo(tx kv.RwTx) (err error) { - //TODO: remove this check once limbo persistency works - if !p.ethCfg.Limbo { - return nil - } - - if err := tx.CreateBucket(TablePoolLimbo); err != nil { - return err - } - - if err := tx.ClearBucket(TablePoolLimbo); err != nil { - return err - } - - for hash, handled := range p.limbo.invalidTxsMap { - hashAsBytes := hexutils.HexToBytes(hash) - key := append([]byte{DbKeyInvalidTxPrefix}, hashAsBytes...) - tx.Put(TablePoolLimbo, key, []byte{handled}) - } - - v := make([]byte, 0, 1024) - for i, txSlot := range p.limbo.limboSlots.Txs { - v = common.EnsureEnoughSize(v, 20+len(txSlot.Rlp)) - sender := p.limbo.limboSlots.Senders.At(i) - - copy(v[:20], sender) - copy(v[20:], txSlot.Rlp) - - key := append([]byte{DbKeySlotsPrefix}, txSlot.IDHash[:]...) - if err := tx.Put(TablePoolLimbo, key, v); err != nil { - return err - } - } - - keyBytes := make([]byte, 14) - vBytes := make([]byte, 8) - keyBytes[0] = DbKeyBatchesPrefix - - for i, limboBatch := range p.limbo.limboBatches { - binary.LittleEndian.PutUint32(keyBytes[1:5], uint32(i)) - - // Witness - keyBytes[5] = DbKeyBatchesWitnessPrefix - binary.LittleEndian.PutUint64(keyBytes[6:14], 0) - if err := tx.Put(TablePoolLimbo, keyBytes, limboBatch.Witness); err != nil { - return err - } - - // L1InfoTreeMinTimestamps - keyBytes[5] = DbKeyBatchesL1InfoTreePrefix - for k, v := range limboBatch.L1InfoTreeMinTimestamps { - binary.LittleEndian.PutUint64(keyBytes[6:14], uint64(k)) - binary.LittleEndian.PutUint64(vBytes[:], v) - if err := tx.Put(TablePoolLimbo, keyBytes, vBytes); err != nil { - return err - } - } - - // TimestampLimit - keyBytes[5] = DbKeyBatchesTimestampLimitPrefix - binary.LittleEndian.PutUint64(keyBytes[6:14], 0) - binary.LittleEndian.PutUint64(vBytes[:], limboBatch.TimestampLimit) - if err := tx.Put(TablePoolLimbo, keyBytes, vBytes); err != nil { - return err - } - - // FirstBlockNumber - keyBytes[5] = DbKeyBatchesFirstBlockNumberPrefix - binary.LittleEndian.PutUint64(keyBytes[6:14], 0) - binary.LittleEndian.PutUint64(vBytes[:], limboBatch.FirstBlockNumber) - if err := tx.Put(TablePoolLimbo, keyBytes, vBytes); err != nil { - return err - } - - // BatchNumber - keyBytes[5] = DbKeyBatchesBatchNumberPrefix - binary.LittleEndian.PutUint64(keyBytes[6:14], 0) - binary.LittleEndian.PutUint64(vBytes[:], limboBatch.BatchNumber) - if err := tx.Put(TablePoolLimbo, keyBytes, vBytes); err != nil { - return err - } - - // BatchNumber - keyBytes[5] = DbKeyBatchesForkIdPrefix - binary.LittleEndian.PutUint64(keyBytes[6:14], 0) - binary.LittleEndian.PutUint64(vBytes[:], limboBatch.ForkId) - if err := tx.Put(TablePoolLimbo, keyBytes, vBytes); err != nil { - return err - } - - // Transactions - Rlp - for j, limboTx := range limboBatch.Transactions { - keyBytes[5] = DbKeyTxRlpPrefix - binary.LittleEndian.PutUint64(keyBytes[6:14], uint64(j)) - if err := tx.Put(TablePoolLimbo, keyBytes, limboTx.Rlp[:]); err != nil { - return err - } - - keyBytes[5] = DbKeyTxStreamBytesPrefix - if err := tx.Put(TablePoolLimbo, keyBytes, limboTx.StreamBytes[:]); err != nil { - return err - } - - keyBytes[5] = DbKeyTxRootPrefix - if err := tx.Put(TablePoolLimbo, keyBytes, limboTx.Root[:]); err != nil { - return err - } - - keyBytes[5] = DbKeyTxHashPrefix - if err := tx.Put(TablePoolLimbo, keyBytes, limboTx.Hash[:]); err != nil { - return err - } - - keyBytes[5] = DbKeyTxSenderPrefix - if err := tx.Put(TablePoolLimbo, keyBytes, limboTx.Sender[:]); err != nil { - return err - } - - keyBytes[5] = DbKeyTxPreviousTxPrefix - v = make([]byte, 4) - binary.LittleEndian.PutUint32(v, limboTx.PreviousTx) - if err := tx.Put(TablePoolLimbo, keyBytes, v); err != nil { - return err - } - } - } - - v = []byte{0} - if p.limbo.awaitingBlockHandling.Load() { - v[0] = 1 - } - if err := tx.Put(TablePoolLimbo, []byte{DbKeyAwaitingBlockHandlingPrefix}, v); err != nil { - return err - } - - return nil -} - -func (p *TxPool) fromDBLimbo(ctx context.Context, tx kv.Tx, cacheView kvcache.CacheView) error { - //TODO: remove this check once limbo persistency works - if !p.ethCfg.Limbo { - return nil - } - - p.limbo.limboSlots = &types.TxSlots{} - parseCtx := types.NewTxParseContext(p.chainID) - parseCtx.WithSender(false) - - it, err := tx.Range(TablePoolLimbo, nil, nil) - if err != nil { - return err - } - - for it.HasNext() { - k, v, err := it.Next() - if err != nil { - return err - } - - switch k[0] { - case DbKeyInvalidTxPrefix: - hash := hexutils.BytesToHex(k[1:]) - p.limbo.invalidTxsMap[hash] = v[0] - case DbKeySlotsPrefix: - addr, txRlp := *(*[20]byte)(v[:20]), v[20:] - txn := &types.TxSlot{} - - _, err = parseCtx.ParseTransaction(txRlp, 0, txn, nil, false /* hasEnvelope */, nil) - if err != nil { - err = fmt.Errorf("err: %w, rlp: %x", err, txRlp) - log.Warn("[txpool] fromDB: parseTransaction", "err", err) - continue - } - - txn.SenderID, txn.Traced = p.senders.getOrCreateID(addr) - binary.BigEndian.Uint64(v) - - // ValidateTx function validates a tx against current network state. - // Limbo transactions are expected to be invalid according to current network state. - // That's why there is no point to check it while recovering the pool from a database. - // These transactions may become valid after some of the current tx in the pool are executed - // so leave the decision whether a limbo transaction (or any other transaction that has been unwound) to the execution stage. - // if reason := p.validateTx(txn, true, cacheView, addr); reason != NotSet && reason != Success { - // return nil - // } - p.limbo.limboSlots.Append(txn, addr[:], true) - case DbKeyBatchesPrefix: - batchesI := binary.LittleEndian.Uint32(k[1:5]) - batchesJ := binary.LittleEndian.Uint64(k[6:14]) - p.limbo.resizeBatches(int(batchesI) + 1) - - switch k[5] { - case DbKeyBatchesWitnessPrefix: - p.limbo.limboBatches[batchesI].Witness = v - case DbKeyBatchesL1InfoTreePrefix: - p.limbo.limboBatches[batchesI].L1InfoTreeMinTimestamps[batchesJ] = binary.LittleEndian.Uint64(v) - case DbKeyBatchesTimestampLimitPrefix: - p.limbo.limboBatches[batchesI].TimestampLimit = binary.LittleEndian.Uint64(v) - case DbKeyBatchesFirstBlockNumberPrefix: - p.limbo.limboBatches[batchesI].FirstBlockNumber = binary.LittleEndian.Uint64(v) - case DbKeyBatchesBatchNumberPrefix: - p.limbo.limboBatches[batchesI].BatchNumber = binary.LittleEndian.Uint64(v) - case DbKeyBatchesForkIdPrefix: - p.limbo.limboBatches[batchesI].ForkId = binary.LittleEndian.Uint64(v) - case DbKeyTxRlpPrefix: - p.limbo.limboBatches[batchesI].resizeTransactions(int(batchesJ) + 1) - p.limbo.limboBatches[batchesI].Transactions[batchesJ].Rlp = v - case DbKeyTxStreamBytesPrefix: - p.limbo.limboBatches[batchesI].resizeTransactions(int(batchesJ) + 1) - p.limbo.limboBatches[batchesI].Transactions[batchesJ].StreamBytes = v - case DbKeyTxRootPrefix: - p.limbo.limboBatches[batchesI].resizeTransactions(int(batchesJ) + 1) - copy(p.limbo.limboBatches[batchesI].Transactions[batchesJ].Root[:], v) - case DbKeyTxHashPrefix: - p.limbo.limboBatches[batchesI].resizeTransactions(int(batchesJ) + 1) - copy(p.limbo.limboBatches[batchesI].Transactions[batchesJ].Hash[:], v) - case DbKeyTxSenderPrefix: - p.limbo.limboBatches[batchesI].resizeTransactions(int(batchesJ) + 1) - copy(p.limbo.limboBatches[batchesI].Transactions[batchesJ].Sender[:], v) - case DbKeyTxPreviousTxPrefix: - p.limbo.limboBatches[batchesI].resizeTransactions(int(batchesJ) + 1) - p.limbo.limboBatches[batchesI].Transactions[batchesJ].PreviousTx = binary.LittleEndian.Uint32(v) - } - case DbKeyAwaitingBlockHandlingPrefix: - if v[0] == 0 { - p.limbo.awaitingBlockHandling.Store(false) - } else { - p.limbo.awaitingBlockHandling.Store(true) - } - default: - panic("Invalid key") - } - - } - - return nil -} - func prepareSendersWithChangedState(txs *types.TxSlots) *LimboSendersWithChangedState { sendersWithChangedState := NewLimboSendersWithChangedState() diff --git a/zk/txpool/pool_zk_limbo_persistent.go b/zk/txpool/pool_zk_limbo_persistent.go new file mode 100644 index 00000000000..1e04b5788ed --- /dev/null +++ b/zk/txpool/pool_zk_limbo_persistent.go @@ -0,0 +1,288 @@ +package txpool + +import ( + "context" + "encoding/binary" + "fmt" + "math" + + "github.com/gateway-fm/cdk-erigon-lib/common" + "github.com/gateway-fm/cdk-erigon-lib/kv" + "github.com/gateway-fm/cdk-erigon-lib/kv/kvcache" + "github.com/gateway-fm/cdk-erigon-lib/types" + "github.com/ledgerwatch/log/v3" + "github.com/status-im/keycard-go/hexutils" +) + +type LimboBlockPersistentHelper struct { + keyBytesBlock []byte + keyBytesBlockUint64Array []byte + keyBytesTx []byte + bytes8Value []byte +} + +func newLimboBlockPersistentHelper() *LimboBlockPersistentHelper { + return &LimboBlockPersistentHelper{ + keyBytesBlock: make([]byte, 9), + keyBytesBlockUint64Array: make([]byte, 17), + keyBytesTx: make([]byte, 13), + bytes8Value: make([]byte, 8), + } +} + +func (h *LimboBlockPersistentHelper) setBlockIndex(unckeckedBlockIndex, invalidBlockIndex int) { + binary.LittleEndian.PutUint32(h.keyBytesBlock[1:5], uint32(unckeckedBlockIndex)) + binary.LittleEndian.PutUint32(h.keyBytesBlock[5:9], uint32(invalidBlockIndex)) + binary.LittleEndian.PutUint32(h.keyBytesBlockUint64Array[1:5], uint32(unckeckedBlockIndex)) + binary.LittleEndian.PutUint32(h.keyBytesBlockUint64Array[5:9], uint32(invalidBlockIndex)) + binary.LittleEndian.PutUint32(h.keyBytesTx[1:5], uint32(unckeckedBlockIndex)) + binary.LittleEndian.PutUint32(h.keyBytesTx[5:9], uint32(invalidBlockIndex)) +} + +func (h *LimboBlockPersistentHelper) setTxIndex(txIndex int) { + binary.LittleEndian.PutUint32(h.keyBytesTx[9:13], uint32(txIndex)) +} + +func (p *TxPool) flushLockedLimbo(tx kv.RwTx) (err error) { + if !p.ethCfg.Limbo { + return nil + } + + if err := tx.CreateBucket(TablePoolLimbo); err != nil { + return err + } + + if err := tx.ClearBucket(TablePoolLimbo); err != nil { + return err + } + + for hash, handled := range p.limbo.invalidTxsMap { + hashAsBytes := hexutils.HexToBytes(hash) + key := append([]byte{DbKeyInvalidTxPrefix}, hashAsBytes...) + tx.Put(TablePoolLimbo, key, []byte{handled}) + } + + v := make([]byte, 0, 1024) + for i, txSlot := range p.limbo.limboSlots.Txs { + v = common.EnsureEnoughSize(v, 20+len(txSlot.Rlp)) + sender := p.limbo.limboSlots.Senders.At(i) + + copy(v[:20], sender) + copy(v[20:], txSlot.Rlp) + + key := append([]byte{DbKeySlotsPrefix}, txSlot.IDHash[:]...) + if err := tx.Put(TablePoolLimbo, key, v); err != nil { + return err + } + } + + limboBlockPersistentHelper := newLimboBlockPersistentHelper() + for i, limboBlock := range p.limbo.uncheckedLimboBlocks { + limboBlockPersistentHelper.setBlockIndex(i, math.MaxUint32) + if err = flushLockedLimboBlock(tx, limboBlock, limboBlockPersistentHelper); err != nil { + return err + } + } + for i, limboBlock := range p.limbo.invalidLimboBlocks { + limboBlockPersistentHelper.setBlockIndex(math.MaxUint32, i) + if err = flushLockedLimboBlock(tx, limboBlock, limboBlockPersistentHelper); err != nil { + return err + } + } + + v = []byte{0} + if p.limbo.awaitingBlockHandling.Load() { + v[0] = 1 + } + if err := tx.Put(TablePoolLimbo, []byte{DbKeyAwaitingBlockHandlingPrefix}, v); err != nil { + return err + } + + return nil +} + +func flushLockedLimboBlock(tx kv.RwTx, limboBlock *LimboBlockDetails, limboBlockPersistentHelper *LimboBlockPersistentHelper) error { + keyBytesBlock := limboBlockPersistentHelper.keyBytesBlock + keyBytesBlockUint64Array := limboBlockPersistentHelper.keyBytesBlockUint64Array + keyBytesTx := limboBlockPersistentHelper.keyBytesTx + bytes8Value := limboBlockPersistentHelper.bytes8Value + + // Witness + keyBytesBlock[0] = DbKeyBlockWitnessPrefix + if err := tx.Put(TablePoolLimbo, keyBytesBlock, limboBlock.Witness); err != nil { + return err + } + + // L1InfoTreeMinTimestamps + keyBytesBlock[0] = DbKeyBlockL1InfoTreePrefix + copy(keyBytesBlockUint64Array, keyBytesBlock) + for k, v := range limboBlock.L1InfoTreeMinTimestamps { + binary.LittleEndian.PutUint64(keyBytesBlockUint64Array[9:17], uint64(k)) + binary.LittleEndian.PutUint64(bytes8Value[:], v) + if err := tx.Put(TablePoolLimbo, keyBytesBlockUint64Array, bytes8Value); err != nil { + return err + } + } + + // BlockTimestamp + keyBytesBlock[0] = DbKeyBlockBlockTimestampPrefix + binary.LittleEndian.PutUint64(bytes8Value[:], limboBlock.BlockTimestamp) + if err := tx.Put(TablePoolLimbo, keyBytesBlock, bytes8Value); err != nil { + return err + } + + // BlockNumber + keyBytesBlock[0] = DbKeyBlockBlockNumberPrefix + binary.LittleEndian.PutUint64(bytes8Value[:], limboBlock.BlockNumber) + if err := tx.Put(TablePoolLimbo, keyBytesBlock, bytes8Value); err != nil { + return err + } + + // BatchNumber + keyBytesBlock[0] = DbKeyBlockBatchNumberPrefix + binary.LittleEndian.PutUint64(bytes8Value[:], limboBlock.BatchNumber) + if err := tx.Put(TablePoolLimbo, keyBytesBlock, bytes8Value); err != nil { + return err + } + + // ForkId + keyBytesBlock[0] = DbKeyBlockForkIdPrefix + binary.LittleEndian.PutUint64(bytes8Value[:], limboBlock.ForkId) + if err := tx.Put(TablePoolLimbo, keyBytesBlock, bytes8Value); err != nil { + return err + } + + for j, limboTx := range limboBlock.Transactions { + limboBlockPersistentHelper.setTxIndex(j) + + // Transaction - Rlp + keyBytesTx[0] = DbKeyTxRlpPrefix + if err := tx.Put(TablePoolLimbo, keyBytesTx, limboTx.Rlp[:]); err != nil { + return err + } + + // Transaction - Stream bytes + keyBytesTx[0] = DbKeyTxStreamBytesPrefix + if err := tx.Put(TablePoolLimbo, keyBytesTx, limboTx.StreamBytes[:]); err != nil { + return err + } + + // Transaction - Root + keyBytesTx[0] = DbKeyTxRootPrefix + if err := tx.Put(TablePoolLimbo, keyBytesTx, limboTx.Root[:]); err != nil { + return err + } + + // Transaction - Hash + keyBytesTx[0] = DbKeyTxHashPrefix + if err := tx.Put(TablePoolLimbo, keyBytesTx, limboTx.Hash[:]); err != nil { + return err + } + + // Transaction - Sender + keyBytesTx[0] = DbKeyTxSenderPrefix + if err := tx.Put(TablePoolLimbo, keyBytesTx, limboTx.Sender[:]); err != nil { + return err + } + } + + return nil +} + +func (p *TxPool) fromDBLimbo(ctx context.Context, tx kv.Tx, cacheView kvcache.CacheView) error { + if !p.ethCfg.Limbo { + return nil + } + + p.limbo.limboSlots = &types.TxSlots{} + parseCtx := types.NewTxParseContext(p.chainID) + parseCtx.WithSender(false) + + it, err := tx.Range(TablePoolLimbo, nil, nil) + if err != nil { + return err + } + + for it.HasNext() { + k, v, err := it.Next() + if err != nil { + return err + } + + switch k[0] { + case DbKeyInvalidTxPrefix: + hash := hexutils.BytesToHex(k[1:]) + p.limbo.invalidTxsMap[hash] = v[0] + case DbKeySlotsPrefix: + addr, txRlp := *(*[20]byte)(v[:20]), v[20:] + txn := &types.TxSlot{} + + _, err = parseCtx.ParseTransaction(txRlp, 0, txn, nil, false /* hasEnvelope */, nil) + if err != nil { + err = fmt.Errorf("err: %w, rlp: %x", err, txRlp) + log.Warn("[txpool] fromDB: parseTransaction", "err", err) + continue + } + + txn.SenderID, txn.Traced = p.senders.getOrCreateID(addr) + binary.BigEndian.Uint64(v) + + // ValidateTx function validates a tx against current network state. + // Limbo transactions are expected to be invalid according to current network state. + // That's why there is no point to check it while recovering the pool from a database. + // These transactions may become valid after some of the current tx in the pool are executed + // so leave the decision whether a limbo transaction (or any other transaction that has been unwound) to the execution stage. + // if reason := p.validateTx(txn, true, cacheView, addr); reason != NotSet && reason != Success { + // return nil + // } + p.limbo.limboSlots.Append(txn, addr[:], true) + case DbKeyBlockWitnessPrefix: + fromDBLimboBlock(p, -1, k, v).Witness = v + case DbKeyBlockL1InfoTreePrefix: + l1InfoTreeKey := binary.LittleEndian.Uint64(k[9:17]) + fromDBLimboBlock(p, -1, k, v).L1InfoTreeMinTimestamps[l1InfoTreeKey] = binary.LittleEndian.Uint64(v) + case DbKeyBlockBlockTimestampPrefix: + fromDBLimboBlock(p, -1, k, v).BlockTimestamp = binary.LittleEndian.Uint64(v) + case DbKeyBlockBlockNumberPrefix: + fromDBLimboBlock(p, -1, k, v).BlockNumber = binary.LittleEndian.Uint64(v) + case DbKeyBlockBatchNumberPrefix: + fromDBLimboBlock(p, -1, k, v).BatchNumber = binary.LittleEndian.Uint64(v) + case DbKeyBlockForkIdPrefix: + fromDBLimboBlock(p, -1, k, v).ForkId = binary.LittleEndian.Uint64(v) + case DbKeyTxRlpPrefix: + txIndex := binary.LittleEndian.Uint32(k[9:13]) + fromDBLimboBlock(p, int(txIndex), k, v).Transactions[txIndex].Rlp = v + case DbKeyTxStreamBytesPrefix: + txIndex := binary.LittleEndian.Uint32(k[9:13]) + fromDBLimboBlock(p, int(txIndex), k, v).Transactions[txIndex].StreamBytes = v + case DbKeyTxRootPrefix: + txIndex := binary.LittleEndian.Uint32(k[9:13]) + copy(fromDBLimboBlock(p, int(txIndex), k, v).Transactions[txIndex].Root[:], v) + case DbKeyTxHashPrefix: + txIndex := binary.LittleEndian.Uint32(k[9:13]) + copy(fromDBLimboBlock(p, int(txIndex), k, v).Transactions[txIndex].Hash[:], v) + case DbKeyTxSenderPrefix: + txIndex := binary.LittleEndian.Uint32(k[9:13]) + copy(fromDBLimboBlock(p, int(txIndex), k, v).Transactions[txIndex].Sender[:], v) + case DbKeyAwaitingBlockHandlingPrefix: + p.limbo.awaitingBlockHandling.Store(v[0] != 0) + default: + panic("Invalid key") + } + + } + + return nil +} + +func fromDBLimboBlock(p *TxPool, txIndex int, k, v []byte) *LimboBlockDetails { + uncheckedBlockIndex := binary.LittleEndian.Uint32(k[1:5]) + invalidBlockIndex := binary.LittleEndian.Uint32(k[5:9]) + if uncheckedBlockIndex != math.MaxUint32 { + p.limbo.resizeUncheckedBlocks(int(uncheckedBlockIndex), txIndex) + return p.limbo.uncheckedLimboBlocks[uncheckedBlockIndex] + } + + p.limbo.resizeInvalidBlocks(int(invalidBlockIndex), txIndex) + return p.limbo.invalidLimboBlocks[invalidBlockIndex] +} diff --git a/zk/txpool/pool_zk_limbo_test.go b/zk/txpool/pool_zk_limbo_persistent_test.go similarity index 89% rename from zk/txpool/pool_zk_limbo_test.go rename to zk/txpool/pool_zk_limbo_persistent_test.go index 8738f8679bc..198d3070e6e 100644 --- a/zk/txpool/pool_zk_limbo_test.go +++ b/zk/txpool/pool_zk_limbo_persistent_test.go @@ -2,7 +2,6 @@ package txpool import ( "context" - "math" "math/big" "os" "testing" @@ -68,55 +67,76 @@ func store(t *testing.T, dbPath string) *TxPool { txn.SenderID = senderId pSource.limbo.limboSlots.Append(txn, tx02Sender, true) - pSource.limbo.limboBatches = append(pSource.limbo.limboBatches, &LimboBatchDetails{ + pSource.limbo.uncheckedLimboBlocks = append(pSource.limbo.uncheckedLimboBlocks, &LimboBlockDetails{ Witness: []byte{1, 4, 1}, L1InfoTreeMinTimestamps: map[uint64]uint64{5: 6}, - TimestampLimit: 51, - FirstBlockNumber: 14, + BlockTimestamp: 51, + BlockNumber: 14, BatchNumber: 5131, ForkId: 11, - Transactions: []*LimboBatchTransactionDetails{ - &LimboBatchTransactionDetails{ + Transactions: []*LimboBlockTransactionDetails{ + &LimboBlockTransactionDetails{ Rlp: []byte{100, 41, 151, 141, 13}, StreamBytes: []byte{5, 15}, Root: common.BytesToHash([]byte{1, 2, 4, 134, 41, 15, 19, 105, 10, 214, 1, 2, 4, 134, 41, 15, 19, 105, 10, 214, 1, 2, 4, 134, 41, 15, 19, 105, 10, 214, 255, 0}), Hash: common.HexToHash("FE4CEE305D5DF787F55057A0F37DEB0F5170A1E9BB361CFD1638BBEAFE66EE4B"), Sender: common.HexToAddress("0x510b131a0b61aeff3c15bc73f03fa73fa12d9004"), - PreviousTx: math.MaxUint32, }, - &LimboBatchTransactionDetails{ + &LimboBlockTransactionDetails{ Rlp: []byte{120, 21, 121, 121, 213}, StreamBytes: []byte{19, 170}, Root: common.BytesToHash([]byte{100, 20, 41, 134, 41, 15, 19, 105, 10, 214, 1, 21, 43, 134, 41, 15, 19, 105, 10, 214, 1, 2, 4, 134, 41, 15, 19, 105, 10, 214, 255, 0}), Hash: common.HexToHash("10FB489D863DBAB249214BE9C128E37A54C3E57F514685191D45DCE8B0778A4F"), Sender: common.HexToAddress("0x92f20480f6c693ab9fddfffff1e400532d24847b"), - PreviousTx: math.MaxUint32, }, }, }) - pSource.limbo.limboBatches = append(pSource.limbo.limboBatches, &LimboBatchDetails{ + pSource.limbo.uncheckedLimboBlocks = append(pSource.limbo.uncheckedLimboBlocks, &LimboBlockDetails{ Witness: []byte{4, 1, 4}, L1InfoTreeMinTimestamps: map[uint64]uint64{10: 20}, - TimestampLimit: 1535151, - FirstBlockNumber: 114, + BlockTimestamp: 1535151, + BlockNumber: 114, BatchNumber: 65131, ForkId: 111, - Transactions: []*LimboBatchTransactionDetails{ - &LimboBatchTransactionDetails{ + Transactions: []*LimboBlockTransactionDetails{ + &LimboBlockTransactionDetails{ Rlp: []byte{100, 41, 151, 141, 13}, StreamBytes: []byte{50, 150}, Root: common.BytesToHash([]byte{10, 20, 40, 34, 141, 15, 19, 105, 10, 214, 1, 2, 4, 134, 41, 15, 19, 105, 10, 214, 1, 2, 4, 134, 41, 15, 19, 105, 10, 214, 255, 0}), Hash: common.HexToHash("A8B39BFE352B06575DBA0063170E5094FD12B61384E2276FA3A77C07CE726886"), Sender: common.HexToAddress("0x510b131a0b61aeff3c15bc73f03fa73fa12d9004"), - PreviousTx: math.MaxUint32, }, - &LimboBatchTransactionDetails{ + &LimboBlockTransactionDetails{ Rlp: []byte{120, 21, 121, 121, 213}, StreamBytes: []byte{191, 70}, Root: common.BytesToHash([]byte{40, 22, 20, 34, 241, 15, 19, 105, 10, 214, 1, 2, 4, 134, 41, 15, 19, 145, 10, 0, 1, 2, 4, 4, 41, 15, 19, 12, 10, 214, 0, 0}), Hash: common.HexToHash("2504231FF7150135925D98A462A331522D9C2F712C85BEBC00B5BEBDF50DD7AA"), Sender: common.HexToAddress("0x92f20480f6c693ab9fddfffff1e400532d24847b"), - PreviousTx: math.MaxUint32, + }, + }, + }) + + pSource.limbo.invalidLimboBlocks = append(pSource.limbo.invalidLimboBlocks, &LimboBlockDetails{ + Witness: []byte{40, 45, 255}, + L1InfoTreeMinTimestamps: map[uint64]uint64{20: 30}, + BlockTimestamp: 9515151, + BlockNumber: 138, + BatchNumber: 165131, + ForkId: 222, + Transactions: []*LimboBlockTransactionDetails{ + &LimboBlockTransactionDetails{ + Rlp: []byte{101, 42, 198, 241, 133}, + StreamBytes: []byte{9, 213}, + Root: common.BytesToHash([]byte{101, 201, 101, 34, 141, 15, 19, 105, 10, 214, 1, 2, 4, 134, 41, 15, 19, 105, 10, 214, 1, 2, 4, 134, 41, 15, 19, 105, 10, 214, 255, 0}), + Hash: common.HexToHash("A8B39BFE352B06575DBB0063170E5094FD12B61384E2276FA3A77C07CE726886"), + Sender: common.HexToAddress("0x510b131a0b61aeef3c15bc73f03fa73fa12d9004"), + }, + &LimboBlockTransactionDetails{ + Rlp: []byte{79, 76, 184, 159, 136}, + StreamBytes: []byte{91, 99}, + Root: common.BytesToHash([]byte{40, 22, 20, 34, 241, 15, 19, 105, 101, 136, 1, 2, 4, 134, 41, 15, 19, 145, 10, 0, 1, 2, 4, 4, 41, 15, 19, 12, 10, 214, 0, 0}), + Hash: common.HexToHash("2504231FF7150135925D98A462A331522D9C2F712C85BEBCD0B5BEBDF50DD7AA"), + Sender: common.HexToAddress("0x92f20480f6c693ab9fddfffff1e400532d24847f"), }, }, }) @@ -141,7 +161,8 @@ func store(t *testing.T, dbPath string) *TxPool { assert.DeepEqual(t, pSource.limbo.invalidTxsMap, pTarget.limbo.invalidTxsMap) assert.DeepEqual(t, pSource.limbo.limboSlots, pTarget.limbo.limboSlots) - assert.DeepEqual(t, pSource.limbo.limboBatches, pTarget.limbo.limboBatches) + assert.DeepEqual(t, pSource.limbo.uncheckedLimboBlocks, pTarget.limbo.uncheckedLimboBlocks) + assert.DeepEqual(t, pSource.limbo.invalidLimboBlocks, pTarget.limbo.invalidLimboBlocks) assert.Equal(t, pSource.limbo.awaitingBlockHandling.Load(), pTarget.limbo.awaitingBlockHandling.Load()) err = tx.Commit() @@ -178,7 +199,8 @@ func restoreRo(t *testing.T, dbPath string, pSource *TxPool) { assert.DeepEqual(t, pSource.limbo.invalidTxsMap, pTarget.limbo.invalidTxsMap) assert.DeepEqual(t, pSource.limbo.limboSlots, pTarget.limbo.limboSlots) - assert.DeepEqual(t, pSource.limbo.limboBatches, pTarget.limbo.limboBatches) + assert.DeepEqual(t, pSource.limbo.uncheckedLimboBlocks, pTarget.limbo.uncheckedLimboBlocks) + assert.DeepEqual(t, pSource.limbo.invalidLimboBlocks, pTarget.limbo.invalidLimboBlocks) assert.Equal(t, pSource.limbo.awaitingBlockHandling.Load(), pTarget.limbo.awaitingBlockHandling.Load()) err = tx.Commit() diff --git a/zk/txpool/pool_zk_limbo_processor.go b/zk/txpool/pool_zk_limbo_processor.go index ef46e18e5b1..99b388643aa 100644 --- a/zk/txpool/pool_zk_limbo_processor.go +++ b/zk/txpool/pool_zk_limbo_processor.go @@ -2,6 +2,7 @@ package txpool import ( "context" + "fmt" "math" "time" @@ -55,18 +56,21 @@ func (_this *LimboSubPoolProcessor) run() { defer log.Info("[Limbo pool processor] End") ctx := context.Background() - limboBatchDetails := _this.txPool.GetLimboDetailsCloned() + limboBlocksDetails := _this.txPool.GetUncheckedLimboBlocksDetailsClonedWeak() - size := len(limboBatchDetails) + size := len(limboBlocksDetails) if size == 0 { return } - for _, limboBatch := range limboBatchDetails { - for _, limboTx := range limboBatch.Transactions { + totalTransactions := 0 + processedTransactions := 0 + for _, limboBlock := range limboBlocksDetails { + for _, limboTx := range limboBlock.Transactions { if !limboTx.hasRoot() { return } + totalTransactions++ } } @@ -83,23 +87,29 @@ func (_this *LimboSubPoolProcessor) run() { unlimitedCounters[k] = math.MaxInt32 } - blockNumbers := []uint64{1} // let's assume that there is a just single block number 1, because the number itself does not matter invalidTxs := []*string{} + invalidBlocksIndices := []int{} + lastAddedInvalidBlockIndex := -1 - for _, limboBatch := range limboBatchDetails { - for _, limboTx := range limboBatch.Transactions { - request := legacy_executor_verifier.NewVerifierRequest(limboBatch.ForkId, limboBatch.BatchNumber, blockNumbers, limboTx.Root, unlimitedCounters) - err := _this.verifier.VerifySync(tx, request, limboBatch.Witness, limboTx.StreamBytes, limboBatch.TimestampLimit, limboBatch.FirstBlockNumber, limboBatch.L1InfoTreeMinTimestamps) + for i, limboBlock := range limboBlocksDetails { + for _, limboTx := range limboBlock.Transactions { + request := legacy_executor_verifier.NewVerifierRequest(limboBlock.ForkId, limboBlock.BatchNumber, []uint64{limboBlock.BlockNumber}, limboTx.Root, unlimitedCounters) + err := _this.verifier.VerifySync(tx, request, limboBlock.Witness, limboTx.StreamBytes, limboBlock.BlockTimestamp, limboBlock.L1InfoTreeMinTimestamps) if err != nil { idHash := hexutils.BytesToHex(limboTx.Hash[:]) invalidTxs = append(invalidTxs, &idHash) + if lastAddedInvalidBlockIndex != i { + invalidBlocksIndices = append(invalidBlocksIndices, i) + lastAddedInvalidBlockIndex = i + } log.Info("[Limbo pool processor]", "invalid tx", limboTx.Hash, "err", err) continue } - log.Info("[Limbo pool processor]", "valid tx", limboTx.Hash) + processedTransactions++ + log.Info("[Limbo pool processor]", "valid tx", limboTx.Hash, "progress", fmt.Sprintf("transactions: %d of %d, blocks: %d of %d", processedTransactions, totalTransactions, i+1, len(limboBlocksDetails))) } } - _this.txPool.MarkProcessedLimboDetails(size, invalidTxs) + _this.txPool.MarkProcessedLimboDetails(size, invalidBlocksIndices, invalidTxs) } diff --git a/zk/txpool/txpool_grpc_server.go b/zk/txpool/txpool_grpc_server.go index bd3f0eb928c..e473953a2b8 100644 --- a/zk/txpool/txpool_grpc_server.go +++ b/zk/txpool/txpool_grpc_server.go @@ -187,10 +187,11 @@ func (s *GrpcServer) Add(ctx context.Context, in *txpool_proto.AddRequest) (*txp j := 0 for i := 0; i < len(in.RlpTxs); i++ { // some incoming txs may be rejected, so - need secnod index - slots.Resize(uint(j + 1)) - slots.Txs[j] = &types.TxSlot{} - slots.IsLocal[j] = true - if _, err := parseCtx.ParseTransaction(in.RlpTxs[i], 0, slots.Txs[j], slots.Senders.At(j), false /* hasEnvelope */, func(hash []byte) error { + txSlot := &types.TxSlot{} + sender := common.Address{} + senderSlice := sender[:] + + if _, err := parseCtx.ParseTransaction(in.RlpTxs[i], 0, txSlot, senderSlice, false /* hasEnvelope */, func(hash []byte) error { if known, _ := s.txPool.IdHashKnown(tx, hash); known { return types.ErrAlreadyKnown } @@ -208,6 +209,11 @@ func (s *GrpcServer) Add(ctx context.Context, in *txpool_proto.AddRequest) (*txp } continue } + + slots.Resize(uint(j + 1)) + slots.Txs[j] = txSlot + copy(slots.Senders.At(j), senderSlice) + slots.IsLocal[j] = true j++ } @@ -238,7 +244,7 @@ func mapDiscardReasonToProto(reason DiscardReason) txpool_proto.ImportResult { return txpool_proto.ImportResult_ALREADY_EXISTS case UnderPriced, ReplaceUnderpriced, FeeTooLow: return txpool_proto.ImportResult_FEE_TOO_LOW - case InvalidSender, NegativeValue, OversizedData, InitCodeTooLarge, RLPTooLong, UnsupportedTx: + case GasLimitTooHigh, InvalidSender, NegativeValue, OversizedData, InitCodeTooLarge, RLPTooLong, UnsupportedTx: return txpool_proto.ImportResult_INVALID default: return txpool_proto.ImportResult_INTERNAL_ERROR diff --git a/zk/types/zk_types.go b/zk/types/zk_types.go index 371b88b0667..0cdfd2358a3 100644 --- a/zk/types/zk_types.go +++ b/zk/types/zk_types.go @@ -7,10 +7,11 @@ import ( "bytes" "encoding/binary" + "fmt" + "github.com/holiman/uint256" "github.com/ledgerwatch/erigon/cl/utils" ethTypes "github.com/ledgerwatch/erigon/core/types" - "fmt" ) const EFFECTIVE_GAS_PRICE_PERCENTAGE_DISABLED = 0 @@ -117,3 +118,10 @@ func (ib *L1InjectedBatch) Unmarshall(input []byte) error { ib.Transaction = append([]byte{}, input[132:]...) return nil } + +type ForkInterval struct { + ForkID uint64 + FromBatchNumber uint64 + ToBatchNumber uint64 + BlockNumber uint64 +} diff --git a/zk/utils.go b/zk/utils.go index 6a8baeea849..a473851756c 100644 --- a/zk/utils.go +++ b/zk/utils.go @@ -6,6 +6,7 @@ import ( "time" "github.com/ledgerwatch/log/v3" + "sync" ) var ErrLimboState = errors.New("Calculating limbo state") @@ -15,10 +16,17 @@ var ErrLimboState = errors.New("Calculating limbo state") func ProgressPrinter(message string, total uint64, quiet bool) (chan uint64, func()) { progress := make(chan uint64) ctDone := make(chan bool) + var once sync.Once + + cleanup := func() { + once.Do(func() { + close(ctDone) + }) + } go func() { defer close(progress) - defer close(ctDone) + defer cleanup() ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() @@ -33,6 +41,9 @@ func ProgressPrinter(message string, total uint64, quiet bool) (chan uint64, fun if total > 0 { pct = (pc * 100) / total } + if pct == 100 { + log.Info(fmt.Sprintf("%s: %d/%d (%d%%)", message, pc, total, pct)) + } case <-ticker.C: if pc > 0 && !quiet { log.Info(fmt.Sprintf("%s: %d/%d (%d%%)", message, pc, total, pct)) @@ -43,7 +54,7 @@ func ProgressPrinter(message string, total uint64, quiet bool) (chan uint64, fun } }() - return progress, func() { ctDone <- true } + return progress, cleanup } // prints progress every 10 seconds @@ -51,10 +62,17 @@ func ProgressPrinter(message string, total uint64, quiet bool) (chan uint64, fun func ProgressPrinterWithoutTotal(message string) (chan uint64, func()) { progress := make(chan uint64) ctDone := make(chan bool) + var once sync.Once + + cleanup := func() { + once.Do(func() { + close(ctDone) + }) + } go func() { defer close(progress) - defer close(ctDone) + defer cleanup() ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() @@ -75,7 +93,7 @@ func ProgressPrinterWithoutTotal(message string) (chan uint64, func()) { } }() - return progress, func() { ctDone <- true } + return progress, cleanup } // prints progress every 10 seconds @@ -83,10 +101,17 @@ func ProgressPrinterWithoutTotal(message string) (chan uint64, func()) { func ProgressPrinterWithoutValues(message string, total uint64) (chan uint64, func()) { progress := make(chan uint64) ctDone := make(chan bool) + var once sync.Once + + cleanup := func() { + once.Do(func() { + close(ctDone) + }) + } go func() { defer close(progress) - defer close(ctDone) + defer cleanup() ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() @@ -111,5 +136,5 @@ func ProgressPrinterWithoutValues(message string, total uint64) (chan uint64, fu } }() - return progress, func() { ctDone <- true } + return progress, cleanup } diff --git a/zk/utils/acc_input_hash _test.go b/zk/utils/acc_input_hash _test.go new file mode 100644 index 00000000000..60b39b91234 --- /dev/null +++ b/zk/utils/acc_input_hash _test.go @@ -0,0 +1,105 @@ +package utils + +import ( + "testing" + + "github.com/gateway-fm/cdk-erigon-lib/common" + "github.com/stretchr/testify/require" +) + +func Test_CalculateEtrogAccInputHash(t *testing.T) { + testCases := []struct { + oldAccInputHash string + batchTransactionData string + l1InfoRoot string + limitTimestamp uint64 + sequencerAddress string + forcedBlockHashL1 string + Expected string + }{ + { + oldAccInputHash: "0x0000000000000000000000000000000000000000000000000000000000000000", + batchTransactionData: "0x0b73e6af6e00000001ee80843b9aca00830186a0944d5cf5032b2a844602278b01199ed191a86c93ff88016345785d8a0000808203e880801cee7e01dc62f69a12c3510c6d64de04ee6346d84b6a017f3e786c7d87f963e75d8cc91fa983cd6d9cf55fff80d73bd26cd333b0f098acc1e58edb1fd484ad731bff0b0000000100000002", + l1InfoRoot: "0x462ed3d694d640f04f637e5e3893e8d12f407a53f50201401fd992bb5ab0faf0", + limitTimestamp: 1944498031, + sequencerAddress: "0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D", + forcedBlockHashL1: "0x0000000000000000000000000000000000000000000000000000000000000000", + Expected: "0xcfae2cfa3b8f3f12abce1bccd90e9b203dfdbe56c0c412114f2d3e67c9a897db", + }, + } + + for _, tc := range testCases { + oldAccInputHash := common.HexToHash(tc.oldAccInputHash) + batchTransactionData := common.FromHex(tc.batchTransactionData) + l1InfoRoot := common.HexToHash(tc.l1InfoRoot) + sequencerAddress := common.HexToAddress(tc.sequencerAddress) + forcedBlockHashL1 := common.HexToHash(tc.forcedBlockHashL1) + + newAccInputHash := CalculateEtrogAccInputHash( + oldAccInputHash, + batchTransactionData, + l1InfoRoot, + tc.limitTimestamp, + sequencerAddress, + forcedBlockHashL1, + ) + + require.Equal(t, common.HexToHash(tc.Expected), *newAccInputHash) + } +} + +func Test_CalculatePreEtrogAccInputHash(t *testing.T) { + testCases := []struct { + oldAccInputHash string + batchTransactionData string + globalExitRoot string + timestamp uint64 + sequencerAddress string + Expected string + }{ + { + oldAccInputHash: "0x0000000000000000000000000000000000000000000000000000000000000000", + batchTransactionData: "0xee80843b9aca00830186a0944d5cf5032b2a844602278b01199ed191a86c93ff88016345785d8a0000808203e880801cee7e01dc62f69a12c3510c6d64de04ee6346d84b6a017f3e786c7d87f963e75d8cc91fa983cd6d9cf55fff80d73bd26cd333b0f098acc1e58edb1fd484ad731b", + globalExitRoot: "0x090bcaf734c4f06c93954a827b45a6e8c67b8e0fd1e0a35a1c5982d6961828f9", + timestamp: 1944498031, + sequencerAddress: "0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D", + Expected: "0x704d5cfd3e44b82028f7f8cae31168267a7422c5a447b90a65134116da5a8432", + }, + } + + for _, tc := range testCases { + oldAccInputHash := common.HexToHash(tc.oldAccInputHash) + batchTransactionData := common.FromHex(tc.batchTransactionData) + globalExitRoot := common.HexToHash(tc.globalExitRoot) + sequencerAddress := common.HexToAddress(tc.sequencerAddress) + + newAccInputHash := CalculatePreEtrogAccInputHash( + oldAccInputHash, + batchTransactionData, + globalExitRoot, + tc.timestamp, + sequencerAddress, + ) + + require.Equal(t, common.HexToHash(tc.Expected), *newAccInputHash) + } +} + +func Test_CalculateBatchHashData(t *testing.T) { + testCases := []struct { + batchL2Data string + Expected string + }{ + { + batchL2Data: "0x0b73e6af6e00000001ee80843b9aca00830186a0944d5cf5032b2a844602278b01199ed191a86c93ff88016345785d8a0000808203e880801cee7e01dc62f69a12c3510c6d64de04ee6346d84b6a017f3e786c7d87f963e75d8cc91fa983cd6d9cf55fff80d73bd26cd333b0f098acc1e58edb1fd484ad731bff0b0000000100000002", + Expected: "0x5e7875ab198c4d93379c92990a5d0111af59a0e62b2c4a0e3898e5bd24a18e58", + }, + } + + for _, tc := range testCases { + data := common.FromHex(tc.batchL2Data) + batchHash := CalculateBatchHashData(data) + + require.Equal(t, common.FromHex(tc.Expected), batchHash) + } +} diff --git a/zk/utils/acc_input_hash.go b/zk/utils/acc_input_hash.go new file mode 100644 index 00000000000..eb2a198d311 --- /dev/null +++ b/zk/utils/acc_input_hash.go @@ -0,0 +1,93 @@ +package utils + +import ( + "math/big" + + "github.com/gateway-fm/cdk-erigon-lib/common" + "github.com/iden3/go-iden3-crypto/keccak256" + "github.com/ledgerwatch/erigon/crypto" +) + +// calculates the new accInputHash based on the old one and data frem one new batch +// this returns the accInputHash for the current batch +// oldAccInputHash - the accInputHash from the previous batch +func CalculateEtrogAccInputHash( + oldAccInputHash common.Hash, + batchTransactionData []byte, + l1InfoRoot common.Hash, + limitTimestamp uint64, + sequencerAddress common.Address, + forcedBlockHashL1 common.Hash, +) *common.Hash { + batchHashData := CalculateBatchHashData(batchTransactionData) + v1 := oldAccInputHash.Bytes() + v2 := batchHashData + v3 := l1InfoRoot.Bytes() + v4 := big.NewInt(0).SetUint64(limitTimestamp).Bytes() + v5 := sequencerAddress.Bytes() + v6 := forcedBlockHashL1.Bytes() + + // Add 0s to make values 32 bytes long + for len(v1) < 32 { + v1 = append([]byte{0}, v1...) + } + for len(v3) < 32 { + v3 = append([]byte{0}, v3...) + } + for len(v4) < 8 { + v4 = append([]byte{0}, v4...) + } + for len(v5) < 20 { + v5 = append([]byte{0}, v5...) + } + for len(v6) < 32 { + v6 = append([]byte{0}, v6...) + } + + hash := common.BytesToHash(keccak256.Hash(v1, v2, v3, v4, v5, v6)) + + return &hash +} + +// calculates the new accInputHash based on the old one and data frem one new batch +// this returns the accInputHash for the current batch +// oldAccInputHash - the accInputHash from the previous batch +func CalculatePreEtrogAccInputHash( + oldAccInputHash common.Hash, + batchTransactionData []byte, + globalExitRoot common.Hash, + timestamp uint64, + sequencerAddress common.Address, +) *common.Hash { + batchHashData := CalculateBatchHashData(batchTransactionData) + v1 := oldAccInputHash.Bytes() + v2 := batchHashData + v3 := globalExitRoot.Bytes() + v4 := big.NewInt(0).SetUint64(timestamp).Bytes() + v5 := sequencerAddress.Bytes() + + // Add 0s to make values 32 bytes long + for len(v1) < 32 { + v1 = append([]byte{0}, v1...) + } + for len(v3) < 32 { + v3 = append([]byte{0}, v3...) + } + for len(v4) < 8 { + v4 = append([]byte{0}, v4...) + } + for len(v5) < 20 { + v5 = append([]byte{0}, v5...) + } + + hash := common.BytesToHash(keccak256.Hash(v1, v2, v3, v4, v5)) + + return &hash +} + +// parses batch transactions bytes into a batchHashData +// used for accInputHash calculation +// transactionBytes are as taken from the sequenceBatches calldata +func CalculateBatchHashData(transactions []byte) []byte { + return crypto.Keccak256(transactions) +} diff --git a/zk/utils/utils.go b/zk/utils/utils.go index 4513ecdebbc..5ad4dc68333 100644 --- a/zk/utils/utils.go +++ b/zk/utils/utils.go @@ -1,6 +1,7 @@ package utils import ( + "errors" "fmt" "github.com/gateway-fm/cdk-erigon-lib/common" @@ -37,7 +38,7 @@ func ShouldShortCircuitExecution(tx kv.RwTx, logPrefix string) (bool, uint64, er } executedBatch, err := hermezDb.GetBatchNoByL2Block(executedBlock) - if err != nil { + if err != nil && !errors.Is(err, hermez_db.ErrorNotStored) { return false, 0, err } @@ -65,7 +66,7 @@ func ShouldShortCircuitExecution(tx kv.RwTx, logPrefix string) (bool, uint64, er } // we've got the highest batch to execute to, now get it's highest block - shortCircuitBlock, err = hermezDb.GetHighestBlockInBatch(shortCircuitBatch) + shortCircuitBlock, _, err = hermezDb.GetHighestBlockInBatch(shortCircuitBatch) if err != nil { return false, 0, err } @@ -77,8 +78,7 @@ func ShouldShortCircuitExecution(tx kv.RwTx, logPrefix string) (bool, uint64, er } type ForkReader interface { - GetLowestBatchByFork(forkId uint64) (uint64, error) - GetLowestBlockInBatch(batchNo uint64) (blockNo uint64, found bool, err error) + GetForkIdBlock(forkId uint64) (uint64, bool, error) } type ForkConfigWriter interface { @@ -86,7 +86,7 @@ type ForkConfigWriter interface { } type DbReader interface { - GetHighestBlockInBatch(batchNo uint64) (uint64, error) + GetHighestBlockInBatch(batchNo uint64) (uint64, bool, error) } func UpdateZkEVMBlockCfg(cfg ForkConfigWriter, hermezDb ForkReader, logPrefix string) error { @@ -94,11 +94,7 @@ func UpdateZkEVMBlockCfg(cfg ForkConfigWriter, hermezDb ForkReader, logPrefix st var foundAny bool = false for _, forkId := range chain.ForkIdsOrdered { - batch, err := hermezDb.GetLowestBatchByFork(uint64(forkId)) - if err != nil { - return err - } - blockNum, found, err := hermezDb.GetLowestBlockInBatch(batch) + blockNum, found, err := hermezDb.GetForkIdBlock(uint64(forkId)) if err != nil { return err } @@ -137,7 +133,7 @@ func RecoverySetBlockConfigForks(blockNum uint64, forkId uint64, cfg ForkConfigW func GetBatchLocalExitRootFromSCStorageForLatestBlock(batchNo uint64, db DbReader, tx kv.Tx) (libcommon.Hash, error) { if batchNo > 0 { - blockNo, err := db.GetHighestBlockInBatch(batchNo) + blockNo, _, err := db.GetHighestBlockInBatch(batchNo) if err != nil { return libcommon.Hash{}, err } diff --git a/zk/utils/utils_test.go b/zk/utils/utils_test.go index 575a340b6c3..7737dd806b8 100644 --- a/zk/utils/utils_test.go +++ b/zk/utils/utils_test.go @@ -3,22 +3,16 @@ package utils import ( "testing" - "github.com/stretchr/testify/assert" "github.com/ledgerwatch/erigon/zk/constants" + "github.com/stretchr/testify/assert" ) type SimpleForkReader struct { - BatchForks map[constants.ForkId]uint64 - LowestBlocks map[uint64]uint64 -} - -func (s *SimpleForkReader) GetLowestBatchByFork(forkId uint64) (uint64, error) { - found, _ := s.BatchForks[constants.ForkId(forkId)] - return found, nil + BlockForks map[constants.ForkId]uint64 } -func (s *SimpleForkReader) GetLowestBlockInBatch(batchNo uint64) (uint64, bool, error) { - found, ok := s.LowestBlocks[batchNo] +func (s *SimpleForkReader) GetForkIdBlock(forkId uint64) (uint64, bool, error) { + found, ok := s.BlockForks[constants.ForkId(forkId)] return found, ok, nil } @@ -39,8 +33,7 @@ func (tc *TestConfig) SetForkIdBlock(forkId constants.ForkId, blockNum uint64) e type testScenario struct { name string - batchForks map[constants.ForkId]uint64 - lowestBlocks map[uint64]uint64 + blockForks map[constants.ForkId]uint64 expectedCalls map[constants.ForkId]uint64 } @@ -48,12 +41,9 @@ func TestUpdateZkEVMBlockCfg(t *testing.T) { scenarios := []testScenario{ { name: "HigherForkEnabled", - batchForks: map[constants.ForkId]uint64{ + blockForks: map[constants.ForkId]uint64{ constants.ForkID9Elderberry2: 900, }, - lowestBlocks: map[uint64]uint64{ - 900: 900, - }, expectedCalls: map[constants.ForkId]uint64{ constants.ForkID9Elderberry2: 900, constants.ForkID8Elderberry: 900, @@ -65,14 +55,10 @@ func TestUpdateZkEVMBlockCfg(t *testing.T) { }, { name: "MiddleForksExplicitlyEnabled", - batchForks: map[constants.ForkId]uint64{ + blockForks: map[constants.ForkId]uint64{ constants.ForkID7Etrog: 700, constants.ForkID6IncaBerry: 600, }, - lowestBlocks: map[uint64]uint64{ - 700: 700, - 600: 600, - }, expectedCalls: map[constants.ForkId]uint64{ constants.ForkID7Etrog: 700, constants.ForkID6IncaBerry: 600, @@ -82,14 +68,10 @@ func TestUpdateZkEVMBlockCfg(t *testing.T) { }, { name: "MissingEnablements", - batchForks: map[constants.ForkId]uint64{ + blockForks: map[constants.ForkId]uint64{ constants.ForkID4: 100, constants.ForkID6IncaBerry: 600, }, - lowestBlocks: map[uint64]uint64{ - 100: 100, - 600: 600, - }, expectedCalls: map[constants.ForkId]uint64{ constants.ForkID6IncaBerry: 600, constants.ForkID5Dragonfruit: 600, @@ -101,7 +83,7 @@ func TestUpdateZkEVMBlockCfg(t *testing.T) { for _, scenario := range scenarios { t.Run(scenario.name, func(t *testing.T) { cfg := NewTestConfig() - reader := &SimpleForkReader{BatchForks: scenario.batchForks, LowestBlocks: scenario.lowestBlocks} + reader := &SimpleForkReader{BlockForks: scenario.blockForks} err := UpdateZkEVMBlockCfg(cfg, reader, "TestPrefix") assert.NoError(t, err, "should not return an error") diff --git a/zk/witness/witness.go b/zk/witness/witness.go index 4a0d9b95ac7..444c38d85c5 100644 --- a/zk/witness/witness.go +++ b/zk/witness/witness.go @@ -8,6 +8,8 @@ import ( "math/big" + "time" + libcommon "github.com/gateway-fm/cdk-erigon-lib/common" "github.com/gateway-fm/cdk-erigon-lib/common/datadir" "github.com/gateway-fm/cdk-erigon-lib/kv" @@ -34,7 +36,6 @@ import ( zkStages "github.com/ledgerwatch/erigon/zk/stages" zkUtils "github.com/ledgerwatch/erigon/zk/utils" "github.com/ledgerwatch/log/v3" - "time" ) var ( @@ -85,7 +86,7 @@ func (g *Generator) GetWitnessByBatch(tx kv.Tx, ctx context.Context, batchNum ui } if badBatch { // we need the header of the block prior to this batch to build up the blocks - previousHeight, err := reader.GetHighestBlockInBatch(batchNum - 1) + previousHeight, _, err := reader.GetHighestBlockInBatch(batchNum - 1) if err != nil { return nil, err }