From 25b336dcac8a22584eeee3f0cec3e4cfd4b22cc9 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 6 Sep 2021 16:35:52 +0200 Subject: [PATCH 01/43] consensus/clique: prevent 0 len extradata from panicing --- consensus/clique/clique.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 449095e7230f..50991b95ee83 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -395,8 +395,11 @@ func (c *Clique) snapshot(chain consensus.ChainHeaderReader, number uint64, hash checkpoint := chain.GetHeaderByNumber(number) if checkpoint != nil { hash := checkpoint.Hash() - - signers := make([]common.Address, (len(checkpoint.Extra)-extraVanity-extraSeal)/common.AddressLength) + signersLen := (len(checkpoint.Extra) - extraVanity - extraSeal) / common.AddressLength + if signersLen < 0 { + signersLen = 0 + } + signers := make([]common.Address, signersLen) for i := 0; i < len(signers); i++ { copy(signers[i][:], checkpoint.Extra[extraVanity+i*common.AddressLength:]) } From 50a145cd4f8d9975db2d85360fc7da31058d5a9c Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 19 Jul 2021 15:24:05 +0800 Subject: [PATCH 02/43] all: work for eth1/2 transtition --- accounts/abi/bind/backends/simulated.go | 2 +- cmd/geth/config.go | 13 +- cmd/utils/flags.go | 17 +- consensus/beacon/consensus.go | 414 ++++++++++++ consensus/clique/clique_test.go | 6 +- consensus/clique/snapshot_test.go | 2 +- consensus/ethash/consensus.go | 6 - core/bench_test.go | 4 +- core/block_validator_test.go | 175 ++++- core/blockchain.go | 857 ++++++++++-------------- core/blockchain_reader.go | 399 +++++++++++ core/blockchain_repair_test.go | 6 +- core/blockchain_sethead_test.go | 2 +- core/blockchain_snapshot_test.go | 24 +- core/blockchain_test.go | 494 ++++++++++++-- core/chain_makers.go | 6 + core/chain_makers_test.go | 2 +- core/dao_test.go | 12 +- core/forkchoice.go | 123 ++++ core/genesis_test.go | 2 +- core/headerchain.go | 251 ++++--- core/headerchain_test.go | 25 +- core/merger.go | 134 ++++ core/rawdb/accessors_metadata.go | 13 + core/rawdb/database.go | 2 +- core/rawdb/schema.go | 3 + core/state_processor_test.go | 4 +- eth/api_backend.go | 2 +- eth/backend.go | 23 +- eth/catalyst/api.go | 182 +++-- eth/catalyst/api_test.go | 230 +++++-- eth/catalyst/api_types.go | 12 +- eth/catalyst/gen_blockparams.go | 16 +- eth/catalyst/gen_ed.go | 34 +- eth/catalyst/sync.go | 74 ++ eth/downloader/downloader.go | 3 + eth/ethconfig/config.go | 55 +- eth/gasprice/gasprice_test.go | 2 +- eth/handler.go | 67 +- eth/handler_eth.go | 12 + eth/handler_eth_test.go | 6 +- eth/handler_test.go | 3 +- eth/protocols/eth/handler_test.go | 2 +- eth/state_accessor.go | 6 +- eth/sync.go | 5 +- eth/tracers/api_test.go | 2 +- les/client.go | 8 +- les/client_handler.go | 14 +- les/fetcher.go | 24 +- les/test_helper.go | 3 +- light/lightchain.go | 53 +- light/lightchain_test.go | 6 +- light/odr_test.go | 4 +- light/trie_test.go | 2 +- light/txpool_test.go | 4 +- miner/miner.go | 4 +- miner/miner_test.go | 4 +- miner/stress/beacon/main.go | 489 ++++++++++++++ miner/worker.go | 12 +- miner/worker_test.go | 6 +- params/config.go | 3 +- tests/block_test_util.go | 2 +- tests/fuzzers/les/les-fuzzer.go | 2 +- 63 files changed, 3356 insertions(+), 1018 deletions(-) create mode 100644 consensus/beacon/consensus.go create mode 100644 core/blockchain_reader.go create mode 100644 core/forkchoice.go create mode 100644 core/merger.go create mode 100644 eth/catalyst/sync.go create mode 100644 miner/stress/beacon/main.go diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index e410522ac8bf..01e564aa24d3 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -78,7 +78,7 @@ type SimulatedBackend struct { func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend { genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc} genesis.MustCommit(database) - blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil, core.NewMerger(database)) backend := &SimulatedBackend{ database: database, diff --git a/cmd/geth/config.go b/cmd/geth/config.go index c97a64f1736c..5e48c0ea6df1 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -32,7 +32,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/scwallet" "github.com/ethereum/go-ethereum/accounts/usbwallet" "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" @@ -159,17 +158,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { if ctx.GlobalIsSet(utils.OverrideLondonFlag.Name) { cfg.Eth.OverrideLondon = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideLondonFlag.Name)) } - backend, eth := utils.RegisterEthService(stack, &cfg.Eth) - - // Configure catalyst. - if ctx.GlobalBool(utils.CatalystFlag.Name) { - if eth == nil { - utils.Fatalf("Catalyst does not work in light client mode.") - } - if err := catalyst.Register(stack, eth); err != nil { - utils.Fatalf("%v", err) - } - } + backend, _ := utils.RegisterEthService(stack, &cfg.Eth, ctx.GlobalBool(utils.CatalystFlag.Name)) // Configure GraphQL if requested if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 52c15c68ec32..0ebb09fb73fc 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -45,6 +45,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/gasprice" @@ -1177,7 +1178,7 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { cfg.NetRestrict = list } - if ctx.GlobalBool(DeveloperFlag.Name) || ctx.GlobalBool(CatalystFlag.Name) { + if ctx.GlobalBool(DeveloperFlag.Name) { // --dev mode can't use p2p networking. cfg.MaxPeers = 0 cfg.ListenAddr = "" @@ -1675,13 +1676,18 @@ func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) { // RegisterEthService adds an Ethereum client to the stack. // The second return value is the full node instance, which may be nil if the // node is running as a light client. -func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend, *eth.Ethereum) { +func RegisterEthService(stack *node.Node, cfg *ethconfig.Config, isCatalyst bool) (ethapi.Backend, *eth.Ethereum) { if cfg.SyncMode == downloader.LightSync { backend, err := les.New(stack, cfg) if err != nil { Fatalf("Failed to register the Ethereum service: %v", err) } stack.RegisterAPIs(tracers.APIs(backend.ApiBackend)) + if isCatalyst { + if err := catalyst.RegisterLight(stack, backend); err != nil { + Fatalf("Failed to register the catalyst service: %v", err) + } + } return backend.ApiBackend, nil } backend, err := eth.New(stack, cfg) @@ -1694,6 +1700,11 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend Fatalf("Failed to create the LES server: %v", err) } } + if isCatalyst { + if err := catalyst.Register(stack, backend); err != nil { + Fatalf("Failed to register the catalyst service: %v", err) + } + } stack.RegisterAPIs(tracers.APIs(backend.APIBackend)) return backend.APIBackend, backend } @@ -1883,7 +1894,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai // TODO(rjl493456442) disable snapshot generation/wiping if the chain is read only. // Disable transaction indexing/unindexing by default. - chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, nil) + chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, nil, core.NewMerger(chainDb)) if err != nil { Fatalf("Can't create BlockChain: %v", err) } diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go new file mode 100644 index 000000000000..878cb60fa2c2 --- /dev/null +++ b/consensus/beacon/consensus.go @@ -0,0 +1,414 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package beacon + +import ( + "errors" + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/trie" +) + +// Proof-of-stake protocol constants. +var ( + beaconDifficulty = common.Big1 // The default block difficulty in the beacon consensus + beaconNonce = types.EncodeNonce(0) // The default block nonce in the beacon consensus +) + +// Various error messages to mark blocks invalid. These should be private to +// prevent engine specific errors from being referenced in the remainder of the +// codebase, inherently breaking if the engine is swapped out. Please put common +// error types into the consensus package. +var ( + errTooManyUncles = errors.New("too many uncles") + errInvalidDifficulty = errors.New("invalid difficulty") + errInvalidMixDigest = errors.New("invalid mix digest") + errInvalidNonce = errors.New("invalid nonce") + errInvalidUncleHash = errors.New("invalid uncle hash") +) + +// Beacon is a consensus engine combines the ethereum 1 consensus and proof-of-stake +// algorithm. There is a special flag inside to decide whether to use legacy consensus +// rules or new rules. The transition rule is described in the eth1/2 merge spec. +// https://hackmd.io/@n0ble/ethereum_consensus_upgrade_mainnet_perspective#Transition-process +// +// The beacon here is a half-functional consensus engine with partial functions which +// is only used for necessary consensus checks. The legacy consensus engine can be any +// engine implements the consensus interface(except the beacon itself). +type Beacon struct { + ethone consensus.Engine // Classic consensus engine used in the eth1, ethash or clique + + // transitioned is the flag whether the transition has been triggered. + // It's triggered by receiving the first "NewHead" message from the + // external consensus engine. + transitioned bool + lock sync.RWMutex +} + +// New creates a consensus engine with the given embedded ethereum 1 engine. +func New(ethone consensus.Engine, transitioned bool) *Beacon { + if _, ok := ethone.(*Beacon); ok { + panic("nested consensus engine") + } + return &Beacon{ + ethone: ethone, + transitioned: transitioned, + } +} + +// Author implements consensus.Engine, returning the header's coinbase as the +// verified author of the block. +func (beacon *Beacon) Author(header *types.Header) (common.Address, error) { + if !beacon.IsPostMergeHeader(header) { + return beacon.ethone.Author(header) + } + return header.Coinbase, nil +} + +// VerifyHeader checks whether a header conforms to the consensus rules of the +// stock Ethereum consensus engine. +func (beacon *Beacon) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error { + if !beacon.IsPostMergeHeader(header) { + return beacon.ethone.VerifyHeader(chain, header, seal) + } + // Short circuit if the header is known, or its parent not + number := header.Number.Uint64() + if chain.GetHeader(header.Hash(), number) != nil { + return nil + } + parent := chain.GetHeader(header.ParentHash, number-1) + if parent == nil { + return consensus.ErrUnknownAncestor + } + // Sanity checks passed, do a proper verification + return beacon.verifyHeader(chain, header, parent) +} + +// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers +// concurrently. The method returns a quit channel to abort the operations and +// a results channel to retrieve the async verifications. +func (beacon *Beacon) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { + if !beacon.IsPostMergeHeader(headers[len(headers)-1]) { + return beacon.ethone.VerifyHeaders(chain, headers, seals) + } + var ( + preHeaders []*types.Header + postHeaders []*types.Header + preSeals []bool + ) + for index, header := range headers { + if beacon.IsPostMergeHeader(header) { + preHeaders = headers[:index] + postHeaders = headers[index:] + preSeals = seals[:index] + break + } + } + // All the headers have passed the transition point, use new rules. + if len(preHeaders) == 0 { + return beacon.verifyHeaders(chain, headers, nil) + } + // The transition point exists in the middle, separate the headers + // into two batches and apply different verification rules for them. + var ( + abort = make(chan struct{}) + results = make(chan error, len(headers)) + ) + go func() { + var ( + old, new, out = 0, len(preHeaders), 0 + errors = make([]error, len(headers)) + done = make([]bool, len(headers)) + oldDone, oldResult = beacon.ethone.VerifyHeaders(chain, preHeaders, preSeals) + newDone, newResult = beacon.verifyHeaders(chain, postHeaders, preHeaders[len(preHeaders)-1]) + ) + for { + for ; done[out]; out++ { + results <- errors[out] + if out == len(headers)-1 { + return + } + } + select { + case err := <-oldResult: + errors[old], done[old] = err, true + old++ + case err := <-newResult: + errors[new], done[new] = err, true + new++ + case <-abort: + close(oldDone) + close(newDone) + return + } + } + }() + return abort, results +} + +// VerifyUncles verifies that the given block's uncles conform to the consensus +// rules of the stock Ethereum consensus engine. +func (beacon *Beacon) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { + if !beacon.IsPostMergeHeader(block.Header()) { + return beacon.ethone.VerifyUncles(chain, block) + } + // Verify that there is no uncle block. It's explicitly disabled in the beacon + if len(block.Uncles()) > 0 { + return errTooManyUncles + } + return nil +} + +// verifyHeader checks whether a header conforms to the consensus rules of the +// stock Ethereum consensus engine. The difference between the beacon and ethash is +// (a) the difficulty, mixhash, nonce, extradata and unclehash are expected +// to be the desired constants +// (b) the timestamp is not verified anymore +func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header) error { + // Ensure that the header's extra-data section is of a reasonable size + if len(header.Extra) != 0 { + return fmt.Errorf("non-empty extra-data(%d)", len(header.Extra)) + } + // Verify the block's difficulty to ensure it's the default constant + if beaconDifficulty.Cmp(header.Difficulty) != 0 { + return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, beaconDifficulty) + } + // Verify that the gas limit is <= 2^63-1 + cap := uint64(0x7fffffffffffffff) + if header.GasLimit > cap { + return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, cap) + } + // Verify that the gasUsed is <= gasLimit + if header.GasUsed > header.GasLimit { + return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) + } + // Verify that the gas limit remains within allowed bounds + diff := int64(parent.GasLimit) - int64(header.GasLimit) + if diff < 0 { + diff *= -1 + } + limit := parent.GasLimit / params.GasLimitBoundDivisor + if uint64(diff) >= limit || header.GasLimit < params.MinGasLimit { + return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit) + } + // Verify that the block number is parent's +1 + if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 { + return consensus.ErrInvalidNumber + } + // Verify the seal parts. Ensure the mixhash, nonce and uncle hash are the expected value. + if header.MixDigest != (common.Hash{}) { + return errInvalidMixDigest + } + if header.Nonce != beaconNonce { + return errInvalidNonce + } + if header.UncleHash != types.EmptyUncleHash { + return errInvalidUncleHash + } + return nil +} + +// verifyHeaders is similar to verifyHeader, but verifies a batch of headers +// concurrently. The method returns a quit channel to abort the operations and +// a results channel to retrieve the async verifications. An additional parent +// header will be passed if the relevant header is not in the database yet. +func (beacon *Beacon) verifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, ancestor *types.Header) (chan<- struct{}, <-chan error) { + var ( + abort = make(chan struct{}) + results = make(chan error, len(headers)) + ) + go func() { + for i, header := range headers { + var parent *types.Header + if i == 0 { + if ancestor != nil { + parent = ancestor + } else { + parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1) + } + } else if headers[i-1].Hash() == headers[i].ParentHash { + parent = headers[i-1] + } + if parent == nil { + select { + case <-abort: + return + case results <- consensus.ErrUnknownAncestor: + } + continue + } + err := beacon.verifyHeader(chain, header, parent) + select { + case <-abort: + return + case results <- err: + } + } + }() + return abort, results +} + +// Prepare implements consensus.Engine, initializing the difficulty field of a +// header to conform to the beacon protocol. The changes are done inline. +func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { + beacon.lock.RLock() + defer beacon.lock.RUnlock() + + // Transition isn't triggered yet, use the legacy rules for preparation. + if !beacon.transitioned { + return beacon.ethone.Prepare(chain, header) + } + parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) + if parent == nil { + return consensus.ErrUnknownAncestor + } + header.Difficulty = beaconDifficulty + return nil +} + +// Finalize implements consensus.Engine, setting the final state on the header +func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { + beacon.lock.RLock() + defer beacon.lock.RUnlock() + + // Finalize is different with Prepare, it can be used in both block generation + // and verification. So determine the consensus rules by header type. + if !beacon.IsPostMergeHeader(header) { + beacon.ethone.Finalize(chain, header, state, txs, uncles) + return + } + // The block reward is no longer handled here. It's done by the + // external consensus engine. + header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) +} + +// FinalizeAndAssemble implements consensus.Engine, setting the final state and +// assembling the block. +func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { + beacon.lock.RLock() + defer beacon.lock.RUnlock() + + // FinalizeAndAssemble is different with Prepare, it can be used in both block + // generation and verification. So determine the consensus rules by header type. + if !beacon.IsPostMergeHeader(header) { + return beacon.ethone.FinalizeAndAssemble(chain, header, state, txs, uncles, receipts) + } + // Finalize and assemble the block + beacon.Finalize(chain, header, state, txs, uncles) + return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil +} + +// Seal generates a new sealing request for the given input block and pushes +// the result into the given channel. +// +// Note, the method returns immediately and will send the result async. More +// than one result may also be returned depending on the consensus algorithm. +func (beacon *Beacon) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { + beacon.lock.RLock() + defer beacon.lock.RUnlock() + + if !beacon.IsPostMergeHeader(block.Header()) { + return beacon.ethone.Seal(chain, block, results, stop) + } + // The seal verification is done by the external consensus engine, + // return directly without pushing any block back. In another word + // beacon won't return any result by `results` channel which may + // blocks the receiver logic forever. + return nil +} + +// SealHash returns the hash of a block prior to it being sealed. +func (beacon *Beacon) SealHash(header *types.Header) common.Hash { + return beacon.ethone.SealHash(header) +} + +// CalcDifficulty is the difficulty adjustment algorithm. It returns +// the difficulty that a new block should have when created at time +// given the parent block's time and difficulty. +func (beacon *Beacon) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { + beacon.lock.RLock() + defer beacon.lock.RUnlock() + + // Transition isn't triggered yet, use the legacy rules for calculation + if !beacon.transitioned { + return beacon.ethone.CalcDifficulty(chain, time, parent) + } + return beaconDifficulty +} + +// APIs implements consensus.Engine, returning the user facing RPC APIs. +func (beacon *Beacon) APIs(chain consensus.ChainHeaderReader) []rpc.API { + return beacon.ethone.APIs(chain) +} + +// Close shutdowns the consensus engine +func (beacon *Beacon) Close() error { + return beacon.ethone.Close() +} + +// IsPostMergeHeader reports the header belongs to the PoS-stage with some special fields. +// This function is not suitable for a part of APIs like Prepare or CalcDifficulty because +// the header difficulty is not set yet. +func (beacon *Beacon) IsPostMergeHeader(header *types.Header) bool { + // These fields can be used to filter out ethash block + if header.Difficulty.Cmp(beaconDifficulty) != 0 { + return false + } + if header.MixDigest != (common.Hash{}) { + return false + } + if header.Nonce != beaconNonce { + return false + } + // Extra field can be used to filter out clique block + if len(header.Extra) != 0 { + return false + } + return true +} + +// MarkTransitioned sets the transitioned flag. +func (beacon *Beacon) MarkTransitioned() { + beacon.lock.Lock() + defer beacon.lock.Unlock() + + beacon.transitioned = true +} + +// InnerEngine returns the embedded eth1 consensus engine. +func (beacon *Beacon) InnerEngine() consensus.Engine { + return beacon.ethone +} + +// SetThreads updates the mining threads. Delegate the call +// to the eth1 engine if it's threaded. +func (beacon *Beacon) SetThreads(threads int) { + type threaded interface { + SetThreads(threads int) + } + if th, ok := beacon.ethone.(threaded); ok { + th.SetThreads(threads) + } +} diff --git a/consensus/clique/clique_test.go b/consensus/clique/clique_test.go index 1bd32acd3746..354da78a87dd 100644 --- a/consensus/clique/clique_test.go +++ b/consensus/clique/clique_test.go @@ -55,7 +55,7 @@ func TestReimportMirroredState(t *testing.T) { genesis := genspec.MustCommit(db) // Generate a batch of blocks, each properly signed - chain, _ := core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil) + chain, _ := core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) defer chain.Stop() blocks, _ := core.GenerateChain(params.AllCliqueProtocolChanges, genesis, engine, db, 3, func(i int, block *core.BlockGen) { @@ -89,7 +89,7 @@ func TestReimportMirroredState(t *testing.T) { db = rawdb.NewMemoryDatabase() genspec.MustCommit(db) - chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil) + chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) defer chain.Stop() if _, err := chain.InsertChain(blocks[:2]); err != nil { @@ -102,7 +102,7 @@ func TestReimportMirroredState(t *testing.T) { // Simulate a crash by creating a new chain on top of the database, without // flushing the dirty states out. Insert the last block, triggering a sidechain // reimport. - chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil) + chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) defer chain.Stop() if _, err := chain.InsertChain(blocks[2:]); err != nil { diff --git a/consensus/clique/snapshot_test.go b/consensus/clique/snapshot_test.go index 094868ca744d..1168c434439c 100644 --- a/consensus/clique/snapshot_test.go +++ b/consensus/clique/snapshot_test.go @@ -450,7 +450,7 @@ func TestClique(t *testing.T) { batches[len(batches)-1] = append(batches[len(batches)-1], block) } // Pass all the headers through clique and ensure tallying succeeds - chain, err := core.NewBlockChain(db, nil, &config, engine, vm.Config{}, nil, nil) + chain, err := core.NewBlockChain(db, nil, &config, engine, vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Errorf("test %d: failed to create test chain: %v", i, err) continue diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 5743387f6c7f..6ad9fc22be03 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -330,8 +330,6 @@ func (ethash *Ethash) CalcDifficulty(chain consensus.ChainHeaderReader, time uin func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int { next := new(big.Int).Add(parent.Number, big1) switch { - case config.IsCatalyst(next): - return big.NewInt(1) case config.IsLondon(next): return calcDifficultyEip3554(time, parent) case config.IsMuirGlacier(next): @@ -639,10 +637,6 @@ var ( // reward. The total reward consists of the static block reward and rewards for // included uncles. The coinbase of each uncle block is also rewarded. func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) { - // Skip block reward in catalyst mode - if config.IsCatalyst(header.Number) { - return - } // Select the correct block reward based on chain progression blockReward := FrontierBlockReward if config.IsByzantium(header.Number) { diff --git a/core/bench_test.go b/core/bench_test.go index ce288d372e08..51cefaa68489 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -175,7 +175,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // Time the insertion of the new chain. // State and blocks are stored in the same DB. - chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer chainman.Stop() b.ReportAllocs() b.ResetTimer() @@ -287,7 +287,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) { if err != nil { b.Fatalf("error opening database at %v: %v", dir, err) } - chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { b.Fatalf("error creating chain: %v", err) } diff --git a/core/block_validator_test.go b/core/block_validator_test.go index 86f9835a0150..80168f6c0fe2 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -17,14 +17,21 @@ package core import ( + "encoding/json" + "math/big" "runtime" "testing" "time" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/beacon" + "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" ) @@ -42,7 +49,7 @@ func TestHeaderVerification(t *testing.T) { headers[i] = block.Header() } // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces - chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer chain.Stop() for i := 0; i < len(blocks); i++ { @@ -76,6 +83,166 @@ func TestHeaderVerification(t *testing.T) { } } +func TestHeaderVerificationForMergingClique(t *testing.T) { testHeaderVerificationForMerging(t, true) } +func TestHeaderVerificationForMergingEthash(t *testing.T) { testHeaderVerificationForMerging(t, false) } + +// Tests the verification for eth1/2 merging, including pre-merge and post-merge +func testHeaderVerificationForMerging(t *testing.T, isClique bool) { + var ( + testdb = rawdb.NewMemoryDatabase() + preBlocks []*types.Block + postBlocks []*types.Block + runEngine consensus.Engine + chainConfig *params.ChainConfig + ) + if isClique { + var ( + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr = crypto.PubkeyToAddress(key.PublicKey) + engine = clique.New(params.AllCliqueProtocolChanges.Clique, testdb) + ) + genspec := &Genesis{ + ExtraData: make([]byte, 32+common.AddressLength+crypto.SignatureLength), + Alloc: map[common.Address]GenesisAccount{ + addr: {Balance: big.NewInt(1)}, + }, + BaseFee: big.NewInt(params.InitialBaseFee), + } + copy(genspec.ExtraData[32:], addr[:]) + genesis := genspec.MustCommit(testdb) + + genEngine := beacon.New(engine, false) + preBlocks, _ = GenerateChain(params.AllCliqueProtocolChanges, genesis, genEngine, testdb, 8, nil) + for i, block := range preBlocks { + header := block.Header() + if i > 0 { + header.ParentHash = preBlocks[i-1].Hash() + } + header.Extra = make([]byte, 32+crypto.SignatureLength) + header.Difficulty = big.NewInt(2) + + sig, _ := crypto.Sign(genEngine.SealHash(header).Bytes(), key) + copy(header.Extra[len(header.Extra)-crypto.SignatureLength:], sig) + preBlocks[i] = block.WithSeal(header) + } + genEngine.MarkTransitioned() + postBlocks, _ = GenerateChain(params.AllCliqueProtocolChanges, preBlocks[len(preBlocks)-1], genEngine, testdb, 8, nil) + chainConfig = params.AllCliqueProtocolChanges + runEngine = beacon.New(engine, false) + } else { + gspec := &Genesis{Config: params.TestChainConfig} + genesis := gspec.MustCommit(testdb) + engine := beacon.New(ethash.NewFaker(), false) + + preBlocks, _ = GenerateChain(params.TestChainConfig, genesis, engine, testdb, 8, nil) + engine.MarkTransitioned() + postBlocks, _ = GenerateChain(params.TestChainConfig, preBlocks[len(preBlocks)-1], engine, testdb, 8, nil) + + chainConfig = params.TestChainConfig + runEngine = beacon.New(ethash.NewFaker(), false) + } + + preHeaders := make([]*types.Header, len(preBlocks)) + for i, block := range preBlocks { + preHeaders[i] = block.Header() + + blob, _ := json.Marshal(block.Header()) + t.Logf("Log header before the merging %d: %v", block.NumberU64(), string(blob)) + } + postHeaders := make([]*types.Header, len(postBlocks)) + for i, block := range postBlocks { + postHeaders[i] = block.Header() + + blob, _ := json.Marshal(block.Header()) + t.Logf("Log header after the merging %d: %v", block.NumberU64(), string(blob)) + } + + // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces + merger := NewMerger(rawdb.NewMemoryDatabase()) + merger.SubscribeLeavePoW(func() { + runEngine.(*beacon.Beacon).MarkTransitioned() + }) + chain, _ := NewBlockChain(testdb, nil, chainConfig, runEngine, vm.Config{}, nil, nil, merger) + defer chain.Stop() + + // Verify the blocks before the merging + for i := 0; i < len(preBlocks); i++ { + _, results := runEngine.VerifyHeaders(chain, []*types.Header{preHeaders[i]}, []bool{true}) + // Wait for the verification result + select { + case result := <-results: + if result != nil { + t.Errorf("test %d: verification failed %v", i, result) + } + case <-time.After(time.Second): + t.Fatalf("test %d: verification timeout", i) + } + // Make sure no more data is returned + select { + case result := <-results: + t.Fatalf("test %d: unexpected result returned: %v", i, result) + case <-time.After(25 * time.Millisecond): + } + chain.InsertChain(preBlocks[i : i+1]) + } + + // Make the transition + merger.LeavePoW() + merger.EnterPoS() + + // Verify the blocks after the merging + for i := 0; i < len(postBlocks); i++ { + _, results := runEngine.VerifyHeaders(chain, []*types.Header{postHeaders[i]}, []bool{true}) + // Wait for the verification result + select { + case result := <-results: + if result != nil { + t.Errorf("test %d: verification failed %v", i, result) + } + case <-time.After(time.Second): + t.Fatalf("test %d: verification timeout", i) + } + // Make sure no more data is returned + select { + case result := <-results: + t.Fatalf("test %d: unexpected result returned: %v", i, result) + case <-time.After(25 * time.Millisecond): + } + chain.InsertChain(postBlocks[i : i+1]) + } + + // Verify the blocks with pre-merge blocks and post-merge blocks + var ( + headers []*types.Header + seals []bool + ) + for _, block := range preBlocks { + headers = append(headers, block.Header()) + seals = append(seals, true) + } + for _, block := range postBlocks { + headers = append(headers, block.Header()) + seals = append(seals, true) + } + _, results := runEngine.VerifyHeaders(chain, headers, seals) + for i := 0; i < len(headers); i++ { + select { + case result := <-results: + if result != nil { + t.Errorf("test %d: verification failed %v", i, result) + } + case <-time.After(time.Second): + t.Fatalf("test %d: verification timeout", i) + } + } + // Make sure no more data is returned + select { + case result := <-results: + t.Fatalf("unexpected result returned: %v", result) + case <-time.After(25 * time.Millisecond): + } +} + // Tests that concurrent header verification works, for both good and bad blocks. func TestHeaderConcurrentVerification2(t *testing.T) { testHeaderConcurrentVerification(t, 2) } func TestHeaderConcurrentVerification8(t *testing.T) { testHeaderConcurrentVerification(t, 8) } @@ -106,11 +273,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) { var results <-chan error if valid { - chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) _, results = chain.engine.VerifyHeaders(chain, headers, seals) chain.Stop() } else { - chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil) + chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) _, results = chain.engine.VerifyHeaders(chain, headers, seals) chain.Stop() } @@ -173,7 +340,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) { defer runtime.GOMAXPROCS(old) // Start the verifications and immediately abort - chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil) + chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer chain.Stop() abort, results := chain.engine.VerifyHeaders(chain, headers, seals) diff --git a/core/blockchain.go b/core/blockchain.go index 66bc395c9dc4..7868dbe8a1ca 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -22,7 +22,6 @@ import ( "fmt" "io" "math/big" - mrand "math/rand" "sort" "sync" "sync/atomic" @@ -42,7 +41,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" lru "github.com/hashicorp/golang-lru" ) @@ -205,15 +203,17 @@ type BlockChain struct { validator Validator // Block and state validator interface prefetcher Prefetcher processor Processor // Block transaction processor interface + forker *ForkChoice vmConfig vm.Config - shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block. + shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block. + terminateInsert func(common.Hash, uint64) bool // Testing hook used to terminate ancient receipt chain insertion. } // NewBlockChain returns a fully initialised block chain using information -// available in the database. It initialises the default Ethereum Validator and -// Processor. -func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool, txLookupLimit *uint64) (*BlockChain, error) { +// available in the database. It initialises the default Ethereum Validator +// and Processor. +func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(header *types.Header) bool, txLookupLimit *uint64, merger *Merger) (*BlockChain, error) { if cacheConfig == nil { cacheConfig = defaultCacheConfig } @@ -234,17 +234,20 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par Journal: cacheConfig.TrieCleanJournal, Preimages: cacheConfig.Preimages, }), - quit: make(chan struct{}), - shouldPreserve: shouldPreserve, - bodyCache: bodyCache, - bodyRLPCache: bodyRLPCache, - receiptsCache: receiptsCache, - blockCache: blockCache, - txLookupCache: txLookupCache, - futureBlocks: futureBlocks, - engine: engine, - vmConfig: vmConfig, - } + quit: make(chan struct{}), + bodyCache: bodyCache, + bodyRLPCache: bodyRLPCache, + receiptsCache: receiptsCache, + blockCache: blockCache, + txLookupCache: txLookupCache, + futureBlocks: futureBlocks, + engine: engine, + vmConfig: vmConfig, + } + bc.forker = NewForkChoice(bc, merger.LeftPoW(), shouldPreserve) + merger.SubscribeLeavePoW(func() { + bc.forker.MarkTransitioned() + }) bc.validator = NewBlockValidator(chainConfig, bc, engine) bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine) bc.processor = NewStateProcessor(chainConfig, bc, engine) @@ -396,11 +399,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par return bc, nil } -// GetVMConfig returns the block chain VM config. -func (bc *BlockChain) GetVMConfig() *vm.Config { - return &bc.vmConfig -} - // empty returns an indicator whether the blockchain is empty. // Note, it's a special case that we connect a non-empty ancient // database with an empty node, so that we can plugin the ancient @@ -648,53 +646,6 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error { return nil } -// GasLimit returns the gas limit of the current HEAD block. -func (bc *BlockChain) GasLimit() uint64 { - return bc.CurrentBlock().GasLimit() -} - -// CurrentBlock retrieves the current head block of the canonical chain. The -// block is retrieved from the blockchain's internal cache. -func (bc *BlockChain) CurrentBlock() *types.Block { - return bc.currentBlock.Load().(*types.Block) -} - -// Snapshots returns the blockchain snapshot tree. -func (bc *BlockChain) Snapshots() *snapshot.Tree { - return bc.snaps -} - -// CurrentFastBlock retrieves the current fast-sync head block of the canonical -// chain. The block is retrieved from the blockchain's internal cache. -func (bc *BlockChain) CurrentFastBlock() *types.Block { - return bc.currentFastBlock.Load().(*types.Block) -} - -// Validator returns the current validator. -func (bc *BlockChain) Validator() Validator { - return bc.validator -} - -// Processor returns the current processor. -func (bc *BlockChain) Processor() Processor { - return bc.processor -} - -// State returns a new mutable state based on the current HEAD block. -func (bc *BlockChain) State() (*state.StateDB, error) { - return bc.StateAt(bc.CurrentBlock().Root()) -} - -// StateAt returns a new mutable state based on a particular point in time. -func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { - return state.New(root, bc.stateCache, bc.snaps) -} - -// StateCache returns the caching database underpinning the blockchain instance. -func (bc *BlockChain) StateCache() state.Database { - return bc.stateCache -} - // Reset purges the entire blockchain, restoring it to its genesis state. func (bc *BlockChain) Reset() error { return bc.ResetWithGenesisBlock(bc.genesisBlock) @@ -779,6 +730,8 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block) { rawdb.WriteHeadBlockHash(batch, block.Hash()) // If the block is better than our head or is on a different chain, force update heads + // TODO(rjl493456442) What if the (header head/fast block head) is lower than the chain + // head? Is it possible in practise? if updateHeads { rawdb.WriteHeadHeaderHash(batch, block.Hash()) rawdb.WriteHeadFastBlockHash(batch, block.Hash()) @@ -797,194 +750,6 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block) { headBlockGauge.Update(int64(block.NumberU64())) } -// Genesis retrieves the chain's genesis block. -func (bc *BlockChain) Genesis() *types.Block { - return bc.genesisBlock -} - -// GetBody retrieves a block body (transactions and uncles) from the database by -// hash, caching it if found. -func (bc *BlockChain) GetBody(hash common.Hash) *types.Body { - // Short circuit if the body's already in the cache, retrieve otherwise - if cached, ok := bc.bodyCache.Get(hash); ok { - body := cached.(*types.Body) - return body - } - number := bc.hc.GetBlockNumber(hash) - if number == nil { - return nil - } - body := rawdb.ReadBody(bc.db, hash, *number) - if body == nil { - return nil - } - // Cache the found body for next time and return - bc.bodyCache.Add(hash, body) - return body -} - -// GetBodyRLP retrieves a block body in RLP encoding from the database by hash, -// caching it if found. -func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue { - // Short circuit if the body's already in the cache, retrieve otherwise - if cached, ok := bc.bodyRLPCache.Get(hash); ok { - return cached.(rlp.RawValue) - } - number := bc.hc.GetBlockNumber(hash) - if number == nil { - return nil - } - body := rawdb.ReadBodyRLP(bc.db, hash, *number) - if len(body) == 0 { - return nil - } - // Cache the found body for next time and return - bc.bodyRLPCache.Add(hash, body) - return body -} - -// HasBlock checks if a block is fully present in the database or not. -func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool { - if bc.blockCache.Contains(hash) { - return true - } - return rawdb.HasBody(bc.db, hash, number) -} - -// HasFastBlock checks if a fast block is fully present in the database or not. -func (bc *BlockChain) HasFastBlock(hash common.Hash, number uint64) bool { - if !bc.HasBlock(hash, number) { - return false - } - if bc.receiptsCache.Contains(hash) { - return true - } - return rawdb.HasReceipts(bc.db, hash, number) -} - -// HasState checks if state trie is fully present in the database or not. -func (bc *BlockChain) HasState(hash common.Hash) bool { - _, err := bc.stateCache.OpenTrie(hash) - return err == nil -} - -// HasBlockAndState checks if a block and associated state trie is fully present -// in the database or not, caching it if present. -func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool { - // Check first that the block itself is known - block := bc.GetBlock(hash, number) - if block == nil { - return false - } - return bc.HasState(block.Root()) -} - -// GetBlock retrieves a block from the database by hash and number, -// caching it if found. -func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { - // Short circuit if the block's already in the cache, retrieve otherwise - if block, ok := bc.blockCache.Get(hash); ok { - return block.(*types.Block) - } - block := rawdb.ReadBlock(bc.db, hash, number) - if block == nil { - return nil - } - // Cache the found block for next time and return - bc.blockCache.Add(block.Hash(), block) - return block -} - -// GetBlockByHash retrieves a block from the database by hash, caching it if found. -func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block { - number := bc.hc.GetBlockNumber(hash) - if number == nil { - return nil - } - return bc.GetBlock(hash, *number) -} - -// GetBlockByNumber retrieves a block from the database by number, caching it -// (associated with its hash) if found. -func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block { - hash := rawdb.ReadCanonicalHash(bc.db, number) - if hash == (common.Hash{}) { - return nil - } - return bc.GetBlock(hash, number) -} - -// GetReceiptsByHash retrieves the receipts for all transactions in a given block. -func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts { - if receipts, ok := bc.receiptsCache.Get(hash); ok { - return receipts.(types.Receipts) - } - number := rawdb.ReadHeaderNumber(bc.db, hash) - if number == nil { - return nil - } - receipts := rawdb.ReadReceipts(bc.db, hash, *number, bc.chainConfig) - if receipts == nil { - return nil - } - bc.receiptsCache.Add(hash, receipts) - return receipts -} - -// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors. -// [deprecated by eth/62] -func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) { - number := bc.hc.GetBlockNumber(hash) - if number == nil { - return nil - } - for i := 0; i < n; i++ { - block := bc.GetBlock(hash, *number) - if block == nil { - break - } - blocks = append(blocks, block) - hash = block.ParentHash() - *number-- - } - return -} - -// GetUnclesInChain retrieves all the uncles from a given block backwards until -// a specific distance is reached. -func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header { - uncles := []*types.Header{} - for i := 0; block != nil && i < length; i++ { - uncles = append(uncles, block.Uncles()...) - block = bc.GetBlock(block.ParentHash(), block.NumberU64()-1) - } - return uncles -} - -// TrieNode retrieves a blob of data associated with a trie node -// either from ephemeral in-memory cache, or from persistent storage. -func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) { - return bc.stateCache.TrieDB().Node(hash) -} - -// ContractCode retrieves a blob of data associated with a contract hash -// either from ephemeral in-memory cache, or from persistent storage. -func (bc *BlockChain) ContractCode(hash common.Hash) ([]byte, error) { - return bc.stateCache.ContractCode(common.Hash{}, hash) -} - -// ContractCodeWithPrefix retrieves a blob of data associated with a contract -// hash either from ephemeral in-memory cache, or from persistent storage. -// -// If the code doesn't exist in the in-memory cache, check the storage with -// new code scheme. -func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) { - type codeReader interface { - ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error) - } - return bc.stateCache.(codeReader).ContractCodeWithPrefix(common.Hash{}, hash) -} - // Stop stops the blockchain service. If any imports are currently in progress // it will abort them using the procInterrupt. func (bc *BlockChain) Stop() { @@ -1133,13 +898,14 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ // Rewind may have occurred, skip in that case. if bc.CurrentHeader().Number.Cmp(head.Number()) >= 0 { - currentFastBlock, td := bc.CurrentFastBlock(), bc.GetTd(head.Hash(), head.NumberU64()) - if bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64()).Cmp(td) < 0 { - rawdb.WriteHeadFastBlockHash(bc.db, head.Hash()) - bc.currentFastBlock.Store(head) - headFastBlockGauge.Update(int64(head.NumberU64())) - return true + reorg, err := bc.forker.Reorg(bc.CurrentFastBlock().Header(), head.Header()) + if err != nil || !reorg { + return false } + rawdb.WriteHeadFastBlockHash(bc.db, head.Hash()) + bc.currentFastBlock.Store(head) + headFastBlockGauge.Update(int64(head.NumberU64())) + return true } return false } @@ -1354,18 +1120,6 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ return 0, nil } -// SetTxLookupLimit is responsible for updating the txlookup limit to the -// original one stored in db if the new mismatches with the old one. -func (bc *BlockChain) SetTxLookupLimit(limit uint64) { - bc.txLookupLimit = limit -} - -// TxLookupLimit retrieves the txlookup limit used by blockchain to prune -// stale transaction indices. -func (bc *BlockChain) TxLookupLimit() uint64 { - return bc.txLookupLimit -} - var lastWrite uint64 // writeBlockWithoutState writes only the block and its metadata to the database, @@ -1400,28 +1154,18 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error { return nil } -// WriteBlockWithState writes the block and all associated state to the database. -func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { - bc.chainmu.Lock() - defer bc.chainmu.Unlock() - - return bc.writeBlockWithState(block, receipts, logs, state, emitHeadEvent) -} - -// writeBlockWithState writes the block and all associated state to the database, -// but is expects the chain mutex to be held. -func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { +// writeBlockWithState writes block, metadata and corresponding state data to the +// database. +func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB) error { bc.wg.Add(1) defer bc.wg.Done() // Calculate the total difficulty of the block ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1) if ptd == nil { - return NonStatTy, consensus.ErrUnknownAncestor + return consensus.ErrUnknownAncestor } // Make sure no inconsistent state is leaked during insertion - currentBlock := bc.CurrentBlock() - localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) externTd := new(big.Int).Add(block.Difficulty(), ptd) // Irrelevant of the canonical status, write the block itself to the database. @@ -1439,15 +1183,13 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. // Commit all cached state changes into underlying memory database. root, err := state.Commit(bc.chainConfig.IsEIP158(block.Number())) if err != nil { - return NonStatTy, err + return err } triedb := bc.stateCache.TrieDB() // If we're running an archive node, always flush if bc.cacheConfig.TrieDirtyDisabled { - if err := triedb.Commit(root, false, nil); err != nil { - return NonStatTy, err - } + return triedb.Commit(root, false, nil) } else { // Full but not archive node, do proper garbage collection triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive @@ -1495,23 +1237,28 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. } } } - // If the total difficulty is higher than our known, add it to the canonical chain - // Second clause in the if statement reduces the vulnerability to selfish mining. - // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf - reorg := externTd.Cmp(localTd) > 0 - currentBlock = bc.CurrentBlock() - if !reorg && externTd.Cmp(localTd) == 0 { - // Split same-difficulty blocks by number, then preferentially select - // the block generated by the local miner as the canonical block. - if block.NumberU64() < currentBlock.NumberU64() { - reorg = true - } else if block.NumberU64() == currentBlock.NumberU64() { - var currentPreserve, blockPreserve bool - if bc.shouldPreserve != nil { - currentPreserve, blockPreserve = bc.shouldPreserve(currentBlock), bc.shouldPreserve(block) - } - reorg = !currentPreserve && (blockPreserve || mrand.Float64() < 0.5) - } + return nil +} + +// WriteBlockWithState writes the block and all associated state to the database. +func (bc *BlockChain) WriteBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { + bc.chainmu.Lock() + defer bc.chainmu.Unlock() + + return bc.writeBlockAndSetHead(block, receipts, logs, state, emitHeadEvent) +} + +// writeBlockAndSetHead writes the block and all associated state to the database, +// and also it applies the given block as the new chain head. This function expects +// the chain mutex to be held. +func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { + if err := bc.writeBlockWithState(block, receipts, logs, state); err != nil { + return NonStatTy, err + } + currentBlock := bc.CurrentBlock() + reorg, err := bc.forker.Reorg(currentBlock.Header(), block.Header()) + if err != nil { + return NonStatTy, err } if reorg { // Reorganise the chain if the parent is not the head block @@ -1537,7 +1284,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. } // In theory we should fire a ChainHeadEvent when we inject // a canonical block, but sometimes we can insert a batch of - // canonicial blocks. Avoid firing too much ChainHeadEvents, + // canonicial blocks. Avoid firing too many ChainHeadEvents, // we will fire an accumulated ChainHeadEvent and disable fire // event here. if emitHeadEvent { @@ -1552,6 +1299,9 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. // addFutureBlock checks if the block is within the max allowed window to get // accepted for future processing, and returns an error if the block is too far // ahead and was not added. +// +// TODO after the transition, the future block shouldn't be kept. Because +// it's not checked in the Geth side anymore. func (bc *BlockChain) addFutureBlock(block *types.Block) error { max := uint64(time.Now().Unix() + maxTimeFutureBlocks) if block.Time() > max { @@ -1564,15 +1314,12 @@ func (bc *BlockChain) addFutureBlock(block *types.Block) error { // InsertChain attempts to insert the given batch of blocks in to the canonical // chain or, otherwise, create a fork. If an error is returned it will return // the index number of the failing block as well an error describing what went -// wrong. -// -// After insertion is done, all accumulated events will be fired. +// wrong. After insertion is done, all accumulated events will be fired. func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) { // Sanity check that we have something meaningful to import if len(chain) == 0 { return 0, nil } - bc.blockProcFeed.Send(true) defer bc.blockProcFeed.Send(false) @@ -1599,23 +1346,6 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) { n, err := bc.insertChain(chain, true) bc.chainmu.Unlock() bc.wg.Done() - - return n, err -} - -// InsertChainWithoutSealVerification works exactly the same -// except for seal verification, seal verification is omitted -func (bc *BlockChain) InsertChainWithoutSealVerification(block *types.Block) (int, error) { - bc.blockProcFeed.Send(true) - defer bc.blockProcFeed.Send(false) - - // Pre-checks passed, start the full block imports - bc.wg.Add(1) - bc.chainmu.Lock() - n, err := bc.insertChain(types.Blocks([]*types.Block{block}), false) - bc.chainmu.Unlock() - bc.wg.Done() - return n, err } @@ -1629,7 +1359,7 @@ func (bc *BlockChain) InsertChainWithoutSealVerification(block *types.Block) (in // completes, then the historic state could be pruned again func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, error) { // If the chain is terminating, don't even bother starting up - if atomic.LoadInt32(&bc.procInterrupt) == 1 { + if bc.insertStopped() { return 0, nil } // Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss) @@ -1669,14 +1399,23 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er // from the canonical chain, which has not been verified. // Skip all known blocks that are behind us var ( - current = bc.CurrentBlock() - localTd = bc.GetTd(current.Hash(), current.NumberU64()) - externTd = bc.GetTd(block.ParentHash(), block.NumberU64()-1) // The first block can't be nil + reorg bool + current = bc.CurrentBlock() ) for block != nil && err == ErrKnownBlock { - externTd = new(big.Int).Add(externTd, block.Difficulty()) - if localTd.Cmp(externTd) < 0 { - break + reorg, err = bc.forker.Reorg(current.Header(), block.Header()) + if err != nil { + return it.index, err + } + if reorg { + // Switch to import mode if the forker says the reorg is necessary + // and also the block is not on the canonical chain. + // In eth2 the forker always returns True for reorg desision(blindly trust + // the external consensus engine), but in order to prevent the unnecessary + // reorgs when importing known blocks, the special case is handled here. + if bc.GetCanonicalHash(block.NumberU64()) != block.Hash() || block.NumberU64() > current.NumberU64() { + break + } } log.Debug("Ignoring already known block", "number", block.Number(), "hash", block.Hash()) stats.ignored++ @@ -1861,7 +1600,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er // Write the block to the chain and get the status. substart = time.Now() - status, err := bc.writeBlockWithState(block, receipts, logs, statedb, false) + status, err := bc.writeBlockAndSetHead(block, receipts, logs, statedb, false) atomic.StoreUint32(&followupInterrupt, 1) if err != nil { return it.index, err @@ -1933,8 +1672,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er // switch over to the new chain if the TD exceeded the current chain. func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (int, error) { var ( - externTd *big.Int - current = bc.CurrentBlock() + externTd *big.Int + lastBlock = block + current = bc.CurrentBlock() ) // The first sidechain block error is already verified to be ErrPrunedAncestor. // Since we don't import them here, we expect ErrUnknownAncestor for the remaining @@ -1985,6 +1725,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()), "root", block.Root()) } + lastBlock = block } // At this point, we've written all sidechain blocks to database. Loop ended // either on some other error or all were processed. If there was some other @@ -1992,8 +1733,12 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i // // If the externTd was larger than our local TD, we now need to reimport the previous // blocks to regenerate the required state - localTd := bc.GetTd(current.Hash(), current.NumberU64()) - if localTd.Cmp(externTd) > 0 { + reorg, err := bc.forker.Reorg(current.Header(), lastBlock.Header()) + if err != nil { + return it.index, err + } + if !reorg { + localTd := bc.GetTd(current.Hash(), current.NumberU64()) log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().Number, "sidetd", externTd, "localtd", localTd) return it.index, err } @@ -2048,9 +1793,92 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i return 0, nil } +// recoverAncestors finds the closest ancestor with available state and re-execute +// all the ancestor blocks since that. +func (bc *BlockChain) recoverAncestors(block *types.Block) error { + // Gather all the sidechain hashes (full blocks may be memory heavy) + var ( + hashes []common.Hash + numbers []uint64 + parent = block + ) + for parent != nil && !bc.HasState(parent.Root()) { + hashes = append(hashes, parent.Hash()) + numbers = append(numbers, parent.NumberU64()) + parent = bc.GetBlock(parent.ParentHash(), parent.NumberU64()-1) + + // If the chain is terminating, stop iteration + if bc.insertStopped() { + log.Debug("Abort during blocks iteration") + return errInsertionInterrupted + } + } + if parent == nil { + return errors.New("missing parent") + } + // Import all the pruned blocks to make the state available + for i := len(hashes) - 1; i >= 0; i-- { + // If the chain is terminating, stop processing blocks + if bc.insertStopped() { + log.Debug("Abort during blocks processing") + return errInsertionInterrupted + } + var b *types.Block + if i == 0 { + b = block + } else { + b = bc.GetBlock(hashes[i], numbers[i]) + } + if err := bc.insertBlock(b); err != nil { + return err + } + } + return nil +} + +// collectLogs collects the logs that were generated or removed during +// the processing of the block that corresponds with the given hash. +// These logs are later announced as deleted or reborn. +func (bc *BlockChain) collectLogs(hash common.Hash, removed bool) []*types.Log { + number := bc.hc.GetBlockNumber(hash) + if number == nil { + return nil + } + receipts := rawdb.ReadReceipts(bc.db, hash, *number, bc.chainConfig) + + var logs []*types.Log + for _, receipt := range receipts { + for _, log := range receipt.Logs { + l := *log + if removed { + l.Removed = true + } + logs = append(logs, &l) + } + } + return logs +} + +// mergeLogs returns a merged log slice with specified sort order. +func (bc *BlockChain) mergeLogs(logs [][]*types.Log, reverse bool) []*types.Log { + var ret []*types.Log + if reverse { + for i := len(logs) - 1; i >= 0; i-- { + ret = append(ret, logs[i]...) + } + } else { + for i := 0; i < len(logs); i++ { + ret = append(ret, logs[i]...) + } + } + return ret +} + // reorg takes two blocks, an old chain and a new chain and will reconstruct the // blocks and inserts them to be part of the new canonical chain and accumulates // potential missing transactions and post an event about them. +// Note the new head block won't be processed here, callers need to handle it +// externally. func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { var ( newChain types.Blocks @@ -2062,49 +1890,6 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { deletedLogs [][]*types.Log rebirthLogs [][]*types.Log - - // collectLogs collects the logs that were generated or removed during - // the processing of the block that corresponds with the given hash. - // These logs are later announced as deleted or reborn - collectLogs = func(hash common.Hash, removed bool) { - number := bc.hc.GetBlockNumber(hash) - if number == nil { - return - } - receipts := rawdb.ReadReceipts(bc.db, hash, *number, bc.chainConfig) - - var logs []*types.Log - for _, receipt := range receipts { - for _, log := range receipt.Logs { - l := *log - if removed { - l.Removed = true - } - logs = append(logs, &l) - } - } - if len(logs) > 0 { - if removed { - deletedLogs = append(deletedLogs, logs) - } else { - rebirthLogs = append(rebirthLogs, logs) - } - } - } - // mergeLogs returns a merged log slice with specified sort order. - mergeLogs = func(logs [][]*types.Log, reverse bool) []*types.Log { - var ret []*types.Log - if reverse { - for i := len(logs) - 1; i >= 0; i-- { - ret = append(ret, logs[i]...) - } - } else { - for i := 0; i < len(logs); i++ { - ret = append(ret, logs[i]...) - } - } - return ret - } ) // Reduce the longer chain to the same number as the shorter one if oldBlock.NumberU64() > newBlock.NumberU64() { @@ -2112,7 +1897,12 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { for ; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) { oldChain = append(oldChain, oldBlock) deletedTxs = append(deletedTxs, oldBlock.Transactions()...) - collectLogs(oldBlock.Hash(), true) + + // Collect deleted logs for notification + logs := bc.collectLogs(oldBlock.Hash(), true) + if len(logs) > 0 { + deletedLogs = append(deletedLogs, logs) + } } } else { // New chain is longer, stash all blocks away for subsequent insertion @@ -2137,8 +1927,12 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { // Remove an old block as well as stash away a new block oldChain = append(oldChain, oldBlock) deletedTxs = append(deletedTxs, oldBlock.Transactions()...) - collectLogs(oldBlock.Hash(), true) + // Collect deleted logs for notification + logs := bc.collectLogs(oldBlock.Hash(), true) + if len(logs) > 0 { + deletedLogs = append(deletedLogs, logs) + } newChain = append(newChain, newBlock) // Step back with both chains @@ -2164,8 +1958,15 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { blockReorgAddMeter.Mark(int64(len(newChain))) blockReorgDropMeter.Mark(int64(len(oldChain))) blockReorgMeter.Mark(1) + } else if len(newChain) > 0 { + // Special case happens in the post merge stage that current head is + // the ancestor of new head while these two blocks are not consecutive + log.Info("Extend chain", "add", len(newChain), "number", newChain[0].NumberU64(), "hash", newChain[0].Hash()) + blockReorgAddMeter.Mark(int64(len(newChain))) } else { - log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash()) + // len(newChain) == 0 && len(oldChain) > 0 + // rewind the canonical chain to a lower point. + log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "oldblocks", len(oldChain), "newnum", newBlock.Number(), "newhash", newBlock.Hash(), "newblocks", len(newChain)) } // Insert the new chain(except the head block(reverse order)), // taking care of the proper incremental order. @@ -2174,8 +1975,10 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { bc.writeHeadBlock(newChain[i]) // Collect reborn logs due to chain reorg - collectLogs(newChain[i].Hash(), false) - + logs := bc.collectLogs(newChain[i].Hash(), false) + if len(logs) > 0 { + rebirthLogs = append(rebirthLogs, logs) + } // Collect the new added transactions. addedTxs = append(addedTxs, newChain[i].Transactions()...) } @@ -2202,10 +2005,10 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { // ever happens if we're reorging empty blocks, which will only happen on idle // networks where performance is not an issue either way. if len(deletedLogs) > 0 { - bc.rmLogsFeed.Send(RemovedLogsEvent{mergeLogs(deletedLogs, true)}) + bc.rmLogsFeed.Send(RemovedLogsEvent{bc.mergeLogs(deletedLogs, true)}) } if len(rebirthLogs) > 0 { - bc.logsFeed.Send(mergeLogs(rebirthLogs, false)) + bc.logsFeed.Send(bc.mergeLogs(rebirthLogs, false)) } if len(oldChain) > 0 { for i := len(oldChain) - 1; i >= 0; i-- { @@ -2215,6 +2018,145 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { return nil } +// InsertBlock accepts a single block and the given consensus engine as the +// parameters. It will firstly execute the block, run the necessary verfication +// upon it and then persist the block and the associate state into the database. +// The key difference between the InsertChain is it won't do the canonical chain +// udpating. It relays on the additional SetChainHead call to finalize the entire +// procedure. +func (bc *BlockChain) InsertBlock(block *types.Block) error { + bc.wg.Add(1) + defer bc.wg.Done() + + bc.chainmu.Lock() + defer bc.chainmu.Unlock() + + return bc.insertBlock(block) +} + +// insertBlock is the inner version of InsertBlock without holding the lock. +func (bc *BlockChain) insertBlock(block *types.Block) error { + // If the chain is terminating, don't even bother starting up + if bc.insertStopped() { + return errInsertionInterrupted + } + // Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss) + senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, block.Number()), []*types.Block{block}) + + // Verify the header with the given consensus engine. + if err := bc.engine.VerifyHeader(bc, block.Header(), true); err != nil { + return err + } + // Verify the block body then. + err := bc.validator.ValidateBody(block) + switch { + // The block is already known, return without any error + // but print a warning log. It's not expected behavior. + case errors.Is(err, ErrKnownBlock): + log.Warn("Received known block", "hash", block.Hash(), "number", block.NumberU64()) + return nil + + // The parent is pruned, try to recover the parent state + case errors.Is(err, consensus.ErrPrunedAncestor): + log.Debug("Pruned ancestor", "number", block.Number(), "hash", block.Hash()) + return bc.recoverAncestors(block) + + // Some other error occurred(include the future block), abort + case err != nil: + return err + } + // If the header is a banned one, straight out abort + if BadHashes[block.Hash()] { + bc.reportBlock(block, nil, ErrBlacklistedHash) + return ErrBlacklistedHash + } + // Retrieve the parent block and it's state to execute on top + start := time.Now() + parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1) + statedb, err := state.New(parent.Root, bc.stateCache, bc.snaps) + if err != nil { + return err + } + // Enable prefetching to pull in trie node paths while processing transactions + statedb.StartPrefetcher("chain") + defer statedb.StopPrefetcher() + + // Process block using the parent state as reference point + substart := time.Now() + receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) + if err != nil { + bc.reportBlock(block, receipts, err) + return err + } + // Update the metrics touched during block processing + accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them + storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete, we can mark them + accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete, we can mark them + storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete, we can mark them + snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete, we can mark them + snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete, we can mark them + triehash := statedb.AccountHashes + statedb.StorageHashes // Save to not double count in validation + trieproc := statedb.SnapshotAccountReads + statedb.AccountReads + statedb.AccountUpdates + trieproc += statedb.SnapshotStorageReads + statedb.StorageReads + statedb.StorageUpdates + blockExecutionTimer.Update(time.Since(substart) - trieproc - triehash) + + // Validate the state using the default validator + substart = time.Now() + if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil { + bc.reportBlock(block, receipts, err) + return err + } + // Update the metrics touched during block validation + accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete, we can mark them + storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete, we can mark them + blockValidationTimer.Update(time.Since(substart) - (statedb.AccountHashes + statedb.StorageHashes - triehash)) + + // Write the block to the chain and get the status. + substart = time.Now() + if err := bc.writeBlockWithState(block, receipts, logs, statedb); err != nil { + return err + } + // Update the metrics touched during block commit + accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them + storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them + snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them + + blockWriteTimer.Update(time.Since(substart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits) + blockInsertTimer.UpdateSince(start) + log.Info("Inserted block", "number", block.Number(), "hash", block.Hash(), "txs", len(block.Transactions()), "elasped", common.PrettyDuration(time.Since(start))) + return nil +} + +// SetChainHead rewinds the chain to set the new head block as the specified +// block. It's possible that after the reorg the relevant state of head +// is missing. It can be fixed by inserting a new block which triggers +// the re-execution. +func (bc *BlockChain) SetChainHead(newBlock *types.Block) error { + bc.wg.Add(1) + defer bc.wg.Done() + + bc.chainmu.Lock() + defer bc.chainmu.Unlock() + + // Run the reorg if necessary and set the given block as new head. + if newBlock.ParentHash() != bc.CurrentBlock().Hash() { + if err := bc.reorg(bc.CurrentBlock(), newBlock); err != nil { + return err + } + } + bc.writeHeadBlock(newBlock) + + // Emit events + logs := bc.collectLogs(newBlock.Hash(), false) + bc.chainFeed.Send(ChainEvent{Block: newBlock, Hash: newBlock.Hash(), Logs: logs}) + if len(logs) > 0 { + bc.logsFeed.Send(logs) + } + bc.chainHeadFeed.Send(ChainHeadEvent{Block: newBlock}) + log.Info("Set the chain head", "number", newBlock.Number(), "hash", newBlock.Hash()) + return nil +} + func (bc *BlockChain) update() { futureTimer := time.NewTicker(5 * time.Second) defer futureTimer.Stop() @@ -2357,131 +2299,6 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i bc.wg.Add(1) defer bc.wg.Done() - _, err := bc.hc.InsertHeaderChain(chain, start) + _, err := bc.hc.InsertHeaderChain(chain, start, bc.forker) return 0, err } - -// CurrentHeader retrieves the current head header of the canonical chain. The -// header is retrieved from the HeaderChain's internal cache. -func (bc *BlockChain) CurrentHeader() *types.Header { - return bc.hc.CurrentHeader() -} - -// GetTd retrieves a block's total difficulty in the canonical chain from the -// database by hash and number, caching it if found. -func (bc *BlockChain) GetTd(hash common.Hash, number uint64) *big.Int { - return bc.hc.GetTd(hash, number) -} - -// GetTdByHash retrieves a block's total difficulty in the canonical chain from the -// database by hash, caching it if found. -func (bc *BlockChain) GetTdByHash(hash common.Hash) *big.Int { - return bc.hc.GetTdByHash(hash) -} - -// GetHeader retrieves a block header from the database by hash and number, -// caching it if found. -func (bc *BlockChain) GetHeader(hash common.Hash, number uint64) *types.Header { - // Blockchain might have cached the whole block, only if not go to headerchain - if block, ok := bc.blockCache.Get(hash); ok { - return block.(*types.Block).Header() - } - - return bc.hc.GetHeader(hash, number) -} - -// GetHeaderByHash retrieves a block header from the database by hash, caching it if -// found. -func (bc *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header { - // Blockchain might have cached the whole block, only if not go to headerchain - if block, ok := bc.blockCache.Get(hash); ok { - return block.(*types.Block).Header() - } - - return bc.hc.GetHeaderByHash(hash) -} - -// HasHeader checks if a block header is present in the database or not, caching -// it if present. -func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool { - return bc.hc.HasHeader(hash, number) -} - -// GetCanonicalHash returns the canonical hash for a given block number -func (bc *BlockChain) GetCanonicalHash(number uint64) common.Hash { - return bc.hc.GetCanonicalHash(number) -} - -// GetBlockHashesFromHash retrieves a number of block hashes starting at a given -// hash, fetching towards the genesis block. -func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { - return bc.hc.GetBlockHashesFromHash(hash, max) -} - -// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or -// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the -// number of blocks to be individually checked before we reach the canonical chain. -// -// Note: ancestor == 0 returns the same block, 1 returns its parent and so on. -func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) { - return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical) -} - -// GetHeaderByNumber retrieves a block header from the database by number, -// caching it (associated with its hash) if found. -func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header { - return bc.hc.GetHeaderByNumber(number) -} - -// GetTransactionLookup retrieves the lookup associate with the given transaction -// hash from the cache or database. -func (bc *BlockChain) GetTransactionLookup(hash common.Hash) *rawdb.LegacyTxLookupEntry { - // Short circuit if the txlookup already in the cache, retrieve otherwise - if lookup, exist := bc.txLookupCache.Get(hash); exist { - return lookup.(*rawdb.LegacyTxLookupEntry) - } - tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash) - if tx == nil { - return nil - } - lookup := &rawdb.LegacyTxLookupEntry{BlockHash: blockHash, BlockIndex: blockNumber, Index: txIndex} - bc.txLookupCache.Add(hash, lookup) - return lookup -} - -// Config retrieves the chain's fork configuration. -func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig } - -// Engine retrieves the blockchain's consensus engine. -func (bc *BlockChain) Engine() consensus.Engine { return bc.engine } - -// SubscribeRemovedLogsEvent registers a subscription of RemovedLogsEvent. -func (bc *BlockChain) SubscribeRemovedLogsEvent(ch chan<- RemovedLogsEvent) event.Subscription { - return bc.scope.Track(bc.rmLogsFeed.Subscribe(ch)) -} - -// SubscribeChainEvent registers a subscription of ChainEvent. -func (bc *BlockChain) SubscribeChainEvent(ch chan<- ChainEvent) event.Subscription { - return bc.scope.Track(bc.chainFeed.Subscribe(ch)) -} - -// SubscribeChainHeadEvent registers a subscription of ChainHeadEvent. -func (bc *BlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription { - return bc.scope.Track(bc.chainHeadFeed.Subscribe(ch)) -} - -// SubscribeChainSideEvent registers a subscription of ChainSideEvent. -func (bc *BlockChain) SubscribeChainSideEvent(ch chan<- ChainSideEvent) event.Subscription { - return bc.scope.Track(bc.chainSideFeed.Subscribe(ch)) -} - -// SubscribeLogsEvent registers a subscription of []*types.Log. -func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { - return bc.scope.Track(bc.logsFeed.Subscribe(ch)) -} - -// SubscribeBlockProcessingEvent registers a subscription of bool where true means -// block processing has started while false means it has stopped. -func (bc *BlockChain) SubscribeBlockProcessingEvent(ch chan<- bool) event.Subscription { - return bc.scope.Track(bc.blockProcFeed.Subscribe(ch)) -} diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go new file mode 100644 index 000000000000..caa5125c627d --- /dev/null +++ b/core/blockchain_reader.go @@ -0,0 +1,399 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" +) + +// CurrentHeader retrieves the current head header of the canonical chain. The +// header is retrieved from the HeaderChain's internal cache. +func (bc *BlockChain) CurrentHeader() *types.Header { + return bc.hc.CurrentHeader() +} + +// CurrentBlock retrieves the current head block of the canonical chain. The +// block is retrieved from the blockchain's internal cache. +func (bc *BlockChain) CurrentBlock() *types.Block { + return bc.currentBlock.Load().(*types.Block) +} + +// CurrentFastBlock retrieves the current fast-sync head block of the canonical +// chain. The block is retrieved from the blockchain's internal cache. +func (bc *BlockChain) CurrentFastBlock() *types.Block { + return bc.currentFastBlock.Load().(*types.Block) +} + +// HasHeader checks if a block header is present in the database or not, caching +// it if present. +func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool { + return bc.hc.HasHeader(hash, number) +} + +// GetHeader retrieves a block header from the database by hash and number, +// caching it if found. +func (bc *BlockChain) GetHeader(hash common.Hash, number uint64) *types.Header { + return bc.hc.GetHeader(hash, number) +} + +// GetHeaderByHash retrieves a block header from the database by hash, caching it if +// found. +func (bc *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header { + return bc.hc.GetHeaderByHash(hash) +} + +// GetHeaderByNumber retrieves a block header from the database by number, +// caching it (associated with its hash) if found. +func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header { + return bc.hc.GetHeaderByNumber(number) +} + +// GetBody retrieves a block body (transactions and uncles) from the database by +// hash, caching it if found. +func (bc *BlockChain) GetBody(hash common.Hash) *types.Body { + // Short circuit if the body's already in the cache, retrieve otherwise + if cached, ok := bc.bodyCache.Get(hash); ok { + body := cached.(*types.Body) + return body + } + number := bc.hc.GetBlockNumber(hash) + if number == nil { + return nil + } + body := rawdb.ReadBody(bc.db, hash, *number) + if body == nil { + return nil + } + // Cache the found body for next time and return + bc.bodyCache.Add(hash, body) + return body +} + +// GetBodyRLP retrieves a block body in RLP encoding from the database by hash, +// caching it if found. +func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue { + // Short circuit if the body's already in the cache, retrieve otherwise + if cached, ok := bc.bodyRLPCache.Get(hash); ok { + return cached.(rlp.RawValue) + } + number := bc.hc.GetBlockNumber(hash) + if number == nil { + return nil + } + body := rawdb.ReadBodyRLP(bc.db, hash, *number) + if len(body) == 0 { + return nil + } + // Cache the found body for next time and return + bc.bodyRLPCache.Add(hash, body) + return body +} + +// HasBlock checks if a block is fully present in the database or not. +func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool { + if bc.blockCache.Contains(hash) { + return true + } + return rawdb.HasBody(bc.db, hash, number) +} + +// HasFastBlock checks if a fast block is fully present in the database or not. +func (bc *BlockChain) HasFastBlock(hash common.Hash, number uint64) bool { + if !bc.HasBlock(hash, number) { + return false + } + if bc.receiptsCache.Contains(hash) { + return true + } + return rawdb.HasReceipts(bc.db, hash, number) +} + +// GetBlock retrieves a block from the database by hash and number, +// caching it if found. +func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { + // Short circuit if the block's already in the cache, retrieve otherwise + if block, ok := bc.blockCache.Get(hash); ok { + return block.(*types.Block) + } + block := rawdb.ReadBlock(bc.db, hash, number) + if block == nil { + return nil + } + // Cache the found block for next time and return + bc.blockCache.Add(block.Hash(), block) + return block +} + +// GetBlockByHash retrieves a block from the database by hash, caching it if found. +func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block { + number := bc.hc.GetBlockNumber(hash) + if number == nil { + return nil + } + return bc.GetBlock(hash, *number) +} + +// GetBlockByNumber retrieves a block from the database by number, caching it +// (associated with its hash) if found. +func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block { + hash := rawdb.ReadCanonicalHash(bc.db, number) + if hash == (common.Hash{}) { + return nil + } + return bc.GetBlock(hash, number) +} + +// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors. +// [deprecated by eth/62] +func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) { + number := bc.hc.GetBlockNumber(hash) + if number == nil { + return nil + } + for i := 0; i < n; i++ { + block := bc.GetBlock(hash, *number) + if block == nil { + break + } + blocks = append(blocks, block) + hash = block.ParentHash() + *number-- + } + return +} + +// GetReceiptsByHash retrieves the receipts for all transactions in a given block. +func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts { + if receipts, ok := bc.receiptsCache.Get(hash); ok { + return receipts.(types.Receipts) + } + number := rawdb.ReadHeaderNumber(bc.db, hash) + if number == nil { + return nil + } + receipts := rawdb.ReadReceipts(bc.db, hash, *number, bc.chainConfig) + if receipts == nil { + return nil + } + bc.receiptsCache.Add(hash, receipts) + return receipts +} + +// GetUnclesInChain retrieves all the uncles from a given block backwards until +// a specific distance is reached. +func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header { + uncles := []*types.Header{} + for i := 0; block != nil && i < length; i++ { + uncles = append(uncles, block.Uncles()...) + block = bc.GetBlock(block.ParentHash(), block.NumberU64()-1) + } + return uncles +} + +// GetCanonicalHash returns the canonical hash for a given block number +func (bc *BlockChain) GetCanonicalHash(number uint64) common.Hash { + return bc.hc.GetCanonicalHash(number) +} + +// GetBlockHashesFromHash retrieves a number of block hashes starting at a given +// hash, fetching towards the genesis block. +func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { + return bc.hc.GetBlockHashesFromHash(hash, max) +} + +// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or +// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the +// number of blocks to be individually checked before we reach the canonical chain. +// +// Note: ancestor == 0 returns the same block, 1 returns its parent and so on. +func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) { + return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical) +} + +// GetTransactionLookup retrieves the lookup associate with the given transaction +// hash from the cache or database. +func (bc *BlockChain) GetTransactionLookup(hash common.Hash) *rawdb.LegacyTxLookupEntry { + // Short circuit if the txlookup already in the cache, retrieve otherwise + if lookup, exist := bc.txLookupCache.Get(hash); exist { + return lookup.(*rawdb.LegacyTxLookupEntry) + } + tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash) + if tx == nil { + return nil + } + lookup := &rawdb.LegacyTxLookupEntry{BlockHash: blockHash, BlockIndex: blockNumber, Index: txIndex} + bc.txLookupCache.Add(hash, lookup) + return lookup +} + +// GetTd retrieves a block's total difficulty in the canonical chain from the +// database by hash and number, caching it if found. +func (bc *BlockChain) GetTd(hash common.Hash, number uint64) *big.Int { + return bc.hc.GetTd(hash, number) +} + +// GetTdByHash retrieves a block's total difficulty in the canonical chain from the +// database by hash, caching it if found. +func (bc *BlockChain) GetTdByHash(hash common.Hash) *big.Int { + return bc.hc.GetTdByHash(hash) +} + +// HasState checks if state trie is fully present in the database or not. +func (bc *BlockChain) HasState(hash common.Hash) bool { + _, err := bc.stateCache.OpenTrie(hash) + return err == nil +} + +// HasBlockAndState checks if a block and associated state trie is fully present +// in the database or not, caching it if present. +func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool { + // Check first that the block itself is known + block := bc.GetBlock(hash, number) + if block == nil { + return false + } + return bc.HasState(block.Root()) +} + +// TrieNode retrieves a blob of data associated with a trie node +// either from ephemeral in-memory cache, or from persistent storage. +func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) { + return bc.stateCache.TrieDB().Node(hash) +} + +// ContractCode retrieves a blob of data associated with a contract hash +// either from ephemeral in-memory cache, or from persistent storage. +func (bc *BlockChain) ContractCode(hash common.Hash) ([]byte, error) { + return bc.stateCache.ContractCode(common.Hash{}, hash) +} + +// ContractCodeWithPrefix retrieves a blob of data associated with a contract +// hash either from ephemeral in-memory cache, or from persistent storage. +// +// If the code doesn't exist in the in-memory cache, check the storage with +// new code scheme. +func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) { + type codeReader interface { + ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error) + } + return bc.stateCache.(codeReader).ContractCodeWithPrefix(common.Hash{}, hash) +} + +// State returns a new mutable state based on the current HEAD block. +func (bc *BlockChain) State() (*state.StateDB, error) { + return bc.StateAt(bc.CurrentBlock().Root()) +} + +// StateAt returns a new mutable state based on a particular point in time. +func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { + return state.New(root, bc.stateCache, bc.snaps) +} + +// Config retrieves the chain's fork configuration. +func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig } + +// Engine retrieves the blockchain's consensus engine. +func (bc *BlockChain) Engine() consensus.Engine { return bc.engine } + +// Snapshots returns the blockchain snapshot tree. +func (bc *BlockChain) Snapshots() *snapshot.Tree { + return bc.snaps +} + +// Validator returns the current validator. +func (bc *BlockChain) Validator() Validator { + return bc.validator +} + +// Processor returns the current processor. +func (bc *BlockChain) Processor() Processor { + return bc.processor +} + +// StateCache returns the caching database underpinning the blockchain instance. +func (bc *BlockChain) StateCache() state.Database { + return bc.stateCache +} + +// GasLimit returns the gas limit of the current HEAD block. +func (bc *BlockChain) GasLimit() uint64 { + return bc.CurrentBlock().GasLimit() +} + +// Genesis retrieves the chain's genesis block. +func (bc *BlockChain) Genesis() *types.Block { + return bc.genesisBlock +} + +// GetVMConfig returns the block chain VM config. +func (bc *BlockChain) GetVMConfig() *vm.Config { + return &bc.vmConfig +} + +// SetTxLookupLimit is responsible for updating the txlookup limit to the +// original one stored in db if the new mismatches with the old one. +func (bc *BlockChain) SetTxLookupLimit(limit uint64) { + bc.txLookupLimit = limit +} + +// TxLookupLimit retrieves the txlookup limit used by blockchain to prune +// stale transaction indices. +func (bc *BlockChain) TxLookupLimit() uint64 { + return bc.txLookupLimit +} + +// SubscribeRemovedLogsEvent registers a subscription of RemovedLogsEvent. +func (bc *BlockChain) SubscribeRemovedLogsEvent(ch chan<- RemovedLogsEvent) event.Subscription { + return bc.scope.Track(bc.rmLogsFeed.Subscribe(ch)) +} + +// SubscribeChainEvent registers a subscription of ChainEvent. +func (bc *BlockChain) SubscribeChainEvent(ch chan<- ChainEvent) event.Subscription { + return bc.scope.Track(bc.chainFeed.Subscribe(ch)) +} + +// SubscribeChainHeadEvent registers a subscription of ChainHeadEvent. +func (bc *BlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription { + return bc.scope.Track(bc.chainHeadFeed.Subscribe(ch)) +} + +// SubscribeChainSideEvent registers a subscription of ChainSideEvent. +func (bc *BlockChain) SubscribeChainSideEvent(ch chan<- ChainSideEvent) event.Subscription { + return bc.scope.Track(bc.chainSideFeed.Subscribe(ch)) +} + +// SubscribeLogsEvent registers a subscription of []*types.Log. +func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { + return bc.scope.Track(bc.logsFeed.Subscribe(ch)) +} + +// SubscribeBlockProcessingEvent registers a subscription of bool where true means +// block processing has started while false means it has stopped. +func (bc *BlockChain) SubscribeBlockProcessingEvent(ch chan<- bool) event.Subscription { + return bc.scope.Track(bc.blockProcFeed.Subscribe(ch)) +} diff --git a/core/blockchain_repair_test.go b/core/blockchain_repair_test.go index aca5546e2048..f5f111e99f68 100644 --- a/core/blockchain_repair_test.go +++ b/core/blockchain_repair_test.go @@ -1783,7 +1783,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) { config.SnapshotLimit = 256 config.SnapshotWait = true } - chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to create chain: %v", err) } @@ -1829,14 +1829,14 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) { // Pull the plug on the database, simulating a hard crash db.Close() - // Start a new blockchain back up and see where the repait leads us + // Start a new blockchain back up and see where the repair leads us db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false) if err != nil { t.Fatalf("Failed to reopen persistent database: %v", err) } defer db.Close() - chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } diff --git a/core/blockchain_sethead_test.go b/core/blockchain_sethead_test.go index 27b6be6e1363..85a5b891f195 100644 --- a/core/blockchain_sethead_test.go +++ b/core/blockchain_sethead_test.go @@ -1982,7 +1982,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) { config.SnapshotLimit = 256 config.SnapshotWait = true } - chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to create chain: %v", err) } diff --git a/core/blockchain_snapshot_test.go b/core/blockchain_snapshot_test.go index a8044ecb4d81..4fb14349f65d 100644 --- a/core/blockchain_snapshot_test.go +++ b/core/blockchain_snapshot_test.go @@ -80,7 +80,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo // will happen during the block insertion. cacheConfig = defaultCacheConfig ) - chain, err := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to create chain: %v", err) } @@ -223,7 +223,7 @@ func (snaptest *snapshotTest) test(t *testing.T) { // Restart the chain normally chain.Stop() - newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -259,13 +259,13 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) { // the crash, we do restart twice here: one after the crash and one // after the normal stop. It's used to ensure the broken snapshot // can be detected all the time. - newchain, err := NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err := NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } newchain.Stop() - newchain, err = NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err = NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -301,7 +301,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) { TrieTimeLimit: 5 * time.Minute, SnapshotLimit: 0, } - newchain, err := NewBlockChain(snaptest.db, cacheConfig, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err := NewBlockChain(snaptest.db, cacheConfig, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -309,7 +309,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) { newchain.Stop() // Restart the chain with enabling the snapshot - newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -337,7 +337,7 @@ func (snaptest *setHeadSnapshotTest) test(t *testing.T) { chain.SetHead(snaptest.setHead) chain.Stop() - newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -368,7 +368,7 @@ func (snaptest *restartCrashSnapshotTest) test(t *testing.T) { // and state committed. chain.Stop() - newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -385,7 +385,7 @@ func (snaptest *restartCrashSnapshotTest) test(t *testing.T) { // journal and latest state will be committed // Restart the chain after the crash - newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -420,7 +420,7 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) { TrieTimeLimit: 5 * time.Minute, SnapshotLimit: 0, } - newchain, err := NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err := NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } @@ -436,13 +436,13 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) { SnapshotLimit: 256, SnapshotWait: false, // Don't wait rebuild } - newchain, err = NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err = NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } // Simulate the blockchain crash. - newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil) + newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 8d94f17aabae..f1e130b19ff1 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -28,7 +28,9 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" @@ -56,7 +58,7 @@ func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *B ) // Initialize a fresh chain with only a genesis block - blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) // Create and inject the requested chain if n == 0 { return db, blockchain, nil @@ -205,6 +207,58 @@ func TestLastBlock(t *testing.T) { } } +// Test inserts the blocks/headers after the fork choice rule is changed. +// The chain is reorged to whatever specified. +func testInsertAfterMerge(t *testing.T, blockchain *BlockChain, i, n int, full bool) { + // Copy old chain up to #i into a new db + db, blockchain2, err := newCanonical(ethash.NewFaker(), i, full) + if err != nil { + t.Fatal("could not make new canonical in testFork", err) + } + defer blockchain2.Stop() + + // Assert the chains have the same header/block at #i + var hash1, hash2 common.Hash + if full { + hash1 = blockchain.GetBlockByNumber(uint64(i)).Hash() + hash2 = blockchain2.GetBlockByNumber(uint64(i)).Hash() + } else { + hash1 = blockchain.GetHeaderByNumber(uint64(i)).Hash() + hash2 = blockchain2.GetHeaderByNumber(uint64(i)).Hash() + } + if hash1 != hash2 { + t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1) + } + + // Trigger the transition explicitly + blockchain2.forker.MarkTransitioned() + + // Extend the newly created chain + if full { + blockChainB := makeBlockChain(blockchain2.CurrentBlock(), n, ethash.NewFaker(), db, forkSeed) + if _, err := blockchain2.InsertChain(blockChainB); err != nil { + t.Fatalf("failed to insert forking chain: %v", err) + } + if blockchain2.CurrentBlock().NumberU64() != blockChainB[len(blockChainB)-1].NumberU64() { + t.Fatalf("failed to reorg to the given chain") + } + if blockchain2.CurrentBlock().Hash() != blockChainB[len(blockChainB)-1].Hash() { + t.Fatalf("failed to reorg to the given chain") + } + } else { + headerChainB := makeHeaderChain(blockchain2.CurrentHeader(), n, ethash.NewFaker(), db, forkSeed) + if _, err := blockchain2.InsertHeaderChain(headerChainB, 1); err != nil { + t.Fatalf("failed to insert forking chain: %v", err) + } + if blockchain2.CurrentHeader().Number.Uint64() != headerChainB[len(headerChainB)-1].Number.Uint64() { + t.Fatalf("failed to reorg to the given chain") + } + if blockchain2.CurrentHeader().Hash() != headerChainB[len(headerChainB)-1].Hash() { + t.Fatalf("failed to reorg to the given chain") + } + } +} + // Tests that given a starting canonical chain of a given size, it can be extended // with various length chains. func TestExtendCanonicalHeaders(t *testing.T) { testExtendCanonical(t, false) } @@ -233,6 +287,25 @@ func testExtendCanonical(t *testing.T, full bool) { testFork(t, processor, length, 10, full, better) } +// Tests that given a starting canonical chain of a given size, it can be extended +// with various length chains. +func TestExtendCanonicalHeadersAfterMerge(t *testing.T) { testExtendCanonicalAfterMerge(t, false) } +func TestExtendCanonicalBlocksAfterMerge(t *testing.T) { testExtendCanonicalAfterMerge(t, true) } + +func testExtendCanonicalAfterMerge(t *testing.T, full bool) { + length := 5 + + // Make first chain starting from genesis + _, processor, err := newCanonical(ethash.NewFaker(), length, full) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + defer processor.Stop() + + testInsertAfterMerge(t, processor, length, 1, full) + testInsertAfterMerge(t, processor, length, 10, full) +} + // Tests that given a starting canonical chain of a given size, creating shorter // forks do not take canonical ownership. func TestShorterForkHeaders(t *testing.T) { testShorterFork(t, false) } @@ -263,6 +336,29 @@ func testShorterFork(t *testing.T, full bool) { testFork(t, processor, 5, 4, full, worse) } +// Tests that given a starting canonical chain of a given size, creating shorter +// forks do not take canonical ownership. +func TestShorterForkHeadersAfterMerge(t *testing.T) { testShorterForkAfterMerge(t, false) } +func TestShorterForkBlocksAfterMerge(t *testing.T) { testShorterForkAfterMerge(t, true) } + +func testShorterForkAfterMerge(t *testing.T, full bool) { + length := 10 + + // Make first chain starting from genesis + _, processor, err := newCanonical(ethash.NewFaker(), length, full) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + defer processor.Stop() + + testInsertAfterMerge(t, processor, 0, 3, full) + testInsertAfterMerge(t, processor, 0, 7, full) + testInsertAfterMerge(t, processor, 1, 1, full) + testInsertAfterMerge(t, processor, 1, 7, full) + testInsertAfterMerge(t, processor, 5, 3, full) + testInsertAfterMerge(t, processor, 5, 4, full) +} + // Tests that given a starting canonical chain of a given size, creating longer // forks do take canonical ownership. func TestLongerForkHeaders(t *testing.T) { testLongerFork(t, false) } @@ -278,19 +374,35 @@ func testLongerFork(t *testing.T, full bool) { } defer processor.Stop() - // Define the difficulty comparator - better := func(td1, td2 *big.Int) { - if td2.Cmp(td1) <= 0 { - t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1) - } + testInsertAfterMerge(t, processor, 0, 11, full) + testInsertAfterMerge(t, processor, 0, 15, full) + testInsertAfterMerge(t, processor, 1, 10, full) + testInsertAfterMerge(t, processor, 1, 12, full) + testInsertAfterMerge(t, processor, 5, 6, full) + testInsertAfterMerge(t, processor, 5, 8, full) +} + +// Tests that given a starting canonical chain of a given size, creating longer +// forks do take canonical ownership. +func TestLongerForkHeadersAfterMerge(t *testing.T) { testLongerForkAfterMerge(t, false) } +func TestLongerForkBlocksAfterMerge(t *testing.T) { testLongerForkAfterMerge(t, true) } + +func testLongerForkAfterMerge(t *testing.T, full bool) { + length := 10 + + // Make first chain starting from genesis + _, processor, err := newCanonical(ethash.NewFaker(), length, full) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) } - // Sum of numbers must be greater than `length` for this to be a longer fork - testFork(t, processor, 0, 11, full, better) - testFork(t, processor, 0, 15, full, better) - testFork(t, processor, 1, 10, full, better) - testFork(t, processor, 1, 12, full, better) - testFork(t, processor, 5, 6, full, better) - testFork(t, processor, 5, 8, full, better) + defer processor.Stop() + + testInsertAfterMerge(t, processor, 0, 11, full) + testInsertAfterMerge(t, processor, 0, 15, full) + testInsertAfterMerge(t, processor, 1, 10, full) + testInsertAfterMerge(t, processor, 1, 12, full) + testInsertAfterMerge(t, processor, 5, 6, full) + testInsertAfterMerge(t, processor, 5, 8, full) } // Tests that given a starting canonical chain of a given size, creating equal @@ -323,6 +435,29 @@ func testEqualFork(t *testing.T, full bool) { testFork(t, processor, 9, 1, full, equal) } +// Tests that given a starting canonical chain of a given size, creating equal +// forks do take canonical ownership. +func TestEqualForkHeadersAfterMerge(t *testing.T) { testEqualForkAfterMerge(t, false) } +func TestEqualForkBlocksAfterMerge(t *testing.T) { testEqualForkAfterMerge(t, true) } + +func testEqualForkAfterMerge(t *testing.T, full bool) { + length := 10 + + // Make first chain starting from genesis + _, processor, err := newCanonical(ethash.NewFaker(), length, full) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + defer processor.Stop() + + testInsertAfterMerge(t, processor, 0, 10, full) + testInsertAfterMerge(t, processor, 1, 9, full) + testInsertAfterMerge(t, processor, 2, 8, full) + testInsertAfterMerge(t, processor, 5, 5, full) + testInsertAfterMerge(t, processor, 6, 4, full) + testInsertAfterMerge(t, processor, 9, 1, full) +} + // Tests that chains missing links do not get accepted by the processor. func TestBrokenHeaderChain(t *testing.T) { testBrokenChain(t, false) } func TestBrokenBlockChain(t *testing.T) { testBrokenChain(t, true) } @@ -515,7 +650,7 @@ func testReorgBadHashes(t *testing.T, full bool) { blockchain.Stop() // Create a new BlockChain and check that it rolled back the state. - ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create new chain manager: %v", err) } @@ -628,7 +763,7 @@ func TestFastVsFullChains(t *testing.T) { // Import the chain as an archive node for the comparison baseline archiveDb := rawdb.NewMemoryDatabase() gspec.MustCommit(archiveDb) - archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer archive.Stop() if n, err := archive.InsertChain(blocks); err != nil { @@ -637,7 +772,7 @@ func TestFastVsFullChains(t *testing.T) { // Fast import the chain as a non-archive node to test fastDb := rawdb.NewMemoryDatabase() gspec.MustCommit(fastDb) - fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer fast.Stop() headers := make([]*types.Header, len(blocks)) @@ -661,7 +796,7 @@ func TestFastVsFullChains(t *testing.T) { t.Fatalf("failed to create temp freezer db: %v", err) } gspec.MustCommit(ancientDb) - ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer ancient.Stop() if n, err := ancient.InsertHeaderChain(headers, 1); err != nil { @@ -783,7 +918,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { archiveCaching := *defaultCacheConfig archiveCaching.TrieDirtyDisabled = true - archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if n, err := archive.InsertChain(blocks); err != nil { t.Fatalf("failed to process block %d: %v", n, err) } @@ -796,7 +931,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { // Import the chain as a non-archive node and ensure all pointers are updated fastDb, delfn := makeDb() defer delfn() - fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer fast.Stop() headers := make([]*types.Header, len(blocks)) @@ -816,7 +951,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { // Import the chain as a ancient-first node and ensure all pointers are updated ancientDb, delfn := makeDb() defer delfn() - ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer ancient.Stop() if n, err := ancient.InsertHeaderChain(headers, 1); err != nil { @@ -835,7 +970,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { // Import the chain as a light node and ensure all pointers are updated lightDb, delfn := makeDb() defer delfn() - light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if n, err := light.InsertHeaderChain(headers, 1); err != nil { t.Fatalf("failed to insert header %d: %v", n, err) } @@ -904,7 +1039,7 @@ func TestChainTxReorgs(t *testing.T) { } }) // Import the chain. This runs all block validation rules. - blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if i, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert original chain[%d]: %v", i, err) } @@ -974,7 +1109,7 @@ func TestLogReorgs(t *testing.T) { signer = types.LatestSigner(gspec.Config) ) - blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer blockchain.Stop() rmLogsCh := make(chan RemovedLogsEvent) @@ -1027,7 +1162,7 @@ func TestLogRebirth(t *testing.T) { genesis = gspec.MustCommit(db) signer = types.LatestSigner(gspec.Config) engine = ethash.NewFaker() - blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil) + blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) ) defer blockchain.Stop() @@ -1090,7 +1225,7 @@ func TestSideLogRebirth(t *testing.T) { gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}} genesis = gspec.MustCommit(db) signer = types.LatestSigner(gspec.Config) - blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) ) defer blockchain.Stop() @@ -1165,7 +1300,7 @@ func TestReorgSideEvent(t *testing.T) { signer = types.LatestSigner(gspec.Config) ) - blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer blockchain.Stop() chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {}) @@ -1297,7 +1432,7 @@ func TestEIP155Transition(t *testing.T) { genesis = gspec.MustCommit(db) ) - blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer blockchain.Stop() blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) { @@ -1405,7 +1540,7 @@ func TestEIP161AccountRemoval(t *testing.T) { } genesis = gspec.MustCommit(db) ) - blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer blockchain.Stop() blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) { @@ -1480,7 +1615,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) { diskdb := rawdb.NewMemoryDatabase() (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -1524,7 +1659,7 @@ func TestTrieForkGC(t *testing.T) { diskdb := rawdb.NewMemoryDatabase() (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -1563,7 +1698,7 @@ func TestLargeReorgTrieGC(t *testing.T) { diskdb := rawdb.NewMemoryDatabase() (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -1624,7 +1759,7 @@ func TestBlockchainRecovery(t *testing.T) { t.Fatalf("failed to create temp freezer db: %v", err) } gspec.MustCommit(ancientDb) - ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) headers := make([]*types.Header, len(blocks)) for i, block := range blocks { @@ -1644,7 +1779,7 @@ func TestBlockchainRecovery(t *testing.T) { rawdb.WriteHeadFastBlockHash(ancientDb, midBlock.Hash()) // Reopen broken blockchain again - ancient, _ = NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + ancient, _ = NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer ancient.Stop() if num := ancient.CurrentBlock().NumberU64(); num != 0 { t.Errorf("head block mismatch: have #%v, want #%v", num, 0) @@ -1696,7 +1831,7 @@ func TestInsertReceiptChainRollback(t *testing.T) { } gspec := Genesis{Config: params.AllEthashProtocolChanges} gspec.MustCommit(ancientDb) - ancientChain, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + ancientChain, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer ancientChain.Stop() // Import the canonical header chain. @@ -1757,7 +1892,7 @@ func TestLowDiffLongChain(t *testing.T) { diskdb := rawdb.NewMemoryDatabase() (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -1793,21 +1928,51 @@ func TestLowDiffLongChain(t *testing.T) { // - C is canon chain, containing blocks [G..Cn..Cm] // - A common ancestor is placed at prune-point + blocksBetweenCommonAncestorAndPruneblock // - The sidechain S is prepended with numCanonBlocksInSidechain blocks from the canon chain -func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommonAncestorAndPruneblock int) { - +// +// The mergePoint can be these values: +// -1: the transition won't happen +// 0: the transition happens since genesis +// 1: the transition happens after some chain segments +func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommonAncestorAndPruneblock int, mergePoint int) { // Generate a canonical chain to act as the main dataset - engine := ethash.NewFaker() - db := rawdb.NewMemoryDatabase() - genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) + var ( + genEngine = beacon.New(ethash.NewFaker(), false) + runEngine = beacon.New(ethash.NewFaker(), false) + db = rawdb.NewMemoryDatabase() + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr = crypto.PubkeyToAddress(key.PublicKey) + nonce = uint64(0) + + gspec = &Genesis{ + Config: params.TestChainConfig, + Alloc: GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}}, + BaseFee: big.NewInt(params.InitialBaseFee), + } + signer = types.LatestSigner(gspec.Config) + genesis, _ = gspec.Commit(db) + ) // Generate and import the canonical chain - blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil) diskdb := rawdb.NewMemoryDatabase() - (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + gspec.MustCommit(diskdb) + chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, runEngine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + // Activate the transition since genesis if required + if mergePoint == 0 { + genEngine.MarkTransitioned() + runEngine.MarkTransitioned() + chain.forker.MarkTransitioned() + } + blocks, _ := GenerateChain(params.TestChainConfig, genesis, genEngine, db, 2*TriesInMemory, func(i int, gen *BlockGen) { + tx, err := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("deadbeef"), big.NewInt(100), 21000, big.NewInt(int64(i+1)*params.GWei), nil), signer, key) + if err != nil { + t.Fatalf("failed to create tx: %v", err) + } + gen.AddTx(tx) + nonce++ + }) if n, err := chain.InsertChain(blocks); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } @@ -1824,6 +1989,14 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) { t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64()) } + + // Activate the transition in the middle of the chain + if mergePoint == 1 { + genEngine.MarkTransitioned() + runEngine.MarkTransitioned() + chain.forker.MarkTransitioned() + } + // Generate the sidechain // First block should be a known block, block after should be a pruned block. So // canon(pruned), side, side... @@ -1831,7 +2004,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon // Generate fork chain, make it longer than canon parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock parent := blocks[parentIndex] - fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { + fork, _ := GenerateChain(params.TestChainConfig, parent, genEngine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) // Prepend the parent(s) @@ -1840,9 +2013,9 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon sidechain = append(sidechain, blocks[parentIndex+1-i]) } sidechain = append(sidechain, fork...) - _, err = chain.InsertChain(sidechain) + n, err := chain.InsertChain(sidechain) if err != nil { - t.Errorf("Got error, %v", err) + t.Errorf("Got error, %v number %d - %d", err, sidechain[n].NumberU64(), n) } head := chain.CurrentBlock() if got := fork[len(fork)-1].Hash(); got != head.Hash() { @@ -1863,11 +2036,28 @@ func TestPrunedImportSide(t *testing.T) { //glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false))) //glogger.Verbosity(3) //log.Root().SetHandler(log.Handler(glogger)) - testSideImport(t, 3, 3) - testSideImport(t, 3, -3) - testSideImport(t, 10, 0) - testSideImport(t, 1, 10) - testSideImport(t, 1, -10) + testSideImport(t, 3, 3, -1) + testSideImport(t, 3, -3, -1) + testSideImport(t, 10, 0, -1) + testSideImport(t, 1, 10, -1) + testSideImport(t, 1, -10, -1) +} + +func TestPrunedImportSideWithMerging(t *testing.T) { + //glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false))) + //glogger.Verbosity(3) + //log.Root().SetHandler(log.Handler(glogger)) + testSideImport(t, 3, 3, 0) + testSideImport(t, 3, -3, 0) + testSideImport(t, 10, 0, 0) + testSideImport(t, 1, 10, 0) + testSideImport(t, 1, -10, 0) + + testSideImport(t, 3, 3, 1) + testSideImport(t, 3, -3, 1) + testSideImport(t, 10, 0, 1) + testSideImport(t, 1, 10, 1) + testSideImport(t, 1, -10, 1) } func TestInsertKnownHeaders(t *testing.T) { testInsertKnownChainData(t, "headers") } @@ -1901,7 +2091,7 @@ func testInsertKnownChainData(t *testing.T, typ string) { (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(chaindb) defer os.RemoveAll(dir) - chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -1995,6 +2185,177 @@ func testInsertKnownChainData(t *testing.T, typ string) { asserter(t, blocks2[len(blocks2)-1]) } +func TestInsertKnownHeadersWithMerging(t *testing.T) { + testInsertKnownChainDataWithMerging(t, "headers", 0) +} +func TestInsertKnownReceiptChainWithMerging(t *testing.T) { + testInsertKnownChainDataWithMerging(t, "receipts", 0) +} +func TestInsertKnownBlocksWithMerging(t *testing.T) { + testInsertKnownChainDataWithMerging(t, "blocks", 0) +} +func TestInsertKnownHeadersAfterMerging(t *testing.T) { + testInsertKnownChainDataWithMerging(t, "headers", 1) +} +func TestInsertKnownReceiptChainAfterMerging(t *testing.T) { + testInsertKnownChainDataWithMerging(t, "receipts", 1) +} +func TestInsertKnownBlocksAfterMerging(t *testing.T) { + testInsertKnownChainDataWithMerging(t, "blocks", 1) +} + +// mergeHeight can be assigned in these values: +// 0: means the merging is applied since genesis +// 1: means the merging is applied after the first segment +func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight int) { + var ( + db = rawdb.NewMemoryDatabase() + genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) + runEngine = beacon.New(ethash.NewFaker(), false) + genEngine = beacon.New(ethash.NewFaker(), false) + ) + applyMerge := func(engine *beacon.Beacon, forker *ForkChoice) { + if engine != nil { + engine.MarkTransitioned() + } + if forker != nil { + forker.MarkTransitioned() + } + } + + // Apply merging since genesis + if mergeHeight == 0 { + applyMerge(genEngine, nil) + } + blocks, receipts := GenerateChain(params.TestChainConfig, genesis, genEngine, db, 32, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) + + // Apply merging after the first segment + if mergeHeight == 1 { + applyMerge(genEngine, nil) + } + // Longer chain and shorter chain + blocks2, receipts2 := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], genEngine, db, 65, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) + blocks3, receipts3 := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], genEngine, db, 64, func(i int, b *BlockGen) { + b.SetCoinbase(common.Address{1}) + b.OffsetTime(-9) // Time shifted, difficulty shouldn't be changed + }) + + // Import the shared chain and the original canonical one + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("failed to create temp freezer dir: %v", err) + } + defer os.Remove(dir) + chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), dir, "", false) + if err != nil { + t.Fatalf("failed to create temp freezer db: %v", err) + } + (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(chaindb) + defer os.RemoveAll(dir) + + chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, runEngine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + var ( + inserter func(blocks []*types.Block, receipts []types.Receipts) error + asserter func(t *testing.T, block *types.Block) + ) + if typ == "headers" { + inserter = func(blocks []*types.Block, receipts []types.Receipts) error { + headers := make([]*types.Header, 0, len(blocks)) + for _, block := range blocks { + headers = append(headers, block.Header()) + } + _, err := chain.InsertHeaderChain(headers, 1) + return err + } + asserter = func(t *testing.T, block *types.Block) { + if chain.CurrentHeader().Hash() != block.Hash() { + t.Fatalf("current head header mismatch, have %v, want %v", chain.CurrentHeader().Hash().Hex(), block.Hash().Hex()) + } + } + } else if typ == "receipts" { + inserter = func(blocks []*types.Block, receipts []types.Receipts) error { + headers := make([]*types.Header, 0, len(blocks)) + for _, block := range blocks { + headers = append(headers, block.Header()) + } + _, err := chain.InsertHeaderChain(headers, 1) + if err != nil { + return err + } + _, err = chain.InsertReceiptChain(blocks, receipts, 0) + return err + } + asserter = func(t *testing.T, block *types.Block) { + if chain.CurrentFastBlock().Hash() != block.Hash() { + t.Fatalf("current head fast block mismatch, have %v, want %v", chain.CurrentFastBlock().Hash().Hex(), block.Hash().Hex()) + } + } + } else { + inserter = func(blocks []*types.Block, receipts []types.Receipts) error { + _, err := chain.InsertChain(blocks) + return err + } + asserter = func(t *testing.T, block *types.Block) { + if chain.CurrentBlock().Hash() != block.Hash() { + t.Fatalf("current head block mismatch, have %v, want %v", chain.CurrentBlock().Hash().Hex(), block.Hash().Hex()) + } + } + } + + // Apply merging since genesis if required + if mergeHeight == 0 { + applyMerge(runEngine, chain.forker) + } + if err := inserter(blocks, receipts); err != nil { + t.Fatalf("failed to insert chain data: %v", err) + } + + // Reimport the chain data again. All the imported + // chain data are regarded "known" data. + if err := inserter(blocks, receipts); err != nil { + t.Fatalf("failed to insert chain data: %v", err) + } + asserter(t, blocks[len(blocks)-1]) + + // Import a long canonical chain with some known data as prefix. + rollback := blocks[len(blocks)/2].NumberU64() + chain.SetHead(rollback - 1) + if err := inserter(blocks, receipts); err != nil { + t.Fatalf("failed to insert chain data: %v", err) + } + asserter(t, blocks[len(blocks)-1]) + + // Apply merging after the first segment + if mergeHeight == 1 { + applyMerge(runEngine, chain.forker) + } + + // Import a longer chain with some known data as prefix. + if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil { + t.Fatalf("failed to insert chain data: %v", err) + } + asserter(t, blocks2[len(blocks2)-1]) + + // Import a shorter chain with some known data as prefix. + // The reorg is expected since the fork choice rule is + // already changed. + if err := inserter(append(blocks, blocks3...), append(receipts, receipts3...)); err != nil { + t.Fatalf("failed to insert chain data: %v", err) + } + // The head shouldn't change. + asserter(t, blocks3[len(blocks3)-1]) + + // Reimport the longer chain again, the reorg is still expected + chain.SetHead(rollback - 1) + if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil { + t.Fatalf("failed to insert chain data: %v", err) + } + asserter(t, blocks2[len(blocks2)-1]) +} + // getLongAndShortChains returns two chains: A is longer, B is heavier. func getLongAndShortChains() (bc *BlockChain, longChain []*types.Block, heavyChain []*types.Block, err error) { // Generate a canonical chain to act as the main dataset @@ -2010,7 +2371,7 @@ func getLongAndShortChains() (bc *BlockChain, longChain []*types.Block, heavyCha diskdb := rawdb.NewMemoryDatabase() (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { return nil, nil, nil, fmt.Errorf("failed to create tester chain: %v", err) } @@ -2188,7 +2549,7 @@ func TestTransactionIndices(t *testing.T) { // Import all blocks into ancient db l := uint64(0) - chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l) + chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2213,7 +2574,7 @@ func TestTransactionIndices(t *testing.T) { t.Fatalf("failed to create temp freezer db: %v", err) } gspec.MustCommit(ancientDb) - chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l) + chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2237,7 +2598,7 @@ func TestTransactionIndices(t *testing.T) { limit = []uint64{0, 64 /* drop stale */, 32 /* shorten history */, 64 /* extend history */, 0 /* restore all */} tails := []uint64{0, 67 /* 130 - 64 + 1 */, 100 /* 131 - 32 + 1 */, 69 /* 132 - 64 + 1 */, 0} for i, l := range limit { - chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l) + chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2315,7 +2676,7 @@ func TestSkipStaleTxIndicesInFastSync(t *testing.T) { // Import all blocks into ancient db, only HEAD-32 indices are kept. l := uint64(32) - chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l) + chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2379,7 +2740,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in diskdb := rawdb.NewMemoryDatabase() gspec.MustCommit(diskdb) - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { b.Fatalf("failed to create tester chain: %v", err) } @@ -2460,8 +2821,9 @@ func TestSideImportPrunedBlocks(t *testing.T) { // Generate and import the canonical chain blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil) diskdb := rawdb.NewMemoryDatabase() + (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2555,7 +2917,7 @@ func TestDeleteCreateRevert(t *testing.T) { diskdb := rawdb.NewMemoryDatabase() gspec.MustCommit(diskdb) - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2669,7 +3031,7 @@ func TestDeleteRecreateSlots(t *testing.T) { chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{ Debug: true, Tracer: vm.NewJSONLogger(nil, os.Stdout), - }, nil, nil) + }, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2749,7 +3111,7 @@ func TestDeleteRecreateAccount(t *testing.T) { chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{ Debug: true, Tracer: vm.NewJSONLogger(nil, os.Stdout), - }, nil, nil) + }, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -2922,7 +3284,7 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) { chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{ //Debug: true, //Tracer: vm.NewJSONLogger(nil, os.Stdout), - }, nil, nil) + }, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -3056,7 +3418,7 @@ func TestInitThenFailCreateContract(t *testing.T) { chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{ //Debug: true, //Tracer: vm.NewJSONLogger(nil, os.Stdout), - }, nil, nil) + }, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -3143,7 +3505,7 @@ func TestEIP2718Transition(t *testing.T) { diskdb := rawdb.NewMemoryDatabase() gspec.MustCommit(diskdb) - chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } @@ -3238,7 +3600,7 @@ func TestEIP1559Transition(t *testing.T) { diskdb := rawdb.NewMemoryDatabase() gspec.MustCommit(diskdb) - chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil) + chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } diff --git a/core/chain_makers.go b/core/chain_makers.go index b113c0d1be9d..5316f0d84e01 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -205,6 +205,12 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine} b.header = makeHeader(chainreader, parent, statedb, b.engine) + // Set the difficulty for clique block. The chain maker doesn't have access + // to a chain, so the difficulty will be lets unset (nil). Set it here to the + // correct value. + if b.header.Difficulty == nil { + b.header.Difficulty = big.NewInt(2) + } // Mutate the state and block according to any hard-fork specs if daoBlock := config.DAOForkBlock; daoBlock != nil { limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index 85a029f7c757..79c8314d61a8 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -79,7 +79,7 @@ func ExampleGenerateChain() { }) // Import the chain. This runs all block validation rules. - blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer blockchain.Stop() if i, err := blockchain.InsertChain(chain); err != nil { diff --git a/core/dao_test.go b/core/dao_test.go index c9c765a3832a..5b5cf18579b5 100644 --- a/core/dao_test.go +++ b/core/dao_test.go @@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { proConf.DAOForkBlock = forkBlock proConf.DAOForkSupport = true - proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil) + proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer proBc.Stop() conDb := rawdb.NewMemoryDatabase() @@ -55,7 +55,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { conConf.DAOForkBlock = forkBlock conConf.DAOForkSupport = false - conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil) + conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer conBc.Stop() if _, err := proBc.InsertChain(prefix); err != nil { @@ -69,7 +69,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { // Create a pro-fork block, and try to feed into the no-fork chain db = rawdb.NewMemoryDatabase() gspec.MustCommit(db) - bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil) + bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer bc.Stop() blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) @@ -94,7 +94,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { // Create a no-fork block, and try to feed into the pro-fork chain db = rawdb.NewMemoryDatabase() gspec.MustCommit(db) - bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil) + bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer bc.Stop() blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) @@ -120,7 +120,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { // Verify that contra-forkers accept pro-fork extra-datas after forking finishes db = rawdb.NewMemoryDatabase() gspec.MustCommit(db) - bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil) + bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer bc.Stop() blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) @@ -140,7 +140,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { // Verify that pro-forkers accept contra-fork extra-datas after forking finishes db = rawdb.NewMemoryDatabase() gspec.MustCommit(db) - bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil) + bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer bc.Stop() blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) diff --git a/core/forkchoice.go b/core/forkchoice.go new file mode 100644 index 000000000000..9bc1f79facef --- /dev/null +++ b/core/forkchoice.go @@ -0,0 +1,123 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + crand "crypto/rand" + "errors" + "math/big" + mrand "math/rand" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" +) + +// ChainReader defines a small collection of methods needed to access the local +// blockchain during header verification. It's implemented by both blockchain +// and lightchain. +type ChainReader interface { + // GetTd returns the total difficulty of a local block. + GetTd(common.Hash, uint64) *big.Int +} + +// ForkChoice is the fork chooser based on the highest total difficulty of the +// chain(the fork choice used in the eth1) and the external fork choice (the fork +// choice used in the eth2). This main goal of this ForkChoice is not only for +// offering fork choice during the eth1/2 merge phase, but also keep the compatibility +// for all other proof-of-work networks. +type ForkChoice struct { + chain ChainReader + rand *mrand.Rand + + // transitioned is the flag whether the chain has started(or finished) + // the transition. It's triggered by receiving the first "NewHead" message + // from the external consensus engine. + transitioned bool + lock sync.RWMutex + + // preserve is a helper function used in td fork choice. + // Miners will prefer to choose the local mined block if the + // local td is equal to the extern one. It can nil for light + // client + preserve func(header *types.Header) bool +} + +func NewForkChoice(chainReader ChainReader, transitioned bool, preserve func(header *types.Header) bool) *ForkChoice { + // Seed a fast but crypto originating random generator + seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + log.Crit("Failed to initialize random seed", "err", err) + } + return &ForkChoice{ + chain: chainReader, + rand: mrand.New(mrand.NewSource(seed.Int64())), + transitioned: transitioned, + preserve: preserve, + } +} + +// Reorg returns the result whether the reorg should be applied +// based on the given external header and local canonical chain. +// In the td mode, the new head is chosen if the corresponding +// total difficulty is higher. In the extern mode, the trusted +// header is always selected as the head. +func (f *ForkChoice) Reorg(current *types.Header, header *types.Header) (bool, error) { + f.lock.RLock() + defer f.lock.RUnlock() + + // Accept the new header as the chain head if the transition + // is already triggered. We assume all the headers after the + // transition come from the trusted consensus layer. + if f.transitioned { + return true, nil + } + var ( + localTD = f.chain.GetTd(current.Hash(), current.Number.Uint64()) + externTd = f.chain.GetTd(header.Hash(), header.Number.Uint64()) + ) + if localTD == nil || externTd == nil { + return false, errors.New("missing td") + } + // If the total difficulty is higher than our known, add it to the canonical chain + // Second clause in the if statement reduces the vulnerability to selfish mining. + // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf + reorg := externTd.Cmp(localTD) > 0 + if !reorg && externTd.Cmp(localTD) == 0 { + number, headNumber := header.Number.Uint64(), current.Number.Uint64() + if number < headNumber { + reorg = true + } else if number == headNumber { + var currentPreserve, externPreserve bool + if f.preserve != nil { + currentPreserve, externPreserve = f.preserve(current), f.preserve(header) + } + reorg = !currentPreserve && (externPreserve || f.rand.Float64() < 0.5) + } + } + return reorg, nil +} + +// MarkTransitioned marks the transition has started. +func (f *ForkChoice) MarkTransitioned() { + f.lock.Lock() + defer f.lock.Unlock() + + f.transitioned = true +} diff --git a/core/genesis_test.go b/core/genesis_test.go index 055be2796c39..b9b166bd3b1a 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -136,7 +136,7 @@ func TestSetupGenesis(t *testing.T) { // Advance to block #4, past the homestead transition block of customg. genesis := oldcustomg.MustCommit(db) - bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil) + bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) defer bc.Stop() blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil) diff --git a/core/headerchain.go b/core/headerchain.go index 7ef7dd43f704..c4b383e51a4c 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -49,15 +49,14 @@ const ( // HeaderChain is responsible for maintaining the header chain including the // header query and updating. // -// The components maintained by headerchain includes: (1) total difficult +// The components maintained by headerchain includes: (1) total difficulty // (2) header (3) block hash -> number mapping (4) canonical number -> hash mapping // and (5) head header flag. // // It is not thread safe either, the encapsulating chain structures should do // the necessary mutex locking/unlocking. type HeaderChain struct { - config *params.ChainConfig - + config *params.ChainConfig chainDb ethdb.Database genesisHeader *types.Header @@ -86,7 +85,6 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c if err != nil { return nil, err } - hc := &HeaderChain{ config: config, chainDb: chainDb, @@ -97,12 +95,10 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c rand: mrand.New(mrand.NewSource(seed.Int64())), engine: engine, } - hc.genesisHeader = hc.GetHeaderByNumber(0) if hc.genesisHeader == nil { return nil, ErrNoGenesis } - hc.currentHeader.Store(hc.genesisHeader) if head := rawdb.ReadHeadBlockHash(chainDb); head != (common.Hash{}) { if chead := hc.GetHeaderByHash(head); chead != nil { @@ -111,7 +107,6 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c } hc.currentHeaderHash = hc.CurrentHeader().Hash() headHeaderGauge.Update(hc.CurrentHeader().Number.Int64()) - return hc, nil } @@ -137,35 +132,87 @@ type headerWriteResult struct { lastHeader *types.Header } -// WriteHeaders writes a chain of headers into the local chain, given that the parents -// are already known. If the total difficulty of the newly inserted chain becomes -// greater than the current known TD, the canonical chain is reorged. -// -// Note: This method is not concurrent-safe with inserting blocks simultaneously -// into the chain, as side effects caused by reorganisations cannot be emulated -// without the real blocks. Hence, writing headers directly should only be done -// in two scenarios: pure-header mode of operation (light clients), or properly -// separated header/block phases (non-archive clients). -func (hc *HeaderChain) writeHeaders(headers []*types.Header) (result *headerWriteResult, err error) { +// Reorg reorgs the local canonical chain into the specified chain. The reorg +// can be classified into two cases: (a) extend the local chain (b) switch the +// head to the given header. +func (hc *HeaderChain) Reorg(headers []*types.Header) error { + // Short circuit if nothing to reorg. + if len(headers) == 0 { + return nil + } + // If the parent of the (first) block is already the canon header, + // we don't have to go backwards to delete canon blocks, but simply + // pile them onto the existing chain. Otherwise, do the necessary + // reorgs. + var ( + first = headers[0] + last = headers[len(headers)-1] + batch = hc.chainDb.NewBatch() + ) + if first.ParentHash != hc.currentHeaderHash { + // Delete any canonical number assignments above the new head + for i := last.Number.Uint64() + 1; ; i++ { + hash := rawdb.ReadCanonicalHash(hc.chainDb, i) + if hash == (common.Hash{}) { + break + } + rawdb.DeleteCanonicalHash(batch, i) + } + // Overwrite any stale canonical number assignments, going + // backwards from the first header in this import until the + // cross link between two chains. + var ( + header = first + headNumber = header.Number.Uint64() + headHash = header.Hash() + ) + for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash { + rawdb.WriteCanonicalHash(batch, headHash, headNumber) + if headNumber == 0 { + break // It shouldn't be reached + } + headHash, headNumber = header.ParentHash, header.Number.Uint64()-1 + header = hc.GetHeader(headHash, headNumber) + if header == nil { + return fmt.Errorf("missing parent %d %x", headNumber, headHash) + } + } + } + // Extend the canonical chain with the new headers + for i := 0; i < len(headers); i++ { + hash := headers[i].Hash() + num := headers[i].Number.Uint64() + rawdb.WriteCanonicalHash(batch, hash, num) + rawdb.WriteHeadHeaderHash(batch, hash) + } + if err := batch.Write(); err != nil { + return err + } + // Last step update all in-memory head header markers + hc.currentHeaderHash = last.Hash() + hc.currentHeader.Store(types.CopyHeader(last)) + headHeaderGauge.Update(last.Number.Int64()) + return nil +} + +// WriteHeaders writes a chain of headers into the local chain, given that the +// parents are already known. The chain head header won't be updated in this +// function, the addtional setChainHead is expected in order to finish the entire +// procedure. +func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) { if len(headers) == 0 { - return &headerWriteResult{}, nil + return 0, nil } ptd := hc.GetTd(headers[0].ParentHash, headers[0].Number.Uint64()-1) if ptd == nil { - return &headerWriteResult{}, consensus.ErrUnknownAncestor + return 0, consensus.ErrUnknownAncestor } var ( - lastNumber = headers[0].Number.Uint64() - 1 // Last successfully imported number - lastHash = headers[0].ParentHash // Last imported header hash - newTD = new(big.Int).Set(ptd) // Total difficulty of inserted chain - - lastHeader *types.Header - inserted []numberHash // Ephemeral lookup of number/hash for the chain - firstInserted = -1 // Index of the first non-ignored header + newTD = new(big.Int).Set(ptd) // Total difficulty of inserted chain + inserted []numberHash // Ephemeral lookup of number/hash for the chain + parentKnown = true // Set to true to force hc.HasHeader check the first iteration + batch = hc.chainDb.NewBatch() ) - - batch := hc.chainDb.NewBatch() - parentKnown := true // Set to true to force hc.HasHeader check the first iteration for i, header := range headers { var hash common.Hash // The headers have already been validated at this point, so we already @@ -191,113 +238,64 @@ func (hc *HeaderChain) writeHeaders(headers []*types.Header) (result *headerWrit inserted = append(inserted, numberHash{number, hash}) hc.headerCache.Add(hash, header) hc.numberCache.Add(hash, number) - if firstInserted < 0 { - firstInserted = i - } } parentKnown = alreadyKnown - lastHeader, lastHash, lastNumber = header, hash, number } - // Skip the slow disk write of all headers if interrupted. if hc.procInterrupt() { log.Debug("Premature abort during headers import") - return &headerWriteResult{}, errors.New("aborted") + return 0, errors.New("aborted") } // Commit to disk! if err := batch.Write(); err != nil { log.Crit("Failed to write headers", "error", err) } - batch.Reset() + return len(inserted), nil +} +// writeHeadersAndSetHead writes a batch of block headers and applies the last +// header as the chain head if the fork choicer says it's ok to update the chain. +// Note: This method is not concurrent-safe with inserting blocks simultaneously +// into the chain, as side effects caused by reorganisations cannot be emulated +// without the real blocks. Hence, writing headers directly should only be done +// in two scenarios: pure-header mode of operation (light clients), or properly +// separated header/block phases (non-archive clients). +func (hc *HeaderChain) writeHeadersAndSetHead(headers []*types.Header, forker *ForkChoice) (*headerWriteResult, error) { + inserted, err := hc.WriteHeaders(headers) + if err != nil { + return nil, err + } var ( - head = hc.CurrentHeader().Number.Uint64() - localTD = hc.GetTd(hc.currentHeaderHash, head) - status = SideStatTy + lastHeader = headers[len(headers)-1] + lastHash = headers[len(headers)-1].Hash() + result = &headerWriteResult{ + status: NonStatTy, + ignored: len(headers) - inserted, + imported: inserted, + lastHash: lastHash, + lastHeader: lastHeader, + } ) - // If the total difficulty is higher than our known, add it to the canonical chain - // Second clause in the if statement reduces the vulnerability to selfish mining. - // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf - reorg := newTD.Cmp(localTD) > 0 - if !reorg && newTD.Cmp(localTD) == 0 { - if lastNumber < head { - reorg = true - } else if lastNumber == head { - reorg = mrand.Float64() < 0.5 + // Ask the fork choicer if the reorg is necessary + if reorg, err := forker.Reorg(hc.CurrentHeader(), lastHeader); err != nil { + return nil, err + } else if !reorg { + if inserted != 0 { + result.status = SideStatTy } + return result, nil } - // If the parent of the (first) block is already the canon header, - // we don't have to go backwards to delete canon blocks, but - // simply pile them onto the existing chain - chainAlreadyCanon := headers[0].ParentHash == hc.currentHeaderHash - if reorg { - // If the header can be added into canonical chain, adjust the - // header chain markers(canonical indexes and head header flag). - // - // Note all markers should be written atomically. - markerBatch := batch // we can reuse the batch to keep allocs down - if !chainAlreadyCanon { - // Delete any canonical number assignments above the new head - for i := lastNumber + 1; ; i++ { - hash := rawdb.ReadCanonicalHash(hc.chainDb, i) - if hash == (common.Hash{}) { - break - } - rawdb.DeleteCanonicalHash(markerBatch, i) - } - // Overwrite any stale canonical number assignments, going - // backwards from the first header in this import - var ( - headHash = headers[0].ParentHash // inserted[0].parent? - headNumber = headers[0].Number.Uint64() - 1 // inserted[0].num-1 ? - headHeader = hc.GetHeader(headHash, headNumber) - ) - for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash { - rawdb.WriteCanonicalHash(markerBatch, headHash, headNumber) - headHash = headHeader.ParentHash - headNumber = headHeader.Number.Uint64() - 1 - headHeader = hc.GetHeader(headHash, headNumber) - } - // If some of the older headers were already known, but obtained canon-status - // during this import batch, then we need to write that now - // Further down, we continue writing the staus for the ones that - // were not already known - for i := 0; i < firstInserted; i++ { - hash := headers[i].Hash() - num := headers[i].Number.Uint64() - rawdb.WriteCanonicalHash(markerBatch, hash, num) - rawdb.WriteHeadHeaderHash(markerBatch, hash) - } - } - // Extend the canonical chain with the new headers - for _, hn := range inserted { - rawdb.WriteCanonicalHash(markerBatch, hn.hash, hn.number) - rawdb.WriteHeadHeaderHash(markerBatch, hn.hash) - } - if err := markerBatch.Write(); err != nil { - log.Crit("Failed to write header markers into disk", "err", err) - } - markerBatch.Reset() - // Last step update all in-memory head header markers - hc.currentHeaderHash = lastHash - hc.currentHeader.Store(types.CopyHeader(lastHeader)) - headHeaderGauge.Update(lastHeader.Number.Int64()) - - // Chain status is canonical since this insert was a reorg. - // Note that all inserts which have higher TD than existing are 'reorg'. - status = CanonStatTy - } - - if len(inserted) == 0 { - status = NonStatTy - } - return &headerWriteResult{ - status: status, - ignored: len(headers) - len(inserted), - imported: len(inserted), - lastHash: lastHash, - lastHeader: lastHeader, - }, nil + // Special case, all the inserted headers are already on the canonical + // header chain, skip the reorg operation. + if hc.GetCanonicalHash(lastHeader.Number.Uint64()) == lastHash && lastHeader.Number.Uint64() <= hc.CurrentHeader().Number.Uint64() { + return result, nil + } + // Apply the reorg operation + if err := hc.Reorg(headers); err != nil { + return nil, err + } + result.status = CanonStatTy + return result, nil } func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) (int, error) { @@ -357,7 +355,7 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) return 0, nil } -// InsertHeaderChain inserts the given headers. +// InsertHeaderChain inserts the given headers and does the reorganisations. // // The validity of the headers is NOT CHECKED by this method, i.e. they need to be // validated by ValidateHeaderChain before calling InsertHeaderChain. @@ -367,20 +365,19 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) // // The returned 'write status' says if the inserted headers are part of the canonical chain // or a side chain. -func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, start time.Time) (WriteStatus, error) { +func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, start time.Time, forker *ForkChoice) (WriteStatus, error) { if hc.procInterrupt() { return 0, errors.New("aborted") } - res, err := hc.writeHeaders(chain) - + res, err := hc.writeHeadersAndSetHead(chain, forker) + if err != nil { + return 0, err + } // Report some public statistics so the user has a clue what's going on context := []interface{}{ "count", res.imported, "elapsed", common.PrettyDuration(time.Since(start)), } - if err != nil { - context = append(context, "err", err) - } if last := res.lastHeader; last != nil { context = append(context, "number", last.Number, "hash", res.lastHash) if timestamp := time.Unix(int64(last.Time), 0); time.Since(timestamp) > time.Minute { diff --git a/core/headerchain_test.go b/core/headerchain_test.go index f3e40b6213f3..5371ff98642d 100644 --- a/core/headerchain_test.go +++ b/core/headerchain_test.go @@ -51,10 +51,10 @@ func verifyUnbrokenCanonchain(hc *HeaderChain) error { return nil } -func testInsert(t *testing.T, hc *HeaderChain, chain []*types.Header, wantStatus WriteStatus, wantErr error) { +func testInsert(t *testing.T, hc *HeaderChain, chain []*types.Header, wantStatus WriteStatus, wantErr error, forker *ForkChoice) { t.Helper() - status, err := hc.InsertHeaderChain(chain, time.Now()) + status, err := hc.InsertHeaderChain(chain, time.Now(), forker) if status != wantStatus { t.Errorf("wrong write status from InsertHeaderChain: got %v, want %v", status, wantStatus) } @@ -80,37 +80,38 @@ func TestHeaderInsertion(t *testing.T) { } // chain A: G->A1->A2...A128 chainA := makeHeaderChain(genesis.Header(), 128, ethash.NewFaker(), db, 10) - // chain B: G->A1->B2...B128 + // chain B: G->A1->B1...B128 chainB := makeHeaderChain(chainA[0], 128, ethash.NewFaker(), db, 10) log.Root().SetHandler(log.StdoutHandler) + forker := NewForkChoice(hc, false, nil) // Inserting 64 headers on an empty chain, expecting // 1 callbacks, 1 canon-status, 0 sidestatus, - testInsert(t, hc, chainA[:64], CanonStatTy, nil) + testInsert(t, hc, chainA[:64], CanonStatTy, nil, forker) // Inserting 64 identical headers, expecting // 0 callbacks, 0 canon-status, 0 sidestatus, - testInsert(t, hc, chainA[:64], NonStatTy, nil) + testInsert(t, hc, chainA[:64], NonStatTy, nil, forker) // Inserting the same some old, some new headers // 1 callbacks, 1 canon, 0 side - testInsert(t, hc, chainA[32:96], CanonStatTy, nil) + testInsert(t, hc, chainA[32:96], CanonStatTy, nil, forker) // Inserting side blocks, but not overtaking the canon chain - testInsert(t, hc, chainB[0:32], SideStatTy, nil) + testInsert(t, hc, chainB[0:32], SideStatTy, nil, forker) // Inserting more side blocks, but we don't have the parent - testInsert(t, hc, chainB[34:36], NonStatTy, consensus.ErrUnknownAncestor) + testInsert(t, hc, chainB[34:36], NonStatTy, consensus.ErrUnknownAncestor, forker) // Inserting more sideblocks, overtaking the canon chain - testInsert(t, hc, chainB[32:97], CanonStatTy, nil) + testInsert(t, hc, chainB[32:97], CanonStatTy, nil, forker) // Inserting more A-headers, taking back the canonicality - testInsert(t, hc, chainA[90:100], CanonStatTy, nil) + testInsert(t, hc, chainA[90:100], CanonStatTy, nil, forker) // And B becomes canon again - testInsert(t, hc, chainB[97:107], CanonStatTy, nil) + testInsert(t, hc, chainB[97:107], CanonStatTy, nil, forker) // And B becomes even longer - testInsert(t, hc, chainB[107:128], CanonStatTy, nil) + testInsert(t, hc, chainB[107:128], CanonStatTy, nil, forker) } diff --git a/core/merger.go b/core/merger.go new file mode 100644 index 000000000000..c6e2f0eceb89 --- /dev/null +++ b/core/merger.go @@ -0,0 +1,134 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "sync" + + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" +) + +// transitionStatus describes the status of eth1/2 transition. This switch +// between modes is a one-way action which is triggered by corresponding +// consensus-layer message. +type transitionStatus struct { + LeftPoW bool // The flag is set when the first NewHead message received + EnteredPoS bool // The flag is set when the first FinalisedBlock message received +} + +// Merger is an internal help structure used to track the eth1/2 transition status. +// It's a common structure can be used in both full node and light client. +type Merger struct { + db ethdb.KeyValueStore + status transitionStatus + leavePoWCalls []func() + enterPoSCalls []func() + lock sync.Mutex +} + +func NewMerger(db ethdb.KeyValueStore) *Merger { + var status transitionStatus + blob := rawdb.ReadTransitionStatus(db) + if len(blob) != 0 { + if err := rlp.DecodeBytes(blob, &status); err != nil { + log.Crit("Failed to decode the transition status", "err", err) + } + } + return &Merger{ + db: db, + status: status, + } +} + +// SubscribeLeavePoW registers callback so that if the chain leaves +// from the PoW stage and enters to 'transition' stage it can be invoked. +func (m *Merger) SubscribeLeavePoW(callback func()) { + m.lock.Lock() + defer m.lock.Unlock() + + m.leavePoWCalls = append(m.leavePoWCalls, callback) +} + +// SubscribeEnterPoS registers callback so that if the chain leaves +// from the 'transition' stage and enters the PoS stage it can be invoked. +func (m *Merger) SubscribeEnterPoS(callback func()) { + m.lock.Lock() + defer m.lock.Unlock() + + m.enterPoSCalls = append(m.enterPoSCalls, callback) +} + +// LeavePoW is called whenever the first NewHead message received +// from the consensus-layer. +func (m *Merger) LeavePoW() { + m.lock.Lock() + defer m.lock.Unlock() + + if m.status.LeftPoW { + return + } + m.status = transitionStatus{LeftPoW: true} + blob, err := rlp.EncodeToBytes(m.status) + if err != nil { + log.Crit("Failed to encode the transition status", "err", err) + } + rawdb.WriteTransitionStatus(m.db, blob) + for _, call := range m.leavePoWCalls { + call() + } + log.Info("Left PoW stage") +} + +// EnterPoS is called whenever the first FinalisedBlock message received +// from the consensus-layer. +func (m *Merger) EnterPoS() { + m.lock.Lock() + defer m.lock.Unlock() + + if m.status.EnteredPoS { + return + } + m.status = transitionStatus{LeftPoW: true, EnteredPoS: true} + blob, err := rlp.EncodeToBytes(m.status) + if err != nil { + log.Crit("Failed to encode the transition status", "err", err) + } + rawdb.WriteTransitionStatus(m.db, blob) + for _, call := range m.enterPoSCalls { + call() + } + log.Info("Entered PoS stage") +} + +// LeftPoW reports whether the chain has left the PoW stage. +func (m *Merger) LeftPoW() bool { + m.lock.Lock() + defer m.lock.Unlock() + + return m.status.LeftPoW +} + +// EnteredPoS reports whether the chain has entered the PoS stage. +func (m *Merger) EnteredPoS() bool { + m.lock.Lock() + defer m.lock.Unlock() + + return m.status.EnteredPoS +} diff --git a/core/rawdb/accessors_metadata.go b/core/rawdb/accessors_metadata.go index 079e335fa6fb..4c72ca714838 100644 --- a/core/rawdb/accessors_metadata.go +++ b/core/rawdb/accessors_metadata.go @@ -138,3 +138,16 @@ func PopUncleanShutdownMarker(db ethdb.KeyValueStore) { log.Warn("Failed to clear unclean-shutdown marker", "err", err) } } + +// ReadTransitionStatus retrieves the eth2 transition status from the database +func ReadTransitionStatus(db ethdb.KeyValueReader) []byte { + data, _ := db.Get(transitionStatusKey) + return data +} + +// WriteTransitionStatus stores the eth2 transition status to the database +func WriteTransitionStatus(db ethdb.KeyValueWriter, data []byte) { + if err := db.Put(transitionStatusKey, data); err != nil { + log.Crit("Failed to store the eth2 transition status", "err", err) + } +} diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 0e116ef999b9..8e538ed0f7f8 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -379,7 +379,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, snapshotRootKey, snapshotJournalKey, snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey, - uncleanShutdownKey, badBlockKey, + uncleanShutdownKey, badBlockKey, transitionStatusKey, } { if bytes.Equal(key, meta) { metadata.Add(size) diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 2505ce90b947..9fd6d39b0023 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -75,6 +75,9 @@ var ( // uncleanShutdownKey tracks the list of local crashes uncleanShutdownKey = []byte("unclean-shutdown") // config prefix for the db + // transitionStatusKey tracks the eth2 transition status. + transitionStatusKey = []byte("eth2-transition") + // Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes). headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 13a9eb810df6..621879bf5150 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -85,7 +85,7 @@ func TestStateProcessorErrors(t *testing.T) { }, } genesis = gspec.MustCommit(db) - blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) ) defer blockchain.Stop() bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) @@ -220,7 +220,7 @@ func TestStateProcessorErrors(t *testing.T) { }, } genesis = gspec.MustCommit(db) - blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, NewMerger(rawdb.NewMemoryDatabase())) ) defer blockchain.Stop() for i, tt := range []struct { diff --git a/eth/api_backend.go b/eth/api_backend.go index 1af33414cda4..3f716cf32813 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -348,7 +348,7 @@ func (b *EthAPIBackend) StartMining(threads int) error { } func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) { - return b.eth.stateAtBlock(block, reexec, base, checkLive) + return b.eth.StateAtBlock(block, reexec, base, checkLive) } func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { diff --git a/eth/backend.go b/eth/backend.go index 793d3b81f1b1..deee71626662 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -71,6 +71,7 @@ type Ethereum struct { handler *handler ethDialCandidates enode.Iterator snapDialCandidates enode.Iterator + merger *core.Merger // DB interfaces chainDb ethdb.Database // Block chain database @@ -140,12 +141,14 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb, stack.ResolvePath(config.TrieCleanCacheJournal)); err != nil { log.Error("Failed to recover state", "error", err) } + merger := core.NewMerger(chainDb) eth := &Ethereum{ config: config, + merger: merger, chainDb: chainDb, eventMux: stack.EventMux(), accountManager: stack.AccountManager(), - engine: ethconfig.CreateConsensusEngine(stack, chainConfig, ðashConfig, config.Miner.Notify, config.Miner.Noverify, chainDb), + engine: ethconfig.CreateConsensusEngine(stack, chainConfig, ðashConfig, config.Miner.Notify, config.Miner.Noverify, chainDb, merger), closeBloomHandler: make(chan struct{}), networkID: config.NetworkId, gasPrice: config.Miner.GasPrice, @@ -188,7 +191,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { Preimages: config.Preimages, } ) - eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit) + eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, merger) if err != nil { return nil, err } @@ -215,6 +218,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { Database: chainDb, Chain: eth.blockchain, TxPool: eth.txPool, + Merger: merger, Network: config.NetworkId, Sync: config.SyncMode, BloomCache: uint64(cacheLimit), @@ -225,7 +229,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { return nil, err } - eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock) + eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock, merger) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil} @@ -256,6 +260,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { stack.RegisterAPIs(eth.APIs()) stack.RegisterProtocols(eth.Protocols()) stack.RegisterLifecycle(eth) + // Check for unclean shutdown if uncleanShutdowns, discards, err := rawdb.PushUncleanShutdownMarker(chainDb); err != nil { log.Error("Could not update unclean-shutdown-marker list", "error", err) @@ -378,10 +383,10 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) { // // We regard two types of accounts as local miner account: etherbase // and accounts specified via `txpool.locals` flag. -func (s *Ethereum) isLocalBlock(block *types.Block) bool { - author, err := s.engine.Author(block.Header()) +func (s *Ethereum) isLocalBlock(header *types.Header) bool { + author, err := s.engine.Author(header) if err != nil { - log.Warn("Failed to retrieve block author", "number", block.NumberU64(), "hash", block.Hash(), "err", err) + log.Warn("Failed to retrieve block author", "number", header.Number.Uint64(), "hash", header.Hash(), "err", err) return false } // Check whether the given address is etherbase. @@ -404,7 +409,7 @@ func (s *Ethereum) isLocalBlock(block *types.Block) bool { // shouldPreserve checks whether we should preserve the given block // during the chain reorg depending on whether the author of block // is a local account. -func (s *Ethereum) shouldPreserve(block *types.Block) bool { +func (s *Ethereum) shouldPreserve(header *types.Header) bool { // The reason we need to disable the self-reorg preserving for clique // is it can be probable to introduce a deadlock. // @@ -424,7 +429,7 @@ func (s *Ethereum) shouldPreserve(block *types.Block) bool { if _, ok := s.engine.(*clique.Clique); ok { return false } - return s.isLocalBlock(block) + return s.isLocalBlock(header) } // SetEtherbase sets the mining reward address. @@ -508,8 +513,10 @@ func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } func (s *Ethereum) IsListening() bool { return true } // Always listening func (s *Ethereum) Downloader() *downloader.Downloader { return s.handler.downloader } func (s *Ethereum) Synced() bool { return atomic.LoadUint32(&s.handler.acceptTxs) == 1 } +func (s *Ethereum) SetSynced() { atomic.StoreUint32(&s.handler.acceptTxs, 1) } func (s *Ethereum) ArchiveMode() bool { return s.config.NoPruning } func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer } +func (s *Ethereum) Merger() *core.Merger { return s.merger } // Protocols returns all the currently configured // network protocols to start. diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 2622c4a148f6..e74376418ef7 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -24,11 +24,14 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" chainParams "github.com/ethereum/go-ethereum/params" @@ -36,33 +39,64 @@ import ( "github.com/ethereum/go-ethereum/trie" ) -// Register adds catalyst APIs to the node. +// Register adds catalyst APIs to the full node. func Register(stack *node.Node, backend *eth.Ethereum) error { - chainconfig := backend.BlockChain().Config() - if chainconfig.CatalystBlock == nil { - return errors.New("catalystBlock is not set in genesis config") - } else if chainconfig.CatalystBlock.Sign() != 0 { - return errors.New("catalystBlock of genesis config must be zero") - } + log.Warn("Catalyst mode enabled", "protocol", "eth") + stack.RegisterAPIs([]rpc.API{ + { + Namespace: "consensus", + Version: "1.0", + Service: NewConsensusAPI(backend, nil), + Public: true, + }, + }) + return nil +} - log.Warn("Catalyst mode enabled") +// RegisterLight adds catalyst APIs to the light client. +func RegisterLight(stack *node.Node, backend *les.LightEthereum) error { + log.Warn("Catalyst mode enabled", "protocol", "les") stack.RegisterAPIs([]rpc.API{ { Namespace: "consensus", Version: "1.0", - Service: newConsensusAPI(backend), + Service: NewConsensusAPI(nil, backend), Public: true, }, }) return nil } -type consensusAPI struct { - eth *eth.Ethereum +type ConsensusAPI struct { + light bool + eth *eth.Ethereum + les *les.LightEthereum + engine consensus.Engine // engine is the post-merge consensus engine, only for block creation + syncer *syncer // syncer is responsible for triggering chain sync } -func newConsensusAPI(eth *eth.Ethereum) *consensusAPI { - return &consensusAPI{eth: eth} +func NewConsensusAPI(eth *eth.Ethereum, les *les.LightEthereum) *ConsensusAPI { + var engine consensus.Engine + if eth == nil { + if b, ok := les.Engine().(*beacon.Beacon); ok { + engine = beacon.New(b.InnerEngine(), true) + } else { + engine = beacon.New(les.Engine(), true) + } + } else { + if b, ok := eth.Engine().(*beacon.Beacon); ok { + engine = beacon.New(b.InnerEngine(), true) + } else { + engine = beacon.New(eth.Engine(), true) + } + } + return &ConsensusAPI{ + light: eth == nil, + eth: eth, + les: les, + engine: engine, + syncer: newSyncer(), + } } // blockExecutionEnv gathers all the data required to execute @@ -91,8 +125,24 @@ func (env *blockExecutionEnv) commitTransaction(tx *types.Transaction, coinbase return nil } -func (api *consensusAPI) makeEnv(parent *types.Block, header *types.Header) (*blockExecutionEnv, error) { - state, err := api.eth.BlockChain().StateAt(parent.Root()) +func (api *ConsensusAPI) makeEnv(parent *types.Block, header *types.Header) (*blockExecutionEnv, error) { + // The parent state might be missing. It can be the special scenario + // that consensus layer tries to build a new block based on the very + // old side chain block and the relevant state is already pruned. So + // try to retrieve the live state from the chain, if it's not existent, + // do the necessary recovery work. + var ( + err error + state *state.StateDB + ) + if api.eth.BlockChain().HasState(parent.Root()) { + state, err = api.eth.BlockChain().StateAt(parent.Root()) + } else { + // The maximum acceptable reorg depth can be limited by the + // finalised block somehow. TODO(rjl493456442) fix the hard- + // coded number here later. + state, err = api.eth.StateAtBlock(parent, 1000, nil, false) + } if err != nil { return nil, err } @@ -107,7 +157,10 @@ func (api *consensusAPI) makeEnv(parent *types.Block, header *types.Header) (*bl // AssembleBlock creates a new block, inserts it into the chain, and returns the "execution // data" required for eth2 clients to process the new block. -func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableData, error) { +func (api *ConsensusAPI) AssembleBlock(params AssembleBlockParams) (*ExecutableData, error) { + if api.light { + return nil, errors.New("not supported") + } log.Info("Producing block", "parentHash", params.ParentHash) bc := api.eth.BlockChain() @@ -117,8 +170,6 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD return nil, fmt.Errorf("cannot assemble block with unknown parent %s", params.ParentHash) } - pool := api.eth.TxPool() - if parent.Time() >= params.Timestamp { return nil, fmt.Errorf("child timestamp lower than parent's: %d >= %d", parent.Time(), params.Timestamp) } @@ -127,12 +178,10 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD log.Info("Producing block too far in the future", "wait", common.PrettyDuration(wait)) time.Sleep(wait) } - - pending, err := pool.Pending(true) + pending, err := api.eth.TxPool().Pending(true) if err != nil { return nil, err } - coinbase, err := api.eth.Etherbase() if err != nil { return nil, err @@ -149,16 +198,14 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD if config := api.eth.BlockChain().Config(); config.IsLondon(header.Number) { header.BaseFee = misc.CalcBaseFee(config, parent.Header()) } - err = api.eth.Engine().Prepare(bc, header) + err = api.engine.Prepare(bc, header) if err != nil { return nil, err } - env, err := api.makeEnv(parent, header) if err != nil { return nil, err } - var ( signer = types.MakeSigner(bc.Config(), header.Number) txHeap = types.NewTransactionsByPriceAndNonce(signer, pending, nil) @@ -209,13 +256,12 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD txHeap.Shift() } } - // Create the block. - block, err := api.eth.Engine().FinalizeAndAssemble(bc, header, env.state, transactions, nil /* uncles */, env.receipts) + block, err := api.engine.FinalizeAndAssemble(bc, header, env.state, transactions, nil /* uncles */, env.receipts) if err != nil { return nil, err } - return &executableData{ + return &ExecutableData{ BlockHash: block.Hash(), ParentHash: block.ParentHash(), Miner: block.Coinbase(), @@ -250,12 +296,11 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { return txs, nil } -func insertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Header, params executableData) (*types.Block, error) { +func InsertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Header, params ExecutableData) (*types.Block, error) { txs, err := decodeTransactions(params.Transactions) if err != nil { return nil, err } - number := big.NewInt(0) number.SetUint64(params.Number) header := &types.Header{ @@ -276,27 +321,42 @@ func insertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Hea header.BaseFee = misc.CalcBaseFee(config, parent) } block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) + if block.Hash() != params.BlockHash { + return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash()) + } return block, nil } // NewBlock creates an Eth1 block, inserts it in the chain, and either returns true, // or false + an error. This is a bit redundant for go, but simplifies things on the // eth2 side. -func (api *consensusAPI) NewBlock(params executableData) (*newBlockResponse, error) { +func (api *ConsensusAPI) NewBlock(params ExecutableData) (*NewBlockResponse, error) { + if api.light { + parent := api.les.BlockChain().GetHeaderByHash(params.ParentHash) + if parent == nil { + return &NewBlockResponse{false}, fmt.Errorf("could not find parent %x", params.ParentHash) + } + block, err := InsertBlockParamsToBlock(api.les.BlockChain().Config(), parent, params) + if err != nil { + return nil, err + } + err = api.les.BlockChain().InsertHeader(block.Header()) + return &NewBlockResponse{err == nil}, err + } parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash) if parent == nil { - return &newBlockResponse{false}, fmt.Errorf("could not find parent %x", params.ParentHash) + return &NewBlockResponse{false}, fmt.Errorf("could not find parent %x", params.ParentHash) } - block, err := insertBlockParamsToBlock(api.eth.BlockChain().Config(), parent.Header(), params) + block, err := InsertBlockParamsToBlock(api.eth.BlockChain().Config(), parent.Header(), params) if err != nil { return nil, err } - _, err = api.eth.BlockChain().InsertChainWithoutSealVerification(block) - return &newBlockResponse{err == nil}, err + err = api.eth.BlockChain().InsertBlock(block) + return &NewBlockResponse{err == nil}, err } // Used in tests to add a the list of transactions from a block to the tx pool. -func (api *consensusAPI) addBlockTxs(block *types.Block) error { +func (api *ConsensusAPI) addBlockTxs(block *types.Block) error { for _, tx := range block.Transactions() { api.eth.TxPool().AddLocal(tx) } @@ -305,11 +365,55 @@ func (api *consensusAPI) addBlockTxs(block *types.Block) error { // FinalizeBlock is called to mark a block as synchronized, so // that data that is no longer needed can be removed. -func (api *consensusAPI) FinalizeBlock(blockHash common.Hash) (*genericResponse, error) { - return &genericResponse{true}, nil +func (api *ConsensusAPI) FinalizeBlock(blockHash common.Hash) (*GenericResponse, error) { + // Finalize the transition if it's the first `FinalisedBlock` event. + merger := api.merger() + if !merger.EnteredPoS() { + merger.EnterPoS() + } + return &GenericResponse{true}, nil } // SetHead is called to perform a force choice. -func (api *consensusAPI) SetHead(newHead common.Hash) (*genericResponse, error) { - return &genericResponse{true}, nil +func (api *ConsensusAPI) SetHead(newHead common.Hash) (*GenericResponse, error) { + // Trigger the transition if it's the first `NewHead` event. + merger := api.merger() + if !merger.LeftPoW() { + merger.LeavePoW() + } + if api.light { + headHeader := api.les.BlockChain().CurrentHeader() + if headHeader.Hash() == newHead { + return &GenericResponse{true}, nil + } + newHeadHeader := api.les.BlockChain().GetHeaderByHash(newHead) + if newHeadHeader == nil { + return &GenericResponse{false}, nil + } + if err := api.les.BlockChain().SetChainHead(newHeadHeader); err != nil { + return &GenericResponse{false}, nil + } + return &GenericResponse{true}, nil + } + headBlock := api.eth.BlockChain().CurrentBlock() + if headBlock.Hash() == newHead { + return &GenericResponse{true}, nil + } + newHeadBlock := api.eth.BlockChain().GetBlockByHash(newHead) + if newHeadBlock == nil { + return &GenericResponse{false}, nil + } + if err := api.eth.BlockChain().SetChainHead(newHeadBlock); err != nil { + return &GenericResponse{false}, nil + } + api.eth.SetSynced() + return &GenericResponse{true}, nil +} + +// Helper function, return the merger instance. +func (api *ConsensusAPI) merger() *core.Merger { + if api.light { + return api.les.Merger() + } + return api.eth.Merger() } diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index f8550fa59b7c..a00e45c60c89 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -19,7 +19,9 @@ package catalyst import ( "math/big" "testing" + "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" @@ -38,10 +40,10 @@ var ( // testAddr is the Ethereum address of the tester account. testAddr = crypto.PubkeyToAddress(testKey.PublicKey) - testBalance = big.NewInt(2e15) + testBalance = big.NewInt(2e18) ) -func generateTestChain() (*core.Genesis, []*types.Block) { +func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) { db := rawdb.NewMemoryDatabase() config := params.AllEthashProtocolChanges genesis := &core.Genesis{ @@ -51,14 +53,17 @@ func generateTestChain() (*core.Genesis, []*types.Block) { Timestamp: 9000, BaseFee: big.NewInt(params.InitialBaseFee), } + testNonce := uint64(0) generate := func(i int, g *core.BlockGen) { g.OffsetTime(5) g.SetExtra([]byte("test")) + tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(config), testKey) + g.AddTx(tx) + testNonce++ } gblock := genesis.ToBlock(db) engine := ethash.NewFaker() - blocks, _ := core.GenerateChain(config, gblock, engine, db, 10, generate) - blocks = append([]*types.Block{gblock}, blocks...) + blocks, _ := core.GenerateChain(config, gblock, engine, db, n, generate) return genesis, blocks } @@ -107,113 +112,202 @@ func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block, } func TestEth2AssembleBlock(t *testing.T) { - genesis, blocks := generateTestChain() - n, ethservice := startEthService(t, genesis, blocks[1:9]) + genesis, blocks := generatePreMergeChain(10) + n, ethservice := startEthService(t, genesis, blocks) defer n.Close() - api := newConsensusAPI(ethservice) + api := NewConsensusAPI(ethservice, nil) signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID) - tx, err := types.SignTx(types.NewTransaction(0, blocks[8].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey) + tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey) if err != nil { t.Fatalf("error signing transaction, err=%v", err) } ethservice.TxPool().AddLocal(tx) - blockParams := assembleBlockParams{ - ParentHash: blocks[8].ParentHash(), - Timestamp: blocks[8].Time(), + blockParams := AssembleBlockParams{ + ParentHash: blocks[9].Hash(), + Timestamp: blocks[9].Time() + 5, } execData, err := api.AssembleBlock(blockParams) - if err != nil { t.Fatalf("error producing block, err=%v", err) } - if len(execData.Transactions) != 1 { t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) } } func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { - genesis, blocks := generateTestChain() - n, ethservice := startEthService(t, genesis, blocks[1:9]) + genesis, blocks := generatePreMergeChain(10) + n, ethservice := startEthService(t, genesis, blocks[:9]) defer n.Close() - api := newConsensusAPI(ethservice) + api := NewConsensusAPI(ethservice, nil) // Put the 10th block's tx in the pool and produce a new block api.addBlockTxs(blocks[9]) - blockParams := assembleBlockParams{ - ParentHash: blocks[9].ParentHash(), - Timestamp: blocks[9].Time(), + blockParams := AssembleBlockParams{ + ParentHash: blocks[8].Hash(), + Timestamp: blocks[8].Time() + 5, } execData, err := api.AssembleBlock(blockParams) if err != nil { t.Fatalf("error producing block, err=%v", err) } - if len(execData.Transactions) != blocks[9].Transactions().Len() { t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) } } +func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) { + t.Helper() + + if len(logsCh) != wantNew { + t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew) + } + if len(rmLogsCh) != wantRemoved { + t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved) + } + // Drain events. + for i := 0; i < len(logsCh); i++ { + <-logsCh + } + for i := 0; i < len(rmLogsCh); i++ { + <-rmLogsCh + } +} + func TestEth2NewBlock(t *testing.T) { - genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 4) - n, ethservice := startEthService(t, genesis, blocks[1:5]) + genesis, preMergeBlocks := generatePreMergeChain(10) + n, ethservice := startEthService(t, genesis, preMergeBlocks) defer n.Close() - api := newConsensusAPI(ethservice) - for i := 5; i < 10; i++ { - p := executableData{ - ParentHash: ethservice.BlockChain().CurrentBlock().Hash(), - Miner: blocks[i].Coinbase(), - StateRoot: blocks[i].Root(), - GasLimit: blocks[i].GasLimit(), - GasUsed: blocks[i].GasUsed(), - Transactions: encodeTransactions(blocks[i].Transactions()), - ReceiptRoot: blocks[i].ReceiptHash(), - LogsBloom: blocks[i].Bloom().Bytes(), - BlockHash: blocks[i].Hash(), - Timestamp: blocks[i].Time(), - Number: uint64(i), - } - success, err := api.NewBlock(p) - if err != nil || !success.Valid { + var ( + api = NewConsensusAPI(ethservice, nil) + parent = preMergeBlocks[len(preMergeBlocks)-1] + + // This EVM code generates a log when the contract is created. + logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") + ) + // The event channels. + newLogCh := make(chan []*types.Log, 10) + rmLogsCh := make(chan core.RemovedLogsEvent, 10) + ethservice.BlockChain().SubscribeLogsEvent(newLogCh) + ethservice.BlockChain().SubscribeRemovedLogsEvent(rmLogsCh) + + for i := 0; i < 10; i++ { + statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) + nonce := statedb.GetNonce(testAddr) + tx, err := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) + ethservice.TxPool().AddLocal(tx) + + execData, err := api.AssembleBlock(AssembleBlockParams{ + ParentHash: parent.Hash(), + Timestamp: parent.Time() + 5, + }) + if err != nil { + t.Fatalf("Failed to create the executable data %v", err) + } + block, err := InsertBlockParamsToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) + if err != nil { + t.Fatalf("Failed to convert executable data to block %v", err) + } + newResp, err := api.NewBlock(*execData) + if err != nil || !newResp.Valid { + t.Fatalf("Failed to insert block: %v", err) + } + if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 { + t.Fatalf("Chain head shouldn't be updated") + } + checkLogEvents(t, newLogCh, rmLogsCh, 0, 0) + + setResp, err := api.SetHead(block.Hash()) + if err != nil || !setResp.Success { t.Fatalf("Failed to insert block: %v", err) } + if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { + t.Fatalf("Chain head should be updated") + } + checkLogEvents(t, newLogCh, rmLogsCh, 1, 0) + + parent = block } - exp := ethservice.BlockChain().CurrentBlock().Hash() - - // Introduce the fork point. - lastBlockNum := blocks[4].Number() - lastBlock := blocks[4] - for i := 0; i < 4; i++ { - lastBlockNum.Add(lastBlockNum, big.NewInt(1)) - p := executableData{ - ParentHash: lastBlock.Hash(), - Miner: forkedBlocks[i].Coinbase(), - StateRoot: forkedBlocks[i].Root(), - Number: lastBlockNum.Uint64(), - GasLimit: forkedBlocks[i].GasLimit(), - GasUsed: forkedBlocks[i].GasUsed(), - Transactions: encodeTransactions(blocks[i].Transactions()), - ReceiptRoot: forkedBlocks[i].ReceiptHash(), - LogsBloom: forkedBlocks[i].Bloom().Bytes(), - BlockHash: forkedBlocks[i].Hash(), - Timestamp: forkedBlocks[i].Time(), - } - success, err := api.NewBlock(p) - if err != nil || !success.Valid { - t.Fatalf("Failed to insert forked block #%d: %v", i, err) - } - lastBlock, err = insertBlockParamsToBlock(ethservice.BlockChain().Config(), lastBlock.Header(), p) + // Introduce fork chain + var ( + head = ethservice.BlockChain().CurrentBlock().NumberU64() + ) + parent = preMergeBlocks[len(preMergeBlocks)-1] + for i := 0; i < 10; i++ { + execData, err := api.AssembleBlock(AssembleBlockParams{ + ParentHash: parent.Hash(), + Timestamp: parent.Time() + 6, + }) + if err != nil { + t.Fatalf("Failed to create the executable data %v", err) + } + block, err := InsertBlockParamsToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) if err != nil { - t.Fatal(err) + t.Fatalf("Failed to convert executable data to block %v", err) + } + newResp, err := api.NewBlock(*execData) + if err != nil || !newResp.Valid { + t.Fatalf("Failed to insert block: %v", err) + } + if ethservice.BlockChain().CurrentBlock().NumberU64() != head { + t.Fatalf("Chain head shouldn't be updated") } + + setResp, err := api.SetHead(block.Hash()) + if err != nil || !setResp.Success { + t.Fatalf("Failed to insert block: %v", err) + } + if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { + t.Fatalf("Chain head should be updated") + } + parent, head = block, block.NumberU64() } +} + +func TestEth2DeepReorg(t *testing.T) { + genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2) + n, ethservice := startEthService(t, genesis, preMergeBlocks) + defer n.Close() - if ethservice.BlockChain().CurrentBlock().Hash() != exp { - t.Fatalf("Wrong head after inserting fork %x != %x", exp, ethservice.BlockChain().CurrentBlock().Hash()) + var ( + api = NewConsensusAPI(ethservice, nil) + parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1] + head = ethservice.BlockChain().CurrentBlock().NumberU64() + ) + if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) { + t.Errorf("Block %d not pruned", parent.NumberU64()) + } + for i := 0; i < 10; i++ { + execData, err := api.AssembleBlock(AssembleBlockParams{ + ParentHash: parent.Hash(), + Timestamp: parent.Time() + 5, + }) + if err != nil { + t.Fatalf("Failed to create the executable data %v", err) + } + block, err := InsertBlockParamsToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) + if err != nil { + t.Fatalf("Failed to convert executable data to block %v", err) + } + newResp, err := api.NewBlock(*execData) + if err != nil || !newResp.Valid { + t.Fatalf("Failed to insert block: %v", err) + } + if ethservice.BlockChain().CurrentBlock().NumberU64() != head { + t.Fatalf("Chain head shouldn't be updated") + } + setResp, err := api.SetHead(block.Hash()) + if err != nil || !setResp.Success { + t.Fatalf("Failed to insert block: %v", err) + } + if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { + t.Fatalf("Chain head should be updated") + } + parent, head = block, block.NumberU64() } } @@ -226,7 +320,7 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) t.Fatal("can't create node:", err) } - ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}} + ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256} ethservice, err := eth.New(n, ethcfg) if err != nil { t.Fatal("can't create eth service:", err) diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index d5d351a99159..4f6c18ce4217 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -21,10 +21,10 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" ) -//go:generate go run github.com/fjl/gencodec -type assembleBlockParams -field-override assembleBlockParamsMarshaling -out gen_blockparams.go +//go:generate go run github.com/fjl/gencodec -type AssembleBlockParams -field-override assembleBlockParamsMarshaling -out gen_blockparams.go // Structure described at https://hackmd.io/T9x2mMA4S7us8tJwEB3FDQ -type assembleBlockParams struct { +type AssembleBlockParams struct { ParentHash common.Hash `json:"parentHash" gencodec:"required"` Timestamp uint64 `json:"timestamp" gencodec:"required"` } @@ -34,10 +34,10 @@ type assembleBlockParamsMarshaling struct { Timestamp hexutil.Uint64 } -//go:generate go run github.com/fjl/gencodec -type executableData -field-override executableDataMarshaling -out gen_ed.go +//go:generate go run github.com/fjl/gencodec -type ExecutableData -field-override executableDataMarshaling -out gen_ed.go // Structure described at https://notes.ethereum.org/@n0ble/rayonism-the-merge-spec#Parameters1 -type executableData struct { +type ExecutableData struct { BlockHash common.Hash `json:"blockHash" gencodec:"required"` ParentHash common.Hash `json:"parentHash" gencodec:"required"` Miner common.Address `json:"miner" gencodec:"required"` @@ -61,10 +61,10 @@ type executableDataMarshaling struct { Transactions []hexutil.Bytes } -type newBlockResponse struct { +type NewBlockResponse struct { Valid bool `json:"valid"` } -type genericResponse struct { +type GenericResponse struct { Success bool `json:"success"` } diff --git a/eth/catalyst/gen_blockparams.go b/eth/catalyst/gen_blockparams.go index a9a08ec3a809..4b4182629960 100644 --- a/eth/catalyst/gen_blockparams.go +++ b/eth/catalyst/gen_blockparams.go @@ -13,33 +13,33 @@ import ( var _ = (*assembleBlockParamsMarshaling)(nil) // MarshalJSON marshals as JSON. -func (a assembleBlockParams) MarshalJSON() ([]byte, error) { - type assembleBlockParams struct { +func (a AssembleBlockParams) MarshalJSON() ([]byte, error) { + type AssembleBlockParams struct { ParentHash common.Hash `json:"parentHash" gencodec:"required"` Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` } - var enc assembleBlockParams + var enc AssembleBlockParams enc.ParentHash = a.ParentHash enc.Timestamp = hexutil.Uint64(a.Timestamp) return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. -func (a *assembleBlockParams) UnmarshalJSON(input []byte) error { - type assembleBlockParams struct { +func (a *AssembleBlockParams) UnmarshalJSON(input []byte) error { + type AssembleBlockParams struct { ParentHash *common.Hash `json:"parentHash" gencodec:"required"` Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` } - var dec assembleBlockParams + var dec AssembleBlockParams if err := json.Unmarshal(input, &dec); err != nil { return err } if dec.ParentHash == nil { - return errors.New("missing required field 'parentHash' for assembleBlockParams") + return errors.New("missing required field 'parentHash' for AssembleBlockParams") } a.ParentHash = *dec.ParentHash if dec.Timestamp == nil { - return errors.New("missing required field 'timestamp' for assembleBlockParams") + return errors.New("missing required field 'timestamp' for AssembleBlockParams") } a.Timestamp = uint64(*dec.Timestamp) return nil diff --git a/eth/catalyst/gen_ed.go b/eth/catalyst/gen_ed.go index 4c2e4c8ead5d..850d8617a5ae 100644 --- a/eth/catalyst/gen_ed.go +++ b/eth/catalyst/gen_ed.go @@ -13,8 +13,8 @@ import ( var _ = (*executableDataMarshaling)(nil) // MarshalJSON marshals as JSON. -func (e executableData) MarshalJSON() ([]byte, error) { - type executableData struct { +func (e ExecutableData) MarshalJSON() ([]byte, error) { + type ExecutableData struct { BlockHash common.Hash `json:"blockHash" gencodec:"required"` ParentHash common.Hash `json:"parentHash" gencodec:"required"` Miner common.Address `json:"miner" gencodec:"required"` @@ -27,7 +27,7 @@ func (e executableData) MarshalJSON() ([]byte, error) { LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } - var enc executableData + var enc ExecutableData enc.BlockHash = e.BlockHash enc.ParentHash = e.ParentHash enc.Miner = e.Miner @@ -48,8 +48,8 @@ func (e executableData) MarshalJSON() ([]byte, error) { } // UnmarshalJSON unmarshals from JSON. -func (e *executableData) UnmarshalJSON(input []byte) error { - type executableData struct { +func (e *ExecutableData) UnmarshalJSON(input []byte) error { + type ExecutableData struct { BlockHash *common.Hash `json:"blockHash" gencodec:"required"` ParentHash *common.Hash `json:"parentHash" gencodec:"required"` Miner *common.Address `json:"miner" gencodec:"required"` @@ -62,52 +62,52 @@ func (e *executableData) UnmarshalJSON(input []byte) error { LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } - var dec executableData + var dec ExecutableData if err := json.Unmarshal(input, &dec); err != nil { return err } if dec.BlockHash == nil { - return errors.New("missing required field 'blockHash' for executableData") + return errors.New("missing required field 'blockHash' for ExecutableData") } e.BlockHash = *dec.BlockHash if dec.ParentHash == nil { - return errors.New("missing required field 'parentHash' for executableData") + return errors.New("missing required field 'parentHash' for ExecutableData") } e.ParentHash = *dec.ParentHash if dec.Miner == nil { - return errors.New("missing required field 'miner' for executableData") + return errors.New("missing required field 'miner' for ExecutableData") } e.Miner = *dec.Miner if dec.StateRoot == nil { - return errors.New("missing required field 'stateRoot' for executableData") + return errors.New("missing required field 'stateRoot' for ExecutableData") } e.StateRoot = *dec.StateRoot if dec.Number == nil { - return errors.New("missing required field 'number' for executableData") + return errors.New("missing required field 'number' for ExecutableData") } e.Number = uint64(*dec.Number) if dec.GasLimit == nil { - return errors.New("missing required field 'gasLimit' for executableData") + return errors.New("missing required field 'gasLimit' for ExecutableData") } e.GasLimit = uint64(*dec.GasLimit) if dec.GasUsed == nil { - return errors.New("missing required field 'gasUsed' for executableData") + return errors.New("missing required field 'gasUsed' for ExecutableData") } e.GasUsed = uint64(*dec.GasUsed) if dec.Timestamp == nil { - return errors.New("missing required field 'timestamp' for executableData") + return errors.New("missing required field 'timestamp' for ExecutableData") } e.Timestamp = uint64(*dec.Timestamp) if dec.ReceiptRoot == nil { - return errors.New("missing required field 'receiptsRoot' for executableData") + return errors.New("missing required field 'receiptsRoot' for ExecutableData") } e.ReceiptRoot = *dec.ReceiptRoot if dec.LogsBloom == nil { - return errors.New("missing required field 'logsBloom' for executableData") + return errors.New("missing required field 'logsBloom' for ExecutableData") } e.LogsBloom = *dec.LogsBloom if dec.Transactions == nil { - return errors.New("missing required field 'transactions' for executableData") + return errors.New("missing required field 'transactions' for ExecutableData") } e.Transactions = make([][]byte, len(dec.Transactions)) for k, v := range dec.Transactions { diff --git a/eth/catalyst/sync.go b/eth/catalyst/sync.go new file mode 100644 index 000000000000..61fd1ef9c166 --- /dev/null +++ b/eth/catalyst/sync.go @@ -0,0 +1,74 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package catalyst + +import ( + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" +) + +type syncer struct { + running bool + newBlocks map[common.Hash]*types.Block + lock sync.Mutex +} + +func newSyncer() *syncer { + return &syncer{ + newBlocks: make(map[common.Hash]*types.Block), + } +} + +// onNewBlock is the action for receiving new block event +func (s *syncer) onNewBlock(block *types.Block) { + s.lock.Lock() + defer s.lock.Unlock() + + if s.running { + return + } + s.newBlocks[block.Hash()] = block +} + +func (s *syncer) hasBlock(hash common.Hash) bool { + s.lock.Lock() + defer s.lock.Unlock() + + _, present := s.newBlocks[hash] + return present +} + +// onNewHead is the action for receiving new head event +func (s *syncer) onNewHead(head common.Hash) { + s.lock.Lock() + defer s.lock.Unlock() + + if s.running { + return + } + _, present := s.newBlocks[head] + if !present { + log.Error("Chain head is set with an unknown header") + return + } + s.running = true + + // todo call the SetHead function exposed by the downloader +} diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index a6bf87acbd22..9bfac76f2b23 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -1720,6 +1720,9 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error { for i, result := range results { blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles) } + // Downloaded blocks are always regarded as trusted after the + // transition. Because the downloaded chain is guided by the + // consensus-layer. if index, err := d.blockchain.InsertChain(blocks); err != nil { if index < len(results) { log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 89cdb75597e0..58ce0824118e 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" @@ -203,32 +204,40 @@ type Config struct { } // CreateConsensusEngine creates a consensus engine for the given chain configuration. -func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine { +func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database, merger *core.Merger) consensus.Engine { // If proof-of-authority is requested, set it up + var engine consensus.Engine if chainConfig.Clique != nil { - return clique.New(chainConfig.Clique, db) + engine = clique.New(chainConfig.Clique, db) + } else { + switch config.PowMode { + case ethash.ModeFake: + log.Warn("Ethash used in fake mode") + case ethash.ModeTest: + log.Warn("Ethash used in test mode") + case ethash.ModeShared: + log.Warn("Ethash used in shared mode") + } + engine = ethash.New(ethash.Config{ + PowMode: config.PowMode, + CacheDir: stack.ResolvePath(config.CacheDir), + CachesInMem: config.CachesInMem, + CachesOnDisk: config.CachesOnDisk, + CachesLockMmap: config.CachesLockMmap, + DatasetDir: config.DatasetDir, + DatasetsInMem: config.DatasetsInMem, + DatasetsOnDisk: config.DatasetsOnDisk, + DatasetsLockMmap: config.DatasetsLockMmap, + NotifyFull: config.NotifyFull, + }, notify, noverify) + engine.(*ethash.Ethash).SetThreads(-1) // Disable CPU mining } - // Otherwise assume proof-of-work - switch config.PowMode { - case ethash.ModeFake: - log.Warn("Ethash used in fake mode") - case ethash.ModeTest: - log.Warn("Ethash used in test mode") - case ethash.ModeShared: - log.Warn("Ethash used in shared mode") + if merger == nil { + return engine } - engine := ethash.New(ethash.Config{ - PowMode: config.PowMode, - CacheDir: stack.ResolvePath(config.CacheDir), - CachesInMem: config.CachesInMem, - CachesOnDisk: config.CachesOnDisk, - CachesLockMmap: config.CachesLockMmap, - DatasetDir: config.DatasetDir, - DatasetsInMem: config.DatasetsInMem, - DatasetsOnDisk: config.DatasetsOnDisk, - DatasetsLockMmap: config.DatasetsLockMmap, - NotifyFull: config.NotifyFull, - }, notify, noverify) - engine.SetThreads(-1) // Disable CPU mining + engine = beacon.New(engine, merger.LeftPoW()) + merger.SubscribeLeavePoW(func() { + engine.(*beacon.Beacon).MarkTransitioned() + }) return engine } diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index feecfddec730..1e774e2c1e36 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -151,7 +151,7 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke // Construct testing chain diskdb := rawdb.NewMemoryDatabase() gspec.Commit(diskdb) - chain, err := core.NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil) + chain, err := core.NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("Failed to create local chain, %v", err) } diff --git a/eth/handler.go b/eth/handler.go index 06a8088bf07c..e41df5c4cd00 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -25,6 +25,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/types" @@ -79,6 +80,7 @@ type handlerConfig struct { Database ethdb.Database // Database for direct sync insertions Chain *core.BlockChain // Blockchain to serve data from TxPool txPool // Transaction pool to propagate from + Merger *core.Merger // The manager for eth1/2 transition Network uint64 // Network identifier to adfvertise Sync downloader.SyncMode // Whether to fast or full sync BloomCache uint64 // Megabytes to alloc for fast sync bloom @@ -108,6 +110,7 @@ type handler struct { blockFetcher *fetcher.BlockFetcher txFetcher *fetcher.TxFetcher peers *peerSet + merger *core.Merger eventMux *event.TypeMux txsCh chan core.NewTxsEvent @@ -138,6 +141,7 @@ func newHandler(config *handlerConfig) (*handler, error) { txpool: config.TxPool, chain: config.Chain, peers: newPeerSet(), + merger: config.Merger, whitelist: config.Whitelist, quitSync: make(chan struct{}), } @@ -186,12 +190,41 @@ func newHandler(config *handlerConfig) (*handler, error) { // Construct the fetcher (short sync) validator := func(header *types.Header) error { + // All the block fetcher activities should be disabled + // after the transition. Print the warning log. + if h.merger.EnteredPoS() { + log.Warn("Unexpected validation activity", "hash", header.Hash(), "number", header.Number) + return errors.New("unexpected behavior after transition") + } + // Reject all the PoS style headers in the first place. No matter + // the chain has finished the transition or not, the PoS headers + // should only come from the trusted consensus layer instead of + // p2p network. + if beacon, ok := h.chain.Engine().(*beacon.Beacon); ok { + if beacon.IsPostMergeHeader(header) { + return errors.New("unexpected post-merge header") + } + } return h.chain.Engine().VerifyHeader(h.chain, header, true) } heighter := func() uint64 { return h.chain.CurrentBlock().NumberU64() } inserter := func(blocks types.Blocks) (int, error) { + // All the block fetcher activities should be disabled + // after the transition. Print the warning log. + if h.merger.EnteredPoS() { + var ctx []interface{} + ctx = append(ctx, "blocks", len(blocks)) + if len(blocks) > 0 { + ctx = append(ctx, "firsthash", blocks[0].Hash()) + ctx = append(ctx, "firstnumber", blocks[0].Number()) + ctx = append(ctx, "lasthash", blocks[len(blocks)-1].Hash()) + ctx = append(ctx, "lastnumber", blocks[len(blocks)-1].Number()) + } + log.Warn("Unexpected insertion activity", ctx...) + return 0, errors.New("unexpected behavior after transition") + } // If sync hasn't reached the checkpoint yet, deny importing weird blocks. // // Ideally we would also compare the head block's timestamp and similarly reject @@ -211,11 +244,26 @@ func newHandler(config *handlerConfig) (*handler, error) { log.Warn("Fast syncing, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash()) return 0, nil } - n, err := h.chain.InsertChain(blocks) - if err == nil { - atomic.StoreUint32(&h.acceptTxs, 1) // Mark initial sync done on any fetcher import + if h.merger.LeftPoW() { + // The blocks from the p2p network is regarded as untrusted + // after the transition. In theory block gossip should be disabled + // entirely whenever the transition is started. But in order to + // handle the transition boundary reorg in the consensus-layer, + // the legacy blocks are still accepted but the chain head won't + // be updated. + for i, block := range blocks { + if err := h.chain.InsertBlock(block); err != nil { + return i, err + } + } + return 0, nil + } else { + n, err := h.chain.InsertChain(blocks) + if err == nil { + atomic.StoreUint32(&h.acceptTxs, 1) // Mark initial sync done on any fetcher import + } + return n, err } - return n, err } h.blockFetcher = fetcher.NewBlockFetcher(false, nil, h.chain.GetBlockByHash, validator, h.BroadcastBlock, heighter, nil, inserter, h.removePeer) @@ -432,6 +480,17 @@ func (h *handler) Stop() { // BroadcastBlock will either propagate a block to a subset of its peers, or // will only announce its availability (depending what's requested). func (h *handler) BroadcastBlock(block *types.Block, propagate bool) { + // Disable the block propagation if the chain has already entered the PoS + // stage. The block propagation is delegated to the consensus layer. + if h.merger.EnteredPoS() { + return + } + // Disable the block propagation if it's the post-merge block. + if beacon, ok := h.chain.Engine().(*beacon.Beacon); ok { + if beacon.IsPostMergeHeader(block.Header()) { + return + } + } hash := block.Hash() peers := h.peers.peersWithoutBlock(hash) diff --git a/eth/handler_eth.go b/eth/handler_eth.go index 3ff9f2245be7..d6e8ac47eda1 100644 --- a/eth/handler_eth.go +++ b/eth/handler_eth.go @@ -180,6 +180,12 @@ func (h *ethHandler) handleBodies(peer *eth.Peer, txs [][]*types.Transaction, un // handleBlockAnnounces is invoked from a peer's message handler when it transmits a // batch of block announcements for the local node to process. func (h *ethHandler) handleBlockAnnounces(peer *eth.Peer, hashes []common.Hash, numbers []uint64) error { + // Drop all incoming block announces from the p2p network if + // the chain already entered the pos stage. TODO perhaps we + // should return the error here to disconnect the legacy node. + if h.merger.EnteredPoS() { + return nil + } // Schedule all the unknown hashes for retrieval var ( unknownHashes = make([]common.Hash, 0, len(hashes)) @@ -200,6 +206,12 @@ func (h *ethHandler) handleBlockAnnounces(peer *eth.Peer, hashes []common.Hash, // handleBlockBroadcast is invoked from a peer's message handler when it transmits a // block broadcast for the local node to process. func (h *ethHandler) handleBlockBroadcast(peer *eth.Peer, block *types.Block, td *big.Int) error { + // Drop all incoming block announces from the p2p network if + // the chain already entered the pos stage. TODO perhaps we + // should return the error here to disconnect the legacy node. + if h.merger.EnteredPoS() { + return nil + } // Schedule the block for import h.blockFetcher.Enqueue(peer.ID(), block) diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go index 039091244fec..8222ba9382f7 100644 --- a/eth/handler_eth_test.go +++ b/eth/handler_eth_test.go @@ -105,8 +105,8 @@ func testForkIDSplit(t *testing.T, protocol uint) { genesisNoFork = gspecNoFork.MustCommit(dbNoFork) genesisProFork = gspecProFork.MustCommit(dbProFork) - chainNoFork, _ = core.NewBlockChain(dbNoFork, nil, configNoFork, engine, vm.Config{}, nil, nil) - chainProFork, _ = core.NewBlockChain(dbProFork, nil, configProFork, engine, vm.Config{}, nil, nil) + chainNoFork, _ = core.NewBlockChain(dbNoFork, nil, configNoFork, engine, vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) + chainProFork, _ = core.NewBlockChain(dbProFork, nil, configProFork, engine, vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) blocksNoFork, _ = core.GenerateChain(configNoFork, genesisNoFork, engine, dbNoFork, 2, nil) blocksProFork, _ = core.GenerateChain(configProFork, genesisProFork, engine, dbProFork, 2, nil) @@ -115,6 +115,7 @@ func testForkIDSplit(t *testing.T, protocol uint) { Database: dbNoFork, Chain: chainNoFork, TxPool: newTestTxPool(), + Merger: core.NewMerger(rawdb.NewMemoryDatabase()), Network: 1, Sync: downloader.FullSync, BloomCache: 1, @@ -123,6 +124,7 @@ func testForkIDSplit(t *testing.T, protocol uint) { Database: dbProFork, Chain: chainProFork, TxPool: newTestTxPool(), + Merger: core.NewMerger(rawdb.NewMemoryDatabase()), Network: 1, Sync: downloader.FullSync, BloomCache: 1, diff --git a/eth/handler_test.go b/eth/handler_test.go index 090bd9239c2e..d3bd2b2385ae 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -137,7 +137,7 @@ func newTestHandlerWithBlocks(blocks int) *testHandler { Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(1000000)}}, }).MustCommit(db) - chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, nil) if _, err := chain.InsertChain(bs); err != nil { @@ -149,6 +149,7 @@ func newTestHandlerWithBlocks(blocks int) *testHandler { Database: db, Chain: chain, TxPool: txpool, + Merger: core.NewMerger(rawdb.NewMemoryDatabase()), Network: 1, Sync: downloader.FastSync, BloomCache: 1, diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index 809f17e36cf5..b3914f34a621 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -69,7 +69,7 @@ func newTestBackendWithGenerator(blocks int, generator func(int, *core.BlockGen) Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}}, }).MustCommit(db) - chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) + chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, generator) if _, err := chain.InsertChain(bs); err != nil { diff --git a/eth/state_accessor.go b/eth/state_accessor.go index ca2002b60dee..aaf6f6c6054b 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -30,12 +30,12 @@ import ( "github.com/ethereum/go-ethereum/trie" ) -// stateAtBlock retrieves the state database associated with a certain block. +// StateAtBlock retrieves the state database associated with a certain block. // If no state is locally available for the given block, a number of blocks // are attempted to be reexecuted to generate the desired state. The optional // base layer statedb can be passed then it's regarded as the statedb of the // parent block. -func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (statedb *state.StateDB, err error) { +func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (statedb *state.StateDB, err error) { var ( current *types.Block database state.Database @@ -152,7 +152,7 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec } // Lookup the statedb of parent block from the live database, // otherwise regenerate it on the flight. - statedb, err := eth.stateAtBlock(parent, reexec, nil, true) + statedb, err := eth.StateAtBlock(parent, reexec, nil, true) if err != nil { return nil, vm.BlockContext{}, nil, err } diff --git a/eth/sync.go b/eth/sync.go index 27941158f3df..015e974946bc 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -145,7 +145,10 @@ func (cs *chainSyncer) nextSyncOp() *chainSyncOp { if cs.doneCh != nil { return nil // Sync already running. } - + // Disable the td based sync trigger after the transition + if cs.handler.merger.LeftPoW() { + return nil + } // Ensure we're at minimum peer count. minPeers := defaultMinSyncPeers if cs.forced { diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 9afd59d596bc..3f3a460e03b3 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -81,7 +81,7 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i i SnapshotLimit: 0, TrieDirtyDisabled: true, // Archive mode } - chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, backend.chainConfig, backend.engine, vm.Config{}, nil, nil) + chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, backend.chainConfig, backend.engine, vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } diff --git a/les/client.go b/les/client.go index 5d07c783e99d..2b86d6d19040 100644 --- a/les/client.go +++ b/les/client.go @@ -63,6 +63,7 @@ type LightEthereum struct { serverPool *vfc.ServerPool serverPoolIterator enode.Iterator pruner *pruner + merger *core.Merger bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports @@ -95,6 +96,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { log.Info("Initialised chain configuration", "config", chainConfig) peers := newServerPeerSet() + merger := core.NewMerger(chainDb) leth := &LightEthereum{ lesCommons: lesCommons{ genesis: genesisHash, @@ -109,7 +111,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { eventMux: stack.EventMux(), reqDist: newRequestDistributor(peers, &mclock.System{}), accountManager: stack.AccountManager(), - engine: ethconfig.CreateConsensusEngine(stack, chainConfig, &config.Ethash, nil, false, chainDb), + merger: merger, + engine: ethconfig.CreateConsensusEngine(stack, chainConfig, &config.Ethash, nil, false, chainDb, merger), bloomRequests: make(chan chan *bloombits.Retrieval), bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations), p2pServer: stack.Server(), @@ -138,7 +141,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { } // Note: NewLightChain adds the trusted checkpoint so it needs an ODR with // indexers already set but not started yet - if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine, checkpoint); err != nil { + if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine, checkpoint, merger); err != nil { return nil, err } leth.chainReader = leth.blockchain @@ -332,6 +335,7 @@ func (s *LightEthereum) Engine() consensus.Engine { return s.engine } func (s *LightEthereum) LesVersion() int { return int(ClientProtocolVersions[0]) } func (s *LightEthereum) Downloader() *downloader.Downloader { return s.handler.downloader } func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux } +func (s *LightEthereum) Merger() *core.Merger { return s.merger } // Protocols returns all the currently configured network protocols to start. func (s *LightEthereum) Protocols() []p2p.Protocol { diff --git a/les/client_handler.go b/les/client_handler.go index 9583bd57ca05..de8bdf92dd8d 100644 --- a/les/client_handler.go +++ b/les/client_handler.go @@ -143,11 +143,13 @@ func (h *clientHandler) handle(p *serverPeer, noInitAnnounce bool) error { connectionTimer.Update(time.Duration(mclock.Now() - connectedAt)) serverConnectionGauge.Update(int64(h.backend.peers.len())) }() - // It's mainly used in testing which requires discarding initial - // signal to prevent syncing. - if !noInitAnnounce { + + // Discard all the announces after the transition + // Also discarding initial signal to prevent syncing during testing. + if !h.backend.merger.LeftPoW() || !noInitAnnounce { h.fetcher.announce(p, &announceData{Hash: p.headInfo.Hash, Number: p.headInfo.Number, Td: p.headInfo.Td}) } + // Mark the peer starts to be served. atomic.StoreUint32(&p.serving, 1) defer atomic.StoreUint32(&p.serving, 0) @@ -212,7 +214,11 @@ func (h *clientHandler) handleMsg(p *serverPeer) error { // Update peer head information first and then notify the announcement p.updateHead(req.Hash, req.Number, req.Td) - h.fetcher.announce(p, &req) + + // Discard all the announces after the transition + if !h.backend.merger.LeftPoW() { + h.fetcher.announce(p, &req) + } } case msg.Code == BlockHeadersMsg: p.Log().Trace("Received block header response message") diff --git a/les/fetcher.go b/les/fetcher.go index d944d32858e7..bfe3aa16ec27 100644 --- a/les/fetcher.go +++ b/les/fetcher.go @@ -71,8 +71,8 @@ type fetcherPeer struct { // These following two fields can track the latest announces // from the peer with limited size for caching. We hold the // assumption that all enqueued announces are td-monotonic. - announces map[common.Hash]*announce // Announcement map - announcesList []common.Hash // FIFO announces list + announces map[common.Hash]*announce // Announcement map + fifo []common.Hash // FIFO announces list } // addAnno enqueues an new trusted announcement. If the queued announces overflow, @@ -87,15 +87,15 @@ func (fp *fetcherPeer) addAnno(anno *announce) { return } fp.announces[hash] = anno - fp.announcesList = append(fp.announcesList, hash) + fp.fifo = append(fp.fifo, hash) // Evict oldest if the announces are oversized. - if len(fp.announcesList)-cachedAnnosThreshold > 0 { - for i := 0; i < len(fp.announcesList)-cachedAnnosThreshold; i++ { - delete(fp.announces, fp.announcesList[i]) + if len(fp.fifo)-cachedAnnosThreshold > 0 { + for i := 0; i < len(fp.fifo)-cachedAnnosThreshold; i++ { + delete(fp.announces, fp.fifo[i]) } - copy(fp.announcesList, fp.announcesList[len(fp.announcesList)-cachedAnnosThreshold:]) - fp.announcesList = fp.announcesList[:cachedAnnosThreshold] + copy(fp.fifo, fp.fifo[len(fp.fifo)-cachedAnnosThreshold:]) + fp.fifo = fp.fifo[:cachedAnnosThreshold] } } @@ -106,8 +106,8 @@ func (fp *fetcherPeer) forwardAnno(td *big.Int) []*announce { cutset int evicted []*announce ) - for ; cutset < len(fp.announcesList); cutset++ { - anno := fp.announces[fp.announcesList[cutset]] + for ; cutset < len(fp.fifo); cutset++ { + anno := fp.announces[fp.fifo[cutset]] if anno == nil { continue // In theory it should never ever happen } @@ -118,8 +118,8 @@ func (fp *fetcherPeer) forwardAnno(td *big.Int) []*announce { delete(fp.announces, anno.data.Hash) } if cutset > 0 { - copy(fp.announcesList, fp.announcesList[cutset:]) - fp.announcesList = fp.announcesList[:len(fp.announcesList)-cutset] + copy(fp.fifo, fp.fifo[cutset:]) + fp.fifo = fp.fifo[:len(fp.fifo)-cutset] } return evicted } diff --git a/les/test_helper.go b/les/test_helper.go index 21d0f191c98a..ea5502395174 100644 --- a/les/test_helper.go +++ b/les/test_helper.go @@ -202,7 +202,7 @@ func newTestClientHandler(backend *backends.SimulatedBackend, odr *LesOdr, index oracle *checkpointoracle.CheckpointOracle ) genesis := gspec.MustCommit(db) - chain, _ := light.NewLightChain(odr, gspec.Config, engine, nil) + chain, _ := light.NewLightChain(odr, gspec.Config, engine, nil, core.NewMerger(rawdb.NewMemoryDatabase())) if indexers != nil { checkpointConfig := ¶ms.CheckpointOracleConfig{ Address: crypto.CreateAddress(bankAddr, 0), @@ -239,6 +239,7 @@ func newTestClientHandler(backend *backends.SimulatedBackend, odr *LesOdr, index engine: engine, blockchain: chain, eventMux: evmux, + merger: core.NewMerger(rawdb.NewMemoryDatabase()), } client.handler = newClientHandler(ulcServers, ulcFraction, nil, client) diff --git a/light/lightchain.go b/light/lightchain.go index ca6fbfac49de..2c3bbd4dcbd9 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -59,6 +59,7 @@ type LightChain struct { chainHeadFeed event.Feed scope event.SubscriptionScope genesisBlock *types.Block + forker *core.ForkChoice bodyCache *lru.Cache // Cache for the most recent block bodies bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format @@ -77,7 +78,7 @@ type LightChain struct { // NewLightChain returns a fully initialised light chain using information // available in the database. It initialises the default Ethereum header // validator. -func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine, checkpoint *params.TrustedCheckpoint) (*LightChain, error) { +func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine, checkpoint *params.TrustedCheckpoint, merger *core.Merger) (*LightChain, error) { bodyCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit) blockCache, _ := lru.New(blockCacheLimit) @@ -92,6 +93,10 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus. blockCache: blockCache, engine: engine, } + bc.forker = core.NewForkChoice(bc, merger.LeftPoW(), nil) + merger.SubscribeLeavePoW(func() { + bc.forker.MarkTransitioned() + }) var err error bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.engine, bc.getProcInterrupt) if err != nil { @@ -369,6 +374,42 @@ func (lc *LightChain) postChainEvents(events []interface{}) { } } +func (lc *LightChain) InsertHeader(header *types.Header) error { + // Verify the header first before obtaing the lock + headers := []*types.Header{header} + if _, err := lc.hc.ValidateHeaderChain(headers, 100); err != nil { + return err + } + // Make sure only one thread manipulates the chain at once + lc.chainmu.Lock() + defer lc.chainmu.Unlock() + + lc.wg.Add(1) + defer lc.wg.Done() + + _, err := lc.hc.WriteHeaders(headers) + log.Info("Inserted header", "number", header.Number, "hash", header.Hash()) + return err +} + +func (lc *LightChain) SetChainHead(header *types.Header) error { + lc.wg.Add(1) + defer lc.wg.Done() + + lc.chainmu.Lock() + defer lc.chainmu.Unlock() + + if err := lc.hc.Reorg([]*types.Header{header}); err != nil { + return err + } + // Emit events + block := types.NewBlockWithHeader(header) + lc.chainFeed.Send(core.ChainEvent{Block: block, Hash: block.Hash()}) + lc.chainHeadFeed.Send(core.ChainHeadEvent{Block: block}) + log.Info("Set the chain head", "number", block.Number(), "hash", block.Hash()) + return nil +} + // InsertHeaderChain attempts to insert the given header chain in to the local // chain, possibly creating a reorg. If an error is returned, it will return the // index number of the failing header as well an error describing what went wrong. @@ -396,25 +437,23 @@ func (lc *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i lc.wg.Add(1) defer lc.wg.Done() - status, err := lc.hc.InsertHeaderChain(chain, start) + status, err := lc.hc.InsertHeaderChain(chain, start, lc.forker) if err != nil || len(chain) == 0 { return 0, err } // Create chain event for the new head block of this insertion. var ( - events = make([]interface{}, 0, 1) lastHeader = chain[len(chain)-1] block = types.NewBlockWithHeader(lastHeader) ) switch status { case core.CanonStatTy: - events = append(events, core.ChainEvent{Block: block, Hash: block.Hash()}) + lc.chainFeed.Send(core.ChainEvent{Block: block, Hash: block.Hash()}) + lc.chainHeadFeed.Send(core.ChainHeadEvent{Block: block}) case core.SideStatTy: - events = append(events, core.ChainSideEvent{Block: block}) + lc.chainSideFeed.Send(core.ChainSideEvent{Block: block}) } - lc.postChainEvents(events) - return 0, err } diff --git a/light/lightchain_test.go b/light/lightchain_test.go index af36ebd96a23..4e1459b575c6 100644 --- a/light/lightchain_test.go +++ b/light/lightchain_test.go @@ -56,7 +56,7 @@ func newCanonical(n int) (ethdb.Database, *LightChain, error) { db := rawdb.NewMemoryDatabase() gspec := core.Genesis{Config: params.TestChainConfig} genesis := gspec.MustCommit(db) - blockchain, _ := NewLightChain(&dummyOdr{db: db, indexerConfig: TestClientIndexerConfig}, gspec.Config, ethash.NewFaker(), nil) + blockchain, _ := NewLightChain(&dummyOdr{db: db, indexerConfig: TestClientIndexerConfig}, gspec.Config, ethash.NewFaker(), nil, core.NewMerger(rawdb.NewMemoryDatabase())) // Create and inject the requested chain if n == 0 { @@ -76,7 +76,7 @@ func newTestLightChain() *LightChain { Config: params.TestChainConfig, } gspec.MustCommit(db) - lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFullFaker(), nil) + lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFullFaker(), nil, core.NewMerger(rawdb.NewMemoryDatabase())) if err != nil { panic(err) } @@ -345,7 +345,7 @@ func TestReorgBadHeaderHashes(t *testing.T) { defer func() { delete(core.BadHashes, headers[3].Hash()) }() // Create a new LightChain and check that it rolled back the state. - ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, ethash.NewFaker(), nil) + ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, ethash.NewFaker(), nil, core.NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("failed to create new chain manager: %v", err) } diff --git a/light/odr_test.go b/light/odr_test.go index fdf657a82ec5..85c2ba86ba0a 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -261,14 +261,14 @@ func testChainOdr(t *testing.T, protocol int, fn odrTestFn) { ) gspec.MustCommit(ldb) // Assemble the test environment - blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil) + blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, 4, testChainGen) if _, err := blockchain.InsertChain(gchain); err != nil { t.Fatal(err) } odr := &testOdr{sdb: sdb, ldb: ldb, indexerConfig: TestClientIndexerConfig} - lightchain, err := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), nil) + lightchain, err := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), nil, core.NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatal(err) } diff --git a/light/trie_test.go b/light/trie_test.go index e8294cc2a235..1b1d49bcd22f 100644 --- a/light/trie_test.go +++ b/light/trie_test.go @@ -44,7 +44,7 @@ func TestNodeIterator(t *testing.T) { genesis = gspec.MustCommit(fulldb) ) gspec.MustCommit(lightdb) - blockchain, _ := core.NewBlockChain(fulldb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil) + blockchain, _ := core.NewBlockChain(fulldb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), fulldb, 4, testChainGen) if _, err := blockchain.InsertChain(gchain); err != nil { panic(err) diff --git a/light/txpool_test.go b/light/txpool_test.go index cc2651d29ae5..ee1a26df0d2f 100644 --- a/light/txpool_test.go +++ b/light/txpool_test.go @@ -91,7 +91,7 @@ func TestTxPool(t *testing.T) { ) gspec.MustCommit(ldb) // Assemble the test environment - blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil) + blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, poolTestBlocks, txPoolTestChainGen) if _, err := blockchain.InsertChain(gchain); err != nil { panic(err) @@ -103,7 +103,7 @@ func TestTxPool(t *testing.T) { discard: make(chan int, 1), mined: make(chan int, 1), } - lightchain, _ := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), nil) + lightchain, _ := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), nil, core.NewMerger(rawdb.NewMemoryDatabase())) txPermanent = 50 pool := NewTxPool(params.TestChainConfig, lightchain, relay) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) diff --git a/miner/miner.go b/miner/miner.go index a4a01b9f4ff7..9aa5777d4b43 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -65,7 +65,7 @@ type Miner struct { stopCh chan struct{} } -func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, isLocalBlock func(block *types.Block) bool) *Miner { +func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, isLocalBlock func(header *types.Header) bool, merger *core.Merger) *Miner { miner := &Miner{ eth: eth, mux: mux, @@ -73,7 +73,7 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even exitCh: make(chan struct{}), startCh: make(chan common.Address), stopCh: make(chan struct{}), - worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, true), + worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, true, merger), } go miner.update() diff --git a/miner/miner_test.go b/miner/miner_test.go index da1e472dbd76..69d5c551fa05 100644 --- a/miner/miner_test.go +++ b/miner/miner_test.go @@ -245,7 +245,7 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux) { // Create consensus engine engine := clique.New(chainConfig.Clique, chainDB) // Create Ethereum backend - bc, err := core.NewBlockChain(chainDB, nil, chainConfig, engine, vm.Config{}, nil, nil) + bc, err := core.NewBlockChain(chainDB, nil, chainConfig, engine, vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) if err != nil { t.Fatalf("can't create new chain %v", err) } @@ -257,5 +257,5 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux) { // Create event Mux mux := new(event.TypeMux) // Create Miner - return New(backend, &config, chainConfig, mux, engine, nil), mux + return New(backend, &config, chainConfig, mux, engine, nil, core.NewMerger(rawdb.NewMemoryDatabase())), mux } diff --git a/miner/stress/beacon/main.go b/miner/stress/beacon/main.go new file mode 100644 index 000000000000..cb5b6b56a1a7 --- /dev/null +++ b/miner/stress/beacon/main.go @@ -0,0 +1,489 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// This file contains a miner stress test for the eth1/2 transition +package main + +import ( + "crypto/ecdsa" + "errors" + "io/ioutil" + "math/big" + "math/rand" + "os" + "path/filepath" + "time" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/fdlimit" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/catalyst" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/les" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params" +) + +type nodetype int + +const ( + legacyMiningNode nodetype = iota + legacyNormalNode + eth2MiningNode + eth2NormalNode + eth2LightClient +) + +var ( + // transitionDifficulty is the target total difficulty for transition + transitionDifficulty = new(big.Int).Mul(big.NewInt(0), params.MinimumDifficulty) + + // blockInterval is the time interval for creating a new eth2 block + blockInterval = time.Second * 3 + blockIntervalInt = 3 + + // finalizationDist is the block distance for finalizing block + finalizationDist = 10 +) + +type ethNode struct { + typ nodetype + api *catalyst.ConsensusAPI + ethBackend *eth.Ethereum + lesBackend *les.LightEthereum + stack *node.Node + enode *enode.Node +} + +func newNode(typ nodetype, genesis *core.Genesis, enodes []*enode.Node) *ethNode { + var ( + err error + api *catalyst.ConsensusAPI + stack *node.Node + ethBackend *eth.Ethereum + lesBackend *les.LightEthereum + ) + // Start the node and wait until it's up + if typ == eth2LightClient { + stack, lesBackend, api, err = makeLightNode(genesis) + } else { + stack, ethBackend, api, err = makeFullNode(genesis) + } + if err != nil { + panic(err) + } + for stack.Server().NodeInfo().Ports.Listener == 0 { + time.Sleep(250 * time.Millisecond) + } + // Connect the node to all the previous ones + for _, n := range enodes { + stack.Server().AddPeer(n) + } + enode := stack.Server().Self() + + // Inject the signer key and start sealing with it + store := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) + if _, err := store.NewAccount(""); err != nil { + panic(err) + } + return ðNode{ + typ: typ, + api: api, + ethBackend: ethBackend, + lesBackend: lesBackend, + stack: stack, + enode: enode, + } +} + +func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) (*catalyst.ExecutableData, error) { + if n.typ != eth2MiningNode { + return nil, errors.New("invalid node type") + } + return n.api.AssembleBlock(catalyst.AssembleBlockParams{ + ParentHash: parentHash, + Timestamp: uint64(time.Now().Unix()), + }) +} + +func (n *ethNode) insertBlock(eb catalyst.ExecutableData) error { + if !eth2types(n.typ) { + return errors.New("invalid node type") + } + response, err := n.api.NewBlock(eb) + if err != nil { + return err + } + if !response.Valid { + return errors.New("failed to insert block") + } + return nil +} + +func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed catalyst.ExecutableData) error { + if !eth2types(n.typ) { + return errors.New("invalid node type") + } + if err := n.insertBlock(ed); err != nil { + return err + } + var config *params.ChainConfig + if n.typ == eth2LightClient { + config = n.lesBackend.BlockChain().Config() + } else { + config = n.ethBackend.BlockChain().Config() + } + block, err := catalyst.InsertBlockParamsToBlock(config, parent, ed) + if err != nil { + return err + } + response, err := n.api.SetHead(block.Hash()) + if err != nil { + return err + } + if !response.Success { + return errors.New("failed to set head") + } + return nil +} + +type nodeManager struct { + genesis *core.Genesis + genesisBlock *types.Block + nodes []*ethNode + enodes []*enode.Node + close chan struct{} +} + +func newNodeManager(genesis *core.Genesis) *nodeManager { + return &nodeManager{ + close: make(chan struct{}), + genesis: genesis, + genesisBlock: genesis.ToBlock(nil), + } +} + +func (mgr *nodeManager) createNode(typ nodetype) { + node := newNode(typ, mgr.genesis, mgr.enodes) + mgr.nodes = append(mgr.nodes, node) + mgr.enodes = append(mgr.enodes, node.enode) +} + +func (mgr *nodeManager) getNodes(typ nodetype) []*ethNode { + var ret []*ethNode + for _, node := range mgr.nodes { + if node.typ == typ { + ret = append(ret, node) + } + } + return ret +} + +func (mgr *nodeManager) startMining() { + for _, node := range append(mgr.getNodes(eth2MiningNode), mgr.getNodes(legacyMiningNode)...) { + if err := node.ethBackend.StartMining(1); err != nil { + panic(err) + } + } +} + +func (mgr *nodeManager) shutdown() { + close(mgr.close) + for _, node := range mgr.nodes { + node.stack.Close() + } +} + +func (mgr *nodeManager) run() { + if len(mgr.nodes) == 0 { + return + } + chain := mgr.nodes[0].ethBackend.BlockChain() + sink := make(chan core.ChainHeadEvent, 1024) + sub := chain.SubscribeChainHeadEvent(sink) + defer sub.Unsubscribe() + + var ( + transitioned bool + parentBlock *types.Block + waitFinalise []*types.Block + ) + timer := time.NewTimer(0) + defer timer.Stop() + <-timer.C // discard the initial tick + + // Handle the by default transition. + if transitionDifficulty.Sign() == 0 { + transitioned = true + parentBlock = mgr.genesisBlock + timer.Reset(blockInterval) + log.Info("Enable the transition by default") + } + + // Handle the block finalization. + checkFinalise := func() { + if parentBlock == nil { + return + } + if len(waitFinalise) == 0 { + return + } + oldest := waitFinalise[0] + if oldest.NumberU64() > parentBlock.NumberU64() { + return + } + distance := parentBlock.NumberU64() - oldest.NumberU64() + if int(distance) < finalizationDist { + return + } + nodes := mgr.getNodes(eth2MiningNode) + nodes = append(nodes, mgr.getNodes(eth2NormalNode)...) + nodes = append(nodes, mgr.getNodes(eth2LightClient)...) + for _, node := range append(nodes) { + node.api.FinalizeBlock(oldest.Hash()) + } + log.Info("Finalised eth2 block", "number", oldest.NumberU64(), "hash", oldest.Hash()) + waitFinalise = waitFinalise[1:] + } + + for { + checkFinalise() + select { + case <-mgr.close: + return + + case ev := <-sink: + if transitioned { + continue + } + td := chain.GetTd(ev.Block.Hash(), ev.Block.NumberU64()) + if td.Cmp(transitionDifficulty) < 0 { + continue + } + transitioned, parentBlock = true, ev.Block + timer.Reset(blockInterval) + log.Info("Transition difficulty reached", "td", td, "target", transitionDifficulty) + + case <-timer.C: + producers := mgr.getNodes(eth2MiningNode) + if len(producers) == 0 { + continue + } + hash, timestamp := parentBlock.Hash(), parentBlock.Time() + if parentBlock.NumberU64() == 0 { + timestamp = uint64(time.Now().Unix()) - uint64(blockIntervalInt) + } + ed, err := producers[0].assembleBlock(hash, timestamp) + if err != nil { + log.Error("Failed to assemble the block", "err", err) + continue + } + block, _ := catalyst.InsertBlockParamsToBlock(chain.Config(), parentBlock.Header(), *ed) + + nodes := mgr.getNodes(eth2MiningNode) + nodes = append(nodes, mgr.getNodes(eth2NormalNode)...) + nodes = append(nodes, mgr.getNodes(eth2LightClient)...) + + for _, node := range nodes { + if err := node.insertBlockAndSetHead(parentBlock.Header(), *ed); err != nil { + log.Error("Failed to insert block", "err", err) + } + } + log.Info("Create and insert eth2 block", "number", ed.Number) + parentBlock = block + waitFinalise = append(waitFinalise, block) + timer.Reset(blockInterval) + } + } +} + +func main() { + log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + fdlimit.Raise(2048) + + // Generate a batch of accounts to seal and fund with + faucets := make([]*ecdsa.PrivateKey, 16) + for i := 0; i < len(faucets); i++ { + faucets[i], _ = crypto.GenerateKey() + } + // Pre-generate the ethash mining DAG so we don't race + ethash.MakeDataset(1, filepath.Join(os.Getenv("HOME"), ".ethash")) + + // Create an Ethash network based off of the Ropsten config + genesis := makeGenesis(faucets) + manager := newNodeManager(genesis) + defer manager.shutdown() + + manager.createNode(eth2NormalNode) + manager.createNode(eth2MiningNode) + manager.createNode(legacyMiningNode) + manager.createNode(legacyNormalNode) + manager.createNode(eth2LightClient) + + // Iterate over all the nodes and start mining + time.Sleep(3 * time.Second) + if transitionDifficulty.Sign() != 0 { + manager.startMining() + } + go manager.run() + + // Start injecting transactions from the faucets like crazy + time.Sleep(3 * time.Second) + nonces := make([]uint64, len(faucets)) + for { + // Pick a random mining node + nodes := manager.getNodes(eth2MiningNode) + + index := rand.Intn(len(faucets)) + node := nodes[index%len(nodes)] + + // Create a self transaction and inject into the pool + tx, err := types.SignTx(types.NewTransaction(nonces[index], crypto.PubkeyToAddress(faucets[index].PublicKey), new(big.Int), 21000, big.NewInt(100000000000+rand.Int63n(65536)), nil), types.HomesteadSigner{}, faucets[index]) + if err != nil { + panic(err) + } + if err := node.ethBackend.TxPool().AddLocal(tx); err != nil { + panic(err) + } + nonces[index]++ + + // Wait if we're too saturated + if pend, _ := node.ethBackend.TxPool().Stats(); pend > 2048 { + time.Sleep(100 * time.Millisecond) + } + } +} + +// makeGenesis creates a custom Ethash genesis block based on some pre-defined +// faucet accounts. +func makeGenesis(faucets []*ecdsa.PrivateKey) *core.Genesis { + genesis := core.DefaultRopstenGenesisBlock() + genesis.Difficulty = params.MinimumDifficulty + genesis.GasLimit = 25000000 + + genesis.Config.ChainID = big.NewInt(18) + genesis.Config.EIP150Hash = common.Hash{} + genesis.BaseFee = big.NewInt(params.InitialBaseFee) + + genesis.Alloc = core.GenesisAlloc{} + for _, faucet := range faucets { + genesis.Alloc[crypto.PubkeyToAddress(faucet.PublicKey)] = core.GenesisAccount{ + Balance: new(big.Int).Exp(big.NewInt(2), big.NewInt(128), nil), + } + } + return genesis +} + +func makeFullNode(genesis *core.Genesis) (*node.Node, *eth.Ethereum, *catalyst.ConsensusAPI, error) { + // Define the basic configurations for the Ethereum node + datadir, _ := ioutil.TempDir("", "") + + config := &node.Config{ + Name: "geth", + Version: params.Version, + DataDir: datadir, + P2P: p2p.Config{ + ListenAddr: "0.0.0.0:0", + NoDiscovery: true, + MaxPeers: 25, + }, + UseLightweightKDF: true, + } + // Create the node and configure a full Ethereum node on it + stack, err := node.New(config) + if err != nil { + return nil, nil, nil, err + } + ethBackend, err := eth.New(stack, ðconfig.Config{ + Genesis: genesis, + NetworkId: genesis.Config.ChainID.Uint64(), + SyncMode: downloader.FullSync, + DatabaseCache: 256, + DatabaseHandles: 256, + TxPool: core.DefaultTxPoolConfig, + GPO: ethconfig.Defaults.GPO, + Ethash: ethconfig.Defaults.Ethash, + Miner: miner.Config{ + GasFloor: genesis.GasLimit * 9 / 10, + GasCeil: genesis.GasLimit * 11 / 10, + GasPrice: big.NewInt(1), + Recommit: 10 * time.Second, // Disable the recommit + }, + LightServ: 100, + LightPeers: 10, + LightNoSyncServe: true, + }) + if err != nil { + return nil, nil, nil, err + } + err = stack.Start() + return stack, ethBackend, catalyst.NewConsensusAPI(ethBackend, nil), err +} + +func makeLightNode(genesis *core.Genesis) (*node.Node, *les.LightEthereum, *catalyst.ConsensusAPI, error) { + // Define the basic configurations for the Ethereum node + datadir, _ := ioutil.TempDir("", "") + + config := &node.Config{ + Name: "geth", + Version: params.Version, + DataDir: datadir, + P2P: p2p.Config{ + ListenAddr: "0.0.0.0:0", + NoDiscovery: true, + MaxPeers: 25, + }, + UseLightweightKDF: true, + } + // Create the node and configure a full Ethereum node on it + stack, err := node.New(config) + if err != nil { + return nil, nil, nil, err + } + lesBackend, err := les.New(stack, ðconfig.Config{ + Genesis: genesis, + NetworkId: genesis.Config.ChainID.Uint64(), + SyncMode: downloader.LightSync, + DatabaseCache: 256, + DatabaseHandles: 256, + TxPool: core.DefaultTxPoolConfig, + GPO: ethconfig.Defaults.GPO, + Ethash: ethconfig.Defaults.Ethash, + LightPeers: 10, + }) + if err != nil { + return nil, nil, nil, err + } + err = stack.Start() + return stack, lesBackend, catalyst.NewConsensusAPI(nil, lesBackend), err +} + +func eth2types(typ nodetype) bool { + if typ == eth2LightClient || typ == eth2NormalNode || typ == eth2MiningNode { + return true + } + return false +} diff --git a/miner/worker.go b/miner/worker.go index 8bdb1eff7ca0..6dd317cecc77 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -128,6 +128,7 @@ type worker struct { engine consensus.Engine eth Backend chain *core.BlockChain + merger *core.Merger // Feeds pendingLogsFeed event.Feed @@ -179,7 +180,7 @@ type worker struct { noempty uint32 // External functions - isLocalBlock func(block *types.Block) bool // Function used to determine whether the specified block is mined by local miner. + isLocalBlock func(header *types.Header) bool // Function used to determine whether the specified block is mined by local miner. // Test hooks newTaskHook func(*task) // Method to call upon receiving a new sealing task. @@ -188,7 +189,7 @@ type worker struct { resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval. } -func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(*types.Block) bool, init bool) *worker { +func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(header *types.Header) bool, init bool, merger *core.Merger) *worker { worker := &worker{ config: config, chainConfig: chainConfig, @@ -196,6 +197,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus eth: eth, mux: mux, chain: eth.BlockChain(), + merger: merger, isLocalBlock: isLocalBlock, localUncles: make(map[common.Hash]*types.Block), remoteUncles: make(map[common.Hash]*types.Block), @@ -464,7 +466,7 @@ func (w *worker) mainLoop() { continue } // Add side block to possible uncle block set depending on the author. - if w.isLocalBlock != nil && w.isLocalBlock(ev.Block) { + if w.isLocalBlock != nil && w.isLocalBlock(ev.Block.Header()) { w.localUncles[ev.Block.Hash()] = ev.Block } else { w.remoteUncles[ev.Block.Hash()] = ev.Block @@ -638,7 +640,7 @@ func (w *worker) resultLoop() { logs = append(logs, receipt.Logs...) } // Commit block and state to database. - _, err := w.chain.WriteBlockWithState(block, receipts, logs, task.state, true) + _, err := w.chain.WriteBlockAndSetHead(block, receipts, logs, task.state, true) if err != nil { log.Error("Failed writing block to chain", "err", err) continue @@ -1022,7 +1024,7 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st if err != nil { return err } - if w.isRunning() { + if w.isRunning() && !w.merger.LeftPoW() { if interval != nil { interval() } diff --git a/miner/worker_test.go b/miner/worker_test.go index 5b35c66dcea8..3b4484adfd2a 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -135,7 +135,7 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine } genesis := gspec.MustCommit(db) - chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{}, nil, nil) + chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain) // Generate a small n-block chain and an uncle block for it @@ -197,7 +197,7 @@ func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction { func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int) (*worker, *testWorkerBackend) { backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks) backend.txPool.AddLocals(pendingTxs) - w := newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false) + w := newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false, core.NewMerger(rawdb.NewMemoryDatabase())) w.setEtherbase(testBankAddress) return w, backend } @@ -232,7 +232,7 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) { // This test chain imports the mined blocks. db2 := rawdb.NewMemoryDatabase() b.genesis.MustCommit(db2) - chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil, nil) + chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) defer chain.Stop() // Ignore empty commit here for less noise. diff --git a/params/config.go b/params/config.go index 16c801825776..1950b3d185ba 100644 --- a/params/config.go +++ b/params/config.go @@ -613,7 +613,7 @@ type Rules struct { ChainID *big.Int IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool - IsBerlin, IsLondon, IsCatalyst bool + IsBerlin, IsLondon bool } // Rules ensures c's ChainID is not nil. @@ -634,6 +634,5 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { IsIstanbul: c.IsIstanbul(num), IsBerlin: c.IsBerlin(num), IsLondon: c.IsLondon(num), - IsCatalyst: c.IsCatalyst(num), } } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index bcf861e09b9c..e3daab9b5d22 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -127,7 +127,7 @@ func (t *BlockTest) Run(snapshotter bool) error { cache.SnapshotLimit = 1 cache.SnapshotWait = true } - chain, err := core.NewBlockChain(db, cache, config, engine, vm.Config{}, nil, nil) + chain, err := core.NewBlockChain(db, cache, config, engine, vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) if err != nil { return err } diff --git a/tests/fuzzers/les/les-fuzzer.go b/tests/fuzzers/les/les-fuzzer.go index 3e1017187345..4aeee43a6656 100644 --- a/tests/fuzzers/les/les-fuzzer.go +++ b/tests/fuzzers/les/les-fuzzer.go @@ -80,7 +80,7 @@ func makechain() (bc *core.BlockChain, addrHashes, txHashes []common.Hash) { addrHashes = append(addrHashes, crypto.Keccak256Hash(addr[:])) txHashes = append(txHashes, tx.Hash()) }) - bc, _ = core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + bc, _ = core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, core.NewMerger(rawdb.NewMemoryDatabase())) if _, err := bc.InsertChain(blocks); err != nil { panic(err) } From 49a489192d128feded5e5497a1386bdcb939d326 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 26 Jul 2021 19:08:48 +0800 Subject: [PATCH 03/43] consensus/beacon, eth: change beacon difficulty to 0 --- consensus/beacon/consensus.go | 47 ++++++++++++++--------------------- eth/catalyst/api.go | 2 +- eth/handler.go | 4 +-- 3 files changed, 21 insertions(+), 32 deletions(-) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 878cb60fa2c2..ab94cc135d74 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -33,7 +33,7 @@ import ( // Proof-of-stake protocol constants. var ( - beaconDifficulty = common.Big1 // The default block difficulty in the beacon consensus + beaconDifficulty = common.Big0 // The default block difficulty in the beacon consensus beaconNonce = types.EncodeNonce(0) // The default block nonce in the beacon consensus ) @@ -61,8 +61,8 @@ type Beacon struct { ethone consensus.Engine // Classic consensus engine used in the eth1, ethash or clique // transitioned is the flag whether the transition has been triggered. - // It's triggered by receiving the first "NewHead" message from the - // external consensus engine. + // It's triggered by receiving the first "POS_CHAINHEAD_SET" message + // from the external consensus engine. transitioned bool lock sync.RWMutex } @@ -81,7 +81,7 @@ func New(ethone consensus.Engine, transitioned bool) *Beacon { // Author implements consensus.Engine, returning the header's coinbase as the // verified author of the block. func (beacon *Beacon) Author(header *types.Header) (common.Address, error) { - if !beacon.IsPostMergeHeader(header) { + if !beacon.IsPoSHeader(header) { return beacon.ethone.Author(header) } return header.Coinbase, nil @@ -90,7 +90,7 @@ func (beacon *Beacon) Author(header *types.Header) (common.Address, error) { // VerifyHeader checks whether a header conforms to the consensus rules of the // stock Ethereum consensus engine. func (beacon *Beacon) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error { - if !beacon.IsPostMergeHeader(header) { + if !beacon.IsPoSHeader(header) { return beacon.ethone.VerifyHeader(chain, header, seal) } // Short circuit if the header is known, or its parent not @@ -110,7 +110,7 @@ func (beacon *Beacon) VerifyHeader(chain consensus.ChainHeaderReader, header *ty // concurrently. The method returns a quit channel to abort the operations and // a results channel to retrieve the async verifications. func (beacon *Beacon) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { - if !beacon.IsPostMergeHeader(headers[len(headers)-1]) { + if !beacon.IsPoSHeader(headers[len(headers)-1]) { return beacon.ethone.VerifyHeaders(chain, headers, seals) } var ( @@ -119,7 +119,7 @@ func (beacon *Beacon) VerifyHeaders(chain consensus.ChainHeaderReader, headers [ preSeals []bool ) for index, header := range headers { - if beacon.IsPostMergeHeader(header) { + if beacon.IsPoSHeader(header) { preHeaders = headers[:index] postHeaders = headers[index:] preSeals = seals[:index] @@ -171,7 +171,7 @@ func (beacon *Beacon) VerifyHeaders(chain consensus.ChainHeaderReader, headers [ // VerifyUncles verifies that the given block's uncles conform to the consensus // rules of the stock Ethereum consensus engine. func (beacon *Beacon) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { - if !beacon.IsPostMergeHeader(block.Header()) { + if !beacon.IsPoSHeader(block.Header()) { return beacon.ethone.VerifyUncles(chain, block) } // Verify that there is no uncle block. It's explicitly disabled in the beacon @@ -295,7 +295,7 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. // Finalize is different with Prepare, it can be used in both block generation // and verification. So determine the consensus rules by header type. - if !beacon.IsPostMergeHeader(header) { + if !beacon.IsPoSHeader(header) { beacon.ethone.Finalize(chain, header, state, txs, uncles) return } @@ -312,7 +312,7 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea // FinalizeAndAssemble is different with Prepare, it can be used in both block // generation and verification. So determine the consensus rules by header type. - if !beacon.IsPostMergeHeader(header) { + if !beacon.IsPoSHeader(header) { return beacon.ethone.FinalizeAndAssemble(chain, header, state, txs, uncles, receipts) } // Finalize and assemble the block @@ -329,7 +329,7 @@ func (beacon *Beacon) Seal(chain consensus.ChainHeaderReader, block *types.Block beacon.lock.RLock() defer beacon.lock.RUnlock() - if !beacon.IsPostMergeHeader(block.Header()) { + if !beacon.IsPoSHeader(block.Header()) { return beacon.ethone.Seal(chain, block, results, stop) } // The seal verification is done by the external consensus engine, @@ -368,25 +368,14 @@ func (beacon *Beacon) Close() error { return beacon.ethone.Close() } -// IsPostMergeHeader reports the header belongs to the PoS-stage with some special fields. -// This function is not suitable for a part of APIs like Prepare or CalcDifficulty because -// the header difficulty is not set yet. -func (beacon *Beacon) IsPostMergeHeader(header *types.Header) bool { - // These fields can be used to filter out ethash block - if header.Difficulty.Cmp(beaconDifficulty) != 0 { - return false - } - if header.MixDigest != (common.Hash{}) { - return false - } - if header.Nonce != beaconNonce { - return false - } - // Extra field can be used to filter out clique block - if len(header.Extra) != 0 { - return false +// IsPoSHeader reports the header belongs to the PoS-stage with some special fields. +// This function is not suitable for a part of APIs like Prepare or CalcDifficulty +// because the header difficulty is not set yet. +func (beacon *Beacon) IsPoSHeader(header *types.Header) bool { + if header.Difficulty == nil { + return false // we should never enter here. } - return true + return header.Difficulty.Cmp(beaconDifficulty) == 0 } // MarkTransitioned sets the transitioned flag. diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index e74376418ef7..8d280906334a 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -311,7 +311,7 @@ func InsertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Hea TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), ReceiptHash: params.ReceiptRoot, Bloom: types.BytesToBloom(params.LogsBloom), - Difficulty: big.NewInt(1), + Difficulty: common.Big0, Number: number, GasLimit: params.GasLimit, GasUsed: params.GasUsed, diff --git a/eth/handler.go b/eth/handler.go index e41df5c4cd00..c45970483ac8 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -201,7 +201,7 @@ func newHandler(config *handlerConfig) (*handler, error) { // should only come from the trusted consensus layer instead of // p2p network. if beacon, ok := h.chain.Engine().(*beacon.Beacon); ok { - if beacon.IsPostMergeHeader(header) { + if beacon.IsPoSHeader(header) { return errors.New("unexpected post-merge header") } } @@ -487,7 +487,7 @@ func (h *handler) BroadcastBlock(block *types.Block, propagate bool) { } // Disable the block propagation if it's the post-merge block. if beacon, ok := h.chain.Engine().(*beacon.Beacon); ok { - if beacon.IsPostMergeHeader(block.Header()) { + if beacon.IsPoSHeader(block.Header()) { return } } From bc08e01e85af672e3f749ec91c9d2951a5c2548f Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 30 Jul 2021 10:36:09 +0800 Subject: [PATCH 04/43] eth: updates --- eth/handler.go | 15 +++++++-------- eth/handler_eth.go | 12 ++++++------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/eth/handler.go b/eth/handler.go index c45970483ac8..57360117a8c6 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -249,21 +249,20 @@ func newHandler(config *handlerConfig) (*handler, error) { // after the transition. In theory block gossip should be disabled // entirely whenever the transition is started. But in order to // handle the transition boundary reorg in the consensus-layer, - // the legacy blocks are still accepted but the chain head won't - // be updated. + // the legacy blocks are still accepted, but only for the terminal + // pow blocks. Spec: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3675.md#halt-the-importing-of-pow-blocks for i, block := range blocks { if err := h.chain.InsertBlock(block); err != nil { return i, err } } return 0, nil - } else { - n, err := h.chain.InsertChain(blocks) - if err == nil { - atomic.StoreUint32(&h.acceptTxs, 1) // Mark initial sync done on any fetcher import - } - return n, err } + n, err := h.chain.InsertChain(blocks) + if err == nil { + atomic.StoreUint32(&h.acceptTxs, 1) // Mark initial sync done on any fetcher import + } + return n, err } h.blockFetcher = fetcher.NewBlockFetcher(false, nil, h.chain.GetBlockByHash, validator, h.BroadcastBlock, heighter, nil, inserter, h.removePeer) diff --git a/eth/handler_eth.go b/eth/handler_eth.go index d6e8ac47eda1..1788a5849c1b 100644 --- a/eth/handler_eth.go +++ b/eth/handler_eth.go @@ -181,10 +181,10 @@ func (h *ethHandler) handleBodies(peer *eth.Peer, txs [][]*types.Transaction, un // batch of block announcements for the local node to process. func (h *ethHandler) handleBlockAnnounces(peer *eth.Peer, hashes []common.Hash, numbers []uint64) error { // Drop all incoming block announces from the p2p network if - // the chain already entered the pos stage. TODO perhaps we - // should return the error here to disconnect the legacy node. + // the chain already entered the pos stage and disconnect the + // remote peer. if h.merger.EnteredPoS() { - return nil + return errors.New("unexpected block announces") } // Schedule all the unknown hashes for retrieval var ( @@ -207,10 +207,10 @@ func (h *ethHandler) handleBlockAnnounces(peer *eth.Peer, hashes []common.Hash, // block broadcast for the local node to process. func (h *ethHandler) handleBlockBroadcast(peer *eth.Peer, block *types.Block, td *big.Int) error { // Drop all incoming block announces from the p2p network if - // the chain already entered the pos stage. TODO perhaps we - // should return the error here to disconnect the legacy node. + // the chain already entered the pos stage and disconnect the + // remote peer. if h.merger.EnteredPoS() { - return nil + return errors.New("unexpected block announces") } // Schedule the block for import h.blockFetcher.Enqueue(peer.ID(), block) From a1645a4b771eb1079a65fda2e2ef8ceae5484d43 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 30 Jul 2021 14:16:45 +0800 Subject: [PATCH 05/43] all: add terminalBlockDifficulty config --- consensus/misc/eip1559_test.go | 36 +++++++++++++++++----------------- eth/catalyst/api_test.go | 1 - eth/handler.go | 9 +++++++++ eth/tracers/tracer_test.go | 2 +- miner/stress/beacon/main.go | 24 ++++++++++++++++++++--- params/config.go | 13 ++++++++---- 6 files changed, 58 insertions(+), 27 deletions(-) diff --git a/consensus/misc/eip1559_test.go b/consensus/misc/eip1559_test.go index fd400b6881d1..23cd9023de24 100644 --- a/consensus/misc/eip1559_test.go +++ b/consensus/misc/eip1559_test.go @@ -29,24 +29,24 @@ import ( // do not use e.g. SetInt() on the numbers. For testing only func copyConfig(original *params.ChainConfig) *params.ChainConfig { return ¶ms.ChainConfig{ - ChainID: original.ChainID, - HomesteadBlock: original.HomesteadBlock, - DAOForkBlock: original.DAOForkBlock, - DAOForkSupport: original.DAOForkSupport, - EIP150Block: original.EIP150Block, - EIP150Hash: original.EIP150Hash, - EIP155Block: original.EIP155Block, - EIP158Block: original.EIP158Block, - ByzantiumBlock: original.ByzantiumBlock, - ConstantinopleBlock: original.ConstantinopleBlock, - PetersburgBlock: original.PetersburgBlock, - IstanbulBlock: original.IstanbulBlock, - MuirGlacierBlock: original.MuirGlacierBlock, - BerlinBlock: original.BerlinBlock, - LondonBlock: original.LondonBlock, - CatalystBlock: original.CatalystBlock, - Ethash: original.Ethash, - Clique: original.Clique, + ChainID: original.ChainID, + HomesteadBlock: original.HomesteadBlock, + DAOForkBlock: original.DAOForkBlock, + DAOForkSupport: original.DAOForkSupport, + EIP150Block: original.EIP150Block, + EIP150Hash: original.EIP150Hash, + EIP155Block: original.EIP155Block, + EIP158Block: original.EIP158Block, + ByzantiumBlock: original.ByzantiumBlock, + ConstantinopleBlock: original.ConstantinopleBlock, + PetersburgBlock: original.PetersburgBlock, + IstanbulBlock: original.IstanbulBlock, + MuirGlacierBlock: original.MuirGlacierBlock, + BerlinBlock: original.BerlinBlock, + LondonBlock: original.LondonBlock, + TerminalTotalDifficulty: original.TerminalTotalDifficulty, + Ethash: original.Ethash, + Clique: original.Clique, } } diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index a00e45c60c89..3c891e3a4882 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -85,7 +85,6 @@ func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block, MuirGlacierBlock: big.NewInt(0), BerlinBlock: big.NewInt(0), LondonBlock: big.NewInt(0), - CatalystBlock: big.NewInt(0), Ethash: new(params.EthashConfig), } genesis := &core.Genesis{ diff --git a/eth/handler.go b/eth/handler.go index 57360117a8c6..0a1919fb681c 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -252,6 +252,15 @@ func newHandler(config *handlerConfig) (*handler, error) { // the legacy blocks are still accepted, but only for the terminal // pow blocks. Spec: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3675.md#halt-the-importing-of-pow-blocks for i, block := range blocks { + ptd := h.chain.GetTd(block.ParentHash(), block.NumberU64()-1) + if ptd == nil { + return 0, nil + } + td := new(big.Int).Add(ptd, block.Difficulty()) + if !h.chain.Config().IsTerminalPoWBlock(ptd, td) { + log.Info("Filtered out non-termimal pow block", "number", block.NumberU64(), "hash", block.Hash()) + return 0, nil + } if err := h.chain.InsertBlock(block); err != nil { return i, err } diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go index 3decca225d7a..63b09bdc6f39 100644 --- a/eth/tracers/tracer_test.go +++ b/eth/tracers/tracer_test.go @@ -207,7 +207,7 @@ func TestNoStepExec(t *testing.T) { } func TestIsPrecompile(t *testing.T) { - chaincfg := ¶ms.ChainConfig{ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: false, EIP150Block: big.NewInt(0), EIP150Hash: common.Hash{}, EIP155Block: big.NewInt(0), EIP158Block: big.NewInt(0), ByzantiumBlock: big.NewInt(100), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(200), MuirGlacierBlock: big.NewInt(0), BerlinBlock: big.NewInt(300), LondonBlock: big.NewInt(0), CatalystBlock: nil, Ethash: new(params.EthashConfig), Clique: nil} + chaincfg := ¶ms.ChainConfig{ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: false, EIP150Block: big.NewInt(0), EIP150Hash: common.Hash{}, EIP155Block: big.NewInt(0), EIP158Block: big.NewInt(0), ByzantiumBlock: big.NewInt(100), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(200), MuirGlacierBlock: big.NewInt(0), BerlinBlock: big.NewInt(300), LondonBlock: big.NewInt(0), TerminalTotalDifficulty: nil, Ethash: new(params.EthashConfig), Clique: nil} chaincfg.ByzantiumBlock = big.NewInt(100) chaincfg.IstanbulBlock = big.NewInt(200) chaincfg.BerlinBlock = big.NewInt(300) diff --git a/miner/stress/beacon/main.go b/miner/stress/beacon/main.go index cb5b6b56a1a7..5d7a070faa53 100644 --- a/miner/stress/beacon/main.go +++ b/miner/stress/beacon/main.go @@ -57,9 +57,26 @@ const ( eth2LightClient ) +func (typ nodetype) String() string { + switch typ { + case legacyMiningNode: + return "legacyMiningNode" + case legacyNormalNode: + return "legacyNormalNode" + case eth2MiningNode: + return "eth2MiningNode" + case eth2NormalNode: + return "eth2NormalNode" + case eth2LightClient: + return "eth2LightClient" + default: + return "undefined" + } +} + var ( // transitionDifficulty is the target total difficulty for transition - transitionDifficulty = new(big.Int).Mul(big.NewInt(0), params.MinimumDifficulty) + transitionDifficulty = new(big.Int).Mul(big.NewInt(20), params.MinimumDifficulty) // blockInterval is the time interval for creating a new eth2 block blockInterval = time.Second * 3 @@ -285,7 +302,7 @@ func (mgr *nodeManager) run() { } transitioned, parentBlock = true, ev.Block timer.Reset(blockInterval) - log.Info("Transition difficulty reached", "td", td, "target", transitionDifficulty) + log.Info("Transition difficulty reached", "td", td, "target", transitionDifficulty, "number", ev.Block.NumberU64(), "hash", ev.Block.Hash()) case <-timer.C: producers := mgr.getNodes(eth2MiningNode) @@ -309,7 +326,7 @@ func (mgr *nodeManager) run() { for _, node := range nodes { if err := node.insertBlockAndSetHead(parentBlock.Header(), *ed); err != nil { - log.Error("Failed to insert block", "err", err) + log.Error("Failed to insert block", "type", node.typ, "err", err) } } log.Info("Create and insert eth2 block", "number", ed.Number) @@ -387,6 +404,7 @@ func makeGenesis(faucets []*ecdsa.PrivateKey) *core.Genesis { genesis.Config.ChainID = big.NewInt(18) genesis.Config.EIP150Hash = common.Hash{} genesis.BaseFee = big.NewInt(params.InitialBaseFee) + genesis.Config.TerminalTotalDifficulty = transitionDifficulty genesis.Alloc = core.GenesisAlloc{} for _, faucet := range faucets { diff --git a/params/config.go b/params/config.go index 1950b3d185ba..fdbff930240b 100644 --- a/params/config.go +++ b/params/config.go @@ -314,7 +314,9 @@ type ChainConfig struct { BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin) LondonBlock *big.Int `json:"londonBlock,omitempty"` // London switch block (nil = no fork, 0 = already on london) - CatalystBlock *big.Int `json:"catalystBlock,omitempty"` // Catalyst switch block (nil = no fork, 0 = already on catalyst) + // TerminalTotalDifficulty is the amount of total difficulty reached by + // the network that triggers the consensus upgrade. + TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"` // Various consensus engines Ethash *EthashConfig `json:"ethash,omitempty"` @@ -432,9 +434,12 @@ func (c *ChainConfig) IsLondon(num *big.Int) bool { return isForked(c.LondonBlock, num) } -// IsCatalyst returns whether num is either equal to the Merge fork block or greater. -func (c *ChainConfig) IsCatalyst(num *big.Int) bool { - return isForked(c.CatalystBlock, num) +// IsTerminalPoWBlock returns whether the given block is the last block of PoW stage. +func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *big.Int) bool { + if c.TerminalTotalDifficulty == nil { + return false + } + return parentTotalDiff.Cmp(c.TerminalTotalDifficulty) < 0 && totalDiff.Cmp(c.TerminalTotalDifficulty) >= 0 } // CheckCompatible checks whether scheduled fork transitions have been imported From d460aa4e71dbc3c6bd7e0a65f00cfaecbf9b5b57 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 30 Jul 2021 15:59:59 +0800 Subject: [PATCH 06/43] miner/stress/beacon: fix stress test --- consensus/beacon/consensus.go | 7 +++---- miner/stress/beacon/main.go | 9 +++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index ab94cc135d74..6126e2571fcb 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -52,7 +52,7 @@ var ( // Beacon is a consensus engine combines the ethereum 1 consensus and proof-of-stake // algorithm. There is a special flag inside to decide whether to use legacy consensus // rules or new rules. The transition rule is described in the eth1/2 merge spec. -// https://hackmd.io/@n0ble/ethereum_consensus_upgrade_mainnet_perspective#Transition-process +// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3675.md // // The beacon here is a half-functional consensus engine with partial functions which // is only used for necessary consensus checks. The legacy consensus engine can be any @@ -78,8 +78,7 @@ func New(ethone consensus.Engine, transitioned bool) *Beacon { } } -// Author implements consensus.Engine, returning the header's coinbase as the -// verified author of the block. +// Author implements consensus.Engine, returning the verified author of the block. func (beacon *Beacon) Author(header *types.Header) (common.Address, error) { if !beacon.IsPoSHeader(header) { return beacon.ethone.Author(header) @@ -182,7 +181,7 @@ func (beacon *Beacon) VerifyUncles(chain consensus.ChainReader, block *types.Blo } // verifyHeader checks whether a header conforms to the consensus rules of the -// stock Ethereum consensus engine. The difference between the beacon and ethash is +// stock Ethereum consensus engine. The difference between the beacon and classic is // (a) the difficulty, mixhash, nonce, extradata and unclehash are expected // to be the desired constants // (b) the timestamp is not verified anymore diff --git a/miner/stress/beacon/main.go b/miner/stress/beacon/main.go index 5d7a070faa53..30e6e8ee044d 100644 --- a/miner/stress/beacon/main.go +++ b/miner/stress/beacon/main.go @@ -435,7 +435,7 @@ func makeFullNode(genesis *core.Genesis) (*node.Node, *eth.Ethereum, *catalyst.C if err != nil { return nil, nil, nil, err } - ethBackend, err := eth.New(stack, ðconfig.Config{ + econfig := ðconfig.Config{ Genesis: genesis, NetworkId: genesis.Config.ChainID.Uint64(), SyncMode: downloader.FullSync, @@ -453,10 +453,15 @@ func makeFullNode(genesis *core.Genesis) (*node.Node, *eth.Ethereum, *catalyst.C LightServ: 100, LightPeers: 10, LightNoSyncServe: true, - }) + } + ethBackend, err := eth.New(stack, econfig) if err != nil { return nil, nil, nil, err } + _, err = les.NewLesServer(stack, ethBackend, econfig) + if err != nil { + log.Crit("Failed to create the LES server", "err", err) + } err = stack.Start() return stack, ethBackend, catalyst.NewConsensusAPI(ethBackend, nil), err } From 150a1eb7f9a64eaec7121a9b6ea21959f3722e31 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 20 Sep 2021 12:25:28 +0200 Subject: [PATCH 07/43] core: rebased on master --- core/blockchain.go | 4 ++-- core/state_processor_test.go | 2 +- les/client_handler.go | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 7868dbe8a1ca..de2826896bb6 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2067,8 +2067,8 @@ func (bc *BlockChain) insertBlock(block *types.Block) error { } // If the header is a banned one, straight out abort if BadHashes[block.Hash()] { - bc.reportBlock(block, nil, ErrBlacklistedHash) - return ErrBlacklistedHash + bc.reportBlock(block, nil, ErrBannedHash) + return ErrBannedHash } // Retrieve the parent block and it's state to execute on top start := time.Now() diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 621879bf5150..40e33ccb934a 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -260,7 +260,7 @@ func TestStateProcessorErrors(t *testing.T) { }, } genesis = gspec.MustCommit(db) - blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, &Merger{}) ) defer blockchain.Stop() for i, tt := range []struct { diff --git a/les/client_handler.go b/les/client_handler.go index de8bdf92dd8d..3125869f6a0c 100644 --- a/les/client_handler.go +++ b/les/client_handler.go @@ -18,6 +18,7 @@ package les import ( "context" + "fmt" "math/big" "math/rand" "sync" @@ -146,7 +147,8 @@ func (h *clientHandler) handle(p *serverPeer, noInitAnnounce bool) error { // Discard all the announces after the transition // Also discarding initial signal to prevent syncing during testing. - if !h.backend.merger.LeftPoW() || !noInitAnnounce { + if !(noInitAnnounce || h.backend.merger.LeftPoW()) { + fmt.Print("adf") h.fetcher.announce(p, &announceData{Hash: p.headInfo.Hash, Number: p.headInfo.Number, Td: p.headInfo.Td}) } From cb89e26116fc37ce1e23c83b140147564c23a10d Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 20 Sep 2021 13:48:03 +0200 Subject: [PATCH 08/43] eth: implemented merge interop spec --- eth/catalyst/api.go | 55 +++++++++++++++++++---- eth/catalyst/api_test.go | 12 ++--- eth/catalyst/api_types.go | 49 +++++++++++++------- eth/catalyst/gen_ed.go | 89 +++++++++++++++++++++++-------------- miner/stress/beacon/main.go | 6 ++- 5 files changed, 146 insertions(+), 65 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 8d280906334a..950f6acc2327 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -58,7 +58,7 @@ func RegisterLight(stack *node.Node, backend *les.LightEthereum) error { log.Warn("Catalyst mode enabled", "protocol", "les") stack.RegisterAPIs([]rpc.API{ { - Namespace: "consensus", + Namespace: "engine", Version: "1.0", Service: NewConsensusAPI(nil, backend), Public: true, @@ -68,11 +68,12 @@ func RegisterLight(stack *node.Node, backend *les.LightEthereum) error { } type ConsensusAPI struct { - light bool - eth *eth.Ethereum - les *les.LightEthereum - engine consensus.Engine // engine is the post-merge consensus engine, only for block creation - syncer *syncer // syncer is responsible for triggering chain sync + light bool + eth *eth.Ethereum + les *les.LightEthereum + engine consensus.Engine // engine is the post-merge consensus engine, only for block creation + syncer *syncer // syncer is responsible for triggering chain sync + preparedBlocks map[int]*ExecutableData } func NewConsensusAPI(eth *eth.Ethereum, les *les.LightEthereum) *ConsensusAPI { @@ -155,9 +156,45 @@ func (api *ConsensusAPI) makeEnv(parent *types.Block, header *types.Header) (*bl return env, nil } +func (api *ConsensusAPI) PreparePayload(params AssembleBlockParams) (*PayloadResponse, error) { + data, err := api.assembleBlock(params) + if err != nil { + return nil, err + } + id := len(api.preparedBlocks) + api.preparedBlocks[id] = data + return &PayloadResponse{PayloadID: uint64(id)}, nil +} + +func (api *ConsensusAPI) GetPayload(PayloadID uint64) (*ExecutableData, error) { + data, ok := api.preparedBlocks[int(PayloadID)] + if !ok { + return nil, errors.New("payload not found") + } + return data, nil +} + +// ConsensusValidated is called to mark a block as valid, so +// that data that is no longer needed can be removed. +func (api *ConsensusAPI) ConsensusValidated(params ConsensusValidatedParams) error { + if params.Status == "VALID" { + // Finalize the transition if it's the first `FinalisedBlock` event. + merger := api.merger() + if !merger.EnteredPoS() { + merger.EnterPoS() + } + } + return nil +} + +func (api *ConsensusAPI) ForkChoiceUpdated(params ForkChoiceParams) error { + _, err := api.SetHead(params.FinalizedBlockHash) + return err +} + // AssembleBlock creates a new block, inserts it into the chain, and returns the "execution // data" required for eth2 clients to process the new block. -func (api *ConsensusAPI) AssembleBlock(params AssembleBlockParams) (*ExecutableData, error) { +func (api *ConsensusAPI) assembleBlock(params AssembleBlockParams) (*ExecutableData, error) { if api.light { return nil, errors.New("not supported") } @@ -264,7 +301,7 @@ func (api *ConsensusAPI) AssembleBlock(params AssembleBlockParams) (*ExecutableD return &ExecutableData{ BlockHash: block.Hash(), ParentHash: block.ParentHash(), - Miner: block.Coinbase(), + Coinbase: block.Coinbase(), StateRoot: block.Root(), Number: block.NumberU64(), GasLimit: block.GasLimit(), @@ -306,7 +343,7 @@ func InsertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Hea header := &types.Header{ ParentHash: params.ParentHash, UncleHash: types.EmptyUncleHash, - Coinbase: params.Miner, + Coinbase: params.Coinbase, Root: params.StateRoot, TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), ReceiptHash: params.ReceiptRoot, diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 3c891e3a4882..fb5a4f7b7810 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -126,7 +126,7 @@ func TestEth2AssembleBlock(t *testing.T) { ParentHash: blocks[9].Hash(), Timestamp: blocks[9].Time() + 5, } - execData, err := api.AssembleBlock(blockParams) + execData, err := api.assembleBlock(blockParams) if err != nil { t.Fatalf("error producing block, err=%v", err) } @@ -148,7 +148,7 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { ParentHash: blocks[8].Hash(), Timestamp: blocks[8].Time() + 5, } - execData, err := api.AssembleBlock(blockParams) + execData, err := api.assembleBlock(blockParams) if err != nil { t.Fatalf("error producing block, err=%v", err) } @@ -196,10 +196,10 @@ func TestEth2NewBlock(t *testing.T) { for i := 0; i < 10; i++ { statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) nonce := statedb.GetNonce(testAddr) - tx, err := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) + tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) ethservice.TxPool().AddLocal(tx) - execData, err := api.AssembleBlock(AssembleBlockParams{ + execData, err := api.assembleBlock(AssembleBlockParams{ ParentHash: parent.Hash(), Timestamp: parent.Time() + 5, }) @@ -237,7 +237,7 @@ func TestEth2NewBlock(t *testing.T) { ) parent = preMergeBlocks[len(preMergeBlocks)-1] for i := 0; i < 10; i++ { - execData, err := api.AssembleBlock(AssembleBlockParams{ + execData, err := api.assembleBlock(AssembleBlockParams{ ParentHash: parent.Hash(), Timestamp: parent.Time() + 6, }) @@ -281,7 +281,7 @@ func TestEth2DeepReorg(t *testing.T) { t.Errorf("Block %d not pruned", parent.NumberU64()) } for i := 0; i < 10; i++ { - execData, err := api.AssembleBlock(AssembleBlockParams{ + execData, err := api.assembleBlock(AssembleBlockParams{ ParentHash: parent.Hash(), Timestamp: parent.Time() + 5, }) diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index 4f6c18ce4217..ef6aabaecd38 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -23,10 +23,12 @@ import ( //go:generate go run github.com/fjl/gencodec -type AssembleBlockParams -field-override assembleBlockParamsMarshaling -out gen_blockparams.go -// Structure described at https://hackmd.io/T9x2mMA4S7us8tJwEB3FDQ +// Structure described at https://github.com/ethereum/execution-apis/pull/74 type AssembleBlockParams struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - Timestamp uint64 `json:"timestamp" gencodec:"required"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + Timestamp uint64 `json:"timestamp" gencodec:"required"` + Random common.Hash `json:"random" gencodec:"required"` + FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` } // JSON type overrides for assembleBlockParams. @@ -36,19 +38,22 @@ type assembleBlockParamsMarshaling struct { //go:generate go run github.com/fjl/gencodec -type ExecutableData -field-override executableDataMarshaling -out gen_ed.go -// Structure described at https://notes.ethereum.org/@n0ble/rayonism-the-merge-spec#Parameters1 +// Structure described at https://github.com/ethereum/execution-apis/pull/74/files type ExecutableData struct { - BlockHash common.Hash `json:"blockHash" gencodec:"required"` - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - Miner common.Address `json:"miner" gencodec:"required"` - StateRoot common.Hash `json:"stateRoot" gencodec:"required"` - Number uint64 `json:"number" gencodec:"required"` - GasLimit uint64 `json:"gasLimit" gencodec:"required"` - GasUsed uint64 `json:"gasUsed" gencodec:"required"` - Timestamp uint64 `json:"timestamp" gencodec:"required"` - ReceiptRoot common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom []byte `json:"logsBloom" gencodec:"required"` - Transactions [][]byte `json:"transactions" gencodec:"required"` + BlockHash common.Hash `json:"blockHash" gencodec:"required"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + Coinbase common.Address `json:"coinbase" gencodec:"required"` + StateRoot common.Hash `json:"stateRoot" gencodec:"required"` + ReceiptRoot common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom []byte `json:"logsBloom" gencodec:"required"` + Random common.Hash `json:"random" gencodec:"required"` + Number uint64 `json:"number" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + Timestamp uint64 `json:"timestamp" gencodec:"required"` + ExtraData []byte `json:"extraData" gencodec:"required"` + BaseFeePerGas uint64 `json:"baseFeePerGas" gencodec:"required"` + Transactions [][]byte `json:"transactions" gencodec:"required"` } // JSON type overrides for executableData. @@ -68,3 +73,17 @@ type NewBlockResponse struct { type GenericResponse struct { Success bool `json:"success"` } + +type PayloadResponse struct { + PayloadID uint64 `json:"payloadId"` +} + +type ConsensusValidatedParams struct { + BlockHash common.Hash `json:"blockHash"` + Status string `json:"status"` +} + +type ForkChoiceParams struct { + HeadBlockHash common.Hash `json:"headBlockHash"` + FinalizedBlockHash common.Hash `json:"finalizedBlockHash"` +} diff --git a/eth/catalyst/gen_ed.go b/eth/catalyst/gen_ed.go index 850d8617a5ae..cdf6cc96233a 100644 --- a/eth/catalyst/gen_ed.go +++ b/eth/catalyst/gen_ed.go @@ -15,29 +15,35 @@ var _ = (*executableDataMarshaling)(nil) // MarshalJSON marshals as JSON. func (e ExecutableData) MarshalJSON() ([]byte, error) { type ExecutableData struct { - BlockHash common.Hash `json:"blockHash" gencodec:"required"` - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - Miner common.Address `json:"miner" gencodec:"required"` - StateRoot common.Hash `json:"stateRoot" gencodec:"required"` - Number hexutil.Uint64 `json:"number" gencodec:"required"` - GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` - ReceiptRoot common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` - Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` + BlockHash common.Hash `json:"blockHash" gencodec:"required"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + Coinbase common.Address `json:"coinbase" gencodec:"required"` + StateRoot common.Hash `json:"stateRoot" gencodec:"required"` + ReceiptRoot common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` + Random common.Hash `json:"random" gencodec:"required"` + Number hexutil.Uint64 `json:"number" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` + ExtraData []byte `json:"extraData" gencodec:"required"` + BaseFeePerGas uint64 `json:"baseFeePerGas" gencodec:"required"` + Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } var enc ExecutableData enc.BlockHash = e.BlockHash enc.ParentHash = e.ParentHash - enc.Miner = e.Miner + enc.Coinbase = e.Coinbase enc.StateRoot = e.StateRoot + enc.ReceiptRoot = e.ReceiptRoot + enc.LogsBloom = e.LogsBloom + enc.Random = e.Random enc.Number = hexutil.Uint64(e.Number) enc.GasLimit = hexutil.Uint64(e.GasLimit) enc.GasUsed = hexutil.Uint64(e.GasUsed) enc.Timestamp = hexutil.Uint64(e.Timestamp) - enc.ReceiptRoot = e.ReceiptRoot - enc.LogsBloom = e.LogsBloom + enc.ExtraData = e.ExtraData + enc.BaseFeePerGas = e.BaseFeePerGas if e.Transactions != nil { enc.Transactions = make([]hexutil.Bytes, len(e.Transactions)) for k, v := range e.Transactions { @@ -50,17 +56,20 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (e *ExecutableData) UnmarshalJSON(input []byte) error { type ExecutableData struct { - BlockHash *common.Hash `json:"blockHash" gencodec:"required"` - ParentHash *common.Hash `json:"parentHash" gencodec:"required"` - Miner *common.Address `json:"miner" gencodec:"required"` - StateRoot *common.Hash `json:"stateRoot" gencodec:"required"` - Number *hexutil.Uint64 `json:"number" gencodec:"required"` - GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` - Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` - ReceiptRoot *common.Hash `json:"receiptsRoot" gencodec:"required"` - LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` - Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` + BlockHash *common.Hash `json:"blockHash" gencodec:"required"` + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + Coinbase *common.Address `json:"coinbase" gencodec:"required"` + StateRoot *common.Hash `json:"stateRoot" gencodec:"required"` + ReceiptRoot *common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` + Random *common.Hash `json:"random" gencodec:"required"` + Number *hexutil.Uint64 `json:"number" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + ExtraData []byte `json:"extraData" gencodec:"required"` + BaseFeePerGas *uint64 `json:"baseFeePerGas" gencodec:"required"` + Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } var dec ExecutableData if err := json.Unmarshal(input, &dec); err != nil { @@ -74,14 +83,26 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'parentHash' for ExecutableData") } e.ParentHash = *dec.ParentHash - if dec.Miner == nil { - return errors.New("missing required field 'miner' for ExecutableData") + if dec.Coinbase == nil { + return errors.New("missing required field 'coinbase' for ExecutableData") } - e.Miner = *dec.Miner + e.Coinbase = *dec.Coinbase if dec.StateRoot == nil { return errors.New("missing required field 'stateRoot' for ExecutableData") } e.StateRoot = *dec.StateRoot + if dec.ReceiptRoot == nil { + return errors.New("missing required field 'receiptsRoot' for ExecutableData") + } + e.ReceiptRoot = *dec.ReceiptRoot + if dec.LogsBloom == nil { + return errors.New("missing required field 'logsBloom' for ExecutableData") + } + e.LogsBloom = *dec.LogsBloom + if dec.Random == nil { + return errors.New("missing required field 'random' for ExecutableData") + } + e.Random = *dec.Random if dec.Number == nil { return errors.New("missing required field 'number' for ExecutableData") } @@ -98,14 +119,14 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'timestamp' for ExecutableData") } e.Timestamp = uint64(*dec.Timestamp) - if dec.ReceiptRoot == nil { - return errors.New("missing required field 'receiptsRoot' for ExecutableData") + if dec.ExtraData == nil { + return errors.New("missing required field 'extraData' for ExecutableData") } - e.ReceiptRoot = *dec.ReceiptRoot - if dec.LogsBloom == nil { - return errors.New("missing required field 'logsBloom' for ExecutableData") + e.ExtraData = dec.ExtraData + if dec.BaseFeePerGas == nil { + return errors.New("missing required field 'baseFeePerGas' for ExecutableData") } - e.LogsBloom = *dec.LogsBloom + e.BaseFeePerGas = *dec.BaseFeePerGas if dec.Transactions == nil { return errors.New("missing required field 'transactions' for ExecutableData") } diff --git a/miner/stress/beacon/main.go b/miner/stress/beacon/main.go index 30e6e8ee044d..24f984f73a9e 100644 --- a/miner/stress/beacon/main.go +++ b/miner/stress/beacon/main.go @@ -140,10 +140,14 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) if n.typ != eth2MiningNode { return nil, errors.New("invalid node type") } - return n.api.AssembleBlock(catalyst.AssembleBlockParams{ + payload, err := n.api.PreparePayload(catalyst.AssembleBlockParams{ ParentHash: parentHash, Timestamp: uint64(time.Now().Unix()), }) + if err != nil { + return nil, err + } + return n.api.GetPayload(payload.PayloadID) } func (n *ethNode) insertBlock(eb catalyst.ExecutableData) error { From 37ddc085da10fff5a32672fd53c5a2dd97bb0013 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 22 Sep 2021 12:55:34 +0200 Subject: [PATCH 09/43] eth: fix tests, remove unused methods --- eth/catalyst/api.go | 110 +++++++++++++++++++++--------------- eth/catalyst/api_test.go | 48 ++++++++++++---- eth/catalyst/api_types.go | 4 ++ miner/stress/beacon/main.go | 11 +--- 4 files changed, 106 insertions(+), 67 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 950f6acc2327..7d79c5aa1a2c 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -39,12 +39,18 @@ import ( "github.com/ethereum/go-ethereum/trie" ) +var ( + VALID = GenericStringResponse{"VALID"} + INVALID = GenericStringResponse{"INVALID"} + SYNCING = GenericStringResponse{"SYNCING"} +) + // Register adds catalyst APIs to the full node. func Register(stack *node.Node, backend *eth.Ethereum) error { log.Warn("Catalyst mode enabled", "protocol", "eth") stack.RegisterAPIs([]rpc.API{ { - Namespace: "consensus", + Namespace: "engine", Version: "1.0", Service: NewConsensusAPI(backend, nil), Public: true, @@ -92,11 +98,12 @@ func NewConsensusAPI(eth *eth.Ethereum, les *les.LightEthereum) *ConsensusAPI { } } return &ConsensusAPI{ - light: eth == nil, - eth: eth, - les: les, - engine: engine, - syncer: newSyncer(), + light: eth == nil, + eth: eth, + les: les, + engine: engine, + syncer: newSyncer(), + preparedBlocks: make(map[int]*ExecutableData), } } @@ -177,19 +184,56 @@ func (api *ConsensusAPI) GetPayload(PayloadID uint64) (*ExecutableData, error) { // ConsensusValidated is called to mark a block as valid, so // that data that is no longer needed can be removed. func (api *ConsensusAPI) ConsensusValidated(params ConsensusValidatedParams) error { - if params.Status == "VALID" { + switch params.Status { + case "VALID": // Finalize the transition if it's the first `FinalisedBlock` event. merger := api.merger() if !merger.EnteredPoS() { merger.EnterPoS() } + return api.SetHead(params.BlockHash) + case "INVALID": + return nil + default: + return errors.New("invalid params.status") } - return nil } func (api *ConsensusAPI) ForkChoiceUpdated(params ForkChoiceParams) error { - _, err := api.SetHead(params.FinalizedBlockHash) - return err + return api.SetHead(params.FinalizedBlockHash) +} + +// ExecutePayload creates an Eth1 block, inserts it in the chain, and returns the status of the chain. +func (api *ConsensusAPI) ExecutePayload(params ExecutableData) (GenericStringResponse, error) { + if api.light { + parent := api.les.BlockChain().GetHeaderByHash(params.ParentHash) + if parent == nil { + return INVALID, fmt.Errorf("could not find parent %x", params.ParentHash) + } + block, err := InsertBlockParamsToBlock(api.les.BlockChain().Config(), parent, params) + if err != nil { + return INVALID, err + } + if err = api.les.BlockChain().InsertHeader(block.Header()); err != nil { + return INVALID, err + } + return VALID, nil + } + if !api.eth.Synced() { + return SYNCING, errors.New("node is not synced yet") + } + parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash) + if parent == nil { + return INVALID, fmt.Errorf("could not find parent %x", params.ParentHash) + } + block, err := InsertBlockParamsToBlock(api.eth.BlockChain().Config(), parent.Header(), params) + if err != nil { + return INVALID, err + } + if err := api.eth.BlockChain().InsertBlock(block); err != nil { + return INVALID, err + } + return VALID, nil } // AssembleBlock creates a new block, inserts it into the chain, and returns the "execution @@ -364,34 +408,6 @@ func InsertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Hea return block, nil } -// NewBlock creates an Eth1 block, inserts it in the chain, and either returns true, -// or false + an error. This is a bit redundant for go, but simplifies things on the -// eth2 side. -func (api *ConsensusAPI) NewBlock(params ExecutableData) (*NewBlockResponse, error) { - if api.light { - parent := api.les.BlockChain().GetHeaderByHash(params.ParentHash) - if parent == nil { - return &NewBlockResponse{false}, fmt.Errorf("could not find parent %x", params.ParentHash) - } - block, err := InsertBlockParamsToBlock(api.les.BlockChain().Config(), parent, params) - if err != nil { - return nil, err - } - err = api.les.BlockChain().InsertHeader(block.Header()) - return &NewBlockResponse{err == nil}, err - } - parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash) - if parent == nil { - return &NewBlockResponse{false}, fmt.Errorf("could not find parent %x", params.ParentHash) - } - block, err := InsertBlockParamsToBlock(api.eth.BlockChain().Config(), parent.Header(), params) - if err != nil { - return nil, err - } - err = api.eth.BlockChain().InsertBlock(block) - return &NewBlockResponse{err == nil}, err -} - // Used in tests to add a the list of transactions from a block to the tx pool. func (api *ConsensusAPI) addBlockTxs(block *types.Block) error { for _, tx := range block.Transactions() { @@ -412,7 +428,7 @@ func (api *ConsensusAPI) FinalizeBlock(blockHash common.Hash) (*GenericResponse, } // SetHead is called to perform a force choice. -func (api *ConsensusAPI) SetHead(newHead common.Hash) (*GenericResponse, error) { +func (api *ConsensusAPI) SetHead(newHead common.Hash) error { // Trigger the transition if it's the first `NewHead` event. merger := api.merger() if !merger.LeftPoW() { @@ -421,30 +437,30 @@ func (api *ConsensusAPI) SetHead(newHead common.Hash) (*GenericResponse, error) if api.light { headHeader := api.les.BlockChain().CurrentHeader() if headHeader.Hash() == newHead { - return &GenericResponse{true}, nil + return nil } newHeadHeader := api.les.BlockChain().GetHeaderByHash(newHead) if newHeadHeader == nil { - return &GenericResponse{false}, nil + return errors.New("head not found") } if err := api.les.BlockChain().SetChainHead(newHeadHeader); err != nil { - return &GenericResponse{false}, nil + return err } - return &GenericResponse{true}, nil + return nil } headBlock := api.eth.BlockChain().CurrentBlock() if headBlock.Hash() == newHead { - return &GenericResponse{true}, nil + return nil } newHeadBlock := api.eth.BlockChain().GetBlockByHash(newHead) if newHeadBlock == nil { - return &GenericResponse{false}, nil + return errors.New("head not found") } if err := api.eth.BlockChain().SetChainHead(newHeadBlock); err != nil { - return &GenericResponse{false}, nil + return err } api.eth.SetSynced() - return &GenericResponse{true}, nil + return nil } // Helper function, return the merger instance. diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index fb5a4f7b7810..73420e7cca9e 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -157,6 +157,32 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { } } +func TestEth2PrepareAndGetPayload(t *testing.T) { + genesis, blocks := generatePreMergeChain(10) + n, ethservice := startEthService(t, genesis, blocks[:9]) + defer n.Close() + + api := NewConsensusAPI(ethservice, nil) + + // Put the 10th block's tx in the pool and produce a new block + api.addBlockTxs(blocks[9]) + blockParams := AssembleBlockParams{ + ParentHash: blocks[8].Hash(), + Timestamp: blocks[8].Time() + 5, + } + respID, err := api.PreparePayload(blockParams) + if err != nil { + t.Fatalf("error preparing payload, err=%v", err) + } + execData, err := api.GetPayload(respID.PayloadID) + if err != nil { + t.Fatalf("error getting payload, err=%v", err) + } + if len(execData.Transactions) != blocks[9].Transactions().Len() { + t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) + } +} + func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan core.RemovedLogsEvent, wantNew, wantRemoved int) { t.Helper() @@ -210,8 +236,8 @@ func TestEth2NewBlock(t *testing.T) { if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } - newResp, err := api.NewBlock(*execData) - if err != nil || !newResp.Valid { + newResp, err := api.ExecutePayload(*execData) + if err != nil || newResp.Status != "VALID" { t.Fatalf("Failed to insert block: %v", err) } if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64()-1 { @@ -219,8 +245,7 @@ func TestEth2NewBlock(t *testing.T) { } checkLogEvents(t, newLogCh, rmLogsCh, 0, 0) - setResp, err := api.SetHead(block.Hash()) - if err != nil || !setResp.Success { + if err := api.ForkChoiceUpdated(ForkChoiceParams{HeadBlockHash: block.Hash(), FinalizedBlockHash: block.Hash()}); err != nil { t.Fatalf("Failed to insert block: %v", err) } if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { @@ -248,16 +273,15 @@ func TestEth2NewBlock(t *testing.T) { if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } - newResp, err := api.NewBlock(*execData) - if err != nil || !newResp.Valid { + newResp, err := api.ExecutePayload(*execData) + if err != nil || newResp.Status != "VALID" { t.Fatalf("Failed to insert block: %v", err) } if ethservice.BlockChain().CurrentBlock().NumberU64() != head { t.Fatalf("Chain head shouldn't be updated") } - setResp, err := api.SetHead(block.Hash()) - if err != nil || !setResp.Success { + if err := api.ForkChoiceUpdated(ForkChoiceParams{HeadBlockHash: block.Hash(), FinalizedBlockHash: block.Hash()}); err != nil { t.Fatalf("Failed to insert block: %v", err) } if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { @@ -292,15 +316,14 @@ func TestEth2DeepReorg(t *testing.T) { if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } - newResp, err := api.NewBlock(*execData) - if err != nil || !newResp.Valid { + newResp, err := api.ExecutePayload(*execData) + if err != nil || newResp.Status != "VALID" { t.Fatalf("Failed to insert block: %v", err) } if ethservice.BlockChain().CurrentBlock().NumberU64() != head { t.Fatalf("Chain head shouldn't be updated") } - setResp, err := api.SetHead(block.Hash()) - if err != nil || !setResp.Success { + if err := api.ForkChoiceUpdated(ForkChoiceParams{HeadBlockHash: block.Hash(), FinalizedBlockHash: block.Hash()}); err != nil { t.Fatalf("Failed to insert block: %v", err) } if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { @@ -332,6 +355,7 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) t.Fatal("can't import test blocks:", err) } ethservice.SetEtherbase(testAddr) + ethservice.SetSynced() return n, ethservice } diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index ef6aabaecd38..0c6044c06489 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -78,6 +78,10 @@ type PayloadResponse struct { PayloadID uint64 `json:"payloadId"` } +type GenericStringResponse struct { + Status string `json:"status"` +} + type ConsensusValidatedParams struct { BlockHash common.Hash `json:"blockHash"` Status string `json:"status"` diff --git a/miner/stress/beacon/main.go b/miner/stress/beacon/main.go index 24f984f73a9e..d2fe89b6b160 100644 --- a/miner/stress/beacon/main.go +++ b/miner/stress/beacon/main.go @@ -154,11 +154,10 @@ func (n *ethNode) insertBlock(eb catalyst.ExecutableData) error { if !eth2types(n.typ) { return errors.New("invalid node type") } - response, err := n.api.NewBlock(eb) + newResp, err := n.api.ExecutePayload(eb) if err != nil { return err - } - if !response.Valid { + } else if newResp.Status != "VALID" { return errors.New("failed to insert block") } return nil @@ -181,13 +180,9 @@ func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed catalyst.Execut if err != nil { return err } - response, err := n.api.SetHead(block.Hash()) - if err != nil { + if err := n.api.ForkChoiceUpdated(catalyst.ForkChoiceParams{HeadBlockHash: block.Hash(), FinalizedBlockHash: block.Hash()}); err != nil { return err } - if !response.Success { - return errors.New("failed to set head") - } return nil } From 881613d245afdd935eeef26e9db64e2f53e73f45 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 22 Sep 2021 13:20:47 +0200 Subject: [PATCH 10/43] eth: fix stress-test --- eth/catalyst/api_test.go | 2 +- les/client_handler.go | 2 -- miner/stress/beacon/main.go | 3 ++- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 73420e7cca9e..622bf3d0def2 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -281,7 +281,7 @@ func TestEth2NewBlock(t *testing.T) { t.Fatalf("Chain head shouldn't be updated") } - if err := api.ForkChoiceUpdated(ForkChoiceParams{HeadBlockHash: block.Hash(), FinalizedBlockHash: block.Hash()}); err != nil { + if err := api.ConsensusValidated(ConsensusValidatedParams{BlockHash: block.Hash(), Status: "VALID"}); err != nil { t.Fatalf("Failed to insert block: %v", err) } if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { diff --git a/les/client_handler.go b/les/client_handler.go index 3125869f6a0c..fd78ab34877f 100644 --- a/les/client_handler.go +++ b/les/client_handler.go @@ -18,7 +18,6 @@ package les import ( "context" - "fmt" "math/big" "math/rand" "sync" @@ -148,7 +147,6 @@ func (h *clientHandler) handle(p *serverPeer, noInitAnnounce bool) error { // Discard all the announces after the transition // Also discarding initial signal to prevent syncing during testing. if !(noInitAnnounce || h.backend.merger.LeftPoW()) { - fmt.Print("adf") h.fetcher.announce(p, &announceData{Hash: p.headInfo.Hash, Number: p.headInfo.Number, Td: p.headInfo.Td}) } diff --git a/miner/stress/beacon/main.go b/miner/stress/beacon/main.go index d2fe89b6b160..274d2f0ca329 100644 --- a/miner/stress/beacon/main.go +++ b/miner/stress/beacon/main.go @@ -122,6 +122,7 @@ func newNode(typ nodetype, genesis *core.Genesis, enodes []*enode.Node) *ethNode enode := stack.Server().Self() // Inject the signer key and start sealing with it + stack.AccountManager().AddBackend(keystore.NewPlaintextKeyStore("beacon-stress")) store := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) if _, err := store.NewAccount(""); err != nil { panic(err) @@ -180,7 +181,7 @@ func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed catalyst.Execut if err != nil { return err } - if err := n.api.ForkChoiceUpdated(catalyst.ForkChoiceParams{HeadBlockHash: block.Hash(), FinalizedBlockHash: block.Hash()}); err != nil { + if err := n.api.ConsensusValidated(catalyst.ConsensusValidatedParams{BlockHash: block.Hash(), Status: "VALID"}); err != nil { return err } return nil From bdb6070a2e490536dfa6ab34172f212a40d91559 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 22 Sep 2021 17:19:37 +0200 Subject: [PATCH 11/43] eth/catalyst: regenerate api types --- eth/catalyst/api_types.go | 20 ++++++++++++------ eth/catalyst/gen_blockparams.go | 22 ++++++++++++++++---- eth/catalyst/gen_ed.go | 8 ++++---- eth/catalyst/gen_payload.go | 36 +++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 eth/catalyst/gen_payload.go diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index 0c6044c06489..70fd11d7bfbd 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -58,12 +58,13 @@ type ExecutableData struct { // JSON type overrides for executableData. type executableDataMarshaling struct { - Number hexutil.Uint64 - GasLimit hexutil.Uint64 - GasUsed hexutil.Uint64 - Timestamp hexutil.Uint64 - LogsBloom hexutil.Bytes - Transactions []hexutil.Bytes + Number hexutil.Uint64 + GasLimit hexutil.Uint64 + GasUsed hexutil.Uint64 + Timestamp hexutil.Uint64 + BaseFeePerGas hexutil.Uint64 + LogsBloom hexutil.Bytes + Transactions []hexutil.Bytes } type NewBlockResponse struct { @@ -74,10 +75,17 @@ type GenericResponse struct { Success bool `json:"success"` } +//go:generate go run github.com/fjl/gencodec -type PayloadResponse -field-override payloadResponseMarshaling -out gen_payload.go + type PayloadResponse struct { PayloadID uint64 `json:"payloadId"` } +// JSON type overrides for payloadResponse. +type payloadResponseMarshaling struct { + PayloadID hexutil.Uint64 +} + type GenericStringResponse struct { Status string `json:"status"` } diff --git a/eth/catalyst/gen_blockparams.go b/eth/catalyst/gen_blockparams.go index 4b4182629960..9928c12908a9 100644 --- a/eth/catalyst/gen_blockparams.go +++ b/eth/catalyst/gen_blockparams.go @@ -15,20 +15,26 @@ var _ = (*assembleBlockParamsMarshaling)(nil) // MarshalJSON marshals as JSON. func (a AssembleBlockParams) MarshalJSON() ([]byte, error) { type AssembleBlockParams struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Random common.Hash `json:"random" gencodec:"required"` + FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` } var enc AssembleBlockParams enc.ParentHash = a.ParentHash enc.Timestamp = hexutil.Uint64(a.Timestamp) + enc.Random = a.Random + enc.FeeRecipient = a.FeeRecipient return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. func (a *AssembleBlockParams) UnmarshalJSON(input []byte) error { type AssembleBlockParams struct { - ParentHash *common.Hash `json:"parentHash" gencodec:"required"` - Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Random *common.Hash `json:"random" gencodec:"required"` + FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"` } var dec AssembleBlockParams if err := json.Unmarshal(input, &dec); err != nil { @@ -42,5 +48,13 @@ func (a *AssembleBlockParams) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'timestamp' for AssembleBlockParams") } a.Timestamp = uint64(*dec.Timestamp) + if dec.Random == nil { + return errors.New("missing required field 'random' for AssembleBlockParams") + } + a.Random = *dec.Random + if dec.FeeRecipient == nil { + return errors.New("missing required field 'feeRecipient' for AssembleBlockParams") + } + a.FeeRecipient = *dec.FeeRecipient return nil } diff --git a/eth/catalyst/gen_ed.go b/eth/catalyst/gen_ed.go index cdf6cc96233a..be57ef1b80fc 100644 --- a/eth/catalyst/gen_ed.go +++ b/eth/catalyst/gen_ed.go @@ -27,7 +27,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` ExtraData []byte `json:"extraData" gencodec:"required"` - BaseFeePerGas uint64 `json:"baseFeePerGas" gencodec:"required"` + BaseFeePerGas hexutil.Uint64 `json:"baseFeePerGas" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } var enc ExecutableData @@ -43,7 +43,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { enc.GasUsed = hexutil.Uint64(e.GasUsed) enc.Timestamp = hexutil.Uint64(e.Timestamp) enc.ExtraData = e.ExtraData - enc.BaseFeePerGas = e.BaseFeePerGas + enc.BaseFeePerGas = hexutil.Uint64(e.BaseFeePerGas) if e.Transactions != nil { enc.Transactions = make([]hexutil.Bytes, len(e.Transactions)) for k, v := range e.Transactions { @@ -68,7 +68,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` ExtraData []byte `json:"extraData" gencodec:"required"` - BaseFeePerGas *uint64 `json:"baseFeePerGas" gencodec:"required"` + BaseFeePerGas *hexutil.Uint64 `json:"baseFeePerGas" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } var dec ExecutableData @@ -126,7 +126,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { if dec.BaseFeePerGas == nil { return errors.New("missing required field 'baseFeePerGas' for ExecutableData") } - e.BaseFeePerGas = *dec.BaseFeePerGas + e.BaseFeePerGas = uint64(*dec.BaseFeePerGas) if dec.Transactions == nil { return errors.New("missing required field 'transactions' for ExecutableData") } diff --git a/eth/catalyst/gen_payload.go b/eth/catalyst/gen_payload.go new file mode 100644 index 000000000000..a0b00fcfd8c3 --- /dev/null +++ b/eth/catalyst/gen_payload.go @@ -0,0 +1,36 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package catalyst + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*payloadResponseMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (p PayloadResponse) MarshalJSON() ([]byte, error) { + type PayloadResponse struct { + PayloadID hexutil.Uint64 `json:"payloadId"` + } + var enc PayloadResponse + enc.PayloadID = hexutil.Uint64(p.PayloadID) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (p *PayloadResponse) UnmarshalJSON(input []byte) error { + type PayloadResponse struct { + PayloadID *hexutil.Uint64 `json:"payloadId"` + } + var dec PayloadResponse + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.PayloadID != nil { + p.PayloadID = uint64(*dec.PayloadID) + } + return nil +} From 034da5ae283f86d6a1ab3e341deffbf047ed9bf5 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 23 Sep 2021 05:26:33 +0200 Subject: [PATCH 12/43] eth, core: remove unusued code, fix typos --- consensus/beacon/consensus.go | 9 ++++----- core/blockchain.go | 6 +++--- core/headerchain.go | 2 +- eth/catalyst/api.go | 29 ++++++++++------------------- eth/catalyst/api_test.go | 6 ++++-- miner/stress/beacon/main.go | 2 +- 6 files changed, 23 insertions(+), 31 deletions(-) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 6126e2571fcb..0e7e31c975eb 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -42,11 +42,10 @@ var ( // codebase, inherently breaking if the engine is swapped out. Please put common // error types into the consensus package. var ( - errTooManyUncles = errors.New("too many uncles") - errInvalidDifficulty = errors.New("invalid difficulty") - errInvalidMixDigest = errors.New("invalid mix digest") - errInvalidNonce = errors.New("invalid nonce") - errInvalidUncleHash = errors.New("invalid uncle hash") + errTooManyUncles = errors.New("too many uncles") + errInvalidMixDigest = errors.New("invalid mix digest") + errInvalidNonce = errors.New("invalid nonce") + errInvalidUncleHash = errors.New("invalid uncle hash") ) // Beacon is a consensus engine combines the ethereum 1 consensus and proof-of-stake diff --git a/core/blockchain.go b/core/blockchain.go index de2826896bb6..487a39c17925 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2019,10 +2019,10 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { } // InsertBlock accepts a single block and the given consensus engine as the -// parameters. It will firstly execute the block, run the necessary verfication +// parameters. It will firstly execute the block, run the necessary verification // upon it and then persist the block and the associate state into the database. // The key difference between the InsertChain is it won't do the canonical chain -// udpating. It relays on the additional SetChainHead call to finalize the entire +// updating. It relays on the additional SetChainHead call to finalize the entire // procedure. func (bc *BlockChain) InsertBlock(block *types.Block) error { bc.wg.Add(1) @@ -2123,7 +2123,7 @@ func (bc *BlockChain) insertBlock(block *types.Block) error { blockWriteTimer.Update(time.Since(substart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits) blockInsertTimer.UpdateSince(start) - log.Info("Inserted block", "number", block.Number(), "hash", block.Hash(), "txs", len(block.Transactions()), "elasped", common.PrettyDuration(time.Since(start))) + log.Info("Inserted block", "number", block.Number(), "hash", block.Hash(), "txs", len(block.Transactions()), "elapsed", common.PrettyDuration(time.Since(start))) return nil } diff --git a/core/headerchain.go b/core/headerchain.go index c4b383e51a4c..6efb4c961744 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -197,7 +197,7 @@ func (hc *HeaderChain) Reorg(headers []*types.Header) error { // WriteHeaders writes a chain of headers into the local chain, given that the // parents are already known. The chain head header won't be updated in this -// function, the addtional setChainHead is expected in order to finish the entire +// function, the additional setChainHead is expected in order to finish the entire // procedure. func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) { if len(headers) == 0 { diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 7d79c5aa1a2c..9547c9b32c15 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -185,14 +185,14 @@ func (api *ConsensusAPI) GetPayload(PayloadID uint64) (*ExecutableData, error) { // that data that is no longer needed can be removed. func (api *ConsensusAPI) ConsensusValidated(params ConsensusValidatedParams) error { switch params.Status { - case "VALID": + case VALID.Status: // Finalize the transition if it's the first `FinalisedBlock` event. merger := api.merger() if !merger.EnteredPoS() { merger.EnterPoS() } - return api.SetHead(params.BlockHash) - case "INVALID": + return api.setHead(params.BlockHash) + case INVALID.Status: return nil default: return errors.New("invalid params.status") @@ -200,7 +200,7 @@ func (api *ConsensusAPI) ConsensusValidated(params ConsensusValidatedParams) err } func (api *ConsensusAPI) ForkChoiceUpdated(params ForkChoiceParams) error { - return api.SetHead(params.FinalizedBlockHash) + return api.setHead(params.FinalizedBlockHash) } // ExecutePayload creates an Eth1 block, inserts it in the chain, and returns the status of the chain. @@ -220,6 +220,8 @@ func (api *ConsensusAPI) ExecutePayload(params ExecutableData) (GenericStringRes return VALID, nil } if !api.eth.Synced() { + // TODO (MariusVanDerWijden) if the node is not synced and we received a finalized block + // we should trigger the reverse header sync here. return SYNCING, errors.New("node is not synced yet") } parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash) @@ -409,26 +411,15 @@ func InsertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Hea } // Used in tests to add a the list of transactions from a block to the tx pool. -func (api *ConsensusAPI) addBlockTxs(block *types.Block) error { - for _, tx := range block.Transactions() { +func (api *ConsensusAPI) insertTransactions(txs types.Transactions) error { + for _, tx := range txs { api.eth.TxPool().AddLocal(tx) } return nil } -// FinalizeBlock is called to mark a block as synchronized, so -// that data that is no longer needed can be removed. -func (api *ConsensusAPI) FinalizeBlock(blockHash common.Hash) (*GenericResponse, error) { - // Finalize the transition if it's the first `FinalisedBlock` event. - merger := api.merger() - if !merger.EnteredPoS() { - merger.EnterPoS() - } - return &GenericResponse{true}, nil -} - -// SetHead is called to perform a force choice. -func (api *ConsensusAPI) SetHead(newHead common.Hash) error { +// setHead is called to perform a force choice. +func (api *ConsensusAPI) setHead(newHead common.Hash) error { // Trigger the transition if it's the first `NewHead` event. merger := api.merger() if !merger.LeftPoW() { diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 622bf3d0def2..a82dda385245 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -67,6 +67,7 @@ func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) { return genesis, blocks } +/* func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block, []*types.Block) { if fork >= n { fork = n - 1 @@ -109,6 +110,7 @@ func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block, forkedBlocks, _ := core.GenerateChain(config, blocks[fork], engine, db, n-fork, generateFork) return genesis, blocks, forkedBlocks } +*/ func TestEth2AssembleBlock(t *testing.T) { genesis, blocks := generatePreMergeChain(10) @@ -143,7 +145,7 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { api := NewConsensusAPI(ethservice, nil) // Put the 10th block's tx in the pool and produce a new block - api.addBlockTxs(blocks[9]) + api.insertTransactions(blocks[9].Transactions()) blockParams := AssembleBlockParams{ ParentHash: blocks[8].Hash(), Timestamp: blocks[8].Time() + 5, @@ -165,7 +167,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { api := NewConsensusAPI(ethservice, nil) // Put the 10th block's tx in the pool and produce a new block - api.addBlockTxs(blocks[9]) + api.insertTransactions(blocks[9].Transactions()) blockParams := AssembleBlockParams{ ParentHash: blocks[8].Hash(), Timestamp: blocks[8].Time() + 5, diff --git a/miner/stress/beacon/main.go b/miner/stress/beacon/main.go index 274d2f0ca329..5751f519bdb9 100644 --- a/miner/stress/beacon/main.go +++ b/miner/stress/beacon/main.go @@ -280,7 +280,7 @@ func (mgr *nodeManager) run() { nodes = append(nodes, mgr.getNodes(eth2NormalNode)...) nodes = append(nodes, mgr.getNodes(eth2LightClient)...) for _, node := range append(nodes) { - node.api.FinalizeBlock(oldest.Hash()) + node.api.ConsensusValidated(catalyst.ConsensusValidatedParams{BlockHash: oldest.Hash(), Status: catalyst.VALID.Status}) } log.Info("Finalised eth2 block", "number", oldest.NumberU64(), "hash", oldest.Hash()) waitFinalise = waitFinalise[1:] From 4e1200085d7013115ea560ddba3e4cf3a491393b Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 23 Sep 2021 06:32:57 +0200 Subject: [PATCH 13/43] eth: added api for exporting and importing blocks --- eth/catalyst/api.go | 94 ++++++++++++++++++++++++++++++------- eth/catalyst/api_test.go | 6 +-- miner/stress/beacon/main.go | 4 +- 3 files changed, 83 insertions(+), 21 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 9547c9b32c15..d54de335694a 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "math/big" + "os" "time" "github.com/ethereum/go-ethereum/common" @@ -35,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" chainParams "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" ) @@ -210,7 +212,7 @@ func (api *ConsensusAPI) ExecutePayload(params ExecutableData) (GenericStringRes if parent == nil { return INVALID, fmt.Errorf("could not find parent %x", params.ParentHash) } - block, err := InsertBlockParamsToBlock(api.les.BlockChain().Config(), parent, params) + block, err := ExecutableDataToBlock(api.les.BlockChain().Config(), parent, params) if err != nil { return INVALID, err } @@ -228,7 +230,7 @@ func (api *ConsensusAPI) ExecutePayload(params ExecutableData) (GenericStringRes if parent == nil { return INVALID, fmt.Errorf("could not find parent %x", params.ParentHash) } - block, err := InsertBlockParamsToBlock(api.eth.BlockChain().Config(), parent.Header(), params) + block, err := ExecutableDataToBlock(api.eth.BlockChain().Config(), parent.Header(), params) if err != nil { return INVALID, err } @@ -344,19 +346,7 @@ func (api *ConsensusAPI) assembleBlock(params AssembleBlockParams) (*ExecutableD if err != nil { return nil, err } - return &ExecutableData{ - BlockHash: block.Hash(), - ParentHash: block.ParentHash(), - Coinbase: block.Coinbase(), - StateRoot: block.Root(), - Number: block.NumberU64(), - GasLimit: block.GasLimit(), - GasUsed: block.GasUsed(), - Timestamp: block.Time(), - ReceiptRoot: block.ReceiptHash(), - LogsBloom: block.Bloom().Bytes(), - Transactions: encodeTransactions(block.Transactions()), - }, nil + return BlockToExecutableData(block), nil } func encodeTransactions(txs []*types.Transaction) [][]byte { @@ -379,7 +369,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { return txs, nil } -func InsertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Header, params ExecutableData) (*types.Block, error) { +func ExecutableDataToBlock(config *chainParams.ChainConfig, parent *types.Header, params ExecutableData) (*types.Block, error) { txs, err := decodeTransactions(params.Transactions) if err != nil { return nil, err @@ -410,6 +400,22 @@ func InsertBlockParamsToBlock(config *chainParams.ChainConfig, parent *types.Hea return block, nil } +func BlockToExecutableData(block *types.Block) *ExecutableData { + return &ExecutableData{ + BlockHash: block.Hash(), + ParentHash: block.ParentHash(), + Coinbase: block.Coinbase(), + StateRoot: block.Root(), + Number: block.NumberU64(), + GasLimit: block.GasLimit(), + GasUsed: block.GasUsed(), + Timestamp: block.Time(), + ReceiptRoot: block.ReceiptHash(), + LogsBloom: block.Bloom().Bytes(), + Transactions: encodeTransactions(block.Transactions()), + } +} + // Used in tests to add a the list of transactions from a block to the tx pool. func (api *ConsensusAPI) insertTransactions(txs types.Transactions) error { for _, tx := range txs { @@ -461,3 +467,59 @@ func (api *ConsensusAPI) merger() *core.Merger { } return api.eth.Merger() } + +// Helper API for the merge f2f + +func (api *ConsensusAPI) ExportChain(path string) error { + if api.light { + return errors.New("cannot export chain in light mode") + } + f, err := os.Create(path) + if err != nil { + return err + } + return api.eth.BlockChain().Export(f) +} + +func (api *ConsensusAPI) ImportChain(path string) error { + if api.light { + return errors.New("cannot import chain in light mode") + } + f, err := os.Open(path) + if err != nil { + return err + } + for { + var block types.Block + if err := block.DecodeRLP(rlp.NewStream(f, 0)); err != nil { + break + } + if err := api.eth.BlockChain().InsertBlock(&block); err != nil { + return err + } + } + return nil +} + +func (api *ConsensusAPI) ExportExecutableData(path string) error { + if api.light { + return errors.New("cannot export chain in light mode") + } + f, err := os.Create(path) + if err != nil { + return err + } + for i := uint64(0); i < api.eth.BlockChain().CurrentBlock().NumberU64(); i++ { + block := api.eth.BlockChain().GetBlockByNumber(i) + exec := BlockToExecutableData(block) + b, err := exec.MarshalJSON() + if err != nil { + return err + } + if _, err := f.Write(b); err != nil { + return err + } + f.Write([]byte("\n")) + } + return nil +} diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index a82dda385245..93ef0c9b7e64 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -234,7 +234,7 @@ func TestEth2NewBlock(t *testing.T) { if err != nil { t.Fatalf("Failed to create the executable data %v", err) } - block, err := InsertBlockParamsToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) + block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } @@ -271,7 +271,7 @@ func TestEth2NewBlock(t *testing.T) { if err != nil { t.Fatalf("Failed to create the executable data %v", err) } - block, err := InsertBlockParamsToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) + block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } @@ -314,7 +314,7 @@ func TestEth2DeepReorg(t *testing.T) { if err != nil { t.Fatalf("Failed to create the executable data %v", err) } - block, err := InsertBlockParamsToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) + block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } diff --git a/miner/stress/beacon/main.go b/miner/stress/beacon/main.go index 5751f519bdb9..2aa291968f23 100644 --- a/miner/stress/beacon/main.go +++ b/miner/stress/beacon/main.go @@ -177,7 +177,7 @@ func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed catalyst.Execut } else { config = n.ethBackend.BlockChain().Config() } - block, err := catalyst.InsertBlockParamsToBlock(config, parent, ed) + block, err := catalyst.ExecutableDataToBlock(config, parent, ed) if err != nil { return err } @@ -318,7 +318,7 @@ func (mgr *nodeManager) run() { log.Error("Failed to assemble the block", "err", err) continue } - block, _ := catalyst.InsertBlockParamsToBlock(chain.Config(), parentBlock.Header(), *ed) + block, _ := catalyst.ExecutableDataToBlock(chain.Config(), parentBlock.Header(), *ed) nodes := mgr.getNodes(eth2MiningNode) nodes = append(nodes, mgr.getNodes(eth2NormalNode)...) From 6cde5349640d302afbad13a5526d0b05485daabe Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 23 Sep 2021 07:57:26 +0200 Subject: [PATCH 14/43] core: updated stale comment --- core/blockchain.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 487a39c17925..e234b5a5ba53 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2018,11 +2018,10 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { return nil } -// InsertBlock accepts a single block and the given consensus engine as the -// parameters. It will firstly execute the block, run the necessary verification +// InsertBlock executes the block, runs the necessary verification // upon it and then persist the block and the associate state into the database. // The key difference between the InsertChain is it won't do the canonical chain -// updating. It relays on the additional SetChainHead call to finalize the entire +// updating. It relies on the additional SetChainHead call to finalize the entire // procedure. func (bc *BlockChain) InsertBlock(block *types.Block) error { bc.wg.Add(1) From dd773c739883c999955b06b1af9e9a18b5fd64bf Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 28 Sep 2021 15:11:45 +0200 Subject: [PATCH 15/43] eth/catalyst: better types, testing, first working protype --- consensus/beacon/consensus.go | 6 ++-- core/chain_makers.go | 2 +- core/types/block.go | 6 ++++ eth/catalyst/api.go | 4 ++- eth/catalyst/api_test.go | 56 ++++++++++++++++++++++++++++++++++- eth/catalyst/api_types.go | 2 +- light/lightchain.go | 8 ++--- 7 files changed, 73 insertions(+), 11 deletions(-) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 0e7e31c975eb..20981ca3e56e 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -48,7 +48,7 @@ var ( errInvalidUncleHash = errors.New("invalid uncle hash") ) -// Beacon is a consensus engine combines the ethereum 1 consensus and proof-of-stake +// Beacon is a consensus engine combines the eth1 consensus and proof-of-stake // algorithm. There is a special flag inside to decide whether to use legacy consensus // rules or new rules. The transition rule is described in the eth1/2 merge spec. // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3675.md @@ -57,7 +57,7 @@ var ( // is only used for necessary consensus checks. The legacy consensus engine can be any // engine implements the consensus interface(except the beacon itself). type Beacon struct { - ethone consensus.Engine // Classic consensus engine used in the eth1, ethash or clique + ethone consensus.Engine // Classic consensus engine used in eth1, e.g. ethash or clique // transitioned is the flag whether the transition has been triggered. // It's triggered by receiving the first "POS_CHAINHEAD_SET" message @@ -66,7 +66,7 @@ type Beacon struct { lock sync.RWMutex } -// New creates a consensus engine with the given embedded ethereum 1 engine. +// New creates a consensus engine with the given embedded eth1 engine. func New(ethone consensus.Engine, transitioned bool) *Beacon { if _, ok := ethone.(*Beacon); ok { panic("nested consensus engine") diff --git a/core/chain_makers.go b/core/chain_makers.go index 5316f0d84e01..403e64b34c31 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -206,7 +206,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse b.header = makeHeader(chainreader, parent, statedb, b.engine) // Set the difficulty for clique block. The chain maker doesn't have access - // to a chain, so the difficulty will be lets unset (nil). Set it here to the + // to a chain, so the difficulty will be left unset (nil). Set it here to the // correct value. if b.header.Difficulty == nil { b.header.Difficulty = big.NewInt(2) diff --git a/core/types/block.go b/core/types/block.go index 360f1eb47c2b..92e5cb77275a 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -85,6 +85,12 @@ type Header struct { // BaseFee was added by EIP-1559 and is ignored in legacy headers. BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"` + + /* + TODO (MariusVanDerWijden) Add this field once needed + // Random was added during the merge and contains the BeaconState randomness + Random common.Hash `json:"random" rlp:"optional"` + */ } // field type overrides for gencodec diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index d54de335694a..9646bd934ee6 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -25,6 +25,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/misc" @@ -175,7 +176,7 @@ func (api *ConsensusAPI) PreparePayload(params AssembleBlockParams) (*PayloadRes return &PayloadResponse{PayloadID: uint64(id)}, nil } -func (api *ConsensusAPI) GetPayload(PayloadID uint64) (*ExecutableData, error) { +func (api *ConsensusAPI) GetPayload(PayloadID hexutil.Uint64) (*ExecutableData, error) { data, ok := api.preparedBlocks[int(PayloadID)] if !ok { return nil, errors.New("payload not found") @@ -389,6 +390,7 @@ func ExecutableDataToBlock(config *chainParams.ChainConfig, parent *types.Header GasLimit: params.GasLimit, GasUsed: params.GasUsed, Time: params.Timestamp, + // TODO (MariusVanDerWijden) add params.Random to header once required } if config.IsLondon(number) { header.BaseFee = misc.CalcBaseFee(config, parent) diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 93ef0c9b7e64..8fb96de6ef28 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -22,6 +22,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" @@ -176,7 +177,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { if err != nil { t.Fatalf("error preparing payload, err=%v", err) } - execData, err := api.GetPayload(respID.PayloadID) + execData, err := api.GetPayload(hexutil.Uint64(respID.PayloadID)) if err != nil { t.Fatalf("error getting payload, err=%v", err) } @@ -361,3 +362,56 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) return n, ethservice } + +func TestFullAPI(t *testing.T) { + genesis, preMergeBlocks := generatePreMergeChain(10) + n, ethservice := startEthService(t, genesis, preMergeBlocks) + defer n.Close() + var ( + api = NewConsensusAPI(ethservice, nil) + parent = ethservice.BlockChain().CurrentBlock() + // This EVM code generates a log when the contract is created. + logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") + ) + for i := 0; i < 10; i++ { + statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) + nonce := statedb.GetNonce(testAddr) + tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) + ethservice.TxPool().AddLocal(tx) + + params := AssembleBlockParams{ + ParentHash: parent.Hash(), + Timestamp: parent.Time() + 1, + Random: crypto.Keccak256Hash([]byte{byte(i)}), + FeeRecipient: parent.Coinbase(), + } + resp, err := api.PreparePayload(params) + if err != nil { + t.Fatalf("can't prepare payload: %v", err) + } + payload, err := api.GetPayload(hexutil.Uint64(resp.PayloadID)) + if err != nil { + t.Fatalf("can't get payload: %v", err) + } + execResp, err := api.ExecutePayload(*payload) + if err != nil { + t.Fatalf("can't execute payload: %v", err) + } + if execResp.Status != VALID.Status { + t.Fatalf("invalid status: %v", execResp.Status) + } + + if err := api.ConsensusValidated(ConsensusValidatedParams{BlockHash: payload.BlockHash, Status: VALID.Status}); err != nil { + t.Fatalf("failed to validate consensus: %v", err) + } + + if err := api.ForkChoiceUpdated(ForkChoiceParams{HeadBlockHash: payload.BlockHash, FinalizedBlockHash: payload.BlockHash}); err != nil { + t.Fatalf("Failed to insert block: %v", err) + } + if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number { + t.Fatalf("Chain head should be updated") + } + parent = ethservice.BlockChain().CurrentBlock() + + } +} diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index 70fd11d7bfbd..acfba6117864 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -51,7 +51,7 @@ type ExecutableData struct { GasLimit uint64 `json:"gasLimit" gencodec:"required"` GasUsed uint64 `json:"gasUsed" gencodec:"required"` Timestamp uint64 `json:"timestamp" gencodec:"required"` - ExtraData []byte `json:"extraData" gencodec:"required"` + ExtraData []byte `json:"extraData" gencodec:"optional"` BaseFeePerGas uint64 `json:"baseFeePerGas" gencodec:"required"` Transactions [][]byte `json:"transactions" gencodec:"required"` } diff --git a/light/lightchain.go b/light/lightchain.go index 2c3bbd4dcbd9..93d474a967e2 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -375,7 +375,7 @@ func (lc *LightChain) postChainEvents(events []interface{}) { } func (lc *LightChain) InsertHeader(header *types.Header) error { - // Verify the header first before obtaing the lock + // Verify the header first before obtaining the lock headers := []*types.Header{header} if _, err := lc.hc.ValidateHeaderChain(headers, 100); err != nil { return err @@ -393,12 +393,12 @@ func (lc *LightChain) InsertHeader(header *types.Header) error { } func (lc *LightChain) SetChainHead(header *types.Header) error { - lc.wg.Add(1) - defer lc.wg.Done() - lc.chainmu.Lock() defer lc.chainmu.Unlock() + lc.wg.Add(1) + defer lc.wg.Done() + if err := lc.hc.Reorg([]*types.Header{header}); err != nil { return err } From adb862674605effc352ff84a799930bdbfbb2c84 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 28 Sep 2021 16:55:47 +0200 Subject: [PATCH 16/43] eth/catalyst: added terminal difficulty check --- eth/catalyst/api.go | 14 +++++++++----- eth/catalyst/api_types.go | 3 ++- eth/catalyst/gen_ed.go | 6 +++--- miner/stress/beacon/main.go | 3 ++- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 9646bd934ee6..47ce8351b251 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -222,15 +222,19 @@ func (api *ConsensusAPI) ExecutePayload(params ExecutableData) (GenericStringRes } return VALID, nil } - if !api.eth.Synced() { - // TODO (MariusVanDerWijden) if the node is not synced and we received a finalized block - // we should trigger the reverse header sync here. - return SYNCING, errors.New("node is not synced yet") - } parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash) if parent == nil { return INVALID, fmt.Errorf("could not find parent %x", params.ParentHash) } + if !api.eth.Synced() { + if api.eth.BlockChain().GetTdByHash(parent.Hash()).Cmp(api.eth.BlockChain().Config().TerminalTotalDifficulty) > 0 { + api.eth.SetSynced() + } else { + // TODO (MariusVanDerWijden) if the node is not synced and we received a finalized block + // we should trigger the reverse header sync here. + return SYNCING, errors.New("node is not synced yet") + } + } block, err := ExecutableDataToBlock(api.eth.BlockChain().Config(), parent.Header(), params) if err != nil { return INVALID, err diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index acfba6117864..6fb7d9b4dbb3 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -51,7 +51,7 @@ type ExecutableData struct { GasLimit uint64 `json:"gasLimit" gencodec:"required"` GasUsed uint64 `json:"gasUsed" gencodec:"required"` Timestamp uint64 `json:"timestamp" gencodec:"required"` - ExtraData []byte `json:"extraData" gencodec:"optional"` + ExtraData []byte `json:"extraData" gencodec:"required"` BaseFeePerGas uint64 `json:"baseFeePerGas" gencodec:"required"` Transactions [][]byte `json:"transactions" gencodec:"required"` } @@ -63,6 +63,7 @@ type executableDataMarshaling struct { GasUsed hexutil.Uint64 Timestamp hexutil.Uint64 BaseFeePerGas hexutil.Uint64 + ExtraData hexutil.Bytes LogsBloom hexutil.Bytes Transactions []hexutil.Bytes } diff --git a/eth/catalyst/gen_ed.go b/eth/catalyst/gen_ed.go index be57ef1b80fc..2d6746047461 100644 --- a/eth/catalyst/gen_ed.go +++ b/eth/catalyst/gen_ed.go @@ -26,7 +26,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` - ExtraData []byte `json:"extraData" gencodec:"required"` + ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"` BaseFeePerGas hexutil.Uint64 `json:"baseFeePerGas" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } @@ -67,7 +67,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` - ExtraData []byte `json:"extraData" gencodec:"required"` + ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"` BaseFeePerGas *hexutil.Uint64 `json:"baseFeePerGas" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } @@ -122,7 +122,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { if dec.ExtraData == nil { return errors.New("missing required field 'extraData' for ExecutableData") } - e.ExtraData = dec.ExtraData + e.ExtraData = *dec.ExtraData if dec.BaseFeePerGas == nil { return errors.New("missing required field 'baseFeePerGas' for ExecutableData") } diff --git a/miner/stress/beacon/main.go b/miner/stress/beacon/main.go index 2aa291968f23..46bf6d7e1476 100644 --- a/miner/stress/beacon/main.go +++ b/miner/stress/beacon/main.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/fdlimit" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" @@ -148,7 +149,7 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) if err != nil { return nil, err } - return n.api.GetPayload(payload.PayloadID) + return n.api.GetPayload(hexutil.Uint64(payload.PayloadID)) } func (n *ethNode) insertBlock(eb catalyst.ExecutableData) error { From 4d4c2987c7976fa079ca5e4185cd5e1614955ffc Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 28 Sep 2021 17:41:39 +0200 Subject: [PATCH 17/43] eth/catalyst: use custom errors --- eth/catalyst/api.go | 14 ++++++++------ rpc/errors.go | 10 ++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 47ce8351b251..42fd0092502e 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -43,9 +43,11 @@ import ( ) var ( - VALID = GenericStringResponse{"VALID"} - INVALID = GenericStringResponse{"INVALID"} - SYNCING = GenericStringResponse{"SYNCING"} + VALID = GenericStringResponse{"VALID"} + INVALID = GenericStringResponse{"INVALID"} + SYNCING = GenericStringResponse{"SYNCING"} + UnknownHeader = rpc.CustomError{Code: 4, Message: "unknown header"} + UnknownPayload = rpc.CustomError{Code: 5, Message: "unknown payload"} ) // Register adds catalyst APIs to the full node. @@ -179,7 +181,7 @@ func (api *ConsensusAPI) PreparePayload(params AssembleBlockParams) (*PayloadRes func (api *ConsensusAPI) GetPayload(PayloadID hexutil.Uint64) (*ExecutableData, error) { data, ok := api.preparedBlocks[int(PayloadID)] if !ok { - return nil, errors.New("payload not found") + return nil, &UnknownPayload } return data, nil } @@ -444,7 +446,7 @@ func (api *ConsensusAPI) setHead(newHead common.Hash) error { } newHeadHeader := api.les.BlockChain().GetHeaderByHash(newHead) if newHeadHeader == nil { - return errors.New("head not found") + return &UnknownHeader } if err := api.les.BlockChain().SetChainHead(newHeadHeader); err != nil { return err @@ -457,7 +459,7 @@ func (api *ConsensusAPI) setHead(newHead common.Hash) error { } newHeadBlock := api.eth.BlockChain().GetBlockByHash(newHead) if newHeadBlock == nil { - return errors.New("head not found") + return &UnknownHeader } if err := api.eth.BlockChain().SetChainHead(newHeadBlock); err != nil { return err diff --git a/rpc/errors.go b/rpc/errors.go index 4c06a745fbd8..184275244a79 100644 --- a/rpc/errors.go +++ b/rpc/errors.go @@ -54,6 +54,7 @@ var ( _ Error = new(invalidRequestError) _ Error = new(invalidMessageError) _ Error = new(invalidParamsError) + _ Error = new(CustomError) ) const defaultErrorCode = -32000 @@ -101,3 +102,12 @@ type invalidParamsError struct{ message string } func (e *invalidParamsError) ErrorCode() int { return -32602 } func (e *invalidParamsError) Error() string { return e.message } + +type CustomError struct { + Code int + Message string +} + +func (e *CustomError) ErrorCode() int { return e.Code } + +func (e *CustomError) Error() string { return e.Message } From b4aaa07515f036bc2668fcb38d3dfa76211948d6 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 29 Sep 2021 11:08:33 +0200 Subject: [PATCH 18/43] eth/catalyst: renamed api types, better adherence to spec --- eth/catalyst/api.go | 17 +++++++++++------ eth/catalyst/api_test.go | 4 ++++ eth/catalyst/api_types.go | 4 ++-- eth/catalyst/gen_ed.go | 12 ++++++------ 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 42fd0092502e..4db7f531b92d 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -90,12 +90,18 @@ type ConsensusAPI struct { func NewConsensusAPI(eth *eth.Ethereum, les *les.LightEthereum) *ConsensusAPI { var engine consensus.Engine if eth == nil { + if les.BlockChain().Config().TerminalTotalDifficulty == nil { + panic("Catalyst started without valid total difficulty") + } if b, ok := les.Engine().(*beacon.Beacon); ok { engine = beacon.New(b.InnerEngine(), true) } else { engine = beacon.New(les.Engine(), true) } } else { + if eth.BlockChain().Config().TerminalTotalDifficulty == nil { + panic("Catalyst started without valid total difficulty") + } if b, ok := eth.Engine().(*beacon.Beacon); ok { engine = beacon.New(b.InnerEngine(), true) } else { @@ -196,8 +202,9 @@ func (api *ConsensusAPI) ConsensusValidated(params ConsensusValidatedParams) err if !merger.EnteredPoS() { merger.EnterPoS() } - return api.setHead(params.BlockHash) + return nil //api.setHead(params.BlockHash) case INVALID.Status: + // TODO (MariusVanDerWijden) delete the block from the bc return nil default: return errors.New("invalid params.status") @@ -229,7 +236,8 @@ func (api *ConsensusAPI) ExecutePayload(params ExecutableData) (GenericStringRes return INVALID, fmt.Errorf("could not find parent %x", params.ParentHash) } if !api.eth.Synced() { - if api.eth.BlockChain().GetTdByHash(parent.Hash()).Cmp(api.eth.BlockChain().Config().TerminalTotalDifficulty) > 0 { + td := api.eth.BlockChain().GetTdByHash(parent.Hash()) + if td != nil && td.Cmp(api.eth.BlockChain().Config().TerminalTotalDifficulty) > 0 { api.eth.SetSynced() } else { // TODO (MariusVanDerWijden) if the node is not synced and we received a finalized block @@ -274,10 +282,7 @@ func (api *ConsensusAPI) assembleBlock(params AssembleBlockParams) (*ExecutableD if err != nil { return nil, err } - coinbase, err := api.eth.Etherbase() - if err != nil { - return nil, err - } + coinbase := params.FeeRecipient num := parent.Number() header := &types.Header{ ParentHash: parent.Hash(), diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 8fb96de6ef28..01240a9833f3 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -47,6 +47,7 @@ var ( func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) { db := rawdb.NewMemoryDatabase() config := params.AllEthashProtocolChanges + config.TerminalTotalDifficulty = big.NewInt(100000000000000) genesis := &core.Genesis{ Config: config, Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, @@ -287,6 +288,9 @@ func TestEth2NewBlock(t *testing.T) { if err := api.ConsensusValidated(ConsensusValidatedParams{BlockHash: block.Hash(), Status: "VALID"}); err != nil { t.Fatalf("Failed to insert block: %v", err) } + if err := api.ForkChoiceUpdated(ForkChoiceParams{FinalizedBlockHash: block.Hash(), HeadBlockHash: block.Hash()}); err != nil { + t.Fatalf("Failed to insert block: %v", err) + } if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { t.Fatalf("Chain head should be updated") } diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index 6fb7d9b4dbb3..4dce11b4856f 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -44,10 +44,10 @@ type ExecutableData struct { ParentHash common.Hash `json:"parentHash" gencodec:"required"` Coinbase common.Address `json:"coinbase" gencodec:"required"` StateRoot common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptRoot common.Hash `json:"receiptsRoot" gencodec:"required"` + ReceiptRoot common.Hash `json:"receiptRoot" gencodec:"required"` LogsBloom []byte `json:"logsBloom" gencodec:"required"` Random common.Hash `json:"random" gencodec:"required"` - Number uint64 `json:"number" gencodec:"required"` + Number uint64 `json:"blockNumber" gencodec:"required"` GasLimit uint64 `json:"gasLimit" gencodec:"required"` GasUsed uint64 `json:"gasUsed" gencodec:"required"` Timestamp uint64 `json:"timestamp" gencodec:"required"` diff --git a/eth/catalyst/gen_ed.go b/eth/catalyst/gen_ed.go index 2d6746047461..bc99a4a0954e 100644 --- a/eth/catalyst/gen_ed.go +++ b/eth/catalyst/gen_ed.go @@ -19,10 +19,10 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { ParentHash common.Hash `json:"parentHash" gencodec:"required"` Coinbase common.Address `json:"coinbase" gencodec:"required"` StateRoot common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptRoot common.Hash `json:"receiptsRoot" gencodec:"required"` + ReceiptRoot common.Hash `json:"receiptRoot" gencodec:"required"` LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` Random common.Hash `json:"random" gencodec:"required"` - Number hexutil.Uint64 `json:"number" gencodec:"required"` + Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"` GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` @@ -60,10 +60,10 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { ParentHash *common.Hash `json:"parentHash" gencodec:"required"` Coinbase *common.Address `json:"coinbase" gencodec:"required"` StateRoot *common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptRoot *common.Hash `json:"receiptsRoot" gencodec:"required"` + ReceiptRoot *common.Hash `json:"receiptRoot" gencodec:"required"` LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` Random *common.Hash `json:"random" gencodec:"required"` - Number *hexutil.Uint64 `json:"number" gencodec:"required"` + Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"` GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` @@ -92,7 +92,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { } e.StateRoot = *dec.StateRoot if dec.ReceiptRoot == nil { - return errors.New("missing required field 'receiptsRoot' for ExecutableData") + return errors.New("missing required field 'receiptRoot' for ExecutableData") } e.ReceiptRoot = *dec.ReceiptRoot if dec.LogsBloom == nil { @@ -104,7 +104,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { } e.Random = *dec.Random if dec.Number == nil { - return errors.New("missing required field 'number' for ExecutableData") + return errors.New("missing required field 'blockNumber' for ExecutableData") } e.Number = uint64(*dec.Number) if dec.GasLimit == nil { From baf5760614b35c551fb797afbd24c66ad718b254 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 29 Sep 2021 19:24:56 +0200 Subject: [PATCH 19/43] eth/catalyst: fix random in payload, payloadid as hexutil --- eth/catalyst/api.go | 13 +++++++------ eth/catalyst/api_test.go | 5 ++--- eth/catalyst/api_types.go | 13 +------------ eth/catalyst/gen_payload.go | 36 ------------------------------------ 4 files changed, 10 insertions(+), 57 deletions(-) delete mode 100644 eth/catalyst/gen_payload.go diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 4db7f531b92d..2b38ead561eb 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -174,14 +174,14 @@ func (api *ConsensusAPI) makeEnv(parent *types.Block, header *types.Header) (*bl return env, nil } -func (api *ConsensusAPI) PreparePayload(params AssembleBlockParams) (*PayloadResponse, error) { +func (api *ConsensusAPI) PreparePayload(params AssembleBlockParams) (hexutil.Uint64, error) { data, err := api.assembleBlock(params) if err != nil { - return nil, err + return hexutil.Uint64(0), err } id := len(api.preparedBlocks) api.preparedBlocks[id] = data - return &PayloadResponse{PayloadID: uint64(id)}, nil + return hexutil.Uint64(id), nil } func (api *ConsensusAPI) GetPayload(PayloadID hexutil.Uint64) (*ExecutableData, error) { @@ -358,7 +358,7 @@ func (api *ConsensusAPI) assembleBlock(params AssembleBlockParams) (*ExecutableD if err != nil { return nil, err } - return BlockToExecutableData(block), nil + return BlockToExecutableData(block, params.Random), nil } func encodeTransactions(txs []*types.Transaction) [][]byte { @@ -413,7 +413,7 @@ func ExecutableDataToBlock(config *chainParams.ChainConfig, parent *types.Header return block, nil } -func BlockToExecutableData(block *types.Block) *ExecutableData { +func BlockToExecutableData(block *types.Block, random common.Hash) *ExecutableData { return &ExecutableData{ BlockHash: block.Hash(), ParentHash: block.ParentHash(), @@ -426,6 +426,7 @@ func BlockToExecutableData(block *types.Block) *ExecutableData { ReceiptRoot: block.ReceiptHash(), LogsBloom: block.Bloom().Bytes(), Transactions: encodeTransactions(block.Transactions()), + Random: random, } } @@ -524,7 +525,7 @@ func (api *ConsensusAPI) ExportExecutableData(path string) error { } for i := uint64(0); i < api.eth.BlockChain().CurrentBlock().NumberU64(); i++ { block := api.eth.BlockChain().GetBlockByNumber(i) - exec := BlockToExecutableData(block) + exec := BlockToExecutableData(block, common.Hash{}) b, err := exec.MarshalJSON() if err != nil { return err diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 01240a9833f3..f274e6d83058 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -22,7 +22,6 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" @@ -178,7 +177,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { if err != nil { t.Fatalf("error preparing payload, err=%v", err) } - execData, err := api.GetPayload(hexutil.Uint64(respID.PayloadID)) + execData, err := api.GetPayload(respID) if err != nil { t.Fatalf("error getting payload, err=%v", err) } @@ -393,7 +392,7 @@ func TestFullAPI(t *testing.T) { if err != nil { t.Fatalf("can't prepare payload: %v", err) } - payload, err := api.GetPayload(hexutil.Uint64(resp.PayloadID)) + payload, err := api.GetPayload(resp) if err != nil { t.Fatalf("can't get payload: %v", err) } diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index 4dce11b4856f..0efeb1aae14f 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -44,7 +44,7 @@ type ExecutableData struct { ParentHash common.Hash `json:"parentHash" gencodec:"required"` Coinbase common.Address `json:"coinbase" gencodec:"required"` StateRoot common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptRoot common.Hash `json:"receiptRoot" gencodec:"required"` + ReceiptRoot common.Hash `json:"receiptRoot" gencodec:"required"` LogsBloom []byte `json:"logsBloom" gencodec:"required"` Random common.Hash `json:"random" gencodec:"required"` Number uint64 `json:"blockNumber" gencodec:"required"` @@ -76,17 +76,6 @@ type GenericResponse struct { Success bool `json:"success"` } -//go:generate go run github.com/fjl/gencodec -type PayloadResponse -field-override payloadResponseMarshaling -out gen_payload.go - -type PayloadResponse struct { - PayloadID uint64 `json:"payloadId"` -} - -// JSON type overrides for payloadResponse. -type payloadResponseMarshaling struct { - PayloadID hexutil.Uint64 -} - type GenericStringResponse struct { Status string `json:"status"` } diff --git a/eth/catalyst/gen_payload.go b/eth/catalyst/gen_payload.go deleted file mode 100644 index a0b00fcfd8c3..000000000000 --- a/eth/catalyst/gen_payload.go +++ /dev/null @@ -1,36 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package catalyst - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/common/hexutil" -) - -var _ = (*payloadResponseMarshaling)(nil) - -// MarshalJSON marshals as JSON. -func (p PayloadResponse) MarshalJSON() ([]byte, error) { - type PayloadResponse struct { - PayloadID hexutil.Uint64 `json:"payloadId"` - } - var enc PayloadResponse - enc.PayloadID = hexutil.Uint64(p.PayloadID) - return json.Marshal(&enc) -} - -// UnmarshalJSON unmarshals from JSON. -func (p *PayloadResponse) UnmarshalJSON(input []byte) error { - type PayloadResponse struct { - PayloadID *hexutil.Uint64 `json:"payloadId"` - } - var dec PayloadResponse - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.PayloadID != nil { - p.PayloadID = uint64(*dec.PayloadID) - } - return nil -} From 66130bc0c41e8b710fca1f866b81e55c3f111ce5 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 30 Sep 2021 10:06:40 +0200 Subject: [PATCH 20/43] eth/catalyst: forkChoiceUpdated -> forkchoiceUpdated --- eth/catalyst/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 2b38ead561eb..d93082504a79 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -211,7 +211,7 @@ func (api *ConsensusAPI) ConsensusValidated(params ConsensusValidatedParams) err } } -func (api *ConsensusAPI) ForkChoiceUpdated(params ForkChoiceParams) error { +func (api *ConsensusAPI) ForkchoiceUpdated(params ForkChoiceParams) error { return api.setHead(params.FinalizedBlockHash) } From 54313d1f429e88dd4156da81e3c0fab6f3f4b864 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 1 Oct 2021 09:52:22 +0200 Subject: [PATCH 21/43] internal/ethapi: update to v1.0.0.alpha.2 This commit updates the code to the new spec, moving payloadId into it's own object. It also fixes an issue with finalizing an empty blockhash. It also properly sets the basefee --- eth/catalyst/api.go | 39 ++++++++++++++++++++++--------------- eth/catalyst/api_test.go | 13 +++++++------ eth/catalyst/api_types.go | 11 +++++++++++ eth/catalyst/gen_ed.go | 4 ++-- eth/catalyst/gen_payload.go | 36 ++++++++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 24 deletions(-) create mode 100644 eth/catalyst/gen_payload.go diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index d93082504a79..982006386bed 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -18,6 +18,7 @@ package catalyst import ( + "bytes" "errors" "fmt" "math/big" @@ -174,14 +175,14 @@ func (api *ConsensusAPI) makeEnv(parent *types.Block, header *types.Header) (*bl return env, nil } -func (api *ConsensusAPI) PreparePayload(params AssembleBlockParams) (hexutil.Uint64, error) { +func (api *ConsensusAPI) PreparePayload(params AssembleBlockParams) (*PayloadResponse, error) { data, err := api.assembleBlock(params) if err != nil { - return hexutil.Uint64(0), err + return nil, err } id := len(api.preparedBlocks) api.preparedBlocks[id] = data - return hexutil.Uint64(id), nil + return &PayloadResponse{PayloadID: uint64(id)}, nil } func (api *ConsensusAPI) GetPayload(PayloadID hexutil.Uint64) (*ExecutableData, error) { @@ -212,7 +213,11 @@ func (api *ConsensusAPI) ConsensusValidated(params ConsensusValidatedParams) err } func (api *ConsensusAPI) ForkchoiceUpdated(params ForkChoiceParams) error { - return api.setHead(params.FinalizedBlockHash) + var emptyHash = common.Hash{} + if !bytes.Equal(params.FinalizedBlockHash[:], emptyHash[:]) { + return api.setHead(params.FinalizedBlockHash) + } + return nil } // ExecutePayload creates an Eth1 block, inserts it in the chain, and returns the status of the chain. @@ -401,6 +406,7 @@ func ExecutableDataToBlock(config *chainParams.ChainConfig, parent *types.Header GasLimit: params.GasLimit, GasUsed: params.GasUsed, Time: params.Timestamp, + BaseFee: parent.BaseFee, // TODO (MariusVanDerWijden) add params.Random to header once required } if config.IsLondon(number) { @@ -415,18 +421,19 @@ func ExecutableDataToBlock(config *chainParams.ChainConfig, parent *types.Header func BlockToExecutableData(block *types.Block, random common.Hash) *ExecutableData { return &ExecutableData{ - BlockHash: block.Hash(), - ParentHash: block.ParentHash(), - Coinbase: block.Coinbase(), - StateRoot: block.Root(), - Number: block.NumberU64(), - GasLimit: block.GasLimit(), - GasUsed: block.GasUsed(), - Timestamp: block.Time(), - ReceiptRoot: block.ReceiptHash(), - LogsBloom: block.Bloom().Bytes(), - Transactions: encodeTransactions(block.Transactions()), - Random: random, + BlockHash: block.Hash(), + ParentHash: block.ParentHash(), + Coinbase: block.Coinbase(), + StateRoot: block.Root(), + Number: block.NumberU64(), + GasLimit: block.GasLimit(), + GasUsed: block.GasUsed(), + BaseFeePerGas: block.BaseFee().Uint64(), + Timestamp: block.Time(), + ReceiptRoot: block.ReceiptHash(), + LogsBloom: block.Bloom().Bytes(), + Transactions: encodeTransactions(block.Transactions()), + Random: random, } } diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index f274e6d83058..b8110648975f 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -22,6 +22,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" @@ -177,7 +178,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { if err != nil { t.Fatalf("error preparing payload, err=%v", err) } - execData, err := api.GetPayload(respID) + execData, err := api.GetPayload(hexutil.Uint64(respID.PayloadID)) if err != nil { t.Fatalf("error getting payload, err=%v", err) } @@ -248,7 +249,7 @@ func TestEth2NewBlock(t *testing.T) { } checkLogEvents(t, newLogCh, rmLogsCh, 0, 0) - if err := api.ForkChoiceUpdated(ForkChoiceParams{HeadBlockHash: block.Hash(), FinalizedBlockHash: block.Hash()}); err != nil { + if err := api.ForkchoiceUpdated(ForkChoiceParams{HeadBlockHash: block.Hash(), FinalizedBlockHash: block.Hash()}); err != nil { t.Fatalf("Failed to insert block: %v", err) } if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { @@ -287,7 +288,7 @@ func TestEth2NewBlock(t *testing.T) { if err := api.ConsensusValidated(ConsensusValidatedParams{BlockHash: block.Hash(), Status: "VALID"}); err != nil { t.Fatalf("Failed to insert block: %v", err) } - if err := api.ForkChoiceUpdated(ForkChoiceParams{FinalizedBlockHash: block.Hash(), HeadBlockHash: block.Hash()}); err != nil { + if err := api.ForkchoiceUpdated(ForkChoiceParams{FinalizedBlockHash: block.Hash(), HeadBlockHash: block.Hash()}); err != nil { t.Fatalf("Failed to insert block: %v", err) } if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { @@ -329,7 +330,7 @@ func TestEth2DeepReorg(t *testing.T) { if ethservice.BlockChain().CurrentBlock().NumberU64() != head { t.Fatalf("Chain head shouldn't be updated") } - if err := api.ForkChoiceUpdated(ForkChoiceParams{HeadBlockHash: block.Hash(), FinalizedBlockHash: block.Hash()}); err != nil { + if err := api.ForkchoiceUpdated(ForkChoiceParams{HeadBlockHash: block.Hash(), FinalizedBlockHash: block.Hash()}); err != nil { t.Fatalf("Failed to insert block: %v", err) } if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { @@ -392,7 +393,7 @@ func TestFullAPI(t *testing.T) { if err != nil { t.Fatalf("can't prepare payload: %v", err) } - payload, err := api.GetPayload(resp) + payload, err := api.GetPayload(hexutil.Uint64(resp.PayloadID)) if err != nil { t.Fatalf("can't get payload: %v", err) } @@ -408,7 +409,7 @@ func TestFullAPI(t *testing.T) { t.Fatalf("failed to validate consensus: %v", err) } - if err := api.ForkChoiceUpdated(ForkChoiceParams{HeadBlockHash: payload.BlockHash, FinalizedBlockHash: payload.BlockHash}); err != nil { + if err := api.ForkchoiceUpdated(ForkChoiceParams{HeadBlockHash: payload.BlockHash, FinalizedBlockHash: payload.BlockHash}); err != nil { t.Fatalf("Failed to insert block: %v", err) } if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number { diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index 0efeb1aae14f..2cc75b82d82d 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -68,6 +68,17 @@ type executableDataMarshaling struct { Transactions []hexutil.Bytes } +//go:generate go run github.com/fjl/gencodec -type PayloadResponse -field-override payloadResponseMarshaling -out gen_payload.go + +type PayloadResponse struct { + PayloadID uint64 `json:"payloadId"` +} + +// JSON type overrides for payloadResponse. +type payloadResponseMarshaling struct { + PayloadID hexutil.Uint64 +} + type NewBlockResponse struct { Valid bool `json:"valid"` } diff --git a/eth/catalyst/gen_ed.go b/eth/catalyst/gen_ed.go index bc99a4a0954e..70f4e196f754 100644 --- a/eth/catalyst/gen_ed.go +++ b/eth/catalyst/gen_ed.go @@ -19,7 +19,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { ParentHash common.Hash `json:"parentHash" gencodec:"required"` Coinbase common.Address `json:"coinbase" gencodec:"required"` StateRoot common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptRoot common.Hash `json:"receiptRoot" gencodec:"required"` + ReceiptRoot common.Hash `json:"receiptRoot" gencodec:"required"` LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` Random common.Hash `json:"random" gencodec:"required"` Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"` @@ -60,7 +60,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { ParentHash *common.Hash `json:"parentHash" gencodec:"required"` Coinbase *common.Address `json:"coinbase" gencodec:"required"` StateRoot *common.Hash `json:"stateRoot" gencodec:"required"` - ReceiptRoot *common.Hash `json:"receiptRoot" gencodec:"required"` + ReceiptRoot *common.Hash `json:"receiptRoot" gencodec:"required"` LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` Random *common.Hash `json:"random" gencodec:"required"` Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"` diff --git a/eth/catalyst/gen_payload.go b/eth/catalyst/gen_payload.go new file mode 100644 index 000000000000..a0b00fcfd8c3 --- /dev/null +++ b/eth/catalyst/gen_payload.go @@ -0,0 +1,36 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package catalyst + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*payloadResponseMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (p PayloadResponse) MarshalJSON() ([]byte, error) { + type PayloadResponse struct { + PayloadID hexutil.Uint64 `json:"payloadId"` + } + var enc PayloadResponse + enc.PayloadID = hexutil.Uint64(p.PayloadID) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (p *PayloadResponse) UnmarshalJSON(input []byte) error { + type PayloadResponse struct { + PayloadID *hexutil.Uint64 `json:"payloadId"` + } + var dec PayloadResponse + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.PayloadID != nil { + p.PayloadID = uint64(*dec.PayloadID) + } + return nil +} From c77104b9c0669768998e9813c02edb6705d7fd70 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 1 Oct 2021 09:56:21 +0200 Subject: [PATCH 22/43] eth/catalyst: properly set extradata --- eth/catalyst/api.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 982006386bed..2ad035eb87b6 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -294,7 +294,7 @@ func (api *ConsensusAPI) assembleBlock(params AssembleBlockParams) (*ExecutableD Number: num.Add(num, common.Big1), Coinbase: coinbase, GasLimit: parent.GasLimit(), // Keep the gas limit constant in this prototype - Extra: []byte{}, + Extra: []byte{}, // TODO (MariusVanDerWijden) properly set extra data Time: params.Timestamp, } if config := api.eth.BlockChain().Config(); config.IsLondon(header.Number) { @@ -407,6 +407,7 @@ func ExecutableDataToBlock(config *chainParams.ChainConfig, parent *types.Header GasUsed: params.GasUsed, Time: params.Timestamp, BaseFee: parent.BaseFee, + Extra: params.ExtraData, // TODO (MariusVanDerWijden) add params.Random to header once required } if config.IsLondon(number) { @@ -434,6 +435,7 @@ func BlockToExecutableData(block *types.Block, random common.Hash) *ExecutableDa LogsBloom: block.Bloom().Bytes(), Transactions: encodeTransactions(block.Transactions()), Random: random, + ExtraData: block.Extra(), } } From 5f9101a9620b567cb319e8396eff312bcf328953 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 1 Oct 2021 10:37:34 +0200 Subject: [PATCH 23/43] eth/catalyst: fix basefee setting --- eth/catalyst/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 2ad035eb87b6..32ada1ace323 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -406,7 +406,7 @@ func ExecutableDataToBlock(config *chainParams.ChainConfig, parent *types.Header GasLimit: params.GasLimit, GasUsed: params.GasUsed, Time: params.Timestamp, - BaseFee: parent.BaseFee, + BaseFee: big.NewInt(int64(params.BaseFeePerGas)), Extra: params.ExtraData, // TODO (MariusVanDerWijden) add params.Random to header once required } From 15ccb34e95bd11b33179a5c3c5aa550ee30322d9 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Sat, 2 Oct 2021 08:13:59 +0200 Subject: [PATCH 24/43] eth/catalyst: use *big.Int for basefee --- eth/catalyst/api.go | 4 ++-- eth/catalyst/api_types.go | 6 ++++-- eth/catalyst/gen_ed.go | 9 +++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 32ada1ace323..be13170ac6ba 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -406,7 +406,7 @@ func ExecutableDataToBlock(config *chainParams.ChainConfig, parent *types.Header GasLimit: params.GasLimit, GasUsed: params.GasUsed, Time: params.Timestamp, - BaseFee: big.NewInt(int64(params.BaseFeePerGas)), + BaseFee: params.BaseFeePerGas, Extra: params.ExtraData, // TODO (MariusVanDerWijden) add params.Random to header once required } @@ -429,7 +429,7 @@ func BlockToExecutableData(block *types.Block, random common.Hash) *ExecutableDa Number: block.NumberU64(), GasLimit: block.GasLimit(), GasUsed: block.GasUsed(), - BaseFeePerGas: block.BaseFee().Uint64(), + BaseFeePerGas: block.BaseFee(), Timestamp: block.Time(), ReceiptRoot: block.ReceiptHash(), LogsBloom: block.Bloom().Bytes(), diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index 2cc75b82d82d..ff0aea39bf24 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -17,6 +17,8 @@ package catalyst import ( + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" ) @@ -52,7 +54,7 @@ type ExecutableData struct { GasUsed uint64 `json:"gasUsed" gencodec:"required"` Timestamp uint64 `json:"timestamp" gencodec:"required"` ExtraData []byte `json:"extraData" gencodec:"required"` - BaseFeePerGas uint64 `json:"baseFeePerGas" gencodec:"required"` + BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"` Transactions [][]byte `json:"transactions" gencodec:"required"` } @@ -62,7 +64,7 @@ type executableDataMarshaling struct { GasLimit hexutil.Uint64 GasUsed hexutil.Uint64 Timestamp hexutil.Uint64 - BaseFeePerGas hexutil.Uint64 + BaseFeePerGas *hexutil.Big ExtraData hexutil.Bytes LogsBloom hexutil.Bytes Transactions []hexutil.Bytes diff --git a/eth/catalyst/gen_ed.go b/eth/catalyst/gen_ed.go index 70f4e196f754..2953ab820fc2 100644 --- a/eth/catalyst/gen_ed.go +++ b/eth/catalyst/gen_ed.go @@ -5,6 +5,7 @@ package catalyst import ( "encoding/json" "errors" + "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -27,7 +28,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"` - BaseFeePerGas hexutil.Uint64 `json:"baseFeePerGas" gencodec:"required"` + BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } var enc ExecutableData @@ -43,7 +44,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { enc.GasUsed = hexutil.Uint64(e.GasUsed) enc.Timestamp = hexutil.Uint64(e.Timestamp) enc.ExtraData = e.ExtraData - enc.BaseFeePerGas = hexutil.Uint64(e.BaseFeePerGas) + enc.BaseFeePerGas = (*hexutil.Big)(e.BaseFeePerGas) if e.Transactions != nil { enc.Transactions = make([]hexutil.Bytes, len(e.Transactions)) for k, v := range e.Transactions { @@ -68,7 +69,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"` - BaseFeePerGas *hexutil.Uint64 `json:"baseFeePerGas" gencodec:"required"` + BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } var dec ExecutableData @@ -126,7 +127,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { if dec.BaseFeePerGas == nil { return errors.New("missing required field 'baseFeePerGas' for ExecutableData") } - e.BaseFeePerGas = uint64(*dec.BaseFeePerGas) + e.BaseFeePerGas = (*big.Int)(dec.BaseFeePerGas) if dec.Transactions == nil { return errors.New("missing required field 'transactions' for ExecutableData") } From 84c734dc343f203f1cf4023c63ea99a94d93ec11 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Sat, 2 Oct 2021 08:17:56 +0200 Subject: [PATCH 25/43] eth/catalyst: check that extradata is not longer than 32 bytes --- eth/catalyst/api.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index be13170ac6ba..6c5ebbfa1fef 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -391,6 +391,9 @@ func ExecutableDataToBlock(config *chainParams.ChainConfig, parent *types.Header if err != nil { return nil, err } + if len(params.ExtraData) > 32 { + return nil, fmt.Errorf("invalid extradata length: %v", len(params.ExtraData)) + } number := big.NewInt(0) number.SetUint64(params.Number) header := &types.Header{ From 299498daee912b89722c9b7df7686c855ab24852 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Sat, 2 Oct 2021 15:41:11 +0200 Subject: [PATCH 26/43] consensus/beacon: verify extradata in header --- consensus/beacon/consensus.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 20981ca3e56e..c8e49341074f 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -186,8 +186,8 @@ func (beacon *Beacon) VerifyUncles(chain consensus.ChainReader, block *types.Blo // (b) the timestamp is not verified anymore func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header) error { // Ensure that the header's extra-data section is of a reasonable size - if len(header.Extra) != 0 { - return fmt.Errorf("non-empty extra-data(%d)", len(header.Extra)) + if len(header.Extra) > 32 { + return fmt.Errorf("extra-data longer than 32 bytes (%d)", len(header.Extra)) } // Verify the block's difficulty to ensure it's the default constant if beaconDifficulty.Cmp(header.Difficulty) != 0 { From 3be59d3171170b7466c3e79218d93062daed640d Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Sun, 3 Oct 2021 07:10:29 +0200 Subject: [PATCH 27/43] eth/backend: correctly authorize inner engines --- eth/backend.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/eth/backend.go b/eth/backend.go index deee71626662..d56be0528f7e 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" @@ -478,6 +479,17 @@ func (s *Ethereum) StartMining(threads int) error { } clique.Authorize(eb, wallet.SignData) } + // Authorize nested engines too + if cl, ok := s.engine.(*beacon.Beacon); ok { + if clique, ok := cl.InnerEngine().(*clique.Clique); ok { + wallet, err := s.accountManager.Find(accounts.Account{Address: eb}) + if wallet == nil || err != nil { + log.Error("Etherbase account unavailable locally", "err", err) + return fmt.Errorf("signer missing: %v", err) + } + clique.Authorize(eb, wallet.SignData) + } + } // If mining is started, we can disable the transaction rejection mechanism // introduced to speed sync times. atomic.StoreUint32(&s.handler.acceptTxs, 1) From 2e331a2359d4cf79dad9ab216fe774a8bce8b424 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Sun, 3 Oct 2021 15:26:22 +0200 Subject: [PATCH 28/43] eth/catalyst: verify that totalDiff is reached when setting head --- eth/catalyst/api.go | 14 ++++++++++++++ eth/catalyst/api_test.go | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 6c5ebbfa1fef..f37c6d0b77d2 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -455,6 +455,20 @@ func (api *ConsensusAPI) setHead(newHead common.Hash) error { // Trigger the transition if it's the first `NewHead` event. merger := api.merger() if !merger.LeftPoW() { + // make sure the parent has enough terminal total difficulty + newHeadBlock := api.eth.BlockChain().GetBlockByHash(newHead) + if newHeadBlock == nil { + return &UnknownHeader + } + parentNo := newHeadBlock.NumberU64() - 1 + if parentNo < 0 { + return errors.New("can't set head on genesis") + } + parent := api.eth.BlockChain().GetBlockByNumber(parentNo) + td := api.eth.BlockChain().GetTdByHash(parent.Hash()) + if td != nil && td.Cmp(api.eth.BlockChain().Config().TerminalTotalDifficulty) < 0 { + return errors.New("total difficulty not reached yet") + } merger.LeavePoW() } if api.light { diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index b8110648975f..73578b810134 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -47,7 +47,7 @@ var ( func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) { db := rawdb.NewMemoryDatabase() config := params.AllEthashProtocolChanges - config.TerminalTotalDifficulty = big.NewInt(100000000000000) + config.TerminalTotalDifficulty = big.NewInt(int64(n + 1)) genesis := &core.Genesis{ Config: config, Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, From 311635d48608643daf4d4f092072994e5afafe4a Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Sun, 3 Oct 2021 15:51:34 +0200 Subject: [PATCH 29/43] eth/catalyst: verify that totalDiff is reached when forkChoiceUpdated --- eth/catalyst/api.go | 38 ++++++++++++++++++++++++-------------- eth/catalyst/api_test.go | 25 ++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index f37c6d0b77d2..65fe9d015241 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -201,6 +201,9 @@ func (api *ConsensusAPI) ConsensusValidated(params ConsensusValidatedParams) err // Finalize the transition if it's the first `FinalisedBlock` event. merger := api.merger() if !merger.EnteredPoS() { + if err := api.checkTerminalTotalDifficulty(params.BlockHash); err != nil { + return err + } merger.EnterPoS() } return nil //api.setHead(params.BlockHash) @@ -215,6 +218,9 @@ func (api *ConsensusAPI) ConsensusValidated(params ConsensusValidatedParams) err func (api *ConsensusAPI) ForkchoiceUpdated(params ForkChoiceParams) error { var emptyHash = common.Hash{} if !bytes.Equal(params.FinalizedBlockHash[:], emptyHash[:]) { + if err := api.checkTerminalTotalDifficulty(params.FinalizedBlockHash); err != nil { + return err + } return api.setHead(params.FinalizedBlockHash) } return nil @@ -450,25 +456,29 @@ func (api *ConsensusAPI) insertTransactions(txs types.Transactions) error { return nil } +func (api *ConsensusAPI) checkTerminalTotalDifficulty(head common.Hash) error { + // make sure the parent has enough terminal total difficulty + newHeadBlock := api.eth.BlockChain().GetBlockByHash(head) + if newHeadBlock == nil { + return &UnknownHeader + } + parentNo := newHeadBlock.NumberU64() - 1 + if parentNo < 0 { + return errors.New("can't set head on genesis") + } + parent := api.eth.BlockChain().GetBlockByNumber(parentNo) + td := api.eth.BlockChain().GetTdByHash(parent.Hash()) + if td != nil && td.Cmp(api.eth.BlockChain().Config().TerminalTotalDifficulty) < 0 { + return errors.New("total difficulty not reached yet") + } + return nil +} + // setHead is called to perform a force choice. func (api *ConsensusAPI) setHead(newHead common.Hash) error { // Trigger the transition if it's the first `NewHead` event. merger := api.merger() if !merger.LeftPoW() { - // make sure the parent has enough terminal total difficulty - newHeadBlock := api.eth.BlockChain().GetBlockByHash(newHead) - if newHeadBlock == nil { - return &UnknownHeader - } - parentNo := newHeadBlock.NumberU64() - 1 - if parentNo < 0 { - return errors.New("can't set head on genesis") - } - parent := api.eth.BlockChain().GetBlockByNumber(parentNo) - td := api.eth.BlockChain().GetTdByHash(parent.Hash()) - if td != nil && td.Cmp(api.eth.BlockChain().Config().TerminalTotalDifficulty) < 0 { - return errors.New("total difficulty not reached yet") - } merger.LeavePoW() } if api.light { diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 73578b810134..43110b8c8d96 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -47,7 +47,6 @@ var ( func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) { db := rawdb.NewMemoryDatabase() config := params.AllEthashProtocolChanges - config.TerminalTotalDifficulty = big.NewInt(int64(n + 1)) genesis := &core.Genesis{ Config: config, Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, @@ -66,6 +65,11 @@ func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) { gblock := genesis.ToBlock(db) engine := ethash.NewFaker() blocks, _ := core.GenerateChain(config, gblock, engine, db, n, generate) + totalDifficulty := big.NewInt(0) + for _, b := range blocks { + totalDifficulty.Add(totalDifficulty, b.Difficulty()) + } + config.TerminalTotalDifficulty = totalDifficulty return genesis, blocks } @@ -161,6 +165,21 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { } } +func TestSetHeadBeforeTotalDifficulty(t *testing.T) { + genesis, blocks := generatePreMergeChain(10) + n, ethservice := startEthService(t, genesis, blocks) + defer n.Close() + + api := NewConsensusAPI(ethservice, nil) + if err := api.ConsensusValidated(ConsensusValidatedParams{BlockHash: blocks[5].Hash(), Status: VALID.Status}); err == nil { + t.Errorf("consensus validated before total terminal difficulty should fail") + } + + if err := api.ForkchoiceUpdated(ForkChoiceParams{FinalizedBlockHash: blocks[5].Hash()}); err == nil { + t.Errorf("fork choice updated before total terminal difficulty should fail") + } +} + func TestEth2PrepareAndGetPayload(t *testing.T) { genesis, blocks := generatePreMergeChain(10) n, ethservice := startEthService(t, genesis, blocks[:9]) @@ -330,8 +349,8 @@ func TestEth2DeepReorg(t *testing.T) { if ethservice.BlockChain().CurrentBlock().NumberU64() != head { t.Fatalf("Chain head shouldn't be updated") } - if err := api.ForkchoiceUpdated(ForkChoiceParams{HeadBlockHash: block.Hash(), FinalizedBlockHash: block.Hash()}); err != nil { - t.Fatalf("Failed to insert block: %v", err) + if err := api.setHead(block.Hash()); err != nil { + t.Fatalf("Failed to set head: %v", err) } if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { t.Fatalf("Chain head should be updated") From 4a8425d0380a1755028d6bfa0ff0b3f13fcd2097 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Sun, 3 Oct 2021 16:15:11 +0200 Subject: [PATCH 30/43] eth/catalyst: verify that totalDiff is reached on executeBlock --- eth/catalyst/api.go | 9 +++-- eth/catalyst/api_test.go | 78 +++++++++++++++++++++------------------- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 65fe9d015241..11baee1dc925 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -246,15 +246,20 @@ func (api *ConsensusAPI) ExecutePayload(params ExecutableData) (GenericStringRes if parent == nil { return INVALID, fmt.Errorf("could not find parent %x", params.ParentHash) } + + td := api.eth.BlockChain().GetTdByHash(parent.Hash()) + ttd := api.eth.BlockChain().Config().TerminalTotalDifficulty if !api.eth.Synced() { - td := api.eth.BlockChain().GetTdByHash(parent.Hash()) - if td != nil && td.Cmp(api.eth.BlockChain().Config().TerminalTotalDifficulty) > 0 { + if td.Cmp(ttd) > 0 { + // first pos block api.eth.SetSynced() } else { // TODO (MariusVanDerWijden) if the node is not synced and we received a finalized block // we should trigger the reverse header sync here. return SYNCING, errors.New("node is not synced yet") } + } else if td.Cmp(ttd) < 0 { + return INVALID, fmt.Errorf("can not execute payload on top of block with low td got: %v threshold %v", td, ttd) } block, err := ExecutableDataToBlock(api.eth.BlockChain().Config(), parent.Header(), params) if err != nil { diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 43110b8c8d96..720a678ceac6 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -318,45 +318,49 @@ func TestEth2NewBlock(t *testing.T) { } func TestEth2DeepReorg(t *testing.T) { - genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2) - n, ethservice := startEthService(t, genesis, preMergeBlocks) - defer n.Close() - - var ( - api = NewConsensusAPI(ethservice, nil) - parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1] - head = ethservice.BlockChain().CurrentBlock().NumberU64() - ) - if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) { - t.Errorf("Block %d not pruned", parent.NumberU64()) - } - for i := 0; i < 10; i++ { - execData, err := api.assembleBlock(AssembleBlockParams{ - ParentHash: parent.Hash(), - Timestamp: parent.Time() + 5, - }) - if err != nil { - t.Fatalf("Failed to create the executable data %v", err) - } - block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) - if err != nil { - t.Fatalf("Failed to convert executable data to block %v", err) + // TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg + // before the totalTerminalDifficulty threshold + /* + genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2) + n, ethservice := startEthService(t, genesis, preMergeBlocks) + defer n.Close() + + var ( + api = NewConsensusAPI(ethservice, nil) + parent = preMergeBlocks[len(preMergeBlocks)-core.TriesInMemory-1] + head = ethservice.BlockChain().CurrentBlock().NumberU64() + ) + if ethservice.BlockChain().HasBlockAndState(parent.Hash(), parent.NumberU64()) { + t.Errorf("Block %d not pruned", parent.NumberU64()) } - newResp, err := api.ExecutePayload(*execData) - if err != nil || newResp.Status != "VALID" { - t.Fatalf("Failed to insert block: %v", err) + for i := 0; i < 10; i++ { + execData, err := api.assembleBlock(AssembleBlockParams{ + ParentHash: parent.Hash(), + Timestamp: parent.Time() + 5, + }) + if err != nil { + t.Fatalf("Failed to create the executable data %v", err) + } + block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) + if err != nil { + t.Fatalf("Failed to convert executable data to block %v", err) + } + newResp, err := api.ExecutePayload(*execData) + if err != nil || newResp.Status != "VALID" { + t.Fatalf("Failed to insert block: %v", err) + } + if ethservice.BlockChain().CurrentBlock().NumberU64() != head { + t.Fatalf("Chain head shouldn't be updated") + } + if err := api.setHead(block.Hash()); err != nil { + t.Fatalf("Failed to set head: %v", err) + } + if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { + t.Fatalf("Chain head should be updated") + } + parent, head = block, block.NumberU64() } - if ethservice.BlockChain().CurrentBlock().NumberU64() != head { - t.Fatalf("Chain head shouldn't be updated") - } - if err := api.setHead(block.Hash()); err != nil { - t.Fatalf("Failed to set head: %v", err) - } - if ethservice.BlockChain().CurrentBlock().NumberU64() != block.NumberU64() { - t.Fatalf("Chain head should be updated") - } - parent, head = block, block.NumberU64() - } + */ } // startEthService creates a full node instance for testing. From ad5da558204368a07afd120a5de338b5aab1ed19 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Sun, 3 Oct 2021 17:08:58 +0200 Subject: [PATCH 31/43] eth/catalyst: prevent nil panic in checkTerminalTotalDifficulty --- eth/catalyst/api.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 11baee1dc925..1d6e72093374 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -472,6 +472,9 @@ func (api *ConsensusAPI) checkTerminalTotalDifficulty(head common.Hash) error { return errors.New("can't set head on genesis") } parent := api.eth.BlockChain().GetBlockByNumber(parentNo) + if parent == nil { + return &UnknownHeader + } td := api.eth.BlockChain().GetTdByHash(parent.Hash()) if td != nil && td.Cmp(api.eth.BlockChain().Config().TerminalTotalDifficulty) < 0 { return errors.New("total difficulty not reached yet") From b798cbdad11b9d7c27287ab5f4a1d6290f27ee82 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Sun, 3 Oct 2021 17:25:08 +0200 Subject: [PATCH 32/43] eth/catalyst: prevent nil panic in checkTerminalTotalDifficulty --- eth/catalyst/api.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 1d6e72093374..64a9bf308439 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -462,18 +462,18 @@ func (api *ConsensusAPI) insertTransactions(txs types.Transactions) error { } func (api *ConsensusAPI) checkTerminalTotalDifficulty(head common.Hash) error { + // shortcut if we entered PoS already + if api.merger().EnteredPoS() { + return nil + } // make sure the parent has enough terminal total difficulty newHeadBlock := api.eth.BlockChain().GetBlockByHash(head) if newHeadBlock == nil { return &UnknownHeader } - parentNo := newHeadBlock.NumberU64() - 1 - if parentNo < 0 { - return errors.New("can't set head on genesis") - } - parent := api.eth.BlockChain().GetBlockByNumber(parentNo) + parent := api.eth.BlockChain().GetBlockByHash(newHeadBlock.ParentHash()) if parent == nil { - return &UnknownHeader + return fmt.Errorf("parent unavailable: %v", newHeadBlock.ParentHash()) } td := api.eth.BlockChain().GetTdByHash(parent.Hash()) if td != nil && td.Cmp(api.eth.BlockChain().Config().TerminalTotalDifficulty) < 0 { From 54a5b2f405f865cf6a66d9132b5cbee1a11a1e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 5 Jul 2021 10:11:53 +0300 Subject: [PATCH 33/43] eth: reqid dispatcher, nuke fast sync, add beacon sync --- cmd/utils/flags.go | 2 +- core/rawdb/accessors_chain.go | 18 - core/rawdb/accessors_snapshot.go | 8 - core/rawdb/accessors_sync.go | 80 ++ core/rawdb/schema.go | 10 + eth/api.go | 10 + eth/downloader/beaconsync.go | 215 +++ eth/downloader/downloader.go | 1184 ++++++----------- eth/downloader/downloader_test.go | 185 +-- eth/downloader/fetchers.go | 113 ++ eth/downloader/fetchers_concurrent.go | 342 +++++ eth/downloader/fetchers_concurrent_bodies.go | 104 ++ eth/downloader/fetchers_concurrent_headers.go | 95 ++ .../fetchers_concurrent_receipts.go | 103 ++ eth/downloader/modes.go | 9 +- eth/downloader/peer.go | 280 +--- eth/downloader/queue.go | 142 +- eth/downloader/queue_test.go | 10 +- eth/downloader/skeleton.go | 958 +++++++++++++ eth/downloader/skeleton_test.go | 257 ++++ eth/downloader/statesync.go | 518 +------- eth/fetcher/block_fetcher.go | 42 +- eth/fetcher/block_fetcher_test.go | 69 +- eth/handler.go | 153 ++- eth/handler_eth.go | 100 +- eth/handler_eth_test.go | 16 +- eth/handler_test.go | 2 +- eth/peer.go | 7 +- eth/protocols/eth/dispatcher.go | 198 +++ eth/protocols/eth/handlers.go | 32 +- eth/protocols/eth/peer.go | 156 ++- eth/protocols/snap/sync.go | 44 +- eth/sync.go | 33 +- eth/sync_test.go | 8 +- ethclient/ethclient.go | 18 +- graphql/graphql.go | 64 +- interfaces.go | 18 +- internal/ethapi/api.go | 40 +- internal/jsre/deps/bindata.go | 6 +- internal/jsre/deps/web3.js | 16 +- internal/web3ext/web3ext.go | 5 + les/downloader/downloader.go | 10 +- les/downloader/downloader_test.go | 4 +- les/downloader/statesync.go | 3 +- mobile/ethereum.go | 20 +- 45 files changed, 3654 insertions(+), 2053 deletions(-) create mode 100644 core/rawdb/accessors_sync.go create mode 100644 eth/downloader/beaconsync.go create mode 100644 eth/downloader/fetchers.go create mode 100644 eth/downloader/fetchers_concurrent.go create mode 100644 eth/downloader/fetchers_concurrent_bodies.go create mode 100644 eth/downloader/fetchers_concurrent_headers.go create mode 100644 eth/downloader/fetchers_concurrent_receipts.go create mode 100644 eth/downloader/skeleton.go create mode 100644 eth/downloader/skeleton_test.go create mode 100644 eth/protocols/eth/dispatcher.go diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 0ebb09fb73fc..1a79907cf05a 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -206,7 +206,7 @@ var ( defaultSyncMode = ethconfig.Defaults.SyncMode SyncModeFlag = TextMarshalerFlag{ Name: "syncmode", - Usage: `Blockchain sync mode ("fast", "full", "snap" or "light")`, + Usage: `Blockchain sync mode ("snap", "full" or "light")`, Value: &defaultSyncMode, } GCModeFlag = cli.StringFlag{ diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index ed1c71e20216..1d1415b089ad 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -247,24 +247,6 @@ func WriteLastPivotNumber(db ethdb.KeyValueWriter, pivot uint64) { } } -// ReadFastTrieProgress retrieves the number of tries nodes fast synced to allow -// reporting correct numbers across restarts. -func ReadFastTrieProgress(db ethdb.KeyValueReader) uint64 { - data, _ := db.Get(fastTrieProgressKey) - if len(data) == 0 { - return 0 - } - return new(big.Int).SetBytes(data).Uint64() -} - -// WriteFastTrieProgress stores the fast sync trie process counter to support -// retrieving it across restarts. -func WriteFastTrieProgress(db ethdb.KeyValueWriter, count uint64) { - if err := db.Put(fastTrieProgressKey, new(big.Int).SetUint64(count).Bytes()); err != nil { - log.Crit("Failed to store fast sync trie progress", "err", err) - } -} - // ReadTxIndexTail retrieves the number of oldest indexed block // whose transaction indices has been indexed. If the corresponding entry // is non-existent in database it means the indexing has been finished. diff --git a/core/rawdb/accessors_snapshot.go b/core/rawdb/accessors_snapshot.go index 88446e079266..61a612c792a6 100644 --- a/core/rawdb/accessors_snapshot.go +++ b/core/rawdb/accessors_snapshot.go @@ -208,11 +208,3 @@ func WriteSnapshotSyncStatus(db ethdb.KeyValueWriter, status []byte) { log.Crit("Failed to store snapshot sync status", "err", err) } } - -// DeleteSnapshotSyncStatus deletes the serialized sync status saved at the last -// shutdown -func DeleteSnapshotSyncStatus(db ethdb.KeyValueWriter) { - if err := db.Delete(snapshotSyncStatusKey); err != nil { - log.Crit("Failed to remove snapshot sync status", "err", err) - } -} diff --git a/core/rawdb/accessors_sync.go b/core/rawdb/accessors_sync.go new file mode 100644 index 000000000000..50dfb848e4e0 --- /dev/null +++ b/core/rawdb/accessors_sync.go @@ -0,0 +1,80 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +import ( + "bytes" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" +) + +// ReadSkeletonSyncStatus retrieves the serialized sync status saved at shutdown. +func ReadSkeletonSyncStatus(db ethdb.KeyValueReader) []byte { + data, _ := db.Get(skeletonSyncStatusKey) + return data +} + +// WriteSkeletonSyncStatus stores the serialized sync status to save at shutdown. +func WriteSkeletonSyncStatus(db ethdb.KeyValueWriter, status []byte) { + if err := db.Put(skeletonSyncStatusKey, status); err != nil { + log.Crit("Failed to store skeleton sync status", "err", err) + } +} + +// DeleteSkeletonSyncStatus deletes the serialized sync status saved at the last +// shutdown +func DeleteSkeletonSyncStatus(db ethdb.KeyValueWriter) { + if err := db.Delete(skeletonSyncStatusKey); err != nil { + log.Crit("Failed to remove skeleton sync status", "err", err) + } +} + +// ReadSkeletonHeader retrieves a block header from the skeleton sync store, +func ReadSkeletonHeader(db ethdb.KeyValueReader, number uint64) *types.Header { + data, _ := db.Get(skeletonHeaderKey(number)) + if len(data) == 0 { + return nil + } + header := new(types.Header) + if err := rlp.Decode(bytes.NewReader(data), header); err != nil { + log.Error("Invalid skeleton header RLP", "number", number, "err", err) + return nil + } + return header +} + +// WriteSkeletonHeader stores a block header into the skeleton sync store. +func WriteSkeletonHeader(db ethdb.KeyValueWriter, header *types.Header) { + data, err := rlp.EncodeToBytes(header) + if err != nil { + log.Crit("Failed to RLP encode header", "err", err) + } + key := skeletonHeaderKey(header.Number.Uint64()) + if err := db.Put(key, data); err != nil { + log.Crit("Failed to store skeleton header", "err", err) + } +} + +// DeleteSkeletonHeader removes all block header data associated with a hash. +func DeleteSkeletonHeader(db ethdb.KeyValueWriter, number uint64) { + if err := db.Delete(skeletonHeaderKey(number)); err != nil { + log.Crit("Failed to delete skeleton header", "err", err) + } +} diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 9fd6d39b0023..7ce8ccf3c02d 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -63,6 +63,9 @@ var ( // snapshotSyncStatusKey tracks the snapshot sync status across restarts. snapshotSyncStatusKey = []byte("SnapshotSyncStatus") + // skeletonSyncStatusKey tracks the skeleton sync status across restarts. + skeletonSyncStatusKey = []byte("SkeletonSyncStatus") + // txIndexTailKey tracks the oldest block whose transactions have been indexed. txIndexTailKey = []byte("TransactionIndexTail") @@ -93,6 +96,8 @@ var ( SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value CodePrefix = []byte("c") // CodePrefix + code hash -> account code + skeletonHeaderPrefix = []byte("S") // skeletonHeaderPrefox + num (uint64 big endian) -> header + preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage configPrefix = []byte("ethereum-config-") // config prefix for the db @@ -210,6 +215,11 @@ func bloomBitsKey(bit uint, section uint64, hash common.Hash) []byte { return key } +// skeletonHeaderKey = skeletonHeaderPrefix + num (uint64 big endian) +func skeletonHeaderKey(number uint64) []byte { + return append(skeletonHeaderPrefix, encodeBlockNumber(number)...) +} + // preimageKey = preimagePrefix + hash func preimageKey(hash common.Hash) []byte { return append(preimagePrefix, hash.Bytes()...) diff --git a/eth/api.go b/eth/api.go index 8b96d1f316d7..0342d943c085 100644 --- a/eth/api.go +++ b/eth/api.go @@ -256,6 +256,16 @@ func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) { return true, nil } +// NewHead requests the node to beacon-sync to the designated head header. +func (api *PrivateAdminAPI) NewHead(blob hexutil.Bytes) error { + header := new(types.Header) + if err := rlp.DecodeBytes(blob, header); err != nil { + return err + } + mode, _ := api.eth.handler.chainSync.modeAndLocalHead() + return api.eth.Downloader().BeaconSync(mode, header) +} + // PublicDebugAPI is the collection of Ethereum full node APIs exposed // over the public debugging endpoint. type PublicDebugAPI struct { diff --git a/eth/downloader/beaconsync.go b/eth/downloader/beaconsync.go new file mode 100644 index 000000000000..1f6fad26ee0f --- /dev/null +++ b/eth/downloader/beaconsync.go @@ -0,0 +1,215 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package downloader + +import ( + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" +) + +// beaconBackfiller is the chain and state backfilling that can be commenced once +// the skeleton syncer has successfully reverse downloaded all the headers up to +// the genesis block or an existing header in the database. Its operation is fully +// directed by the skeleton sync's head/tail events. +type beaconBackfiller struct { + downloader *Downloader // Downloader to direct via this callback implementation + syncMode SyncMode // Sync mode to use for backfilling the skeleton chains + success func() // Callback to run on successful sync cycle completion + filling bool // Flag whether the downloader is backfilling or not + lock sync.Mutex // Mutex protecting the sync lock +} + +// newBeaconBackfiller is a helper method to create the backfiller. +func newBeaconBackfiller(dl *Downloader, success func()) backfiller { + return &beaconBackfiller{ + downloader: dl, + success: success, + } +} + +// suspend cancels any background downloader threads. +func (b *beaconBackfiller) suspend() { + b.downloader.Cancel() +} + +// resume starts the downloader threads for backfilling state and chain data. +func (b *beaconBackfiller) resume() { + b.lock.Lock() + b.filling = true + mode := b.syncMode + b.lock.Unlock() + + // Start the backfilling on its own thread since the downloader does not have + // its own lifecycle runloop. + go func() { + // Set the backfiller to non-filling when download completes + defer func() { + b.lock.Lock() + b.filling = false + b.lock.Unlock() + }() + // If the downloader fails, report an error as in beacon chain mode there + // should be no errors as long as the chain we're syncing to is valid. + if err := b.downloader.synchronise("", common.Hash{}, nil, mode, true); err != nil { + log.Error("Beacon backfilling failed", "err", err) + return + } + // Synchronization succeeded. Since this happens async, notify the outer + // context to disable snap syncing and enable transaction propagation. + if b.success != nil { + b.success() + } + }() +} + +// setMode updates the sync mode from the current one to the requested one. If +// there's an active sync in progress, it will be cancelled and restarted. +func (b *beaconBackfiller) setMode(mode SyncMode) { + // Update the old sync mode and track if it was changed + b.lock.Lock() + updated := b.syncMode != mode + filling := b.filling + b.syncMode = mode + b.lock.Unlock() + + // If the sync mode was changed mid-sync, restart. This should never ever + // really happen, we just handle it to detect programming errors. + if !updated || !filling { + return + } + log.Error("Downloader sync mode changed mid-run", "old", mode.String(), "new", mode.String()) + b.suspend() + b.resume() +} + +// BeaconSync is the Ethereum 2 version of the chain synchronization, where the +// chain is not downloaded from genesis onward, rather from trusted head announces +// backwards. +// +// Internally backfilling and state sync is done the same way, but the header +// retrieval and scheduling is replaced. +func (d *Downloader) BeaconSync(mode SyncMode, head *types.Header) error { + // When the downloader starts a sync cycle, it needs to be aware of the sync + // mode to use (full, snap). To keep the skeleton chain oblivious, inject the + // mode into the backfiller directly. + // + // Super crazy dangerous type cast. Should be fine (TM), we're only using a + // different backfiller implementation for skeleton tests. + d.skeleton.filler.(*beaconBackfiller).setMode(mode) + + // Signal the skeleton sync to switch to a new head, however it wants + if err := d.skeleton.Sync(head); err != nil { + return err + } + return nil +} + +// findBeaconAncestor tries to locate the common ancestor link of the local chain +// and the beacon chain just requested. In the general case when our node was in +// sync and on the correct chain, checking the top N links should already get us +// a match. In the rare scenario when we ended up on a long reorganisation (i.e. +// none of the head links match), we do a binary search to find the ancestor. +func (d *Downloader) findBeaconAncestor() uint64 { + // Figure out the current local head position + var head *types.Header + + switch d.getMode() { + case FullSync: + head = d.blockchain.CurrentBlock().Header() + case SnapSync: + head = d.blockchain.CurrentFastBlock().Header() + default: + head = d.lightchain.CurrentHeader() + } + number := head.Number.Uint64() + + // If the head is present in the skeleton chain, return that + if head.Hash() == d.skeleton.Header(number).Hash() { + return number + } + // Head header not present, binary search to find the ancestor + start, end := uint64(0), number + for start+1 < end { + // Split our chain interval in two, and request the hash to cross check + check := (start + end) / 2 + + h := d.skeleton.Header(check) + n := h.Number.Uint64() + + var known bool + switch d.getMode() { + case FullSync: + known = d.blockchain.HasBlock(h.Hash(), n) + case SnapSync: + known = d.blockchain.HasFastBlock(h.Hash(), n) + default: + known = d.lightchain.HasHeader(h.Hash(), n) + } + if !known { + end = check + continue + } + start = check + } + return start +} + +// fetchBeaconHeaders feeds skeleton headers to the downloader queue for scheduling +// until sync errors or is finished. +func (d *Downloader) fetchBeaconHeaders(from uint64) error { + head, err := d.skeleton.Head() + if err != nil { + return err + } + for { + // Retrieve a batch of headers and feed it to the header processor + headers := make([]*types.Header, 0, maxHeadersProcess) + for i := 0; i < maxHeadersProcess && from <= head.Number.Uint64(); i++ { + headers = append(headers, d.skeleton.Header(from)) + from++ + } + select { + case d.headerProcCh <- headers: + case <-d.cancelCh: + return errCanceled + } + // If we still have headers to import, loop and keep pushing them + if from <= head.Number.Uint64() { + continue + } + // If the pivot block is committed, signal header sync termination + if atomic.LoadInt32(&d.committed) == 1 { + d.headerProcCh <- nil + return nil + } + // State sync still going, wait a bit for new headers and retry + select { + case <-time.After(fsHeaderContCheck): + case <-d.cancelCh: + return errCanceled + } + head, err = d.skeleton.Head() + if err != nil { + return err + } + } +} diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 9bfac76f2b23..0463025eaaa2 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -30,12 +30,10 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" ) @@ -45,7 +43,6 @@ var ( MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request MaxSkeletonSize = 128 // Number of header fetches to need for a skeleton assembly MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request - MaxStateFetch = 384 // Amount of node state values to allow fetching per request maxQueuedHeaders = 32 * 1024 // [eth/62] Maximum number of headers to queue for import (DOS protection) maxHeadersProcess = 2048 // Number of header download results to import at once into the chain @@ -98,9 +95,8 @@ type Downloader struct { stateBloom *trie.SyncBloom // Bloom filter for fast trie node and contract code existence checks // Statistics - syncStatsChainOrigin uint64 // Origin block number where syncing started at - syncStatsChainHeight uint64 // Highest block number known when syncing started - syncStatsState stateSyncStats + syncStatsChainOrigin uint64 // Origin block number where syncing started at + syncStatsChainHeight uint64 // Highest block number known when syncing started syncStatsLock sync.RWMutex // Lock protecting the sync stats fields lightchain LightChain @@ -117,12 +113,10 @@ type Downloader struct { ancientLimit uint64 // The maximum block number which can be regarded as ancient data. // Channels - headerCh chan dataPack // Channel receiving inbound block headers - bodyCh chan dataPack // Channel receiving inbound block bodies - receiptCh chan dataPack // Channel receiving inbound receipts - bodyWakeCh chan bool // Channel to signal the block body fetcher of new tasks - receiptWakeCh chan bool // Channel to signal the receipt fetcher of new tasks - headerProcCh chan []*types.Header // Channel to feed the header processor new tasks + headerProcCh chan []*types.Header // Channel to feed the header processor new tasks + + // Skeleton sync + skeleton *skeleton // Header skeleton to backfill the chain with (eth2 mode) // State sync pivotHeader *types.Header // Pivot block header to dynamically push the syncing state root @@ -131,8 +125,6 @@ type Downloader struct { snapSync bool // Whether to run state sync over the snap protocol SnapSyncer *snap.Syncer // TODO(karalabe): make private! hack for now stateSyncStart chan *stateSync - trackStateReq chan *stateReq - stateCh chan dataPack // Channel receiving inbound node state data // Cancellation and termination cancelPeer string // Identifier of the peer currently being used as the master (cancel on drop) @@ -204,7 +196,7 @@ type BlockChain interface { } // New creates a new downloader to fetch hashes and blocks from remote peers. -func New(checkpoint uint64, stateDb ethdb.Database, stateBloom *trie.SyncBloom, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn) *Downloader { +func New(checkpoint uint64, stateDb ethdb.Database, stateBloom *trie.SyncBloom, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn, success func()) *Downloader { if lightchain == nil { lightchain = chain } @@ -218,21 +210,13 @@ func New(checkpoint uint64, stateDb ethdb.Database, stateBloom *trie.SyncBloom, blockchain: chain, lightchain: lightchain, dropPeer: dropPeer, - headerCh: make(chan dataPack, 1), - bodyCh: make(chan dataPack, 1), - receiptCh: make(chan dataPack, 1), - bodyWakeCh: make(chan bool, 1), - receiptWakeCh: make(chan bool, 1), headerProcCh: make(chan []*types.Header, 1), quitCh: make(chan struct{}), - stateCh: make(chan dataPack), SnapSyncer: snap.NewSyncer(stateDb), stateSyncStart: make(chan *stateSync), - syncStatsState: stateSyncStats{ - processed: rawdb.ReadFastTrieProgress(stateDb), - }, - trackStateReq: make(chan *stateReq), } + dl.skeleton = newSkeleton(stateDb, dl.peers, dropPeer, newBeaconBackfiller(dl, success)) + go dl.stateFetcher() return dl } @@ -254,19 +238,31 @@ func (d *Downloader) Progress() ethereum.SyncProgress { switch { case d.blockchain != nil && mode == FullSync: current = d.blockchain.CurrentBlock().NumberU64() - case d.blockchain != nil && mode == FastSync: + case d.blockchain != nil && mode == SnapSync: current = d.blockchain.CurrentFastBlock().NumberU64() case d.lightchain != nil: current = d.lightchain.CurrentHeader().Number.Uint64() default: log.Error("Unknown downloader chain/mode combo", "light", d.lightchain != nil, "full", d.blockchain != nil, "mode", mode) } + progress, pending := d.SnapSyncer.Progress() + return ethereum.SyncProgress{ - StartingBlock: d.syncStatsChainOrigin, - CurrentBlock: current, - HighestBlock: d.syncStatsChainHeight, - PulledStates: d.syncStatsState.processed, - KnownStates: d.syncStatsState.processed + d.syncStatsState.pending, + StartingBlock: d.syncStatsChainOrigin, + CurrentBlock: current, + HighestBlock: d.syncStatsChainHeight, + SyncedAccounts: progress.AccountSynced, + SyncedAccountBytes: uint64(progress.AccountBytes), + SyncedBytecodes: progress.BytecodeSynced, + SyncedBytecodeBytes: uint64(progress.BytecodeBytes), + SyncedStorage: progress.StorageSynced, + SyncedStorageBytes: uint64(progress.StorageBytes), + HealedTrienodes: progress.TrienodeHealSynced, + HealedTrienodeBytes: uint64(progress.TrienodeHealBytes), + HealedBytecodes: progress.BytecodeHealSynced, + HealedBytecodeBytes: uint64(progress.BytecodeHealBytes), + HealingTrienodes: pending.TrienodeHeal, + HealingBytecode: pending.BytecodeHeal, } } @@ -320,10 +316,10 @@ func (d *Downloader) UnregisterPeer(id string) error { return nil } -// Synchronise tries to sync up our local block chain with a remote peer, both +// LegacySync tries to sync up our local block chain with a remote peer, both // adding various sanity checks as well as wrapping it with various log entries. -func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode SyncMode) error { - err := d.synchronise(id, head, td, mode) +func (d *Downloader) LegacySync(id string, head common.Hash, td *big.Int, mode SyncMode) error { + err := d.synchronise(id, head, td, mode, false) switch err { case nil, errBusy, errCanceled: @@ -349,7 +345,7 @@ func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode // synchronise will select the peer and use it for synchronising. If an empty string is given // it will use the best peer possible and synchronize if its TD is higher than our own. If any of the // checks fail an error will be returned. This method is synchronous -func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode SyncMode) error { +func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode SyncMode, beaconMode bool) error { // Mock out the synchronisation if testing if d.synchroniseMock != nil { return d.synchroniseMock(id, hash) @@ -374,37 +370,23 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode // sync mode. Long term we could drop fast sync or merge the two together, // but until snap becomes prevalent, we should support both. TODO(karalabe). if mode == SnapSync { - if !d.snapSync { - // Snap sync uses the snapshot namespace to store potentially flakey data until - // sync completely heals and finishes. Pause snapshot maintenance in the mean - // time to prevent access. - if snapshots := d.blockchain.Snapshots(); snapshots != nil { // Only nil in tests - snapshots.Disable() - } - log.Warn("Enabling snapshot sync prototype") - d.snapSync = true + // Snap sync uses the snapshot namespace to store potentially flakey data until + // sync completely heals and finishes. Pause snapshot maintenance in the mean- + // time to prevent access. + if snapshots := d.blockchain.Snapshots(); snapshots != nil { // Only nil in tests + snapshots.Disable() } - mode = FastSync } // Reset the queue, peer set and wake channels to clean any internal leftover state d.queue.Reset(blockCacheMaxItems, blockCacheInitialItems) d.peers.Reset() - for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh} { + for _, ch := range []chan bool{d.queue.blockWakeCh, d.queue.receiptWakeCh} { select { case <-ch: default: } } - for _, ch := range []chan dataPack{d.headerCh, d.bodyCh, d.receiptCh} { - for empty := false; !empty; { - select { - case <-ch: - default: - empty = true - } - } - } for empty := false; !empty; { select { case <-d.headerProcCh: @@ -424,11 +406,14 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode atomic.StoreUint32(&d.mode, uint32(mode)) // Retrieve the origin peer and initiate the downloading process - p := d.peers.Peer(id) - if p == nil { - return errUnknownPeer + var p *peerConnection + if !beaconMode { // Beacon mode doesn't need a peer to sync from + p = d.peers.Peer(id) + if p == nil { + return errUnknownPeer + } } - return d.syncWithPeer(p, hash, td) + return d.syncWithPeer(p, hash, td, beaconMode) } func (d *Downloader) getMode() SyncMode { @@ -437,7 +422,7 @@ func (d *Downloader) getMode() SyncMode { // syncWithPeer starts a block synchronization based on the hash chain from the // specified peer and head hash. -func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.Int) (err error) { +func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.Int, beaconMode bool) (err error) { d.mux.Post(StartEvent{}) defer func() { // reset on error @@ -448,33 +433,57 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I d.mux.Post(DoneEvent{latest}) } }() - if p.version < eth.ETH66 { - return fmt.Errorf("%w: advertized %d < required %d", errTooOld, p.version, eth.ETH66) - } mode := d.getMode() - log.Debug("Synchronising with the network", "peer", p.id, "eth", p.version, "head", hash, "td", td, "mode", mode) + if !beaconMode { + log.Debug("Synchronising with the network", "peer", p.id, "eth", p.version, "head", hash, "td", td, "mode", mode) + } else { + log.Debug("Backfilling with the network", "mode", mode) + } defer func(start time.Time) { log.Debug("Synchronisation terminated", "elapsed", common.PrettyDuration(time.Since(start))) }(time.Now()) // Look up the sync boundaries: the common ancestor and the target block - latest, pivot, err := d.fetchHead(p) - if err != nil { - return err - } - if mode == FastSync && pivot == nil { - // If no pivot block was returned, the head is below the min full block - // threshold (i.e. new chain). In that case we won't really fast sync - // anyway, but still need a valid pivot block to avoid some code hitting - // nil panics on an access. - pivot = d.blockchain.CurrentBlock().Header() + var latest, pivot *types.Header + if !beaconMode { + // In legacy mode, use the master peer to retrieve the headers from + latest, pivot, err = d.fetchHead(p) + if err != nil { + return err + } + if mode == SnapSync && pivot == nil { + // If no pivot block was returned, the head is below the min full block + // threshold (i.e. new chain). In that case we won't really fast sync + // anyway, but still need a valid pivot block to avoid some code hitting + // nil panics on an access. + pivot = d.blockchain.CurrentBlock().Header() + } + } else { + // In beacon mode, user the skeleton chain to retrieve the headers from + latest, err = d.skeleton.Head() + if err != nil { + return err + } + // Opposed to legacy mode, in beacon mode we trust the chain we've been + // told to sync to, so no need to leave a gap between the pivot and head + // to full sync. Still, the downloader's been architected to do a full + // block import after the pivot, so make it off by one to avoid having + // to special case everything internally. + pivot = d.skeleton.Header(latest.Number.Uint64() - 1) } height := latest.Number.Uint64() - origin, err := d.findAncestor(p, latest) - if err != nil { - return err + var origin uint64 + if !beaconMode { + // In legacy mode, reach out to the network and find the ancestor + origin, err = d.findAncestor(p, latest) + if err != nil { + return err + } + } else { + // In beacon mode, use the skeleton chain for the ancestor lookup + origin = d.findBeaconAncestor() } d.syncStatsLock.Lock() if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin { @@ -484,7 +493,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I d.syncStatsLock.Unlock() // Ensure our origin point is below any fast sync pivot point - if mode == FastSync { + if mode == SnapSync { if height <= uint64(fsMinFullBlocks) { origin = 0 } else { @@ -498,10 +507,10 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I } } d.committed = 1 - if mode == FastSync && pivot.Number.Uint64() != 0 { + if mode == SnapSync && pivot.Number.Uint64() != 0 { d.committed = 0 } - if mode == FastSync { + if mode == SnapSync { // Set the ancient data limitation. // If we are running fast sync, all block data older than ancientLimit will be // written to the ancient store. More recent data will be written to the active @@ -545,13 +554,21 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I if d.syncInitHook != nil { d.syncInitHook(origin, height) } + var headerFetcher func() error + if !beaconMode { + // In legacy mode, headers are retrieved from the network + headerFetcher = func() error { return d.fetchHeaders(p, origin+1) } + } else { + // In beacon mode, headers are served by the skeleton syncer + headerFetcher = func() error { return d.fetchBeaconHeaders(origin + 1) } + } fetchers := []func() error{ - func() error { return d.fetchHeaders(p, origin+1) }, // Headers are always retrieved - func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync - func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync - func() error { return d.processHeaders(origin+1, td) }, + headerFetcher, // Headers are always retrieved + func() error { return d.fetchBodies(origin+1, beaconMode) }, // Bodies are retrieved during normal and fast sync + func() error { return d.fetchReceipts(origin+1, beaconMode) }, // Receipts are retrieved during fast sync + func() error { return d.processHeaders(origin+1, td, beaconMode) }, } - if mode == FastSync { + if mode == SnapSync { d.pivotLock.Lock() d.pivotHeader = pivot d.pivotLock.Unlock() @@ -643,60 +660,38 @@ func (d *Downloader) fetchHead(p *peerConnection) (head *types.Header, pivot *ty // Request the advertised remote head block and wait for the response latest, _ := p.peer.Head() fetch := 1 - if mode == FastSync { + if mode == SnapSync { fetch = 2 // head + pivot headers } - go p.peer.RequestHeadersByHash(latest, fetch, fsMinFullBlocks-1, true) - - ttl := d.peers.rates.TargetTimeout() - timeout := time.After(ttl) - for { - select { - case <-d.cancelCh: - return nil, nil, errCanceled - - case packet := <-d.headerCh: - // Discard anything not from the origin peer - if packet.PeerId() != p.id { - log.Debug("Received headers from incorrect peer", "peer", packet.PeerId()) - break - } - // Make sure the peer gave us at least one and at most the requested headers - headers := packet.(*headerPack).headers - if len(headers) == 0 || len(headers) > fetch { - return nil, nil, fmt.Errorf("%w: returned headers %d != requested %d", errBadPeer, len(headers), fetch) - } - // The first header needs to be the head, validate against the checkpoint - // and request. If only 1 header was returned, make sure there's no pivot - // or there was not one requested. - head := headers[0] - if (mode == FastSync || mode == LightSync) && head.Number.Uint64() < d.checkpoint { - return nil, nil, fmt.Errorf("%w: remote head %d below checkpoint %d", errUnsyncedPeer, head.Number, d.checkpoint) - } - if len(headers) == 1 { - if mode == FastSync && head.Number.Uint64() > uint64(fsMinFullBlocks) { - return nil, nil, fmt.Errorf("%w: no pivot included along head header", errBadPeer) - } - p.log.Debug("Remote head identified, no pivot", "number", head.Number, "hash", head.Hash()) - return head, nil, nil - } - // At this point we have 2 headers in total and the first is the - // validated head of the chain. Check the pivot number and return, - pivot := headers[1] - if pivot.Number.Uint64() != head.Number.Uint64()-uint64(fsMinFullBlocks) { - return nil, nil, fmt.Errorf("%w: remote pivot %d != requested %d", errInvalidChain, pivot.Number, head.Number.Uint64()-uint64(fsMinFullBlocks)) - } - return head, pivot, nil - - case <-timeout: - p.log.Debug("Waiting for head header timed out", "elapsed", ttl) - return nil, nil, errTimeout - - case <-d.bodyCh: - case <-d.receiptCh: - // Out of bounds delivery, ignore + headers, err := d.fetchHeadersByHash(p, latest, fetch, fsMinFullBlocks-1, true) + if err != nil { + return nil, nil, err + } + // Make sure the peer gave us at least one and at most the requested headers + if len(headers) == 0 || len(headers) > fetch { + return nil, nil, fmt.Errorf("%w: returned headers %d != requested %d", errBadPeer, len(headers), fetch) + } + // The first header needs to be the head, validate against the checkpoint + // and request. If only 1 header was returned, make sure there's no pivot + // or there was not one requested. + head = headers[0] + if (mode == SnapSync || mode == LightSync) && head.Number.Uint64() < d.checkpoint { + return nil, nil, fmt.Errorf("%w: remote head %d below checkpoint %d", errUnsyncedPeer, head.Number, d.checkpoint) + } + if len(headers) == 1 { + if mode == SnapSync && head.Number.Uint64() > uint64(fsMinFullBlocks) { + return nil, nil, fmt.Errorf("%w: no pivot included along head header", errBadPeer) } + p.log.Debug("Remote head identified, no pivot", "number", head.Number, "hash", head.Hash()) + return head, nil, nil + } + // At this point we have 2 headers in total and the first is the + // validated head of the chian. Check the pivot number and return, + pivot = headers[1] + if pivot.Number.Uint64() != head.Number.Uint64()-uint64(fsMinFullBlocks) { + return nil, nil, fmt.Errorf("%w: remote pivot %d != requested %d", errInvalidChain, pivot.Number, head.Number.Uint64()-uint64(fsMinFullBlocks)) } + return head, pivot, nil } // calculateRequestSpan calculates what headers to request from a peer when trying to determine the @@ -767,7 +762,7 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) switch mode { case FullSync: localHeight = d.blockchain.CurrentBlock().NumberU64() - case FastSync: + case SnapSync: localHeight = d.blockchain.CurrentFastBlock().NumberU64() default: localHeight = d.lightchain.CurrentHeader().Number.Uint64() @@ -822,76 +817,52 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) return ancestor, nil } -func (d *Downloader) findAncestorSpanSearch(p *peerConnection, mode SyncMode, remoteHeight, localHeight uint64, floor int64) (commonAncestor uint64, err error) { +func (d *Downloader) findAncestorSpanSearch(p *peerConnection, mode SyncMode, remoteHeight, localHeight uint64, floor int64) (uint64, error) { from, count, skip, max := calculateRequestSpan(remoteHeight, localHeight) p.log.Trace("Span searching for common ancestor", "count", count, "from", from, "skip", skip) - go p.peer.RequestHeadersByNumber(uint64(from), count, skip, false) - + headers, err := d.fetchHeadersByNumber(p, uint64(from), count, skip, false) + if err != nil { + return 0, err + } // Wait for the remote response to the head fetch number, hash := uint64(0), common.Hash{} - ttl := d.peers.rates.TargetTimeout() - timeout := time.After(ttl) - - for finished := false; !finished; { - select { - case <-d.cancelCh: - return 0, errCanceled - - case packet := <-d.headerCh: - // Discard anything not from the origin peer - if packet.PeerId() != p.id { - log.Debug("Received headers from incorrect peer", "peer", packet.PeerId()) - break - } - // Make sure the peer actually gave something valid - headers := packet.(*headerPack).headers - if len(headers) == 0 { - p.log.Warn("Empty head header set") - return 0, errEmptyHeaderSet - } - // Make sure the peer's reply conforms to the request - for i, header := range headers { - expectNumber := from + int64(i)*int64(skip+1) - if number := header.Number.Int64(); number != expectNumber { - p.log.Warn("Head headers broke chain ordering", "index", i, "requested", expectNumber, "received", number) - return 0, fmt.Errorf("%w: %v", errInvalidChain, errors.New("head headers broke chain ordering")) - } - } - // Check if a common ancestor was found - finished = true - for i := len(headers) - 1; i >= 0; i-- { - // Skip any headers that underflow/overflow our requested set - if headers[i].Number.Int64() < from || headers[i].Number.Uint64() > max { - continue - } - // Otherwise check if we already know the header or not - h := headers[i].Hash() - n := headers[i].Number.Uint64() - - var known bool - switch mode { - case FullSync: - known = d.blockchain.HasBlock(h, n) - case FastSync: - known = d.blockchain.HasFastBlock(h, n) - default: - known = d.lightchain.HasHeader(h, n) - } - if known { - number, hash = n, h - break - } - } - - case <-timeout: - p.log.Debug("Waiting for head header timed out", "elapsed", ttl) - return 0, errTimeout - - case <-d.bodyCh: - case <-d.receiptCh: - // Out of bounds delivery, ignore + // Make sure the peer actually gave something valid + if len(headers) == 0 { + p.log.Warn("Empty head header set") + return 0, errEmptyHeaderSet + } + // Make sure the peer's reply conforms to the request + for i, header := range headers { + expectNumber := from + int64(i)*int64(skip+1) + if number := header.Number.Int64(); number != expectNumber { + p.log.Warn("Head headers broke chain ordering", "index", i, "requested", expectNumber, "received", number) + return 0, fmt.Errorf("%w: %v", errInvalidChain, errors.New("head headers broke chain ordering")) + } + } + // Check if a common ancestor was found + for i := len(headers) - 1; i >= 0; i-- { + // Skip any headers that underflow/overflow our requested set + if headers[i].Number.Int64() < from || headers[i].Number.Uint64() > max { + continue + } + // Otherwise check if we already know the header or not + h := headers[i].Hash() + n := headers[i].Number.Uint64() + + var known bool + switch mode { + case FullSync: + known = d.blockchain.HasBlock(h, n) + case SnapSync: + known = d.blockchain.HasFastBlock(h, n) + default: + known = d.lightchain.HasHeader(h, n) + } + if known { + number, hash = n, h + break } } // If the head fetch already found an ancestor, return @@ -906,7 +877,7 @@ func (d *Downloader) findAncestorSpanSearch(p *peerConnection, mode SyncMode, re return 0, errNoAncestorFound } -func (d *Downloader) findAncestorBinarySearch(p *peerConnection, mode SyncMode, remoteHeight uint64, floor int64) (commonAncestor uint64, err error) { +func (d *Downloader) findAncestorBinarySearch(p *peerConnection, mode SyncMode, remoteHeight uint64, floor int64) (uint64, error) { hash := common.Hash{} // Ancestor not found, we need to binary search over our chain @@ -920,65 +891,39 @@ func (d *Downloader) findAncestorBinarySearch(p *peerConnection, mode SyncMode, // Split our chain interval in two, and request the hash to cross check check := (start + end) / 2 - ttl := d.peers.rates.TargetTimeout() - timeout := time.After(ttl) - - go p.peer.RequestHeadersByNumber(check, 1, 0, false) - - // Wait until a reply arrives to this request - for arrived := false; !arrived; { - select { - case <-d.cancelCh: - return 0, errCanceled - - case packet := <-d.headerCh: - // Discard anything not from the origin peer - if packet.PeerId() != p.id { - log.Debug("Received headers from incorrect peer", "peer", packet.PeerId()) - break - } - // Make sure the peer actually gave something valid - headers := packet.(*headerPack).headers - if len(headers) != 1 { - p.log.Warn("Multiple headers for single request", "headers", len(headers)) - return 0, fmt.Errorf("%w: multiple headers (%d) for single request", errBadPeer, len(headers)) - } - arrived = true - - // Modify the search interval based on the response - h := headers[0].Hash() - n := headers[0].Number.Uint64() - - var known bool - switch mode { - case FullSync: - known = d.blockchain.HasBlock(h, n) - case FastSync: - known = d.blockchain.HasFastBlock(h, n) - default: - known = d.lightchain.HasHeader(h, n) - } - if !known { - end = check - break - } - header := d.lightchain.GetHeaderByHash(h) // Independent of sync mode, header surely exists - if header.Number.Uint64() != check { - p.log.Warn("Received non requested header", "number", header.Number, "hash", header.Hash(), "request", check) - return 0, fmt.Errorf("%w: non-requested header (%d)", errBadPeer, header.Number) - } - start = check - hash = h - - case <-timeout: - p.log.Debug("Waiting for search header timed out", "elapsed", ttl) - return 0, errTimeout - - case <-d.bodyCh: - case <-d.receiptCh: - // Out of bounds delivery, ignore - } + headers, err := d.fetchHeadersByNumber(p, check, 1, 0, false) + if err != nil { + return 0, err + } + // Make sure the peer actually gave something valid + if len(headers) != 1 { + p.log.Warn("Multiple headers for single request", "headers", len(headers)) + return 0, fmt.Errorf("%w: multiple headers (%d) for single request", errBadPeer, len(headers)) + } + // Modify the search interval based on the response + h := headers[0].Hash() + n := headers[0].Number.Uint64() + + var known bool + switch mode { + case FullSync: + known = d.blockchain.HasBlock(h, n) + case SnapSync: + known = d.blockchain.HasFastBlock(h, n) + default: + known = d.lightchain.HasHeader(h, n) } + if !known { + end = check + continue + } + header := d.lightchain.GetHeaderByHash(h) // Independent of sync mode, header surely exists + if header.Number.Uint64() != check { + p.log.Warn("Received non requested header", "number", header.Number, "hash", header.Hash(), "request", check) + return 0, fmt.Errorf("%w: non-requested header (%d)", errBadPeer, header.Number) + } + start = check + hash = h } // Ensure valid ancestry and return if int64(start) <= floor { @@ -1001,223 +946,191 @@ func (d *Downloader) fetchHeaders(p *peerConnection, from uint64) error { p.log.Debug("Directing header downloads", "origin", from) defer p.log.Debug("Header download terminated") - // Create a timeout timer, and the associated header fetcher - skeleton := true // Skeleton assembly phase or finishing up - pivoting := false // Whether the next request is pivot verification - request := time.Now() // time of the last skeleton fetch request - timeout := time.NewTimer(0) // timer to dump a non-responsive active peer - <-timeout.C // timeout channel should be initially empty - defer timeout.Stop() - - var ttl time.Duration - getHeaders := func(from uint64) { - request = time.Now() + // Start pulling the header chain skeleton until all is done + var ( + skeleton = true // Skeleton assembly phase or finishing up + pivoting = false // Whether the next request is pivot verification + ancestor = from + mode = d.getMode() + ) + for { + // Pull the next batch of headers, it either: + // - Pivot check to see if the chain moved too far + // - Skeleton retrieval to permit concurrent header fetches + // - Full header retrieval if we're near the chain head + var ( + headers []*types.Header + err error + ) + switch { + case pivoting: + d.pivotLock.RLock() + pivot := d.pivotHeader.Number.Uint64() + d.pivotLock.RUnlock() - ttl = d.peers.rates.TargetTimeout() - timeout.Reset(ttl) + p.log.Trace("Fetching next pivot header", "number", pivot+uint64(fsMinFullBlocks)) + headers, err = d.fetchHeadersByNumber(p, pivot+uint64(fsMinFullBlocks), 2, fsMinFullBlocks-9, false) // move +64 when it's 2x64-8 deep - if skeleton { + case skeleton: p.log.Trace("Fetching skeleton headers", "count", MaxHeaderFetch, "from", from) - go p.peer.RequestHeadersByNumber(from+uint64(MaxHeaderFetch)-1, MaxSkeletonSize, MaxHeaderFetch-1, false) - } else { + headers, err = d.fetchHeadersByNumber(p, from+uint64(MaxHeaderFetch)-1, MaxSkeletonSize, MaxHeaderFetch-1, false) + + default: p.log.Trace("Fetching full headers", "count", MaxHeaderFetch, "from", from) - go p.peer.RequestHeadersByNumber(from, MaxHeaderFetch, 0, false) + headers, err = d.fetchHeadersByNumber(p, from, MaxHeaderFetch, 0, false) } - } - getNextPivot := func() { - pivoting = true - request = time.Now() - - ttl = d.peers.rates.TargetTimeout() - timeout.Reset(ttl) - - d.pivotLock.RLock() - pivot := d.pivotHeader.Number.Uint64() - d.pivotLock.RUnlock() + switch err { + case nil: + // Headers retrieved, continue with processing - p.log.Trace("Fetching next pivot header", "number", pivot+uint64(fsMinFullBlocks)) - go p.peer.RequestHeadersByNumber(pivot+uint64(fsMinFullBlocks), 2, fsMinFullBlocks-9, false) // move +64 when it's 2x64-8 deep - } - // Start pulling the header chain skeleton until all is done - ancestor := from - getHeaders(from) + case errCanceled: + // Sync cancelled, no issue, propagate up + return err - mode := d.getMode() - for { - select { - case <-d.cancelCh: - return errCanceled + default: + // Header retrieval either timed out, or the peer failed in some strange way + // (e.g. disconnect). Consider the master peer bad and drop + d.dropPeer(p.id) - case packet := <-d.headerCh: - // Make sure the active peer is giving us the skeleton headers - if packet.PeerId() != p.id { - log.Debug("Received skeleton from incorrect peer", "peer", packet.PeerId()) - break + // Finish the sync gracefully instead of dumping the gathered data though + for _, ch := range []chan bool{d.queue.blockWakeCh, d.queue.receiptWakeCh} { + select { + case ch <- false: + case <-d.cancelCh: + } } - headerReqTimer.UpdateSince(request) - timeout.Stop() - - // If the pivot is being checked, move if it became stale and run the real retrieval - var pivot uint64 - - d.pivotLock.RLock() - if d.pivotHeader != nil { - pivot = d.pivotHeader.Number.Uint64() + select { + case d.headerProcCh <- nil: + case <-d.cancelCh: } - d.pivotLock.RUnlock() + return fmt.Errorf("%w: header request failed: %v", errBadPeer, err) + } + // If the pivot is being checked, move if it became stale and run the real retrieval + var pivot uint64 - if pivoting { - if packet.Items() == 2 { - // Retrieve the headers and do some sanity checks, just in case - headers := packet.(*headerPack).headers + d.pivotLock.RLock() + if d.pivotHeader != nil { + pivot = d.pivotHeader.Number.Uint64() + } + d.pivotLock.RUnlock() - if have, want := headers[0].Number.Uint64(), pivot+uint64(fsMinFullBlocks); have != want { - log.Warn("Peer sent invalid next pivot", "have", have, "want", want) - return fmt.Errorf("%w: next pivot number %d != requested %d", errInvalidChain, have, want) - } - if have, want := headers[1].Number.Uint64(), pivot+2*uint64(fsMinFullBlocks)-8; have != want { - log.Warn("Peer sent invalid pivot confirmer", "have", have, "want", want) - return fmt.Errorf("%w: next pivot confirmer number %d != requested %d", errInvalidChain, have, want) - } - log.Warn("Pivot seemingly stale, moving", "old", pivot, "new", headers[0].Number) - pivot = headers[0].Number.Uint64() + if pivoting { + if len(headers) == 2 { + if have, want := headers[0].Number.Uint64(), pivot+uint64(fsMinFullBlocks); have != want { + log.Warn("Peer sent invalid next pivot", "have", have, "want", want) + return fmt.Errorf("%w: next pivot number %d != requested %d", errInvalidChain, have, want) + } + if have, want := headers[1].Number.Uint64(), pivot+2*uint64(fsMinFullBlocks)-8; have != want { + log.Warn("Peer sent invalid pivot confirmer", "have", have, "want", want) + return fmt.Errorf("%w: next pivot confirmer number %d != requested %d", errInvalidChain, have, want) + } + log.Warn("Pivot seemingly stale, moving", "old", pivot, "new", headers[0].Number) + pivot = headers[0].Number.Uint64() - d.pivotLock.Lock() - d.pivotHeader = headers[0] - d.pivotLock.Unlock() + d.pivotLock.Lock() + d.pivotHeader = headers[0] + d.pivotLock.Unlock() - // Write out the pivot into the database so a rollback beyond - // it will reenable fast sync and update the state root that - // the state syncer will be downloading. - rawdb.WriteLastPivotNumber(d.stateDB, pivot) - } - pivoting = false - getHeaders(from) - continue + // Write out the pivot into the database so a rollback beyond + // it will reenable fast sync and update the state root that + // the state syncer will be downloading. + rawdb.WriteLastPivotNumber(d.stateDB, pivot) } - // If the skeleton's finished, pull any remaining head headers directly from the origin - if skeleton && packet.Items() == 0 { - skeleton = false - getHeaders(from) - continue - } - // If no more headers are inbound, notify the content fetchers and return - if packet.Items() == 0 { - // Don't abort header fetches while the pivot is downloading - if atomic.LoadInt32(&d.committed) == 0 && pivot <= from { - p.log.Debug("No headers, waiting for pivot commit") - select { - case <-time.After(fsHeaderContCheck): - getHeaders(from) - continue - case <-d.cancelCh: - return errCanceled - } - } - // Pivot done (or not in fast sync) and no more headers, terminate the process - p.log.Debug("No more headers available") + // Disable the pivot check and fetch the next batch of headers + pivoting = false + continue + } + // If the skeleton's finished, pull any remaining head headers directly from the origin + if skeleton && len(headers) == 0 { + skeleton = false + continue + } + // If no more headers are inbound, notify the content fetchers and return + if len(headers) == 0 { + // Don't abort header fetches while the pivot is downloading + if atomic.LoadInt32(&d.committed) == 0 && pivot <= from { + p.log.Debug("No headers, waiting for pivot commit") select { - case d.headerProcCh <- nil: - return nil + case <-time.After(fsHeaderContCheck): + continue case <-d.cancelCh: return errCanceled } } - headers := packet.(*headerPack).headers - - // If we received a skeleton batch, resolve internals concurrently - if skeleton { - filled, proced, err := d.fillHeaderSkeleton(from, headers) - if err != nil { - p.log.Debug("Skeleton chain invalid", "err", err) - return fmt.Errorf("%w: %v", errInvalidChain, err) - } - headers = filled[proced:] - from += uint64(proced) - } else { - // If we're closing in on the chain head, but haven't yet reached it, delay - // the last few headers so mini reorgs on the head don't cause invalid hash - // chain errors. - if n := len(headers); n > 0 { - // Retrieve the current head we're at - var head uint64 - if mode == LightSync { - head = d.lightchain.CurrentHeader().Number.Uint64() - } else { - head = d.blockchain.CurrentFastBlock().NumberU64() - if full := d.blockchain.CurrentBlock().NumberU64(); head < full { - head = full - } - } - // If the head is below the common ancestor, we're actually deduplicating - // already existing chain segments, so use the ancestor as the fake head. - // Otherwise we might end up delaying header deliveries pointlessly. - if head < ancestor { - head = ancestor - } - // If the head is way older than this batch, delay the last few headers - if head+uint64(reorgProtThreshold) < headers[n-1].Number.Uint64() { - delay := reorgProtHeaderDelay - if delay > n { - delay = n - } - headers = headers[:n-delay] - } - } + // Pivot done (or not in fast sync) and no more headers, terminate the process + p.log.Debug("No more headers available") + select { + case d.headerProcCh <- nil: + return nil + case <-d.cancelCh: + return errCanceled } - // Insert all the new headers and fetch the next batch - if len(headers) > 0 { - p.log.Trace("Scheduling new headers", "count", len(headers), "from", from) - select { - case d.headerProcCh <- headers: - case <-d.cancelCh: - return errCanceled - } - from += uint64(len(headers)) - - // If we're still skeleton filling fast sync, check pivot staleness - // before continuing to the next skeleton filling - if skeleton && pivot > 0 { - getNextPivot() + } + // If we received a skeleton batch, resolve internals concurrently + if skeleton { + filled, proced, err := d.fillHeaderSkeleton(from, headers) + if err != nil { + p.log.Debug("Skeleton chain invalid", "err", err) + return fmt.Errorf("%w: %v", errInvalidChain, err) + } + headers = filled[proced:] + from += uint64(proced) + } else { + // If we're closing in on the chain head, but haven't yet reached it, delay + // the last few headers so mini reorgs on the head don't cause invalid hash + // chain errors. + if n := len(headers); n > 0 { + // Retrieve the current head we're at + var head uint64 + if mode == LightSync { + head = d.lightchain.CurrentHeader().Number.Uint64() } else { - getHeaders(from) + head = d.blockchain.CurrentFastBlock().NumberU64() + if full := d.blockchain.CurrentBlock().NumberU64(); head < full { + head = full + } } - } else { - // No headers delivered, or all of them being delayed, sleep a bit and retry - p.log.Trace("All headers delayed, waiting") - select { - case <-time.After(fsHeaderContCheck): - getHeaders(from) - continue - case <-d.cancelCh: - return errCanceled + // If the head is below the common ancestor, we're actually deduplicating + // already existing chain segments, so use the ancestor as the fake head. + // Otherwise, we might end up delaying header deliveries pointlessly. + if head < ancestor { + head = ancestor + } + // If the head is way older than this batch, delay the last few headers + if head+uint64(reorgProtThreshold) < headers[n-1].Number.Uint64() { + delay := reorgProtHeaderDelay + if delay > n { + delay = n + } + headers = headers[:n-delay] } } - - case <-timeout.C: - if d.dropPeer == nil { - // The dropPeer method is nil when `--copydb` is used for a local copy. - // Timeouts can occur if e.g. compaction hits at the wrong time, and can be ignored - p.log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", p.id) - break + } + // Insert all the new headers and fetch the next batch + if len(headers) > 0 { + p.log.Trace("Scheduling new headers", "count", len(headers), "from", from) + select { + case d.headerProcCh <- headers: + case <-d.cancelCh: + return errCanceled } - // Header retrieval timed out, consider the peer bad and drop - p.log.Debug("Header request timed out", "elapsed", ttl) - headerTimeoutMeter.Mark(1) - d.dropPeer(p.id) + from += uint64(len(headers)) - // Finish the sync gracefully instead of dumping the gathered data though - for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh} { - select { - case ch <- false: - case <-d.cancelCh: - } + // If we're still skeleton filling fast sync, check pivot staleness + // before continuing to the next skeleton filling + if skeleton && pivot > 0 { + pivoting = true } + continue + } else { + // No headers delivered, or all of them being delayed, sleep a bit and retry + p.log.Trace("All headers delayed, waiting") select { - case d.headerProcCh <- nil: + case <-time.After(fsHeaderContCheck): + continue case <-d.cancelCh: + return errCanceled } - return fmt.Errorf("%w: header request timed out", errBadPeer) } } } @@ -1235,25 +1148,7 @@ func (d *Downloader) fillHeaderSkeleton(from uint64, skeleton []*types.Header) ( log.Debug("Filling up skeleton", "from", from) d.queue.ScheduleSkeleton(from, skeleton) - var ( - deliver = func(packet dataPack) (int, error) { - pack := packet.(*headerPack) - return d.queue.DeliverHeaders(pack.peerID, pack.headers, d.headerProcCh) - } - expire = func() map[string]int { return d.queue.ExpireHeaders(d.peers.rates.TargetTimeout()) } - reserve = func(p *peerConnection, count int) (*fetchRequest, bool, bool) { - return d.queue.ReserveHeaders(p, count), false, false - } - fetch = func(p *peerConnection, req *fetchRequest) error { return p.FetchHeaders(req.From, MaxHeaderFetch) } - capacity = func(p *peerConnection) int { return p.HeaderCapacity(d.peers.rates.TargetRoundTrip()) } - setIdle = func(p *peerConnection, accepted int, deliveryTime time.Time) { - p.SetHeadersIdle(accepted, deliveryTime) - } - ) - err := d.fetchParts(d.headerCh, deliver, d.queue.headerContCh, expire, - d.queue.PendingHeaders, d.queue.InFlightHeaders, reserve, - nil, fetch, d.queue.CancelHeaders, capacity, d.peers.HeaderIdlePeers, setIdle, "headers") - + err := d.concurrentFetch((*headerQueue)(d), false) log.Debug("Skeleton fill terminated", "err", err) filled, proced := d.queue.RetrieveHeaders() @@ -1263,22 +1158,9 @@ func (d *Downloader) fillHeaderSkeleton(from uint64, skeleton []*types.Header) ( // fetchBodies iteratively downloads the scheduled block bodies, taking any // available peers, reserving a chunk of blocks for each, waiting for delivery // and also periodically checking for timeouts. -func (d *Downloader) fetchBodies(from uint64) error { +func (d *Downloader) fetchBodies(from uint64, beaconMode bool) error { log.Debug("Downloading block bodies", "origin", from) - - var ( - deliver = func(packet dataPack) (int, error) { - pack := packet.(*bodyPack) - return d.queue.DeliverBodies(pack.peerID, pack.transactions, pack.uncles) - } - expire = func() map[string]int { return d.queue.ExpireBodies(d.peers.rates.TargetTimeout()) } - fetch = func(p *peerConnection, req *fetchRequest) error { return p.FetchBodies(req) } - capacity = func(p *peerConnection) int { return p.BlockCapacity(d.peers.rates.TargetRoundTrip()) } - setIdle = func(p *peerConnection, accepted int, deliveryTime time.Time) { p.SetBodiesIdle(accepted, deliveryTime) } - ) - err := d.fetchParts(d.bodyCh, deliver, d.bodyWakeCh, expire, - d.queue.PendingBlocks, d.queue.InFlightBlocks, d.queue.ReserveBodies, - d.bodyFetchHook, fetch, d.queue.CancelBodies, capacity, d.peers.BodyIdlePeers, setIdle, "bodies") + err := d.concurrentFetch((*bodyQueue)(d), beaconMode) log.Debug("Block body download terminated", "err", err) return err @@ -1287,230 +1169,18 @@ func (d *Downloader) fetchBodies(from uint64) error { // fetchReceipts iteratively downloads the scheduled block receipts, taking any // available peers, reserving a chunk of receipts for each, waiting for delivery // and also periodically checking for timeouts. -func (d *Downloader) fetchReceipts(from uint64) error { - log.Debug("Downloading transaction receipts", "origin", from) +func (d *Downloader) fetchReceipts(from uint64, beaconMode bool) error { + log.Debug("Downloading receipts", "origin", from) + err := d.concurrentFetch((*receiptQueue)(d), beaconMode) - var ( - deliver = func(packet dataPack) (int, error) { - pack := packet.(*receiptPack) - return d.queue.DeliverReceipts(pack.peerID, pack.receipts) - } - expire = func() map[string]int { return d.queue.ExpireReceipts(d.peers.rates.TargetTimeout()) } - fetch = func(p *peerConnection, req *fetchRequest) error { return p.FetchReceipts(req) } - capacity = func(p *peerConnection) int { return p.ReceiptCapacity(d.peers.rates.TargetRoundTrip()) } - setIdle = func(p *peerConnection, accepted int, deliveryTime time.Time) { - p.SetReceiptsIdle(accepted, deliveryTime) - } - ) - err := d.fetchParts(d.receiptCh, deliver, d.receiptWakeCh, expire, - d.queue.PendingReceipts, d.queue.InFlightReceipts, d.queue.ReserveReceipts, - d.receiptFetchHook, fetch, d.queue.CancelReceipts, capacity, d.peers.ReceiptIdlePeers, setIdle, "receipts") - - log.Debug("Transaction receipt download terminated", "err", err) + log.Debug("Receipt download terminated", "err", err) return err } -// fetchParts iteratively downloads scheduled block parts, taking any available -// peers, reserving a chunk of fetch requests for each, waiting for delivery and -// also periodically checking for timeouts. -// -// As the scheduling/timeout logic mostly is the same for all downloaded data -// types, this method is used by each for data gathering and is instrumented with -// various callbacks to handle the slight differences between processing them. -// -// The instrumentation parameters: -// - errCancel: error type to return if the fetch operation is cancelled (mostly makes logging nicer) -// - deliveryCh: channel from which to retrieve downloaded data packets (merged from all concurrent peers) -// - deliver: processing callback to deliver data packets into type specific download queues (usually within `queue`) -// - wakeCh: notification channel for waking the fetcher when new tasks are available (or sync completed) -// - expire: task callback method to abort requests that took too long and return the faulty peers (traffic shaping) -// - pending: task callback for the number of requests still needing download (detect completion/non-completability) -// - inFlight: task callback for the number of in-progress requests (wait for all active downloads to finish) -// - throttle: task callback to check if the processing queue is full and activate throttling (bound memory use) -// - reserve: task callback to reserve new download tasks to a particular peer (also signals partial completions) -// - fetchHook: tester callback to notify of new tasks being initiated (allows testing the scheduling logic) -// - fetch: network callback to actually send a particular download request to a physical remote peer -// - cancel: task callback to abort an in-flight download request and allow rescheduling it (in case of lost peer) -// - capacity: network callback to retrieve the estimated type-specific bandwidth capacity of a peer (traffic shaping) -// - idle: network callback to retrieve the currently (type specific) idle peers that can be assigned tasks -// - setIdle: network callback to set a peer back to idle and update its estimated capacity (traffic shaping) -// - kind: textual label of the type being downloaded to display in log messages -func (d *Downloader) fetchParts(deliveryCh chan dataPack, deliver func(dataPack) (int, error), wakeCh chan bool, - expire func() map[string]int, pending func() int, inFlight func() bool, reserve func(*peerConnection, int) (*fetchRequest, bool, bool), - fetchHook func([]*types.Header), fetch func(*peerConnection, *fetchRequest) error, cancel func(*fetchRequest), capacity func(*peerConnection) int, - idle func() ([]*peerConnection, int), setIdle func(*peerConnection, int, time.Time), kind string) error { - - // Create a ticker to detect expired retrieval tasks - ticker := time.NewTicker(100 * time.Millisecond) - defer ticker.Stop() - - update := make(chan struct{}, 1) - - // Prepare the queue and fetch block parts until the block header fetcher's done - finished := false - for { - select { - case <-d.cancelCh: - return errCanceled - - case packet := <-deliveryCh: - deliveryTime := time.Now() - // If the peer was previously banned and failed to deliver its pack - // in a reasonable time frame, ignore its message. - if peer := d.peers.Peer(packet.PeerId()); peer != nil { - // Deliver the received chunk of data and check chain validity - accepted, err := deliver(packet) - if errors.Is(err, errInvalidChain) { - return err - } - // Unless a peer delivered something completely else than requested (usually - // caused by a timed out request which came through in the end), set it to - // idle. If the delivery's stale, the peer should have already been idled. - if !errors.Is(err, errStaleDelivery) { - setIdle(peer, accepted, deliveryTime) - } - // Issue a log to the user to see what's going on - switch { - case err == nil && packet.Items() == 0: - peer.log.Trace("Requested data not delivered", "type", kind) - case err == nil: - peer.log.Trace("Delivered new batch of data", "type", kind, "count", packet.Stats()) - default: - peer.log.Debug("Failed to deliver retrieved data", "type", kind, "err", err) - } - } - // Blocks assembled, try to update the progress - select { - case update <- struct{}{}: - default: - } - - case cont := <-wakeCh: - // The header fetcher sent a continuation flag, check if it's done - if !cont { - finished = true - } - // Headers arrive, try to update the progress - select { - case update <- struct{}{}: - default: - } - - case <-ticker.C: - // Sanity check update the progress - select { - case update <- struct{}{}: - default: - } - - case <-update: - // Short circuit if we lost all our peers - if d.peers.Len() == 0 { - return errNoPeers - } - // Check for fetch request timeouts and demote the responsible peers - for pid, fails := range expire() { - if peer := d.peers.Peer(pid); peer != nil { - // If a lot of retrieval elements expired, we might have overestimated the remote peer or perhaps - // ourselves. Only reset to minimal throughput but don't drop just yet. If even the minimal times - // out that sync wise we need to get rid of the peer. - // - // The reason the minimum threshold is 2 is because the downloader tries to estimate the bandwidth - // and latency of a peer separately, which requires pushing the measures capacity a bit and seeing - // how response times reacts, to it always requests one more than the minimum (i.e. min 2). - if fails > 2 { - peer.log.Trace("Data delivery timed out", "type", kind) - setIdle(peer, 0, time.Now()) - } else { - peer.log.Debug("Stalling delivery, dropping", "type", kind) - - if d.dropPeer == nil { - // The dropPeer method is nil when `--copydb` is used for a local copy. - // Timeouts can occur if e.g. compaction hits at the wrong time, and can be ignored - peer.log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", pid) - } else { - d.dropPeer(pid) - - // If this peer was the master peer, abort sync immediately - d.cancelLock.RLock() - master := pid == d.cancelPeer - d.cancelLock.RUnlock() - - if master { - d.cancel() - return errTimeout - } - } - } - } - } - // If there's nothing more to fetch, wait or terminate - if pending() == 0 { - if !inFlight() && finished { - log.Debug("Data fetching completed", "type", kind) - return nil - } - break - } - // Send a download request to all idle peers, until throttled - progressed, throttled, running := false, false, inFlight() - idles, total := idle() - pendCount := pending() - for _, peer := range idles { - // Short circuit if throttling activated - if throttled { - break - } - // Short circuit if there is no more available task. - if pendCount = pending(); pendCount == 0 { - break - } - // Reserve a chunk of fetches for a peer. A nil can mean either that - // no more headers are available, or that the peer is known not to - // have them. - request, progress, throttle := reserve(peer, capacity(peer)) - if progress { - progressed = true - } - if throttle { - throttled = true - throttleCounter.Inc(1) - } - if request == nil { - continue - } - if request.From > 0 { - peer.log.Trace("Requesting new batch of data", "type", kind, "from", request.From) - } else { - peer.log.Trace("Requesting new batch of data", "type", kind, "count", len(request.Headers), "from", request.Headers[0].Number) - } - // Fetch the chunk and make sure any errors return the hashes to the queue - if fetchHook != nil { - fetchHook(request.Headers) - } - if err := fetch(peer, request); err != nil { - // Although we could try and make an attempt to fix this, this error really - // means that we've double allocated a fetch task to a peer. If that is the - // case, the internal state of the downloader and the queue is very wrong so - // better hard crash and note the error instead of silently accumulating into - // a much bigger issue. - panic(fmt.Sprintf("%v: %s fetch assignment failed", peer, kind)) - } - running = true - } - // Make sure that we have peers available for fetching. If all peers have been tried - // and all failed throw an error - if !progressed && !throttled && !running && len(idles) == total && pendCount > 0 { - return errPeersUnavailable - } - } - } -} - // processHeaders takes batches of retrieved headers from an input channel and // keeps processing and scheduling them into the header chain and downloader's // queue until the stream ends or a failure occurs. -func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { +func (d *Downloader) processHeaders(origin uint64, td *big.Int, beaconMode bool) error { // Keep a count of uncertain headers to roll back var ( rollback uint64 // Zero means no rollback (fine as you can't unroll the genesis) @@ -1552,41 +1222,46 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { // Terminate header processing if we synced up if len(headers) == 0 { // Notify everyone that headers are fully processed - for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh} { + for _, ch := range []chan bool{d.queue.blockWakeCh, d.queue.receiptWakeCh} { select { case ch <- false: case <-d.cancelCh: } } - // If no headers were retrieved at all, the peer violated its TD promise that it had a - // better chain compared to ours. The only exception is if its promised blocks were - // already imported by other means (e.g. fetcher): - // - // R , L : Both at block 10 - // R: Mine block 11, and propagate it to L - // L: Queue block 11 for import - // L: Notice that R's head and TD increased compared to ours, start sync - // L: Import of block 11 finishes - // L: Sync begins, and finds common ancestor at 11 - // L: Request new headers up from 11 (R's TD was higher, it must have something) - // R: Nothing to give - if mode != LightSync { - head := d.blockchain.CurrentBlock() - if !gotHeaders && td.Cmp(d.blockchain.GetTd(head.Hash(), head.NumberU64())) > 0 { - return errStallingPeer + // If we're in legacy sync mode, we need to check total difficulty + // violations from malicious peers. That is not needed in beacon + // mode and we can skip to terminating sync. + if !beaconMode { + // If no headers were retrieved at all, the peer violated its TD promise that it had a + // better chain compared to ours. The only exception is if its promised blocks were + // already imported by other means (e.g. fetcher): + // + // R , L : Both at block 10 + // R: Mine block 11, and propagate it to L + // L: Queue block 11 for import + // L: Notice that R's head and TD increased compared to ours, start sync + // L: Import of block 11 finishes + // L: Sync begins, and finds common ancestor at 11 + // L: Request new headers up from 11 (R's TD was higher, it must have something) + // R: Nothing to give + if mode != LightSync { + head := d.blockchain.CurrentBlock() + if !gotHeaders && td.Cmp(d.blockchain.GetTd(head.Hash(), head.NumberU64())) > 0 { + return errStallingPeer + } } - } - // If fast or light syncing, ensure promised headers are indeed delivered. This is - // needed to detect scenarios where an attacker feeds a bad pivot and then bails out - // of delivering the post-pivot blocks that would flag the invalid content. - // - // This check cannot be executed "as is" for full imports, since blocks may still be - // queued for processing when the header download completes. However, as long as the - // peer gave us something useful, we're already happy/progressed (above check). - if mode == FastSync || mode == LightSync { - head := d.lightchain.CurrentHeader() - if td.Cmp(d.lightchain.GetTd(head.Hash(), head.Number.Uint64())) > 0 { - return errStallingPeer + // If fast or light syncing, ensure promised headers are indeed delivered. This is + // needed to detect scenarios where an attacker feeds a bad pivot and then bails out + // of delivering the post-pivot blocks that would flag the invalid content. + // + // This check cannot be executed "as is" for full imports, since blocks may still be + // queued for processing when the header download completes. However, as long as the + // peer gave us something useful, we're already happy/progressed (above check). + if mode == SnapSync || mode == LightSync { + head := d.lightchain.CurrentHeader() + if td.Cmp(d.lightchain.GetTd(head.Hash(), head.Number.Uint64())) > 0 { + return errStallingPeer + } } } // Disable any rollback and return @@ -1611,7 +1286,7 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { chunk := headers[:limit] // In case of header only syncing, validate the chunk immediately - if mode == FastSync || mode == LightSync { + if mode == SnapSync || mode == LightSync { // If we're importing pure headers, verify based on their recentness var pivot uint64 @@ -1621,6 +1296,8 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { } d.pivotLock.RUnlock() + fmt.Println(pivot) + frequency := fsHeaderCheckFrequency if chunk[len(chunk)-1].Number.Uint64()+uint64(fsHeaderForceVerify) > pivot { frequency = 1 @@ -1629,14 +1306,14 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { rollbackErr = err // If some headers were inserted, track them as uncertain - if (mode == FastSync || frequency > 1) && n > 0 && rollback == 0 { + if (mode == SnapSync || frequency > 1) && n > 0 && rollback == 0 { rollback = chunk[0].Number.Uint64() } log.Warn("Invalid header encountered", "number", chunk[n].Number, "hash", chunk[n].Hash(), "parent", chunk[n].ParentHash, "err", err) return fmt.Errorf("%w: %v", errInvalidChain, err) } // All verifications passed, track all headers within the alloted limits - if mode == FastSync { + if mode == SnapSync { head := chunk[len(chunk)-1].Number.Uint64() if head-rollback > uint64(fsHeaderSafetyNet) { rollback = head - uint64(fsHeaderSafetyNet) @@ -1646,9 +1323,9 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { } } // Unless we're doing light chains, schedule the headers for associated content retrieval - if mode == FullSync || mode == FastSync { + if mode == FullSync || mode == SnapSync { // If we've reached the allowed number of pending headers, stall a bit - for d.queue.PendingBlocks() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders { + for d.queue.PendingBodies() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders { select { case <-d.cancelCh: rollbackErr = errCanceled @@ -1674,7 +1351,7 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { d.syncStatsLock.Unlock() // Signal the content downloaders of the availablility of new tasks - for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh} { + for _, ch := range []chan bool{d.queue.blockWakeCh, d.queue.receiptWakeCh} { select { case ch <- true: default: @@ -1942,27 +1619,6 @@ func (d *Downloader) commitPivotBlock(result *fetchResult) error { return nil } -// DeliverHeaders injects a new batch of block headers received from a remote -// node into the download schedule. -func (d *Downloader) DeliverHeaders(id string, headers []*types.Header) error { - return d.deliver(d.headerCh, &headerPack{id, headers}, headerInMeter, headerDropMeter) -} - -// DeliverBodies injects a new batch of block bodies received from a remote node. -func (d *Downloader) DeliverBodies(id string, transactions [][]*types.Transaction, uncles [][]*types.Header) error { - return d.deliver(d.bodyCh, &bodyPack{id, transactions, uncles}, bodyInMeter, bodyDropMeter) -} - -// DeliverReceipts injects a new batch of receipts received from a remote node. -func (d *Downloader) DeliverReceipts(id string, receipts [][]*types.Receipt) error { - return d.deliver(d.receiptCh, &receiptPack{id, receipts}, receiptInMeter, receiptDropMeter) -} - -// DeliverNodeData injects a new batch of node state data received from a remote node. -func (d *Downloader) DeliverNodeData(id string, data [][]byte) error { - return d.deliver(d.stateCh, &statePack{id, data}, stateInMeter, stateDropMeter) -} - // DeliverSnapPacket is invoked from a peer's message handler when it transmits a // data packet for the local node to consume. func (d *Downloader) DeliverSnapPacket(peer *snap.Peer, packet snap.Packet) error { @@ -1988,27 +1644,3 @@ func (d *Downloader) DeliverSnapPacket(peer *snap.Peer, packet snap.Packet) erro return fmt.Errorf("unexpected snap packet type: %T", packet) } } - -// deliver injects a new batch of data received from a remote node. -func (d *Downloader) deliver(destCh chan dataPack, packet dataPack, inMeter, dropMeter metrics.Meter) (err error) { - // Update the delivery metrics for both good and failed deliveries - inMeter.Mark(int64(packet.Items())) - defer func() { - if err != nil { - dropMeter.Mark(int64(packet.Items())) - } - }() - // Deliver or abort if the sync is canceled while queuing - d.cancelLock.RLock() - cancel := d.cancelCh - d.cancelLock.RUnlock() - if cancel == nil { - return errNoSyncActive - } - select { - case destCh <- packet: - return nil - case <-cancel: - return errNoSyncActive - } -} diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 17cd3630c98e..e5a4b4c2402e 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -89,7 +89,7 @@ func newTester() *downloadTester { tester.stateDb = rawdb.NewMemoryDatabase() tester.stateDb.Put(testGenesis.Root().Bytes(), []byte{0x00}) - tester.downloader = New(0, tester.stateDb, trie.NewSyncBloom(1, tester.stateDb), new(event.TypeMux), tester, nil, tester.dropPeer) + tester.downloader = New(0, tester.stateDb, trie.NewSyncBloom(1, tester.stateDb), new(event.TypeMux), tester, nil, tester.dropPeer, nil) return tester } @@ -110,7 +110,7 @@ func (dl *downloadTester) sync(id string, td *big.Int, mode SyncMode) error { dl.lock.RUnlock() // Synchronise with the chosen peer and ensure proper cleanup afterwards - err := dl.downloader.synchronise(id, hash, td, mode) + err := dl.downloader.synchronise(id, hash, td, mode, false) select { case <-dl.downloader.cancelCh: // Ok, downloader fully cancelled after sync cycle @@ -432,56 +432,92 @@ func (dlp *downloadTesterPeer) Head() (common.Hash, *big.Int) { // RequestHeadersByHash constructs a GetBlockHeaders function based on a hashed // origin; associated with a particular peer in the download tester. The returned // function can be used to retrieve batches of headers from the particular peer. -func (dlp *downloadTesterPeer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error { - result := dlp.chain.headersByHash(origin, amount, skip, reverse) - go dlp.dl.downloader.DeliverHeaders(dlp.id, result) - return nil +func (dlp *downloadTesterPeer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool, sink chan *eth.Response) (*eth.Request, error) { + headers := dlp.chain.headersByHash(origin, amount, skip, reverse) + + req := ð.Request{ + Peer: dlp.id, + } + res := ð.Response{ + Req: req, + Res: (*eth.BlockHeadersPacket)(&headers), + Time: 1, + Done: make(chan error, 1), // Ignore the returned status + } + go func() { + sink <- res + }() + return req, nil } // RequestHeadersByNumber constructs a GetBlockHeaders function based on a numbered // origin; associated with a particular peer in the download tester. The returned // function can be used to retrieve batches of headers from the particular peer. -func (dlp *downloadTesterPeer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error { - result := dlp.chain.headersByNumber(origin, amount, skip, reverse) - go dlp.dl.downloader.DeliverHeaders(dlp.id, result) - return nil +func (dlp *downloadTesterPeer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool, sink chan *eth.Response) (*eth.Request, error) { + headers := dlp.chain.headersByNumber(origin, amount, skip, reverse) + + req := ð.Request{ + Peer: dlp.id, + } + res := ð.Response{ + Req: req, + Res: (*eth.BlockHeadersPacket)(&headers), + Time: 1, + Done: make(chan error, 1), // Ignore the returned status + } + go func() { + sink <- res + }() + return req, nil } // RequestBodies constructs a getBlockBodies method associated with a particular // peer in the download tester. The returned function can be used to retrieve // batches of block bodies from the particularly requested peer. -func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash) error { - txs, uncles := dlp.chain.bodies(hashes) - go dlp.dl.downloader.DeliverBodies(dlp.id, txs, uncles) - return nil +func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) { + txset, uncleset := dlp.chain.bodies(hashes) + + bodies := make([]*eth.BlockBody, len(txset)) + for i, txs := range txset { + bodies[i] = ð.BlockBody{ + Transactions: txs, + Uncles: uncleset[i], + } + } + req := ð.Request{ + Peer: dlp.id, + } + res := ð.Response{ + Req: req, + Res: (*eth.BlockBodiesPacket)(&bodies), + Time: 1, + Done: make(chan error, 1), // Ignore the returned status + } + go func() { + sink <- res + }() + return req, nil } // RequestReceipts constructs a getReceipts method associated with a particular // peer in the download tester. The returned function can be used to retrieve // batches of block receipts from the particularly requested peer. -func (dlp *downloadTesterPeer) RequestReceipts(hashes []common.Hash) error { +func (dlp *downloadTesterPeer) RequestReceipts(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) { receipts := dlp.chain.receipts(hashes) - go dlp.dl.downloader.DeliverReceipts(dlp.id, receipts) - return nil -} -// RequestNodeData constructs a getNodeData method associated with a particular -// peer in the download tester. The returned function can be used to retrieve -// batches of node state data from the particularly requested peer. -func (dlp *downloadTesterPeer) RequestNodeData(hashes []common.Hash) error { - dlp.dl.lock.RLock() - defer dlp.dl.lock.RUnlock() - - results := make([][]byte, 0, len(hashes)) - for _, hash := range hashes { - if data, err := dlp.dl.peerDb.Get(hash.Bytes()); err == nil { - if !dlp.missingStates[hash] { - results = append(results, data) - } - } + req := ð.Request{ + Peer: dlp.id, } - go dlp.dl.downloader.DeliverNodeData(dlp.id, results) - return nil + res := ð.Response{ + Req: req, + Res: (*eth.ReceiptsPacket)(&receipts), + Time: 1, + Done: make(chan error, 1), // Ignore the returned status + } + go func() { + sink <- res + }() + return req, nil } // assertOwnChain checks if the local chain contains the correct number of items @@ -523,7 +559,7 @@ func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, leng } func TestCanonicalSynchronisation66Full(t *testing.T) { testCanonSync(t, eth.ETH66, FullSync) } -func TestCanonicalSynchronisation66Fast(t *testing.T) { testCanonSync(t, eth.ETH66, FastSync) } +func TestCanonicalSynchronisation66Fast(t *testing.T) { testCanonSync(t, eth.ETH66, SnapSync) } func TestCanonicalSynchronisation66Light(t *testing.T) { testCanonSync(t, eth.ETH66, LightSync) } func testCanonSync(t *testing.T, protocol uint, mode SyncMode) { @@ -546,7 +582,7 @@ func testCanonSync(t *testing.T, protocol uint, mode SyncMode) { // Tests that if a large batch of blocks are being downloaded, it is throttled // until the cached blocks are retrieved. func TestThrottling66Full(t *testing.T) { testThrottling(t, eth.ETH66, FullSync) } -func TestThrottling66Fast(t *testing.T) { testThrottling(t, eth.ETH66, FastSync) } +func TestThrottling66Fast(t *testing.T) { testThrottling(t, eth.ETH66, SnapSync) } func testThrottling(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -628,7 +664,7 @@ func testThrottling(t *testing.T, protocol uint, mode SyncMode) { // this test common ancestor lookup should *not* be short circuited, and a full // binary search should be executed. func TestForkedSync66Full(t *testing.T) { testForkedSync(t, eth.ETH66, FullSync) } -func TestForkedSync66Fast(t *testing.T) { testForkedSync(t, eth.ETH66, FastSync) } +func TestForkedSync66Fast(t *testing.T) { testForkedSync(t, eth.ETH66, SnapSync) } func TestForkedSync66Light(t *testing.T) { testForkedSync(t, eth.ETH66, LightSync) } func testForkedSync(t *testing.T, protocol uint, mode SyncMode) { @@ -657,7 +693,7 @@ func testForkedSync(t *testing.T, protocol uint, mode SyncMode) { // Tests that synchronising against a much shorter but much heavyer fork works // corrently and is not dropped. func TestHeavyForkedSync66Full(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, FullSync) } -func TestHeavyForkedSync66Fast(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, FastSync) } +func TestHeavyForkedSync66Fast(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, SnapSync) } func TestHeavyForkedSync66Light(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, LightSync) } func testHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) { @@ -688,7 +724,7 @@ func testHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) { // chain head, ensuring that malicious peers cannot waste resources by feeding // long dead chains. func TestBoundedForkedSync66Full(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, FullSync) } -func TestBoundedForkedSync66Fast(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, FastSync) } +func TestBoundedForkedSync66Fast(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, SnapSync) } func TestBoundedForkedSync66Light(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, LightSync) } func testBoundedForkedSync(t *testing.T, protocol uint, mode SyncMode) { @@ -721,7 +757,7 @@ func TestBoundedHeavyForkedSync66Full(t *testing.T) { testBoundedHeavyForkedSync(t, eth.ETH66, FullSync) } func TestBoundedHeavyForkedSync66Fast(t *testing.T) { - testBoundedHeavyForkedSync(t, eth.ETH66, FastSync) + testBoundedHeavyForkedSync(t, eth.ETH66, SnapSync) } func TestBoundedHeavyForkedSync66Light(t *testing.T) { testBoundedHeavyForkedSync(t, eth.ETH66, LightSync) @@ -750,29 +786,9 @@ func testBoundedHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) { tester.terminate() } -// Tests that an inactive downloader will not accept incoming block headers, -// bodies and receipts. -func TestInactiveDownloader63(t *testing.T) { - t.Parallel() - - tester := newTester() - defer tester.terminate() - - // Check that neither block headers nor bodies are accepted - if err := tester.downloader.DeliverHeaders("bad peer", []*types.Header{}); err != errNoSyncActive { - t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive) - } - if err := tester.downloader.DeliverBodies("bad peer", [][]*types.Transaction{}, [][]*types.Header{}); err != errNoSyncActive { - t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive) - } - if err := tester.downloader.DeliverReceipts("bad peer", [][]*types.Receipt{}); err != errNoSyncActive { - t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive) - } -} - // Tests that a canceled download wipes all previously accumulated state. func TestCancel66Full(t *testing.T) { testCancel(t, eth.ETH66, FullSync) } -func TestCancel66Fast(t *testing.T) { testCancel(t, eth.ETH66, FastSync) } +func TestCancel66Fast(t *testing.T) { testCancel(t, eth.ETH66, SnapSync) } func TestCancel66Light(t *testing.T) { testCancel(t, eth.ETH66, LightSync) } func testCancel(t *testing.T, protocol uint, mode SyncMode) { @@ -801,7 +817,7 @@ func testCancel(t *testing.T, protocol uint, mode SyncMode) { // Tests that synchronisation from multiple peers works as intended (multi thread sanity test). func TestMultiSynchronisation66Full(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, FullSync) } -func TestMultiSynchronisation66Fast(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, FastSync) } +func TestMultiSynchronisation66Fast(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, SnapSync) } func TestMultiSynchronisation66Light(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, LightSync) } func testMultiSynchronisation(t *testing.T, protocol uint, mode SyncMode) { @@ -827,7 +843,7 @@ func testMultiSynchronisation(t *testing.T, protocol uint, mode SyncMode) { // Tests that synchronisations behave well in multi-version protocol environments // and not wreak havoc on other nodes in the network. func TestMultiProtoSynchronisation66Full(t *testing.T) { testMultiProtoSync(t, eth.ETH66, FullSync) } -func TestMultiProtoSynchronisation66Fast(t *testing.T) { testMultiProtoSync(t, eth.ETH66, FastSync) } +func TestMultiProtoSynchronisation66Fast(t *testing.T) { testMultiProtoSync(t, eth.ETH66, SnapSync) } func TestMultiProtoSynchronisation66Light(t *testing.T) { testMultiProtoSync(t, eth.ETH66, LightSync) } func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) { @@ -861,7 +877,7 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) { // Tests that if a block is empty (e.g. header only), no body request should be // made, and instead the header should be assembled into a whole block in itself. func TestEmptyShortCircuit66Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, FullSync) } -func TestEmptyShortCircuit66Fast(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, FastSync) } +func TestEmptyShortCircuit66Fast(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, SnapSync) } func TestEmptyShortCircuit66Light(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, LightSync) } func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) { @@ -896,7 +912,7 @@ func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) { } } for _, receipt := range chain.receiptm { - if mode == FastSync && len(receipt) > 0 { + if mode == SnapSync && len(receipt) > 0 { receiptsNeeded++ } } @@ -911,12 +927,14 @@ func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) { // Tests that headers are enqueued continuously, preventing malicious nodes from // stalling the downloader by feeding gapped header chains. func TestMissingHeaderAttack66Full(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, FullSync) } -func TestMissingHeaderAttack66Fast(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, FastSync) } +func TestMissingHeaderAttack66Fast(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, SnapSync) } func TestMissingHeaderAttack66Light(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, LightSync) } func testMissingHeaderAttack(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() + //log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + tester := newTester() defer tester.terminate() @@ -939,7 +957,7 @@ func testMissingHeaderAttack(t *testing.T, protocol uint, mode SyncMode) { // Tests that if requested headers are shifted (i.e. first is missing), the queue // detects the invalid numbering. func TestShiftedHeaderAttack66Full(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, FullSync) } -func TestShiftedHeaderAttack66Fast(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, FastSync) } +func TestShiftedHeaderAttack66Fast(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, SnapSync) } func TestShiftedHeaderAttack66Light(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, LightSync) } func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) { @@ -971,7 +989,7 @@ func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) { // Tests that upon detecting an invalid header, the recent ones are rolled back // for various failure scenarios. Afterwards a full sync is attempted to make // sure no state was corrupted. -func TestInvalidHeaderRollback66Fast(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH66, FastSync) } +func TestInvalidHeaderRollback66Fast(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH66, SnapSync) } func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) { t.Parallel() @@ -1011,7 +1029,7 @@ func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) { if head := tester.CurrentHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch { t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch) } - if mode == FastSync { + if mode == SnapSync { if head := tester.CurrentBlock().NumberU64(); head != 0 { t.Errorf("fast sync pivot block #%d not rolled back", head) } @@ -1034,7 +1052,7 @@ func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) { if head := tester.CurrentHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch { t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch) } - if mode == FastSync { + if mode == SnapSync { if head := tester.CurrentBlock().NumberU64(); head != 0 { t.Errorf("fast sync pivot block #%d not rolled back", head) } @@ -1065,7 +1083,7 @@ func TestHighTDStarvationAttack66Full(t *testing.T) { testHighTDStarvationAttack(t, eth.ETH66, FullSync) } func TestHighTDStarvationAttack66Fast(t *testing.T) { - testHighTDStarvationAttack(t, eth.ETH66, FastSync) + testHighTDStarvationAttack(t, eth.ETH66, SnapSync) } func TestHighTDStarvationAttack66Light(t *testing.T) { testHighTDStarvationAttack(t, eth.ETH66, LightSync) @@ -1128,7 +1146,7 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) { // Simulate a synchronisation and check the required result tester.downloader.synchroniseMock = func(string, common.Hash) error { return tt.result } - tester.downloader.Synchronise(id, tester.genesis.Hash(), big.NewInt(1000), FullSync) + tester.downloader.LegacySync(id, tester.genesis.Hash(), big.NewInt(1000), FullSync) if _, ok := tester.peers[id]; !ok != tt.drop { t.Errorf("test %d: peer drop mismatch for %v: have %v, want %v", i, tt.result, !ok, tt.drop) } @@ -1138,7 +1156,7 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) { // Tests that synchronisation progress (origin block number, current block number // and highest block number) is tracked and updated correctly. func TestSyncProgress66Full(t *testing.T) { testSyncProgress(t, eth.ETH66, FullSync) } -func TestSyncProgress66Fast(t *testing.T) { testSyncProgress(t, eth.ETH66, FastSync) } +func TestSyncProgress66Fast(t *testing.T) { testSyncProgress(t, eth.ETH66, SnapSync) } func TestSyncProgress66Light(t *testing.T) { testSyncProgress(t, eth.ETH66, LightSync) } func testSyncProgress(t *testing.T, protocol uint, mode SyncMode) { @@ -1207,8 +1225,8 @@ func checkProgress(t *testing.T, d *Downloader, stage string, want ethereum.Sync t.Helper() p := d.Progress() - p.KnownStates, p.PulledStates = 0, 0 - want.KnownStates, want.PulledStates = 0, 0 + //p.KnownStates, p.PulledStates = 0, 0 + //want.KnownStates, want.PulledStates = 0, 0 if p != want { t.Fatalf("%s progress mismatch:\nhave %+v\nwant %+v", stage, p, want) } @@ -1218,7 +1236,7 @@ func checkProgress(t *testing.T, d *Downloader, stage string, want ethereum.Sync // number) is tracked and updated correctly in case of a fork (or manual head // revertal). func TestForkedSyncProgress66Full(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, FullSync) } -func TestForkedSyncProgress66Fast(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, FastSync) } +func TestForkedSyncProgress66Fast(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, SnapSync) } func TestForkedSyncProgress66Light(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, LightSync) } func testForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { @@ -1290,7 +1308,7 @@ func testForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { // origin is not updated in the next sync cycle, as it should be considered the // continuation of the previous sync and not a new instance. func TestFailedSyncProgress66Full(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, FullSync) } -func TestFailedSyncProgress66Fast(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, FastSync) } +func TestFailedSyncProgress66Fast(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, SnapSync) } func TestFailedSyncProgress66Light(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, LightSync) } func testFailedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { @@ -1359,7 +1377,7 @@ func testFailedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { // Tests that if an attacker fakes a chain height, after the attack is detected, // the progress height is successfully reduced at the next sync invocation. func TestFakedSyncProgress66Full(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, FullSync) } -func TestFakedSyncProgress66Fast(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, FastSync) } +func TestFakedSyncProgress66Fast(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, SnapSync) } func TestFakedSyncProgress66Light(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, LightSync) } func testFakedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { @@ -1429,10 +1447,11 @@ func testFakedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { }) } +/* // This test reproduces an issue where unexpected deliveries would // block indefinitely if they arrived at the right time. func TestDeliverHeadersHang66Full(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, FullSync) } -func TestDeliverHeadersHang66Fast(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, FastSync) } +func TestDeliverHeadersHang66Fast(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, SnapSync) } func TestDeliverHeadersHang66Light(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, LightSync) } func testDeliverHeadersHang(t *testing.T, protocol uint, mode SyncMode) { @@ -1510,7 +1529,7 @@ func (ftp *floodingTestPeer) RequestHeadersByNumber(from uint64, count, skip int } return nil } - +*/ func TestRemoteHeaderRequestSpan(t *testing.T) { testCases := []struct { remoteHeight uint64 @@ -1589,7 +1608,7 @@ func TestRemoteHeaderRequestSpan(t *testing.T) { // Tests that peers below a pre-configured checkpoint block are prevented from // being fast-synced from, avoiding potential cheap eclipse attacks. func TestCheckpointEnforcement66Full(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, FullSync) } -func TestCheckpointEnforcement66Fast(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, FastSync) } +func TestCheckpointEnforcement66Fast(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, SnapSync) } func TestCheckpointEnforcement66Light(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, LightSync) } @@ -1608,13 +1627,13 @@ func testCheckpointEnforcement(t *testing.T, protocol uint, mode SyncMode) { tester.newPeer("peer", protocol, chain) var expect error - if mode == FastSync || mode == LightSync { + if mode == SnapSync || mode == LightSync { expect = errUnsyncedPeer } if err := tester.sync("peer", nil, mode); !errors.Is(err, expect) { t.Fatalf("block sync error mismatch: have %v, want %v", err, expect) } - if mode == FastSync || mode == LightSync { + if mode == SnapSync || mode == LightSync { assertOwnChain(t, tester, 1) } else { assertOwnChain(t, tester, chain.len()) diff --git a/eth/downloader/fetchers.go b/eth/downloader/fetchers.go new file mode 100644 index 000000000000..063d0d824e3f --- /dev/null +++ b/eth/downloader/fetchers.go @@ -0,0 +1,113 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package downloader + +import ( + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/protocols/eth" +) + +// fetchHeadersByHash is a blocking version of Peer.RequestHeadersByHash which +// handles all the cancellation, interruption and timeout mechanisms of a data +// retrieval to allow blocking API calls. +func (d *Downloader) fetchHeadersByHash(p *peerConnection, hash common.Hash, amount int, skip int, reverse bool) ([]*types.Header, error) { + // Create the response sink and send the network request + start := time.Now() + resCh := make(chan *eth.Response) + + req, err := p.peer.RequestHeadersByHash(hash, amount, skip, reverse, resCh) + if err != nil { + return nil, err + } + defer req.Close() + + // Wait until the response arrives, the request is cancelled or times out + ttl := d.peers.rates.TargetTimeout() + + timeoutTimer := time.NewTimer(ttl) + defer timeoutTimer.Stop() + + select { + case <-d.cancelCh: + return nil, errCanceled + + case <-timeoutTimer.C: + // Header retrieval timed out, update the metrics + p.log.Debug("Header request timed out", "elapsed", ttl) + headerTimeoutMeter.Mark(1) + + return nil, errTimeout + + case res := <-resCh: + // Headers successfully retrieved, update the metrics + headerReqTimer.Update(time.Since(start)) + + // Don't reject the packet even if it turns out to be bad, downloader will + // disconnect the peer on its own terms. Simply delivery the headers to + // be processed by the caller + res.Done <- nil + + return *res.Res.(*eth.BlockHeadersPacket), nil + } +} + +// fetchHeadersByNumber is a blocking version of Peer.RequestHeadersByNumber which +// handles all the cancellation, interruption and timeout mechanisms of a data +// retrieval to allow blocking API calls. +func (d *Downloader) fetchHeadersByNumber(p *peerConnection, number uint64, amount int, skip int, reverse bool) ([]*types.Header, error) { + // Create the response sink and send the network request + start := time.Now() + resCh := make(chan *eth.Response) + + req, err := p.peer.RequestHeadersByNumber(number, amount, skip, reverse, resCh) + if err != nil { + return nil, err + } + defer req.Close() + + // Wait until the response arrives, the request is cancelled or times out + ttl := d.peers.rates.TargetTimeout() + + timeoutTimer := time.NewTimer(ttl) + defer timeoutTimer.Stop() + + select { + case <-d.cancelCh: + return nil, errCanceled + + case <-timeoutTimer.C: + // Header retrieval timed out, update the metrics + p.log.Debug("Header request timed out", "elapsed", ttl) + headerTimeoutMeter.Mark(1) + + return nil, errTimeout + + case res := <-resCh: + // Headers successfully retrieved, update the metrics + headerReqTimer.Update(time.Since(start)) + + // Don't reject the packet even if it turns out to be bad, downloader will + // disconnect the peer on its own terms. Simply delivery the headers to + // be processed by the caller + res.Done <- nil + + return *res.Res.(*eth.BlockHeadersPacket), nil + } +} diff --git a/eth/downloader/fetchers_concurrent.go b/eth/downloader/fetchers_concurrent.go new file mode 100644 index 000000000000..2ec9b0897a8d --- /dev/null +++ b/eth/downloader/fetchers_concurrent.go @@ -0,0 +1,342 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package downloader + +import ( + "errors" + "sort" + "time" + + "github.com/ethereum/go-ethereum/common/prque" + "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/log" +) + +// typedQueue is an interface defining the adaptor needed to translate the type +// specific downloader/queue schedulers into the type-agnostic general concurrent +// fetcher algorithm calls. +type typedQueue interface { + // waker returns a notification channel that gets pinged in case more fetches + // have been queued up, so the fetcher might assign it to idle peers. + waker() chan bool + + // pending returns the number of wrapped items that are currently queued for + // fetching by the concurrent downloader. + pending() int + + // capacity is responsible for calculating how many items of the abstracted + // type a particular peer is estimated to be able to retrieve within the + // alloted round trip time. + capacity(peer *peerConnection, rtt time.Duration) int + + // updateCapacity is responsible for updating how many items of the abstracted + // type a particular peer is estimated to be able to retrieve in a unit time. + updateCapacity(peer *peerConnection, items int, elapsed time.Duration) + + // reserve is responsible for allocating a requested number of pending items + // from the download queue to the specified peer. + reserve(peer *peerConnection, items int) (*fetchRequest, bool, bool) + + // unreserve is resposible for removing the current retrieval allocation + // assigned to a specific peer and placing it back into the pool to allow + // reassigning to some other peer. + unreserve(peer string) int + + // request is responsible for converting a generic fetch request into a typed + // one and sending it to the remote peer for fulfillment. + request(peer *peerConnection, req *fetchRequest, resCh chan *eth.Response) (*eth.Request, error) + + // deliver is responsible for taking a generic response packet from the + // concurrent fetcher, unpacking the type specific data and delivering + // it to the downloader's queue. + deliver(peer *peerConnection, packet *eth.Response) (int, error) +} + +// concurrentFetch iteratively downloads scheduled block parts, taking available +// peers, reserving a chunk of fetch requests for each and waiting for delivery +// or timeouts. +func (d *Downloader) concurrentFetch(queue typedQueue, beaconMode bool) error { + // Create a delivery channel to accept responses from all peers + responses := make(chan *eth.Response) + + // Track the currently active requests and their timeout order + requests := make(map[string]*eth.Request) + defer func() { + // Abort all requests on sync cycle cancellation. The requests will still + // be fulfilled by the remote side, but the dispatcher will not wait to + // deliver them since nobody's going to be listening. + for _, req := range requests { + req.Close() + } + }() + ordering := make(map[*eth.Request]int) + timeouts := prque.New(func(data interface{}, index int) { + ordering[data.(*eth.Request)] = index + }) + + timeout := time.NewTimer(0) + if !timeout.Stop() { + <-timeout.C + } + defer timeout.Stop() + + // Subscribe to peer lifecycle events to schedule tasks to new joiners and + // reschedule tasks upon disconnections. We don't care which event happened + // for simplicity, so just use a single channel. + peering := make(chan *peeringEvent, 64) // arbitrary buffer, just some burst protection + + peeringSub := d.peers.SubscribeEvents(peering) + defer peeringSub.Unsubscribe() + + // Prepare the queue and fetch block parts until the block header fetcher's done + finished := false + for { + // Short circuit if we lost all our peers + if d.peers.Len() == 0 && !beaconMode { + return errNoPeers + } + // If there's nothing more to fetch, wait or terminate + if queue.pending() == 0 { + if len(requests) == 0 && finished { + return nil + } + } else { + // Send a download request to all idle peers, until throttled + var ( + idles []*peerConnection + caps []int + ) + for _, peer := range d.peers.AllPeers() { + if _, ok := requests[peer.id]; !ok { + idles = append(idles, peer) + caps = append(caps, queue.capacity(peer, time.Second)) + } + } + sort.Sort(&peerCapacitySort{idles, caps}) + + var ( + progressed bool + throttled bool + queued = queue.pending() + ) + for _, peer := range idles { + // Short circuit if throttling activated or there are no more + // queued tasks to be retrieved + if throttled { + break + } + if queued = queue.pending(); queued == 0 { + break + } + // Reserve a chunk of fetches for a peer. A nil can mean either that + // no more headers are available, or that the peer is known not to + // have them. + request, progress, throttle := queue.reserve(peer, queue.capacity(peer, d.peers.rates.TargetRoundTrip())) + if progress { + progressed = true + } + if throttle { + throttled = true + throttleCounter.Inc(1) + } + if request == nil { + continue + } + // Fetch the chunk and make sure any errors return the hashes to the queue + req, err := queue.request(peer, request, responses) + if err != nil { + // Sending the request failed, which generally means the peer + // was diconnected in between assignment and network send. + // Although all peer removal operations return allocated tasks + // to the queue, that is async, and we can do better here by + // immediately pushing the unfulfilled requests. + queue.unreserve(peer.id) // TODO(karalabe): This needs a non-expiration method + continue + } + requests[peer.id] = req + + ttl := d.peers.rates.TargetTimeout() + ordering[req] = timeouts.Size() + + timeouts.Push(req, -time.Now().Add(ttl).UnixNano()) + if timeouts.Size() == 1 { + timeout.Reset(ttl) + } + } + // Make sure that we have peers available for fetching. If all peers have been tried + // and all failed throw an error + if !progressed && !throttled && len(requests) == 0 && len(idles) == d.peers.Len() && queued > 0 && !beaconMode { + return errPeersUnavailable + } + } + // Wait for something to happen + select { + case <-d.cancelCh: + // If sync was cancelled, tear down the parallel retriever. Pending + // requests will be cancelled locally, and the remote responses will + // be dropped when they arrive + return errCanceled + + case event := <-peering: + // A peer joined or left, the tasks queue and allocations need to be + // checked for potential assignment or reassignment + peerid := event.peer.id + + if event.join { + // Sanity check the internal state; this can be dropped later + if _, ok := requests[peerid]; ok { + event.peer.log.Error("Pending request exists for joining peer") + } + // Loop back to the entry point for task assignment + continue + } + // A peer left, any existing requests need to be untracked, pending + // tasks returned and possible reassignment checked + if req, ok := requests[peerid]; ok { + queue.unreserve(peerid) // TODO(karalabe): This needs a non-expiration method + delete(requests, peerid) + req.Close() + + if index, live := ordering[req]; live { + timeouts.Remove(index) + if index == 0 { + if !timeout.Stop() { + <-timeout.C + } + if timeouts.Size() > 0 { + _, exp := timeouts.Peek() + timeout.Reset(time.Until(time.Unix(0, -exp))) + } + } + delete(ordering, req) + } + } + + case <-timeout.C: + // Retrieve the next request which should have timed out. The check + // below is purely for to catch programming errors, given the correct + // code, there's no possible order of events that should result in a + // timeout firing for a non-existent event. + item, exp := timeouts.Peek() + if now, at := time.Now(), time.Unix(0, -exp); now.Before(at) { + log.Error("Timeout triggered but not reached", "left", at.Sub(now)) + timeout.Reset(at.Sub(now)) + continue + } + req := item.(*eth.Request) + + // Stop tracking the timed out request from a timing perspective, + // but leave it in the current pool of requests to avoid assigning + // a second one to an already overloaded node. + delete(ordering, req) + + timeouts.Pop() + if timeouts.Size() > 0 { + _, exp := timeouts.Peek() + timeout.Reset(time.Until(time.Unix(0, -exp))) + } + // New timeout potentially set if there are more requests pending, + // reschedule the failed one to a free peer + fails := queue.unreserve(req.Peer) + + // Finally, update the peer's retrieval capacity, or if it's already + // below the minimum allowance, drop the peer. If a lot of retrieval + // elements expired, we might have overestimated the remote peer or + // perhaps ourselves. Only reset to minimal throughput but don't drop + // just yet. + // + // The reason the minimum threshold is 2 is that the downloader tries + // to estimate the bandwidth and latency of a peer separately, which + // requires pushing the measured capacity a bit and seeing how response + // times reacts, to it always requests one more than the minimum (i.e. + // min 2). + peer := d.peers.Peer(req.Peer) + if peer == nil { + // If the peer got disconnected in between, we should really have + // short-circuited it already. Just in case there's some strange + // codepath, leave this check in not to crash. + log.Error("Delivery timeout from unknown peer", "peer", req.Peer) + continue + } + if fails > 2 { + queue.updateCapacity(peer, 0, 0) + } else { + d.dropPeer(peer.id) + + // If this peer was the master peer, abort sync immediately + d.cancelLock.RLock() + master := peer.id == d.cancelPeer + d.cancelLock.RUnlock() + + if master { + d.cancel() + return errTimeout + } + } + + case res := <-responses: + // Response arrived, it may be for an existing or an already timed + // out request. If the former, update the timeout heap and perhaps + // reschedule the timeout timer. + index, live := ordering[res.Req] + if live { + timeouts.Remove(index) + if index == 0 { + if !timeout.Stop() { + <-timeout.C + } + if timeouts.Size() > 0 { + _, exp := timeouts.Peek() + timeout.Reset(time.Until(time.Unix(0, -exp))) + } + } + delete(ordering, res.Req) + } + if _, ok := requests[res.Req.Peer]; ok { + delete(requests, res.Req.Peer) + } else { + log.Error("Response arrived to non-tracked request") + } + // Signal the dispatcher that the round trip is done. We'll drop the + // peer if the data turns out to be junk. + res.Done <- nil + res.Req.Close() + + // If the peer was previously banned and failed to deliver its pack + // in a reasonable time frame, ignore its message. + if peer := d.peers.Peer(res.Req.Peer); peer != nil { + // Deliver the received chunk of data and check chain validity + accepted, err := queue.deliver(peer, res) + if errors.Is(err, errInvalidChain) { + return err + } + // Unless a peer delivered something completely else than requested (usually + // caused by a timed out request which came through in the end), set it to + // idle. If the delivery's stale, the peer should have already been idled. + if !errors.Is(err, errStaleDelivery) { + queue.updateCapacity(peer, accepted, res.Time) + } + } + + case cont := <-queue.waker(): + // The header fetcher sent a continuation flag, check if it's done + if !cont { + finished = true + } + } + } +} diff --git a/eth/downloader/fetchers_concurrent_bodies.go b/eth/downloader/fetchers_concurrent_bodies.go new file mode 100644 index 000000000000..259811f578a6 --- /dev/null +++ b/eth/downloader/fetchers_concurrent_bodies.go @@ -0,0 +1,104 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package downloader + +import ( + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/log" +) + +// bodyQueue implements typedQueue and is a type adapter between the generic +// concurrent fetcher and the downloader. +type bodyQueue Downloader + +// waker returns a notification channel that gets pinged in case more body +// fetches have been queued up, so the fetcher might assign it to idle peers. +func (q *bodyQueue) waker() chan bool { + return q.queue.blockWakeCh +} + +// pending returns the number of bodies that are currently queued for fetching +// by the concurrent downloader. +func (q *bodyQueue) pending() int { + return q.queue.PendingBodies() +} + +// capacity is responsible for calculating how many bodies a particular peer is +// estimated to be able to retrieve within the alloted round trip time. +func (q *bodyQueue) capacity(peer *peerConnection, rtt time.Duration) int { + return peer.BodyCapacity(rtt) +} + +// updateCapacity is responsible for updating how many bodies a particular peer +// is estimated to be able to retrieve in a unit time. +func (q *bodyQueue) updateCapacity(peer *peerConnection, items int, span time.Duration) { + peer.UpdateBodyRate(items, span) +} + +// reserve is responsible for allocating a requested number of pending bodies +// from the download queue to the specified peer. +func (q *bodyQueue) reserve(peer *peerConnection, items int) (*fetchRequest, bool, bool) { + return q.queue.ReserveBodies(peer, items) +} + +// unreserve is resposible for removing the current body retrieval allocation +// assigned to a specific peer and placing it back into the pool to allow +// reassigning to some other peer. +func (q *bodyQueue) unreserve(peer string) int { + fails := q.queue.ExpireBodies(peer) + if fails > 2 { + log.Trace("Body delivery timed out", "peer", peer) + } else { + log.Debug("Body delivery stalling", "peer", peer) + } + return fails +} + +// request is responsible for converting a generic fetch request into a body +// one and sending it to the remote peer for fulfillment. +func (q *bodyQueue) request(peer *peerConnection, req *fetchRequest, resCh chan *eth.Response) (*eth.Request, error) { + peer.log.Trace("Requesting new batch of bodies", "count", len(req.Headers), "from", req.Headers[0].Number) + if q.bodyFetchHook != nil { + q.bodyFetchHook(req.Headers) + } + + hashes := make([]common.Hash, 0, len(req.Headers)) + for _, header := range req.Headers { + hashes = append(hashes, header.Hash()) + } + return peer.peer.RequestBodies(hashes, resCh) +} + +// deliver is responsible for taking a generic response packet from the concurrent +// fetcher, unpacking the body data and delivering it to the downloader's queue. +func (q *bodyQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) { + txs, uncles := packet.Res.(*eth.BlockBodiesPacket).Unpack() + + accepted, err := q.queue.DeliverBodies(peer.id, txs, uncles) + switch { + case err == nil && len(txs) == 0: + peer.log.Trace("Requested bodies delivered") + case err == nil: + peer.log.Trace("Delivered new batch of bodies", "count", len(txs), "accepted", accepted) + default: + peer.log.Debug("Failed to deliver retrieved bodies", "err", err) + } + return accepted, err +} diff --git a/eth/downloader/fetchers_concurrent_headers.go b/eth/downloader/fetchers_concurrent_headers.go new file mode 100644 index 000000000000..f30b279d2c73 --- /dev/null +++ b/eth/downloader/fetchers_concurrent_headers.go @@ -0,0 +1,95 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package downloader + +import ( + "time" + + "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/log" +) + +// headerQueue implements typedQueue and is a type adapter between the generic +// concurrent fetcher and the downloader. +type headerQueue Downloader + +// waker returns a notification channel that gets pinged in case more header +// fetches have been queued up, so the fetcher might assign it to idle peers. +func (q *headerQueue) waker() chan bool { + return q.queue.headerContCh +} + +// pending returns the number of headers that are currently queued for fetching +// by the concurrent downloader. +func (q *headerQueue) pending() int { + return q.queue.PendingHeaders() +} + +// capacity is responsible for calculating how many headers a particular peer is +// estimated to be able to retrieve within the alloted round trip time. +func (q *headerQueue) capacity(peer *peerConnection, rtt time.Duration) int { + return peer.HeaderCapacity(rtt) +} + +// updateCapacity is responsible for updating how many headers a particular peer +// is estimated to be able to retrieve in a unit time. +func (q *headerQueue) updateCapacity(peer *peerConnection, items int, span time.Duration) { + peer.UpdateHeaderRate(items, span) +} + +// reserve is responsible for allocating a requested number of pending headers +// from the download queue to the specified peer. +func (q *headerQueue) reserve(peer *peerConnection, items int) (*fetchRequest, bool, bool) { + return q.queue.ReserveHeaders(peer, items), false, false +} + +// unreserve is resposible for removing the current header retrieval allocation +// assigned to a specific peer and placing it back into the pool to allow +// reassigning to some other peer. +func (q *headerQueue) unreserve(peer string) int { + fails := q.queue.ExpireHeaders(peer) + if fails > 2 { + log.Trace("Header delivery timed out", "peer", peer) + } else { + log.Debug("Header delivery stalling", "peer", peer) + } + return fails +} + +// request is responsible for converting a generic fetch request into a header +// one and sending it to the remote peer for fulfillment. +func (q *headerQueue) request(peer *peerConnection, req *fetchRequest, resCh chan *eth.Response) (*eth.Request, error) { + peer.log.Trace("Requesting new batch of headers", "from", req.From) + return peer.peer.RequestHeadersByNumber(req.From, MaxHeaderFetch, 0, false, resCh) +} + +// deliver is responsible for taking a generic response packet from the concurrent +// fetcher, unpacking the header data and delivering it to the downloader's queue. +func (q *headerQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) { + headers := *packet.Res.(*eth.BlockHeadersPacket) + + accepted, err := q.queue.DeliverHeaders(peer.id, headers, q.headerProcCh) + switch { + case err == nil && len(headers) == 0: + peer.log.Trace("Requested headers delivered") + case err == nil: + peer.log.Trace("Delivered new batch of headers", "count", len(headers), "accepted", accepted) + default: + peer.log.Debug("Failed to deliver retrieved headers", "err", err) + } + return accepted, err +} diff --git a/eth/downloader/fetchers_concurrent_receipts.go b/eth/downloader/fetchers_concurrent_receipts.go new file mode 100644 index 000000000000..fafdfe4ee8c7 --- /dev/null +++ b/eth/downloader/fetchers_concurrent_receipts.go @@ -0,0 +1,103 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package downloader + +import ( + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/log" +) + +// receiptQueue implements typedQueue and is a type adapter between the generic +// concurrent fetcher and the downloader. +type receiptQueue Downloader + +// waker returns a notification channel that gets pinged in case more reecipt +// fetches have been queued up, so the fetcher might assign it to idle peers. +func (q *receiptQueue) waker() chan bool { + return q.queue.receiptWakeCh +} + +// pending returns the number of receipt that are currently queued for fetching +// by the concurrent downloader. +func (q *receiptQueue) pending() int { + return q.queue.PendingReceipts() +} + +// capacity is responsible for calculating how many receipts a particular peer is +// estimated to be able to retrieve within the alloted round trip time. +func (q *receiptQueue) capacity(peer *peerConnection, rtt time.Duration) int { + return peer.ReceiptCapacity(rtt) +} + +// updateCapacity is responsible for updating how many receipts a particular peer +// is estimated to be able to retrieve in a unit time. +func (q *receiptQueue) updateCapacity(peer *peerConnection, items int, span time.Duration) { + peer.UpdateReceiptRate(items, span) +} + +// reserve is responsible for allocating a requested number of pending receipts +// from the download queue to the specified peer. +func (q *receiptQueue) reserve(peer *peerConnection, items int) (*fetchRequest, bool, bool) { + return q.queue.ReserveReceipts(peer, items) +} + +// unreserve is resposible for removing the current receipt retrieval allocation +// assigned to a specific peer and placing it back into the pool to allow +// reassigning to some other peer. +func (q *receiptQueue) unreserve(peer string) int { + fails := q.queue.ExpireReceipts(peer) + if fails > 2 { + log.Trace("Receipt delivery timed out", "peer", peer) + } else { + log.Debug("Receipt delivery stalling", "peer", peer) + } + return fails +} + +// request is responsible for converting a generic fetch request into a recipt +// one and sending it to the remote peer for fulfillment. +func (q *receiptQueue) request(peer *peerConnection, req *fetchRequest, resCh chan *eth.Response) (*eth.Request, error) { + peer.log.Trace("Requesting new batch of receipts", "count", len(req.Headers), "from", req.Headers[0].Number) + if q.receiptFetchHook != nil { + q.receiptFetchHook(req.Headers) + } + hashes := make([]common.Hash, 0, len(req.Headers)) + for _, header := range req.Headers { + hashes = append(hashes, header.Hash()) + } + return peer.peer.RequestReceipts(hashes, resCh) +} + +// deliver is responsible for taking a generic response packet from the concurrent +// fetcher, unpacking the body data and delivering it to the downloader's queue. +func (q *receiptQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) { + receipts := *packet.Res.(*eth.ReceiptsPacket) + + accepted, err := q.queue.DeliverReceipts(peer.id, receipts) + switch { + case err == nil && len(receipts) == 0: + peer.log.Trace("Requested receipts delivered") + case err == nil: + peer.log.Trace("Delivered new batch of receipts", "count", len(receipts), "accepted", accepted) + default: + peer.log.Debug("Failed to deliver retrieved receipts", "err", err) + } + return accepted, err +} diff --git a/eth/downloader/modes.go b/eth/downloader/modes.go index 3ea14d22d7e0..d388b9ee4d46 100644 --- a/eth/downloader/modes.go +++ b/eth/downloader/modes.go @@ -24,7 +24,6 @@ type SyncMode uint32 const ( FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks - FastSync // Quickly download the headers, full sync only at the chain SnapSync // Download the chain and the state via compact snapshots LightSync // Download only the headers and terminate afterwards ) @@ -38,8 +37,6 @@ func (mode SyncMode) String() string { switch mode { case FullSync: return "full" - case FastSync: - return "fast" case SnapSync: return "snap" case LightSync: @@ -53,8 +50,6 @@ func (mode SyncMode) MarshalText() ([]byte, error) { switch mode { case FullSync: return []byte("full"), nil - case FastSync: - return []byte("fast"), nil case SnapSync: return []byte("snap"), nil case LightSync: @@ -68,14 +63,12 @@ func (mode *SyncMode) UnmarshalText(text []byte) error { switch string(text) { case "full": *mode = FullSync - case "fast": - *mode = FastSync case "snap": *mode = SnapSync case "light": *mode = LightSync default: - return fmt.Errorf(`unknown sync mode %q, want "full", "fast" or "light"`, text) + return fmt.Errorf(`unknown sync mode %q, want "full", "snap" or "light"`, text) } return nil } diff --git a/eth/downloader/peer.go b/eth/downloader/peer.go index 863294832971..028e10a50f09 100644 --- a/eth/downloader/peer.go +++ b/eth/downloader/peer.go @@ -22,9 +22,7 @@ package downloader import ( "errors" "math/big" - "sort" "sync" - "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" @@ -48,16 +46,6 @@ var ( type peerConnection struct { id string // Unique identifier of the peer - headerIdle int32 // Current header activity state of the peer (idle = 0, active = 1) - blockIdle int32 // Current block activity state of the peer (idle = 0, active = 1) - receiptIdle int32 // Current receipt activity state of the peer (idle = 0, active = 1) - stateIdle int32 // Current node data activity state of the peer (idle = 0, active = 1) - - headerStarted time.Time // Time instance when the last header fetch was started - blockStarted time.Time // Time instance when the last block (body) fetch was started - receiptStarted time.Time // Time instance when the last receipt fetch was started - stateStarted time.Time // Time instance when the last node data fetch was started - rates *msgrate.Tracker // Tracker to hone in on the number of items retrievable per second lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously) @@ -71,16 +59,15 @@ type peerConnection struct { // LightPeer encapsulates the methods required to synchronise with a remote light peer. type LightPeer interface { Head() (common.Hash, *big.Int) - RequestHeadersByHash(common.Hash, int, int, bool) error - RequestHeadersByNumber(uint64, int, int, bool) error + RequestHeadersByHash(common.Hash, int, int, bool, chan *eth.Response) (*eth.Request, error) + RequestHeadersByNumber(uint64, int, int, bool, chan *eth.Response) (*eth.Request, error) } // Peer encapsulates the methods required to synchronise with a remote full peer. type Peer interface { LightPeer - RequestBodies([]common.Hash) error - RequestReceipts([]common.Hash) error - RequestNodeData([]common.Hash) error + RequestBodies([]common.Hash, chan *eth.Response) (*eth.Request, error) + RequestReceipts([]common.Hash, chan *eth.Response) (*eth.Request, error) } // lightPeerWrapper wraps a LightPeer struct, stubbing out the Peer-only methods. @@ -89,21 +76,18 @@ type lightPeerWrapper struct { } func (w *lightPeerWrapper) Head() (common.Hash, *big.Int) { return w.peer.Head() } -func (w *lightPeerWrapper) RequestHeadersByHash(h common.Hash, amount int, skip int, reverse bool) error { - return w.peer.RequestHeadersByHash(h, amount, skip, reverse) +func (w *lightPeerWrapper) RequestHeadersByHash(h common.Hash, amount int, skip int, reverse bool, sink chan *eth.Response) (*eth.Request, error) { + return w.peer.RequestHeadersByHash(h, amount, skip, reverse, sink) } -func (w *lightPeerWrapper) RequestHeadersByNumber(i uint64, amount int, skip int, reverse bool) error { - return w.peer.RequestHeadersByNumber(i, amount, skip, reverse) +func (w *lightPeerWrapper) RequestHeadersByNumber(i uint64, amount int, skip int, reverse bool, sink chan *eth.Response) (*eth.Request, error) { + return w.peer.RequestHeadersByNumber(i, amount, skip, reverse, sink) } -func (w *lightPeerWrapper) RequestBodies([]common.Hash) error { +func (w *lightPeerWrapper) RequestBodies([]common.Hash, chan *eth.Response) (*eth.Request, error) { panic("RequestBodies not supported in light client mode sync") } -func (w *lightPeerWrapper) RequestReceipts([]common.Hash) error { +func (w *lightPeerWrapper) RequestReceipts([]common.Hash, chan *eth.Response) (*eth.Request, error) { panic("RequestReceipts not supported in light client mode sync") } -func (w *lightPeerWrapper) RequestNodeData([]common.Hash) error { - panic("RequestNodeData not supported in light client mode sync") -} // newPeerConnection creates a new downloader peer. func newPeerConnection(id string, version uint, peer Peer, logger log.Logger) *peerConnection { @@ -121,114 +105,28 @@ func (p *peerConnection) Reset() { p.lock.Lock() defer p.lock.Unlock() - atomic.StoreInt32(&p.headerIdle, 0) - atomic.StoreInt32(&p.blockIdle, 0) - atomic.StoreInt32(&p.receiptIdle, 0) - atomic.StoreInt32(&p.stateIdle, 0) - p.lacking = make(map[common.Hash]struct{}) } -// FetchHeaders sends a header retrieval request to the remote peer. -func (p *peerConnection) FetchHeaders(from uint64, count int) error { - // Short circuit if the peer is already fetching - if !atomic.CompareAndSwapInt32(&p.headerIdle, 0, 1) { - return errAlreadyFetching - } - p.headerStarted = time.Now() - - // Issue the header retrieval request (absolute upwards without gaps) - go p.peer.RequestHeadersByNumber(from, count, 0, false) - - return nil -} - -// FetchBodies sends a block body retrieval request to the remote peer. -func (p *peerConnection) FetchBodies(request *fetchRequest) error { - // Short circuit if the peer is already fetching - if !atomic.CompareAndSwapInt32(&p.blockIdle, 0, 1) { - return errAlreadyFetching - } - p.blockStarted = time.Now() - - go func() { - // Convert the header set to a retrievable slice - hashes := make([]common.Hash, 0, len(request.Headers)) - for _, header := range request.Headers { - hashes = append(hashes, header.Hash()) - } - p.peer.RequestBodies(hashes) - }() - - return nil -} - -// FetchReceipts sends a receipt retrieval request to the remote peer. -func (p *peerConnection) FetchReceipts(request *fetchRequest) error { - // Short circuit if the peer is already fetching - if !atomic.CompareAndSwapInt32(&p.receiptIdle, 0, 1) { - return errAlreadyFetching - } - p.receiptStarted = time.Now() - - go func() { - // Convert the header set to a retrievable slice - hashes := make([]common.Hash, 0, len(request.Headers)) - for _, header := range request.Headers { - hashes = append(hashes, header.Hash()) - } - p.peer.RequestReceipts(hashes) - }() - - return nil +// UpdateHeaderRate updates the peer's estimated header retrieval throughput with +// the current measurement. +func (p *peerConnection) UpdateHeaderRate(delivered int, elapsed time.Duration) { + p.rates.Update(eth.BlockHeadersMsg, elapsed, delivered) } -// FetchNodeData sends a node state data retrieval request to the remote peer. -func (p *peerConnection) FetchNodeData(hashes []common.Hash) error { - // Short circuit if the peer is already fetching - if !atomic.CompareAndSwapInt32(&p.stateIdle, 0, 1) { - return errAlreadyFetching - } - p.stateStarted = time.Now() - - go p.peer.RequestNodeData(hashes) - - return nil +// UpdateBodyRate updates the peer's estimated body retrieval throughput with the +// current measurement. +func (p *peerConnection) UpdateBodyRate(delivered int, elapsed time.Duration) { + p.rates.Update(eth.BlockBodiesMsg, elapsed, delivered) } -// SetHeadersIdle sets the peer to idle, allowing it to execute new header retrieval -// requests. Its estimated header retrieval throughput is updated with that measured -// just now. -func (p *peerConnection) SetHeadersIdle(delivered int, deliveryTime time.Time) { - p.rates.Update(eth.BlockHeadersMsg, deliveryTime.Sub(p.headerStarted), delivered) - atomic.StoreInt32(&p.headerIdle, 0) +// UpdateReceiptRate updates the peer's estimated receipt retrieval throughput +// with the current measurement. +func (p *peerConnection) UpdateReceiptRate(delivered int, elapsed time.Duration) { + p.rates.Update(eth.ReceiptsMsg, elapsed, delivered) } -// SetBodiesIdle sets the peer to idle, allowing it to execute block body retrieval -// requests. Its estimated body retrieval throughput is updated with that measured -// just now. -func (p *peerConnection) SetBodiesIdle(delivered int, deliveryTime time.Time) { - p.rates.Update(eth.BlockBodiesMsg, deliveryTime.Sub(p.blockStarted), delivered) - atomic.StoreInt32(&p.blockIdle, 0) -} - -// SetReceiptsIdle sets the peer to idle, allowing it to execute new receipt -// retrieval requests. Its estimated receipt retrieval throughput is updated -// with that measured just now. -func (p *peerConnection) SetReceiptsIdle(delivered int, deliveryTime time.Time) { - p.rates.Update(eth.ReceiptsMsg, deliveryTime.Sub(p.receiptStarted), delivered) - atomic.StoreInt32(&p.receiptIdle, 0) -} - -// SetNodeDataIdle sets the peer to idle, allowing it to execute new state trie -// data retrieval requests. Its estimated state retrieval throughput is updated -// with that measured just now. -func (p *peerConnection) SetNodeDataIdle(delivered int, deliveryTime time.Time) { - p.rates.Update(eth.NodeDataMsg, deliveryTime.Sub(p.stateStarted), delivered) - atomic.StoreInt32(&p.stateIdle, 0) -} - -// HeaderCapacity retrieves the peers header download allowance based on its +// HeaderCapacity retrieves the peer's header download allowance based on its // previously discovered throughput. func (p *peerConnection) HeaderCapacity(targetRTT time.Duration) int { cap := p.rates.Capacity(eth.BlockHeadersMsg, targetRTT) @@ -238,9 +136,9 @@ func (p *peerConnection) HeaderCapacity(targetRTT time.Duration) int { return cap } -// BlockCapacity retrieves the peers block download allowance based on its +// BodyCapacity retrieves the peer's body download allowance based on its // previously discovered throughput. -func (p *peerConnection) BlockCapacity(targetRTT time.Duration) int { +func (p *peerConnection) BodyCapacity(targetRTT time.Duration) int { cap := p.rates.Capacity(eth.BlockBodiesMsg, targetRTT) if cap > MaxBlockFetch { cap = MaxBlockFetch @@ -258,16 +156,6 @@ func (p *peerConnection) ReceiptCapacity(targetRTT time.Duration) int { return cap } -// NodeDataCapacity retrieves the peers state download allowance based on its -// previously discovered throughput. -func (p *peerConnection) NodeDataCapacity(targetRTT time.Duration) int { - cap := p.rates.Capacity(eth.NodeDataMsg, targetRTT) - if cap > MaxStateFetch { - cap = MaxStateFetch - } - return cap -} - // MarkLacking appends a new entity to the set of items (blocks, receipts, states) // that a peer is known not to have (i.e. have been requested before). If the // set reaches its maximum allowed capacity, items are randomly dropped off. @@ -294,14 +182,19 @@ func (p *peerConnection) Lacks(hash common.Hash) bool { return ok } +// peeringEvent is sent on the peer event feed when a remote peer connects or +// disconnects. +type peeringEvent struct { + peer *peerConnection + join bool +} + // peerSet represents the collection of active peer participating in the chain // download procedure. type peerSet struct { - peers map[string]*peerConnection - rates *msgrate.Trackers // Set of rate trackers to give the sync a common beat - - newPeerFeed event.Feed - peerDropFeed event.Feed + peers map[string]*peerConnection + rates *msgrate.Trackers // Set of rate trackers to give the sync a common beat + events event.Feed // Feed to publish peer lifecycle events on lock sync.RWMutex } @@ -314,14 +207,9 @@ func newPeerSet() *peerSet { } } -// SubscribeNewPeers subscribes to peer arrival events. -func (ps *peerSet) SubscribeNewPeers(ch chan<- *peerConnection) event.Subscription { - return ps.newPeerFeed.Subscribe(ch) -} - -// SubscribePeerDrops subscribes to peer departure events. -func (ps *peerSet) SubscribePeerDrops(ch chan<- *peerConnection) event.Subscription { - return ps.peerDropFeed.Subscribe(ch) +// SubscribeEvents subscribes to peer arrival and departure events. +func (ps *peerSet) SubscribeEvents(ch chan<- *peeringEvent) event.Subscription { + return ps.events.Subscribe(ch) } // Reset iterates over the current peer set, and resets each of the known peers @@ -355,7 +243,7 @@ func (ps *peerSet) Register(p *peerConnection) error { ps.peers[p.id] = p ps.lock.Unlock() - ps.newPeerFeed.Send(p) + ps.events.Send(&peeringEvent{peer: p, join: true}) return nil } @@ -372,7 +260,7 @@ func (ps *peerSet) Unregister(id string) error { ps.rates.Untrack(id) ps.lock.Unlock() - ps.peerDropFeed.Send(p) + ps.events.Send(&peeringEvent{peer: p, join: false}) return nil } @@ -404,98 +292,22 @@ func (ps *peerSet) AllPeers() []*peerConnection { return list } -// HeaderIdlePeers retrieves a flat list of all the currently header-idle peers -// within the active peer set, ordered by their reputation. -func (ps *peerSet) HeaderIdlePeers() ([]*peerConnection, int) { - idle := func(p *peerConnection) bool { - return atomic.LoadInt32(&p.headerIdle) == 0 - } - throughput := func(p *peerConnection) int { - return p.rates.Capacity(eth.BlockHeadersMsg, time.Second) - } - return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput) -} - -// BodyIdlePeers retrieves a flat list of all the currently body-idle peers within -// the active peer set, ordered by their reputation. -func (ps *peerSet) BodyIdlePeers() ([]*peerConnection, int) { - idle := func(p *peerConnection) bool { - return atomic.LoadInt32(&p.blockIdle) == 0 - } - throughput := func(p *peerConnection) int { - return p.rates.Capacity(eth.BlockBodiesMsg, time.Second) - } - return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput) -} - -// ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers -// within the active peer set, ordered by their reputation. -func (ps *peerSet) ReceiptIdlePeers() ([]*peerConnection, int) { - idle := func(p *peerConnection) bool { - return atomic.LoadInt32(&p.receiptIdle) == 0 - } - throughput := func(p *peerConnection) int { - return p.rates.Capacity(eth.ReceiptsMsg, time.Second) - } - return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput) -} - -// NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle -// peers within the active peer set, ordered by their reputation. -func (ps *peerSet) NodeDataIdlePeers() ([]*peerConnection, int) { - idle := func(p *peerConnection) bool { - return atomic.LoadInt32(&p.stateIdle) == 0 - } - throughput := func(p *peerConnection) int { - return p.rates.Capacity(eth.NodeDataMsg, time.Second) - } - return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput) -} - -// idlePeers retrieves a flat list of all currently idle peers satisfying the -// protocol version constraints, using the provided function to check idleness. -// The resulting set of peers are sorted by their capacity. -func (ps *peerSet) idlePeers(minProtocol, maxProtocol uint, idleCheck func(*peerConnection) bool, capacity func(*peerConnection) int) ([]*peerConnection, int) { - ps.lock.RLock() - defer ps.lock.RUnlock() - - var ( - total = 0 - idle = make([]*peerConnection, 0, len(ps.peers)) - tps = make([]int, 0, len(ps.peers)) - ) - for _, p := range ps.peers { - if p.version >= minProtocol && p.version <= maxProtocol { - if idleCheck(p) { - idle = append(idle, p) - tps = append(tps, capacity(p)) - } - total++ - } - } - - // And sort them - sortPeers := &peerCapacitySort{idle, tps} - sort.Sort(sortPeers) - return sortPeers.p, total -} - // peerCapacitySort implements sort.Interface. // It sorts peer connections by capacity (descending). type peerCapacitySort struct { - p []*peerConnection - tp []int + peers []*peerConnection + caps []int } func (ps *peerCapacitySort) Len() int { - return len(ps.p) + return len(ps.peers) } func (ps *peerCapacitySort) Less(i, j int) bool { - return ps.tp[i] > ps.tp[j] + return ps.caps[i] > ps.caps[j] } func (ps *peerCapacitySort) Swap(i, j int) { - ps.p[i], ps.p[j] = ps.p[j], ps.p[i] - ps.tp[i], ps.tp[j] = ps.tp[j], ps.tp[i] + ps.peers[i], ps.peers[j] = ps.peers[j], ps.peers[i] + ps.caps[i], ps.caps[j] = ps.caps[j], ps.caps[i] } diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 04ec12cfa9e7..6915cd4195e2 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -54,8 +54,8 @@ var ( // fetchRequest is a currently running data retrieval operation. type fetchRequest struct { Peer *peerConnection // Peer to which the request was sent - From uint64 // [eth/62] Requested chain element index (used for skeleton fills only) - Headers []*types.Header // [eth/62] Requested headers, sorted by request order + From uint64 // Requested chain element index (used for skeleton fills only) + Headers []*types.Header // Requested headers, sorted by request order Time time.Time // Time when the request was made } @@ -127,10 +127,12 @@ type queue struct { blockTaskPool map[common.Hash]*types.Header // Pending block (body) retrieval tasks, mapping hashes to headers blockTaskQueue *prque.Prque // Priority queue of the headers to fetch the blocks (bodies) for blockPendPool map[string]*fetchRequest // Currently pending block (body) retrieval operations + blockWakeCh chan bool // Channel to notify the block fetcher of new tasks receiptTaskPool map[common.Hash]*types.Header // Pending receipt retrieval tasks, mapping hashes to headers receiptTaskQueue *prque.Prque // Priority queue of the headers to fetch the receipts for receiptPendPool map[string]*fetchRequest // Currently pending receipt retrieval operations + receiptWakeCh chan bool // Channel to notify when receipt fetcher of new tasks resultCache *resultStore // Downloaded but not yet delivered fetch results resultSize common.StorageSize // Approximate size of a block (exponential moving average) @@ -146,9 +148,11 @@ type queue struct { func newQueue(blockCacheLimit int, thresholdInitialSize int) *queue { lock := new(sync.RWMutex) q := &queue{ - headerContCh: make(chan bool), + headerContCh: make(chan bool, 1), blockTaskQueue: prque.New(nil), + blockWakeCh: make(chan bool, 1), receiptTaskQueue: prque.New(nil), + receiptWakeCh: make(chan bool, 1), active: sync.NewCond(lock), lock: lock, } @@ -196,8 +200,8 @@ func (q *queue) PendingHeaders() int { return q.headerTaskQueue.Size() } -// PendingBlocks retrieves the number of block (body) requests pending for retrieval. -func (q *queue) PendingBlocks() int { +// PendingBodies retrieves the number of block body requests pending for retrieval. +func (q *queue) PendingBodies() int { q.lock.Lock() defer q.lock.Unlock() @@ -212,15 +216,6 @@ func (q *queue) PendingReceipts() int { return q.receiptTaskQueue.Size() } -// InFlightHeaders retrieves whether there are header fetch requests currently -// in flight. -func (q *queue) InFlightHeaders() bool { - q.lock.Lock() - defer q.lock.Unlock() - - return len(q.headerPendPool) > 0 -} - // InFlightBlocks retrieves whether there are block fetch requests currently in // flight. func (q *queue) InFlightBlocks() bool { @@ -318,7 +313,7 @@ func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header { q.blockTaskQueue.Push(header, -int64(header.Number.Uint64())) } // Queue for receipt retrieval - if q.mode == FastSync && !header.EmptyReceipts() { + if q.mode == SnapSync && !header.EmptyReceipts() { if _, ok := q.receiptTaskPool[hash]; ok { log.Warn("Header already scheduled for receipt fetch", "number", header.Number, "hash", hash) } else { @@ -383,6 +378,13 @@ func (q *queue) Results(block bool) []*fetchResult { throttleThreshold := uint64((common.StorageSize(blockCacheMemory) + q.resultSize - 1) / q.resultSize) throttleThreshold = q.resultCache.SetThrottleThreshold(throttleThreshold) + // With results removed from the cache, wake throttled fetchers + for _, ch := range []chan bool{q.blockWakeCh, q.receiptWakeCh} { + select { + case ch <- true: + default: + } + } // Log some info at certain times if time.Since(q.lastStatLog) > 60*time.Second { q.lastStatLog = time.Now() @@ -503,7 +505,7 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common // we can ask the resultcache if this header is within the // "prioritized" segment of blocks. If it is not, we need to throttle - stale, throttle, item, err := q.resultCache.AddFetch(header, q.mode == FastSync) + stale, throttle, item, err := q.resultCache.AddFetch(header, q.mode == SnapSync) if stale { // Don't put back in the task queue, this item has already been // delivered upstream @@ -566,40 +568,6 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common return request, progress, throttled } -// CancelHeaders aborts a fetch request, returning all pending skeleton indexes to the queue. -func (q *queue) CancelHeaders(request *fetchRequest) { - q.lock.Lock() - defer q.lock.Unlock() - q.cancel(request, q.headerTaskQueue, q.headerPendPool) -} - -// CancelBodies aborts a body fetch request, returning all pending headers to the -// task queue. -func (q *queue) CancelBodies(request *fetchRequest) { - q.lock.Lock() - defer q.lock.Unlock() - q.cancel(request, q.blockTaskQueue, q.blockPendPool) -} - -// CancelReceipts aborts a body fetch request, returning all pending headers to -// the task queue. -func (q *queue) CancelReceipts(request *fetchRequest) { - q.lock.Lock() - defer q.lock.Unlock() - q.cancel(request, q.receiptTaskQueue, q.receiptPendPool) -} - -// Cancel aborts a fetch request, returning all pending hashes to the task queue. -func (q *queue) cancel(request *fetchRequest, taskQueue *prque.Prque, pendPool map[string]*fetchRequest) { - if request.From > 0 { - taskQueue.Push(request.From, -int64(request.From)) - } - for _, header := range request.Headers { - taskQueue.Push(header, -int64(header.Number.Uint64())) - } - delete(pendPool, request.Peer.id) -} - // Revoke cancels all pending requests belonging to a given peer. This method is // meant to be called during a peer drop to quickly reassign owned data fetches // to remaining nodes. @@ -607,6 +575,10 @@ func (q *queue) Revoke(peerID string) { q.lock.Lock() defer q.lock.Unlock() + if request, ok := q.headerPendPool[peerID]; ok { + q.headerTaskQueue.Push(request.From, -int64(request.From)) + delete(q.headerPendPool, peerID) + } if request, ok := q.blockPendPool[peerID]; ok { for _, header := range request.Headers { q.blockTaskQueue.Push(header, -int64(header.Number.Uint64())) @@ -621,62 +593,60 @@ func (q *queue) Revoke(peerID string) { } } -// ExpireHeaders checks for in flight requests that exceeded a timeout allowance, -// canceling them and returning the responsible peers for penalisation. -func (q *queue) ExpireHeaders(timeout time.Duration) map[string]int { +// ExpireHeaders cancels a request that timed out and moves the pending fetch +// task back into the queue for rescheduling. +func (q *queue) ExpireHeaders(peer string) int { q.lock.Lock() defer q.lock.Unlock() - return q.expire(timeout, q.headerPendPool, q.headerTaskQueue, headerTimeoutMeter) + headerTimeoutMeter.Mark(1) + return q.expire(peer, q.headerPendPool, q.headerTaskQueue) } // ExpireBodies checks for in flight block body requests that exceeded a timeout // allowance, canceling them and returning the responsible peers for penalisation. -func (q *queue) ExpireBodies(timeout time.Duration) map[string]int { +func (q *queue) ExpireBodies(peer string) int { q.lock.Lock() defer q.lock.Unlock() - return q.expire(timeout, q.blockPendPool, q.blockTaskQueue, bodyTimeoutMeter) + bodyTimeoutMeter.Mark(1) + return q.expire(peer, q.blockPendPool, q.blockTaskQueue) } // ExpireReceipts checks for in flight receipt requests that exceeded a timeout // allowance, canceling them and returning the responsible peers for penalisation. -func (q *queue) ExpireReceipts(timeout time.Duration) map[string]int { +func (q *queue) ExpireReceipts(peer string) int { q.lock.Lock() defer q.lock.Unlock() - return q.expire(timeout, q.receiptPendPool, q.receiptTaskQueue, receiptTimeoutMeter) + receiptTimeoutMeter.Mark(1) + return q.expire(peer, q.receiptPendPool, q.receiptTaskQueue) } -// expire is the generic check that move expired tasks from a pending pool back -// into a task pool, returning all entities caught with expired tasks. +// expire is the generic check that moves a specific expired task from a pending +// pool back into a task pool. // -// Note, this method expects the queue lock to be already held. The -// reason the lock is not obtained in here is because the parameters already need -// to access the queue, so they already need a lock anyway. -func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue *prque.Prque, timeoutMeter metrics.Meter) map[string]int { - // Iterate over the expired requests and return each to the queue - expiries := make(map[string]int) - for id, request := range pendPool { - if time.Since(request.Time) > timeout { - // Update the metrics with the timeout - timeoutMeter.Mark(1) - - // Return any non satisfied requests to the pool - if request.From > 0 { - taskQueue.Push(request.From, -int64(request.From)) - } - for _, header := range request.Headers { - taskQueue.Push(header, -int64(header.Number.Uint64())) - } - // Add the peer to the expiry report along the number of failed requests - expiries[id] = len(request.Headers) - - // Remove the expired requests from the pending pool directly - delete(pendPool, id) - } +// Note, this method expects the queue lock to be already held. The reason the +// lock is not obtained in here is that the parameters already need to access +// the queue, so they already need a lock anyway. +func (q *queue) expire(peer string, pendPool map[string]*fetchRequest, taskQueue *prque.Prque) int { + // Retrieve the request being expired and log an error if it's non-existnet, + // as there's no order of events that should lead to such expirations. + req := pendPool[peer] + if req == nil { + log.Error("Expired request does not exist", "peer", peer) + return 0 + } + delete(pendPool, peer) + + // Return any non-satisfied requests to the pool + if req.From > 0 { + taskQueue.Push(req.From, -int64(req.From)) + } + for _, header := range req.Headers { + taskQueue.Push(header, -int64(header.Number.Uint64())) } - return expiries + return len(req.Headers) } // DeliverHeaders injects a header retrieval response into the header results @@ -684,7 +654,7 @@ func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, // if they do not map correctly to the skeleton. // // If the headers are accepted, the method makes an attempt to deliver the set -// of ready headers to the processor to keep the pipeline full. However it will +// of ready headers to the processor to keep the pipeline full. However, it will // not block to prevent stalling other pending deliveries. func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh chan []*types.Header) (int, error) { q.lock.Lock() diff --git a/eth/downloader/queue_test.go b/eth/downloader/queue_test.go index cde5f306a2c0..9dc27a593832 100644 --- a/eth/downloader/queue_test.go +++ b/eth/downloader/queue_test.go @@ -104,7 +104,7 @@ func TestBasics(t *testing.T) { if !q.Idle() { t.Errorf("new queue should be idle") } - q.Prepare(1, FastSync) + q.Prepare(1, SnapSync) if res := q.Results(false); len(res) != 0 { t.Fatal("new queue should have 0 results") } @@ -114,7 +114,7 @@ func TestBasics(t *testing.T) { if q.Idle() { t.Errorf("queue should not be idle") } - if got, exp := q.PendingBlocks(), chain.Len(); got != exp { + if got, exp := q.PendingBodies(), chain.Len(); got != exp { t.Errorf("wrong pending block count, got %d, exp %d", got, exp) } // Only non-empty receipts get added to task-queue @@ -197,13 +197,13 @@ func TestEmptyBlocks(t *testing.T) { q := newQueue(10, 10) - q.Prepare(1, FastSync) + q.Prepare(1, SnapSync) // Schedule a batch of headers q.Schedule(emptyChain.headers(), 1) if q.Idle() { t.Errorf("queue should not be idle") } - if got, exp := q.PendingBlocks(), len(emptyChain.blocks); got != exp { + if got, exp := q.PendingBodies(), len(emptyChain.blocks); got != exp { t.Errorf("wrong pending block count, got %d, exp %d", got, exp) } if got, exp := q.PendingReceipts(), 0; got != exp { @@ -272,7 +272,7 @@ func XTestDelivery(t *testing.T) { } q := newQueue(10, 10) var wg sync.WaitGroup - q.Prepare(1, FastSync) + q.Prepare(1, SnapSync) wg.Add(1) go func() { // deliver headers diff --git a/eth/downloader/skeleton.go b/eth/downloader/skeleton.go new file mode 100644 index 000000000000..698acfc9c1dd --- /dev/null +++ b/eth/downloader/skeleton.go @@ -0,0 +1,958 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package downloader + +import ( + "encoding/json" + "errors" + "math/rand" + "sort" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/protocols/eth" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" +) + +// scratchHeaders is the number of headers to store in a scratch space to allow +// concurrent downloads. A header is about 0.5KB in size, so there is no worry +// about using too much memory. The only catch is that we can only validate gaps +// afer they're linked to the head, so the bigger the scratch space, the larger +// potential for invalid headers. +// +// The current scratch space of 131072 headers is expected to use 64MB RAM. +const scratchHeaders = 131072 + +// requestHeaders is the number of header to request from a remote peer in a single +// network packet. Although the skeleton downloader takes into consideration peer +// capacities when picking idlers, the packet size was decided to remain constant +// since headers are relatively small and it's easier to work with fixed batches +// vs. dynamic interval fillings. +const requestHeaders = 512 + +// errSyncLinked is an internal helper error to signal that the current sync +// cycle linked up to the genesis block, this the skeleton syncer should ping +// the backfiller to resume. Since we already have that logic on sync start, +// piggie-back on that instead of 2 entrypoints. +var errSyncLinked = errors.New("sync linked") + +// errSyncMerged is an internal helper error to signal that the current sync +// cycle merged with a previously aborted subchain, thus the skeleton syncer +// should abort and restart with the new state. +var errSyncMerged = errors.New("sync merged") + +// errSyncReorged is an internal helper error to signal that the head chain of +// the current sync cycle was (partially) reorged, thus the skeleton syncer +// should abort and restart with the new state. +var errSyncReorged = errors.New("sync reorged") + +// errTerminated is returned if the sync mechanism was terminated for this run of +// the process. This is usually the case when Geth is shutting down and some events +// might still be propagating. +var errTerminated = errors.New("terminated") + +func init() { + // Tuning parameters is nice, but the scratch space must be assignable in + // full to peers. It's a useless cornercase to support a dangling half-group. + if scratchHeaders%requestHeaders != 0 { + panic("Please make scratchHeaders divisible by requestHeaders") + } +} + +// subchain is a contiguous header chain segment that is backed by the database, +// but may not be linked to the live chain. The skeleton downloader may produce +// a new one of these every time it is restarted until the subchain grows large +// enough to connect with a previous subchain. +// +// The subchains use the exact same database namespace and are not disjoint from +// each other. As such, extending one to overlap the other entails reducing the +// second one first. This combined buffer model is used to avoid having to move +// data on disk when two subchains are joined together. +type subchain struct { + Head uint64 // Block number of the newest header in the subchain + Tail uint64 // Block number of the oldest header in the subchain + Next common.Hash // Block hash of the next oldest header in the subchain +} + +// skeletonProgress is a database entry to allow suspending and resuming a chain +// sync. As the skeleton header chain is downloaded backwards, restarts can and +// will produce temporarilly disjoint subchains. There is no way to restart a +// suspended skeleton sync without prior knowlege of all prior suspension points. +type skeletonProgress struct { + Subchains []*subchain // Disjoint subchains downloaded until now +} + +// headerRequest tracks a pending header request to ensure responses are to +// actual requests and to validate any security constraints. +// +// Concurrency note: header requests and responses are handled concurrently from +// the main runloop to allow Keccak256 hash verifications on the peer's thread and +// to drop on invalid response. The request struct must contain all the data to +// construct the response without accessing runloop internals (i.e. subchains). +// That is only included to allow the runloop to match a response to the task being +// synced without having yet another set of maps. +type headerRequest struct { + peer string // Peer to which this request is assigned + id uint64 // Request ID of this request + + deliver chan *headerResponse // Channel to deliver successful response on + revert chan *headerRequest // Channel to deliver request failure on + cancel chan struct{} // Channel to track sync cancellation + stale chan struct{} // Channel to signal the request was dropped + + head uint64 // Head number of the requested batch of headers +} + +// headerResponse is an already verified remote response to a header request. +type headerResponse struct { + peer *peerConnection // Peer from which this response originates + reqid uint64 // Request ID that this response fulfils + headers []*types.Header // Chain of headers +} + +// backfiller is a callback interface through which the skeleton sync can tell +// the downloader that it should suspend or resume backfilling on specific head +// events (e.g. suspend on forks or gaps, resume on successfull linkups). +type backfiller interface { + // suspend requests the backfiller to abort any running full or snap sync + // based on the skeleton chain as it might be invalid. The backfiller should + // gracefully handle multiple consecutive suspends without a resume, even + // on initial sartup. + suspend() + + // resume requests the backfiller to start running fill or snap sync based on + // the skeleton chain as it has successfully been linked. Appending new heads + // to the end of the chain will not result in suspend/resume cycles. + resume() +} + +// skeleton represents a header chain synchronized after the Ethereum 2 merge, +// where blocks aren't validated any more via PoW in a forward fashion, rather +// are dictated and extended at the head via the beacon chain and backfilled on +// the original Ethereum 1 block sync protocol. +// +// Since the skeleton is grown backwards from head to genesis, it is handled as +// a separate entity, not mixed in with the logical sequential transition of the +// blocks. Once the skeleton is connected to an existing, validated chain, the +// headers will be moved into the main downloader for filling and execution. +// +// Opposed to the Ethereum 1 block synchronization which is trustless (and uses a +// master peer to minimize the attack surface), Ethereum 2 block synchronization +// starts from a trusted head. As such, there is no need for a master peer any +// more and headers can be requested fully concurrently (though some batches might +// be discarded if they don't link up correctly). +// +// Although a skeleton is part of a sync cycle, it is not recreated, rather stays +// alive throughout the lifetime of the downloader. This allows it to be extended +// concurrently with the sync cycle, since extensions arrive from an API surface, +// not from within (vs. Ethereum 1 sync). +// +// Since the skeleton tracks the entire header chain until it is cosumed by the +// forward block filling, it needs 0.5KB/block storage. At current mainnet sizes +// this is only possible with a disk backend. Since the skeleton is separate from +// the node's header chain, storing the headers ephemerally until sync finishes +// is wasted disk IO, but it's a price we're going to pay to keep things simple +// for now. +type skeleton struct { + db ethdb.Database // Database backing the skeleton + filler backfiller // Chain syncer suspended/resumed by head events + + peers *peerSet // Set of peers we can sync from + idles map[string]*peerConnection // Set of idle peers in the current sync cycle + drop peerDropFn // Drops a peer for misbehaving + + progress *skeletonProgress // Sync progress tracker for resumption and metrics + started time.Time // Timestamp when the skeleton syncer was created + logged time.Time // Timestamp when progress was last logged to the user + pulled uint64 // Number of headers downloaded in this run + + scratchSpace []*types.Header // Scratch space to accumulate headers in (first = recent) + scratchOwners []string // Peer IDs owning chunks of the scratch space (pend or delivered) + scratchHead uint64 // Block number of the first item in the scratch space + + requests map[uint64]*headerRequest // Header requests currently running + + headEvents chan *types.Header // Notification channel for new heads + terminate chan chan error // Termination channel to abort sync + terminated chan struct{} // Channel to signal that the syner is dead + + // Callback hooks used during testing + syncStarting func() // callback triggered after a sync cycle is inited but before started +} + +// newSkeleton creates a new sync skeleton that tracks a potentially dangling +// header chain until it's linked into an existing set of blocks. +func newSkeleton(db ethdb.Database, peers *peerSet, drop peerDropFn, filler backfiller) *skeleton { + sk := &skeleton{ + db: db, + filler: filler, + peers: peers, + drop: drop, + requests: make(map[uint64]*headerRequest), + headEvents: make(chan *types.Header), + terminate: make(chan chan error), + terminated: make(chan struct{}), + } + go sk.startup() + return sk +} + +// startup is an initial background loop which waits for an event to start or +// tear the syncer down. This is required to make the skeleton sync loop once +// per process but at the same time not start before the beacon chain announces +// a new (existing) head. +func (s *skeleton) startup() { + // Close a notification channel so anyone sending us events will know if the + // sync loop was torn down for good. + defer close(s.terminated) + + // Wait for startup or teardown + select { + case errc := <-s.terminate: + // No head was announced but Geth is shutting down + errc <- nil + return + + case head := <-s.headEvents: + // New head announced, start syncing to it, looping every time a current + // cycle is terminated due to a chain event (head reorg, old chain merge) + s.started = time.Now() + + for { + // If the sync cycle terminated or was terminated, propagate up when + // higher layers request termination. There's no fancy explicit error + // signalling as the sync loop should never terminate (TM). + newhead, err := s.sync(head) + switch { + case err == errSyncLinked: + // Sync cycle linked up to the genesis block. Tear down the loop + // and restart it so, it can properly notify the backfiller. Don't + // account a new head. + head = nil + + case err == errSyncMerged: + // Subchains were merged, we just need to reinit the internal + // start to continue on the tail of the merged chain. Don't + // announce a new head, + head = nil + + case err == errSyncReorged: + // The subchain being synced got modified at the head in a + // way that requires resyncing it. Restart sync with the new + // head to force a cleanup. + head = newhead + + case err == errTerminated: + // Sync was requested to be terminated from within, stop and + // return (no need to pass a message, was already done internally) + return + + default: + // Sync either successfully terminated or failed with an unhandled + // error. Abort and wait until Geth requests a termination. + errc := <-s.terminate + errc <- err + return + } + } + } +} + +// Terminate tears down the syncer indefinitely. +func (s *skeleton) Terminate() error { + // Request termination and fetch any errors + errc := make(chan error) + s.terminate <- errc + err := <-errc + + // Wait for full shutdown (not necessary, but cleaner) + <-s.terminated + return err +} + +// Sync starts or resumes a previous sync cycle to download and maintain a reverse +// header chain starting at the head and leading towards genesis to an available +// ancestor. +// +// This method does not block, rather it just waits until the syncer receives the +// fed header. What the syncer does with it is the syncer's problem. +func (s *skeleton) Sync(head *types.Header) error { + select { + case s.headEvents <- head: + return nil + case <-s.terminated: + return errTerminated + } +} + +// sync is the internal version of Sync that executes a single sync cycle, either +// until some termination condition is reached, or until the current cycle merges +// with a previously aborted run. +func (s *skeleton) sync(head *types.Header) (*types.Header, error) { + // If we're continuing a previous merge interrupt, just access the existing + // old state without initing from disk. + if head == nil { + head = rawdb.ReadSkeletonHeader(s.db, s.progress.Subchains[0].Head) + } else { + // Otherwise, initialize the sync, trimming and previous leftovers until + // we're consistent with the newly requested chain head + s.initSync(head) + } + // Create the scratch space to fill with concurrently downloaded headers + s.scratchSpace = make([]*types.Header, scratchHeaders) + defer func() { s.scratchSpace = nil }() // don't hold on to references after sync + + s.scratchOwners = make([]string, scratchHeaders/requestHeaders) + defer func() { s.scratchOwners = nil }() // don't hold on to references after sync + + s.scratchHead = s.progress.Subchains[0].Tail - 1 // tail must not be 0! + + // If the sync is already done, resume the backfiller. When the loop stops, + // terminate the backfiller too. + if s.scratchHead == 0 { + s.filler.resume() + } + defer s.filler.suspend() + + // Create a set of unique channels for this sync cycle. We need these to be + // ephemeral so a data race doesn't accidentally deliver something stale on + // a persistent channel across syncs (yup, this happened) + var ( + requestFails = make(chan *headerRequest) + responses = make(chan *headerResponse) + ) + cancel := make(chan struct{}) + defer close(cancel) + + log.Debug("Starting reverse header sync cycle", "head", head.Number, "hash", head.Hash(), "cont", s.scratchHead) + + // Whether sync completed or not, disregard any future packets + defer func() { + log.Debug("Terminating reverse header sync cycle", "head", head.Number, "hash", head.Hash(), "cont", s.scratchHead) + s.requests = make(map[uint64]*headerRequest) + }() + + // Start tracking idle peers for task assignments + peering := make(chan *peeringEvent, 64) // arbitrary buffer, just some burst protection + + peeringSub := s.peers.SubscribeEvents(peering) + defer peeringSub.Unsubscribe() + + s.idles = make(map[string]*peerConnection) + for _, peer := range s.peers.AllPeers() { + s.idles[peer.id] = peer + } + // Nofity any tester listening for startup events + if s.syncStarting != nil { + s.syncStarting() + } + for { + // Something happened, try to assign new tasks to any idle peers + s.assingTasks(responses, requestFails, cancel) + + // Wait for something to happen + select { + case event := <-peering: + // A peer joined or left, the tasks queue and allocations need to be + // checked for potential assignment or reassignment + peerid := event.peer.id + if event.join { + s.idles[peerid] = event.peer + } else { + s.revertRequests(peerid) + delete(s.idles, peerid) + } + + case errc := <-s.terminate: + errc <- nil + return nil, errTerminated + + case head := <-s.headEvents: + // New head was announced, try to integrate it. If successful, nothing + // needs to be done as the head simply extended the last range. For now + // we don't seamlessly integrate reorgs to keep things simple. If the + // network starts doing many mini reorgs, it might be worthwhile handling + // a limited depth without an error. + if reorged := s.processNewHead(head); reorged { + return head, errSyncReorged + } + + case req := <-requestFails: + s.revertRequest(req) + + case res := <-responses: + // Process the batch of headers. If though processing we managed to + // link the curret subchain to a previously downloaded one, abort the + // sync and restart with the merged subchains. We could probably hack + // the internal state to switch the scratch space over to the tail of + // the extended subchain, but since the scenario is rare, it's cleaner + // to rely on the restart mechanism than a stateful modification. + if merged := s.processResponse(res); merged { + return nil, errSyncMerged + } + // If we've just reached the genesis block, tear down the sync cycle + // and restart it to resume the backfiller. We could just as well do + // a signalling here, but it's a tad cleaner to have only one entry + // pathway to suspending/resuming it. + return nil, errSyncLinked + } + } +} + +// initSync attempts to get the skeleton sync into a consistent state wrt any +// past state on disk and the newly requested head to sync to. If the new head +// is nil, the method will return and continue from the previous head. +func (s *skeleton) initSync(head *types.Header) { + // Extract the head number, we'll need it all over + number := head.Number.Uint64() + + // Retrieve the previously saved sync progress + if status := rawdb.ReadSkeletonSyncStatus(s.db); len(status) > 0 { + s.progress = new(skeletonProgress) + if err := json.Unmarshal(status, s.progress); err != nil { + log.Error("Failed to decode skeleton sync status", "err", err) + } else { + // Previous sync was available, print some continuation logs + for _, subchain := range s.progress.Subchains { + log.Debug("Restarting skeleton subchain", "head", subchain.Head, "tail", subchain.Tail) + } + // Create a new subchain for the head (unless the last can be extended), + // trimming anything it would overwrite + headchain := &subchain{ + Head: number, + Tail: number, + Next: head.ParentHash, + } + for len(s.progress.Subchains) > 0 { + // If the last chain is above the new head, delete altogether + lastchain := s.progress.Subchains[0] + if lastchain.Tail >= headchain.Tail { + log.Debug("Dropping skeleton subchain", "head", lastchain.Head, "tail", lastchain.Tail) + s.progress.Subchains = s.progress.Subchains[1:] + continue + } + // Otherwise truncate the last chain if needed and abort trimming + if lastchain.Head >= headchain.Tail { + log.Debug("Trimming skeleton subchain", "oldhead", lastchain.Head, "newhead", headchain.Tail-1, "tail", lastchain.Tail) + lastchain.Head = headchain.Tail - 1 + } + break + } + // If the last subchain can be extended, we're lucky. Otherwise create + // a new subchain sync task. + var extended bool + if n := len(s.progress.Subchains); n > 0 { + lastchain := s.progress.Subchains[0] + if lastchain.Head == headchain.Tail-1 { + lasthead := rawdb.ReadSkeletonHeader(s.db, lastchain.Head) + if lasthead.Hash() == head.ParentHash { + log.Debug("Extended skeleton subchain with new head", "head", headchain.Tail, "tail", lastchain.Tail) + lastchain.Head = headchain.Tail + extended = true + } + } + } + if !extended { + log.Debug("Created new skeleton subchain", "head", number, "tail", number) + s.progress.Subchains = append([]*subchain{headchain}, s.progress.Subchains...) + } + // Update the database with the new sync stats and insert the new + // head header. We won't delete any trimmed skeleton headers since + // those will be outside the index space of the many subchains and + // the database space will be reclaimed eventually when processing + // blocks above the current head (TODO(karalabe): don't forget). + batch := s.db.NewBatch() + + rawdb.WriteSkeletonHeader(batch, head) + s.saveSyncStatus(batch) + + if err := batch.Write(); err != nil { + log.Crit("Failed to write skeleton sync status", "err", err) + } + return + } + } + // Either we've failed to decode the previus state, or there was none. Start + // a fresh sync with a single subchain represented by the currently sent + // chain head. + s.progress = &skeletonProgress{ + Subchains: []*subchain{ + { + Head: number, + Tail: number, + Next: head.ParentHash, + }, + }, + } + batch := s.db.NewBatch() + + rawdb.WriteSkeletonHeader(batch, head) + s.saveSyncStatus(batch) + + if err := batch.Write(); err != nil { + log.Crit("Failed to write initial skeleton sync status", "err", err) + } + log.Debug("Created initial skeleton subchain", "head", number, "tail", number) +} + +// saveSyncStatus marshals the remaining sync tasks into leveldb. +func (s *skeleton) saveSyncStatus(db ethdb.KeyValueWriter) { + status, err := json.Marshal(s.progress) + if err != nil { + panic(err) // This can only fail during implementation + } + rawdb.WriteSkeletonSyncStatus(db, status) +} + +// processNewHead does the internal shuffling for a new head marker and either +// accepts and integrates it into the skeleton or requests a reorg. Upon reorg, +// the syncer will tear itself down and restart with a fresh head. It is simpler +// to reconstruct the sync state than to mutate it and hope for the best. +func (s *skeleton) processNewHead(head *types.Header) bool { + // If the header cannot be inserted without interruption, return an error for + // the outer loop to tear down the skeleton sync and restart it + number := head.Number.Uint64() + + lastchain := s.progress.Subchains[0] + if lastchain.Tail >= number { + log.Warn("Beacon chain reorged", "tail", lastchain.Tail, "newHead", number) + return true + } + if lastchain.Head+1 < number { + log.Warn("Beacon chain gapped", "head", lastchain.Head, "newHead", number) + return true + } + if parent := rawdb.ReadSkeletonHeader(s.db, number-1); parent.Hash() != head.ParentHash { + log.Warn("Beacon chain forked", "ancestor", parent.Number, "hash", parent.Hash(), "want", head.ParentHash) + return true + } + // New header seems to be in the last subchain range. Unwind any extra headers + // from the chain tip and insert the new head. We won't delete any trimmed + // skeleton headers since those will be outside the index space of the many + // subchains and the database space will be reclaimed eventually when processing + // blocks above the current head (TODO(karalabe): don't forget). + batch := s.db.NewBatch() + + rawdb.WriteSkeletonHeader(batch, head) + lastchain.Head = number + s.saveSyncStatus(batch) + + if err := batch.Write(); err != nil { + log.Crit("Failed to write skeleton sync status", "err", err) + } + return false +} + +// assingTasks attempts to match idle peers to pending header retrievals. +func (s *skeleton) assingTasks(success chan *headerResponse, fail chan *headerRequest, cancel chan struct{}) { + // Sort the peers by download capacity to use faster ones if many available + idlers := &peerCapacitySort{ + peers: make([]*peerConnection, 0, len(s.idles)), + caps: make([]int, 0, len(s.idles)), + } + targetTTL := s.peers.rates.TargetTimeout() + for _, peer := range s.idles { + idlers.peers = append(idlers.peers, peer) + idlers.caps = append(idlers.caps, s.peers.rates.Capacity(peer.id, eth.BlockHeadersMsg, targetTTL)) + } + if len(idlers.peers) == 0 { + return + } + sort.Sort(idlers) + + // Find header regions not yet downloading and fill them + for task, owner := range s.scratchOwners { + // If we're out of idle peers, stop assigning tasks + if len(idlers.peers) == 0 { + return + } + // Skip any tasks already filling + if owner != "" { + continue + } + // If we've reached the genesis, stop assigning tasks + if uint64(task*requestHeaders) >= s.scratchHead { + return + } + // Found a task and have peers available, assign it + idle := idlers.peers[0] + + idlers.peers = idlers.peers[1:] + idlers.caps = idlers.caps[1:] + + // Matched a pending task to an idle peer, allocate a unique request id + var reqid uint64 + for { + reqid = uint64(rand.Int63()) + if reqid == 0 { + continue + } + if _, ok := s.requests[reqid]; ok { + continue + } + break + } + // Generate the network query and send it to the peer + req := &headerRequest{ + peer: idle.id, + id: reqid, + deliver: success, + revert: fail, + cancel: cancel, + stale: make(chan struct{}), + head: s.scratchHead - uint64(task*requestHeaders), + } + s.requests[reqid] = req + delete(s.idles, idle.id) + + // Generate the network query and send it to the peer + go s.executeTask(idle, req) + + // Inject the request into the task to block further assignments + s.scratchOwners[task] = idle.id + } +} + +// executeTask executes a single fetch request, blocking until either a result +// arrives or a timeouts / cancellation is triggered. The method should be run +// on its own goroutine and will deliver on the requested channels. +func (s *skeleton) executeTask(peer *peerConnection, req *headerRequest) { + start := time.Now() + resCh := make(chan *eth.Response) + + // Figure out how many headers to fetch. Usually this will be a full batch, + // but for the very tail of the chain, trim the request to the number left. + // Since nodes may or may not return the genesis header for a batch request, + // don't even request it. The parent hash of block #1 is enough to link. + requestCount := requestHeaders + if req.head < requestHeaders { + requestCount = int(req.head) + } + peer.log.Trace("Fetching skeleton headers", "from", req.head, "count", requestCount) + netreq, err := peer.peer.RequestHeadersByNumber(req.head, requestCount, 0, true, resCh) + if err != nil { + peer.log.Trace("Failed to request headers", "err", err) + s.scheduleRevertRequest(req) + return + } + defer netreq.Close() + + // Wait until the response arrives, the request is cancelled or times out + ttl := s.peers.rates.TargetTimeout() + + timeoutTimer := time.NewTimer(ttl) + defer timeoutTimer.Stop() + + select { + case <-req.cancel: + peer.log.Debug("Header request cancelled") + s.scheduleRevertRequest(req) + + case <-timeoutTimer.C: + // Header retrieval timed out, update the metrics + peer.log.Trace("Header request timed out", "elapsed", ttl) + headerTimeoutMeter.Mark(1) + s.peers.rates.Update(peer.id, eth.BlockHeadersMsg, 0, 0) + s.scheduleRevertRequest(req) + + case res := <-resCh: + // Headers successfully retrieved, update the metrics + headers := *res.Res.(*eth.BlockHeadersPacket) + + headerReqTimer.Update(time.Since(start)) + s.peers.rates.Update(peer.id, eth.BlockHeadersMsg, res.Time, len(headers)) + + // Cross validate the headers with the requests + switch { + case len(headers) == 0: + // No headers were delivered, reject the response and reschedule + peer.log.Debug("No headers delivered") + res.Done <- errors.New("no headers delivered") + s.scheduleRevertRequest(req) + + case headers[0].Number.Uint64() != req.head: + // Header batch anchored at non-requested number + peer.log.Debug("Invalid header response head", "have", headers[0].Number, "want", req.head) + res.Done <- errors.New("invalid header batch anchor") + s.scheduleRevertRequest(req) + + case headers[0].Number.Uint64() >= requestHeaders && len(headers) != requestHeaders: + // Invalid number of non-genesis headers delivered, reject the response and reschedule + peer.log.Debug("Invalid non-genesis header count", "have", len(headers), "want", requestHeaders) + res.Done <- errors.New("not enough non-genesis headers delivered") + s.scheduleRevertRequest(req) + + case headers[0].Number.Uint64() < requestHeaders && uint64(len(headers)) != headers[0].Number.Uint64(): + // Invalid number of genesis headers delivered, reject the response and reschedule + peer.log.Debug("Invalid genesis header count", "have", len(headers), "want", headers[0].Number.Uint64()) + res.Done <- errors.New("not enough genesis headers delivered") + s.scheduleRevertRequest(req) + + default: + // Packet seems structurally valid, check hash progression and if it + // is correct too, deliver for storage + for i := 0; i < len(headers)-1; i++ { + if headers[i].ParentHash != headers[i+1].Hash() { + peer.log.Debug("Invalid genesis header count", "have", len(headers), "want", headers[0].Number.Uint64()) + res.Done <- errors.New("not enough genesis headers delivered") + s.scheduleRevertRequest(req) + return + } + } + // Hash chain is valid. The delivery might still be junk as we're + // downloading batches concurrently (so no way to link the headers + // until gaps are filled); in that case, we'll nuke the peer when + // we detect the fault. + res.Done <- nil + + select { + case req.deliver <- &headerResponse{ + peer: peer, + reqid: req.id, + headers: headers, + }: + case <-req.cancel: + } + } + } +} + +// revertRequests locates all the currently pending reuqests from a particular +// peer and reverts them, rescheduling for others to fulfill. +func (s *skeleton) revertRequests(peer string) { + // Gather the requests first, revertals need the lock too + var requests []*headerRequest + for _, req := range s.requests { + if req.peer == peer { + requests = append(requests, req) + } + } + // Revert all the requests matching the peer + for _, req := range requests { + s.revertRequest(req) + } +} + +// scheduleRevertRequest asks the event loop to clean up a request and return +// all failed retrieval tasks to the scheduler for reassignment. +func (s *skeleton) scheduleRevertRequest(req *headerRequest) { + select { + case req.revert <- req: + // Sync event loop notified + case <-req.cancel: + // Sync cycle got cancelled + case <-req.stale: + // Request already reverted + } +} + +// revertRequest cleans up a request and returns all failed retrieval tasks to +// the scheduler for reassignment. +// +// Note, this needs to run on the event runloop thread to reschedule to idle peers. +// On peer threads, use scheduleRevertRequest. +func (s *skeleton) revertRequest(req *headerRequest) { + log.Trace("Reverting header request", "peer", req.peer, "reqid", req.id) + select { + case <-req.stale: + log.Trace("Header request already reverted", "peer", req.peer, "reqid", req.id) + return + default: + } + close(req.stale) + + // Remove the request from the tracked set + delete(s.requests, req.id) + + // Remove the request from the tracked set and mark the task as not-pending, + // ready for resheduling + s.scratchOwners[(s.scratchHead-req.head)/requestHeaders] = "" +} + +func (s *skeleton) processResponse(res *headerResponse) bool { + res.peer.log.Trace("Processing header response", "head", res.headers[0].Number, "hash", res.headers[0].Hash(), "count", len(res.headers)) + + // Whether or not the response is valid, we can mark the peer as idle and + // notify the scheduler to assign a new task. If the response is invalid, + // we'll drop the peer in a bit. + s.idles[res.peer.id] = res.peer + + // Ensure the response is for a valid request + if _, ok := s.requests[res.reqid]; !ok { + // Request stale, perhaps the peer timed out but came through in the end + res.peer.log.Warn("Unexpected header packet") + return false + } + delete(s.requests, res.reqid) + + // Insert the delivered headers into the scratch space independent of the + // content or continuation; those will be validated in a moment + head := res.headers[0].Number.Uint64() + copy(s.scratchSpace[s.scratchHead-head:], res.headers) + + // If there's still a gap in the head of the scratch space, abort + if s.scratchSpace[0] == nil { + return false + } + // Try to consume any head headers, validating the boundary conditions + var merged bool // Whether subchains were merged + + batch := s.db.NewBatch() + for s.scratchSpace[0] != nil { + // Next batch of headers available, cross-reference with the subchain + // we are extending and either accept or discard + if s.progress.Subchains[0].Next != s.scratchSpace[0].Hash() { + // Print a log messages to track what's going on + tail := s.progress.Subchains[0].Tail + want := s.progress.Subchains[0].Next + have := s.scratchSpace[0].Hash() + + log.Warn("Invalid skeleton headers", "peer", s.scratchOwners[0], "number", tail-1, "want", want, "have", have) + + // The peer delivered junk, or at least not the subchain we are + // syncing to. Free up the scratch space and assignment, reassign + // and drop the original peer. + for i := 0; i < requestHeaders; i++ { + s.scratchSpace[i] = nil + } + s.drop(s.scratchOwners[0]) + s.scratchOwners[0] = "" + break + } + // Scratch delivery matches required subchain, deliver the batch of + // headers and push the subchain forward + var consumed int + for _, header := range s.scratchSpace[:requestHeaders] { + if header != nil { // nil when the genesis is reached + consumed++ + + rawdb.WriteSkeletonHeader(batch, header) + s.pulled++ + + s.progress.Subchains[0].Tail-- + s.progress.Subchains[0].Next = header.ParentHash + } + } + // Batch of headers consumed, shift the download window forward + head := s.progress.Subchains[0].Head + tail := s.progress.Subchains[0].Tail + next := s.progress.Subchains[0].Next + + log.Trace("Primary subchain extended", "head", head, "tail", tail, "next", next) + + copy(s.scratchSpace, s.scratchSpace[requestHeaders:]) + for i := 0; i < requestHeaders; i++ { + s.scratchSpace[scratchHeaders-i-1] = nil + } + copy(s.scratchOwners, s.scratchOwners[1:]) + s.scratchOwners[scratchHeaders/requestHeaders-1] = "" + + s.scratchHead -= uint64(consumed) + + // If the subchain extended into the next subchain, we need to handle + // the overlap. Since there could be many overlaps (come on), do this + // in a loop. + for len(s.progress.Subchains) > 1 && s.progress.Subchains[1].Head >= s.progress.Subchains[0].Tail { + // Extract some stats from the second subchain + head := s.progress.Subchains[1].Head + tail := s.progress.Subchains[1].Tail + next := s.progress.Subchains[1].Next + + // Since we just overwrote part of the next subchain, we need to trim + // its head independent of matching or mismatching content + if s.progress.Subchains[1].Tail >= s.progress.Subchains[0].Tail { + // Fully overwritten, get rid of the subchain as a whole + log.Debug("Previous subchain fully overwritten", "head", head, "tail", tail, "next", next) + s.progress.Subchains = append(s.progress.Subchains[:1], s.progress.Subchains[2:]...) + continue + } else { + // Partially overwritten, trim the head to the overwritten size + log.Debug("Previous subchain partially overwritten", "head", head, "tail", tail, "next", next) + s.progress.Subchains[1].Head = s.progress.Subchains[0].Tail - 1 + } + // If the old subchain is an extension of the new one, merge the two + // and let the skeleton syncer restart (to clean internal state) + if rawdb.ReadSkeletonHeader(s.db, s.progress.Subchains[1].Head).Hash() == s.progress.Subchains[0].Next { + log.Debug("Previous subchain merged", "head", head, "tail", tail, "next", next) + s.progress.Subchains[0].Tail = s.progress.Subchains[1].Tail + s.progress.Subchains[0].Next = s.progress.Subchains[1].Next + + s.progress.Subchains = append(s.progress.Subchains[:1], s.progress.Subchains[2:]...) + merged = true + } + } + } + s.saveSyncStatus(batch) + if err := batch.Write(); err != nil { + log.Crit("Failed to write skeleton headers and progress", "err", err) + } + // Print a progress report to make the UX a bit nicer + left := s.progress.Subchains[0].Tail - 1 + if time.Since(s.logged) > 8*time.Second || left == 0 { + s.logged = time.Now() + + if s.pulled == 0 { + log.Info("Beacon sync starting", "left", left) + } else { + eta := float64(time.Since(s.started)) / float64(s.pulled) * float64(left) + log.Info("Syncing beacon headers", "downloaded", s.pulled, "left", left, "eta", common.PrettyDuration(eta)) + } + } + return merged +} + +// Head retrieves the current head tracked by the skeleton syncer. This method +// is meant to be used by the backfiller, whose life cycle is controlled by the +// skeleton syncer. +// +// Note, the method will not use the internal state of the skeleton, but will +// rather blindly pull stuff from the database. This is fine, because the back- +// filler will only run when the skeleton chain is fully downloaded and stable. +// There might be new heads appended, but those are stomic from the perspective +// of this method. Any head reorg will first tear down the backfiller and only +// then make the modification. +func (s *skeleton) Head() (*types.Header, error) { + // Read the current sync progress from disk and figure out the current head. + // Although there's a lot of error handling here, these are mostly as sanity + // checks to avoid crashing if a programming error happens. These should not + // happen in live code. + status := rawdb.ReadSkeletonSyncStatus(s.db) + if len(status) == 0 { + return nil, errors.New("beacon sync not yet started") + } + s.progress = new(skeletonProgress) + if err := json.Unmarshal(status, s.progress); err != nil { + return nil, err + } + if s.progress.Subchains[0].Tail != 1 { + return nil, errors.New("beacon sync not yet finished") + } + return rawdb.ReadSkeletonHeader(s.db, s.progress.Subchains[0].Head), nil +} + +// Header retrieves a specific header tracked by the skeleton syncer. This method +// is meant to be used by the backfiller, whose life cycle is controlled by the +// skeleton syncer. +// +// Note, outside the permitted runtimes, this method might return nil results and +// subsequent calls might return headers from different chains. +func (s *skeleton) Header(number uint64) *types.Header { + return rawdb.ReadSkeletonHeader(s.db, number) +} diff --git a/eth/downloader/skeleton_test.go b/eth/downloader/skeleton_test.go new file mode 100644 index 000000000000..7c2e07a43866 --- /dev/null +++ b/eth/downloader/skeleton_test.go @@ -0,0 +1,257 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package downloader + +import ( + "encoding/json" + "math/big" + "os" + "testing" + + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" +) + +// hookedBackfiller is a tester backfiller with all interface methods mocked and +// hooked so tests can implement only the things they need. +type hookedBackfiller struct { + // suspendHook is an optional hook to be called when the filler is requested + // to be suspended. + suspendHook func() + + // resumeHook is an optional hook to be called when the filler is requested + // to be resumed. + resumeHook func() +} + +// suspend requests the backfiller to abort any running full or snap sync +// based on the skeleton chain as it might be invalid. The backfiller should +// gracefully handle multiple consecutive suspends without a resume, even +// on initial sartup. +func (hf *hookedBackfiller) suspend() { + if hf.suspendHook != nil { + hf.suspendHook() + } +} + +// resume requests the backfiller to start running fill or snap sync based on +// the skeleton chain as it has successfully been linked. Appending new heads +// to the end of the chain will not result in suspend/resume cycles. +func (hf *hookedBackfiller) resume() { + if hf.resumeHook != nil { + hf.resumeHook() + } +} + +// newNoopBackfiller creates a hooked backfiller with all callbacks disabled, +// essentially acting as a noop. +func newNoopBackfiller() backfiller { + return new(hookedBackfiller) +} + +// Tests various sync initialzations based on previous leftovers in the database +// and announced heads. +func TestSkeletonSyncInit(t *testing.T) { + log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + + // Create a few key headers + var ( + genesis = &types.Header{Number: big.NewInt(0)} + block49 = &types.Header{Number: big.NewInt(49)} + block49B = &types.Header{Number: big.NewInt(49), Extra: []byte("B")} + block50 = &types.Header{Number: big.NewInt(50), ParentHash: block49.Hash()} + ) + tests := []struct { + headers []*types.Header // Database content (beside the genesis) + oldstate []*subchain // Old sync state with various interrupted subchains + head *types.Header // New head header to announce to reorg to + newstate []*subchain // Expected sync state after the reorg + }{ + // Completely empty database with only the genesis set. The sync is expected + // to create a single subchain with the requested head. + { + head: block50, + newstate: []*subchain{{Head: 50, Tail: 50}}, + }, + // Empty database with only the genesis set with a leftover empty sync + // progess. This is a synthetic case, just for the sake of covering things. + { + oldstate: []*subchain{}, + head: block50, + newstate: []*subchain{{Head: 50, Tail: 50}}, + }, + // A single leftover subchain is present, older than the new head. The + // old subchain should be left as is and a new one appended to the sync + // status. + { + oldstate: []*subchain{{Head: 10, Tail: 5}}, + head: block50, + newstate: []*subchain{ + {Head: 50, Tail: 50}, + {Head: 10, Tail: 5}, + }, + }, + // Multiple leftover subchains are present, older than the new head. The + // old subchains should be left as is and a new one appended to the sync + // status. + { + oldstate: []*subchain{ + {Head: 20, Tail: 15}, + {Head: 10, Tail: 5}, + }, + head: block50, + newstate: []*subchain{ + {Head: 50, Tail: 50}, + {Head: 20, Tail: 15}, + {Head: 10, Tail: 5}, + }, + }, + // A single leftover subchain is present, newer than the new head. The + // newer subchain should be deleted and a fresh one created for the head. + { + oldstate: []*subchain{{Head: 65, Tail: 60}}, + head: block50, + newstate: []*subchain{{Head: 50, Tail: 50}}, + }, + // Multiple leftover subchain is present, newer than the new head. The + // newer subchains should be deleted and a fresh one created for the head. + { + oldstate: []*subchain{ + {Head: 75, Tail: 70}, + {Head: 65, Tail: 60}, + }, + head: block50, + newstate: []*subchain{{Head: 50, Tail: 50}}, + }, + + // Two leftover subchains are present, one fully older and one fully + // newer than the announced head. The head should delete the newer one, + // keeping the older one. + { + oldstate: []*subchain{ + {Head: 65, Tail: 60}, + {Head: 10, Tail: 5}, + }, + head: block50, + newstate: []*subchain{ + {Head: 50, Tail: 50}, + {Head: 10, Tail: 5}, + }, + }, + // Multiple leftover subchains are present, some fully older and some + // fully newer than the announced head. The head should delete the newer + // ones, keeping the older ones. + { + oldstate: []*subchain{ + {Head: 75, Tail: 70}, + {Head: 65, Tail: 60}, + {Head: 20, Tail: 15}, + {Head: 10, Tail: 5}, + }, + head: block50, + newstate: []*subchain{ + {Head: 50, Tail: 50}, + {Head: 20, Tail: 15}, + {Head: 10, Tail: 5}, + }, + }, + // A single leftover subchain is present and the new head is extending + // it with one more header. We expect the subchain head to be pushed + // forward. + { + headers: []*types.Header{block49}, + oldstate: []*subchain{{Head: 49, Tail: 5}}, + head: block50, + newstate: []*subchain{{Head: 50, Tail: 5}}, + }, + // A single leftover subchain is present and although the new head does + // extend it number wise, the hash chain does not link up. We expect a + // new subchain to be created for the dangling head. + { + headers: []*types.Header{block49B}, + oldstate: []*subchain{{Head: 49, Tail: 5}}, + head: block50, + newstate: []*subchain{ + {Head: 50, Tail: 50}, + {Head: 49, Tail: 5}, + }, + }, + // A single leftover subchain is present. A new head is announced that + // links into the middle of it, correctly anchoring into an existing + // header. We expect the old subchain to be truncated and extended with + // the new head. + { + headers: []*types.Header{block49}, + oldstate: []*subchain{{Head: 100, Tail: 5}}, + head: block50, + newstate: []*subchain{{Head: 50, Tail: 5}}, + }, + // A single leftover subchain is present. A new head is announced that + // links into the middle of it, but does not anchor into an existing + // header. We expect the old subchain to be truncated and a new chain + // be created for the dangling head. + { + headers: []*types.Header{block49B}, + oldstate: []*subchain{{Head: 100, Tail: 5}}, + head: block50, + newstate: []*subchain{ + {Head: 50, Tail: 50}, + {Head: 49, Tail: 5}, + }, + }, + } + for i, tt := range tests { + // Create a fresh database and initialize it with the starting state + db := rawdb.NewMemoryDatabase() + + rawdb.WriteHeader(db, genesis) + for _, header := range tt.headers { + rawdb.WriteSkeletonHeader(db, header) + } + if tt.oldstate != nil { + blob, _ := json.Marshal(&skeletonProgress{Subchains: tt.oldstate}) + rawdb.WriteSkeletonSyncStatus(db, blob) + } + // Create a skeleton sync and run a cycle + wait := make(chan struct{}) + + skeleton := newSkeleton(db, newPeerSet(), func(string) {}, newNoopBackfiller()) + skeleton.syncStarting = func() { close(wait) } + skeleton.Sync(tt.head) + + <-wait + skeleton.Terminate() + + // Ensure the correct resulting sync status + var progress skeletonProgress + json.Unmarshal(rawdb.ReadSkeletonSyncStatus(db), &progress) + + if len(progress.Subchains) != len(tt.newstate) { + t.Errorf("test %d: subchain count mismatch: have %d, want %d", i, len(progress.Subchains), len(tt.newstate)) + continue + } + for j := 0; j < len(progress.Subchains); j++ { + if progress.Subchains[j].Head != tt.newstate[j].Head { + t.Errorf("test %d: subchain %d head mismatch: have %d, want %d", i, j, progress.Subchains[j].Head, tt.newstate[j].Head) + } + if progress.Subchains[j].Tail != tt.newstate[j].Tail { + t.Errorf("test %d: subchain %d tail mismatch: have %d, want %d", i, j, progress.Subchains[j].Tail, tt.newstate[j].Tail) + } + } + } +} diff --git a/eth/downloader/statesync.go b/eth/downloader/statesync.go index 6c53e5577a87..501af63ed5c4 100644 --- a/eth/downloader/statesync.go +++ b/eth/downloader/statesync.go @@ -17,48 +17,12 @@ package downloader import ( - "fmt" "sync" - "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/trie" - "golang.org/x/crypto/sha3" ) -// stateReq represents a batch of state fetch requests grouped together into -// a single data retrieval network packet. -type stateReq struct { - nItems uint16 // Number of items requested for download (max is 384, so uint16 is sufficient) - trieTasks map[common.Hash]*trieTask // Trie node download tasks to track previous attempts - codeTasks map[common.Hash]*codeTask // Byte code download tasks to track previous attempts - timeout time.Duration // Maximum round trip time for this to complete - timer *time.Timer // Timer to fire when the RTT timeout expires - peer *peerConnection // Peer that we're requesting from - delivered time.Time // Time when the packet was delivered (independent when we process it) - response [][]byte // Response data of the peer (nil for timeouts) - dropped bool // Flag whether the peer dropped off early -} - -// timedOut returns if this request timed out. -func (req *stateReq) timedOut() bool { - return req.response == nil -} - -// stateSyncStats is a collection of progress stats to report during a state trie -// sync to RPC requests as well as to display in user logs. -type stateSyncStats struct { - processed uint64 // Number of state entries processed - duplicate uint64 // Number of state entries downloaded twice - unexpected uint64 // Number of non-requested state entries received - pending uint64 // Number of still pending state entries -} - // syncState starts downloading state with the given root hash. func (d *Downloader) syncState(root common.Hash) *stateSync { // Create the state sync @@ -85,8 +49,6 @@ func (d *Downloader) stateFetcher() { for next := s; next != nil; { next = d.runStateSync(next) } - case <-d.stateCh: - // Ignore state responses while no sync is running. case <-d.quitCh: return } @@ -96,216 +58,44 @@ func (d *Downloader) stateFetcher() { // runStateSync runs a state synchronisation until it completes or another root // hash is requested to be switched over to. func (d *Downloader) runStateSync(s *stateSync) *stateSync { - var ( - active = make(map[string]*stateReq) // Currently in-flight requests - finished []*stateReq // Completed or failed requests - timeout = make(chan *stateReq) // Timed out active requests - ) log.Trace("State sync starting", "root", s.root) - defer func() { - // Cancel active request timers on exit. Also set peers to idle so they're - // available for the next sync. - for _, req := range active { - req.timer.Stop() - req.peer.SetNodeDataIdle(int(req.nItems), time.Now()) - } - }() go s.run() defer s.Cancel() - // Listen for peer departure events to cancel assigned tasks - peerDrop := make(chan *peerConnection, 1024) - peerSub := s.d.peers.SubscribePeerDrops(peerDrop) - defer peerSub.Unsubscribe() - for { - // Enable sending of the first buffered element if there is one. - var ( - deliverReq *stateReq - deliverReqCh chan *stateReq - ) - if len(finished) > 0 { - deliverReq = finished[0] - deliverReqCh = s.deliver - } - select { - // The stateSync lifecycle: case next := <-d.stateSyncStart: - d.spindownStateSync(active, finished, timeout, peerDrop) return next case <-s.done: - d.spindownStateSync(active, finished, timeout, peerDrop) return nil - - // Send the next finished request to the current sync: - case deliverReqCh <- deliverReq: - // Shift out the first request, but also set the emptied slot to nil for GC - copy(finished, finished[1:]) - finished[len(finished)-1] = nil - finished = finished[:len(finished)-1] - - // Handle incoming state packs: - case pack := <-d.stateCh: - // Discard any data not requested (or previously timed out) - req := active[pack.PeerId()] - if req == nil { - log.Debug("Unrequested node data", "peer", pack.PeerId(), "len", pack.Items()) - continue - } - // Finalize the request and queue up for processing - req.timer.Stop() - req.response = pack.(*statePack).states - req.delivered = time.Now() - - finished = append(finished, req) - delete(active, pack.PeerId()) - - // Handle dropped peer connections: - case p := <-peerDrop: - // Skip if no request is currently pending - req := active[p.id] - if req == nil { - continue - } - // Finalize the request and queue up for processing - req.timer.Stop() - req.dropped = true - req.delivered = time.Now() - - finished = append(finished, req) - delete(active, p.id) - - // Handle timed-out requests: - case req := <-timeout: - // If the peer is already requesting something else, ignore the stale timeout. - // This can happen when the timeout and the delivery happens simultaneously, - // causing both pathways to trigger. - if active[req.peer.id] != req { - continue - } - req.delivered = time.Now() - // Move the timed out data back into the download queue - finished = append(finished, req) - delete(active, req.peer.id) - - // Track outgoing state requests: - case req := <-d.trackStateReq: - // If an active request already exists for this peer, we have a problem. In - // theory the trie node schedule must never assign two requests to the same - // peer. In practice however, a peer might receive a request, disconnect and - // immediately reconnect before the previous times out. In this case the first - // request is never honored, alas we must not silently overwrite it, as that - // causes valid requests to go missing and sync to get stuck. - if old := active[req.peer.id]; old != nil { - log.Warn("Busy peer assigned new state fetch", "peer", old.peer.id) - // Move the previous request to the finished set - old.timer.Stop() - old.dropped = true - old.delivered = time.Now() - finished = append(finished, old) - } - // Start a timer to notify the sync loop if the peer stalled. - req.timer = time.AfterFunc(req.timeout, func() { - timeout <- req - }) - active[req.peer.id] = req } } } -// spindownStateSync 'drains' the outstanding requests; some will be delivered and other -// will time out. This is to ensure that when the next stateSync starts working, all peers -// are marked as idle and de facto _are_ idle. -func (d *Downloader) spindownStateSync(active map[string]*stateReq, finished []*stateReq, timeout chan *stateReq, peerDrop chan *peerConnection) { - log.Trace("State sync spinning down", "active", len(active), "finished", len(finished)) - for len(active) > 0 { - var ( - req *stateReq - reason string - ) - select { - // Handle (drop) incoming state packs: - case pack := <-d.stateCh: - req = active[pack.PeerId()] - reason = "delivered" - // Handle dropped peer connections: - case p := <-peerDrop: - req = active[p.id] - reason = "peerdrop" - // Handle timed-out requests: - case req = <-timeout: - reason = "timeout" - } - if req == nil { - continue - } - req.peer.log.Trace("State peer marked idle (spindown)", "req.items", int(req.nItems), "reason", reason) - req.timer.Stop() - delete(active, req.peer.id) - req.peer.SetNodeDataIdle(int(req.nItems), time.Now()) - } - // The 'finished' set contains deliveries that we were going to pass to processing. - // Those are now moot, but we still need to set those peers as idle, which would - // otherwise have been done after processing - for _, req := range finished { - req.peer.SetNodeDataIdle(int(req.nItems), time.Now()) - } -} - // stateSync schedules requests for downloading a particular state trie defined // by a given state root. type stateSync struct { - d *Downloader // Downloader instance to access and manage current peerset - - root common.Hash // State root currently being synced - sched *trie.Sync // State trie sync scheduler defining the tasks - keccak crypto.KeccakState // Keccak256 hasher to verify deliveries with - - trieTasks map[common.Hash]*trieTask // Set of trie node tasks currently queued for retrieval - codeTasks map[common.Hash]*codeTask // Set of byte code tasks currently queued for retrieval + d *Downloader // Downloader instance to access and manage current peerset + root common.Hash // State root currently being synced - numUncommitted int - bytesUncommitted int - - started chan struct{} // Started is signalled once the sync loop starts - - deliver chan *stateReq // Delivery channel multiplexing peer responses - cancel chan struct{} // Channel to signal a termination request - cancelOnce sync.Once // Ensures cancel only ever gets called once - done chan struct{} // Channel to signal termination completion - err error // Any error hit during sync (set before completion) -} - -// trieTask represents a single trie node download task, containing a set of -// peers already attempted retrieval from to detect stalled syncs and abort. -type trieTask struct { - path [][]byte - attempts map[string]struct{} -} - -// codeTask represents a single byte code download task, containing a set of -// peers already attempted retrieval from to detect stalled syncs and abort. -type codeTask struct { - attempts map[string]struct{} + started chan struct{} // Started is signalled once the sync loop starts + cancel chan struct{} // Channel to signal a termination request + cancelOnce sync.Once // Ensures cancel only ever gets called once + done chan struct{} // Channel to signal termination completion + err error // Any error hit during sync (set before completion) } // newStateSync creates a new state trie download scheduler. This method does not // yet start the sync. The user needs to call run to initiate. func newStateSync(d *Downloader, root common.Hash) *stateSync { return &stateSync{ - d: d, - root: root, - sched: state.NewStateSync(root, d.stateDB, d.stateBloom, nil), - keccak: sha3.NewLegacyKeccak256().(crypto.KeccakState), - trieTasks: make(map[common.Hash]*trieTask), - codeTasks: make(map[common.Hash]*codeTask), - deliver: make(chan *stateReq), - cancel: make(chan struct{}), - done: make(chan struct{}), - started: make(chan struct{}), + d: d, + root: root, + cancel: make(chan struct{}), + done: make(chan struct{}), + started: make(chan struct{}), } } @@ -314,11 +104,7 @@ func newStateSync(d *Downloader, root common.Hash) *stateSync { // finish. func (s *stateSync) run() { close(s.started) - if s.d.snapSync { - s.err = s.d.SnapSyncer.Sync(s.root, s.cancel) - } else { - s.err = s.loop() - } + s.err = s.d.SnapSyncer.Sync(s.root, s.cancel) close(s.done) } @@ -335,281 +121,3 @@ func (s *stateSync) Cancel() error { }) return s.Wait() } - -// loop is the main event loop of a state trie sync. It it responsible for the -// assignment of new tasks to peers (including sending it to them) as well as -// for the processing of inbound data. Note, that the loop does not directly -// receive data from peers, rather those are buffered up in the downloader and -// pushed here async. The reason is to decouple processing from data receipt -// and timeouts. -func (s *stateSync) loop() (err error) { - // Listen for new peer events to assign tasks to them - newPeer := make(chan *peerConnection, 1024) - peerSub := s.d.peers.SubscribeNewPeers(newPeer) - defer peerSub.Unsubscribe() - defer func() { - cerr := s.commit(true) - if err == nil { - err = cerr - } - }() - - // Keep assigning new tasks until the sync completes or aborts - for s.sched.Pending() > 0 { - if err = s.commit(false); err != nil { - return err - } - s.assignTasks() - // Tasks assigned, wait for something to happen - select { - case <-newPeer: - // New peer arrived, try to assign it download tasks - - case <-s.cancel: - return errCancelStateFetch - - case <-s.d.cancelCh: - return errCanceled - - case req := <-s.deliver: - // Response, disconnect or timeout triggered, drop the peer if stalling - log.Trace("Received node data response", "peer", req.peer.id, "count", len(req.response), "dropped", req.dropped, "timeout", !req.dropped && req.timedOut()) - if req.nItems <= 2 && !req.dropped && req.timedOut() { - // 2 items are the minimum requested, if even that times out, we've no use of - // this peer at the moment. - log.Warn("Stalling state sync, dropping peer", "peer", req.peer.id) - if s.d.dropPeer == nil { - // The dropPeer method is nil when `--copydb` is used for a local copy. - // Timeouts can occur if e.g. compaction hits at the wrong time, and can be ignored - req.peer.log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", req.peer.id) - } else { - s.d.dropPeer(req.peer.id) - - // If this peer was the master peer, abort sync immediately - s.d.cancelLock.RLock() - master := req.peer.id == s.d.cancelPeer - s.d.cancelLock.RUnlock() - - if master { - s.d.cancel() - return errTimeout - } - } - } - // Process all the received blobs and check for stale delivery - delivered, err := s.process(req) - req.peer.SetNodeDataIdle(delivered, req.delivered) - if err != nil { - log.Warn("Node data write error", "err", err) - return err - } - } - } - return nil -} - -func (s *stateSync) commit(force bool) error { - if !force && s.bytesUncommitted < ethdb.IdealBatchSize { - return nil - } - start := time.Now() - b := s.d.stateDB.NewBatch() - if err := s.sched.Commit(b); err != nil { - return err - } - if err := b.Write(); err != nil { - return fmt.Errorf("DB write error: %v", err) - } - s.updateStats(s.numUncommitted, 0, 0, time.Since(start)) - s.numUncommitted = 0 - s.bytesUncommitted = 0 - return nil -} - -// assignTasks attempts to assign new tasks to all idle peers, either from the -// batch currently being retried, or fetching new data from the trie sync itself. -func (s *stateSync) assignTasks() { - // Iterate over all idle peers and try to assign them state fetches - peers, _ := s.d.peers.NodeDataIdlePeers() - for _, p := range peers { - // Assign a batch of fetches proportional to the estimated latency/bandwidth - cap := p.NodeDataCapacity(s.d.peers.rates.TargetRoundTrip()) - req := &stateReq{peer: p, timeout: s.d.peers.rates.TargetTimeout()} - - nodes, _, codes := s.fillTasks(cap, req) - - // If the peer was assigned tasks to fetch, send the network request - if len(nodes)+len(codes) > 0 { - req.peer.log.Trace("Requesting batch of state data", "nodes", len(nodes), "codes", len(codes), "root", s.root) - select { - case s.d.trackStateReq <- req: - req.peer.FetchNodeData(append(nodes, codes...)) // Unified retrieval under eth/6x - case <-s.cancel: - case <-s.d.cancelCh: - } - } - } -} - -// fillTasks fills the given request object with a maximum of n state download -// tasks to send to the remote peer. -func (s *stateSync) fillTasks(n int, req *stateReq) (nodes []common.Hash, paths []trie.SyncPath, codes []common.Hash) { - // Refill available tasks from the scheduler. - if fill := n - (len(s.trieTasks) + len(s.codeTasks)); fill > 0 { - nodes, paths, codes := s.sched.Missing(fill) - for i, hash := range nodes { - s.trieTasks[hash] = &trieTask{ - path: paths[i], - attempts: make(map[string]struct{}), - } - } - for _, hash := range codes { - s.codeTasks[hash] = &codeTask{ - attempts: make(map[string]struct{}), - } - } - } - // Find tasks that haven't been tried with the request's peer. Prefer code - // over trie nodes as those can be written to disk and forgotten about. - nodes = make([]common.Hash, 0, n) - paths = make([]trie.SyncPath, 0, n) - codes = make([]common.Hash, 0, n) - - req.trieTasks = make(map[common.Hash]*trieTask, n) - req.codeTasks = make(map[common.Hash]*codeTask, n) - - for hash, t := range s.codeTasks { - // Stop when we've gathered enough requests - if len(nodes)+len(codes) == n { - break - } - // Skip any requests we've already tried from this peer - if _, ok := t.attempts[req.peer.id]; ok { - continue - } - // Assign the request to this peer - t.attempts[req.peer.id] = struct{}{} - codes = append(codes, hash) - req.codeTasks[hash] = t - delete(s.codeTasks, hash) - } - for hash, t := range s.trieTasks { - // Stop when we've gathered enough requests - if len(nodes)+len(codes) == n { - break - } - // Skip any requests we've already tried from this peer - if _, ok := t.attempts[req.peer.id]; ok { - continue - } - // Assign the request to this peer - t.attempts[req.peer.id] = struct{}{} - - nodes = append(nodes, hash) - paths = append(paths, t.path) - - req.trieTasks[hash] = t - delete(s.trieTasks, hash) - } - req.nItems = uint16(len(nodes) + len(codes)) - return nodes, paths, codes -} - -// process iterates over a batch of delivered state data, injecting each item -// into a running state sync, re-queuing any items that were requested but not -// delivered. Returns whether the peer actually managed to deliver anything of -// value, and any error that occurred. -func (s *stateSync) process(req *stateReq) (int, error) { - // Collect processing stats and update progress if valid data was received - duplicate, unexpected, successful := 0, 0, 0 - - defer func(start time.Time) { - if duplicate > 0 || unexpected > 0 { - s.updateStats(0, duplicate, unexpected, time.Since(start)) - } - }(time.Now()) - - // Iterate over all the delivered data and inject one-by-one into the trie - for _, blob := range req.response { - hash, err := s.processNodeData(blob) - switch err { - case nil: - s.numUncommitted++ - s.bytesUncommitted += len(blob) - successful++ - case trie.ErrNotRequested: - unexpected++ - case trie.ErrAlreadyProcessed: - duplicate++ - default: - return successful, fmt.Errorf("invalid state node %s: %v", hash.TerminalString(), err) - } - // Delete from both queues (one delivery is enough for the syncer) - delete(req.trieTasks, hash) - delete(req.codeTasks, hash) - } - // Put unfulfilled tasks back into the retry queue - npeers := s.d.peers.Len() - for hash, task := range req.trieTasks { - // If the node did deliver something, missing items may be due to a protocol - // limit or a previous timeout + delayed delivery. Both cases should permit - // the node to retry the missing items (to avoid single-peer stalls). - if len(req.response) > 0 || req.timedOut() { - delete(task.attempts, req.peer.id) - } - // If we've requested the node too many times already, it may be a malicious - // sync where nobody has the right data. Abort. - if len(task.attempts) >= npeers { - return successful, fmt.Errorf("trie node %s failed with all peers (%d tries, %d peers)", hash.TerminalString(), len(task.attempts), npeers) - } - // Missing item, place into the retry queue. - s.trieTasks[hash] = task - } - for hash, task := range req.codeTasks { - // If the node did deliver something, missing items may be due to a protocol - // limit or a previous timeout + delayed delivery. Both cases should permit - // the node to retry the missing items (to avoid single-peer stalls). - if len(req.response) > 0 || req.timedOut() { - delete(task.attempts, req.peer.id) - } - // If we've requested the node too many times already, it may be a malicious - // sync where nobody has the right data. Abort. - if len(task.attempts) >= npeers { - return successful, fmt.Errorf("byte code %s failed with all peers (%d tries, %d peers)", hash.TerminalString(), len(task.attempts), npeers) - } - // Missing item, place into the retry queue. - s.codeTasks[hash] = task - } - return successful, nil -} - -// processNodeData tries to inject a trie node data blob delivered from a remote -// peer into the state trie, returning whether anything useful was written or any -// error occurred. -func (s *stateSync) processNodeData(blob []byte) (common.Hash, error) { - res := trie.SyncResult{Data: blob} - s.keccak.Reset() - s.keccak.Write(blob) - s.keccak.Read(res.Hash[:]) - err := s.sched.Process(res) - return res.Hash, err -} - -// updateStats bumps the various state sync progress counters and displays a log -// message for the user to see. -func (s *stateSync) updateStats(written, duplicate, unexpected int, duration time.Duration) { - s.d.syncStatsLock.Lock() - defer s.d.syncStatsLock.Unlock() - - s.d.syncStatsState.pending = uint64(s.sched.Pending()) - s.d.syncStatsState.processed += uint64(written) - s.d.syncStatsState.duplicate += uint64(duplicate) - s.d.syncStatsState.unexpected += uint64(unexpected) - - if written > 0 || duplicate > 0 || unexpected > 0 { - log.Info("Imported new state entries", "count", written, "elapsed", common.PrettyDuration(duration), "processed", s.d.syncStatsState.processed, "pending", s.d.syncStatsState.pending, "trieretry", len(s.trieTasks), "coderetry", len(s.codeTasks), "duplicate", s.d.syncStatsState.duplicate, "unexpected", s.d.syncStatsState.unexpected) - } - if written > 0 { - rawdb.WriteFastTrieProgress(s.d.stateDB, s.d.syncStatsState.processed) - } -} diff --git a/eth/fetcher/block_fetcher.go b/eth/fetcher/block_fetcher.go index 45983c97ce64..81dda425408b 100644 --- a/eth/fetcher/block_fetcher.go +++ b/eth/fetcher/block_fetcher.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/trie" @@ -74,10 +75,10 @@ type HeaderRetrievalFn func(common.Hash) *types.Header type blockRetrievalFn func(common.Hash) *types.Block // headerRequesterFn is a callback type for sending a header retrieval request. -type headerRequesterFn func(common.Hash) error +type headerRequesterFn func(common.Hash, chan *eth.Response) (*eth.Request, error) // bodyRequesterFn is a callback type for sending a body retrieval request. -type bodyRequesterFn func([]common.Hash) error +type bodyRequesterFn func([]common.Hash, chan *eth.Response) (*eth.Request, error) // headerVerifierFn is a callback type to verify a block's header for fast propagation. type headerVerifierFn func(header *types.Header) error @@ -460,15 +461,28 @@ func (f *BlockFetcher) loop() { // Create a closure of the fetch and schedule in on a new thread fetchHeader, hashes := f.fetching[hashes[0]].fetchHeader, hashes - go func() { + go func(peer string) { if f.fetchingHook != nil { f.fetchingHook(hashes) } for _, hash := range hashes { headerFetchMeter.Mark(1) - fetchHeader(hash) // Suboptimal, but protocol doesn't allow batch header retrievals + go func() { + resCh := make(chan *eth.Response) + + req, err := fetchHeader(hash, resCh) + if err != nil { + return // Legacy code, yolo + } + defer req.Close() + + res := <-resCh + res.Done <- nil + + f.FilterHeaders(peer, *res.Res.(*eth.BlockHeadersPacket), time.Now().Add(res.Time)) + }() } - }() + }(peer) } // Schedule the next fetch if blocks are still pending f.rescheduleFetch(fetchTimer) @@ -496,8 +510,24 @@ func (f *BlockFetcher) loop() { if f.completingHook != nil { f.completingHook(hashes) } + fetchBodies := f.completing[hashes[0]].fetchBodies bodyFetchMeter.Mark(int64(len(hashes))) - go f.completing[hashes[0]].fetchBodies(hashes) + + go func(peer string, hashes []common.Hash) { + resCh := make(chan *eth.Response) + + req, err := fetchBodies(hashes, resCh) + if err != nil { + return // Legacy code, yolo + } + defer req.Close() + + res := <-resCh + res.Done <- nil + + txs, uncles := res.Res.(*eth.BlockBodiesPacket).Unpack() + f.FilterBodies(peer, txs, uncles, time.Now()) + }(peer, hashes) } // Schedule the next fetch if blocks are still pending f.rescheduleComplete(completeTimer) diff --git a/eth/fetcher/block_fetcher_test.go b/eth/fetcher/block_fetcher_test.go index b6d1125b5653..a123710fb543 100644 --- a/eth/fetcher/block_fetcher_test.go +++ b/eth/fetcher/block_fetcher_test.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" ) @@ -195,16 +196,26 @@ func (f *fetcherTester) makeHeaderFetcher(peer string, blocks map[common.Hash]*t closure[hash] = block } // Create a function that return a header from the closure - return func(hash common.Hash) error { + return func(hash common.Hash, sink chan *eth.Response) (*eth.Request, error) { // Gather the blocks to return headers := make([]*types.Header, 0, 1) if block, ok := closure[hash]; ok { headers = append(headers, block.Header()) } // Return on a new thread - go f.fetcher.FilterHeaders(peer, headers, time.Now().Add(drift)) - - return nil + req := ð.Request{ + Peer: peer, + } + res := ð.Response{ + Req: req, + Res: (*eth.BlockHeadersPacket)(&headers), + Time: drift, + Done: make(chan error, 1), // Ignore the returned status + } + go func() { + sink <- res + }() + return req, nil } } @@ -215,7 +226,7 @@ func (f *fetcherTester) makeBodyFetcher(peer string, blocks map[common.Hash]*typ closure[hash] = block } // Create a function that returns blocks from the closure - return func(hashes []common.Hash) error { + return func(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) { // Gather the block bodies to return transactions := make([][]*types.Transaction, 0, len(hashes)) uncles := make([][]*types.Header, 0, len(hashes)) @@ -227,9 +238,26 @@ func (f *fetcherTester) makeBodyFetcher(peer string, blocks map[common.Hash]*typ } } // Return on a new thread - go f.fetcher.FilterBodies(peer, transactions, uncles, time.Now().Add(drift)) - - return nil + bodies := make([]*eth.BlockBody, len(transactions)) + for i, txs := range transactions { + bodies[i] = ð.BlockBody{ + Transactions: txs, + Uncles: uncles[i], + } + } + req := ð.Request{ + Peer: peer, + } + res := ð.Response{ + Req: req, + Res: (*eth.BlockBodiesPacket)(&bodies), + Time: drift, + Done: make(chan error, 1), // Ignore the returned status + } + go func() { + sink <- res + }() + return req, nil } } @@ -368,13 +396,13 @@ func testConcurrentAnnouncements(t *testing.T, light bool) { secondBodyFetcher := tester.makeBodyFetcher("second", blocks, 0) counter := uint32(0) - firstHeaderWrapper := func(hash common.Hash) error { + firstHeaderWrapper := func(hash common.Hash, sink chan *eth.Response) (*eth.Request, error) { atomic.AddUint32(&counter, 1) - return firstHeaderFetcher(hash) + return firstHeaderFetcher(hash, sink) } - secondHeaderWrapper := func(hash common.Hash) error { + secondHeaderWrapper := func(hash common.Hash, sink chan *eth.Response) (*eth.Request, error) { atomic.AddUint32(&counter, 1) - return secondHeaderFetcher(hash) + return secondHeaderFetcher(hash, sink) } // Iteratively announce blocks until all are imported imported := make(chan interface{}) @@ -468,15 +496,20 @@ func testPendingDeduplication(t *testing.T, light bool) { delay := 50 * time.Millisecond counter := uint32(0) - headerWrapper := func(hash common.Hash) error { + headerWrapper := func(hash common.Hash, sink chan *eth.Response) (*eth.Request, error) { atomic.AddUint32(&counter, 1) // Simulate a long running fetch - go func() { - time.Sleep(delay) - headerFetcher(hash) - }() - return nil + resink := make(chan *eth.Response) + req, err := headerFetcher(hash, resink) + if err == nil { + go func() { + res := <-resink + time.Sleep(delay) + sink <- res + }() + } + return req, err } checkNonExist := func() bool { return tester.getBlock(hashes[0]) == nil diff --git a/eth/handler.go b/eth/handler.go index 0a1919fb681c..0498e1fd611b 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -82,8 +82,8 @@ type handlerConfig struct { TxPool txPool // Transaction pool to propagate from Merger *core.Merger // The manager for eth1/2 transition Network uint64 // Network identifier to adfvertise - Sync downloader.SyncMode // Whether to fast or full sync - BloomCache uint64 // Megabytes to alloc for fast sync bloom + Sync downloader.SyncMode // Whether to snap or full sync + BloomCache uint64 // Megabytes to alloc for snap sync bloom EventMux *event.TypeMux // Legacy event mux, deprecate for `feed` Checkpoint *params.TrustedCheckpoint // Hard coded checkpoint for sync challenges Whitelist map[uint64]common.Hash // Hard coded whitelist for sync challenged @@ -93,8 +93,7 @@ type handler struct { networkID uint64 forkFilter forkid.Filter // Fork ID filter, constant across the lifetime of the node - fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks) - snapSync uint32 // Flag whether fast sync should operate on top of the snap protocol + snapSync uint32 // Flag whether snap sync is enabled (gets disabled if we already have blocks) acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing) checkpointNumber uint64 // Block number for the sync progress validator to cross reference @@ -146,29 +145,26 @@ func newHandler(config *handlerConfig) (*handler, error) { quitSync: make(chan struct{}), } if config.Sync == downloader.FullSync { - // The database seems empty as the current block is the genesis. Yet the fast - // block is ahead, so fast sync was enabled for this node at a certain point. + // The database seems empty as the current block is the genesis. Yet the snap + // block is ahead, so snap sync was enabled for this node at a certain point. // The scenarios where this can happen is - // * if the user manually (or via a bad block) rolled back a fast sync node + // * if the user manually (or via a bad block) rolled back a snap sync node // below the sync point. - // * the last fast sync is not finished while user specifies a full sync this + // * the last snap sync is not finished while user specifies a full sync this // time. But we don't have any recent state for full sync. - // In these cases however it's safe to reenable fast sync. + // In these cases however it's safe to reenable snap sync. fullBlock, fastBlock := h.chain.CurrentBlock(), h.chain.CurrentFastBlock() if fullBlock.NumberU64() == 0 && fastBlock.NumberU64() > 0 { - h.fastSync = uint32(1) - log.Warn("Switch sync mode from full sync to fast sync") + h.snapSync = uint32(1) + log.Warn("Switch sync mode from full sync to snap sync") } } else { if h.chain.CurrentBlock().NumberU64() > 0 { - // Print warning log if database is not empty to run fast sync. - log.Warn("Switch sync mode from fast sync to full sync") + // Print warning log if database is not empty to run snap sync. + log.Warn("Switch sync mode from snap sync to full sync") } else { - // If fast sync was requested and our database is empty, grant it - h.fastSync = uint32(1) - if config.Sync == downloader.SnapSync { - h.snapSync = uint32(1) - } + // If snap sync was requested and our database is empty, grant it + h.snapSync = uint32(1) } } // If we have trusted checkpoints, enforce them on the chain @@ -176,17 +172,37 @@ func newHandler(config *handlerConfig) (*handler, error) { h.checkpointNumber = (config.Checkpoint.SectionIndex+1)*params.CHTFrequency - 1 h.checkpointHash = config.Checkpoint.SectionHead } - // Construct the downloader (long sync) and its backing state bloom if fast + // If sync succeeds, pass a callback to potentially disable snap sync mode + // and enable transaction propagation. + success := func() { + // If we were running snap sync and it finished, disable doing another + // round on next sync cycle + if atomic.LoadUint32(&h.snapSync) == 1 { + log.Info("Snap sync complete, auto disabling") + atomic.StoreUint32(&h.snapSync, 0) + } + // If we've successfully finished a sync cycle and passed any required + // checkpoint, enable accepting transactions from the network + head := h.chain.CurrentBlock() + if head.NumberU64() >= h.checkpointNumber { + // Checkpoint passed, sanity check the timestamp to have a fallback mechanism + // for non-checkpointed (number = 0) private networks. + if head.Time() >= uint64(time.Now().AddDate(0, -1, 0).Unix()) { + atomic.StoreUint32(&h.acceptTxs, 1) + } + } + } + // Construct the downloader (long sync) and its backing state bloom if snap // sync is requested. The downloader is responsible for deallocating the state // bloom when it's done. // Note: we don't enable it if snap-sync is performed, since it's very heavy - // and the heal-portion of the snap sync is much lighter than fast. What we particularly + // and the heal-portion of the snap sync is much lighter than snap. What we particularly // want to avoid, is a 90%-finished (but restarted) snap-sync to begin // indexing the entire trie - if atomic.LoadUint32(&h.fastSync) == 1 && atomic.LoadUint32(&h.snapSync) == 0 { + if atomic.LoadUint32(&h.snapSync) == 1 && atomic.LoadUint32(&h.snapSync) == 0 { h.stateBloom = trie.NewSyncBloom(config.BloomCache, config.Database) } - h.downloader = downloader.New(h.checkpointNumber, config.Database, h.stateBloom, h.eventMux, h.chain, nil, h.removePeer) + h.downloader = downloader.New(h.checkpointNumber, config.Database, h.stateBloom, h.eventMux, h.chain, nil, h.removePeer, success) // Construct the fetcher (short sync) validator := func(header *types.Header) error { @@ -235,12 +251,12 @@ func newHandler(config *handlerConfig) (*handler, error) { log.Warn("Unsynced yet, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash()) return 0, nil } - // If fast sync is running, deny importing weird blocks. This is a problematic - // clause when starting up a new network, because fast-syncing miners might not + // If snap sync is running, deny importing weird blocks. This is a problematic + // clause when starting up a new network, because snap-syncing miners might not // accept each others' blocks until a restart. Unfortunately we haven't figured // out a way yet where nodes can decide unilaterally whether the network is new // or not. This should be fixed if we figure out a solution. - if atomic.LoadUint32(&h.fastSync) == 1 { + if atomic.LoadUint32(&h.snapSync) == 1 { log.Warn("Fast syncing, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash()) return 0, nil } @@ -364,30 +380,93 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error { // after this will be sent via broadcasts. h.syncTransactions(peer) + // Create a notification channel for pending requests if the peer goes down + dead := make(chan struct{}) + defer close(dead) + // If we have a trusted CHT, reject all peers below that (avoid fast sync eclipse) if h.checkpointHash != (common.Hash{}) { // Request the peer's checkpoint header for chain height/weight validation - if err := peer.RequestHeadersByNumber(h.checkpointNumber, 1, 0, false); err != nil { + resCh := make(chan *eth.Response) + if _, err := peer.RequestHeadersByNumber(h.checkpointNumber, 1, 0, false, resCh); err != nil { return err } // Start a timer to disconnect if the peer doesn't reply in time - p.syncDrop = time.AfterFunc(syncChallengeTimeout, func() { - peer.Log().Warn("Checkpoint challenge timed out, dropping", "addr", peer.RemoteAddr(), "type", peer.Name()) - h.removePeer(peer.ID()) - }) - // Make sure it's cleaned up if the peer dies off - defer func() { - if p.syncDrop != nil { - p.syncDrop.Stop() - p.syncDrop = nil + go func() { + timeout := time.NewTimer(syncChallengeTimeout) + defer timeout.Stop() + + select { + case res := <-resCh: + headers := ([]*types.Header)(*res.Res.(*eth.BlockHeadersPacket)) + if len(headers) == 0 { + // If we're doing a snap sync, we must enforce the checkpoint + // block to avoid eclipse attacks. Unsynced nodes are welcome + // to connect after we're done joining the network. + if atomic.LoadUint32(&h.snapSync) == 1 { + peer.Log().Warn("Dropping unsynced node during sync", "addr", peer.RemoteAddr(), "type", peer.Name()) + res.Done <- errors.New("unsynced node cannot serve sync") + return + } + res.Done <- nil + return + } + // Validate the header and either drop the peer or continue + if len(headers) > 1 { + res.Done <- errors.New("too many headers in checkpoint response") + return + } + if headers[0].Hash() != h.checkpointHash { + res.Done <- errors.New("checkpoint hash mismatch") + return + } + res.Done <- nil + + case <-timeout.C: + peer.Log().Warn("Checkpoint challenge timed out, dropping", "addr", peer.RemoteAddr(), "type", peer.Name()) + h.removePeer(peer.ID()) + + case <-dead: + // Peer handler terminated, abort all goroutines } }() } // If we have any explicit whitelist block hashes, request them - for number := range h.whitelist { - if err := peer.RequestHeadersByNumber(number, 1, 0, false); err != nil { + for number, hash := range h.whitelist { + resCh := make(chan *eth.Response) + if _, err := peer.RequestHeadersByNumber(number, 1, 0, false, resCh); err != nil { return err } + go func(number uint64, hash common.Hash) { + timeout := time.NewTimer(syncChallengeTimeout) + defer timeout.Stop() + + select { + case res := <-resCh: + headers := ([]*types.Header)(*res.Res.(*eth.BlockHeadersPacket)) + if len(headers) == 0 { + // Whitelisted blocks are allowed to be missing if the remote + // node is not yet synced + res.Done <- nil + return + } + // Validate the header and either drop the peer or continue + if len(headers) > 1 { + res.Done <- errors.New("too many headers in whitelist response") + return + } + if headers[0].Number.Uint64() != number || headers[0].Hash() != hash { + peer.Log().Info("Whitelist mismatch, dropping peer", "number", number, "hash", headers[0].Hash(), "want", hash) + res.Done <- errors.New("whitelist block mismatch") + return + } + peer.Log().Debug("Whitelist block verified", "number", number, "hash", hash) + + case <-timeout.C: + peer.Log().Warn("Whitelist challenge timed out, dropping", "addr", peer.RemoteAddr(), "type", peer.Name()) + h.removePeer(peer.ID()) + } + }(number, hash) } // Handle incoming messages until the connection is torn down return handler(peer) diff --git a/eth/handler_eth.go b/eth/handler_eth.go index 1788a5849c1b..ae1563cd8823 100644 --- a/eth/handler_eth.go +++ b/eth/handler_eth.go @@ -64,24 +64,21 @@ func (h *ethHandler) AcceptTxs() bool { func (h *ethHandler) Handle(peer *eth.Peer, packet eth.Packet) error { // Consume any broadcasts and announces, forwarding the rest to the downloader switch packet := packet.(type) { - case *eth.BlockHeadersPacket: - return h.handleHeaders(peer, *packet) + case *eth.BlockHeadersPacket: // TODO(karalabe): Delete before PR merge + log.Error("Old header handler still got called") + return errors.New("old header handler still got called") - case *eth.BlockBodiesPacket: - txset, uncleset := packet.Unpack() - return h.handleBodies(peer, txset, uncleset) + case *eth.BlockBodiesPacket: // TODO(karalabe): Delete before PR merge + log.Error("Old body handler still got called") + return errors.New("old body handler still got called") - case *eth.NodeDataPacket: - if err := h.downloader.DeliverNodeData(peer.ID(), *packet); err != nil { - log.Debug("Failed to deliver node state data", "err", err) - } - return nil + case *eth.NodeDataPacket: // TODO(karalabe): Delete before PR merge + log.Error("Old node data handler still got called") + return errors.New("old node data handler still got called") - case *eth.ReceiptsPacket: - if err := h.downloader.DeliverReceipts(peer.ID(), *packet); err != nil { - log.Debug("Failed to deliver receipts", "err", err) - } - return nil + case *eth.ReceiptsPacket: // TODO(karalabe): Delete before PR merge + log.Error("Old receipt handler still got called") + return errors.New("old receipt handler still got called") case *eth.NewBlockHashesPacket: hashes, numbers := packet.Unpack() @@ -104,79 +101,6 @@ func (h *ethHandler) Handle(peer *eth.Peer, packet eth.Packet) error { } } -// handleHeaders is invoked from a peer's message handler when it transmits a batch -// of headers for the local node to process. -func (h *ethHandler) handleHeaders(peer *eth.Peer, headers []*types.Header) error { - p := h.peers.peer(peer.ID()) - if p == nil { - return errors.New("unregistered during callback") - } - // If no headers were received, but we're expencting a checkpoint header, consider it that - if len(headers) == 0 && p.syncDrop != nil { - // Stop the timer either way, decide later to drop or not - p.syncDrop.Stop() - p.syncDrop = nil - - // If we're doing a fast (or snap) sync, we must enforce the checkpoint block to avoid - // eclipse attacks. Unsynced nodes are welcome to connect after we're done - // joining the network - if atomic.LoadUint32(&h.fastSync) == 1 { - peer.Log().Warn("Dropping unsynced node during sync", "addr", peer.RemoteAddr(), "type", peer.Name()) - return errors.New("unsynced node cannot serve sync") - } - } - // Filter out any explicitly requested headers, deliver the rest to the downloader - filter := len(headers) == 1 - if filter { - // If it's a potential sync progress check, validate the content and advertised chain weight - if p.syncDrop != nil && headers[0].Number.Uint64() == h.checkpointNumber { - // Disable the sync drop timer - p.syncDrop.Stop() - p.syncDrop = nil - - // Validate the header and either drop the peer or continue - if headers[0].Hash() != h.checkpointHash { - return errors.New("checkpoint hash mismatch") - } - return nil - } - // Otherwise if it's a whitelisted block, validate against the set - if want, ok := h.whitelist[headers[0].Number.Uint64()]; ok { - if hash := headers[0].Hash(); want != hash { - peer.Log().Info("Whitelist mismatch, dropping peer", "number", headers[0].Number.Uint64(), "hash", hash, "want", want) - return errors.New("whitelist block mismatch") - } - peer.Log().Debug("Whitelist block verified", "number", headers[0].Number.Uint64(), "hash", want) - } - // Irrelevant of the fork checks, send the header to the fetcher just in case - headers = h.blockFetcher.FilterHeaders(peer.ID(), headers, time.Now()) - } - if len(headers) > 0 || !filter { - err := h.downloader.DeliverHeaders(peer.ID(), headers) - if err != nil { - log.Debug("Failed to deliver headers", "err", err) - } - } - return nil -} - -// handleBodies is invoked from a peer's message handler when it transmits a batch -// of block bodies for the local node to process. -func (h *ethHandler) handleBodies(peer *eth.Peer, txs [][]*types.Transaction, uncles [][]*types.Header) error { - // Filter out any explicitly requested bodies, deliver the rest to the downloader - filter := len(txs) > 0 || len(uncles) > 0 - if filter { - txs, uncles = h.blockFetcher.FilterBodies(peer.ID(), txs, uncles, time.Now()) - } - if len(txs) > 0 || len(uncles) > 0 || !filter { - err := h.downloader.DeliverBodies(peer.ID(), txs, uncles) - if err != nil { - log.Debug("Failed to deliver bodies", "err", err) - } - } - return nil -} - // handleBlockAnnounces is invoked from a peer's message handler when it transmits a // batch of block announcements for the local node to process. func (h *ethHandler) handleBlockAnnounces(peer *eth.Peer, hashes []common.Hash, numbers []uint64) error { diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go index 8222ba9382f7..904a04685fcc 100644 --- a/eth/handler_eth_test.go +++ b/eth/handler_eth_test.go @@ -462,23 +462,23 @@ func TestCheckpointChallenge(t *testing.T) { }{ // If checkpointing is not enabled locally, don't challenge and don't drop {downloader.FullSync, false, false, false, false, false}, - {downloader.FastSync, false, false, false, false, false}, + {downloader.SnapSync, false, false, false, false, false}, // If checkpointing is enabled locally and remote response is empty, only drop during fast sync {downloader.FullSync, true, false, true, false, false}, - {downloader.FastSync, true, false, true, false, true}, // Special case, fast sync, unsynced peer + {downloader.SnapSync, true, false, true, false, true}, // Special case, fast sync, unsynced peer // If checkpointing is enabled locally and remote response mismatches, always drop {downloader.FullSync, true, false, false, false, true}, - {downloader.FastSync, true, false, false, false, true}, + {downloader.SnapSync, true, false, false, false, true}, // If checkpointing is enabled locally and remote response matches, never drop {downloader.FullSync, true, false, false, true, false}, - {downloader.FastSync, true, false, false, true, false}, + {downloader.SnapSync, true, false, false, true, false}, // If checkpointing is enabled locally and remote times out, always drop {downloader.FullSync, true, true, false, true, true}, - {downloader.FastSync, true, true, false, true, true}, + {downloader.SnapSync, true, true, false, true, true}, } for _, tt := range tests { t.Run(fmt.Sprintf("sync %v checkpoint %v timeout %v empty %v match %v", tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match), func(t *testing.T) { @@ -500,10 +500,10 @@ func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpo handler := newTestHandler() defer handler.close() - if syncmode == downloader.FastSync { - atomic.StoreUint32(&handler.handler.fastSync, 1) + if syncmode == downloader.SnapSync { + atomic.StoreUint32(&handler.handler.snapSync, 1) } else { - atomic.StoreUint32(&handler.handler.fastSync, 0) + atomic.StoreUint32(&handler.handler.snapSync, 0) } var response *types.Header if checkpoint { diff --git a/eth/handler_test.go b/eth/handler_test.go index d3bd2b2385ae..51bac8a6f8c1 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -151,7 +151,7 @@ func newTestHandlerWithBlocks(blocks int) *testHandler { TxPool: txpool, Merger: core.NewMerger(rawdb.NewMemoryDatabase()), Network: 1, - Sync: downloader.FastSync, + Sync: downloader.SnapSync, BloomCache: 1, }) handler.Start(1000) diff --git a/eth/peer.go b/eth/peer.go index 1cea9c640e83..024a6e619371 100644 --- a/eth/peer.go +++ b/eth/peer.go @@ -18,8 +18,6 @@ package eth import ( "math/big" - "sync" - "time" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/snap" @@ -36,11 +34,8 @@ type ethPeerInfo struct { // ethPeer is a wrapper around eth.Peer to maintain a few extra metadata. type ethPeer struct { *eth.Peer - snapExt *snapPeer // Satellite `snap` connection - - syncDrop *time.Timer // Connection dropper if `eth` sync progress isn't validated in time + snapExt *snapPeer // Satellite `snap` connection snapWait chan struct{} // Notification channel for snap connections - lock sync.RWMutex // Mutex protecting the internal fields } // info gathers and returns some `eth` protocol metadata known about a peer. diff --git a/eth/protocols/eth/dispatcher.go b/eth/protocols/eth/dispatcher.go new file mode 100644 index 000000000000..b76f7fe36e2f --- /dev/null +++ b/eth/protocols/eth/dispatcher.go @@ -0,0 +1,198 @@ +// Copyright 2021 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "errors" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/p2p" +) + +var ( + // errDisconnected is returned is a request is attempted to be made to a peer + // that was already closed. + errDisconnected = errors.New("disconnected") + + // errDanglingResponse is returned if a response arrives with a request id + // which does not match to any existing pending requests. + errDanglingResponse = errors.New("response to non-existent request") + + // errMismatchingResponseType is retruned if the remote peer sent a different + // packet type as a response to a request than what the local node expected. + errMismatchingResponseType = errors.New("mismatching response type") +) + +// Request is a pending request to allow tracking it and delivering a response +// back to the requester on their chosen channel. +type Request struct { + id uint64 // Request ID to match up replies to + sent time.Time // Timestamp when the request was sent + + sink chan *Response // Channel to deliver the response on + cancel chan struct{} // Channel to cancel requests ahead of time + + code uint64 // Message code of the request packet + want uint64 // Message code of the response packet + data interface{} // Data content of the request packet + + Peer string // Demultiplexer if cross-peer requests are batched together +} + +// Close aborts an in-flight request. Although there's no way to notify the +// remote peer about the cancellation, this method notifies the dispatcher to +// discard any late responses. +func (r *Request) Close() { + if r.cancel != nil { // Nil only in external packet tests + close(r.cancel) + } +} + +// request is a wrapper around a client Request that has an error channel to +// signal on if sending the request already failed on a network level. +type request struct { + req *Request + fail chan error +} + +// Response is a reply packet to a previously created request. It is delivered +// on the channel assigned by the requester subsystem and contains the original +// request embedded to allow uniquely matching it caller side. +type Response struct { + id uint64 // Request ID to match up this reply to + recv time.Time // Timestamp when the request was received + code uint64 // Response packet type to cross validate with request + + Req *Request // Original request to cross-reference with + Res interface{} // Remote response for the request query + Time time.Duration // Time it took for the request to be served + Done chan error // Channel to signal message handling to the reader +} + +// response is a wrapper around a remote Response that has an error channel to +// signal on if processing the response failed. +type response struct { + res *Response + fail chan error +} + +// dispatchRequest schedules the request to the dispatcher for tracking and +// network serialization, blocking until it's successfully sent. +// +// The returned Request must either be closed before discarding it, or the reply +// must be waited for and the Response's Done channel signalled. +func (p *Peer) dispatchRequest(req *Request) error { + reqOp := &request{ + req: req, + fail: make(chan error), + } + req.cancel = make(chan struct{}) + req.Peer = p.id + + select { + case p.reqDispatch <- reqOp: + return <-reqOp.fail + case <-p.term: + return errDisconnected + } +} + +// dispatchRequest fulfils a pending request and delivers it to the requested +// sink. +func (p *Peer) dispatchResponse(res *Response) error { + resOp := &response{ + res: res, + fail: make(chan error), + } + res.recv = time.Now() + res.Done = make(chan error) + + select { + case p.resDispatch <- resOp: + // Ensure the response is accepted by the dispatcher + if err := <-resOp.fail; err != nil { + return nil + } + // Deliver the filled out response and wait until it's handled + select { + case res.Req.sink <- res: + return <-res.Done // Response delivered, return any errors + case <-res.Req.cancel: + return nil // Request cancelled, silently discard response + } + + case <-p.term: + return errDisconnected + } +} + +// dispatchRequests is a loop that accepts requests from higher layer packages, +// pushes it to the network and tracks and dispatches the responses back to the +// original requester. +func (p *Peer) dispatchRequests() { + pending := make(map[uint64]*Request) + + for { + select { + case reqOp := <-p.reqDispatch: + req := reqOp.req + req.sent = time.Now() + + requestTracker.Track(p.id, p.version, req.code, req.want, req.id) + err := p2p.Send(p.rw, req.code, req.data) + reqOp.fail <- err + + if err == nil { + pending[req.id] = req + } + + case resOp := <-p.resDispatch: + res := resOp.res + res.Req = pending[res.id] + res.Time = res.recv.Sub(res.Req.sent) + + switch { + case res.Req == nil: + // Response arrived with an untracked ID. Since even cancelled + // requests are tracked until fulfilment, a dangling repsponse + // means the remote peer implements the protocol badly. + resOp.fail <- errDanglingResponse + + case res.Req.want != res.code: + // Response arrived, but it's a different packet type than the + // one expected by the requester. Either the local code is bad, + // or the remote peer send junk. In neither cases can we handle + // the packet. + resOp.fail <- fmt.Errorf("%w: have %d, want %d", errMismatchingResponseType, res.code, res.Req.want) + + default: + // All dispatcher checks passed and the response was initialized + // with the matching request. Signal to the delivery routine that + // it can wait for a handler response and dispatch the data. + resOp.fail <- nil + + // Stop tracking the request, the response dispatcher will deliver + requestTracker.Fulfil(p.id, p.version, res.code, res.id) + delete(pending, res.id) + } + + case <-p.term: + return + } + } +} diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index e54838cbc56e..569bd70b51f8 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -277,9 +277,11 @@ func handleBlockHeaders66(backend Backend, msg Decoder, peer *Peer) error { if err := msg.Decode(res); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } - requestTracker.Fulfil(peer.id, peer.version, BlockHeadersMsg, res.RequestId) - - return backend.Handle(peer, &res.BlockHeadersPacket) + return peer.dispatchResponse(&Response{ + id: res.RequestId, + code: BlockHeadersMsg, + Res: &res.BlockHeadersPacket, + }) } func handleBlockBodies66(backend Backend, msg Decoder, peer *Peer) error { @@ -288,9 +290,11 @@ func handleBlockBodies66(backend Backend, msg Decoder, peer *Peer) error { if err := msg.Decode(res); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } - requestTracker.Fulfil(peer.id, peer.version, BlockBodiesMsg, res.RequestId) - - return backend.Handle(peer, &res.BlockBodiesPacket) + return peer.dispatchResponse(&Response{ + id: res.RequestId, + code: BlockBodiesMsg, + Res: &res.BlockBodiesPacket, + }) } func handleNodeData66(backend Backend, msg Decoder, peer *Peer) error { @@ -299,9 +303,11 @@ func handleNodeData66(backend Backend, msg Decoder, peer *Peer) error { if err := msg.Decode(res); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } - requestTracker.Fulfil(peer.id, peer.version, NodeDataMsg, res.RequestId) - - return backend.Handle(peer, &res.NodeDataPacket) + return peer.dispatchResponse(&Response{ + id: res.RequestId, + code: NodeDataMsg, + Res: &res.NodeDataPacket, + }) } func handleReceipts66(backend Backend, msg Decoder, peer *Peer) error { @@ -310,9 +316,11 @@ func handleReceipts66(backend Backend, msg Decoder, peer *Peer) error { if err := msg.Decode(res); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } - requestTracker.Fulfil(peer.id, peer.version, ReceiptsMsg, res.RequestId) - - return backend.Handle(peer, &res.ReceiptsPacket) + return peer.dispatchResponse(&Response{ + id: res.RequestId, + code: ReceiptsMsg, + Res: &res.ReceiptsPacket, + }) } func handleNewPooledTransactionHashes(backend Backend, msg Decoder, peer *Peer) error { diff --git a/eth/protocols/eth/peer.go b/eth/protocols/eth/peer.go index 1b4cfeb3da7a..51ef1a1f2d64 100644 --- a/eth/protocols/eth/peer.go +++ b/eth/protocols/eth/peer.go @@ -84,6 +84,9 @@ type Peer struct { txBroadcast chan []common.Hash // Channel used to queue transaction propagation requests txAnnounce chan []common.Hash // Channel used to queue transaction announcement requests + reqDispatch chan *request // Dispatch channel to send requests and track then until fulfilment + resDispatch chan *response // Dispatch channel to fulfil pending requests and untrack them + term chan struct{} // Termination channel to stop the broadcasters lock sync.RWMutex // Mutex protecting the internal fields } @@ -102,6 +105,8 @@ func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool) *Pe queuedBlockAnns: make(chan *types.Block, maxQueuedBlockAnns), txBroadcast: make(chan []common.Hash), txAnnounce: make(chan []common.Hash), + reqDispatch: make(chan *request), + resDispatch: make(chan *response), txpool: txpool, term: make(chan struct{}), } @@ -109,6 +114,7 @@ func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool) *Pe go peer.broadcastBlocks() go peer.broadcastTransactions() go peer.announceTransactions() + go peer.dispatchRequests() return peer } @@ -323,94 +329,148 @@ func (p *Peer) ReplyReceiptsRLP(id uint64, receipts []rlp.RawValue) error { // RequestOneHeader is a wrapper around the header query functions to fetch a // single header. It is used solely by the fetcher. -func (p *Peer) RequestOneHeader(hash common.Hash) error { +func (p *Peer) RequestOneHeader(hash common.Hash, sink chan *Response) (*Request, error) { p.Log().Debug("Fetching single header", "hash", hash) id := rand.Uint64() - requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id) - return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{ - RequestId: id, - GetBlockHeadersPacket: &GetBlockHeadersPacket{ - Origin: HashOrNumber{Hash: hash}, - Amount: uint64(1), - Skip: uint64(0), - Reverse: false, + req := &Request{ + id: id, + sink: sink, + code: GetBlockHeadersMsg, + want: BlockHeadersMsg, + data: &GetBlockHeadersPacket66{ + RequestId: id, + GetBlockHeadersPacket: &GetBlockHeadersPacket{ + Origin: HashOrNumber{Hash: hash}, + Amount: uint64(1), + Skip: uint64(0), + Reverse: false, + }, }, - }) + } + if err := p.dispatchRequest(req); err != nil { + return nil, err + } + return req, nil } // RequestHeadersByHash fetches a batch of blocks' headers corresponding to the // specified header query, based on the hash of an origin block. -func (p *Peer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error { +func (p *Peer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool, sink chan *Response) (*Request, error) { p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse) id := rand.Uint64() - requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id) - return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{ - RequestId: id, - GetBlockHeadersPacket: &GetBlockHeadersPacket{ - Origin: HashOrNumber{Hash: origin}, - Amount: uint64(amount), - Skip: uint64(skip), - Reverse: reverse, + req := &Request{ + id: id, + sink: sink, + code: GetBlockHeadersMsg, + want: BlockHeadersMsg, + data: &GetBlockHeadersPacket66{ + RequestId: id, + GetBlockHeadersPacket: &GetBlockHeadersPacket{ + Origin: HashOrNumber{Hash: origin}, + Amount: uint64(amount), + Skip: uint64(skip), + Reverse: reverse, + }, }, - }) + } + if err := p.dispatchRequest(req); err != nil { + return nil, err + } + return req, nil } // RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the // specified header query, based on the number of an origin block. -func (p *Peer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error { +func (p *Peer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool, sink chan *Response) (*Request, error) { p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse) id := rand.Uint64() - requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id) - return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{ - RequestId: id, - GetBlockHeadersPacket: &GetBlockHeadersPacket{ - Origin: HashOrNumber{Number: origin}, - Amount: uint64(amount), - Skip: uint64(skip), - Reverse: reverse, + req := &Request{ + id: id, + sink: sink, + code: GetBlockHeadersMsg, + want: BlockHeadersMsg, + data: &GetBlockHeadersPacket66{ + RequestId: id, + GetBlockHeadersPacket: &GetBlockHeadersPacket{ + Origin: HashOrNumber{Number: origin}, + Amount: uint64(amount), + Skip: uint64(skip), + Reverse: reverse, + }, }, - }) + } + if err := p.dispatchRequest(req); err != nil { + return nil, err + } + return req, nil } // RequestBodies fetches a batch of blocks' bodies corresponding to the hashes // specified. -func (p *Peer) RequestBodies(hashes []common.Hash) error { +func (p *Peer) RequestBodies(hashes []common.Hash, sink chan *Response) (*Request, error) { p.Log().Debug("Fetching batch of block bodies", "count", len(hashes)) id := rand.Uint64() - requestTracker.Track(p.id, p.version, GetBlockBodiesMsg, BlockBodiesMsg, id) - return p2p.Send(p.rw, GetBlockBodiesMsg, &GetBlockBodiesPacket66{ - RequestId: id, - GetBlockBodiesPacket: hashes, - }) + req := &Request{ + id: id, + sink: sink, + code: GetBlockBodiesMsg, + want: BlockBodiesMsg, + data: &GetBlockBodiesPacket66{ + RequestId: id, + GetBlockBodiesPacket: hashes, + }, + } + if err := p.dispatchRequest(req); err != nil { + return nil, err + } + return req, nil } // RequestNodeData fetches a batch of arbitrary data from a node's known state // data, corresponding to the specified hashes. -func (p *Peer) RequestNodeData(hashes []common.Hash) error { +func (p *Peer) RequestNodeData(hashes []common.Hash, sink chan *Response) (*Request, error) { p.Log().Debug("Fetching batch of state data", "count", len(hashes)) id := rand.Uint64() - requestTracker.Track(p.id, p.version, GetNodeDataMsg, NodeDataMsg, id) - return p2p.Send(p.rw, GetNodeDataMsg, &GetNodeDataPacket66{ - RequestId: id, - GetNodeDataPacket: hashes, - }) + req := &Request{ + id: id, + sink: sink, + code: GetNodeDataMsg, + want: NodeDataMsg, + data: &GetNodeDataPacket66{ + RequestId: id, + GetNodeDataPacket: hashes, + }, + } + if err := p.dispatchRequest(req); err != nil { + return nil, err + } + return req, nil } // RequestReceipts fetches a batch of transaction receipts from a remote node. -func (p *Peer) RequestReceipts(hashes []common.Hash) error { +func (p *Peer) RequestReceipts(hashes []common.Hash, sink chan *Response) (*Request, error) { p.Log().Debug("Fetching batch of receipts", "count", len(hashes)) id := rand.Uint64() - requestTracker.Track(p.id, p.version, GetReceiptsMsg, ReceiptsMsg, id) - return p2p.Send(p.rw, GetReceiptsMsg, &GetReceiptsPacket66{ - RequestId: id, - GetReceiptsPacket: hashes, - }) + req := &Request{ + id: id, + sink: sink, + code: GetReceiptsMsg, + want: ReceiptsMsg, + data: &GetReceiptsPacket66{ + RequestId: id, + GetReceiptsPacket: hashes, + }, + } + if err := p.dispatchRequest(req); err != nil { + return nil, err + } + return req, nil } // RequestTxs fetches a batch of transactions from a remote node. diff --git a/eth/protocols/snap/sync.go b/eth/protocols/snap/sync.go index 9ef9d7571107..df364b183018 100644 --- a/eth/protocols/snap/sync.go +++ b/eth/protocols/snap/sync.go @@ -325,10 +325,10 @@ type healTask struct { codeTasks map[common.Hash]struct{} // Set of byte code tasks currently queued for retrieval } -// syncProgress is a database entry to allow suspending and resuming a snapshot state +// SyncProgress is a database entry to allow suspending and resuming a snapshot state // sync. Opposed to full and fast sync, there is no way to restart a suspended // snap sync without prior knowledge of the suspension point. -type syncProgress struct { +type SyncProgress struct { Tasks []*accountTask // The suspended account tasks (contract tasks within) // Status report during syncing phase @@ -342,12 +342,15 @@ type syncProgress struct { // Status report during healing phase TrienodeHealSynced uint64 // Number of state trie nodes downloaded TrienodeHealBytes common.StorageSize // Number of state trie bytes persisted to disk - TrienodeHealDups uint64 // Number of state trie nodes already processed - TrienodeHealNops uint64 // Number of state trie nodes not requested BytecodeHealSynced uint64 // Number of bytecodes downloaded BytecodeHealBytes common.StorageSize // Number of bytecodes persisted to disk - BytecodeHealDups uint64 // Number of bytecodes already processed - BytecodeHealNops uint64 // Number of bytecodes not requested +} + +// SyncPending is analogous to SyncProgress, but it's used to report on pending +// ephemeral sync progress that doesn't get persisted into the database. +type SyncPending struct { + TrienodeHeal uint64 // Number of state trie nodes pending + BytecodeHeal uint64 // Number of bytecodes pending } // SyncPeer abstracts out the methods required for a peer to be synced against @@ -671,7 +674,7 @@ func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error { // loadSyncStatus retrieves a previously aborted sync status from the database, // or generates a fresh one if none is available. func (s *Syncer) loadSyncStatus() { - var progress syncProgress + var progress SyncProgress if status := rawdb.ReadSnapshotSyncStatus(s.db); status != nil { if err := json.Unmarshal(status, &progress); err != nil { @@ -775,7 +778,7 @@ func (s *Syncer) saveSyncStatus() { } } // Store the actual progress markers - progress := &syncProgress{ + progress := &SyncProgress{ Tasks: s.tasks, AccountSynced: s.accountSynced, AccountBytes: s.accountBytes, @@ -795,6 +798,31 @@ func (s *Syncer) saveSyncStatus() { rawdb.WriteSnapshotSyncStatus(s.db, status) } +// Progress returns the snap sync status statistics. +func (s *Syncer) Progress() (*SyncProgress, *SyncPending) { + s.lock.Lock() + defer s.lock.Unlock() + + progress := &SyncProgress{ + AccountSynced: s.accountSynced, + AccountBytes: s.accountBytes, + BytecodeSynced: s.bytecodeSynced, + BytecodeBytes: s.bytecodeBytes, + StorageSynced: s.storageSynced, + StorageBytes: s.storageBytes, + TrienodeHealSynced: s.trienodeHealSynced, + TrienodeHealBytes: s.trienodeHealBytes, + BytecodeHealSynced: s.bytecodeHealSynced, + BytecodeHealBytes: s.bytecodeHealBytes, + } + pending := new(SyncPending) + if s.healer != nil { + pending.TrienodeHeal = uint64(len(s.healer.trieTasks)) + pending.BytecodeHeal = uint64(len(s.healer.codeTasks)) + } + return progress, pending +} + // cleanAccountTasks removes account range retrieval tasks that have already been // completed. func (s *Syncer) cleanAccountTasks() { diff --git a/eth/sync.go b/eth/sync.go index 015e974946bc..6138524d995a 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -165,10 +165,7 @@ func (cs *chainSyncer) nextSyncOp() *chainSyncOp { return nil } mode, ourTD := cs.modeAndLocalHead() - if mode == downloader.FastSync && atomic.LoadUint32(&cs.handler.snapSync) == 1 { - // Fast sync via the snap protocol - mode = downloader.SnapSync - } + op := peerToSyncOp(mode, peer) if op.td.Cmp(ourTD) <= 0 { return nil // We're in sync. @@ -182,19 +179,19 @@ func peerToSyncOp(mode downloader.SyncMode, p *eth.Peer) *chainSyncOp { } func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) { - // If we're in fast sync mode, return that directly - if atomic.LoadUint32(&cs.handler.fastSync) == 1 { + // If we're in snap sync mode, return that directly + if atomic.LoadUint32(&cs.handler.snapSync) == 1 { block := cs.handler.chain.CurrentFastBlock() td := cs.handler.chain.GetTdByHash(block.Hash()) - return downloader.FastSync, td + return downloader.SnapSync, td } // We are probably in full sync, but we might have rewound to before the - // fast sync pivot, check if we should reenable + // snap sync pivot, check if we should reenable if pivot := rawdb.ReadLastPivotNumber(cs.handler.database); pivot != nil { if head := cs.handler.chain.CurrentBlock(); head.NumberU64() < *pivot { block := cs.handler.chain.CurrentFastBlock() td := cs.handler.chain.GetTdByHash(block.Hash()) - return downloader.FastSync, td + return downloader.SnapSync, td } } // Nope, we're really full syncing @@ -211,15 +208,15 @@ func (cs *chainSyncer) startSync(op *chainSyncOp) { // doSync synchronizes the local blockchain with a remote peer. func (h *handler) doSync(op *chainSyncOp) error { - if op.mode == downloader.FastSync || op.mode == downloader.SnapSync { - // Before launch the fast sync, we have to ensure user uses the same + if op.mode == downloader.SnapSync { + // Before launch the snap sync, we have to ensure user uses the same // txlookup limit. - // The main concern here is: during the fast sync Geth won't index the + // The main concern here is: during the snap sync Geth won't index the // block(generate tx indices) before the HEAD-limit. But if user changes - // the limit in the next fast sync(e.g. user kill Geth manually and + // the limit in the next snap sync(e.g. user kill Geth manually and // restart) then it will be hard for Geth to figure out the oldest block // has been indexed. So here for the user-experience wise, it's non-optimal - // that user can't change limit during the fast sync. If changed, Geth + // that user can't change limit during the snap sync. If changed, Geth // will just blindly use the original one. limit := h.chain.TxLookupLimit() if stored := rawdb.ReadFastTxLookupLimit(h.database); stored == nil { @@ -229,15 +226,11 @@ func (h *handler) doSync(op *chainSyncOp) error { log.Warn("Update txLookup limit", "provided", limit, "updated", *stored) } } - // Run the sync cycle, and disable fast sync if we're past the pivot block - err := h.downloader.Synchronise(op.peer.ID(), op.head, op.td, op.mode) + // Run the sync cycle, and disable snap sync if we're past the pivot block + err := h.downloader.LegacySync(op.peer.ID(), op.head, op.td, op.mode) if err != nil { return err } - if atomic.LoadUint32(&h.fastSync) == 1 { - log.Info("Fast sync complete, auto disabling") - atomic.StoreUint32(&h.fastSync, 0) - } if atomic.LoadUint32(&h.snapSync) == 1 { log.Info("Snap sync complete, auto disabling") atomic.StoreUint32(&h.snapSync, 0) diff --git a/eth/sync_test.go b/eth/sync_test.go index e96b9ee81f69..ee51c3019f18 100644 --- a/eth/sync_test.go +++ b/eth/sync_test.go @@ -37,14 +37,14 @@ func testFastSyncDisabling(t *testing.T, protocol uint) { // Create an empty handler and ensure it's in fast sync mode empty := newTestHandler() - if atomic.LoadUint32(&empty.handler.fastSync) == 0 { + if atomic.LoadUint32(&empty.handler.snapSync) == 0 { t.Fatalf("fast sync disabled on pristine blockchain") } defer empty.close() // Create a full handler and ensure fast sync ends up disabled full := newTestHandlerWithBlocks(1024) - if atomic.LoadUint32(&full.handler.fastSync) == 1 { + if atomic.LoadUint32(&full.handler.snapSync) == 1 { t.Fatalf("fast sync not disabled on non-empty blockchain") } defer full.close() @@ -69,11 +69,11 @@ func testFastSyncDisabling(t *testing.T, protocol uint) { time.Sleep(250 * time.Millisecond) // Check that fast sync was disabled - op := peerToSyncOp(downloader.FastSync, empty.handler.peers.peerWithHighestTD()) + op := peerToSyncOp(downloader.SnapSync, empty.handler.peers.peerWithHighestTD()) if err := empty.handler.doSync(op); err != nil { t.Fatal("sync failed:", err) } - if atomic.LoadUint32(&empty.handler.fastSync) == 1 { + if atomic.LoadUint32(&empty.handler.snapSync) == 1 { t.Fatalf("fast sync not disabled after successful synchronisation") } } diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 9f68323134f7..decfd7a0fa27 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -284,14 +284,6 @@ func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (* return r, err } -type rpcProgress struct { - StartingBlock hexutil.Uint64 - CurrentBlock hexutil.Uint64 - HighestBlock hexutil.Uint64 - PulledStates hexutil.Uint64 - KnownStates hexutil.Uint64 -} - // SyncProgress retrieves the current progress of the sync algorithm. If there's // no sync currently running, it returns nil. func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) { @@ -304,17 +296,11 @@ func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, err if err := json.Unmarshal(raw, &syncing); err == nil { return nil, nil // Not syncing (always false) } - var progress *rpcProgress + var progress *ethereum.SyncProgress if err := json.Unmarshal(raw, &progress); err != nil { return nil, err } - return ðereum.SyncProgress{ - StartingBlock: uint64(progress.StartingBlock), - CurrentBlock: uint64(progress.CurrentBlock), - HighestBlock: uint64(progress.HighestBlock), - PulledStates: uint64(progress.PulledStates), - KnownStates: uint64(progress.KnownStates), - }, nil + return progress, nil } // SubscribeNewHead subscribes to notifications about the current blockchain head diff --git a/graphql/graphql.go b/graphql/graphql.go index 0da9faa95bc5..13906353445c 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -1221,32 +1221,66 @@ type SyncState struct { func (s *SyncState) StartingBlock() hexutil.Uint64 { return hexutil.Uint64(s.progress.StartingBlock) } - func (s *SyncState) CurrentBlock() hexutil.Uint64 { return hexutil.Uint64(s.progress.CurrentBlock) } - func (s *SyncState) HighestBlock() hexutil.Uint64 { return hexutil.Uint64(s.progress.HighestBlock) } - -func (s *SyncState) PulledStates() *hexutil.Uint64 { - ret := hexutil.Uint64(s.progress.PulledStates) - return &ret +func (s *SyncState) SyncedAccounts() hexutil.Uint64 { + return hexutil.Uint64(s.progress.SyncedAccounts) } - -func (s *SyncState) KnownStates() *hexutil.Uint64 { - ret := hexutil.Uint64(s.progress.KnownStates) - return &ret +func (s *SyncState) SyncedAccountBytes() hexutil.Uint64 { + return hexutil.Uint64(s.progress.SyncedAccountBytes) +} +func (s *SyncState) SyncedBytecodes() hexutil.Uint64 { + return hexutil.Uint64(s.progress.SyncedBytecodes) +} +func (s *SyncState) SyncedBytecodeBytes() hexutil.Uint64 { + return hexutil.Uint64(s.progress.SyncedBytecodeBytes) +} +func (s *SyncState) SyncedStorage() hexutil.Uint64 { + return hexutil.Uint64(s.progress.SyncedStorage) +} +func (s *SyncState) SyncedStorageBytes() hexutil.Uint64 { + return hexutil.Uint64(s.progress.SyncedStorageBytes) +} +func (s *SyncState) HealedTrienodes() hexutil.Uint64 { + return hexutil.Uint64(s.progress.HealedTrienodes) +} +func (s *SyncState) HealedTrienodeBytes() hexutil.Uint64 { + return hexutil.Uint64(s.progress.HealedTrienodeBytes) +} +func (s *SyncState) HealedBytecodes() hexutil.Uint64 { + return hexutil.Uint64(s.progress.HealedBytecodes) +} +func (s *SyncState) HealedBytecodeBytes() hexutil.Uint64 { + return hexutil.Uint64(s.progress.HealedBytecodeBytes) +} +func (s *SyncState) HealingTrienodes() hexutil.Uint64 { + return hexutil.Uint64(s.progress.HealingTrienodes) +} +func (s *SyncState) HealingBytecode() hexutil.Uint64 { + return hexutil.Uint64(s.progress.HealingBytecode) } // Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not // yet received the latest block headers from its pears. In case it is synchronizing: -// - startingBlock: block number this node started to synchronise from -// - currentBlock: block number this node is currently importing -// - highestBlock: block number of the highest block header this node has received from peers -// - pulledStates: number of state entries processed until now -// - knownStates: number of known state entries that still need to be pulled +// - startingBlock: block number this node started to synchronise from +// - currentBlock: block number this node is currently importing +// - highestBlock: block number of the highest block header this node has received from peers +// - syncedAccounts: number of accounts downloaded +// - syncedAccountBytes: number of account trie bytes persisted to disk +// - syncedBytecodes: number of bytecodes downloaded +// - syncedBytecodeBytes: number of bytecode bytes downloaded +// - syncedStorage: number of storage slots downloaded +// - syncedStorageBytes: number of storage trie bytes persisted to disk +// - healedTrienodes: number of state trie nodes downloaded +// - healedTrienodeBytes: number of state trie bytes persisted to disk +// - healedBytecodes: number of bytecodes downloaded +// - healedBytecodeBytes: number of bytecodes persisted to disk +// - healingTrienodes: number of state trie nodes pending +// - healingBytecode: number of bytecodes pending func (r *Resolver) Syncing() (*SyncState, error) { progress := r.backend.SyncProgress() diff --git a/interfaces.go b/interfaces.go index b9d0bb880e58..daea1afb6250 100644 --- a/interfaces.go +++ b/interfaces.go @@ -101,8 +101,22 @@ type SyncProgress struct { StartingBlock uint64 // Block number where sync began CurrentBlock uint64 // Current block number where sync is at HighestBlock uint64 // Highest alleged block number in the chain - PulledStates uint64 // Number of state trie entries already downloaded - KnownStates uint64 // Total number of state trie entries known about + + // Fields belonging to snap sync + SyncedAccounts uint64 // Number of accounts downloaded + SyncedAccountBytes uint64 // Number of account trie bytes persisted to disk + SyncedBytecodes uint64 // Number of bytecodes downloaded + SyncedBytecodeBytes uint64 // Number of bytecode bytes downloaded + SyncedStorage uint64 // Number of storage slots downloaded + SyncedStorageBytes uint64 // Number of storage trie bytes persisted to disk + + HealedTrienodes uint64 // Number of state trie nodes downloaded + HealedTrienodeBytes uint64 // Number of state trie bytes persisted to disk + HealedBytecodes uint64 // Number of bytecodes downloaded + HealedBytecodeBytes uint64 // Number of bytecodes persisted to disk + + HealingTrienodes uint64 // Number of state trie nodes pending + HealingBytecode uint64 // Number of bytecodes pending } // ChainSyncReader wraps access to the node's current sync status. If there's no diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 32757e75ffd5..2b7712d19278 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -130,11 +130,21 @@ func (s *PublicEthereumAPI) Syncing() (interface{}, error) { } // Otherwise gather the block sync stats return map[string]interface{}{ - "startingBlock": hexutil.Uint64(progress.StartingBlock), - "currentBlock": hexutil.Uint64(progress.CurrentBlock), - "highestBlock": hexutil.Uint64(progress.HighestBlock), - "pulledStates": hexutil.Uint64(progress.PulledStates), - "knownStates": hexutil.Uint64(progress.KnownStates), + "startingBlock": hexutil.Uint64(progress.StartingBlock), + "currentBlock": hexutil.Uint64(progress.CurrentBlock), + "highestBlock": hexutil.Uint64(progress.HighestBlock), + "syncedAccounts": hexutil.Uint64(progress.SyncedAccounts), + "syncedAccountBytes": hexutil.Uint64(progress.SyncedAccountBytes), + "syncedBytecodes": hexutil.Uint64(progress.SyncedBytecodes), + "syncedBytecodeBytes": hexutil.Uint64(progress.SyncedBytecodeBytes), + "syncedStorage": hexutil.Uint64(progress.SyncedStorage), + "syncedStorageBytes": hexutil.Uint64(progress.SyncedStorageBytes), + "healedTrienodes": hexutil.Uint64(progress.HealedTrienodes), + "healedTrienodeBytes": hexutil.Uint64(progress.HealedTrienodeBytes), + "healedBytecodes": hexutil.Uint64(progress.HealedBytecodes), + "healedBytecodeBytes": hexutil.Uint64(progress.HealedBytecodeBytes), + "healingTrienodes": hexutil.Uint64(progress.HealingTrienodes), + "healingBytecode": hexutil.Uint64(progress.HealingBytecode), }, nil } @@ -1916,29 +1926,21 @@ func NewPublicDebugAPI(b Backend) *PublicDebugAPI { } // GetHeaderRlp retrieves the RLP encoded for of a single header. -func (api *PublicDebugAPI) GetHeaderRlp(ctx context.Context, number uint64) (string, error) { +func (api *PublicDebugAPI) GetHeaderRlp(ctx context.Context, number uint64) (hexutil.Bytes, error) { header, _ := api.b.HeaderByNumber(ctx, rpc.BlockNumber(number)) if header == nil { - return "", fmt.Errorf("header #%d not found", number) + return nil, fmt.Errorf("header #%d not found", number) } - encoded, err := rlp.EncodeToBytes(header) - if err != nil { - return "", err - } - return fmt.Sprintf("%x", encoded), nil + return rlp.EncodeToBytes(header) } // GetBlockRlp retrieves the RLP encoded for of a single block. -func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (string, error) { +func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (hexutil.Bytes, error) { block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) if block == nil { - return "", fmt.Errorf("block #%d not found", number) - } - encoded, err := rlp.EncodeToBytes(block) - if err != nil { - return "", err + return nil, fmt.Errorf("block #%d not found", number) } - return fmt.Sprintf("%x", encoded), nil + return rlp.EncodeToBytes(block) } // TestSignCliqueBlock fetches the given block number, and attempts to sign it as a clique header with the diff --git a/internal/jsre/deps/bindata.go b/internal/jsre/deps/bindata.go index 97e167eb68cb..77de47b7c1b8 100644 --- a/internal/jsre/deps/bindata.go +++ b/internal/jsre/deps/bindata.go @@ -1,7 +1,7 @@ // Code generated by go-bindata. DO NOT EDIT. // sources: // bignumber.js (17.314kB) -// web3.js (401.802kB) +// web3.js (402.504kB) package deps @@ -90,7 +90,7 @@ func bignumberJs() (*asset, error) { return a, nil } -var _web3Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x6b\x7b\x13\x39\xd2\x38\x0e\xbf\xcf\xa7\x50\xfc\xdc\x0f\xb6\x89\xb1\x73\x60\x18\xc6\x99\x0c\x1b\x02\x33\x64\x6f\x20\x5c\x40\x76\x76\xef\x6c\x96\xab\xed\x96\xed\x1e\xda\xdd\xfe\x75\xb7\x73\x18\x92\xef\xfe\xbf\x54\x3a\x95\x0e\x7d\x70\x12\xe6\xb4\xc9\x0b\x70\x4b\xa5\x53\xa9\x54\x2a\x95\x4a\x55\x19\xfd\x7f\xcb\x28\xa3\x7b\x9d\xc9\x32\x19\x17\x51\x9a\x10\xda\x29\x7a\x49\x2f\xeb\x7e\x51\x29\x79\x27\xed\x2d\xbb\x5f\xa2\x49\x67\x3d\x39\x49\x4f\xf9\xaf\x02\x7e\x9d\x05\x19\x09\xf6\x8a\xcb\x05\x4d\x27\x44\xd6\xb5\xd7\x92\x45\x5b\x0f\x1e\x88\xc4\x5d\x56\x66\xf9\xe0\x41\xd0\xcd\x68\xb1\xcc\x12\x12\x74\xd2\xde\xfa\x66\x97\xa5\x47\x32\x2d\x12\x69\xac\xd6\xc9\x5e\x42\xcf\xc9\xcb\x2c\x4b\xb3\x4e\xeb\x20\x48\x92\xb4\x20\x93\x28\x09\xc9\x3c\x0d\x97\x31\x25\xed\xd6\x46\xba\xd1\x6a\xb7\xba\xbb\xc5\x2c\x4b\xcf\xc9\xa4\x3f\x4e\x43\xba\xd7\x7a\x73\xf4\xe2\xf8\xf5\xcb\x4f\x6f\x8f\x3e\x7e\xfa\xf1\xe8\xf8\xed\x8b\x56\x6f\x72\xcd\xea\x8b\xf7\x58\xdf\xf7\xbe\xd0\x8b\x45\x9a\x15\xf9\xf0\xcb\xf5\xf5\x2e\x1b\xc3\xc9\xe6\x69\x7f\x1c\xc4\x71\x27\xee\x8b\xac\x9e\xec\x7d\x87\xf2\x01\x26\x7b\x00\xb8\x75\x7a\x42\x4f\x77\x45\x57\xf3\x4e\xf2\x2c\x19\xd2\xee\x75\x2f\xee\xe9\x92\xb4\xc7\x71\x77\x2d\xa0\x58\x93\x32\x13\x7a\x11\x35\xc2\xd5\x24\xcd\x3a\x0c\x3a\xdd\xdb\xdc\x4d\xbf\xcf\xfa\x31\x4d\xa6\xc5\x6c\x37\xdd\xd8\xe8\xe6\x9d\x8c\x21\x5e\x75\xe3\xba\xdb\xf9\xb2\x35\x3c\x51\x5d\x16\x55\xf4\x38\x96\x7a\xa2\xed\xee\x97\x35\x9e\x20\x3b\xb3\x77\xb2\x46\xc8\x97\x35\x42\x08\x69\x8d\xd3\x24\x2f\x82\xa4\x68\x0d\x49\x91\x2d\x69\x8f\xa7\x46\xc9\x62\x59\xe4\xad\x21\x39\x81\x6f\x09\x0d\x79\x49\x30\xa7\xad\x21\x69\x7d\x4a\xcf\x13\x9a\xb5\x7a\x3a\x87\x8d\x8e\xe5\x04\x61\x98\xd1\x3c\x6f\x89\x9c\x6b\xf8\xff\x54\x54\x2d\x8b\xc3\xff\x22\x2d\x5d\x16\xf5\xed\xa5\x9f\x50\x11\xa3\xbd\xd1\x65\x41\xf3\x9d\x6d\x7f\x7b\x12\x48\x61\x7a\x8d\x90\xeb\xde\x9d\x20\xe0\x46\xfd\x51\xc3\x41\xd8\x6b\x86\x80\x95\x51\xfd\x47\x1d\xfa\x38\x4d\x0a\x9a\x14\xb7\x1e\xfc\x9f\x72\xde\xd9\x8c\xfd\x61\xa6\x7d\x12\xc4\xf9\x6f\x37\xf4\x8c\xe6\x34\x3b\xf3\xad\xfa\x3f\xfa\xa4\xe5\xcb\xd1\x7b\x3a\x8d\xf2\x22\x0b\xfe\x0b\x26\xaf\x57\x55\x07\x3d\x3f\xba\x15\xdf\x2f\xb2\x20\xc9\x27\x5e\xd6\xf7\x67\xc1\x41\x66\x91\xc2\xea\x48\xc8\x69\xf1\xa1\x9a\xa4\xee\x0c\x17\x76\xd3\xbf\x49\xa3\x5f\x79\x02\x82\x26\x88\xaf\xaa\x60\x91\x45\xf3\x20\xbb\xf4\xf6\x23\x4d\xe3\xda\xc9\xdb\x17\x6d\xfd\x79\x51\x68\xee\xc1\x95\xd5\x94\x21\xe1\xa0\x74\x1b\xff\x23\x21\xc1\xdb\xfb\x30\xca\xd3\xf3\xe4\x16\x3d\x0f\x92\x34\xb9\x9c\xa7\xcb\x7c\x85\xae\x47\x49\x48\x2f\x68\x68\xec\x5d\x77\x36\xb1\xba\x72\xd4\x1d\xb3\xf6\xf3\x28\xb9\x0d\xe3\xde\x5f\x02\x26\x5e\x26\x21\x0d\x5b\x16\x9a\xe8\x19\x23\x84\xbf\x00\x8e\x46\x51\x18\x36\xc3\xd1\xcd\xea\x3f\x0b\xe2\xa5\xb7\xfb\xcb\x28\x29\xb6\xbf\x79\x52\x3d\x05\x6f\xe9\xf9\xf3\xe8\x77\x44\xfe\xad\xd6\xdc\xc1\x2c\x48\xa6\xbf\x27\xe9\xdc\x09\xe5\x94\xd4\x8d\xa4\xfa\x4a\xaa\xf1\x62\xe6\x1d\xdf\x8d\x6a\x11\xb4\x76\xba\xb6\x76\xdd\xfb\x72\x7d\xda\xdb\xfe\xdd\x0e\xfd\x7f\xa1\x33\xef\xef\x24\x3b\x4e\x96\x49\x78\x63\x52\xb9\xf5\xc6\x75\x7f\xec\xfd\x73\x1f\x7b\xef\x0f\x7d\x7f\xe4\x33\x87\x77\xf0\xe2\xbc\xf0\x47\x93\x36\xbf\xee\x66\xae\xf7\xaa\x9d\x3b\xdb\xab\x56\x9d\xf7\x49\x96\xce\x6f\x39\xed\x45\x7a\xcb\xa3\xe6\xed\x04\xbe\xdf\x77\xdd\xfc\x11\xf0\x17\x25\x61\x94\xd1\x71\x71\xe8\xdd\x33\x57\xe8\xc9\xed\x26\x22\x1a\x07\x8b\x8f\xbf\xeb\x64\xf8\x31\xd9\xec\xb4\x4b\x17\x69\x1e\x55\x1d\xd4\x17\xc1\x65\x30\x8a\xa9\x29\x14\xfc\x2e\x5c\xa9\x8c\xe6\xee\xe4\xf8\x75\x3b\x1a\xd8\x97\xe3\x7d\x61\xe2\xf3\xb7\x3f\xc9\xdc\x09\x92\x4a\xea\x6e\x46\x67\xbf\x03\xfa\xff\xb0\x58\xbf\x8b\xf3\xe3\x8d\xf9\xe4\xd7\xc6\xba\xcd\xf4\xee\xd1\xde\x10\xed\xb7\xde\xb8\xbe\xf6\xcc\x1e\x7a\xb6\xb4\x2a\x39\xee\x71\x13\x39\x0e\x8c\x37\xc8\x9e\xb4\x70\xe8\xb4\xfb\x83\x49\x9a\xcd\x83\xa2\xa0\x59\xde\xee\xee\x02\xc0\x87\x34\x8e\xc2\xa8\xb8\xfc\x78\xb9\xa0\x26\x2c\x6b\x9f\x41\xad\x0d\x1e\x3e\x5c\x23\x0f\x0d\x48\xa1\x73\x27\x51\x4e\x02\xb2\xc8\xd2\x94\x01\x93\x62\x16\x14\x24\xa3\x0b\x76\xc8\x4a\x8a\x9c\x88\xb9\x23\x2c\x93\xd5\x70\x58\x90\x79\x50\x8c\x67\x34\x1f\xb2\x4f\x91\x8d\x7e\x9e\x9c\xe2\x8f\xc7\xc6\xd7\xa9\x99\xb9\x63\x7d\x9f\x9e\x3c\x39\x3d\x39\xed\x91\x7e\xbf\xbf\x46\x1e\x0e\x9c\xb1\xc9\x1e\xef\x11\x65\x4d\xd3\xe9\x8a\x29\x2e\x66\x51\xde\xff\x04\x0b\xe3\x47\x89\x20\x06\xd8\xe7\xe8\x3a\x64\x19\x87\x49\xb1\x8b\x80\xf9\xbe\xed\x83\x3e\x82\x1c\xd1\xdc\xee\xda\xf5\xee\xda\x9a\xa7\x1f\xfd\x45\x96\x16\x1c\x6b\x7b\x24\xa1\xe7\x46\x5f\x3b\x5f\xae\xbb\xbb\xd5\xa5\xfa\x20\xbd\x64\xcb\x71\x91\xb2\xc6\x3d\xb0\x75\xed\xf6\xa3\x5c\xcc\xb9\x46\x08\x23\x47\x89\x14\x61\xd7\xb2\xbe\xce\x12\xfb\x30\x6f\x9d\x81\xc0\x76\xe7\xdf\x27\x9d\x93\xcd\x47\xdf\x9d\x3e\xec\xfe\xfb\xb4\xfb\x6c\xd0\xe5\xe3\x34\x0f\x0e\xa5\xdd\xba\xee\x7d\x69\x61\x52\x6c\x0d\xbf\xeb\xb5\x38\xbd\xb5\x86\x5b\x8f\xaf\x4f\x7b\xdf\xfc\xce\xe4\xfd\x3c\x4d\xe3\x1a\xda\x1e\x31\x90\x12\xc2\x66\x79\xf2\x7f\x4e\xa5\xf0\xeb\xb1\xfe\x79\x8a\x92\x77\xf0\x47\x1d\x19\x43\xcf\x6e\x4a\xc3\xac\xf0\x2a\x44\xcc\xe1\x6d\x0a\x66\xa9\x2b\x92\xaf\x59\xa4\x82\x76\x79\x8b\x55\x65\x6f\x42\xb5\xff\x61\xa8\x35\x69\xf6\xe1\xff\x34\x22\x5a\xd1\x9f\x7a\x8a\x7d\xf2\x7b\x53\x2c\xdb\xc3\x14\xc9\x16\x7e\x9a\x2d\x66\x94\xc0\x66\x07\x84\xdb\xf7\x51\x2e\xcb\x55\x3f\x04\x5d\xc2\xcf\xc7\xe8\xf7\x29\xce\xd8\x31\xbe\x4c\xfa\x25\x62\x6b\x55\x3f\x9f\x1a\xf5\x88\xa2\x1e\x2a\x87\x4e\xde\x98\xcc\x59\xe9\x95\xe8\x9c\x17\x70\x08\x9d\x25\xaf\x4a\xe9\x66\x99\x2a\x52\xe7\x8d\x56\x96\xbe\x19\xb1\xb3\x4a\x38\xa9\x7f\xd9\xea\x5d\x77\x6f\x46\xf8\xa2\x77\xf5\x94\xff\x6d\x13\xca\x1f\x3c\x84\x0e\x7f\x9c\x45\x39\x99\x44\x31\x65\x94\xba\x08\xb2\x82\xa4\x13\x72\x4e\x47\x3b\xfd\x5f\xf2\xfe\x1a\x80\x88\x2f\x06\x30\xc9\x28\x25\x79\x3a\x29\xce\x83\x8c\x0e\xc9\x65\xba\x24\xe3\x20\x21\x19\x0d\xa3\xbc\xc8\xa2\xd1\xb2\xa0\x24\x2a\x48\x90\x84\x83\x34\x23\xf3\x34\x8c\x26\x97\x50\x47\x54\x90\x65\x12\xd2\x0c\x08\xbe\xa0\xd9\x3c\x67\xed\xb0\x8f\x9f\xde\x1e\x93\xd7\x34\xcf\x69\x46\x7e\xa2\x09\xcd\x82\x98\xbc\x5b\x8e\xe2\x68\x4c\x5e\x47\x63\x9a\xe4\x94\x04\x39\x59\xb0\x94\x7c\x46\x43\x32\xba\x14\x54\x44\xc9\x8f\xac\x33\x1f\x44\x67\xc8\x8f\xe9\x32\x09\x03\x36\xe6\x1e\xa1\x51\x31\xa3\x19\x39\xa3\x59\xce\x66\x68\x47\xb6\x25\x6a\xec\x91\x34\x83\x5a\x3a\x41\xc1\xc6\x90\x91\x74\xc1\x0a\x76\x49\x90\x5c\x92\x38\x28\x74\x59\x17\x05\x7a\xa4\x21\x89\x12\xa8\x76\x96\xca\x95\x1d\x15\xe4\x3c\x8a\x63\x32\xa2\x64\x99\xd3\xc9\x32\xe6\x82\xe3\x68\x59\x90\x9f\x0f\x3f\xbe\x3a\x3a\xfe\x48\xf6\xdf\xfe\x8b\xfc\xbc\xff\xfe\xfd\xfe\xdb\x8f\xff\xda\x25\xe7\x51\x31\x4b\x97\x05\x61\x12\x25\xd4\x15\xcd\x17\x71\x44\x43\x72\x1e\x64\x59\x90\x14\x97\x24\x9d\x40\x15\x6f\x5e\xbe\x3f\x78\xb5\xff\xf6\xe3\xfe\xf3\xc3\xd7\x87\x1f\xff\x45\xd2\x8c\xfc\x78\xf8\xf1\xed\xcb\x0f\x1f\xc8\x8f\x47\xef\xc9\x3e\x79\xb7\xff\xfe\xe3\xe1\xc1\xf1\xeb\xfd\xf7\xe4\xdd\xf1\xfb\x77\x47\x1f\x5e\xf6\x09\xf9\x40\x59\xc7\x28\xd4\x50\x8f\xe8\x09\xcc\x59\x46\x49\x48\x8b\x20\x8a\xe5\xfc\xff\x2b\x5d\x92\x7c\x96\x2e\xe3\x90\xcc\x82\x33\x4a\x32\x3a\xa6\xd1\x19\x0d\x49\x40\xc6\xe9\xe2\xb2\xf1\x44\x42\x65\x41\x9c\x26\x53\x18\xb6\xa2\x32\x42\x0e\x27\x24\x49\x8b\x1e\xc9\x29\x25\xdf\xcf\x8a\x62\x31\x1c\x0c\xce\xcf\xcf\xfb\xd3\x64\xd9\x4f\xb3\xe9\x20\xe6\x15\xe4\x83\x1f\xfa\x6b\x0f\x07\x92\xd9\xfe\x0d\xc8\x76\x9c\x86\x34\xeb\xff\x02\x2c\xf2\x6f\xc1\xb2\x98\xa5\x19\x79\x13\x64\xf4\x33\xf9\xdf\xb4\xa0\xe7\xd1\xf8\x57\xf2\xfd\x9c\x7d\xff\x8d\x16\xb3\x90\x9e\xf5\xc7\xe9\xfc\x07\x00\x0e\x83\x82\x92\xed\xcd\xad\x6f\x80\xe1\xd5\x6f\x05\x15\x02\x2c\x2a\x23\xe4\x31\xdf\xde\x21\x24\x05\x04\xcc\x76\x41\x1f\xe4\x61\x52\x98\x80\x51\x52\xf8\xe0\x8e\x1d\xc0\x65\x09\xe4\x8b\xcb\x24\x98\x47\x63\xc9\xc6\x51\x89\x90\xe7\x00\x8f\xf2\x95\xfc\x50\x64\x51\x32\x35\xcb\xe4\x90\xe6\x83\x7e\x4f\x03\x6b\x8c\x19\x0d\xbc\x63\x3c\x76\x41\x97\x65\xb0\x9e\x6e\xab\xfe\x02\x70\x94\x8b\x01\x1a\x9c\x39\x47\x55\xf4\x60\x87\x15\x7c\x5a\x5a\x88\xa3\xfc\xbe\xaa\x02\xb6\x11\x0e\x7c\x75\xa5\x4e\x8f\xa4\x04\x7a\x3f\xcb\x82\x4b\x0e\xce\x99\xb8\x25\x0a\x1c\x30\xfa\x44\x12\x80\x58\x49\x9c\x43\x84\xa4\x48\x09\x4d\x18\x0d\x0f\x42\xca\xfe\x53\xad\x30\x66\x1c\x70\x36\xc9\xb8\x92\x90\x6b\xcd\x8d\x99\xd7\x8d\x47\xcc\xc0\x72\x73\x67\x86\x24\xb2\x07\x35\xe4\x46\x17\x81\xf7\xcf\x69\x31\x4b\x43\x4f\xb7\xb8\x72\x3d\xcd\xe6\x84\x4b\x2e\xa9\x31\x23\x6b\x84\xaf\x41\x51\xfc\x93\x98\x19\x91\x45\xfe\x06\xbd\x27\x5f\x38\xf1\x5c\x2b\xb1\xfc\x6f\x1c\xf3\x39\xf9\x82\x2b\xbb\x86\x2c\x78\xab\x90\x93\x2f\xf0\xae\xe1\x9a\x88\xcf\x88\xf1\x06\x2e\x11\x31\x32\x84\xbe\xb0\x9d\x88\xb1\x7b\x40\x88\x81\x0c\xb4\x53\xe3\x2e\x39\x38\x92\x28\x62\xd8\xcc\x4d\xf1\x0e\x61\xad\x3f\x89\xe2\x82\x66\x1d\x54\xb6\x8b\x74\x10\x82\x8a\x0a\x21\x14\x48\x22\x00\x9d\x42\xf7\x64\xf3\x74\x97\xf3\xcf\x68\x42\x3a\xeb\xb8\x11\x5c\x07\x7f\xa0\xc1\x9f\x72\xb4\xa3\xe4\x2c\x88\xa3\x50\xd3\x00\xab\x71\x7d\x48\xda\x64\x83\xe0\xca\xd7\xb0\xac\x81\x6b\x36\x29\xb0\x84\xd2\xc8\x22\x0e\xa2\x84\xd3\x97\x35\x8d\x1c\xe0\x9d\xc8\x29\x9f\x45\x91\x7e\x34\xfa\x85\x8e\x8b\x6b\xab\x42\x39\xc9\xba\x1c\xaf\x36\xb4\xe0\xca\xa7\x0e\x75\xc3\x99\xb9\x1e\x2f\x6f\x09\x5c\x30\x69\xa8\x58\xde\x39\x61\xc0\xa7\x3d\x72\x02\xe0\xa7\xdd\x66\xa8\x89\xa3\x1c\x24\x20\xbe\xf8\xca\xb1\x93\x63\x34\x00\x0b\xe0\xd8\xf1\xa5\x2f\x74\x81\x32\xc4\x38\xcd\x36\xc2\x4d\xee\x2e\x7d\x81\x9d\xbc\x8c\xbe\x73\x49\xe0\x53\x5a\xe0\x15\x98\x0b\xce\x21\x48\x96\x15\x13\x7d\x63\x25\x8c\x1a\xfa\xf3\x60\xd1\x29\xe3\xb1\xa0\x95\xf3\xac\x11\x83\x77\xf2\x9a\x3b\xbc\xa7\x27\x50\xe4\x94\xb3\x67\xf9\xa5\x56\x11\xea\x8f\xd8\xa7\x8e\x26\x93\x9c\x16\x4e\xa7\x32\x1a\x2e\xc7\x14\xf5\x2b\x18\x8f\x7b\xa4\xa6\x73\x80\x9d\x22\x28\xa2\xf1\xbb\x20\x2b\x5e\xc3\x4b\x22\xab\xe6\xbe\x9d\xdf\xf1\xf4\x53\xd6\x95\x31\xa6\x44\xc3\x0f\x6e\x95\x6f\x82\x62\xd6\x9f\xc4\x69\x9a\x75\x3a\x4e\x8b\x1b\x64\x67\xab\x4b\x06\x64\x67\xbb\x4b\x1e\x92\x9d\x6d\x31\x68\x84\xbe\x60\x3c\x26\x1b\xa4\xa3\x36\x1d\x03\xeb\x25\x28\x24\xcf\xd0\xde\x45\xc8\xce\x36\x19\x1a\x09\x25\x9d\x95\xa8\xef\x91\x4d\x8c\xfd\x8c\xe6\xcb\xb8\x90\xd4\xc3\x67\xf0\xcd\x32\x2e\xa2\x9f\xa3\x62\xc6\xe7\x44\x52\xa0\xd1\xb7\x9e\xa2\xa3\x9e\x39\x83\xb2\x72\x31\x42\x5e\xbf\x79\xe2\xf3\x93\xbe\xd5\xaa\x6f\x0d\x34\xec\x01\x5a\x23\x6a\x78\xad\xd6\xae\x5e\x38\x34\x9e\x88\x11\x8b\xce\x8a\x5d\x21\xcd\x5e\x06\xe3\x59\xc7\x66\x4c\x11\xa6\x2d\xc6\xf5\x4b\xe7\x4b\xcf\xd5\x69\x17\x17\xe2\x08\x81\xae\x6c\xb8\xda\xce\x8e\xd9\x7d\xb9\x8e\x10\x11\xaa\xb5\xcb\xa8\x98\xc6\x13\x01\x62\xcf\x11\x74\xc0\xed\x92\xc4\x13\x7c\xd8\x93\x85\x9b\x30\x97\xe2\xc6\x1e\xa1\xe2\x19\x1e\x19\x90\x6d\x0d\x7a\x4d\x68\x9c\x53\x6b\x78\x83\x01\x09\xd3\xa4\x5d\x90\x20\x0c\x89\x28\x55\xa4\x66\x95\x7d\x12\x15\xed\x9c\x04\x71\x46\x83\xf0\x92\x8c\xd3\x65\x52\xd0\xb0\x04\x4b\x5f\x69\x9c\xd7\x7a\x11\x0e\x06\xe4\xe3\xd1\x8b\xa3\x21\x99\x44\xd3\x65\x46\x09\x3b\xb0\x25\x34\x67\x27\x40\x76\x4a\xbb\xcc\x4d\x66\xf5\x5b\x10\xc9\x1f\x67\x92\xcd\xc9\xa0\x18\x81\x12\x2b\x25\xcb\x5c\xa1\x35\xa3\x93\x00\xd4\x31\xe7\xb3\x34\xa6\xbc\x87\x51\x32\x5d\xaf\x61\x04\x15\x3c\xc0\xe6\xfc\x62\xd0\x3d\x92\x3a\x2b\xdf\x58\xe4\x72\x4e\x6a\x45\x7d\xcf\x16\xd7\x71\x55\x63\x88\x80\x78\xc3\xe4\x3c\xd0\x64\x9d\xd3\xc2\x99\x53\x4e\x56\x6f\x83\x39\xb5\xf7\x21\x9d\x83\xe5\x4c\xb7\xac\x67\xf3\xa9\xde\xcf\x74\xc5\x9e\x3a\x15\x5f\x14\x18\xd4\x52\xad\xfc\xab\x18\xb6\xac\x64\x91\xd1\xb3\x28\x5d\xe6\xaa\x43\xdb\xbb\x0c\x25\x51\x42\xa2\xa4\x70\x4a\xd4\xe1\x1f\xf5\xd7\xd7\x20\xfb\x9b\xa4\x19\x81\x47\xc2\x11\xd9\x23\x5b\xbb\x24\x22\xdf\xcb\x01\xc8\xf7\xc2\x24\xda\xd8\x28\x2b\xce\xfe\xac\x3e\x6f\xec\x91\x8d\x8e\xc4\x41\x44\x1e\x91\xad\x53\x26\xe1\x93\xab\x2b\xb2\xb9\x5b\x5a\x49\x05\x2b\x17\xf4\xb0\x41\x22\xf2\xb0\x6c\xe6\x36\xec\x5e\x30\xe1\xa0\x8c\xed\xcb\xbf\x6b\x27\xd5\x4c\xb9\xee\x76\xba\xd6\x14\x0e\x06\x64\x12\x65\x79\x41\x68\x4c\xe7\x34\x29\xd8\xf9\x8a\xa3\xa9\x47\xf2\xcf\xd1\x82\x44\xc5\x2a\x53\x6e\x60\x7f\xd3\x87\x7d\x86\xbf\xca\x19\x80\xa7\xf3\x61\x18\xb1\x46\x82\x58\x2d\x72\x81\x4f\x87\xff\xb8\xf8\xf6\xf3\x45\x4d\x3a\x25\x0c\xe2\x24\x22\x1b\x64\xeb\x54\xf2\x09\xb2\x41\x9c\x6e\x78\xd0\x5e\x8b\x60\x8b\xf9\x79\x20\xc5\x56\xe9\xa1\x7d\x4e\x15\x37\x66\x3d\x7f\x68\xa6\xc2\x84\x2d\x13\x53\xb7\x5c\xfc\x35\x94\x49\xca\x18\xd2\x66\x15\x43\x22\x8d\x68\xba\x96\xa3\x0c\x06\x64\x1c\xc4\xe3\x65\x1c\x14\x54\x0a\x3e\xec\xc8\x27\xfa\x42\xa2\x82\xce\x6f\xc1\x8e\x18\x2b\x3a\xf9\x13\x31\xa5\xae\x0d\x7b\xbd\xd2\xbe\x72\xcb\x09\xf9\xfd\x18\x0c\x66\x2e\x5f\x9d\xb7\x10\x47\x5b\x24\xfa\x51\xa3\x0d\x11\xba\x48\x71\x33\x99\x56\x68\x8c\x38\x64\x63\x8d\x91\x4c\x57\xb7\x9a\x4a\x25\xe2\xd7\x25\x95\xeb\x41\x50\xc3\x1e\xf1\x0f\xea\xf7\xe9\x88\x50\x31\xad\x23\xe2\xd0\x20\xdb\x34\x41\x4b\xa5\x92\xa8\x04\x21\x65\x3a\xa2\x72\x84\x88\x12\x70\xc2\x80\xd6\x34\x62\xaa\x35\x44\x78\x88\xbe\xd3\xb1\x81\x9b\xd5\x15\x44\xb2\x14\xa7\x62\x0c\xcf\x89\x38\xf7\x9e\xc2\xad\xe3\xfe\x1d\x6b\x94\xf8\x90\x3b\x30\x32\xb9\xbe\xb4\x5a\xc4\xd0\x8b\xc8\x1a\xb5\x86\xa9\x4a\xe5\xa0\x47\x55\xab\x67\xc0\x18\xe5\x1c\x88\x95\xb9\xeb\x91\x36\x51\x47\xa9\x93\xa8\x4f\x0e\x16\x5d\x2b\x65\x92\x83\x01\xc9\x97\x73\x7e\x43\xe7\xd9\xa5\x84\x88\xa8\xe0\x45\x75\x27\xd1\x29\xe3\x8a\xea\x0b\xb6\x24\x1f\xff\x91\xcd\x9b\x88\x90\xd2\xa6\x83\x82\xc1\x80\x64\x74\x9e\x9e\xc1\x35\x26\x19\x2f\xb3\x8c\xc9\xa7\x4a\x38\x4d\x21\x59\x74\x33\xca\xa1\xe7\x9e\xde\xe6\xab\x68\xfc\x24\x32\x1b\x6b\xfe\x8c\x91\x91\x47\x4e\xfd\x8d\x29\xed\x83\xb5\x0e\x4b\xae\x75\xbc\xa7\x56\xc9\xe3\x3c\x54\x56\x58\x57\x0e\x92\xac\xd8\x0e\x86\x2f\x49\xcc\xfb\x0b\xde\x5b\xd6\xd6\x58\xdc\x32\x61\x53\x0b\xe8\x7d\x87\xdb\xab\xda\x26\x18\xe2\x5a\xb4\xd3\xed\x79\xb3\x9f\xa7\x69\x5c\x96\xc7\x84\x90\x92\xac\xe3\x8a\x3c\x7c\xb9\x59\xda\x6c\x55\x26\xe7\xc2\x65\xb9\xef\x69\x50\xda\xe3\x63\x9e\xb9\xc6\x08\xc2\xb5\xdf\x00\xd4\x29\x9b\x0d\x69\x38\x3b\x7c\xdc\x6b\xf1\xbb\xdf\xd6\xf0\x1b\xf8\xc9\xfa\xd6\x1a\x3e\x61\xbf\xf1\x75\x6c\x6b\xf8\xb4\xe7\xb3\xf5\x88\x92\xa2\x35\xdc\xda\x64\x3f\x33\x1a\xc4\xad\xe1\xd6\x36\xfb\xcd\x6f\x65\x5b\xc3\xad\x1d\xf6\xb5\xe4\x50\xd0\xc0\x52\x80\x3d\xb9\x3e\xed\x3d\xfd\x2d\xed\xa2\x6a\xae\xa1\x6f\x66\x4d\x84\x2b\x59\xc5\xa8\xc8\x2c\x67\xdb\x16\xe1\xdc\x15\x4d\x8c\xfc\x45\x2b\x2c\x8d\xcc\x9e\x34\xa9\xeb\x16\x76\x47\x25\xc6\x46\x8d\x1a\x45\x57\xe2\xde\xe9\x92\x6c\x27\x5b\xd2\x06\x26\x4c\xd6\xb0\xeb\x2d\x99\xbe\xbb\xb7\x64\xba\xb7\x64\xfa\x6f\xb1\x64\xd2\x0b\xe1\xae\xcc\x99\x9e\x47\xd3\xb7\xcb\xf9\x08\x58\xa1\xe2\xce\xa3\x68\x9a\x40\x62\xff\x17\xc5\xc9\x97\x45\x14\x9b\xf6\x35\xfd\x01\xa4\xf1\x7f\x25\xd8\xd8\x0b\x32\x4e\x93\x49\xe4\x18\x03\xc9\x93\x19\xda\x15\xe0\xec\x02\xdb\x82\x1c\x38\xe7\xd5\x39\x01\x7e\x4f\xe0\xc1\x06\x3b\x67\x31\xbe\xa5\xad\x64\x61\x29\xb0\xb9\x01\xe5\xcc\x43\x86\x63\x0e\x19\xe5\x24\xa1\xd3\xa0\x88\xce\x68\x4f\x72\x22\xb8\x38\x2a\xce\xd3\x76\x4e\xc6\xe9\x7c\x21\xa5\x55\x28\xc5\xe6\x56\x95\x9c\xc4\x69\x50\x44\xc9\x94\x2c\xd2\x28\x29\x7a\xfc\x3a\x94\x91\x7d\x98\x9e\x27\xd6\x99\xce\x54\x93\xb8\xc7\xb7\x2b\x8e\xe5\x2b\x85\xef\x6b\x39\x16\xb6\x94\x12\x4a\x43\x38\x45\x8f\xf4\x1c\x87\x7e\x63\x18\x40\xda\xb5\xb2\xf3\x31\xdb\x35\x18\x30\xd4\x2f\xb9\xb0\x6a\xb7\xcf\xe7\xa2\x33\xee\xbf\xfc\xf8\xea\xd3\xf3\xc3\x9f\xde\x1e\xbf\x79\xfe\xf2\xfd\xa7\xf7\x47\xc7\x6f\x5f\x1c\xbe\xfd\xe9\xd3\x9b\xa3\x17\x2f\xd1\x19\x4e\x69\xe2\x60\x26\xfb\x8b\x20\x7c\x4d\x27\x45\x87\x7f\x15\xe9\xc7\xf3\x34\x3f\x50\x58\x14\x6d\xf6\x8b\x54\x88\x4b\x5b\x4f\xba\x3d\xf2\xe4\xb1\x79\xc3\x83\x77\x4b\x18\x4e\x87\x37\x62\x1a\x60\x98\x13\x2f\x0f\xbf\x25\x38\x7f\xae\xce\xc6\xe6\xa1\x79\x55\x1c\xba\x52\x87\x81\x45\x0f\x42\x8a\xf4\x15\xbd\x90\xe3\xce\x97\xa3\xbc\xc8\x3a\xdb\x08\x7f\xb1\x75\xb5\xcf\x8b\x4b\x2d\xf7\x06\x79\xb2\xd3\x25\x03\x8c\x22\x1b\xdd\xef\xa3\xe9\xac\x10\xc5\x7a\x24\x26\x0f\xbf\x32\x3e\xc5\x0e\x7c\xa7\x68\x2d\x95\xe9\x6e\x8d\x5d\x79\x3c\x33\xd1\xaa\xb4\x73\xbf\xdb\x0c\x58\x6a\x53\xde\x58\xb7\xcf\xd7\xfc\x06\xa9\x9f\xa0\x3a\x4e\xc7\x25\xf9\xf2\x15\xf1\x41\xe6\xdf\x76\xee\x94\x71\x67\xf3\x59\x9b\x64\xe9\xfc\xb8\x98\x3c\xbd\x9f\x38\xcf\xc4\x89\x77\x46\x65\x8c\x4c\xbc\x42\x92\x93\xc6\xbe\x69\x90\xac\xce\xc8\xec\x27\x47\xe5\x73\xd6\xde\xbc\xdd\x5f\x9b\x6c\x88\xea\xc9\x33\x42\xda\x5b\x6d\x32\x24\xed\xcd\xf6\xed\x79\x54\x1d\x26\xd9\x89\x95\x95\xfa\x07\x83\xcb\x09\x13\x8c\xe7\xcb\xb8\x88\xb8\x50\x39\xba\x24\xdb\xff\x99\x33\xf1\x5c\xd9\xd0\x05\xac\xe6\x82\x4e\x69\x56\xb1\x95\xbc\x17\xb5\xd6\xed\xdf\xab\xce\x88\xb0\x65\x2e\x99\x11\x81\x26\x8b\xfa\x18\xd6\x54\x8b\x6a\x73\x8d\xe6\x34\xb7\xb2\xb6\xbb\xfd\x45\x7a\xde\xd9\xda\x7e\xda\xed\x9a\x28\x3d\x98\xd1\xf1\x67\x12\x4d\x0c\x9c\x22\xb1\xc8\x42\x44\x1e\x4d\x13\x1a\x1e\xe6\x6f\x75\xb6\xa3\x88\x56\x75\xcc\xe8\x85\xe8\xb1\x89\x0c\x49\xb4\x70\xe8\x83\xb6\x0b\x53\x12\x4b\xd9\x91\xe5\x3c\x62\x62\x78\x10\xe7\xda\x6a\xd9\x6e\xbd\x16\x5f\x3e\x0c\x49\x76\xb3\xd9\x23\x5b\xdd\x1e\xd9\x7a\x82\xe4\x91\xed\xae\x91\xdb\x25\x7b\x7b\x7b\x8c\x64\xbd\x54\x98\x31\xf6\xf1\x28\x88\xa1\x53\x84\xab\x0e\xf4\x85\x07\x17\x35\x5d\x22\xe2\x8a\x04\x5b\x08\x34\xc8\xc3\xb1\x83\x65\x38\xd3\x82\x61\x45\xbb\x4a\x38\x84\x65\x11\x4d\x09\x97\xd3\x2d\x7a\x53\x5d\x30\xf0\x67\x18\xc5\x32\x60\x3e\x8f\x7b\xbc\x37\x48\x97\xd9\xe9\x92\xab\x2b\xd2\xda\x6c\x09\x1d\xf1\x60\x40\xc6\x8a\x8a\x98\xf0\x2c\x27\x52\xb5\xce\x81\x60\x96\x95\x98\xed\x4a\xd8\xf2\xf2\xd6\x9a\x64\x31\xb1\x1e\xfd\xa3\x67\x72\xf9\x7c\xce\xa3\x64\x69\x2f\x81\xf6\xe4\x96\x7f\x6d\xa8\x5b\x56\xbe\xa5\xee\xc6\x1a\x74\xe8\x06\xe4\xb3\xac\xa6\x9f\xe3\x4a\x02\xf2\x91\x0e\x5d\x89\x76\x44\xf3\x2e\xd5\x1c\xdf\x05\xd9\x7c\x1d\x94\x09\x7e\x5f\x86\x32\x87\x71\xd7\xa2\x0c\x30\x86\xe4\x61\x13\x45\xa2\x39\x17\x45\x0e\x27\xf7\x99\x9b\x5b\x2b\x51\xc0\xf4\xc3\xe8\x2c\x0a\x69\xf8\xfc\xb2\x82\x81\xdf\x84\x9a\x6a\x70\x73\x7c\xd7\xc8\x59\x96\x62\xe7\x78\x65\xf4\x1c\xdf\x06\x3f\xee\x15\x2c\xaf\x5a\xa1\xa8\x4c\xdc\xd2\xaf\xa5\x1b\xe3\x45\x6e\x6b\xe6\x5c\x94\xe2\x48\x34\xed\xa2\xc8\x11\xce\x7c\x18\xf2\x2c\x2f\xd8\xac\x6e\x29\xad\x6d\xb5\xc9\x33\xbe\x2f\x0b\xb7\x18\xab\x61\xb3\xf4\xd8\x88\x1e\xe5\x56\x6c\x7c\x31\x9d\x68\xc4\x31\xf1\xa1\xe2\x60\xe3\xc8\x1d\x49\x30\xa7\xfc\x75\x0f\xfb\x65\xc9\x5f\x02\x86\xd5\xa9\x6a\xf0\x60\xde\x39\x81\x42\x1b\x3d\x82\x35\xe5\xac\x90\x78\x5f\x4d\xf6\x48\xd9\x33\xdd\x87\xdd\x01\x3a\xcf\xe4\xd1\xaf\x82\x27\xe6\x70\x45\x25\xca\x9f\x6c\x9d\x9a\x72\x70\x7b\xf3\x82\xc9\xcb\xee\xe4\xf6\xf3\x38\x1a\x53\x26\x96\x6c\x93\x87\x50\xdd\x8a\x74\x5e\x33\x33\xf8\x08\x7e\x67\x13\xb4\x2a\xfa\x4b\xf5\x00\xce\x26\xa3\xce\x87\x16\x1f\xe0\x88\x13\x37\x60\x36\xe6\x9e\x3c\xee\x8a\x3d\xbc\x48\x05\x7c\x97\x3c\x94\x47\x4a\xdf\x0c\x58\x15\x71\xd1\xf0\xc9\xe3\x9e\x68\x7f\xb5\x29\xa8\x38\x92\xf3\xe1\x7b\xce\xe4\x77\x8a\xfd\x20\x1f\x47\x51\x15\xfe\x3d\x67\xf9\xdf\x10\xf3\x52\xa5\x03\xaa\x81\x66\xf8\x5f\x6d\x02\xb4\x6f\x9a\xb2\x19\xd8\xd7\xde\x6b\x4a\xa6\xa0\x94\xb7\x97\xa0\x5c\x55\xe8\x62\xdb\xe7\xbd\x66\x05\x69\xca\xc0\x5d\x6b\xf3\xa2\x45\x36\x88\x38\xe0\x00\xda\xf9\x6f\x65\x53\xf0\x78\xb3\x47\x70\x52\x99\xc3\x80\x2f\xd2\xee\x03\x1d\x34\x87\xd6\x77\xcf\x86\x81\x15\x3b\x74\x52\x1c\x38\xbc\xc0\x87\x65\x19\x4e\x29\x8e\xcc\xa1\x9b\xe4\xf6\x23\x4d\xe3\xa1\x9d\xe0\x40\x31\x09\x64\x68\x27\x60\x28\x25\x96\x0d\xed\x04\x17\xea\xd8\x01\x3b\xf6\xc2\xe1\x46\x75\x8a\xa7\x3e\x17\xf0\xd8\x0f\x89\x07\xab\x53\x3c\x70\x18\xdb\x28\xc9\x85\xf4\x4d\x8f\x9b\xe3\x96\x33\x27\x08\xa7\xb9\xb0\x82\xea\x87\xde\x75\x77\x2d\xef\x74\xcd\x9b\xa1\xd6\x70\xeb\x69\xaf\x65\xde\x28\xb5\x86\xdb\x60\xbe\x00\x0b\xa3\x35\xdc\xda\xea\xb5\xf0\xbd\x54\x6b\x68\x7e\x5e\x9f\xf6\xb6\x36\x7f\x67\x7f\x2e\x87\xdc\x30\xbe\xc2\x01\x51\x94\x14\x65\xfe\x87\xc4\xd5\x55\x94\x14\xdc\x35\x0b\xfb\xf1\x58\xfd\x3a\xd5\x89\x3b\xe8\xb7\xe5\xb9\x25\x4a\x0a\xee\xb7\x25\x4a\x8a\x27\x8f\x15\xd8\x53\x5d\xd1\xf6\x37\x4f\x4a\xea\x62\xf0\x35\x7e\x8c\xec\xa3\xe1\x57\x74\xc5\x05\xe0\xb6\x0d\xc2\x61\x52\xac\x68\x76\x61\x94\xa8\xb0\xb6\x80\xe6\x2a\x4a\xde\xc8\xb6\x22\x4a\x0a\x29\x2a\x3e\xbb\x91\x3f\x17\xde\xab\x7a\x1b\x88\xad\x46\x21\xec\xee\x8d\x20\xee\x8d\x20\xfe\xbc\x46\x10\x44\x5b\x41\x70\x51\xe9\x8e\x0c\x20\x1a\xd8\x35\xd8\xac\x9e\xdb\x2d\xa4\x60\x8d\xae\xdd\x76\xf4\x3d\x12\xea\xf9\x8c\x26\xea\xb1\x62\x8f\x1b\x7e\x33\x01\x5c\x79\x6f\x90\x92\xe5\xc0\x6b\x18\x61\xe9\xbe\xed\xb7\x89\xc0\x49\xa5\xfc\xc8\xff\xbf\xba\x22\xed\x36\xe2\xb3\xa9\x7c\xb6\xc0\x7f\xec\xa2\x77\x86\x51\x22\x5a\x6f\xec\xee\x63\x4a\x0b\x6c\xef\x0b\xd6\xe3\xed\x5c\xbe\x02\x05\x5e\xc2\x2a\x31\x4c\xdd\xb5\x7c\xcf\x2d\x5d\x4d\x29\x5a\xaa\x99\x74\xad\xb8\x32\xd2\x91\x7d\xec\x1a\xd6\xec\x80\x1e\x6c\xcd\x6e\x37\x52\x69\x87\x06\x26\xfe\xc6\xb1\x03\xdf\x3d\x36\x46\xc6\x38\xa3\x8c\x98\xe4\x7a\x30\x7d\xb2\x70\x72\x0f\xa3\xc9\x84\x82\x35\x32\x47\xb9\x75\x2e\x39\x57\x8f\x42\xf0\x71\x44\xa2\x44\xcc\x92\x34\x5c\x4e\xbc\x87\x10\xf3\xe8\xc2\xb6\x43\x5f\x3f\x82\x05\xe7\x30\xaa\x17\xe5\xa8\x3c\xf7\x3f\x98\x35\xe9\xae\xf4\x4a\x4f\x13\xa4\x22\xd5\x55\x30\x9a\xce\x47\x51\xe2\xba\xb7\x29\xd2\x29\x65\xdc\x9d\xd5\x40\xa7\x7d\xbe\xa8\x82\xc5\x82\x26\xb0\x96\x82\x84\x3f\x80\xb0\xb0\x2b\x6a\xab\xbb\x84\x11\x8c\x69\x16\x8d\x19\x7b\x92\xbd\xaa\x2f\x2c\x6e\x4f\xd3\x89\x80\x85\x7d\xa8\x12\xb5\x72\x78\x75\x7a\xbf\x2a\xb4\x2a\xbd\x05\xbf\x32\xd9\x25\xf5\xd8\x1d\x07\x71\x2c\xf0\x2b\xef\x70\xf8\x88\x66\x81\x5e\xba\x79\xf4\xab\xf0\x2c\x08\x77\x75\xb3\x20\xef\xb1\xff\x25\xa1\x81\xef\x5f\xcf\xa5\x1d\xc6\xb7\x32\x04\xf5\xeb\x4c\x2b\x51\xe3\x77\xcd\xe4\x5b\xb8\x62\x55\xac\xef\xed\x81\x74\x31\x89\x12\xeb\xa1\x52\x1d\x12\xb4\xcb\x22\x51\x95\xb8\x5e\xb6\x95\x06\x3c\x77\x3f\x7f\x5e\x7e\xf4\xe7\x1a\x5f\x57\x43\xd3\x60\x99\x19\xb5\x57\x0d\x7a\x1d\x46\xad\xdf\xff\x77\xc9\x33\xd2\x6e\x93\x61\x33\x6b\x2c\x84\x32\xaf\x4d\xd6\x0a\x78\x63\xbc\x9f\x2b\x27\x94\xcc\xe8\x7b\xeb\xa5\xf5\x17\x7e\x9c\xc9\xbd\x47\x5e\x09\x07\x98\xe1\x07\x73\x4c\x64\x40\xe2\x95\x58\xd4\x8d\x79\x51\x08\x4e\x95\x6c\xfc\xf9\x9c\x33\xa9\xe5\xb5\x4b\xf8\x95\x1f\x29\xa1\x3b\x31\x61\x9d\xd5\x51\x67\x6c\x6b\x25\xb8\x43\x9b\x92\x1f\x79\x32\x21\x90\x37\xf0\x0d\xb0\x48\xe7\x8b\xe2\x12\xab\x04\x1b\x6c\xa2\xb5\xab\xd0\xa4\x47\xc4\x9e\x86\x20\x7d\xac\x80\x1b\xe9\x6e\xaa\xd4\xd1\x94\x17\x13\x95\x03\x11\x55\xd6\x8d\xc1\xb8\x58\xd9\xf0\x88\x05\x37\x19\x87\x7e\x89\x57\xee\x1c\xea\x75\x94\x17\xce\xb3\xbf\x13\x63\x34\xa7\x1e\x8f\x50\x95\xa3\xd7\x35\xbb\xdb\x8b\x7a\x14\x24\xaf\xe9\x97\x8b\x90\x9b\xb5\x8a\x47\x70\x4a\x15\x59\xa4\x05\x7a\xe8\xca\x0b\x4b\xe1\x88\x3b\x1d\x22\xc6\xc3\x3e\xf5\x7e\x50\x80\x9a\x6f\x8a\x8c\xbd\x4d\xad\x47\xbe\x7d\x95\x2c\x48\xfb\xf6\xcb\xf6\x14\x62\x36\x4f\xf6\x70\x8f\x35\x2c\x1e\xc6\xc6\x9e\xab\xe8\x17\x4f\xb5\xdc\xe7\x59\x1c\x52\x8b\x40\x9d\x14\x3f\xb9\x55\x4f\xe6\x06\x03\x39\xdd\xf4\x8c\x66\x97\xc5\x0c\x1c\x91\xa0\x7a\x30\x76\x5c\xaf\x53\xd2\x1c\xcd\xc1\x8f\xf1\x4c\xd7\x7f\x43\xa1\x1c\x2f\xdd\x69\x13\xae\xd2\xf9\xba\x47\xda\x6d\xa9\x7c\xaf\x50\x52\xbc\xe3\xb3\x64\xe9\xf4\x94\xfa\xee\xfa\xb4\xb7\xd5\x28\xd0\xde\x57\xd4\xc9\xc1\x6d\x74\xb5\x52\x2e\x63\x20\x25\x5a\x39\x69\x63\xc6\xfe\xe7\xaa\x32\xf8\xf5\x58\xff\x3c\x45\xc9\x3b\xf8\xc3\xd2\xcd\xb1\x34\xae\x9c\x63\xbf\xa4\x76\x8e\xfd\x7e\x8a\xaa\x43\xfa\x39\xa7\xc6\x06\x1a\x3a\xe7\xee\x7d\x15\x15\x1d\x2b\xbc\x8a\x8e\x8e\xc3\xdb\x4a\x3a\x96\xba\xa2\x96\xce\x2c\x52\xa1\xa6\xe3\x2d\x56\x95\xbd\x89\xa2\x8e\xe1\xb6\x44\x51\xd7\xcc\x4b\xbe\xe8\x56\x03\x45\x5d\xa3\x50\x5e\x5f\xeb\x65\x9d\xe7\xf6\x6f\x15\xf2\xe0\xc5\x57\x21\x10\x59\xc2\x26\x11\x9e\xbe\x22\x91\xd8\x85\x2a\xc8\x44\xb6\x5b\x5d\xfe\x46\x3a\x5d\x2e\x49\x35\x79\x30\xe7\x69\xef\x6e\x9f\xca\xa9\x51\x36\xa0\xbb\xbb\x0f\x3d\x52\xf9\x78\xc7\xc3\x87\x91\x7f\xdb\x28\x6f\xee\xd8\x76\x4c\xb3\x22\x88\x12\xbf\x73\x5b\x07\x91\xfc\x36\xa9\x86\xa8\x39\x50\xdf\x4c\xaf\x26\x6b\x51\xc4\xca\xa8\x75\x05\x51\xd0\x6c\xce\x8e\xfc\xd1\x04\x6a\x36\xfb\x1d\x0a\x97\xb5\x64\x1a\x9d\xd1\x44\x9a\xb4\x98\x47\xea\x32\x5f\xb9\x96\xfd\x0b\x3f\x66\x6b\x73\x5b\xc0\x32\xaf\xdc\x69\xd7\x6f\x7c\x8b\x21\x9a\x2f\x11\xee\x99\xb6\x55\x78\x85\xe3\xf4\x8c\x66\xd9\x79\x16\x15\x05\x05\x73\x2f\xde\xab\x16\xd9\x80\xde\x37\xc6\xdd\x39\x68\xd9\x73\xfc\x8a\x1f\xac\x20\xf4\x51\x34\x4a\x04\x0a\x0b\xd7\xe9\xb0\xfd\xd0\xbe\x11\x32\x5d\xad\xa4\xd5\x9c\xd6\xda\x96\xe0\xcd\xe3\x3f\xc0\x8f\xc1\xc1\x00\x54\xe1\xc1\x9c\xad\x0a\x70\x79\x28\xb4\x59\x6c\xbc\x8c\x13\x50\x7e\xc7\x10\x47\x9f\x29\x09\x48\x1e\x25\xd3\x98\x2a\x27\x5c\x00\xd9\x37\xec\xa1\x81\x82\xb9\x8f\x19\xee\x93\x83\xb7\x76\x75\x45\x4e\xda\x27\x5b\xa7\xed\xd3\xae\x12\x06\x6b\x7c\x00\x88\xee\x99\x78\x67\x5f\xd8\xaf\x61\x89\xe8\xce\x6d\xa0\x38\x2a\xc0\x56\x61\xab\x47\x1e\x81\x31\xf6\x26\xf4\x65\x0b\x7b\xa1\xd1\x1d\x72\x04\x59\xe9\xa5\xa1\x27\xfd\x3a\x94\x9d\x16\xa4\x37\x87\x87\x12\x50\x37\x30\x18\x90\x20\x8e\xc9\x28\xc8\xa3\x31\x77\x7e\x00\x2f\x05\x76\xb6\x85\x02\x27\x4e\xd9\xc9\x58\xf6\xa6\x47\x76\xb6\xeb\x8c\x4e\xcc\x85\x2d\x38\x9a\x3c\x81\x4b\x5d\x24\xa1\x53\x10\x20\x21\x22\xd4\xc9\x69\x8b\xec\xfd\x00\xeb\x53\xa7\x3d\xe6\x89\x95\xca\xb4\x7d\x59\xdb\xaa\x1c\x60\x46\x4b\x7b\x56\xb1\xda\x71\xab\xa5\x34\xab\x7d\x7e\x19\xde\x60\x1c\xa2\xdb\xb5\xb6\x51\x54\xe4\xc1\x03\x82\xbf\x4f\xd0\x6f\xe4\xff\xed\x54\xee\xba\x2a\x2c\xc6\x60\x7a\xa3\xb9\x11\xcb\xb7\x6a\x6a\xe4\x2c\x98\x73\x23\x26\xcc\x9c\x1a\xe4\x6e\xed\x96\x33\x63\xf5\xab\x62\x62\x50\x9b\x5f\x7b\x5e\xee\x72\x62\x4c\xbf\x27\x9a\x91\xa2\x99\x80\xb3\x51\x0b\x6c\x11\xb6\x39\xd2\xf9\x21\xa9\x25\x8c\x15\xb6\xc4\x54\x6c\x3d\x56\x80\xdb\xa7\x27\x3b\x02\x54\xa6\x71\x10\x05\xb1\x75\x6a\x25\xe8\x6f\x77\x77\x00\xac\xde\x60\x7b\xc0\x63\x11\x43\xac\xdf\x13\x50\x63\x77\x34\x91\xd1\x84\x74\x50\x16\xe2\x90\x36\x3f\xbe\xe1\xc4\x02\xc3\xf6\xbd\x86\xd8\xaa\x98\x72\xb1\x49\xc8\x53\xb5\x6f\x9e\x61\xde\x7c\x53\xdd\x52\xc1\xf7\x9c\x09\x17\x9f\x2d\x63\xde\x8d\x8a\x4e\xcc\xca\xf1\x74\x6b\xd7\x6b\x8d\xe6\x59\x65\xf0\xa1\x88\xfc\xd2\xf9\x35\x5c\x28\x96\xee\xf6\xc2\x55\x51\x1c\xe4\x05\x39\x39\x65\xc2\x04\xaf\xf7\x46\xd3\xbe\xee\x9f\x77\x35\x07\x20\x67\x11\xc7\xc1\x12\x1c\x68\xf4\x33\x28\xf8\x54\x34\xd0\x84\x48\x2a\x8c\x63\xd1\x11\x46\x71\x60\xfb\xa6\x89\x8c\x2e\x49\x48\x27\xc1\x32\x06\x45\x68\xbe\x64\x72\xaa\xda\x98\x5b\xc2\x47\x4d\x4f\xc4\x78\xb4\x67\xd1\x38\x46\xdd\x80\x01\xeb\x1d\x71\x45\x51\xb8\xe1\xe9\xad\xd4\xa8\x5e\x3a\x6a\x97\x3a\x62\xb4\x44\x72\x7b\x8d\x00\xc5\x0b\x52\x3e\x69\x31\x8a\xef\x91\x16\x5b\x04\xec\xbf\xd3\xd6\xa9\xa6\x76\x01\x81\xd2\xa0\x50\xb2\x8c\xed\x67\x0f\x68\x36\x1b\xa1\xcd\xf6\x2e\x67\xf5\xb7\x66\x21\xb8\x1e\xaa\x9c\x95\xc0\xf7\x06\xe1\x29\x8f\xcf\x7a\x0e\x37\xbc\x6c\x38\xc6\x78\xd9\xbf\xb0\xea\x2d\x22\x16\xdc\xaa\xf3\xef\x13\x7e\x1a\xff\xf7\x69\xb7\x5e\x44\x10\xca\x5b\xe5\xea\xa1\xfc\xde\xc1\x8a\x61\x21\xa1\x9b\xb3\x0e\xf9\xf0\xd4\xbd\xcb\xb2\x70\xe6\xb9\xb4\x10\xf7\xe8\xf6\xc6\xe0\x75\x46\x6d\xde\xca\x08\x3f\xa8\xd2\x03\xaa\xcd\x16\x6a\x5c\xc1\x2a\xfb\x6f\x6c\x4c\xbc\x4b\x4a\xff\xfc\x5e\x51\x5d\xa7\xb2\x34\x9e\x60\x67\xb2\x82\x95\x39\x85\xd4\xb3\xe4\x93\x53\x9f\x07\xf1\xfe\x62\x99\xcf\x3a\x8e\x5b\x52\xf9\x4c\x5b\xfa\x18\x75\x6b\x66\x63\x71\x1d\xae\x9f\xf9\xbc\x7f\xe2\x96\x90\x13\xcf\xce\x59\x8f\x60\xe7\xb2\x96\x6f\xd2\x5b\x79\xf4\x15\x13\x88\x3d\xf9\xde\x7a\xfe\xa0\xeb\x8e\xd4\x21\x10\xff\xdb\xcf\x9f\xcf\x1d\x6b\x8d\x1b\xd6\xd2\x89\x60\xb3\x09\x7e\x52\x2b\xe6\x63\xe5\xd9\x58\x73\xee\x08\x2d\xdd\x91\xb1\x24\x91\x3b\xdb\x26\x0e\x41\xf9\xfd\xe8\x24\x4b\xe7\x5e\x73\x03\x0e\xe5\xe3\x2d\x23\xfb\xc1\x8e\x65\x20\x64\x58\x06\xad\xf0\x60\x4a\x32\x35\xde\x72\x03\x16\x25\x06\x82\x59\x94\xe1\x4c\xb3\x86\x55\x7d\x15\x5e\x05\x7b\x13\xbe\xb1\xe4\x82\xae\x78\xe2\x03\xdd\x93\x82\x8e\x40\xd7\x43\xb2\x0d\xc6\x0f\x5d\xe9\xce\x59\x20\xaf\x6c\x11\x55\xd6\x89\x9b\x77\x2a\xf6\xad\x28\x28\xf0\xa1\xe0\x77\xec\xb8\xf4\x06\xd9\xe1\x1e\xef\xf9\x6e\x9b\x33\x90\x9c\x04\x93\x82\x66\x6a\x91\xe0\xfe\xde\x68\xad\xfa\xcb\xf8\x1c\x77\x6b\xce\x51\xe2\xb0\x9b\x54\x62\x4f\xc4\x8d\x79\x5b\x56\x3f\x76\xea\x51\xea\x43\xda\x0e\x78\x53\xc9\x68\x1a\x72\x1a\xf2\xb0\xba\x6f\x0c\x76\x63\xaf\x1a\xa6\x11\xa3\x32\xbd\xcd\xa2\x69\xdf\x20\xd1\xdd\x72\xad\x3f\xc4\x1e\x82\xff\x1a\x52\xbf\x34\x48\x6d\xf8\xf7\x87\x22\xfe\x7b\xda\x47\x7f\xbf\x0b\xed\x13\x2f\xe9\xe3\xe8\x8c\x37\x25\x7d\x3b\x86\xd8\x8a\x9b\x8a\x43\xac\x76\xfd\xcd\x76\x16\xb3\x17\xab\xd4\x2f\xe6\xcf\x4b\x6f\xb1\x43\x5f\xfe\xf5\x57\xbe\x84\x17\xe2\xd6\xcf\x35\x52\xad\xeb\x7e\x87\x6c\x91\x0d\xb3\x77\x5d\xee\x90\x89\x87\x11\xf3\x4c\x3d\x77\x3f\x6c\x5d\xba\x19\x0f\xb6\x2b\x9c\xd9\x1b\xb8\xb6\x2c\xbe\x0c\x2e\xb6\xb6\xe2\xd8\xf0\x9c\xab\x95\xb5\xdd\x35\xd5\xaa\xde\x8b\x44\xab\xeb\xb5\x17\xbc\xe5\x57\xbb\xea\x4d\xdc\xf5\x69\x6f\xeb\xf7\x8e\xbb\x7f\x5c\xff\xec\x6d\x59\xf1\xee\x4d\x78\x22\x81\xff\xb9\xad\xcb\x52\x3f\x7d\x5b\xa2\xb7\x6f\x4b\xfc\x60\x6d\xe9\x79\xfd\xb6\x54\xcf\xdf\x96\xe8\xfd\xdb\x12\x3d\x80\x5b\x9a\x2f\xe0\x9c\x1a\x1b\x58\xd8\x38\xfe\x51\xbe\xe2\x23\xb8\x63\xef\x2b\xb8\xe3\xd5\x9f\xc1\x1d\x37\x7d\x07\x77\xec\x3e\x84\x3b\xbe\x83\x97\x70\xcb\x5b\x3f\x85\x3b\x6e\xfc\x16\xee\xf7\x0e\xea\x7f\xdc\xc0\xe2\x6c\x59\x65\x72\x26\x5d\xab\xf0\x1f\x82\x38\x91\xd5\xd9\x12\x9b\x9d\x2d\x0d\x2b\xb1\xa5\xcf\xf0\x6c\xa9\x2d\xcf\x96\xd8\xf4\x6c\x89\x6d\xcf\x96\x96\xf1\x99\xa7\xde\x26\x8b\xe3\x37\xb5\x3f\x3b\xf6\x1b\xa0\x1d\xdf\xc0\x02\xed\xb8\xb1\x09\xda\xb1\xc7\x06\xcd\x2e\x7d\xb3\x35\x52\x61\x86\xd6\x74\x91\x34\x37\x44\xfb\xb6\xc9\x2a\x69\x2f\x73\x0a\x8a\xd9\x71\xd1\xe6\xd1\xf8\xa6\x29\xa1\xc9\x19\x09\x53\x0a\xd6\x0a\xf0\x3a\x30\x48\x42\x70\x60\x4b\xfe\xf9\xe6\xf5\xab\xa2\x58\xbc\xa7\xff\x6f\x49\xf3\x62\x0d\x04\xb3\xcb\x05\x4d\x27\x56\x0e\xf7\x63\xa3\xde\x6f\xb4\x25\x5e\x44\xc3\x7d\x1b\x9a\x7c\xb9\xde\x5d\x33\x22\x45\x96\x42\x9a\x09\x20\xa9\xff\x92\xcf\xd8\xee\x13\x4d\x93\x34\xa3\xc3\x38\x4a\xe8\xda\x35\xb7\x58\x65\x78\x68\xe4\xea\xfe\xfe\xe5\xec\xfd\xcb\xd9\x3f\xf1\xcb\x59\xfe\x6a\x56\xd8\xb0\x19\xcf\x66\xf9\x86\x43\x6e\xf6\x7a\x56\xec\x7d\xc7\x45\x14\x43\x9d\x5c\x9f\x09\x6b\x87\x3f\x4f\x72\xc0\xa2\xe2\x52\xb1\x44\x5d\x64\x1c\x07\x79\x4e\x4e\xa0\xc8\xa9\xe8\x26\xcf\xd0\x4c\x98\x57\xb5\x36\x80\x7b\x23\x58\xa5\x42\xb9\xca\x38\x08\xa9\xf0\x64\xdd\xdc\xc9\x39\x40\xb2\x9a\x8e\xdf\x1e\x7e\xfc\xc0\xce\xd6\x30\x09\xed\x73\x1a\xb5\x39\x69\xb6\x3f\xa3\xdf\x6f\xd0\xef\x9f\xd0\xef\xfc\xd7\x60\x94\xca\x8f\x49\x94\x24\xf4\x52\x7d\xd1\x79\x91\xc2\x53\x46\x99\xb2\x88\xc6\x66\x42\x12\x24\x66\xc2\x3c\x1a\x67\x76\x4a\x1c\x47\x4e\x21\x03\xde\x00\x95\x1f\x46\x91\x69\x16\x24\xa1\x1a\x8a\x91\xf5\x93\xf1\xf5\xd1\xf8\x7a\x67\x7c\xbd\x34\xbe\xfe\xcf\xf8\xfa\x97\xf1\xf5\xd6\xf8\x7a\x61\x7c\xfd\xc3\xf8\x3a\xe6\x5f\x6b\xa7\xe5\xae\x6b\xd8\x1c\xbd\xdb\x7f\xc1\xa6\x78\x48\x76\xb6\x7b\x2a\xf1\xc3\xe1\x4f\x6f\xf7\x3f\x1e\xbf\x7f\xf9\xe9\xf5\xcb\xb7\x3f\x7d\x7c\x35\x24\x8f\x75\x26\xcc\xea\x50\xff\xd4\x39\x25\x94\x33\x24\x5f\x88\x95\xa0\x9d\xa8\x43\xc6\xa7\x17\x47\x3f\xbf\x25\xd7\xba\xa6\x77\x47\xaf\x5f\x33\xe8\x8f\x87\x6f\x5e\x1e\x1d\x7f\x1c\x92\xad\xcd\xcd\xcd\x81\xe8\xa1\xb8\xf1\x7e\x1e\xa7\xe3\xcf\x43\xd2\x66\xac\x33\x2f\xda\x46\xde\xfe\x18\xe2\x18\x0f\xf5\xdb\x46\xfe\x00\x83\xed\xe7\x75\xbe\x4f\xee\xe3\x60\xdc\x6f\x64\x7f\xf5\x8d\x6c\x4d\xb9\x80\xc8\x67\xc1\xce\x5d\x79\x80\x38\xc8\x2e\x17\x45\xfa\xf7\x0f\x78\x73\x18\x43\xda\x23\x1d\xfe\x82\x35\xe8\x05\x18\xb0\x9c\xb6\x37\xb4\x93\xeb\xbe\x01\x28\x2e\xc7\x0f\x54\x45\x12\x79\xf0\x40\xe6\xf6\xa5\xbf\x08\x2e\x26\xcf\xe8\x45\xdb\x7e\x45\x67\x78\xfe\xfa\x81\x6c\xb3\xd2\xb6\xeb\xe3\x6d\xe9\x2e\xd2\x2c\x4e\xe4\x65\xb8\xba\xe0\xb7\x9c\xb3\x13\xeb\xb5\x1d\x07\x95\x38\x62\x9d\xeb\xbf\xa2\x17\x7d\xd0\x5e\x0a\xcf\xbd\x3e\x1b\x23\x86\x15\x39\x6c\xdd\x3a\x3f\xd1\x71\xf5\xdb\x90\x6c\x7f\xf3\x84\x97\x44\x8f\x93\xe5\x9b\x33\xc6\xf2\x14\x8e\x5b\xc3\x6f\xbe\xeb\xb5\x4c\x94\xb7\x86\x4f\x37\xaf\x4f\x7b\xdb\x8d\x7c\x3e\xdd\xf3\xbd\x7b\xbe\xf7\xe7\xe5\x7b\x9a\xed\xf1\x77\xfe\x77\xc0\xf7\x2c\xd9\x7d\x75\xd1\xdd\x23\xb9\xcb\x82\x3e\xc1\x7d\xa5\x50\x43\x36\xaf\xed\x0f\x04\xbb\xd7\xb1\x88\x26\x4f\x31\x00\xfb\x56\x22\xfc\x32\x89\x8a\x37\xc1\x42\x89\x8b\x6d\x29\x51\x0f\x39\x0f\x6a\x6f\x4a\x59\x93\x49\xed\x43\xcd\x16\xdb\x5b\x86\x9c\x3f\x44\x19\x9b\x9b\xaa\xd0\xff\x56\xe4\x8d\x82\xd1\x28\x98\x52\xd5\x12\xce\x43\xc2\xff\xd0\xce\x9b\x7b\xea\x44\xd9\x6f\xaa\xb3\xe3\xf4\x8c\xc6\xc1\x58\x36\x6b\x67\xeb\x33\xc6\xd0\x97\x3d\xf5\x57\x8e\x20\x7e\xaa\x85\xc8\x67\x41\x92\xa4\x89\x31\x6e\x13\x42\x9f\x6b\x86\x15\x10\x35\xad\xc0\xc9\x6a\xe8\x81\xc0\xa8\xd4\xe7\xa5\x61\x35\x50\x5d\x4d\xe2\xec\x36\xf4\x02\x19\x95\xa9\xf3\x98\x3d\x36\x0f\xa0\x7f\x88\x26\xa0\x41\xae\x1e\x38\x04\xfa\xd9\x84\xf5\x81\xe2\xb9\x86\x53\x5f\x65\xc5\xb8\xbf\x8d\xea\xc6\xd5\x37\x2d\x80\xca\x14\x2b\x94\x61\xc5\xfc\xc6\x56\xda\x11\xc3\x22\x08\x85\x29\x29\x98\x7a\x5e\x2c\xe8\x98\x6d\x5e\xca\x3c\x1f\x1b\x5d\x09\xef\x29\x3e\xcb\x29\x5d\xc5\x88\x32\xb8\x50\x84\xe3\xb2\x6c\xb0\xc6\xb3\x20\x0b\xc6\x05\xcd\x72\xa9\xe2\x87\x7b\x79\x51\x1a\xed\x23\xde\x36\xa2\x69\xd2\x43\xb6\xd0\x64\x73\xcd\xef\xf6\x23\x9a\xce\x0a\x22\x3d\xd2\x5a\xde\x7d\xc5\x18\x0c\x69\x93\x83\xf4\xa0\x77\x79\x0f\xda\xf1\xf8\x18\xe2\x16\x22\x00\x03\x11\x69\xe1\xb5\xaa\xba\x21\xde\xea\xf6\x7f\x49\xa3\x04\x82\x35\x90\x67\x50\x07\x19\x92\xd6\x66\xab\x4b\x36\x04\x70\x89\xe1\xdb\x8d\xe7\x02\xa2\xf5\xfc\xd9\x27\x03\x06\xb1\xe2\x6c\x88\x1e\x6e\x70\x8f\xcb\x37\x9d\x97\x32\x43\x44\xd3\x11\x0d\x6c\x9d\x60\x86\x08\x91\x3c\x5c\x1f\xd3\xd6\xbc\x70\x6f\xcd\x15\xb3\x12\x25\xac\x12\x3f\xb2\xb0\x3f\x6a\x8f\xa3\x24\xd6\xb8\x36\x3b\xe4\x1e\x48\x8e\xf9\xd6\xae\x44\xfa\x19\x0f\xf6\x3c\x18\x90\x1f\xa3\x24\x24\xfc\x71\x97\xe8\xa8\x0a\xd6\xcc\x24\x8a\x56\x4b\xdf\xe4\x83\xed\x4b\x0f\xe2\x47\xcd\xe8\x85\x34\x61\x56\x67\x2e\x96\xc6\x4f\x3d\xec\xc4\x51\x7e\x56\x62\xd5\x6c\xe3\x77\x2f\x60\x5c\x23\x6c\x6a\x76\x49\xb4\xb1\xb7\x8d\xc1\x65\x20\x64\x6c\xdb\xa1\x9b\xea\x44\xac\x1d\x11\xfa\x42\xb5\x30\x21\x1d\x5e\x64\x6f\x8f\x6c\x76\x8d\x53\xda\x28\xa3\xc1\x67\x0d\xca\x46\xb9\xb1\x47\xc4\xab\x72\x36\x83\x07\xb3\x20\x3b\x48\x43\x0a\x35\x78\x0f\x61\x6c\xb2\xa5\x39\x4e\x5e\x64\xcd\x28\x84\x4f\xda\x4a\x24\xb2\xcf\x8a\xfc\x76\x34\x02\xcd\xfd\xf7\x10\xc9\x4d\x66\x3e\x2f\xca\x5e\xa7\x9b\x93\xed\xf1\x31\xdf\x59\x64\x74\x12\x5d\xf0\x08\x5a\x9b\x17\x5d\x36\x0b\xc0\x35\xfc\xee\xed\x45\xa8\xb7\xf2\xd9\xf7\xda\x2e\xc3\x11\x34\x88\x81\x9b\x57\x06\x13\xf0\x85\xf8\x34\x7c\xed\x0b\xb7\xeb\xa2\x1b\x98\x2a\x18\xc5\x0b\xcc\xf3\xd9\x87\xe5\x20\xcc\xb6\xf9\x72\x90\x33\xc2\x5a\xd2\xd4\x31\x49\x33\xdb\x84\x2e\x2f\xb2\xb2\x70\xf8\x68\x46\x19\xd4\x58\xcc\xcd\x7e\xd1\x89\x6e\xb6\xd2\xc1\x3a\x51\x44\x06\x37\xbc\xb6\x69\x10\xd6\xdf\x8d\x3d\x92\xc8\x7d\xe1\x7b\xb2\x4d\x9e\xb1\x93\x0d\xd9\x20\x6c\x3f\x48\x7c\x34\x21\x5c\xc8\xcf\xe8\xc5\x5d\x92\x86\x15\x73\xc0\xa6\x8d\x1a\xd6\xf0\x9b\x11\x87\xc3\x33\x10\x75\xfc\x36\x14\xf0\xbb\x4d\xab\xe5\xb1\x74\xb2\x8c\x63\x85\x86\x01\x3d\xa3\x49\xc1\x1f\x0a\x00\xcb\xff\x25\x4f\x13\x12\x8c\x22\x9b\xc7\x4b\xb7\x89\x1f\xd3\x1f\x97\x71\x6c\xbf\xa1\x94\x8f\x09\x58\xe9\x47\xbc\xb4\xfb\x18\x8a\x37\xec\xb4\xab\x19\xbb\xdb\x86\x21\x48\xb1\xca\xb1\xea\x94\x7d\xf7\xc1\x84\x22\x4a\x42\x7a\x71\x34\xe9\xb4\x3b\xed\x2e\xf8\x86\x7c\xb4\xe5\x79\x0e\xa9\xe0\x1d\x3b\xc1\xe2\x72\x41\x45\x73\x00\x04\x54\x64\xfa\x33\xeb\x44\xdd\x2f\x32\x7e\x70\x9f\xc1\xef\x92\x6b\x21\x8a\x99\x96\x7f\xaa\x15\xb2\x41\xda\x1d\x36\x73\xaa\xf6\x0d\xd2\xee\xb6\x1b\xad\xbd\x30\xca\x17\x71\x70\xc9\xe7\x05\x7c\x8c\x26\x05\x93\x6d\x15\x36\xec\x37\x6b\x17\x90\xfd\x82\x17\xab\x7a\xe1\xca\x6a\x33\x27\xdf\xbf\xbc\x8c\x1e\xb0\x2d\xcd\xa2\x18\x3a\xed\xcb\x60\x8b\x97\x1d\x61\x56\xd7\x25\x8f\x7e\x50\x89\x6a\x5a\xdd\xbe\x55\x3e\x7c\x56\x36\x9b\xce\xcc\x1a\x68\x16\x60\x7c\xb2\xc9\x33\xfb\x4d\xab\x78\x0f\xc6\xd6\x8c\x76\x36\x32\x18\xe8\x81\xa6\x67\x34\x8b\xd3\x20\xa4\xa1\x52\x04\x7b\xd6\x04\x1e\xc0\x47\x4d\x24\x65\x6f\x1a\x07\xe4\xe3\xd1\x8b\xa3\x21\x99\x07\x9f\x41\x35\x1c\x25\x67\xcb\x38\xa1\x59\x30\x8a\xe9\x5d\x0e\x50\x9f\x06\xec\xd7\xbb\x5b\xe4\x11\x41\xd9\xdd\x6e\x3f\xa3\x8b\x38\x18\xd3\x4e\x9b\xb4\xc1\xa9\x1b\x3b\x2d\xb4\xcc\x08\x91\x69\x72\x46\xb3\x22\xd7\xf1\x36\x41\xee\x0b\xe9\x38\x9a\x07\xb1\xcd\x64\xa3\xc4\xcf\xec\x8b\xf4\x05\x2f\xe0\x52\x5e\x65\xec\x4c\xd3\xad\x21\x17\xf0\x44\x4d\xb5\xd1\x1f\x8b\xd4\x0d\x8e\xa9\xc2\xcf\x34\x19\x63\xad\x6c\xcb\x78\xe2\x5d\x8d\x0b\xd5\x55\x1d\x99\x35\x91\x5a\x52\x77\x7c\x9e\xb8\xdc\x42\x7d\x6a\xee\x28\xc6\x61\x9f\x03\xc4\x34\xcf\x3f\xce\x82\xa4\xb3\x09\x4e\x64\x1f\x71\xab\x73\x61\xbd\x2f\x08\x6b\xab\x0b\xb1\x5b\x51\x8e\x81\xc5\xfd\x25\xb8\x69\x16\xa8\x0c\x92\x4b\xe1\x78\x47\xb8\x23\x4d\xca\xd1\xda\x17\x78\xdd\x4f\x42\xae\xfe\xe7\x34\x14\x4d\x2e\x73\xe1\x48\x3d\x27\x23\x3a\x49\x33\xda\x77\xe8\xea\x95\x38\x3a\x54\xe3\xfe\x4a\xec\x41\x35\xa4\xf5\x0a\xf6\x79\x03\xf9\x6a\xfd\x3e\x14\xa6\x62\xf3\xe0\x82\x87\xad\xbc\x88\x8a\xcb\x21\x79\x0a\x2a\x6c\xb9\xeb\x44\xb9\x70\x69\x0c\x45\xbb\xf6\x26\x83\x26\xb9\xb3\xc1\x20\x76\x8d\xa2\x78\x3a\xab\x0b\x5b\x65\x85\x21\xdd\x19\xa3\x1d\x76\x0a\xe1\x48\x6b\x7b\xab\x80\xf8\x4a\x7f\xff\x70\xf4\xb6\xaf\xb0\xcc\xdb\xd3\x0e\x2c\xc1\x75\x6c\x4e\x02\x3b\x94\x67\x8f\x2c\x82\x3c\x67\xbc\xab\x98\x65\xe9\x72\x3a\x33\x57\x80\x1a\x88\xa0\x35\xa8\xd5\xbd\x9c\xd4\x5c\xed\x11\x9c\x96\x3c\x32\x6f\xe9\x88\x25\x80\x78\xdb\x61\x56\x57\x53\xdb\x99\xb4\x1f\x45\x15\x90\xce\x7a\x94\xff\x18\x25\x51\x41\x2d\xa4\x5b\xdd\x00\x09\x11\x75\xc2\x94\xb2\xdc\x8e\xa2\x75\xf1\x5e\x6c\x2a\x7c\x1d\xb0\xf3\x52\x02\xdc\x9f\xfc\x4c\x6d\x41\x6a\x4a\x0b\x08\x57\x7c\x34\x39\x4e\x22\xaf\xb6\x0b\xca\x16\x33\x2a\x7e\xa8\x05\x47\x8a\xb4\xa7\xb4\x53\xca\x21\xba\x37\x6a\xa3\xea\x87\xaa\xa6\xc3\x3b\xd3\x85\x22\xe0\xb6\x2b\x27\x34\xcb\xd2\x4c\xba\xa4\xe1\x3d\xce\x49\x92\x16\x64\x9c\x66\x19\x1d\x17\xc3\x73\xb5\x6e\xcc\x5e\x1b\x0b\x88\x15\x94\x24\xb0\xe4\x99\xf0\xdf\x33\xf8\xaf\x5f\xa4\xaf\xd3\x73\x9a\x1d\x04\x39\xed\x00\x73\xe1\xfa\x5e\xcd\xc7\x18\xd4\x3f\xc4\x2d\xb3\xb8\xba\x39\x61\xff\x9f\xea\xa3\x38\x02\xc1\x7e\xbf\x31\xe1\x71\x4f\x64\x09\x3d\x27\x2f\xd9\xa8\x3a\x6d\xb8\xea\x85\x8e\x80\xad\xea\xbf\xdb\x05\xa1\x17\x51\x5e\xe4\x3d\xb2\x88\x69\x90\x83\x58\x0c\x23\x4f\x13\x85\xaa\x49\x1a\xc7\xe9\x79\x94\x4c\xa1\x64\xce\xb8\xa0\xb5\x8c\x44\x0f\x7b\xe0\x5f\xa1\xa7\x9f\x7d\x54\x44\x89\x55\xbd\x07\xef\x57\xa6\x57\xe1\xe0\x33\x85\x45\xc8\x19\x3e\x5c\x46\x47\x60\x4f\xab\x98\x2c\x27\x01\xc6\x6a\xc1\x57\x05\x9f\x78\x8e\x5a\x41\x59\xef\xd2\x3c\x8f\x46\x31\x9f\x42\x70\xa1\x21\x8c\xfa\x3e\x1c\x32\xf9\x32\x2b\xf8\x4f\x26\x52\x4b\x6c\xbd\x9c\x4c\xa2\xe9\xa5\xf8\x38\x92\xa4\xf4\x88\x7c\x66\xcd\xf3\x3f\x7d\x5d\x05\x9f\xe2\x66\x8b\x83\xcd\x35\x98\xba\x5c\xe2\x9f\xf2\x2a\x8a\xc3\x4d\x35\x9c\xba\xff\xe1\x9f\xe2\xc2\x48\xe7\xf1\x02\x8f\x1e\xa9\x85\xa9\xef\x71\x78\x81\x5f\x83\x51\x6a\xe4\x79\x4a\xc8\x7b\x18\x3e\x00\xb8\xbe\xc1\x79\xbc\x04\xea\x05\x2a\xcc\x3f\x05\x16\x10\x08\xb1\x20\xd0\x07\x5c\xa6\x08\x84\x50\x8d\xc3\x29\xfa\x5d\xc8\xdf\xb6\x48\xc1\xf9\x82\x75\xf2\xbd\x52\x72\x3a\x27\x87\x71\x90\xb0\x93\x41\xa0\x58\xb3\x48\x17\xba\xb2\x34\x23\x01\x79\xf5\xf2\x9f\x70\x08\x97\xd2\xda\x9d\x31\x14\xb5\xcf\xca\xa3\xdd\xcf\x33\x2a\xfd\xec\x05\xe8\x2a\x57\x44\x41\x41\xc1\x02\xd8\x7a\x0a\x72\x72\x4e\xd9\x02\xd1\x0e\x56\xe4\x30\xd6\x90\x34\xf4\x33\x35\x8e\xe4\x72\x9c\x98\xa5\x70\x51\x87\xd5\x2c\x99\x04\x16\x8a\x78\x09\x1c\x35\xd6\xe4\x54\x9c\x3b\x59\xf2\x10\xde\x86\x45\x05\xe4\x99\xd1\xc8\x10\x7f\x21\xc9\xaa\x76\xf9\x06\x1c\xc7\x9e\x15\x7c\x4e\xa3\xfb\x05\xfb\xdf\xb2\xc4\x8b\xb4\x6a\x81\xa3\xf3\xc2\x6f\xb6\xd4\xd9\x6a\xfb\x1d\x17\x3b\x20\xe4\x6e\x96\x7a\x11\xcd\x69\xfe\x7b\x2c\xf3\x44\x28\x17\xd9\xe2\x56\xaa\xaa\x9c\x1f\xf3\xd9\x16\x4d\x94\x29\x8b\x43\x0d\xaa\x23\x8d\x68\x42\x53\x81\xbc\x3a\x64\x53\xaf\x49\xc1\xac\x4d\x39\xb9\xd2\x15\x68\x00\x85\x7e\x6c\x7b\x63\x4d\x42\xcd\xf1\xe7\x1b\x26\x03\xc2\xaa\x97\xe5\xc5\x8f\xab\x2b\xb2\xb9\xeb\x3d\xdb\x88\x7a\x9d\xb3\x09\x4f\x37\x0e\x44\x02\xe5\xb2\x27\x0f\x1e\x10\xf1\xdb\x27\xf3\xb3\x26\xed\x5c\x7c\xc0\xf0\xb9\x40\x33\x44\x31\x51\x58\xa9\x44\x36\x2f\xda\xbd\x76\x1b\xdf\xb7\x58\x8e\xd2\x7c\xa5\x31\x9d\x94\x8a\x74\x89\x0c\x1d\xeb\xa1\x14\x45\x27\x1c\x4c\x06\xf1\x50\x27\x31\x61\x35\x09\xb0\xc5\x79\xda\xce\xc9\x58\x85\x74\x71\x48\xcb\x8c\xf8\xd2\x84\xbe\x4a\xa8\x06\x9d\x91\xcd\x3a\x4d\x7d\x97\x41\x32\x0c\x7c\x84\x28\xcb\xb7\x5e\xe1\xc5\x77\x07\x39\xad\x53\x05\xb0\x46\xa2\x76\xea\x5a\x93\x5b\xfe\xb5\x60\x96\xfb\x8b\x78\x99\xeb\x2e\x88\x6f\xaf\x77\x43\x05\x64\x2a\x92\x66\x74\xfc\x39\x97\xa7\x26\xce\x22\xe5\x2d\x67\x2e\xde\xca\xc5\x97\xe0\xc6\xd7\x1b\x8c\x98\x93\xfc\xd8\x1b\x88\xd8\x0c\x29\x8c\x1a\x60\xeb\x3f\x40\x05\xb0\x63\x3b\x08\xae\x24\xa6\xce\xaa\xdc\x98\x39\x51\xde\xd2\xa0\x0d\xfe\xb3\x79\x71\xb2\xf9\xe8\xbb\xe0\xd1\xe4\xf4\xcb\xe3\xcd\xeb\xff\x19\x44\xfd\x82\xe6\x85\x02\x5f\x61\xf0\x15\x63\xfe\x4a\xa3\x6d\x30\x4e\x50\x00\x0c\xfe\xd3\xd9\xbc\xe8\x3e\xab\x1c\x28\xa6\xc0\xc1\x40\x07\xcb\xe2\xe1\xb0\xa0\x7b\xdc\x85\xb0\xb0\x3a\x9c\xc3\x43\x5e\xb6\x21\xa3\x61\x9b\x14\x2c\x3c\x01\x12\xd3\x57\x85\xb7\x33\x66\x5f\x18\xa3\x43\x60\xfb\x8f\x7e\xf4\x82\x59\x5d\x86\xd8\x5d\xed\x1c\xbc\x1d\xe7\x73\xf6\xef\x38\x58\xe4\x20\x3c\x88\xdf\x3d\xec\x9e\xd1\xee\x2d\xf7\x3a\x8f\x3a\x6b\x54\x7e\xa4\xf6\x76\x8e\x19\x1a\x8c\x67\x64\x1c\xe4\x4e\x35\x51\xce\xa9\x64\x39\x17\xb3\x83\x48\x89\xaf\xb1\xe6\x04\xc5\xdb\xca\x97\xf3\x39\x0d\x4b\x69\xcb\x6a\xee\xae\x69\xcc\xaa\xbe\x8a\xd6\x06\x03\x3e\x20\x0b\x39\x81\x2a\x29\x7e\x39\x1b\x90\xd6\x86\x08\x88\x57\x41\x0e\xae\x68\x66\xc1\x8e\x6c\xc4\xd4\xa4\x48\x59\xc7\xe7\xee\xe5\xf1\x26\xdc\x50\x12\x8b\x3c\xc0\x75\x77\x31\x23\x31\x85\xc7\xd4\x28\xfe\xde\x62\x41\x33\xd6\x5b\x39\x0f\x09\xc4\x2e\x9c\x46\x3c\xbc\x5d\x90\xd3\x79\xb0\x60\xf3\xb1\x65\xe8\xf9\x3a\xca\x7e\x01\x75\x1a\x9c\xb2\x6d\x3d\xe9\x92\x1f\xc8\xb7\x6c\x37\x17\x59\x27\xd1\x69\xbf\x48\x8f\x59\x43\x42\x13\xb4\xbe\xb7\x87\x32\x81\xe2\xab\x2b\xfc\x7e\xcf\x53\x23\xd6\x2d\x59\x35\x96\x78\x0a\x47\x6b\x52\x73\x7c\x83\xef\xeb\xe8\x0b\x8a\x4c\xdf\x88\x83\x9e\x24\xc7\x12\x5a\x2c\xd2\x3b\xa5\x45\xa9\xbc\x56\xfb\xf2\x0a\xa4\x88\x54\xc6\x8a\xfc\xec\x47\xd7\xa2\x9d\x76\x5b\xd0\x92\x4b\xa7\x06\x82\x6f\x44\xb5\x08\x68\xec\xf4\x9e\x55\x54\x41\xc7\xb2\x17\xe8\xd6\xdd\xa6\x69\x60\x79\x33\x6d\xf9\xc7\xa8\xf4\x3b\x76\xee\x99\x70\xff\xf9\xf2\x22\x4e\x91\xb8\x41\xc1\x75\x04\x6c\x92\x90\xdd\xff\x8d\xbd\x52\xea\x46\xf4\x65\xb3\xd2\xda\x9a\x2a\x69\xd3\x2a\x69\x4a\x9e\x5a\xd2\x34\x18\x69\x91\x32\x89\x32\x0a\xc9\xf6\x26\x77\x19\xf4\x48\xdc\x0f\xf2\x36\xf9\xf3\x84\xcd\x0b\xc2\x6d\x3b\x5c\xdb\xae\x5a\x52\xf6\x5f\xf6\x0b\xe7\x03\x98\x6f\x2b\xfb\xad\x66\xf4\x6b\x49\x33\xde\x6d\x4f\xfa\xd4\x95\xf8\x40\x32\x3c\xdf\x6b\xab\xb6\x59\x4f\x45\xe2\xee\xcb\x57\x9f\x09\x21\x23\x2f\xc2\x8d\x92\xaa\x51\x3f\xa6\xea\x91\xc7\x9b\xfe\x4b\x02\xe9\x87\x58\x1e\xa6\x73\x2d\xe5\xd6\xc7\xd8\xf4\x9e\x24\x7d\x37\x5f\x46\xdc\x4d\xbe\x93\xf9\xce\x80\xa4\xc3\xbb\x61\x89\x85\xb2\x6f\x49\x5e\x04\xc9\x98\x71\x11\x5d\xf8\xea\x4a\x21\x4d\x14\x86\xc7\x6b\xf0\xcb\xf0\x9b\xe1\x4d\xe5\xa6\x11\xc0\x8b\x54\x95\xed\xa6\x88\x92\xe7\xe1\x3a\x2c\x7d\x70\x6c\x8b\x1a\xa2\xc8\x13\x21\xc9\x8b\x1f\xc1\x5a\x45\xcf\x60\x34\xbc\x6f\xed\xbb\x43\x0f\xef\x4b\x63\xdc\xc8\x1e\xd7\x63\xe7\x47\x6d\x43\xb2\x2a\x7e\x64\xd1\x1b\x61\x48\x96\x68\x37\x1c\x11\xeb\x53\x51\x3f\x1c\xde\xf5\x1b\x0c\xe6\x48\xf4\xad\xe1\x62\x60\xf2\x45\xb2\x8c\x63\x08\x92\xd0\x71\x57\x08\xd8\x6d\x83\x0a\xc3\x33\x76\x71\x5d\xdb\x70\xe4\x23\xde\xd9\x06\xec\x80\x03\xde\x84\x19\xf0\xa4\x1b\x4d\xa4\xe8\x5e\xd3\xd1\x80\x07\xc0\xfa\xb1\x38\x01\x35\x1a\x8e\xc4\x0d\x8a\xd1\x90\xa5\x41\xc1\xca\x31\xd8\x07\x12\xbe\x8f\x82\x89\x5c\x2a\xa9\xce\x1c\xc4\xdf\x73\x73\x5d\x69\x03\x84\xca\x31\xb0\x62\xf6\xa3\x01\xe5\x39\x29\xbb\x74\xf7\xa9\xf5\x75\xb8\x98\xe4\xaf\x70\xb5\x2d\xeb\x35\x19\x43\xd4\xa7\x0e\xf5\xec\x6d\xf8\x38\xba\xca\xa8\x03\x31\xee\x97\x6c\x02\xe9\x72\x4e\x46\x71\x3a\xfe\x4c\x66\x34\x08\x69\xc6\x3e\xd2\xb9\x6d\xb4\x11\xe5\xcf\x59\xb2\x4f\x68\x98\xd1\x0b\xe5\x16\x1d\xca\x92\x49\x14\x17\xb6\x32\xd3\x43\xb0\x00\x6b\x78\x1f\x66\x29\x95\xe7\xfc\x6f\xb6\xb6\xf5\x41\x9f\x83\xd7\xe0\xa5\xfc\x98\xce\xeb\xc2\x55\xf9\x4e\xe9\x2e\x94\x2f\xe0\xb0\x3e\x69\xaf\xb9\xfd\xb8\xc1\xcc\xc4\x29\x13\xf3\x16\xd1\xd8\x9d\x87\x8f\x2c\xb9\x6e\x1e\x0a\x05\x54\x31\x01\x50\x93\x31\x01\x50\xac\x72\x02\x9e\x3c\xd6\xf8\xe7\xd0\x37\xc6\x3f\x54\x85\x6b\xf2\xa1\xdf\x01\xba\x11\xf6\x4b\xfc\x8e\x08\x91\x6f\x28\x7f\xf4\x64\x2a\xbc\xf9\x19\xaa\x5f\x3c\x1d\x04\xc3\x21\xff\x4f\xa6\x08\x03\x92\xa1\xfe\xc9\x73\x90\x71\xc9\x10\x7f\xc8\x72\xc7\xc5\xe4\xe9\x50\xfc\x2f\xd3\xc0\x5c\x65\x28\x7f\xe8\x7a\x38\xac\xfc\xa5\xd3\x05\xbc\xfa\x29\xea\x71\x6d\x6e\x87\xbe\x44\x0e\xed\x9a\x72\x0e\x3d\x69\x06\xac\xb4\x9a\x1c\xda\x09\x72\x1c\x3f\x53\x18\xc5\xcf\x14\x8d\x01\xd2\xc4\x0f\x09\xa7\xa4\xc5\x21\xfe\x90\xb9\xa6\xca\x7a\xe8\xa4\x28\xac\x71\x41\x7d\xa8\x7f\xf2\x1c\x24\x1d\x0f\xf1\x87\xcc\x35\x4e\x22\x43\x3b\x41\x42\xa1\x7c\x2b\xc7\x3a\xba\x0f\xdd\x24\xd9\x43\x07\xd2\x49\x92\x75\x4a\x61\x6c\x88\x7e\xe3\xfe\x26\xd3\xa1\xfa\x25\xd3\xf9\x9e\x3a\x54\xbf\xd4\xe8\xf9\x7a\x1f\xea\x9f\x6a\x4c\x6c\x97\x1c\xca\x1f\x32\x95\x6d\x58\x43\xf1\xbf\xaa\x83\xf1\xbb\xa1\xfc\x21\x53\x81\x6d\x0c\xe5\x8f\x1e\x2c\x30\xee\x9f\x4e\x3c\xea\x6e\x0d\xb7\xbe\xeb\x55\xba\xb7\xe9\xb5\x96\xc5\xe4\x69\x6b\xf8\xf4\x9b\xeb\xd3\xde\xf6\x56\x13\x87\x0f\xe6\x12\xde\xe3\x0b\xb8\x25\xfc\x1c\xb4\x86\xa4\xb5\xd9\xdf\xde\xec\x6f\xb5\xd6\xae\xa5\x27\xb8\xed\x46\x81\x8a\xef\x1d\x49\xdc\x3b\x92\xf8\x2b\x38\x92\x10\xb5\xac\xb9\xae\xe0\xfe\x4e\x27\x93\x8c\x5e\x92\x9f\xa3\x78\xfc\x99\x92\xef\x7f\xa1\x93\x89\xed\x4d\xa2\xa1\xc3\x38\x00\x8b\x82\x84\x1c\x31\x89\x3b\x00\xa8\x28\x48\x5c\xb0\x1f\x83\x11\x03\xfb\x47\x3a\xa5\x71\x5e\xd0\x38\xa6\x19\xf9\x7e\x02\x89\x2e\xf0\x4f\xc1\x19\xf9\x39\x4d\x43\xf2\xfd\xb4\xd4\xcb\xc5\x63\xed\xdd\x47\xb8\x82\x7c\x13\x24\xc1\xd4\x74\x3d\xd1\x1f\x30\x2c\x0c\x32\x0e\x30\xe7\x00\xd2\xc5\xc4\xe1\x08\x0e\x47\x36\x70\x34\x0a\x12\x09\xf2\x12\xac\xf8\x6d\x08\x2e\x79\xe5\x03\x5a\xcc\x24\xe0\x8b\xe7\x15\x70\xe1\x48\xb9\x9b\x9d\x55\xd5\x97\xcf\x54\x7d\x6f\xc1\x31\x79\x19\x60\x42\x0b\x09\xf8\x8e\x66\x39\xbc\xa4\x2a\x87\x5e\x08\x10\xd5\x89\xf3\x20\x9b\x57\x75\x83\xe5\x2b\x60\x5a\x14\x10\xb4\xc9\x85\xcf\x45\x96\x04\x95\x5c\xc5\x80\x94\xec\x82\x9d\xa8\xb4\x6f\x8f\x28\xb6\x2a\x44\x51\xe5\xcb\x5d\x84\x70\x20\xe9\x8c\x49\xbc\xdb\xa0\x49\xe8\xe9\x1b\xcf\x90\x60\xcf\xe1\xc4\xe4\x42\x8d\x58\xba\xc2\x64\x96\x2e\x68\x56\x5c\x7a\xe0\x16\x22\x4b\x82\xbe\x2a\x8a\xc5\xbb\x2c\x3d\x8b\x42\x2f\xb9\xb1\x85\xba\x10\xd9\x8a\xd8\x16\xe3\x8a\x12\xd1\x62\x6c\x17\x68\xe6\xd0\x70\x6d\x4d\xc9\xea\x3f\xd3\xd1\x0e\xe9\xc8\x6a\x4c\xa7\xbc\x99\xbd\x42\x12\x7a\x6e\x2d\x1b\x5d\x12\xf9\xe7\x15\x91\x56\x51\xcf\x25\x14\x02\xa2\xfc\xa9\x0b\x3d\x67\xcb\x05\xfc\xf4\xe3\x2a\xc2\x91\xc8\x7c\xf1\xdc\xc9\xcb\x67\xb2\xe4\x87\x99\x5b\x32\x81\x35\xc0\x72\xdf\xd2\xc2\xc9\x5d\x68\xc2\x67\x20\x72\x1d\x38\x70\xa3\x5f\x7f\x95\x6d\x30\xba\x76\xfb\xa0\x09\x1c\x80\xc4\x67\x07\xc3\x68\xca\xd6\x47\x8d\x60\x11\x0d\xd5\x66\x28\xfe\xe7\x47\x0e\xdc\x49\x81\xad\xdc\x28\x8a\xc9\x67\x68\x7c\xf5\x14\x0c\xa2\x97\x21\xfe\x70\x9a\xf8\xa4\xd6\x00\xff\xe1\x0c\x50\x00\x74\x74\xfb\x82\x9c\x23\x9a\x0f\xd1\xef\x0e\x37\xe6\xb9\xee\xee\x32\x89\x69\x30\x00\x0f\xbc\x39\x25\x7a\x0c\x29\xdf\x89\xc1\x25\xd0\x1a\x23\x37\xcf\xf8\xea\xc6\x56\x3a\x2e\x26\x34\xca\x3a\x65\x38\x4d\x8a\x29\x0f\x87\x0c\xae\xa7\x71\x5c\x78\x65\xd2\xf6\xf4\x25\xa3\x3c\x56\x84\xee\xc5\x67\x4a\x17\x87\xf9\x87\xcb\x64\x1c\x25\xd3\xca\xae\x40\x59\x0b\xbe\x19\x05\x7a\x3a\x82\xf9\xc2\x73\x6d\xbf\x62\x41\xc9\x57\x30\xdc\x9b\x14\x7c\x79\x60\xe4\x8b\x59\x09\x05\xdf\x1e\x38\xf1\xec\x5a\x82\xb1\x4f\x07\x0a\xbf\xc0\xe5\x80\x2a\xc5\x0b\x6b\xd4\x29\x13\x3c\x6d\xeb\xe7\x54\xb2\x79\x91\xe2\xad\xd5\x86\x46\x69\x9e\xba\x31\x2e\x65\xed\x55\x38\xe5\x16\x8e\x12\xf2\x67\xea\x1f\x19\x86\x12\xdf\x0e\x1c\x36\x6c\xe1\x90\x2a\xc5\x03\xeb\xde\x0a\xcb\x32\x07\xf6\x6d\xa1\xd3\xe7\xb2\xb2\x4e\x8e\xa7\xdd\xc3\xe7\xfb\x6f\x51\x63\xec\xd3\x81\xd2\xde\x69\x38\x98\xf8\xf6\xc1\x49\xc7\x29\x0a\x10\x12\xd8\x2e\x66\x2f\x7c\xbe\xf5\xe3\x87\xdc\xfc\x52\xc8\x74\xae\x68\x5e\xd7\xc1\x9d\xb4\x0d\x59\x76\x7d\x1a\x46\x19\xa8\x8a\xc7\xc1\x02\x1e\x5f\xa0\x0b\x4c\xcf\x8c\x1e\x1e\xec\xbf\x33\xd6\x3e\x2b\x87\x2d\xe4\x22\x2e\x4a\xb2\xe5\xcb\xa4\x4a\x9e\x6f\xbc\xf5\x64\x10\x7d\xd1\x8c\x5c\xd9\xe0\x4f\x46\xf1\xdf\xaa\x80\xa3\x27\x8a\x77\xc3\x5e\x27\xc4\x91\x8e\x79\xe7\x9c\x80\x0e\xa6\x2d\xf7\xa4\x24\x0d\x69\xbb\x67\x40\x4c\xc1\x2e\x64\x48\xda\x4c\xe8\xf8\x34\x8e\x23\x9a\x14\xff\xe0\xe0\x6d\x7d\x27\xdd\xed\xdd\xa4\x35\x5a\x9c\xa7\xd9\xe7\xb2\x06\x13\x5a\x7c\x12\xa0\x16\x88\x19\x2f\x60\x68\xaf\xf2\x5b\x76\x8b\x0a\x85\x76\x59\xbf\x68\x31\xfb\x04\x73\x3d\x4e\xe3\x7f\xfc\x0e\xfd\x3b\x9f\x45\xf9\x42\xb9\x46\x76\xba\x97\xcf\x66\xb7\x46\x1b\xfc\x3c\xf5\xee\x25\x51\x7e\x90\x26\x09\x77\xd9\x84\x96\x5b\xd7\xa0\xbd\x8e\x77\xbb\x7c\xf0\xc0\xbb\x8d\xe2\x2a\x3b\x5d\xff\x0e\xc6\x9d\x14\x48\x99\xbc\x94\xe6\xc1\x38\x14\x02\x27\x08\x89\xc6\xab\xb7\x65\x75\x4b\x67\xa2\xf8\x84\xc0\x55\x4e\xc6\xc1\xa2\x35\xdc\xde\x64\x49\xf8\x48\xd2\x1a\x6e\x6f\xb1\x34\x7d\x1c\x68\x0d\xb7\x1f\xab\x14\x2e\x3a\xb5\x86\xdb\x4f\x55\x12\x16\xee\x5b\xc3\x9d\x6d\x95\xc1\x56\x78\x6b\xb8\xb3\xa3\x13\xb4\x50\xdf\x1a\xee\xe8\x4a\xf5\xb1\xb0\x35\xdc\xf9\xd6\x49\xa6\xc5\xac\x35\xdc\x79\xea\xa4\x27\xb4\x68\x0d\x77\xbe\x73\xd2\xa5\x20\xdc\x1a\x3e\xde\x74\x32\xf3\xd9\xac\x35\x7c\xbc\xe5\xa6\x33\x59\xb8\x35\x7c\xac\xbb\x2f\xcf\x38\xad\xe1\xe3\x6f\x54\xa2\x79\x70\x6e\x0d\x1f\x3f\x51\x59\x52\x6a\x69\x0d\x1f\x7f\x5b\xad\xdb\xbb\x3e\xed\x6d\xef\xdc\x6b\xde\xee\x35\x6f\xff\x2d\x9a\xb7\x20\x8e\xc1\xbf\xc4\xed\xdc\xb8\x22\x05\x97\xa3\x0a\xf1\xe9\x42\x64\x94\x98\x97\x67\xdc\xa2\x1f\xe9\x18\xa0\x37\x12\x4e\xc7\x8c\xa9\x0b\x8e\xe4\xea\x69\xbc\x8a\x9a\x1f\xe1\x72\xd7\xaa\x0c\xd2\x24\xc4\x39\x0f\x7d\x64\x82\x48\x56\x24\x32\x95\x73\xd7\xfd\x38\x36\x86\x62\x0a\x46\xe6\xd1\xaa\x07\x37\xf5\x3d\x62\x99\x96\x95\x28\x3d\xcc\x04\x7c\x44\xfe\x85\x5f\xce\xb3\xff\x70\xb2\x63\x2e\xc9\x37\x21\xa7\x87\xd5\x51\xbe\x2d\xa9\x55\xba\x03\xdf\x53\xbf\xae\xae\x20\xfc\x0d\xb1\xdd\x3e\xb0\x44\x48\x3d\x69\x33\x29\x14\xc2\x0a\xb4\x7b\xa4\x5d\xa4\xfc\xe7\x69\x9f\xa3\x19\x85\x3b\x9c\x78\x6e\x43\x45\x33\x27\x93\x53\x30\x70\x51\xf6\xa1\xe2\x86\xb4\xeb\x89\x99\x6d\x55\xc3\xfa\xc3\x8a\xef\x21\xe2\xe1\x1e\x74\xa0\x23\xfc\xbc\xa4\x63\xe0\xe9\x06\xa5\xcd\x82\x7e\xb7\x05\xae\x28\x34\x5e\x0d\x3c\x9b\x8f\xbb\xb0\x73\x8a\x2a\x8c\x7b\x82\x16\x87\x41\x11\xc8\x11\xb0\xdf\x7d\xf6\x0f\xd9\x43\xbf\xaf\xae\xc0\x28\x56\x01\xc0\x55\x72\x2e\x41\xc4\xd7\xd5\x95\x0e\xbe\x09\xda\x46\xd6\xb4\xbc\x23\x47\x80\x27\x9b\xa7\xfd\x9c\x31\x04\xe5\x61\x9d\x41\xcf\x85\x80\xa3\x29\xcc\x9d\xae\x5f\x3c\xd3\x85\x5b\xd9\x13\xa6\xb6\x42\xba\x73\x2f\x6d\x3b\xbf\xa8\xe7\xe9\xdd\x93\xcd\x53\xf4\xf0\x6a\x1d\xda\xef\x92\x2f\xf0\xd4\x21\x48\x92\xb4\x20\x93\x28\x09\x79\xbf\xa2\x64\xca\x1b\x7a\xa6\x9a\x1f\xa7\x49\x9e\xc6\xb4\x7f\x1e\x64\x49\xa7\x8d\x4b\x70\x67\x39\x8c\x15\xc7\xe9\xb4\x8d\x4c\x5f\x45\x8f\x19\x2a\x1c\x87\x4b\x54\xb0\x21\x1c\x98\x0b\xe6\xae\xe3\x5b\x9d\x3d\xde\xad\x9e\x49\x10\xe6\x11\x0a\x6a\x94\xbe\x0e\x61\x8a\x1b\x2c\xc7\x0b\x3a\x66\x12\x80\x67\x3d\xf6\xc0\x21\xd3\x28\x18\x7f\x56\x21\x44\xc1\x13\x81\x38\xec\xca\xeb\xd6\x4e\x90\x4d\x97\xf0\x12\xe4\x44\xfd\x42\xce\x78\x4c\x2b\x74\x59\x23\x84\x7e\xae\x2c\x86\xdd\xc6\x75\x1c\x08\x36\xf1\x5b\xa6\x1b\x0b\xcd\x36\x92\x65\x1c\x3b\xe8\x4e\x25\xa5\x09\xe7\x77\xfa\x00\x2c\x21\x26\x28\xc8\x1a\xd7\xcc\x02\x26\xfb\xa3\xc8\x54\x1a\x22\xf1\x9b\x73\xf6\x4e\xda\x83\x83\x52\xbb\xe7\x65\xac\x3d\xc9\xde\xd9\x61\xab\xd3\xed\xe9\x86\x10\x86\xeb\x67\x2a\x28\x8a\x60\x3c\xfb\x98\x1e\x48\x3f\x58\x78\xca\xa4\x73\x2c\x7c\xe6\xd6\x53\xcb\xc7\xcd\x3f\x9d\xe1\xc8\xa2\xfd\x20\x8e\xd5\x7e\x22\x80\x4b\xce\x14\x4e\x37\xd5\x01\xc3\x73\xc2\xf0\x1e\x31\x80\x54\x5b\xc3\x6d\x90\xee\xf9\xaa\x6f\x0d\xb7\x41\x76\xc7\x21\xdb\x76\x00\xd8\xda\x08\x5b\xc3\xc7\x3b\x4c\x64\x7e\x7c\x2f\x32\xdf\x8b\xcc\x7f\x6d\x91\x19\x45\x7b\x81\xb3\xf7\x5d\x85\x7b\xf9\x7b\x9e\x26\xd9\x62\x6c\xca\x9b\xbf\xf0\x44\x75\x75\x98\x65\xa9\x2d\x02\xf3\x34\x25\x89\xba\x2a\x0a\x36\x58\x43\xc8\x74\x64\x4c\x40\xc7\xa7\x52\x49\x53\x64\xe4\x22\xae\x77\x8d\x9f\xc0\x20\x0c\xa5\x4b\x47\xc6\x8e\x45\x61\xf0\x92\x0d\x5d\x13\x09\x96\x45\x60\x10\x86\x1e\x1b\x5b\x22\xc6\xcf\x0b\x15\xda\xba\x75\xb0\x06\xe3\xc4\xac\x38\x0c\x7d\x32\xb7\x6f\xe0\x39\x0f\x0a\x2e\x21\x6a\x47\x24\x99\x76\x55\xff\x05\x8c\xb7\x6b\xbe\xfd\xdc\x74\x2e\xa0\xf0\x6b\x74\xd3\x9d\x02\x7d\x4f\x94\x84\x5c\xcd\x24\x61\x7b\xa8\x6e\x9a\x65\x3d\x21\x89\xe6\xae\x4c\xcc\xc9\x87\xff\x12\xc2\xa2\x06\x10\xf8\xc1\x1e\x26\x15\x2a\x7b\x04\x5e\xb7\x97\x3c\x60\x13\x55\x9e\x00\xcc\x29\x3e\x1e\x94\x0a\xec\xbc\x48\x49\xb5\x4c\xac\x91\xfd\x11\x95\xf6\x1d\xd9\xc7\x2e\xb0\x2e\x16\x51\x3f\xca\xff\x11\xc4\x51\xf8\x9e\xe6\x8b\x34\xc9\xa9\x68\xca\x79\x7c\xe7\x8c\xc1\xdf\x5e\x87\xaf\xb1\xfe\x61\x72\xe6\xad\x75\xd7\xa9\xf4\xda\xed\x5f\x69\xe5\xdc\x65\x93\x33\x58\xbe\xe7\x82\x6b\x08\x5f\x86\x68\xbc\x2f\xfa\x00\x4e\x23\x70\x82\x13\xc4\x5e\x4f\x85\x3a\xdf\x10\xbf\x28\x01\x94\xa5\xf5\x93\x7c\xf0\xad\xe1\x36\xe8\xd1\xc4\x8a\x6c\x0d\x77\xc0\xea\xad\x51\x90\xef\xfb\x0d\xff\x7e\xc3\xff\xf3\x6e\xf8\x7a\xbf\x57\x62\xf9\x1d\xa9\xc8\x1a\xea\xaa\xd8\x89\x27\xb3\xc0\x72\x21\xeb\x0f\x20\x73\x55\x75\x9a\x84\x43\xef\xa6\xb0\x1e\x4c\x3e\x88\x12\xd0\xfb\xe8\x10\x82\xc0\x94\xc6\xd0\x88\x38\xee\xdb\x3f\xb9\x7a\x09\x3f\x32\x83\x6d\xde\x7e\xa7\xcc\xe1\x0e\x34\xd8\x3b\x09\xa5\xe4\x02\x30\xf6\xbd\x26\xd2\x95\xb3\x99\xea\x6d\x40\x38\xfb\xf5\x57\x6d\x3e\xf5\x1c\x45\x3d\x51\xce\xba\xd5\x09\x46\x91\x47\x0d\x82\xdc\x3e\x13\xcb\xcf\x32\x8f\xef\xbd\xb7\x47\xda\xa8\x4f\x6d\xf2\xe0\x81\xe1\xc7\x19\x9d\x9b\x79\xb3\x86\xb3\xff\xeb\xae\xb5\x0d\x57\x35\xe8\xf1\x0c\x4d\x3a\x90\x58\xb2\x5d\x43\x1e\x77\x18\xed\xd9\x19\xac\x8a\x18\x58\xee\x69\x1a\x68\x4f\x1c\xde\x39\x42\x39\xa8\x42\x23\xd2\xf2\x48\xed\x55\x03\xe9\x51\xc5\xf3\x12\x9e\xa2\xf8\xd1\xda\xfb\xb2\x29\x08\x43\x49\xc3\xb9\x3e\x86\x63\xda\x90\x69\xd7\xaa\xa6\x52\x7a\xe2\xa4\xe2\xaf\xb2\xf2\x64\xaf\x8f\xeb\x37\x27\x14\xf4\x0a\x71\x95\xd9\xc7\x9a\x2a\xa5\xfd\x51\xfd\xf9\x44\x8b\x99\x54\x37\xeb\x4e\x9a\x7e\x2f\x6a\x55\xa9\x13\x47\xcd\xa1\x11\xa0\x55\xa5\x0d\xe6\x95\x73\x8b\x46\x93\xca\xf9\xcd\xdd\xcd\xa8\x5d\x5f\xbd\xa2\x46\x32\xbc\xbb\x98\x5b\xce\x7b\x2d\xb5\xb2\xe0\xac\x42\xdb\xa8\x78\xac\x39\x79\xae\xde\x8a\x77\xac\x74\x3a\xf7\xe3\xb8\x72\xba\x00\x48\x5c\xf4\xac\x4c\x60\x5c\x15\x5a\xd3\xc1\xd5\xa9\xcd\x78\x14\xe8\x2a\xd5\xca\xa8\xad\x8a\xdc\x94\x9b\x1c\xb0\xfd\x93\x93\x3e\xa5\x45\x2e\x8c\x57\xe2\x4b\x12\xd2\x45\x9c\x5e\xd2\x50\x9a\x08\xc2\xf3\xc1\xf1\x2c\x88\x12\xfb\xb9\x1a\xd4\xf6\x63\x9a\xc9\x1e\x79\x7c\x0f\xc8\x03\xab\x8f\x24\xe5\xba\xbc\x56\xaa\xc5\x35\xc3\x43\xee\xb1\xbc\xdc\xd0\xcf\xda\x4a\x5a\xc4\x06\x0f\xb2\x25\xa4\xb0\xd4\xe4\x0b\xf1\x9a\x21\x90\x8c\xa3\xe6\xfd\x11\x82\x94\xef\xc9\x87\x65\x90\x3f\x18\x90\xf3\x20\xe2\xea\x72\x10\xb9\x16\x85\x56\xc1\xca\x9b\x32\x73\xde\xc5\x52\x50\xf1\xa2\x75\xc7\x68\xd7\x74\xbc\xbc\x4e\xe1\x69\xb2\xd1\xbe\xbd\x2b\x41\x7f\x37\x36\x76\xcd\x63\xd3\x60\x40\xf2\x22\x5d\x70\x5d\x6d\x94\x4c\x49\x30\x61\x5d\xf9\x66\x93\xcf\x55\x4e\x3a\x45\x34\xa7\xe9\xb2\xe8\x3a\x47\x47\x8e\x80\x1f\xc8\x37\x9b\xde\xc3\x22\xef\x7d\x9f\xd5\xfe\xb3\xa8\x5c\x87\x54\xe8\x92\x2f\xd7\x9e\x33\x9d\x8d\x40\xfe\x60\xcf\x7b\x0e\x55\x33\xe2\x3d\x6d\xea\x93\x9f\xf6\x0b\xac\x18\x13\xdc\x97\x04\x7c\x65\x8c\x19\x61\x83\x8f\xe0\x11\x93\x98\x97\x49\x68\x63\xa0\xed\x3b\x7c\xd2\x18\x39\x14\xc1\x7f\x8e\x37\xe2\x1b\xb7\xca\x96\x1f\xae\x59\xf9\x13\x71\xb1\x66\x50\xcd\x94\x16\x1f\x75\x53\xef\x39\xa9\x69\x8e\x82\xba\xf1\x2a\xc8\x67\x98\xa8\x7a\x92\x30\xbb\xfe\x23\x7c\x34\xe9\x08\x00\x3f\xb5\x79\x0b\x79\x3b\x08\x11\x8c\x44\x5d\xfd\xb1\xb9\x00\xcd\x1e\x41\x98\x23\x7f\x77\xe4\x5f\x99\xf3\xf6\x27\xca\x79\x7b\xd9\x5f\x34\xe9\x98\x14\x77\x75\x45\xd6\xa1\xc5\xca\x62\x44\xb1\x6e\x0f\x6d\xe2\xbf\x9b\x2c\x01\xfc\xd7\x70\x39\xd8\x43\x4a\x43\x14\x21\x7a\xa7\x72\x66\xe4\xdf\x60\xa0\xee\xf9\xe2\x74\x8a\xa8\x16\x8e\x15\x92\x8d\xaf\xb7\xbb\x35\xcd\x13\x43\x54\x53\x1c\xb5\x64\xaa\x1b\x54\x36\x18\x10\xbe\x59\x49\x71\x21\x48\x42\x22\x6e\x46\x48\x30\x0d\xa2\x44\xac\x9c\x73\x2a\x02\xfc\xd5\xfc\xf9\x65\x4f\x7b\x03\xac\xa9\xc1\x96\x75\x9c\xed\xbf\x66\x48\x63\xee\x96\x4d\x5c\x0a\xb2\x2d\x81\xed\x8e\x39\x1d\xa7\x49\x48\x18\xc3\xad\xad\x04\x91\x6e\x3d\xb1\x12\x83\x23\x82\x2e\xac\x69\x87\xbd\x5e\x8c\xee\xb8\x43\xd8\x75\x3b\x12\x25\xc4\x89\x16\x71\xca\xbc\x48\x33\x1a\x2a\x37\xee\x5c\x02\x01\x8d\xcf\x34\xc8\x49\x30\x67\x1b\x52\xdf\xcb\xaf\xed\xbf\x52\xfe\x6d\xff\x79\xbc\xcb\xdf\x45\x17\xab\x7b\x78\x5d\x9a\x5b\xc6\x31\xdc\x12\x36\x24\xd2\x4e\x36\x3d\x50\xa0\x2b\x06\x49\xe8\x3f\x06\xec\x98\x7d\xa9\x7c\x69\x58\x52\x9c\x05\x56\x73\x68\xb0\x2b\xc5\x07\x06\x38\x55\x05\xa3\xc8\xb8\x5c\xe0\x2f\x8a\xa8\x3c\xbe\x43\x5a\x30\x8a\xc8\x1e\x83\x94\x72\xd6\x43\xae\x09\xad\x1f\x93\x3e\x21\x25\x24\x40\xa2\xa9\x28\x2e\x6b\x91\x63\x4b\xe8\xb9\x4a\x92\x63\x4a\x2e\xaf\x31\x31\x58\xba\x91\x2d\x69\x53\x10\xc4\xdd\x15\x8b\x6e\x57\x14\xb5\xe5\x60\x43\xb2\x10\xbe\x4e\xa4\xa2\x38\x74\x4a\xfb\x24\x65\x01\xa1\xa4\x65\x7d\xfc\x93\x49\xaa\x2d\x3d\xf1\x50\x68\xa0\x27\x82\xa1\xd4\x77\xfd\x42\x2a\xb6\xe8\x6f\x65\x0d\xec\x4f\xfd\xe0\xd2\xb5\x3a\x45\x62\xfa\xeb\x48\x3a\xe8\xa9\xd9\xc7\x1c\x6c\x30\xe0\xa1\x15\xb5\x95\x85\x51\xa9\xb6\x95\xf8\x72\xbd\xcb\x80\x25\x96\xd6\xcd\xb6\x05\x62\x50\xc5\x70\xc6\xcd\xe0\x2d\x0e\x10\x32\x7e\x94\x10\x47\x63\x0a\x57\x0d\xda\x5e\xc3\x8a\xfe\xe7\xb3\x1d\x01\xfb\x8f\x72\x8b\x11\xe2\x58\x8d\xe4\xfd\x45\xba\x30\x1c\xcc\x99\xdd\x8b\x83\xbc\x10\x90\x4e\xd5\xfe\xee\x70\x42\xea\xb0\x82\xe0\xbc\x68\x5d\xbd\x38\x81\x38\xb4\x90\x6e\xf7\x49\xa3\xb0\xa6\x4b\xac\x21\x01\xdc\xe7\x41\x49\x7e\x20\x9b\x76\x6d\x62\xa6\x25\xed\xef\xcb\xb5\x5c\xaf\x05\x90\x7f\xb7\x52\x09\x22\x34\x59\xcc\x52\xaa\xd3\x94\xa9\x1d\x1e\xd6\xba\xd9\xe5\xfe\x22\xb8\x0c\x46\x31\xf5\x75\xcf\x3d\x0e\x70\xfb\xa9\x9c\x26\xa1\x0e\x48\x95\xa4\xc9\x23\x51\x09\x46\x87\xbd\x4d\x5c\x97\x4d\x3d\xf8\xf6\x63\x9c\xd1\xaf\x82\xed\xc8\xa5\xd2\x83\x11\xa3\x5a\xe5\x04\x81\xed\xdb\xc6\x1e\xaf\x68\xd7\x9c\xc4\xd2\x1b\x41\x7c\xa2\x35\x74\x00\x52\xee\x83\x58\x08\xa6\x96\x20\xa4\xe4\x3c\xc8\x95\x40\xb9\x66\xe2\x8a\x2f\x6d\xb8\x7a\x45\x47\x18\x6d\x98\x65\xdd\xbf\xce\x82\x7c\xe6\x43\x3a\xeb\x35\xcd\xb2\xb2\x9b\x48\x7c\xe5\xe8\xbb\x57\xac\x92\x78\x98\x38\x1a\x86\xfc\xda\x0b\x71\x5d\xd6\x13\x7f\x5b\x25\xc7\x2e\xb2\x07\x65\x4a\x84\xaf\x52\x09\x71\x12\x65\x79\x51\x2e\x20\xae\x28\xe3\x95\x68\x40\x7c\x6a\x0f\xdf\xf5\xab\xf1\x55\xe7\xf8\x12\x02\x6d\xf2\x81\xd7\xcd\xb3\xd5\x58\x53\x94\xd7\xa2\x7a\x95\xa1\xfb\x79\x9a\xd2\xc9\x73\x20\xa1\x2b\x13\xd8\x95\x9b\x20\x3b\xdf\xbe\xe0\x76\xa5\x90\x24\x3e\x0d\x03\xb4\x1b\x0b\x5e\xb6\xd6\xac\x4e\x3b\xeb\xd9\xd4\x45\x4d\xd7\xa6\x0c\x34\x51\xf5\x0f\xd6\x06\x03\x6b\x07\x36\x2e\x70\xb4\xcb\x63\xa4\xbe\xb4\x2a\xef\xf0\x7d\x79\x30\x30\x7c\xe9\x96\x86\x9d\x1e\x8f\xc1\x2d\x6e\xca\xe3\x34\x45\xc9\xb4\x42\x36\x33\xd5\xd8\xe6\xc8\xf9\x24\x5e\xbb\x9c\x08\x8b\x43\x55\xa2\x10\xf9\x82\xa4\xae\xa6\x12\xd1\x84\x24\xa9\xae\x81\xb1\xb7\x45\x90\xe7\x34\xec\xb1\x2a\xb4\xeb\x3b\x06\x91\xa3\x25\x6d\xf2\x32\x45\x78\x30\x03\x16\x3a\x0d\x73\x48\x9f\xef\x54\xd3\x66\x95\xac\x2c\x43\x69\x4b\x79\xad\xad\x2c\x66\xc8\xb5\x24\xc4\xaa\x81\x08\x61\xd2\xa8\x40\x75\xa9\x27\x0b\x8c\xe8\x38\x58\xe6\x94\x9d\xc4\xc3\x34\x69\x17\xe4\x3c\x48\xc0\x28\x29\x5f\xa4\x51\xcc\xaf\xc3\x93\x82\x66\x93\x60\xac\xbc\x63\x37\x38\x8a\x37\x39\x6e\xdb\xfb\x54\x3d\x43\x24\x8e\x7f\x5d\xb5\xa8\xd1\xe2\xfc\x89\x16\xdc\x5d\x33\xdb\x20\x7b\xe4\x7c\x16\x8d\x67\x60\x35\xc0\xd6\x77\x91\x8a\x7d\x8c\x2c\xe2\x65\x5e\x7f\xf7\x2a\x18\x41\xcd\x04\x6b\xee\xe1\xb7\x64\xaa\x91\x61\x57\x17\x54\x55\xb1\x7a\x01\xf2\x36\xc2\x63\xb9\xe0\x88\xac\x95\x6f\x24\xc8\x54\x09\x31\xe6\x53\x87\x3e\xb7\x48\x6f\xce\x7d\x3d\xc7\x1e\xef\x79\xb7\xc1\xfd\x79\x19\x6f\x72\x4e\xc3\xde\x63\x70\xc9\x53\x16\xdf\x81\xd8\xdd\xfe\xb4\x61\x38\xc7\x9f\xfb\x7a\x85\x78\x4e\xd3\x5e\xbb\x25\x8b\x6e\x77\x95\xfd\xb3\x69\x2c\xd1\x1a\x7e\x5b\x66\x02\xad\x4c\x1a\x5a\xc3\xed\x1d\xd7\x26\x5a\x8c\xbc\x35\xdc\xd9\xba\x3e\xed\x6d\x3f\xb9\xb7\x7d\xba\xb7\x7d\xfa\x6b\xdb\x3e\x21\x63\x67\x61\x03\x79\x07\xd6\xce\x25\x7e\x2c\x85\x75\x25\x7f\x98\x75\x34\x91\x97\xce\xfb\xd9\x34\x1f\x96\xa8\x6e\x90\x90\x27\x8e\xb0\xa2\x12\x1c\xfb\x4e\x6e\x27\x8c\x7d\xca\x4a\x09\xb6\x71\x02\x3e\xdf\xf3\xf5\xe1\xfd\xbb\x03\xce\xdc\x6f\xd3\x01\x1e\x70\x09\x58\x2d\x85\x17\x8c\x45\x4a\xde\xbf\x3b\x10\x17\x05\xfe\x0e\x88\xf7\xe8\xe0\x45\x51\xb7\x3c\x4b\x73\x7c\xfd\xe5\x36\x7e\x70\xf4\xf6\xed\xcb\x83\x8f\x87\x47\x6f\xc9\xcb\xf7\xef\x8f\xde\x0f\xc9\x81\xd2\xff\x8e\x79\x95\xfc\x48\x1f\x52\xd2\xde\x20\xac\x3e\xb2\xd1\xee\xfb\xfb\xa0\x5d\xde\x34\x1d\xbb\x7a\x68\xcf\xb5\x08\x05\x5b\x3d\x11\xaf\xcc\xdf\x84\xb4\xa4\x1d\x12\xdb\x2a\x18\x0d\x13\xde\xa5\xd1\x3c\x0f\xa6\x94\xec\x91\xf5\x75\xf1\xd4\x90\x6d\xeb\xe2\x77\x9f\x87\x8c\x75\x52\xfa\xb2\xd8\x33\xe2\x4d\x1e\x12\x35\x5d\x7f\xff\x70\xf4\x16\x66\x25\x53\x5d\xf2\x84\x59\x15\x7d\x73\x1e\x93\x69\x1c\x88\xaa\xcd\xd1\xea\xd9\xfc\xc8\xef\xab\xf1\x78\xe7\x79\xd3\x29\xfd\x78\xf8\xe6\xe5\xd1\xf1\xc7\x21\x11\xb7\xde\x8c\xb8\x58\x27\xe7\x39\xd9\x20\x6d\xf6\x5f\x30\x9e\x31\x8e\xd1\x36\x62\xda\x08\x3f\x92\xdf\xde\xef\x56\xf7\xbb\xd5\x5f\x7b\xb7\x42\x9b\x15\x3c\xbb\xfc\xa3\x9a\xe9\x36\x7f\xcd\xde\xe8\x11\xfd\x1d\xbe\x65\x97\x4e\x87\xd8\xfa\x57\x87\x33\x1c\x93\x29\x37\x8e\x21\xe2\x91\x2d\xb4\xa5\x0f\x0b\xb6\x15\xf2\xd7\x7e\x08\xbf\x90\xb6\xbc\x48\x93\x8e\xf3\x79\xec\x0a\x52\xf1\x1e\x39\x4f\x93\x6e\xcd\x1b\x7a\x94\x99\xa4\xc9\xe5\x3c\x5d\xaa\x16\x55\x42\xc9\xe9\x4d\x22\x6d\x4a\x25\xae\x68\xc8\xe5\x01\x88\x62\xe0\x84\x6b\x12\x69\xea\x78\xf6\x3c\x4d\xe3\x6b\x08\xaf\x1a\x82\x0f\x72\xbe\x49\x50\x0e\x19\xa2\xd9\x81\x07\x22\x34\x34\x3c\xa6\xcb\x13\x1f\x44\x23\x60\x8b\x52\xd4\x3e\x58\x33\xa6\x09\xbb\xdf\x62\x10\xa6\xe7\x28\x5e\xaf\x1d\x81\x01\x21\xdf\xbd\x13\x89\x3c\xa2\x42\xd4\x17\x35\xc1\x05\x87\xf8\x5d\x62\xef\xea\x2f\xaf\x0d\x96\x4b\xaf\x88\x31\xb6\x39\x7d\x86\xdc\x07\x38\xb8\x31\xb2\x70\x1d\x6a\xf7\xe0\xde\x70\x41\xde\x0a\xca\x51\x87\xaa\xab\xf2\x12\xc4\x29\xd1\xf5\x50\xde\xd1\xf4\xda\x7c\x74\xb0\x42\x3d\x43\x2b\x84\x43\xf3\x8a\x71\xe1\xa2\xd5\xf4\xb0\xd2\x88\xa4\x2b\xf5\x1b\x0d\x27\x8f\xa6\x49\x50\x2c\x33\x7b\x38\x38\xbd\x6c\x3c\x18\xa6\x7c\x3c\x0a\xaa\x6a\x40\xe0\xc1\xa0\x79\xff\xc5\x13\x07\x49\xde\x82\x23\x05\x49\xa8\x54\x4b\x45\x0a\x41\x89\x27\x51\x12\xc4\x7e\xb3\x67\x5e\x87\xcf\xa8\x14\xaf\x6b\x2b\x4b\x54\x6f\x20\x45\xe6\xd1\x33\x9a\x5d\x16\x33\xae\xb2\x9e\x8f\x22\x60\x19\x29\x8f\x12\x0d\x7d\x13\x71\x16\x2a\xb1\xe5\xf1\x0d\x22\xba\xe3\xb8\xb6\x53\x8b\x5b\xfd\x42\xaf\x00\xef\x3d\x88\x68\x7f\x1d\xca\x41\x47\x9d\x6b\x11\xa9\xd7\x5c\xb7\x76\x1e\xb7\x9f\xa2\x72\xfe\xb2\x55\x38\x17\xe4\x8e\x3a\x25\xb5\x77\xba\xae\x4a\x53\xcc\xd3\x47\xd9\xb1\xdb\xb2\x74\x14\xc3\xa2\x92\x9f\x83\xe7\x65\x11\x4c\x5b\x94\x3f\x89\x20\xc6\x94\x65\x0d\x20\x80\xf0\xfc\x31\xba\xd1\xc9\xc9\x32\x8e\x4b\x9e\xb8\x68\xcd\x22\x71\x6f\xff\x4d\x85\x30\xd4\x57\x16\x9a\x11\x32\xad\xd1\x9c\x55\x5c\xf7\x0b\xec\x3b\x8f\x63\x3a\x7c\xfb\xea\x91\x33\xfb\xea\xbc\x6b\x07\xd7\x5b\xa9\x36\xe8\x7b\x0d\xc5\x99\x44\x32\x4e\x93\x71\x50\x74\x8c\xd9\xef\x96\x3b\xb2\x29\xe5\x7a\xc2\x8b\x4d\x39\xd7\xb3\x77\x5b\x5a\xc6\xe1\x42\x7e\xf7\xe0\xf2\x30\xc1\x15\x84\xe5\x10\x9c\x10\x78\x2d\xa1\x6a\xf6\xc1\x03\xd0\x37\x98\xbd\xa8\xde\xa6\xcb\xbd\xef\x00\x0e\xee\xd0\xfd\x4e\x90\x4d\xad\xd5\xa5\xc5\xc7\x67\x46\xc9\x21\xfe\x12\xae\x79\xb6\x90\x2b\x14\x31\x3e\x71\xff\xa2\xea\xb5\x9f\x6a\xf1\xc9\x24\x5f\x94\x94\x86\xeb\xdb\xee\xee\xb2\x95\xf9\x4b\x1a\x25\x9d\x56\xcb\xad\x5c\xbd\x8a\xe3\xe4\xc6\xf1\x84\xaf\x37\x40\x36\xec\xb0\x65\xde\xed\xe1\x1e\xe1\xab\x9a\x24\x2d\x0e\x8d\xbe\x2a\x14\x7a\x1c\x0e\x69\xe0\x86\x6d\xc3\xb3\x85\x6e\xcf\x6a\x05\xb7\xaf\x36\x12\xc4\xb5\xd3\x65\xb1\x58\x16\xaf\xd3\xa9\x66\xd7\xc2\x19\x0f\x5a\x2d\xd2\xfb\x0f\x77\x34\x83\xc4\x32\x13\x4c\x73\x6b\x18\x93\xed\x07\x8a\xc3\xf0\x5b\x2e\x83\x9f\x66\x34\x5c\x8e\x29\x9a\xab\x60\x3c\xee\x11\xe1\x8b\x12\xf3\x93\x60\x3c\x3e\x11\xc9\x9c\x27\x32\xa4\x88\x6f\x49\xe5\xcf\xcc\x29\xeb\xe7\xb3\x68\x52\x74\xba\x64\xe8\x60\x54\x66\x39\x4a\xab\x60\x3c\x96\x5a\x2a\x6e\xed\xcd\x49\x9b\xc6\xb4\xa0\x72\x1c\xda\x4b\x92\x99\xce\xa9\xea\x06\x2c\x03\xdd\x5f\x89\x87\x25\x62\x69\xb3\xad\x9e\x8b\x71\xa5\x9e\x15\xee\x4a\x2e\x32\x1a\xae\x16\x7e\x3c\x9e\x1b\x6c\xe9\xe7\x8f\xee\x92\x69\xbb\xde\x25\x53\x55\xf1\xad\x72\x23\x3b\xb3\x02\x62\x48\x80\x86\xf3\x07\x5b\xec\xb0\xfd\x3e\x39\x02\xe5\x1f\xca\x01\x54\x29\x2d\x63\xdb\xff\x06\xaf\x1a\xad\x67\x6d\xde\x27\x8d\x95\xd4\xf8\xb5\xbc\x4d\x31\x50\xf3\xe4\x5a\xc6\x01\xa5\x81\x21\xb4\x74\x82\x00\x4e\x0d\xea\xf5\x01\x60\x07\x56\x9a\x28\xbc\xa0\x27\x8a\xdd\xf3\xb6\x4f\x4b\x07\x60\x58\x4d\x78\xef\x84\x0d\x5c\x22\x97\x58\x55\x57\xc2\x75\x8e\xb2\x6e\xe8\x1b\xeb\x69\x13\x05\xfc\x6d\x9d\x5d\x0e\xfc\xba\xc9\x37\x9c\x06\x3d\xfa\xbf\xea\x48\x22\x38\x88\xc8\xda\x60\x40\x3e\x1e\xbd\x38\x1a\x92\x8c\x72\x8b\xac\x1e\xc9\x53\x61\x3a\xa3\xae\xb8\xb4\x31\x4e\xc0\x35\x5d\x7d\x56\x2e\x2a\xda\x39\x49\xe8\x98\xe6\x79\x90\x5d\xb2\xc5\x02\x21\xb0\x73\x46\x6e\x6d\x70\x58\x0c\xee\xa2\xc9\x79\x9a\x7d\xe6\x52\xde\x7c\x19\x17\xd1\x22\x46\xa1\x1c\xcc\xe0\x29\x7e\xff\x46\x83\x87\xc4\x6b\xcc\xfd\x8d\xb4\xe5\xe6\x75\x98\x66\x0c\xb2\x79\xc3\x88\x54\x37\x46\x43\xbe\x71\x98\x27\x13\x55\xaa\x2f\x71\xe4\xf3\x60\xb3\xce\x3a\x77\xe2\xc2\x9e\xfa\xce\x0f\x65\xb0\x16\x3b\x25\x8e\x81\xa3\xd9\x4f\xe1\xd0\xc9\x57\x53\x8d\x1d\xa4\xb7\x9e\xd2\x23\x94\xae\x5f\x10\xbc\x3d\x26\x07\xc0\x73\xe4\xe6\x39\x3e\x6c\xf0\x1c\xc5\xf4\x84\x49\x8f\xd9\x45\x8f\xe9\xa7\x28\x96\xd3\xc2\x0a\x15\xe3\x73\x72\x55\x79\x10\xab\x9e\xee\x88\x56\x8c\x57\xc3\x78\x86\x5c\x46\x2f\x44\x47\x39\xb9\x5c\x79\xd8\xaa\xe0\x1d\x0c\x9c\x20\xc3\x51\x7a\xd1\x37\xd8\x91\xfe\xd8\x25\x12\x40\x72\x21\xf8\x7f\x57\xa6\x2a\x96\xc3\x7f\xa8\x74\xc4\x68\xe4\x4f\x53\x8e\xa4\x17\xe2\x7d\xb7\xcb\xcd\x39\x1a\xb4\x6b\xa2\x12\xfe\x5c\xc2\x91\x5b\xc3\x1d\x70\x61\x84\xbd\x86\x33\xc6\xfc\xdd\xfd\xcd\xe8\xfd\xcd\xe8\x5f\xfb\x66\x54\x5c\x8b\x8a\x37\xbf\xff\x15\x01\xf6\xee\xd4\x65\x38\x1c\x02\x1e\x92\x83\x34\x39\xa3\x8c\x15\x05\x22\xe6\x31\x9c\x83\xe1\x2c\x00\x81\x8b\x65\x24\x17\x46\xc0\x41\x9c\xa7\x24\x88\xe3\xf4\x3c\xe7\xf1\xd9\x41\x51\x97\xf7\xd7\x58\x45\x52\xf0\x7f\x13\x5d\xd0\xf0\x9a\x67\xad\xb9\xf7\x1a\x6b\xe2\x46\xb5\x48\xed\x28\xc7\x42\x65\xa9\x0e\x9c\x1d\x53\x25\x4a\xae\xae\x64\x84\x74\x9d\xd1\x56\x3a\xd4\x76\xd7\x56\x06\xf0\xb3\x9c\x10\x91\xb8\x62\x96\xf7\xa1\x23\xf5\x8b\x46\x43\x5c\x0f\x71\x34\x01\x55\x73\x17\x6a\xdf\x74\xea\x04\x48\xc1\xf7\xf1\x93\x56\xe3\xce\x48\x46\x51\x52\xed\xc0\x91\x8b\x89\x9a\x8c\xd3\xca\xcb\x1f\xdb\x12\x36\x55\xfa\x7d\x71\xd8\xea\xb1\x49\x38\xa3\x59\x34\x01\xc7\x1e\x19\x1d\x07\x8c\xe3\xa0\x48\x35\x0f\x1e\x90\x38\xf8\xf5\x92\xc4\x69\x10\x92\xf0\x32\x09\xe6\xd1\x98\xa4\x09\xcd\xa1\x35\x31\x21\xba\x21\x11\xcd\x3a\x55\x7a\x02\x80\x92\x06\xf6\xb2\x71\x07\x8a\xcd\xd6\x94\x16\x47\xea\x90\xec\x71\xe1\xcc\x26\x46\x0b\xac\x75\xfe\x01\xb0\x32\x41\x4c\x89\x3c\x26\x97\xdf\x7a\x18\x9a\xfe\xd2\xab\x17\x9e\x9d\x9f\x47\x10\xb0\x04\xf5\x8a\x80\x0e\x22\xa7\xfc\x04\x3d\x74\x9e\x56\x71\xe1\x7d\x99\x51\xa1\x5e\xec\xc1\x05\xde\x98\xaf\x0e\x7e\x38\x9e\xd1\x0b\x9f\xda\x40\x6b\x4d\xad\x04\xcb\x15\x65\x83\x22\x86\xe6\x53\xc4\xd5\x2e\x55\xca\x5b\x0a\x7f\x19\x85\xfb\x99\x88\x4f\xce\xaa\x12\x8b\xac\x4b\x86\x72\xbd\x09\x30\x57\x56\xf2\x5d\x13\x78\xde\xd7\x41\x37\x87\x56\xb7\x7b\x0e\x1c\x5b\x02\x1a\x8a\x7d\xb9\x30\x45\x8a\xeb\x71\xf3\x03\x19\x96\x59\x02\x05\x38\x28\xb3\xdd\x1a\xdc\x5f\x0d\x57\xba\xd6\xea\xab\x72\x5d\x5f\xef\x6e\x52\xa3\x28\x65\xea\xa7\xd0\x41\x87\x53\x60\x3e\x63\x14\xe8\x41\xb8\x45\xea\x52\x55\xb3\x1f\x86\xfc\x59\x84\x52\xa2\x05\x49\x48\x72\x5a\xe4\x64\xb9\x80\x0c\x71\x1a\x01\x96\x11\x15\x34\x63\x7b\x47\x7a\x26\x84\x2d\xe1\xc7\xb4\xbf\xb6\x86\x9e\x46\xbc\x4e\xa7\xf9\x7e\xf1\xa1\x08\xb2\x62\xcd\xd6\x34\xe6\x34\x9e\xa8\xc4\x89\xfb\x80\x59\xb0\x70\xb3\x16\x23\x50\x18\x8d\x27\x8e\x13\x1f\xf9\xca\x6e\x4a\x0b\xae\xcf\x62\x85\xad\xa7\x76\xa0\x5f\xd0\xc3\xcc\xa1\x7b\x44\x9e\x3c\x2d\x9e\xc1\x5a\xe9\xfb\x18\x07\x64\x4c\x69\xd1\xb1\x1e\xfd\x08\x4b\x46\xe7\x94\x33\x18\x88\x17\x34\xf0\x4c\x94\xf5\x51\xa0\x0d\xcc\x26\xe1\xa2\x5b\x26\x4a\xb3\x23\x70\x85\xd1\xef\xf7\xc9\x2f\x4b\xee\x09\x98\xb5\xc9\x78\xaf\x73\x5e\x2e\x79\x19\x59\xf1\x2a\xf2\xda\x7e\x02\x6b\xad\x74\x35\x0c\xff\x19\x93\x67\x7a\x0f\xa6\xdc\x90\xb3\xee\x9d\x26\x7f\xbc\x63\x9a\x7d\x1a\xfd\xab\x77\xc4\xfa\xf5\x48\x77\x91\xc6\x31\x27\x1f\x3f\xd9\x0a\xda\xd4\x60\x36\x5d\x2a\x95\x08\xa8\x6d\x93\x37\xca\x0c\xd7\x20\x96\xb4\x84\x5c\xc4\x8c\xa6\xce\x9c\x4a\x23\x0b\x46\x7a\x72\xac\xbe\x49\xf0\x3d\x9b\xf2\xd1\x44\xda\xf8\x24\xdf\x94\x3a\x6e\x46\x19\xda\x4c\x19\x86\xa6\x95\xd7\xcf\xac\x04\x5d\xc9\x50\x16\x72\x49\xe7\x56\xe8\xb9\x1d\x91\x96\xea\x03\xa0\x4f\xb6\x37\x6a\xc6\x78\xde\xa5\x71\xcc\xf8\x8c\xee\x09\xa7\xc1\x21\x2f\xc2\xce\x69\x74\x4e\x93\x02\x8e\x9c\x7d\x46\x71\x30\x34\xbd\x97\x2c\x84\xa1\xfd\x09\xc7\x14\x90\xe3\x61\x78\xda\x93\x57\x54\x46\x72\x4f\x13\xa3\xc8\xc1\x7e\x8c\xb8\x82\x18\xe8\x97\x6d\xd6\x32\x6c\xa1\x43\xe2\x96\x4c\xd6\x23\x4e\x7c\x0f\xb9\xdc\x3c\xb7\x03\x3d\x71\x9a\x3a\xc8\x28\x8c\x09\xec\xb5\x0f\x3c\x2f\x1d\x81\xd9\x71\x0d\x36\xba\x70\x35\xf0\x81\x34\x7c\xab\xa8\xca\x4a\x75\x5d\xa5\xca\x1e\xbf\x52\xcd\xec\x0c\xb2\x25\x20\xa5\x2e\xe3\x4b\xad\x31\xb5\xb0\xa9\xc5\x60\x4b\xf4\x45\xd0\x0e\x1a\xcc\x04\x04\x29\x67\xde\x7d\x32\xa6\x56\x88\xb0\xac\x51\x19\x62\xcb\x3d\x28\xcb\xd7\x6c\xcf\xc9\xc2\xd7\x4e\xea\x77\x69\xbf\xfb\x09\x3d\x17\xb7\x4e\x18\x07\xd8\x59\x18\x67\x92\x51\x68\xf8\xc6\xf3\x33\xc7\x9a\x65\xdf\x19\x8f\x3c\x62\xee\x78\x54\xcb\x07\x89\xe0\xc8\xe2\x5c\x58\x41\xbd\x96\x47\x52\x97\xbd\x54\x94\xf5\x77\xa3\x5a\xef\x6c\x2c\x6d\x46\x04\xa1\xeb\x08\x10\xfb\x6a\xc8\x28\x5c\x32\xb0\x33\xc7\x82\x26\x21\x18\xb8\xa9\x49\x0e\x72\x50\xb4\x24\x39\xa3\x50\xe5\x0c\x46\x57\x94\x4e\x00\x98\x15\x62\x52\x4f\x97\x2b\x57\x54\xeb\xcb\x24\xc8\xf3\x68\x9a\xd0\xb0\xef\xf6\xd1\xa6\x28\x1f\x4f\xf6\xcd\x8e\x92\xb1\xc6\xa3\x9a\x09\xf2\x36\x83\x4d\xc6\xd0\x48\xb4\x3d\x31\x89\xb1\x74\x18\xc4\x19\x0d\xc2\x4b\xfd\x60\x5d\x0b\x8a\xf9\xed\x29\xcd\x14\x64\xa5\xf4\x5a\x37\xae\x68\xd2\xb1\x5a\x53\x4e\xe0\x36\x5d\x97\x5c\x7a\x65\x72\x2e\xee\xf3\x0b\xc9\xa4\xe8\x22\x15\x63\x8b\xe6\x73\x1a\x46\x41\x41\xe3\x4b\xbb\x59\x41\xee\xe3\xa6\xb4\x6d\x4a\x27\x50\x7d\xa7\xc4\xd5\x84\xcf\x6d\x15\xd6\x64\x73\x96\xcf\xb6\x1f\x3e\x18\x74\x97\x7b\xee\x84\xe9\xb0\x37\x73\x93\xb7\x71\xc3\x3e\xd4\x0f\xa9\x8e\x31\x98\x23\x1e\x8d\x35\x4f\xe2\xba\xd4\x1d\x08\xc2\x35\xba\x13\xbe\x6e\x3a\x10\xbc\xef\xd6\x8f\xc7\x91\x1c\xd2\x85\x14\x1c\xcc\x81\xd4\xf0\x77\x78\x5a\x3e\x4f\xcf\xa4\x4a\x93\x04\xf9\x65\x32\x56\x87\x1f\x9f\x60\xe4\xe3\xdb\xcb\x04\xde\x4e\x1b\x08\x40\x32\x86\x85\x2d\x87\x77\x61\x43\xf8\x55\x6a\x36\x04\x7f\x07\xa3\x53\x2b\x66\xbb\xcf\x7b\x82\x23\x53\x78\x4d\x4e\x54\x49\x5b\x28\xb7\x76\xd4\x12\x3b\xca\xc1\x80\x1c\x4e\x34\x67\x8c\x72\xf5\xae\xef\x92\x0a\xff\x2b\x24\x2a\x88\x76\xd3\xa5\xcb\x9d\xcf\x28\x18\x63\x88\xd1\x77\x09\x67\xaa\x39\x89\x0a\x93\xad\x7a\x37\x6a\x87\xd8\xd5\x32\xf3\xed\x1e\x3e\xf4\x8b\x1a\xed\x09\xc5\xfb\x31\x84\x48\xf1\xf0\xb7\xaf\xe8\xa0\xc7\x92\xc7\x33\x6a\x5b\xef\xc5\xe9\xb4\xac\x5d\x62\x31\xa6\x8a\xb3\x05\xd4\x32\x64\x7b\x42\x89\x3f\x3e\x7f\xc4\x12\x13\xc4\x39\x00\xd8\x03\x6b\x4e\x47\x8e\x9f\x29\x21\x88\x1f\xbe\xe0\x09\x43\x41\x63\x9d\x6e\x9f\xef\xc8\xe3\x40\x7a\x2c\x04\xbf\x2a\x34\x24\x6c\x75\xcf\xb2\x34\x49\x97\xb9\x72\x5f\x28\x0c\x03\xd8\x6e\x6f\xbb\x22\xe2\xd5\x08\x61\xb7\xed\x35\xaf\x05\xa7\x12\xa9\xb6\xd2\x6b\x42\x40\xae\x0d\x1d\xab\xa1\x7e\x0e\x6f\x31\x6f\xd7\x35\xfc\xd8\xb9\x22\xe5\xb8\x75\x82\xbf\x55\x5c\x90\x5e\x9f\xf6\x76\x36\x9b\x5c\x81\xb6\x97\x39\xd7\x8b\x8f\x8b\xf6\xda\xfd\x85\xe8\xfd\x85\xe8\x9f\xf8\x42\x54\x3f\x15\x45\x2a\xeb\x9b\xbc\x17\x15\xc0\x2b\xdc\x64\xfa\x82\xbf\x35\x7e\x62\x9a\x4c\xa2\xa9\x17\x8e\x67\x49\xc0\xc3\x51\x60\x05\x75\x89\x46\x41\xe2\x09\xd4\x02\xda\x64\x1e\x69\x8a\xdb\x48\xf3\xcb\xcc\x51\x34\x15\x1e\x0c\x2c\x2b\x46\x0e\xf4\x3c\x9a\x5a\x4a\x7d\x6c\xcd\xc8\x35\xce\x57\x1c\xe2\x4a\xc1\x5e\x9b\x5e\xab\x74\x3a\xb6\xc4\x05\x3d\x63\x49\x1b\x86\x54\xc4\x7b\xe7\x7d\x86\x56\xa4\xaa\xac\x04\xdb\x55\x4a\xa0\x28\x7f\x97\x51\x71\x0d\x8a\x6e\x27\x8c\xba\x47\x3a\xdd\x6a\x60\x84\x4b\xb0\x83\x84\x70\x7f\x4f\xae\xae\xdc\x3c\x71\x36\xf5\x67\xd2\x20\x8b\x23\x56\x14\x75\x2d\x59\x2c\x8b\x17\x74\x12\x2c\x63\xef\xc5\x49\x5d\x1f\xd9\x8e\x6c\xb7\xa3\xae\x7c\xbd\xe1\x5b\x18\xc9\xf4\x43\xd4\xa2\xc7\xf7\x54\xf9\x3d\x0e\xee\x82\x35\x8a\xdf\xa2\xfb\xf6\x8b\x2e\x2e\xa0\xb0\x5a\x4a\xe6\xd8\x68\xd4\x53\x21\xca\xf6\xe0\x41\xd2\xd6\x2b\x7a\xe1\x19\xb9\x58\x55\x7c\xb0\x39\x32\x8a\x4c\x27\x24\x30\x7c\x03\x82\xe3\x49\x65\x47\xa0\xec\x02\xd8\xba\x7b\xf5\xf2\x9f\xd6\x72\x83\x3a\x98\x5c\xec\x5d\x68\x52\x97\x6f\xf8\xd8\x75\x0c\xdf\xe5\x15\xb9\xd4\xf6\xbb\x75\x7a\x23\x7f\x7f\x31\x2e\x8f\xe1\xfa\x0f\x5d\xc1\xc2\xe7\xd5\x95\x45\x43\xfb\x63\x88\xbb\x80\x1c\x9f\x61\x78\x8f\xc7\x2d\x59\x2d\xf4\x49\xb8\xa1\xf2\x5f\x3d\x9a\x72\x10\xae\xba\x48\x45\xc0\xe8\xa8\x20\xf3\x68\x3a\xe3\x82\xa3\xf2\x5e\x2c\x94\x54\x4e\xcb\x45\x5a\xdb\x6e\x91\x9a\xad\x9e\xb4\xe7\xc1\xc5\x8f\x94\xbe\xa3\xd9\x4f\x41\xde\xee\x11\xf6\xfd\x2e\x8b\xd2\x2c\x2a\x2e\x8d\xf4\x69\x90\xbf\xcb\xa2\x31\x15\xbf\xd9\x7f\x30\xcd\xec\x47\x92\x26\x63\xea\x7b\xc5\xf8\x99\x5e\x56\xbc\x63\xfc\x4c\x2f\x9b\xbe\x64\x84\x9a\x1c\x5c\xf3\x1a\xf6\x90\xdd\xc5\x0b\x3a\x8e\xe6\x41\xdc\xc1\x00\xee\x4b\x32\xf3\xb2\xf5\x6b\x13\x3b\xf2\xb9\x79\xd7\x34\xef\xab\xfa\xee\x49\xff\xa6\xd4\x7d\x4f\xd7\x7f\x44\xba\x16\x42\x91\x43\xd8\x70\xff\x2a\x83\x09\x09\xaa\xf6\x8a\x4a\x8d\xe9\xf9\xc2\x14\x8f\x44\xfa\x9a\x21\x13\xd5\x52\x70\x71\xd1\xfd\xa2\x34\x83\x17\x7d\xbc\x9d\xae\xcb\xd3\xb9\xd6\x88\x99\x00\xca\x43\x46\x2a\xf1\x67\x02\xa8\x37\x20\x2c\x1d\xe1\x02\x5e\x9b\xf9\xab\x77\xa0\xbc\x6d\xd8\x50\x52\xf9\x77\xd1\x07\x92\xf2\x17\x82\x2c\x0d\x39\x0d\x72\x3f\xdc\x34\xc8\x0d\x28\x20\x5f\x04\xaa\x45\x45\x94\x6f\x0c\x15\xaf\x0d\x93\x50\x35\x55\xdb\x60\x25\xf5\x63\x18\xc3\xcf\xa7\x6a\xc9\x59\x75\xd5\x2d\xba\xe0\xe5\x2d\x3b\xb0\x46\x0f\x8a\x8b\xbe\x34\xfc\xf3\x56\x80\x9f\x19\x4b\x2d\xc4\xc5\xca\xcb\x46\x86\xe8\xb9\xc9\xf2\x11\xd1\x82\x2a\x57\x91\x0a\x5e\xb5\xca\x52\xb2\x2b\xb6\x5c\xd6\xe0\xa8\x43\x3a\xca\x50\xcd\xda\xf2\x41\xb9\xf4\xe9\x81\xd2\xa4\x27\x33\x1b\x2c\xb5\x52\xd0\xf2\x26\x4b\x16\x9d\x0a\x86\xb3\x9c\x2f\xe3\xa0\x88\xce\xe8\x4f\x41\x7e\x9c\xc3\x9b\xbe\xb2\xaa\x1c\x58\xab\xae\x69\x6d\x0d\x53\xa3\x1c\x1a\x3b\x9d\x4c\xe8\x58\xd4\xcc\x57\x6e\xc9\x72\x28\x2f\xe0\xa3\xe7\x52\x68\x7b\x51\x9a\xd6\x22\xb2\x58\x9c\x4e\x6d\xe3\x4b\x9d\x81\x22\x0a\x39\xda\x41\x50\xf1\x79\x75\x83\x9e\x07\xca\x0c\xb6\x4e\x11\x28\x5a\x6a\xb4\x0e\x81\xc8\x9a\xaf\x3c\x38\x38\x55\x2e\x36\xa8\xb0\xc1\x52\x33\x6b\xc2\x26\x46\x50\x83\xf6\xd9\x4d\x94\x73\x26\xf0\x52\x28\xf4\x0f\xf8\x68\xd8\x1f\x05\x39\xad\xe5\x8d\x3e\x50\x1f\x19\x78\xe0\x0c\x02\xe0\xf9\xd3\x20\x7f\x1d\xcd\xa3\xc2\x43\xbf\x26\x80\x28\xab\x12\x4b\x88\xde\xc8\x37\xca\xe4\xd1\xaf\xbe\xdd\x4e\x67\x1a\xd0\x45\x34\xa7\x79\x11\xcc\x17\xa5\x45\x14\x84\x5e\x58\x3c\x23\x29\x63\x5b\x46\x76\x59\xb5\x4a\xa7\x82\x3a\x13\x46\x93\x49\x34\x5e\xc6\xf0\xae\xa7\x0c\xd3\x1a\xc8\x1c\x48\x5a\x04\xf1\x8b\x26\x15\x58\x90\x58\x6a\x36\x57\xaa\x00\xd7\x3c\xce\x5c\xaf\x6e\xb6\x2b\x6b\x46\x05\x9d\x77\xed\x17\x7d\x8e\x59\x25\x40\xb9\x17\xd8\xc6\xaa\xf6\x49\x6d\xbc\x60\xdd\xf2\x1e\x71\x9d\x4c\x83\xc5\x1d\xa7\x53\xef\x2a\xc6\x1c\xc5\xb7\x86\xe3\x74\xaa\xd5\x6f\xee\x42\x86\x7a\x8d\xc5\x8c\x2b\xc4\x4b\x19\x5d\x7b\x44\x13\xf6\x65\x6c\x6a\x6a\x9c\x56\x86\x87\xc6\xec\xa2\xbb\xb8\x4e\x67\xd7\x32\x2a\x6e\xb0\xfd\x79\x2b\x31\x9a\x88\xd3\xa9\xa7\x6a\x99\x5a\x52\xa5\x2a\x64\x9e\x2e\xe0\x2a\xa7\xfe\xc4\x7c\x3e\x8b\x72\xc6\x8d\x17\x69\x5e\xdc\xe0\xc8\xfc\x2e\xcd\xab\xa5\x22\x37\x06\x53\x25\xd7\x76\x2b\xc5\x13\xcd\x3a\xa9\xcc\x42\x07\x03\xe8\x74\x7f\x11\x5c\xc2\xbb\x8a\x3d\x43\x4d\x86\xb3\x04\x92\x21\xa9\x28\x62\xef\x79\x4d\x66\x62\xd8\xf3\x34\xfb\xfc\x31\x7d\x97\xa5\x67\xb4\xbc\x0c\x02\xc2\x65\x17\x42\xe2\x2d\x2f\x28\x21\x50\x68\x81\x09\x8e\x3c\x65\x58\x52\x73\xd6\xc2\x3b\xc9\xdd\xac\x60\x9e\x82\xd2\xc9\x9e\xf1\xf5\x8c\x9c\xa0\xcf\x53\x32\x54\x66\x0c\xd7\xba\x55\xae\x83\xe7\xea\xf8\x38\x4e\xcf\xe1\x59\x89\xd4\x6b\x54\x55\x5f\xfd\x0c\x82\xc7\x4e\x64\xc4\x44\xd2\x24\xbe\xe4\x01\x21\x0a\xe3\x75\x86\x7c\x21\xc1\x5f\x42\xf8\x1e\xf6\xc8\x67\x12\x64\x68\x3f\xda\xc1\x0f\x24\xec\xa3\x35\xeb\x63\x23\xde\xa5\xee\x83\x80\xfe\x85\x95\xaa\x97\x9b\xd5\x51\xba\x9f\xac\xcd\x7b\x85\x6a\xc2\x16\x74\x0d\xf8\xa5\x17\x8b\x28\xbb\xf4\xac\x78\x94\x8b\xc9\x2d\xe7\xee\x63\xbc\xd0\x2c\xaf\x6c\x09\x58\xa0\x9e\x05\x00\x94\xed\x93\x25\x2c\x88\xee\xae\x6f\x55\xbe\x0f\xce\x25\xc9\x88\x14\x2f\x18\xaa\x7e\x3f\x1f\x47\x91\xbd\x7c\x65\x19\xbc\xdb\xfe\x3d\x17\x88\x53\x70\x48\x9a\xd3\xeb\x50\x35\x00\xfe\x94\x21\x0a\x9a\x8f\x39\x0c\x06\xab\xac\x08\x58\x9b\x78\x35\x96\x2e\x46\xbd\xdc\x6e\xb1\x92\xac\x6b\x00\x8e\xa2\x66\xf4\xaf\x98\xaa\xad\x91\xf3\x85\x4b\xc1\x66\x3e\x11\xbf\xc6\x4b\xe8\x39\xdc\xe8\x75\xcc\x60\xda\x70\xd5\x31\x0a\x92\x7e\x94\xff\x23\x88\xa3\xb0\x03\xb1\x2e\x44\xca\x8b\x28\xa3\xe3\xa2\xe3\xbb\xe7\x10\x2e\xc5\x00\x50\xd4\xd8\xe9\x3a\x97\x28\x58\xd0\xd1\x31\x88\x64\x0f\x3c\xd5\x1a\x5e\xeb\x3c\x15\x35\xa8\x42\xf4\xcc\xac\x89\xab\x27\x6c\x1b\x12\xe1\x57\x5c\xc2\xb6\x65\x50\x70\xbd\xd0\x3f\x5c\x26\xe3\x28\xf1\x4b\x2b\xc2\x91\x37\xba\x79\x5a\x37\x93\x88\xeb\xd7\xc8\x10\xcf\xc0\x0b\x12\x18\x21\x46\xc9\x14\x0e\x38\xde\xe3\xad\x0b\x66\xfa\x92\x12\x6e\x9d\x6a\x2a\xc0\x50\x66\xf9\x59\x34\x9d\xd1\xbc\xae\x3c\x86\x42\xb4\x23\x72\x3f\x27\xe9\x79\xf2\xa1\x08\x0a\xea\xf3\x2b\x88\x72\xcb\x1b\xc0\x55\xec\xda\x35\x2c\x96\x71\x4c\xc3\xba\x2a\x30\x54\xc9\x51\x57\xbb\x97\x2a\x89\x20\x50\x77\x81\x3a\xac\x85\xe8\xe9\x7a\x2a\x2a\xa8\x29\x69\xdc\xad\x0d\x3d\x69\x08\xd6\x77\x36\x18\x96\x67\xa1\x92\x36\x2f\x19\xfa\x93\x51\x09\x63\xe7\x1b\x7a\xd2\x38\x6c\xd9\xf5\xfb\xb0\x34\x07\x97\xf3\x0f\xa8\x3c\xaf\xa4\xac\xad\x93\xf3\x54\x61\x83\x18\xbd\x37\x54\x0e\x43\x6f\x2a\x86\xc7\xa7\x9a\xa1\x27\x0d\xc3\x5a\x68\xf4\x24\x62\x68\x9b\x0b\x0d\x4b\xd2\x39\xf7\x32\x6c\xb6\xf8\x05\x53\x6b\xb8\xf5\xb4\xcc\x13\x11\x63\xe1\xad\xe1\xce\xce\xf5\x69\x6f\x67\xeb\xde\x8b\xc5\xbd\xd1\xd6\x7f\x8d\xd1\x96\xa0\xf4\xbb\x08\x47\xb3\x9a\xef\xfe\x86\x96\x5a\x3c\x5a\x8e\x69\x82\xc5\xd3\xbe\x42\x10\x80\xe6\x6e\xfb\x83\x38\x1e\x58\x91\x2d\xe1\x3d\xae\x1d\x16\xc7\x75\xe6\x2f\x8d\xca\xdd\x08\x60\x15\x4e\xfc\x7d\x21\xc0\x3e\xf1\xcd\x4d\x38\x99\xc7\xc1\x6f\x57\x77\x00\xaf\x2b\x15\xbb\x03\xae\x95\x27\xdd\xae\x5a\x88\x9b\x17\xc0\xb1\x0e\xea\x94\xdf\x18\x46\x06\xc7\x15\x20\xe2\x13\x43\xdc\x49\x00\x02\xb6\x3f\xd8\x93\x61\xb8\xaa\x04\xbb\x00\xfd\x84\x0b\x9f\x2c\xb2\x69\x8e\x65\xdd\x1b\x84\x7c\x96\x32\xbe\x0e\x8f\x07\x7e\x20\x80\xd7\xf3\x47\x47\xd9\x34\xe7\x51\x06\xd6\x85\xf0\xd6\xac\xc3\x58\x18\xab\xec\x34\xee\xde\x0f\x0e\x29\xc9\x1c\x1c\xec\x4f\xbc\x6e\x74\x07\xe7\x1f\x9b\xed\xbb\xa0\x42\x4c\xec\x68\x3c\x34\x44\x44\x55\x48\x3f\x1c\x85\xd8\x17\xb7\x2a\xca\xc9\x38\xcd\x32\xd7\xa5\x24\x9c\x80\x82\x82\xee\x67\xd3\xdc\x17\xe5\x4f\xc7\x19\x7f\x48\xfe\x06\x27\xa8\x9c\x7c\x81\xf3\xd3\x35\x6b\x2f\x2a\xc4\x9b\x0e\xc3\xeb\xa4\x67\xaa\x70\x3b\xa5\x73\xa4\x0f\xb9\x1c\x0a\x50\xe4\xd8\x82\x04\x1a\xf1\xec\x8c\xcf\x1f\x02\x80\x42\xc8\x70\xe7\x02\x9b\x27\x38\x11\xd4\xd1\xbb\xd8\x56\x1b\xc0\xd3\xbf\x2c\xb8\xcc\xd7\x8c\xb9\x5b\xef\x38\xc1\x1f\x83\xae\x72\x49\xce\xce\xc5\x8e\x8a\xde\xba\x31\x10\xe0\xdc\xbd\x70\x25\xbc\xbe\x94\x30\xca\x58\x05\xac\x57\xb4\x70\x56\x97\xd8\x91\x84\xb8\xbe\xb7\x57\x46\xc8\xe6\xcb\x25\x76\xf6\x15\xf1\xd7\x2a\xa2\xa4\x75\x1c\x87\x02\x55\x2e\x78\xa5\x52\x06\x6c\x94\x30\xa9\x18\x41\x24\xd2\x77\x1c\xcc\x43\x5e\xce\xa6\xa1\x5d\x6f\x97\xb8\xe3\x0d\x62\xd5\xaa\x36\xd9\xaa\xa4\x3c\xd5\x7e\x25\xd9\x19\x61\x47\x57\x67\x18\xab\xf2\x0b\x33\x5a\x68\x49\x38\xd2\x6b\xcd\xcd\xf1\xf2\xe9\x78\x62\x83\x16\xa9\xdf\xf3\xbf\x11\x3b\x74\x8f\x94\x78\xf5\xf7\x39\x87\x17\xef\x56\xd0\x70\x8d\xa0\xa4\x15\xb6\x4f\x25\x51\x6b\x24\xea\x6f\x16\x9d\xc3\x5b\xbc\x72\xde\x6f\x14\xa3\x43\xb8\x17\xdf\xec\x91\xa7\x52\x1b\x54\xd1\xc4\x32\x59\x04\xe3\xcf\x47\x5c\x0f\x6d\x58\x01\x42\x92\xa1\x1b\x32\x93\x74\x17\x4c\x37\x4b\xb2\x2a\xfe\x43\x91\xde\x1e\xd9\x26\xcf\x64\xa2\xf4\x80\x4e\xe4\x39\x50\xbb\x04\x50\x7e\xcb\xcb\x1c\xa0\x63\x21\xa7\x27\x8a\x9b\x33\x2a\x74\x29\xd8\x7d\xb3\x8a\x7d\x77\xb2\x79\x4a\x86\x3e\x27\xdd\x07\x10\xfb\x39\x40\xe1\xb6\x25\xb2\xec\x80\xde\x41\x1c\xe3\xc5\xdd\xef\xf7\xe5\xfa\x3e\xb0\xcb\x5a\x9b\x8f\xe3\x1e\xe7\x90\x6f\x77\x10\xd6\x57\x82\xb2\xdd\x28\x50\x35\xf4\xcc\xb8\xfd\x32\x99\xfb\x82\x83\xb7\x87\xf2\xd0\x15\x18\xaf\xc3\x82\x24\x34\x7d\xa8\x48\x30\x1e\xf8\x9a\x9f\x8c\x58\x1d\x3c\x68\x20\x03\x17\x68\xf3\xd2\xae\x98\x55\x88\xdb\x5b\x47\xb5\xd0\xab\xb2\xd8\xc8\xab\x04\x3e\xf6\xef\x9b\x52\x06\xb3\xac\x25\xd5\x1e\x03\x07\x19\x2d\xff\x09\x97\xc9\x86\x58\x88\xd9\x0f\xb8\x7d\x36\xa5\x2f\x5c\x04\x8b\x3f\x76\x31\xad\xd0\xe7\xae\x9a\x25\x97\x96\x70\xfa\x5a\x7e\xdd\xf7\x14\x57\xeb\x68\x15\xe3\xa3\xc5\x8c\x23\x41\x54\xdd\x33\xba\xe6\x3e\xbc\x83\x52\x78\x09\x77\x8c\xf5\x80\x9c\x8d\x3b\x4f\x65\x9b\x34\xd8\x73\x1d\xdc\xb8\x3c\x00\xb9\xb7\x91\x2f\x2a\x0c\x27\x08\x3d\x6e\x5c\xb1\x6b\x3a\x15\xe6\x9d\xa6\xa1\xe3\x40\xbd\xc8\x2e\xad\x77\x83\x08\x14\x9e\x0a\x96\x8f\x97\x18\x6f\x1b\xc7\xf0\xb8\xbc\xe3\xb8\xa8\xe1\x14\xbf\x47\xa8\xd7\x39\x8e\xdd\x79\xd9\x3a\x92\x64\x2a\x37\x8a\x26\xe7\x4a\x7b\xdb\x30\x8b\xd4\xee\x0a\x56\x0b\x7f\xaa\xa5\x56\xbb\x66\x24\x49\x09\x40\x61\x8e\xf9\x03\xd9\x84\x43\x8d\x71\xd6\x74\xa5\x43\x1c\x31\x34\x48\xf8\x3b\xed\x24\x14\xbe\x1c\x21\xe4\x6b\xf2\x48\x1e\x54\x9d\xd8\xb7\x35\xcb\xd5\x88\xf7\xc6\xd6\x8d\x35\x0f\x1d\xf3\x16\x4f\x54\x57\x0b\xde\xdc\xd1\x3e\xcd\x8b\x68\x1e\x14\xf4\xa7\x00\x14\x88\x75\x54\x85\xc0\xeb\x28\x0a\xd7\x7c\x17\xd4\xf4\xf5\xa9\xa3\xd9\x0c\xa1\x71\xd5\xcd\x8e\x07\xb4\x6c\x66\xde\xcb\x66\xa8\x0c\x1d\x06\x21\x50\xa4\x2e\x50\xc8\x07\x78\x2a\xa6\xb4\x78\x61\x87\xf6\x91\x3b\xab\x5d\x4d\xdd\x5c\x89\xba\xee\x78\x9e\x1a\x21\x5e\xde\xe8\x8a\x95\x59\x11\xa9\xdf\x2b\x35\xdf\x22\x00\x21\x2e\x2a\xf1\x8c\xc8\xbe\x12\x61\xbf\x6d\x34\x42\x55\xff\x8d\x02\x12\xaa\x42\xab\x0e\xf2\x6b\x46\x27\xd4\x3a\x1a\x36\xc0\x6c\x31\x96\x6e\xaf\x72\x7e\x6a\xae\x63\x44\x02\xba\xdc\x2a\xa5\x62\x5c\xa2\xec\x1f\x9b\x2b\x11\x23\x4a\x8b\x04\xc3\x62\x8a\x11\x6c\x04\xcf\x89\xeb\x27\xce\xd2\xb8\x3e\x03\xe7\xb3\x9f\x58\x8f\xdb\x64\xc8\x3f\xac\x9d\xa4\xdd\x73\x84\x97\xa1\xf6\xcf\xa6\xf2\x94\x67\x39\x31\x9c\x53\x9d\xc5\x3b\x2e\xfd\x96\x72\x06\x59\x4b\x0c\x32\xac\x4a\xd9\xf6\xa3\x02\x16\x55\x6f\x3d\x9e\xd8\x42\x78\x82\x0b\x43\xd0\x59\x37\xb1\xa3\xad\x71\x60\x9b\x2f\xb0\x0c\x25\x7d\x73\xe8\xb4\xb2\xad\xc2\x42\x67\x3f\x58\x2c\xe2\x4b\xe1\x29\xa8\x11\x61\x75\x6d\x33\x36\xbe\x05\x58\xcd\xb0\xc4\x1b\xd5\x5d\x33\x0f\x22\xfe\x8e\x66\x3c\x3a\x04\xcf\xad\x63\xef\x78\x26\xec\x6b\x85\xdf\x91\xe9\x7a\xc5\x63\x57\x49\xa5\xe0\xe2\xb0\xa9\x31\x5c\x06\xe8\x4a\xcd\xde\xc9\x2f\x2b\x6e\x8a\x48\x7c\x24\x3a\xa9\xb4\x98\xde\xad\xa5\x8b\x1f\xf6\xf9\xa7\x8c\x3d\x24\xcb\x02\x81\x47\xd9\x78\x19\x07\xd9\xfa\xfa\xfa\x7a\x75\xc4\x21\x49\x41\xbb\x77\x12\x73\x88\x6b\x7f\x5b\xc3\xed\x27\x7e\x07\x2e\xdb\xf7\xb7\xff\xf7\xb7\xff\x7f\xed\xdb\x7f\x71\xf5\xcf\x60\x65\x4c\x28\x7f\x24\x8b\xdf\x2d\x46\x85\xcf\xb2\xa0\xda\x10\x60\x6d\x30\x80\x98\x57\x41\xc6\x48\x99\xed\x60\xcb\xdc\x1c\x22\x23\xb8\x30\x9a\x4c\x68\x46\x93\x82\xd0\xe4\x2c\x87\x42\xa3\x2c\x3d\xcf\x69\xb6\x86\x1c\x7a\x9e\x47\x49\x98\x9e\x83\xc6\x02\x45\x7a\x20\x0f\x1e\x88\x9c\xfe\x3f\xdf\xbc\x7e\x55\x14\x0b\xe1\x2b\x96\x73\x4d\x33\x8d\xec\xf9\x61\x81\xf5\x89\x40\x05\xd1\x34\x49\x19\x23\x88\xa3\x84\xb2\x9e\x24\x69\x48\xd7\x90\x77\x30\xa7\x46\x35\xf0\x8b\x79\xcc\x46\x26\x36\xb6\x76\xb7\x69\x23\xd7\x1c\x93\xff\x7c\xf5\x7e\xdb\xa8\x6e\x96\x6d\xb7\xbb\xa5\xa5\xa4\xe4\xc0\x5a\x78\x27\x91\xe9\x9a\x44\x80\xfc\xc4\x44\x7b\x70\x8f\xc9\x9d\x69\xb3\x5e\x2a\x03\x08\xa3\x3c\xde\xf2\x67\x69\x5e\xf4\x48\x11\xcd\x69\xba\x2c\x7a\xac\xc2\xac\x07\x4a\xe6\xf3\x34\x13\x8f\xd1\x60\x33\x61\x70\x64\x8f\xc0\x7f\x57\x57\xa4\x2d\x88\x3d\x4e\xc7\x41\xcc\x12\x87\x4f\xbf\x79\xfc\x0d\x04\x96\xe5\x7b\x0f\xaf\x90\xed\x84\xe2\xd7\xd5\x15\xd9\x54\xd9\xac\x19\xb2\x07\xad\xa9\x34\xd9\x28\xd9\x53\xed\xd7\x0a\x4f\x8b\x8c\x2e\x20\x52\x1b\x3d\xb7\xa6\xcc\x92\x9d\x04\xe0\x7b\x74\x96\x11\x92\xd3\xf3\x34\x8d\x69\x90\x5c\xc3\x1d\x2b\xdb\x9f\xa5\x04\xa3\xb1\x2c\xdc\x32\xa2\x03\x9f\xd9\x96\xe1\xfb\x07\x63\x1a\xc9\x5d\x66\x07\xcc\x8b\x40\x56\x3d\x47\x35\xbf\x41\xe1\x84\xc4\x78\x18\xdc\x00\xea\x6c\x42\xb4\x78\x05\x43\x7e\xf5\x7e\x5b\xc7\x75\xe5\x92\x16\xc2\x3c\x9a\x08\x06\x63\x38\xbf\xb3\x2a\x32\xc6\xc3\xab\x04\x79\x58\xd6\x9a\x2e\x68\xd2\x69\xbf\x3b\xfa\xf0\x51\x86\xa2\xe4\x84\xc3\x3b\xb7\xbb\x86\x3c\xe9\xc1\xdc\x3e\x78\x60\x4e\xaa\x71\xe8\x5b\x82\x41\x4d\xfb\x79\x90\x47\x63\xd2\x26\x1b\xd0\x85\xe7\x4b\xc6\x1e\x50\x15\x1b\xa4\x3d\x54\x57\x85\xaa\x9e\x7e\x91\x8a\x47\x6b\xed\x51\x90\xd3\x27\x8f\xdb\xd6\xf8\xb5\x1f\xe9\x57\x34\x08\x69\xd6\x69\xef\x03\x5f\x8d\x7e\x0d\xf8\x69\x0b\xda\xe7\x23\xac\x28\xc4\xe4\x63\x9a\x14\x8f\xd8\x41\xbb\xdd\x23\x6d\x26\xf9\x47\x63\xa8\x62\xf0\x4b\x2e\xd5\x8e\xea\xc6\x4a\x4c\x59\x0d\xb9\xf2\x88\x23\x97\xc9\x18\x1d\xaa\x6d\x4d\xb2\xef\xe2\x79\x81\xae\xaf\xfd\xb1\xa5\xab\x48\x2f\xb7\x63\x0d\x4a\x5d\x9a\x4d\x72\x92\x66\x4c\x5a\x15\xc1\x8a\x81\x1e\xb5\x76\x5f\x63\x2e\x09\x3b\xf0\x20\x82\x47\x91\x89\x26\x97\xaa\x7e\x81\x64\xa9\xc8\xc7\x6e\xa2\x7d\xd6\x00\x07\x69\x92\x50\xf1\x6c\x41\x52\x98\xa6\x44\xe3\x72\x51\xb6\x2e\x03\x36\x7c\xa4\x17\x85\xd3\x41\x01\x8b\x5e\x6b\x08\xeb\x78\xb3\x5b\x55\x5d\x7a\x2f\xea\xef\xf8\x1a\xc4\xab\xa4\x79\xec\x60\xa0\x81\xa0\x86\x08\xf6\x15\xc7\xa9\xa0\x04\x91\xf5\xa3\x13\xad\x83\x14\x59\x34\x9d\xd2\x8c\x87\x18\x62\xb3\x0f\x62\x8b\xf2\x17\xca\x70\x50\x47\x30\xd0\x03\x1f\xd5\x98\x91\x82\x9b\xd0\x0f\x18\xaf\xec\x1a\xdc\x24\x01\xdf\xce\x79\x11\x14\x74\x3c\x0b\x92\xa9\x5f\x81\xc0\xcd\xfb\x25\xe2\x83\xf0\x12\x0c\xeb\xe1\x46\xf8\x31\xe3\x30\x36\xcb\x5b\x37\x23\xfd\x36\xa0\x18\x0d\x28\x6f\x95\x50\x08\x29\xfb\x32\xab\x86\xa2\xe0\x4c\xe6\xbd\xb5\x52\x37\x56\x2b\xd2\x16\xc1\x57\x5b\xf6\xc5\x96\xd1\x32\x3b\x0b\x5e\x5b\x28\xd6\x1b\x81\x8b\x59\xb3\xb2\xbc\xaf\x97\xde\x47\x5e\xaa\x83\x37\x0f\xb1\x90\xef\x96\x03\xd8\x5d\xa8\x62\x02\x62\xa5\xe1\x75\xa5\x2f\xcb\xe3\x4b\x46\xef\xfc\x6d\x29\x2c\x2e\x46\xd5\x25\x6b\x2b\xca\x45\xfd\xd4\x64\xa6\x4a\x08\x90\x0a\x4e\x5b\x18\x60\xe7\x87\xa4\x5d\x90\x49\x10\xc5\x34\xec\x93\x23\x76\x4e\x3b\x8f\xd8\xd9\x23\x80\xa8\x60\xe5\xab\x09\xb5\xe9\x99\x0b\x8d\x4f\xa5\xcf\x50\xd1\x27\xa2\x70\x48\xbe\x53\x7f\x52\xdf\xc7\x76\x9f\x6c\x31\x1e\x92\xf6\x76\x7f\x53\x29\x0f\xa5\xfe\xb1\x9d\xd0\xe2\x53\x1c\xe5\x05\x4d\xa2\x64\xaa\xb2\x95\xf6\xf0\xd4\x30\xe8\x92\x0a\xae\x8c\x87\x38\x73\xc9\x57\x5a\x15\xb2\x41\xea\x49\x70\xd4\x05\x78\xe8\x52\x55\x60\x9c\xf6\x99\x98\xdb\x1a\x3e\x65\xbf\x0c\xf9\xb9\x35\xdc\xfa\x96\x9d\xfc\x77\xee\x4f\xfe\xf7\x27\xff\xbf\xf8\xc9\x5f\x1b\xfe\xc3\xa3\xc5\x3b\x32\xfa\x57\x86\x9c\xf8\x54\x39\x8a\xa6\xdc\x06\xb7\xff\x0b\x3f\xa1\xf3\x7b\x90\xf0\x35\x9d\x98\x1b\x82\x8a\xf5\x78\x89\x1e\xce\x19\x1b\x27\x87\xe0\xec\xe2\x7c\xc6\x7a\xdf\x31\x0d\xb4\xbe\xe7\x85\xc9\x43\xb2\xed\xbe\xbc\x03\x8b\x3f\x26\xc5\x9b\xef\x0f\x89\xff\x45\x9c\x60\xee\xef\xc4\xa9\x2e\x48\xc8\xe1\xf3\xfd\xb7\x62\x92\x43\xf2\xdd\xb7\x64\x9c\xce\x17\x4b\x11\x67\x65\x74\x49\xe6\xe9\x59\x94\x4c\x51\x34\xb1\xc7\x64\x3c\x0b\x32\xd8\x0b\xf8\xcd\x6c\xc8\x4d\xa9\xa4\xb9\xba\x84\x8e\x29\x7f\xb4\x50\xa4\xac\x41\x8e\xab\x9c\x74\xf6\xc9\x1e\xd9\xda\xec\x91\xe7\xec\xff\xad\x1e\xe9\xf7\xfb\x3d\xf2\x7f\x64\x8f\xec\x7c\xd3\x65\x87\x1d\x92\x2f\xe8\x38\x9a\x44\x7c\x21\x1d\x7e\x38\xda\xda\x79\xb2\xf5\xc4\x36\x31\x8b\xf2\x14\xd2\xc5\x38\x5c\xaf\xb2\xd7\xfc\x4d\x2c\xeb\x08\x1b\xa0\x79\xb5\x86\x6f\x96\x85\x24\x15\x4a\x30\xe1\xda\xc0\xac\xdf\x98\x50\x56\x31\x9e\x47\x36\xa2\xf6\x7e\xbb\xcf\xd0\x72\x90\x86\x74\xbf\xe8\x6c\x22\xad\x35\x1b\x5b\xfb\xff\x9c\x6c\xce\x00\xf9\xbb\x5d\x20\xd6\x22\x3d\x5e\x2c\x68\x76\x10\xe4\x5a\x95\x8d\xb2\xf3\xe5\x28\x2f\xb2\xce\xe3\xae\x7c\x91\x2b\x12\x36\x7b\x8f\xad\x1b\x33\x9e\xbb\x88\xa3\xa2\xd3\x6e\x77\xcd\xc7\xca\x49\xd7\xb4\xae\x1a\xa7\x21\x1b\x5c\xe2\xeb\xbc\x94\x0f\x01\xe6\x87\x3d\xb2\xcf\x04\x42\xf8\xf8\x7e\x8f\xfc\x5f\xd7\x89\x01\xe0\x99\x59\x31\xb1\x06\xa4\x72\x2a\x1b\x52\xf2\x88\xec\x93\x0d\xb2\xb5\x89\xec\x8c\x7c\x7e\xf1\x65\xec\x51\xdb\x86\xe9\xba\xdb\xff\x25\x8d\x12\x36\x4c\xdb\x52\x71\xbc\x04\x8f\xbb\x30\xc5\x6f\x8e\x5e\x30\xc2\xde\xda\x94\x4c\x49\x58\xf8\x01\xe5\x7b\x28\xee\xdb\xcd\x27\x8f\x6d\x82\x9b\xa7\xe1\x77\xdf\x6e\x6d\x96\x11\x9a\x49\x5f\xda\x8f\x31\xa7\x26\x51\xb8\x92\x8a\x32\x3a\x0f\xa2\x84\xeb\x8e\x58\x9e\xbe\x7b\x14\xce\x5d\x4c\xf6\x20\x80\xb5\xdd\xf2\x76\xd7\x72\x5b\x03\xcc\x4a\x82\x29\x8b\xd7\xef\x0c\x13\x39\xdd\x24\xc8\xda\x87\x49\xc1\x3d\xe2\xf4\xc8\xd6\x66\x97\xfc\xff\x19\xd6\x36\x9c\x5a\xb8\x53\x1c\x61\x7e\xee\x7b\x81\xab\xea\x52\x25\x75\x7d\xc6\x3c\xd5\xbf\x43\xe2\x26\xe8\xb0\x0e\x84\xc1\x3f\x5c\xa8\x43\x82\x78\xeb\x20\xd8\xa7\x9c\x2f\xff\xe4\x0c\xb0\xb7\x6b\xff\x24\x08\x4b\x68\xbd\xe4\xdc\xae\x3a\x51\x66\xeb\xfa\x49\x21\x08\xd1\x72\x2e\x5f\xe7\x58\x44\xc5\x60\xf6\x55\x8e\xd3\xf7\x00\x65\x49\x31\x9a\x0d\xe1\x5a\xb1\x35\xac\x15\x63\x39\x7d\x54\x63\x9d\xcf\x80\x20\x7f\x2e\x7d\x06\xa0\x97\x0a\x22\x1a\x28\xd9\x7a\x82\x58\xd8\x28\xc8\xe9\xce\x13\xb2\x07\x65\xb4\x7a\x68\xe7\x89\x61\x02\x10\x86\x94\x6b\x16\x61\x0f\xec\xf0\x42\x3d\xb2\xf5\x8d\x29\x09\xab\x7e\x3e\x1f\x05\x49\x87\x17\x33\x99\x9f\xb5\x98\x85\x5b\x12\xb4\x70\x9f\xb3\xa1\x17\xa9\xb1\x7b\xb1\xe9\x23\xe0\x5a\x35\xbb\x94\x2b\x9a\x2b\x93\xc0\x5e\xf7\x1d\x8f\x05\x91\xa4\x85\x10\xca\xbe\x8f\x7e\x68\x4d\x41\x22\xe1\xee\x6e\x26\x1a\xa9\xf9\x2c\xe0\xd2\x1a\xec\x6f\x17\xe3\x78\x99\x47\x67\x2a\x74\x65\x34\x8a\xe2\xa8\x50\x02\xce\x28\x48\x3e\x0f\x46\x59\x90\x8c\x67\x24\xa7\xd9\x59\x34\x96\x1b\x60\xc0\x3d\xbd\xb6\xbe\x1f\x44\x3f\xf4\x6d\x1a\x52\x61\x24\x72\xb9\x0b\x4d\x68\xc6\xb6\xa1\x20\x9e\xa6\x59\x54\xcc\xe6\x24\xa4\xf9\x38\x8b\x46\x9c\x2d\x09\xf9\x87\x26\xfd\xf3\xe8\x73\xb4\xa0\x61\x14\x80\x10\xc4\xbe\x06\x87\x49\x41\xb3\x24\xe0\x4f\x27\x3e\x3d\x0f\x92\xcf\x9f\x84\x9b\xd9\x4f\x7c\x5e\xff\x7f\x3f\x89\x91\x26\xd3\x4f\x6c\x88\x9f\xe0\x2d\xd1\xa7\x30\x9a\x46\xce\x53\x0e\x39\x35\x3e\x8a\x1c\xc9\x3d\x55\xce\x80\xf4\x19\x53\xa4\x9e\x6d\xb6\x01\xad\x3e\xb7\x57\xe4\xc8\x62\x8b\x62\x46\x0f\xf8\x3e\xd5\xfe\xe7\xcb\xf6\xee\x9a\x97\x67\x0a\x1e\xdb\xb1\x76\xee\x0e\xae\x60\x83\xb4\x37\x41\x54\x82\x56\xb0\xb9\x0b\x43\xc7\x0b\x86\x0d\xb2\x47\x3a\x5c\x9c\xea\x7c\xf7\x94\x3c\xd2\x4d\x74\xe5\xb3\x81\x47\xdb\xd6\x7e\xab\xbc\x6e\x98\x4d\xa1\x3a\x45\x83\x35\x6a\x2b\xc1\x44\x10\xae\x80\xb0\x79\x00\xf1\x28\xc9\x8b\xa8\x58\x16\xd2\x59\x72\x14\xd2\xa4\x60\x9b\x96\xed\x78\x9f\xd7\x72\x98\x84\x51\x46\x4d\x03\x06\xf3\x8d\x4d\xde\x93\xb2\xac\x7a\x64\x03\xaf\xa6\x5a\xa8\xa5\x16\x34\xd5\xd2\x6d\xb5\x56\xe1\x45\x66\x4f\xbc\x0e\x94\xcd\x23\xb0\xc9\x19\xda\x2f\x3f\xbe\x62\xf3\x20\x5f\xb7\x60\x0c\xa0\x54\xd5\xb7\xae\xc5\xaf\xd3\x2a\x7e\x2d\x9f\xd2\x71\xe4\x8a\xe8\xdc\x51\xce\x5f\xca\x61\x3e\xee\xc8\x9d\xe0\x41\xa5\x54\xde\x54\x7b\x91\x47\xf1\x21\x15\x1e\xfc\x39\x1d\x6f\x49\x09\x9d\x87\xc8\x3f\x4b\xa5\x9c\x10\x61\x3f\x2f\x11\x27\x2b\x2c\xfc\x69\x27\x2f\xb5\xba\x72\x85\x05\xe8\x7a\xe9\xeb\x41\x3c\x66\x1d\x95\xc1\x3b\xaa\x1e\x49\x3d\x5a\x1b\x18\x1b\xd6\xd6\xb8\xa3\xb4\x28\x61\xf0\x9f\x7f\xbe\x3c\xd9\x7c\xf4\xdd\xe9\x97\xed\xeb\xce\xcb\x8f\xaf\xd8\xef\xfd\x47\xff\x77\xfa\x65\x6b\xe7\xfa\x4a\x7d\xec\x6c\xf6\x76\xb6\xae\xbb\xff\x33\xe8\x17\xa0\x04\x55\x1b\xb8\xf1\x2e\xaf\x8c\x31\x20\x70\xfe\x3c\x6f\x6b\x45\x84\x89\x27\x98\x70\xfa\xf7\xa2\xed\x85\x5e\x82\x77\x83\xb7\x17\xee\x4a\xb2\x10\xa7\x07\x85\x1f\xf7\xec\x3c\x86\x38\xff\xfe\xbc\x6f\x6e\x38\xec\x09\x89\x92\x92\x81\x1b\xdc\xe7\x6e\x86\xee\x65\x23\x8d\x06\xbf\xbd\xd9\xc8\x6a\x93\x8b\x94\x6c\xa4\xf9\x72\xce\x00\x8f\x73\x71\x7c\x98\xa7\xe1\xa3\xef\xbe\x7d\xb4\xb5\xa9\xb2\xe1\x8c\x0b\xbd\x1b\xa7\x31\xe9\x1c\x7e\x38\x1a\x1c\xbe\x3c\x20\xec\xdc\x30\xdc\xde\xdc\xdc\xe9\xda\x3c\x19\x55\xeb\x9e\x42\x51\xae\x33\x70\x99\xd7\x70\xd8\xe2\x4c\xb8\xdd\x23\xdb\xcd\x6c\x55\x31\x53\x35\xb6\x14\x42\xa7\x7d\xf2\xcf\xf7\x2f\x7f\x72\x1c\x09\xaa\x02\xfe\xd1\x94\xd6\xe8\x4e\x2a\x82\xac\x1b\x9e\x26\x80\x0e\x78\x99\x73\x86\xfc\x6d\x8f\x3c\xee\x92\x21\x69\xb7\x1b\x8d\x7b\x1c\x47\xf0\x90\x4c\x75\x10\x94\x4f\x51\x62\x8f\x8f\x61\xe1\xa7\xfd\x7f\x1c\xfd\xf8\xaf\xa3\xf7\xff\x6b\xcf\x2a\xd4\x51\x32\xa7\x76\xfd\xde\xc9\xe5\x40\xb7\x1e\xfb\xd6\xd6\xea\x23\x17\xab\xc9\x7f\x2e\x71\x0f\x1e\xee\xd0\x9c\x0a\x9c\xe1\x05\x9e\x73\x08\xbe\x77\x12\x83\xf3\x39\x20\x33\x0e\x1d\xee\x80\x1f\xa3\x43\x6c\xe9\x51\x46\x9e\x3f\xd4\x29\xc5\x38\xa1\xf2\x33\x8a\x79\x9e\xd9\x7a\xd2\xed\x91\xed\x4d\xe5\xe2\xcc\x90\xf2\x24\x7a\xad\x41\xca\xc2\xcd\x16\x68\x89\x57\xaa\x43\xc8\xe2\x4a\x7d\xac\x57\x6c\x0d\xcd\xcf\xeb\xd3\xde\xce\xe3\x7b\x35\xfe\xbd\x1a\xff\x2f\xae\xc6\x17\x2a\xfc\xc5\xb8\xda\x7e\xef\x16\x16\x77\x2d\x1d\xa2\xb0\xb5\xbb\x52\x68\xb5\x1a\x3b\x3d\xae\x67\x5a\x8c\xbd\x96\x60\x8b\xa0\x98\xf5\x48\x42\x0d\xeb\xef\x4f\xa0\xb9\x70\x1e\x9e\xca\xab\x6a\x1c\xdc\x59\x7a\x2d\x10\xf6\x3a\x60\xe3\xc3\xfe\xe3\xa9\x3a\x6b\xac\x6e\x78\x81\x2b\x16\x32\xa1\xf3\x85\x41\x0f\x75\x79\xe5\x8a\xd2\x2a\xd6\x4f\x93\x4e\x1b\x46\xd5\xc6\xc1\x50\xbb\x86\xfd\x74\x9e\x32\x26\xc6\xdf\x12\x1e\xbe\x3b\x20\xfa\x5e\x99\xbf\x30\x6c\xf7\x08\x45\xac\xf7\x13\x67\x83\xe2\xc2\xbb\x63\x3b\xc3\xf4\xf6\x20\x09\x71\xfb\xa8\xf9\xd2\xca\xc8\x9a\x7a\x63\xf0\xfa\xf0\xc3\xc7\x97\x6f\x61\x05\x1d\x1c\xbd\x7d\xfb\xf2\xe0\xe3\xe1\xd1\x5b\xf2\xfe\xe5\x87\x77\x47\x6f\x3f\xbc\xfc\x50\xda\x6a\x18\x14\x01\x6e\x96\x7d\xe3\xcd\x69\xf0\x50\x98\x11\xce\x83\x8b\x71\x3a\x5f\xc4\xf4\x22\x2a\x2e\x87\xe4\x09\x50\x96\xd5\x43\xd0\x85\x2a\x3b\x04\x56\x95\xde\x6f\xba\x9e\xc8\x35\xc2\xe6\xe0\x8b\x19\xc8\x1a\x0e\x7e\xa1\x6d\x3b\x21\xba\xc3\x03\x7c\x03\x7f\x09\xc9\xf9\x2c\x1a\xcf\xc8\x3c\x28\xc6\x33\x21\xbe\xf2\x4d\x88\x31\xb4\xd0\x28\xe7\x89\x5a\x00\x4d\xfb\x23\x5d\xc3\x75\x94\xd3\x5b\xb0\x40\xf0\xc7\xa1\x8d\x26\x9d\x4f\x7e\x42\x3e\x81\xb7\x71\x51\x78\xea\xba\x43\x57\x85\xd9\x58\x05\xd8\xae\x03\x65\xc7\x0c\x2f\x8d\xa5\x0b\xd5\x88\xbe\xdb\x15\x5d\x3b\x58\x9c\x44\x19\x35\x3c\x02\xd8\xe8\x2a\x1b\x0f\x1b\x8a\xa7\xf5\x0a\x70\x1d\xd8\x17\x9b\xb6\xe8\xbf\x90\xc6\xb4\xa0\x55\x35\xd8\x83\xb1\x71\x83\x5f\x61\xff\xcc\x76\x2d\x20\x44\x41\x10\xbc\x3e\x50\xee\x70\x5b\xa9\x84\x3b\xcb\x21\x29\x77\xb5\x1c\x15\xfd\xb5\x35\x29\x0c\x9a\x24\xbc\x66\xab\x3d\xe0\x45\x26\x13\xfe\x34\xcf\x43\xe2\x91\x59\x18\x7b\x56\xc5\xab\xca\x66\x83\x3d\x4b\x5e\xfb\x07\xf7\x6c\xae\x1d\xf4\xca\x25\xfe\xe2\xe5\xa3\x83\x57\xc7\x6f\xff\xf7\xe5\x7b\x55\x4f\x48\xc7\xb3\x65\xf2\x99\x86\xe2\x55\x09\x7f\x31\x2a\xfe\xfa\x19\x5d\xc4\xc1\x98\x76\x06\xff\xbe\x3e\xf9\x77\xf2\xef\xec\xf4\xd9\xbf\xbf\x0c\xa6\xbd\xf6\xf5\xd5\xa3\x47\x57\x5f\xda\x5d\x70\x2d\xfc\xc5\x0b\xff\xef\x53\x59\xe2\x44\x94\x39\x65\x85\x4e\x64\xa9\xd3\x13\x7f\x39\xbb\x94\x51\xa8\xa4\x8c\x6e\x0b\xb5\xa4\x1a\x42\x65\xc4\x35\x1f\xcb\x6e\x4b\x4e\x6a\x60\xc0\x5d\xb3\x80\x78\xc4\x5f\x06\x03\xb8\x03\xa5\xc2\x1d\x06\x78\xda\x80\x0a\xd6\x1c\xd2\x67\x79\x07\x2c\xcb\x5c\xb9\xc2\xef\x8c\x05\x43\x36\x08\x7f\xff\x6a\x88\xea\xea\xce\xda\xe2\x64\xae\x53\x03\x9f\x2d\x18\xf4\x1d\x95\x12\xd6\x34\xdc\x98\x66\xcd\x5d\x7c\xba\x33\x7b\x76\x67\xc4\xd0\xc1\xe7\xae\xb2\xa0\x06\xd7\x77\xc9\x98\xc6\xe0\x50\x5f\x3e\xe2\x34\xca\x8c\x63\x1a\x64\xd2\x84\xcb\x6a\x45\x24\x5b\x0b\xda\x0f\x04\xbe\x1a\x0a\x59\x91\x6f\x8f\x33\xcb\xdb\x7b\x1d\xfe\xab\xb4\xab\x14\x38\xc3\xf0\xd7\x3d\xb2\xb5\xb9\xb9\x49\x1e\xf2\xcb\x19\xcf\x5d\xab\xd7\xf1\x03\xbc\xdb\x03\xec\x48\x7c\x31\x0e\x92\x53\x41\x2f\x3c\x16\x8b\x78\xd7\xb7\x3a\xaa\xdc\x19\xb3\x48\x04\xc2\xfd\x08\xcb\x4a\xa7\xc3\x9c\x45\xf0\x80\xf0\xa6\xdd\x9e\xa5\xad\xc7\xe0\xc2\xf9\x0f\xe3\x91\x3f\x89\x2d\x34\x08\xc3\x1c\x47\x0a\x17\x56\x0e\xae\x34\xc6\xd5\xc3\xbd\x35\xbe\xe1\xca\x83\x81\x38\x6b\x47\xdc\x5d\xbd\xe0\x7a\xb0\x1b\xcb\x5b\x21\x95\x7a\x18\xf2\x52\x41\x96\x45\x67\x14\x33\xdc\x20\x54\xb3\x27\xdb\xab\xe0\xb0\x1e\x68\xc3\x5b\xbd\xdf\xa6\x14\xc9\x14\xf2\xb5\x7a\x14\x92\xab\x2b\xf9\x75\xb2\x79\xaa\xb6\x4c\xb8\xc2\xe6\x7d\xd3\xd0\x22\xc1\x2c\xc1\x13\xb1\x44\xe7\xdd\xbc\xc8\x9e\xea\x4d\x95\xc4\xcb\x40\xfb\xaa\x61\x59\xb7\xdc\xd5\xe4\x3a\xc2\x2b\x95\x9c\xcf\xa8\xf4\x3b\x10\x72\xb1\x1c\x4e\x5f\xa0\x71\x67\xfb\x7b\x88\xd0\x2c\x88\xb8\x02\xb5\xae\x7d\xa7\x3a\xda\x4f\xd2\xac\xc3\xf0\xf2\x99\x5e\xf2\x93\xa2\x6f\x00\xa6\x13\x98\x8e\x1f\xa8\x3f\x0b\xf2\xa3\xf3\xe4\x1d\x04\x5a\x2a\x2e\x21\x76\xa1\xc5\x05\x4a\xd0\xf3\x99\x5e\x9e\x96\xdb\x76\xb6\xd3\x84\x1c\xbe\x3b\x68\xdb\x41\xfc\x85\x6c\x51\x51\xa7\x63\x66\xa1\x97\xc9\x01\xf6\x41\x28\xdc\x8d\x13\x74\xdc\x88\x72\x92\x17\x11\x0f\x46\x12\x85\x88\xa8\xb1\x59\x68\x29\xc2\xfd\x76\x9c\x9d\xf2\xd3\x92\x94\x03\xd8\xee\x91\x51\xd1\x8f\x1e\xa7\x02\xb3\x57\xd3\x34\xa1\x42\xf3\xd4\x59\xff\x64\x8b\xfd\xe7\x59\x54\x80\xbf\x14\x8b\x1b\x21\x10\xeb\x08\xf5\xc9\x3d\x43\x49\x17\x83\xeb\x65\xb5\x0b\x05\x92\x77\xe8\x55\x2f\x08\xd6\x30\xfd\x58\xf5\xd2\x0f\xe8\xe9\x0a\x31\x36\xd9\x5d\x83\x73\xaf\x80\x22\x89\xa6\x7a\x2c\x11\xcf\x11\xaa\xf6\xac\x29\x7b\x19\xa2\x67\xbf\xbe\x51\x55\x58\x3c\xdf\x4c\x6c\x50\x54\x8d\xa5\x06\x73\x28\xb5\xfb\x28\xb1\xfe\x7c\xfb\xa4\x65\x76\x27\xb4\x89\xd6\x19\xc5\x71\xc7\xf3\xaf\x74\x09\x56\xd6\xfa\xb5\x59\xab\xbd\x61\xb3\xdb\x8d\x76\x8b\xe4\xd8\x30\xbb\x8f\xed\xb4\x35\x1f\x84\x17\x5b\x69\x41\xf2\xe5\x62\x91\x66\x05\xe8\xd6\xf8\x4d\xed\xbb\x03\xa2\xb4\x2a\x6d\xc3\x11\x64\x39\x61\x36\x7e\xa9\x70\x93\xc5\x58\x4f\x65\x2b\x51\x98\xf7\x58\x0f\x34\x55\x69\x41\x8f\x1c\xea\xda\xbb\x69\xa9\xb7\x1b\x57\x8f\xab\x31\xe8\x38\x69\x2f\x79\xa5\x7d\x7d\xda\xdb\xf9\xe6\x5e\xa5\x7b\xaf\xd2\xfd\xaf\x50\xe9\x8a\x87\x15\xb7\x7a\x8e\xbd\x1f\x64\x69\x42\xfe\x77\x39\x0f\xce\xa2\x9c\x7c\x1f\xb0\xcf\xbf\x7d\xe6\x9f\xfd\x39\xf5\xaa\x7b\x07\x03\x72\x98\x44\x45\x14\xc4\xd1\xaf\x94\xfc\x9d\xf7\x82\x11\x6a\x40\x72\xb0\xc4\x92\x06\x37\x30\x50\xb6\x54\x0d\x27\xe7\x7d\xd0\xea\xca\x62\x32\x8a\x88\x08\x40\x75\x18\x0e\xc9\x66\xdd\xcd\x1b\xb7\xf6\x60\xc3\xb7\xdd\xea\x7a\xcd\x4c\xbc\xee\x74\xf5\x2b\x34\x19\xeb\x6a\x22\x11\x0a\x2d\x69\x83\x1e\x8f\x13\x5e\xfe\x3a\xa5\x87\x54\x3d\x13\x59\x8d\xcc\x92\xbe\x77\xbd\x6e\x88\xd0\x08\x58\x7b\x4e\xef\x07\x6b\x02\x3d\x25\xae\x78\x79\x5b\x3d\xd1\x98\xe1\x34\x95\x67\x75\xcb\x54\xcb\xb2\x49\xc7\x98\x47\x99\xed\xae\xb7\x51\xd4\xa9\x20\x3c\x63\x67\x54\x39\x3b\xe4\xf0\x05\xe4\xc8\xde\xa9\x49\xdb\xd8\x28\xf3\x33\xe4\x7f\xfd\xc3\xdf\x0a\x39\xd5\xe8\x6c\xf9\x3c\x48\x8c\x54\xa5\xcb\x77\x41\xfc\x7f\x76\x60\x92\x2f\x84\x9a\x1b\x5e\x48\x1c\xa8\xc3\xa3\x34\x20\xf2\x9b\xea\x28\x65\x5d\x5d\xe4\x33\xcf\xcb\x6c\xab\x01\xbf\x79\x86\x44\x83\xd5\x9e\x15\x10\x99\x27\x5a\x97\xa1\xdc\xa7\x0f\xd2\x39\x0b\xa0\x67\xaa\xed\x3e\x3d\xa3\xd9\x65\x47\x7a\x43\xfe\x10\x25\xd3\x98\xbe\xe1\x08\xef\x92\x21\xf1\x66\xe8\x9a\xc4\xb4\xaa\x8e\xf8\xc1\xc5\x04\xaa\x83\x96\x12\xde\x25\xdd\x20\x0b\x22\x99\xc6\x29\xd2\xb0\x2d\x12\x19\x72\x7e\xf6\xf6\xf6\x38\xd5\x60\x20\xe1\x76\x41\xc2\xb2\x33\x37\x03\xe3\xd7\xba\x6d\x5f\x75\x42\x86\xb5\x7c\x4a\x0e\x06\x3c\x34\x9f\x4a\x12\x5e\xd9\x31\x73\x91\xeb\xb1\x91\x3f\x79\xce\x88\x46\xf0\x1e\xad\x86\x1d\x3d\x67\x40\xe5\x2e\xbe\x45\xc7\x2d\xfe\xc2\xeb\xca\x39\x53\x15\x55\x49\x01\x27\xec\x82\xf2\x48\x2c\x8a\x8e\xe4\x3d\x5d\x32\x89\x68\x1c\x5a\xa6\x07\xa2\x15\xa3\xa7\x16\xcf\xc1\x1d\xb4\x18\x0f\xef\x9a\x45\x86\x32\xd9\x8a\xfa\x20\xc9\xc2\x75\x84\xe5\xb0\x37\x09\xdb\x97\xac\x4d\x7e\x0b\x16\x67\xea\xe1\x1d\x59\x51\xd4\x27\xe4\x44\x26\x06\x3e\xb9\x17\x03\xef\xc5\xc0\xbf\xb6\x18\xa8\xdf\xe7\xf1\x45\x73\x57\x2f\xf4\xee\xe6\xee\x9e\x81\xbc\x91\xea\xc6\x52\x63\x65\x38\x27\x8a\x48\x2d\xd2\x0a\x99\x7d\xa2\x53\xa4\x70\xb9\x26\x73\xd9\xa7\x71\x71\x0f\x3c\x4f\xe7\x6b\xc9\x60\x13\x81\x81\x4f\x7e\x1c\x94\x50\x1b\x42\xe3\x0c\x54\x82\x7b\x7a\xf6\x15\xb1\x72\x0c\xa5\x2b\x68\x0c\xde\x04\x49\x30\xa5\xfa\x75\x3e\x63\x59\x1c\x15\x86\x2a\x40\xba\xf0\xd0\xe0\x68\xbf\x9f\x1b\x18\x72\x2a\xce\xe6\x35\xf6\xef\x21\x65\x1c\x26\x4a\x4c\xff\x9e\x96\xf8\x37\x0a\x72\xee\x73\xa1\x2c\x12\xc5\x94\x82\x97\x4a\xcf\x26\x65\x7a\x9a\xb7\x1d\x8b\xca\x36\xcd\xf6\x80\xc4\x1c\x44\x88\x36\x4a\x63\x4d\x18\xee\x44\x51\xf8\x1c\x45\x1c\xca\x8e\x4f\xfa\x32\xcc\x99\x60\xa3\x52\xea\xdc\x1c\x73\x67\x9c\xfa\x92\x42\x84\xe6\x10\xdb\xae\x1a\x67\x9f\xbc\x61\xac\x3c\xa2\xb9\x08\x22\x0d\xf8\x70\xbc\x50\x1a\x9e\x3d\x1b\xe3\x4d\x0e\xea\xea\xed\x32\x8e\xb5\x63\x8c\x1e\x93\x22\xe9\x45\x04\xd7\x66\x3e\xdc\xfd\x31\xe3\x0f\xdd\x59\xd8\x1d\xb2\xf6\xb5\xe2\xee\x38\x98\x6c\x14\x6d\xc7\x0e\x70\xa2\x42\xc9\x98\x07\x31\x52\x13\x3e\xe6\xfd\xbb\x03\x11\x61\xa2\x3a\x76\x8c\x46\x9b\x70\xf5\xca\x09\x0f\x90\xae\x4e\x9c\x36\x9a\x38\xe8\x21\x83\x74\xb1\x64\x10\x9d\x4a\xf2\xa0\x03\xd5\x52\x89\x8d\x75\x0f\x77\x2d\xa1\x20\xdf\xe3\x46\x4f\x69\x4b\x86\x54\x4e\x17\x7b\x04\x82\x64\x57\x85\x90\x22\xcf\xf4\x6f\x4e\xdd\x50\xe4\x94\xb1\x03\xf4\x59\xe3\x59\xdf\xc1\x3a\xe7\xf7\x2a\x7a\x2d\xc6\xbc\x8b\x78\xee\x80\xb7\xfa\xac\x68\xba\x23\x2e\xc1\xbd\x27\x46\x8a\x19\x2c\x17\xa3\xd0\xde\xac\xc0\xd9\x0c\x1c\x7b\x9e\x79\x01\x54\x55\xde\xd8\x24\x02\x17\xbe\x90\x45\xf2\xfd\x94\xa4\xc3\x15\x22\x17\x05\x72\xdd\x36\x42\x42\xb3\x18\x44\xd8\x1d\xab\xd8\x47\x6c\x2f\xc9\x2b\x3b\x5f\x16\xf2\x04\x00\xa3\x65\x80\x01\x21\xcf\x08\x30\xa4\x8e\x29\x7e\x2d\x88\x54\x67\x80\x66\xa9\x44\x99\x51\xe5\x56\x19\xab\x38\x1c\x54\x49\x17\xb9\x1c\x9f\xa6\xb4\x35\xfa\x05\xa3\x8b\x65\xc8\xa1\x8d\x96\x51\x1c\x02\xc2\xc4\xa0\x58\xa6\xe3\xdf\x16\x18\xfe\xc7\xa3\x17\x47\xeb\xeb\xeb\x20\xde\xb7\x73\xb2\x9c\xc6\x97\x7d\x11\x45\x8c\x1d\x08\x96\x39\xdb\x13\x0b\xd5\x4a\x82\x5c\xca\xb2\xdf\xd2\xae\x46\xdd\x90\x30\xc6\x01\x19\xea\xbd\xf5\x96\x11\xe9\x69\xf4\xcb\x09\xcb\x3e\xd9\x3c\x3d\x65\x62\x17\xfe\xbc\xba\x52\x76\x9b\x36\x28\xff\xb1\x05\x65\xd8\x58\x76\xfd\x57\x45\x56\xed\x00\x49\x10\x17\x76\xd0\xab\x10\x55\x76\x8b\xaa\x2e\xd5\xb5\xd1\x29\x0f\x81\x92\xf8\x9f\x65\x11\xc7\xcf\xb7\x90\xdf\xf5\x69\x78\x15\x3f\xd0\xc4\x8a\x60\xe1\x0b\x55\x60\x9c\xd5\xa1\x2d\x53\xa2\xd4\x17\x53\xfa\x7e\xc6\x88\xc5\xa2\xcc\xeb\x3c\xa6\x79\x76\xc3\x1c\x5e\xb4\x83\x99\x99\x32\x8a\xb4\x0c\x68\xbc\xe1\x54\xcc\xee\x1a\xd5\x94\x0f\xc1\xbe\x86\x12\xa4\xc2\xb2\x9a\x7a\x7a\x96\x61\xae\x68\x52\xef\xce\x51\x72\xc8\x65\x46\xe1\x86\xf4\xfd\xbb\x03\xe5\x81\x89\x9b\xb2\x8c\x83\x44\x09\x9b\x51\x22\x94\x2e\x7e\x5f\x4f\x99\xeb\xeb\xb1\xdf\xef\x5f\xe3\xf8\x6e\xb6\x2f\x3d\xad\xc9\x94\x45\x3d\x9c\xb4\xce\xa7\x7d\xa9\xbb\xf9\x55\x88\x50\xd2\x80\xe9\x93\x1e\xcf\x5a\x19\xa2\x45\xc9\x12\xc5\xce\x1b\x69\x03\xd3\xf4\xfa\xef\xdb\x7b\xbd\xcf\xbd\xde\xe7\xaf\xad\xf7\x11\x4a\x9f\x70\x74\x8b\x9b\x3f\x9f\xde\x47\x69\x6b\xb0\xe2\x87\x33\x27\xa5\xd1\x79\xf1\xdc\xe0\x23\x6c\x18\xa6\xcb\x0f\x47\x53\x01\x23\xb5\x92\x77\x2b\x02\x85\xad\x69\x79\x29\xef\x78\x6c\xfa\xc5\x05\x17\xf9\x42\x2c\xe9\xca\x92\x83\x3a\xac\x66\xb4\xb3\x08\x20\x47\xed\xd2\xf1\x75\xd0\xd2\x37\xeb\x5d\xbe\x3c\x60\xd1\x62\x59\xa8\xc7\x6b\x09\x3d\x17\xd8\xec\xe8\xed\x92\x09\x1d\x43\xd2\x56\x70\x56\x1c\x8d\x21\x69\x87\xa3\x4f\xbe\x5c\x29\x26\xee\xa8\x3e\xa9\x46\xa7\xb4\x59\xa3\x0a\xce\xdb\xa8\x2f\x57\x36\xba\xed\x36\xba\x58\x16\xaf\xe8\x45\xfd\x30\x5f\xd1\x8b\xb2\x31\x9a\x59\xd5\x03\xac\x6f\x8b\x03\x95\x0d\xcd\xdf\x96\x35\x2e\xb1\x19\x9d\x68\x38\x39\x11\x3d\x8d\xe4\x9e\x18\x7a\x4f\x74\x0b\x80\x4f\x4b\x76\xae\x17\xcf\xf5\xae\xc5\x69\xa7\x35\xdc\x81\x2d\xea\xe9\xfd\x16\x75\xbf\x45\xfd\xb5\xb7\x28\x7d\x35\x41\x8b\xd9\x8d\xee\x25\x04\xf0\xdd\xbe\x4a\x2c\x89\xfe\xef\x0b\xff\xef\xbb\x04\xf1\xdf\x83\xd4\x6c\x9b\x0c\x44\x9a\x23\x5b\x40\x0b\x91\x2c\xc1\xc6\x65\xed\x8d\xd3\x64\x12\x4d\x25\x18\x0a\x85\x83\xa1\x65\x64\x15\x09\x76\x2e\x9e\xad\x19\x17\x34\x22\x51\xc2\xfc\xc8\x43\x81\x5b\xc8\x80\x44\x09\x72\x98\x7f\xb8\x4c\xc6\x7c\x8b\xc1\x50\x39\x4f\x95\x60\x8c\x15\x67\xd4\x06\x12\xa9\xaa\x2e\xee\xa0\x08\x43\x44\xa3\x20\x91\xd9\xdc\xeb\xa1\xd3\x1f\x99\xac\x84\x10\xf0\x99\xd6\xe4\xce\x40\xe9\xbc\xc5\x1b\x41\x50\x02\x6e\x9e\x76\xc9\x83\x07\x44\xfc\xee\x83\x4e\xf0\x68\xd2\x69\x6f\x5e\xb4\xb9\xeb\x92\xcd\x2e\x79\x46\x5a\xb4\x98\xb1\xdd\x03\x02\x93\x3e\xbf\x7c\x15\xe4\xb3\x16\x19\xda\xc9\x5c\xa3\xdb\xd2\x52\x02\x8a\xff\xf4\x63\x96\xce\x9f\xff\x06\x3d\x6d\x8b\x2e\xa1\xb0\x42\xcf\x2f\xa1\x61\xd6\xe9\xfd\x24\x3c\x64\xe5\x54\x34\x2f\x2f\x24\x1f\x87\x82\xd5\xe3\x59\x26\xe3\x98\xfe\x46\x03\x38\x66\x6d\xd5\x74\x1d\xc3\x94\x76\x5a\xce\x0f\x1a\xe7\x41\xba\x4c\x1a\x5d\x33\xdd\xc1\x38\xbc\x6d\x73\x12\xc2\x43\x29\x01\xe3\xa3\x72\xa6\xe0\x37\xec\xff\xb1\x6a\x10\x4d\x86\x33\x09\x18\xc0\xe8\xb3\xea\xde\xcb\x62\x76\xd7\x07\x84\xc6\x87\x83\x3b\x3a\x1b\x40\x00\xe0\xf2\xb3\x01\x57\x7d\x70\x2e\x1e\x51\x6f\x8f\x16\xb8\x33\x8b\x9a\x7e\x2c\x6e\xd0\x05\x74\xc7\xcd\xb9\x2b\xf7\x7f\x41\xb0\x87\xee\xc3\xe7\xfb\x6f\xad\x60\x64\x82\xa7\x72\xad\x0c\x7f\x40\x2b\x74\x33\xd7\x6b\x6b\xbc\x77\x7d\x6e\x19\xa5\xde\xd2\xbc\x2c\x66\x5a\x1b\xd4\x23\x6d\x1c\xba\xb9\xdd\x13\xc3\x9c\xd2\x62\x58\xa2\xf3\x94\xbe\x4a\xfb\xb8\xa0\x18\x49\x4f\xe8\xe9\x8c\xc2\x67\x41\x6c\x44\x19\xeb\x5b\x81\xb3\xcf\x82\xd8\x71\x46\xa2\xd2\xae\xd7\x00\x3d\x2b\x0d\x45\xf8\xf9\xbb\xc9\x60\x44\xd1\x9b\x0c\x47\x14\x6d\x38\xa0\x26\x67\x51\xc6\x5d\x82\x18\x2c\x37\x6b\x4f\x4e\x02\xd0\x3d\x3d\x49\x36\xe5\xe4\xab\x23\x14\xb2\xe6\x34\xae\xf0\x86\xe4\x44\x0b\x54\xfc\x7a\x4f\xb8\xd1\xfc\x51\xdf\xe6\xd9\x10\x38\xf2\x39\xe7\x27\x0a\x18\x85\x8e\xb4\xee\xb1\x86\xb8\x1a\x9e\xa7\x7c\xd6\x28\xa0\x92\x63\x73\x9a\x05\x53\xba\x5f\x34\x39\x39\x0b\xd0\x52\x1c\xf9\x20\xd4\xa1\xb6\x02\x4b\x7c\xdd\x71\x8e\x5d\xa4\x70\xb2\x5c\x05\x2d\xde\x81\x09\xe7\x8e\x35\x63\x62\x50\xa5\xc3\xb1\x32\x7f\xfb\xf9\xf6\x0e\x4c\xae\xfa\x3a\x7a\xe6\xec\xc8\x1a\x9a\x12\x19\x6f\x37\x2c\x5f\x6f\x7b\xce\x12\xd7\xf6\xaf\x6c\xf1\x92\xeb\xd5\xe8\x97\x35\x51\x4d\xbb\xb0\xff\xd6\x63\x02\xc0\x1c\x4c\x28\x89\xee\x6b\x60\x02\x91\xf2\x2d\x06\xdd\x5b\x2b\xa1\xec\xf9\x22\x8a\xf9\xe1\xad\x96\xbc\x05\x68\x05\x8d\xbb\x10\x12\x0f\x9b\xe5\xf4\x67\xcb\x6b\x0d\xe9\xd1\x2e\xe6\x74\xab\x4a\x64\x75\x3b\xb8\x75\xcb\x89\xaa\x9a\x1b\x39\x85\x2f\xe8\x38\x9a\x07\x71\x39\x2a\xb4\x14\xd8\x10\x09\xba\x40\x09\x51\xfe\x71\x07\x6c\x0a\x4f\x35\x83\x2d\x8f\x95\x5c\x72\x04\x03\xf9\xba\x72\xd0\xf5\x2b\x08\x55\x58\xcd\x3c\x3e\x7a\x4e\xa8\x2b\x8d\x49\x95\x72\x06\x57\x76\xf8\xfd\x23\x71\x9a\x9b\xe0\xe9\x3d\x1d\xd3\x68\xd1\x80\xcc\xdd\x32\x4d\x08\xc0\x05\xbd\x2d\x05\x88\x1a\x1b\x0f\xb0\xe1\x2a\xae\xe5\x62\x9e\xc1\xd9\x80\x4d\x28\x80\x8b\x45\x77\x24\x20\xd6\x2e\x6f\x76\x40\x7a\x1f\x9c\x37\x5f\xe2\x6e\x01\x3f\x22\x2a\xe1\x9a\x70\x36\x86\x07\x8f\x2c\xe4\x86\x96\xae\xeb\x6d\xa3\xae\xde\xbc\x9f\xf6\x4c\xf9\xd6\x98\x6f\x1c\xd1\x34\x59\x61\x1c\x26\x74\xc9\x38\x4a\x81\xbe\xf2\x38\x1a\x74\xbe\xbc\xc7\x77\x2e\x6b\x97\x10\x8e\x30\xee\xaa\xea\x28\x04\xfe\xf7\x76\xd4\xca\xb9\x49\x47\xd9\x5e\x70\x67\x27\x02\x33\x42\x7a\xd5\x98\x10\xa4\x7f\x68\x7e\x80\x9b\x50\x8c\x31\xc2\x5b\x71\xa5\x31\x97\x4f\x65\x5c\xf3\xba\x69\xe3\xd0\x7d\x19\xec\xbc\x64\x0a\xcd\x3a\x7d\x63\x2d\xed\xc8\xeb\xd7\xaf\x1b\xf6\x21\x2e\xa5\x20\x55\xd3\x4a\x2d\x7f\xa0\xd9\x82\xd6\x6e\x4f\x0a\x03\x1c\xba\x1a\x01\x0e\x4c\x45\x2f\xf2\xe5\x68\x1e\x15\x3f\xa7\x59\x9d\x94\xa4\x01\x4b\x56\xba\x2f\xbf\xfa\xea\xbb\x41\xab\x02\xaa\x74\x2b\x2e\x69\xcf\x3a\xe2\x38\xd7\xdf\x5a\xf1\xd3\xc3\x69\x4a\xd1\x61\xa4\x1e\xa4\xa1\x09\x06\x4b\xd8\x48\x01\xd9\xdf\x2a\x24\x0e\x60\x6e\x49\x5b\x7c\x70\x21\xf4\x51\xc2\xc8\x43\x05\xcb\xd2\x95\x60\x5a\x06\x20\x64\xa7\xb2\x6c\xab\x51\xd3\xa0\x17\x31\x12\x9d\xe8\x8a\x01\x28\xcf\xdc\xaf\xcc\x42\xa5\x25\x50\xf3\xe6\x8a\x76\x32\x5e\xbf\x7e\xed\x02\x73\xea\x47\x55\x2a\xc2\x34\x06\xcd\x12\xe0\x5b\x58\x38\xf0\x98\x6c\x4a\xd9\x5d\xe5\xa3\x59\xd3\x11\x23\x5d\xa5\x75\x35\x4d\x47\xd5\xc2\x8d\x92\x51\x90\x63\x45\x85\xe8\x00\x30\x4a\xb1\x6e\x05\x8c\x02\xb9\xee\xf6\x56\x68\x63\x1e\x25\xa6\x75\x8b\xd3\x82\x80\xb8\x61\xfd\xb3\x20\x9f\x65\x41\x51\x39\x86\x12\x98\x46\x3b\xc3\xea\x3d\x92\xb7\xb3\x15\x1d\xf2\x83\xd4\x9f\x33\xc4\x75\xb0\x79\xb8\x58\xbd\x87\xd3\x20\x7f\x97\x45\xe3\x4a\x9c\x95\xc0\xdc\x58\x09\xbc\x7a\x2f\x45\xd8\xa1\xbc\xaa\x97\x0a\xe6\x86\x6d\x8c\xd0\x15\x5a\x45\x33\xe5\x60\x5f\x89\x86\x64\x4c\x86\x7f\x70\x5b\x9b\xaa\xbe\xd9\xa0\xa8\x45\xcc\x42\x8c\x6b\x97\xfe\x58\xdb\x31\xa0\x2b\xcd\x51\x64\xbc\x58\x08\xc6\x45\x9a\x49\xf1\x47\x5a\x3e\x80\x19\x71\x8f\x30\x58\xc3\x96\x58\x40\xfb\x1a\x9b\x48\x4b\x07\xe7\x29\x6a\x0f\x3d\xb7\xe3\x50\x07\x19\x05\x4b\x25\x78\x3c\x76\x60\x99\xd3\xa3\x38\x45\xdc\x7a\x42\xd7\xc3\x50\x21\xc3\xcd\x5b\x37\x76\x3d\x69\x8e\xd1\xa7\xc5\xac\xd3\xed\xb9\x24\xfb\x3a\x9d\x22\xd9\xb8\x59\x97\x7c\x03\xd5\x86\x18\xd5\x2e\xf4\x25\xf6\x3b\xa2\x40\x7f\x1a\xa7\xa3\x20\xee\x33\xa4\xf6\x03\x37\x59\xc4\x3c\xf3\x35\x19\x8d\x83\xc5\xdb\x9b\x36\xcb\x0a\x3b\x8d\xf2\xc4\xaa\x26\x91\x55\x8a\x6e\xd0\x7e\xfa\x80\x63\x4a\xc9\x02\x1d\xff\xf4\xd4\x79\xa3\x7a\x59\xcc\xb4\x3d\x9f\x65\x98\xd3\x1a\x6e\x3d\xed\xb5\x1c\x03\x21\x61\xa0\xae\x2d\x73\x5a\xc3\xed\x6f\x20\x81\xcf\x69\x6b\xb8\xfd\x1d\xff\x54\xb4\xd0\x1a\xee\xf0\x22\xd1\x28\x48\x5a\xc3\x9d\x9d\x9e\x69\x3e\xf8\xff\xb1\xf7\xee\xfb\x6d\xdb\xcc\xa2\xe8\xdf\xc9\x53\xa0\xdd\xe7\x6b\xa4\x98\xb6\x75\xb7\xa3\xc4\x5d\xcb\x91\xed\xd8\x2b\x71\xec\x6d\x3b\x6d\xbf\xed\x9f\xeb\x1f\x25\x42\x16\x1b\x89\xd4\x22\x29\x5f\xd2\x78\xbf\xcf\x79\x8e\xf3\x62\xe7\x87\xc1\x85\xb8\x52\x94\x2f\x69\xda\x65\x7f\x6b\xa5\x22\x09\x0c\x06\xc0\x60\x30\x18\xcc\x05\x1e\xd9\x20\xfd\xd8\x6d\xb5\xe0\x99\x9b\x11\xfd\xd8\x6d\x51\xf0\x8c\xb3\xff\xd8\x6d\x51\xb4\xf8\x65\xef\x8f\xdd\x16\x69\x90\x1b\x01\xfd\xd8\x6d\x35\x6f\xcf\xbc\xe6\xab\x27\x7b\xc4\x27\x7b\xc4\x7f\xb6\x3d\xa2\xcb\x18\xf1\xde\x36\xf3\xe5\xcd\x04\x4b\xd8\x00\x42\xb9\x8f\x38\x7b\x4c\x13\x7b\x78\x3b\xdf\x64\x25\x37\xae\xbf\x8b\xcd\x4a\x09\x93\xfa\xd5\xd5\xd5\x3c\x26\x8d\x2d\xce\x0d\x4b\xd8\x48\x58\x3c\x80\xc3\xd9\x08\xf9\xd3\x50\xc2\xfd\x91\x0e\x24\x66\x32\x7a\x4d\xe0\x51\x33\xd6\xdf\x55\xb8\xc2\x38\xd1\x75\xe3\x46\x2b\xae\x42\x0b\x08\x7c\xb2\xf8\x65\x6c\x6a\x1f\x71\x66\xd9\xd4\xd4\xcd\x4b\xde\x5d\x6e\xcf\xbc\x56\xed\x69\xb7\x78\xda\x2d\xfe\xd9\xbb\xc5\x77\x6a\xbd\xfe\x70\x86\xe6\x25\xed\xe0\x73\x53\xce\x43\x9c\xa4\x71\xe4\x8f\x9f\xec\x39\x1f\xdb\x9e\xf3\xb6\x9c\x85\x5f\x84\xaf\x72\xb3\xc1\x22\xfd\x70\x5e\xd0\x54\x11\x4f\xd9\xac\x9e\x5b\x0b\xdd\xe3\x86\x32\x9c\x90\x8d\xe0\xc8\xbf\x7a\x8f\xe7\xdd\x5c\xc8\x45\x5f\x78\xcf\x9f\x3d\xd3\x71\x33\x0a\x14\x78\xa6\x95\xbf\x89\x33\xdb\x11\x1f\x24\xc3\xbd\x67\xcf\x4a\xde\x4d\x97\xbe\x82\xc3\x83\x23\x3c\x88\x2f\x69\x70\xa8\xa2\x3b\x2b\x5e\xce\x8a\xab\xfa\xb5\x60\x40\x66\xd1\x38\x1e\x7c\x2e\x47\x29\x4a\xd9\x02\x62\x71\x95\x2b\x63\xf0\x58\x6e\xdc\x9c\xa3\xf7\xc0\x37\xdf\xf9\xdc\xcf\xbd\xfe\x5e\xe4\x9a\xd3\x76\x6d\xec\xec\x52\xf9\xf9\x29\x37\x3b\xc5\x73\xb3\xc8\x5d\xa6\x3e\x37\x1a\xf2\x36\xc9\x9a\x35\x2c\x35\x22\x2d\xde\xfc\xad\x42\x41\xd2\xed\x09\xa7\x6a\xd7\x6d\x87\xf3\x52\x44\x02\x27\xcb\xbb\x8f\x77\x3e\xd8\x9c\xa3\x16\xce\xa7\x43\x2e\xec\x10\xcb\x4d\xb9\x9c\x6f\xb7\xb9\x70\x6e\x51\x11\x69\x5a\x21\x5d\x4e\xaf\x3f\xc9\xe9\x4f\x72\xfa\x3f\x5b\x4e\x67\x42\x7a\x3a\x72\x68\x75\xe6\x88\xdf\x38\xc1\xb3\x09\x01\xfd\xf3\x1c\x25\xd0\x20\x4e\xf0\x4a\x18\xab\x72\xfa\x5a\xe9\xc0\x09\x25\x1d\x2d\xe7\xf9\x6b\x42\xa1\xe3\xd1\xe8\xd1\xb5\x43\xdf\x8f\x3c\x4e\xb8\xe3\xf1\x48\xb9\xdd\xc0\x57\x2c\xe8\xf4\xce\xb7\xb8\xd0\x49\x47\xf3\x2f\x74\xd2\x11\x5c\xe8\x50\xc1\x65\x91\x7b\x9b\x22\x39\xdf\xbd\x39\x19\xe2\x81\xb4\x35\x5d\x5a\x6f\xea\x98\x88\x90\x8e\x46\xe7\xf6\x02\xaa\x51\x08\xb2\xe8\xb2\x8a\x1a\x0d\xa3\x61\xec\x6e\xd1\xf2\xf5\x7e\xcd\xa5\x38\xdb\xf7\xaf\x19\x11\x1c\x87\x5f\xf4\xcb\x61\xa9\xed\x79\x45\x55\xab\x9f\xbb\x20\x12\x46\x87\xf1\xaf\xc5\x08\xd8\x8a\xdc\xaf\xe1\x89\x9f\x7c\x3e\x49\x66\x69\x86\x83\x43\x6c\x5c\x06\x4b\xcd\x17\x17\xbc\x1f\x12\x11\x26\x32\xdd\xa1\x1f\x16\xb4\xef\x2c\x73\x3f\x0a\xf0\x83\xe0\x30\x09\x2f\xfd\x0c\xd3\x23\xa1\xa3\xf5\xa2\x62\xf7\xeb\x3b\x4d\xfa\x35\xb7\xfb\x45\xc5\xee\x87\xc0\xc8\x4f\xe7\xb6\xee\x2c\x73\xbf\xa6\x2f\x70\x46\x37\xf4\xc2\xb1\x2f\x28\x75\xff\xe6\x4b\xcc\x7d\x51\xb1\x7b\xd3\xfd\xf1\xcd\xa4\xb0\x71\x57\x91\x7b\x53\xfd\xbc\x86\x5d\x45\xee\x3b\xe4\x44\x8e\xcb\x30\x05\xbd\x93\xc4\x93\x43\x3f\x4d\xaf\xe2\x24\x28\x1a\xff\x92\x75\xee\xbd\x0e\xe6\x8d\x89\xab\xc8\xbd\xc9\x70\x5e\xc3\xae\x22\x0f\xc1\x7a\xe6\xb5\x5d\x50\xca\xde\xbc\x78\x58\x5d\x45\xe9\xac\x0f\x37\x6f\x34\xa5\xf1\x2c\xca\x9f\x27\x61\x9a\x86\xd1\xc5\xf3\xd2\xd8\x4e\xe3\x54\xbf\xba\x92\xb0\xb4\x7c\xb5\xe8\x29\x50\xb1\xde\x11\xcd\xbf\xe5\x3a\x1e\x8d\xa4\x04\x62\x9a\xed\x85\x72\x8a\xd6\x2c\x23\x5a\x8d\xa7\x33\xf4\xd3\x19\xfa\x9f\x7d\x86\xce\xef\xba\xfa\x5f\xbe\x68\x77\x5d\x9b\x63\x7c\x8d\xde\xe2\x04\x5f\xa4\x5f\xfc\xf4\x4b\x88\xde\xf8\x63\x7c\xfd\x9f\x49\x36\x4c\x57\x46\x33\xf5\x38\xdc\x61\xd1\x4c\x8f\xf0\x10\x27\x38\x1a\xe0\x2e\x22\xed\xa7\xdd\xd5\xd5\x8b\x30\x1b\xcd\xfa\x2b\x83\x78\xb2\xca\x4f\xdd\xab\x17\xf1\xb2\xf8\xdd\x1f\xc7\xfd\xd5\xf4\xca\x4f\x26\xab\x61\x94\xe1\x24\xf2\xc7\xab\xa4\x4b\xf8\x3a\xe3\xff\x5d\xb9\x88\xff\xd7\x87\x66\xf3\x91\xaf\xc6\xf2\xfb\xae\x63\x82\xcd\x3f\xfc\x70\x0d\x3f\xfe\x16\x97\x5d\xd4\xf2\x15\x67\x57\x71\xf2\xf9\x08\x43\xa8\xda\x22\x45\xb9\x5e\xdc\xd4\x96\xf7\xbf\x7c\x39\x2f\x28\x75\x1f\xdf\xbc\x9b\x68\xb0\x1d\xf9\xfd\x31\x9e\x87\xa5\x54\xd2\x8e\xa0\xbd\xc0\x7d\x70\xbb\xf2\xa7\x25\x71\xcb\x4b\x3a\x70\xb3\x16\xb8\x07\x6e\x41\x7c\x15\xb1\x28\xc4\x45\x88\xf1\x62\x76\xac\x2c\x5f\xcb\xbb\x9b\x3a\x10\x9b\x4d\x4b\xa0\x45\x0b\xd9\x91\x32\xbe\xdd\x1b\xa5\x04\x67\x49\x88\x2f\xe7\x45\x80\xe0\xc5\xec\x68\x59\xbe\xde\x87\xb4\x32\xb2\xdb\xcd\x21\x2a\x52\xc6\x41\x4e\xda\xa7\x7b\x0f\xd1\x05\x2e\xe1\xce\x6c\xc7\x45\xfd\x70\x8f\x31\xa1\xd9\x1b\xe6\xc4\x48\xb5\xe3\xa0\x7e\xb8\xf7\x68\xb0\x84\x2d\xc5\xc8\xd0\x42\x76\x7c\x8c\x6f\x1c\xa5\x56\x29\x94\x0a\x6e\x75\x0d\x15\xa7\xce\x96\xa5\xdb\xbf\x9c\x1f\x4a\x2f\x73\x46\x94\xbf\xe4\x7c\x40\xba\x71\x9c\xaa\xcf\x9c\xfa\x25\x40\x84\x04\xf3\xc7\x0b\x2c\x5d\x4c\x4e\x67\xd2\x83\x24\x8b\x3f\xea\x35\xe3\x28\xbc\x74\xfa\xc6\x90\x39\x81\xef\xce\x33\x64\x31\x6c\x8b\x52\x56\x81\x0d\xdf\x1d\xc7\x2b\xcb\xf9\x8a\x08\x4b\xb6\x60\xb7\xd6\x7b\xc9\xe6\xd3\x99\xea\xe9\x4c\xf5\xcf\x3e\x53\xb1\x03\x15\xbf\x20\xfa\xb6\x51\xda\xef\x62\x58\xcd\xbd\xa3\xfc\x69\xc8\x85\x71\x9a\xe2\x2f\x1b\x15\x59\xa0\xd1\xeb\xb2\xc2\xa8\x94\xbc\x74\x76\x33\x25\xf2\x01\x8b\x40\xf9\xfa\xb9\xc4\xc0\xc3\x6c\x30\xaa\x90\xef\x7a\x6e\x91\x81\x9f\x62\xf4\x82\x50\x7c\x9a\xbd\xe8\x2a\x9f\x60\xb2\x92\x8b\x74\x25\x1d\x85\xc3\xac\xa2\x25\x14\x41\x46\x72\xc0\x9a\x59\x80\xb1\x64\x70\x5f\x8b\xf0\x15\x8d\x40\x45\x2f\x64\x5f\x5b\xd0\x98\xe2\x28\x08\xa3\x8b\x47\xc7\xe3\x90\xb6\x23\xdb\x10\xd9\x90\x62\xa1\x03\x4d\x6c\x34\x70\x46\x65\x9a\x60\xe5\x56\x92\x0e\x44\xa9\xf9\x96\x84\x0c\x9a\x2e\x23\x28\xa4\x60\x91\x9d\x2c\x52\x75\x14\x46\x69\xe6\x8f\xc7\xa5\x5a\xd6\x4a\xdb\x7d\xdd\xdd\x85\x0a\xf0\xb8\xc0\xd9\x87\xf8\xa2\x44\x40\x31\x52\xca\xe9\x63\x4f\x5b\xd4\x8a\x14\xb4\x3a\x8d\xe7\xc6\xe1\x20\x45\xe6\xb4\xd7\x1b\xf9\xd1\x05\x2e\xd1\xa4\x4d\xf8\xa0\x20\x64\x93\x2c\x65\xf4\x14\x41\x88\x74\x4c\x6a\x24\x1e\x8f\x65\x79\x60\x61\x7e\x93\x8e\x46\x2b\xc0\x1a\x0d\x76\x93\x8e\x4c\x76\xe3\x16\x9f\xe6\xdc\xd2\x18\x64\x80\x8c\x5b\x1a\xc5\x92\xe0\x41\xd5\xf4\x6e\x62\x44\x36\x4d\xfd\xe3\x21\x62\x92\x2e\x32\xae\x29\x68\xb3\x0c\x07\xbd\xe8\xfd\x9a\xd7\xc8\xf8\x01\xda\x96\x49\xcf\x90\x44\x29\x0e\x38\x1b\x75\xc9\x3f\x14\x58\x3a\x1a\x75\xc9\x3f\x54\x78\xb5\x25\x64\x68\xb5\x9e\x44\xd2\x27\x91\xf4\x1f\x2e\x92\xe6\x7a\x7e\xee\x63\xfd\x40\xd9\xa2\xa9\x7f\xf8\x11\xbe\x20\xf3\xec\x27\x9b\xfd\xd0\x91\x97\x20\x5d\x7d\xa7\x16\x85\x44\xff\x5c\x3d\x1f\x0e\xfc\xa9\x0c\xc4\x05\x63\xaf\xb7\x79\x68\x42\x90\x30\x61\x8e\xe8\xcc\x7a\x19\x6d\xa0\x17\xb5\xeb\x41\x27\x78\x15\x34\x06\x41\xab\xf5\xca\x5f\x6b\xb7\x06\xad\x57\xad\x46\xa7\x85\xeb\xeb\xb5\x57\x83\x76\x0d\x37\x5b\x41\xa7\xd5\xee\x34\xfa\x2f\x72\x5c\x6c\x60\xfc\xba\x5f\xaf\xd7\xfb\x83\xda\x5a\x6b\xf0\x6a\x30\xf4\xd7\xd6\xeb\xc3\xda\xa0\xb9\x8e\x3b\xcd\x7e\xd0\xae\x0f\x5e\xd5\xfb\xeb\xfe\xb0\x56\x7b\xe1\xe6\x4d\x14\xc7\xae\x24\xe9\xfa\xfd\xb0\x6b\x19\xc4\x9c\x13\x32\x37\xf8\xae\xb5\x7f\x74\xa7\xa7\x85\x09\xda\x06\x64\x7d\x5c\x2d\x70\xcd\xee\x52\xa8\x0a\xc7\x2c\x9e\xc5\x1f\xbb\x75\xef\xc7\x39\xf3\xf4\x63\xb7\x41\x98\x6d\xfb\x89\xd9\x3e\x31\xdb\x7f\x36\xb3\xcd\x79\x2d\x57\x7e\x69\xcc\xb6\xc8\x30\x79\x98\xc4\x5f\xf0\xc4\x8f\x56\x02\xfc\xf3\xb7\x4a\xe7\xaf\x5f\x90\xde\x27\x5d\x3f\x55\x24\x4a\xdf\xe9\x0b\x25\x23\x81\x56\x22\xd5\x4b\xdc\x25\xf7\xfe\xe2\x19\xfe\x0b\xb2\xf5\xf3\xb1\x78\xfc\x7c\xfd\x65\x73\x7c\xdf\x3b\xc5\xb7\xa5\x4b\x05\x49\xbe\x6d\xa1\x5b\xb4\x11\xfe\x0f\xdb\x5b\x5a\x17\xd2\x6d\x7f\x27\xe9\xb5\x9d\xfd\x7e\xa0\x04\xdb\x3f\x6c\x50\xc2\xd1\x5e\x91\x0d\x65\x18\x46\x38\xb8\x4f\x06\x6e\x9e\x39\x36\x8b\x11\x4b\x5b\x9d\xa7\xb3\x86\x74\xdc\xe2\xb0\x2d\x12\xb2\xae\xa0\x7d\xb2\xb1\x85\x38\x65\x94\x04\xc3\xa4\x8d\xa5\x96\x03\x7b\x6e\x36\x7d\x3e\xae\x3b\x6c\xa4\xbe\x7e\x9c\x8d\xc7\xb7\x92\xb1\x7b\x38\x44\xf8\x3a\x4c\xa1\xb8\x75\xc8\xb5\x16\x0b\x73\xca\xf3\x0c\x36\xbc\x35\x9a\xc3\x46\xce\xbb\xbf\x8c\xea\x67\x55\x47\x9a\xf9\x95\x69\x3c\xad\x54\x21\x6f\x35\xbb\xf7\x22\xfc\x1f\xd6\x13\x8c\xd6\x0f\xb2\x70\xa3\x0e\x37\xb5\x6f\xc8\x31\xcb\x62\x3b\x29\xaa\x76\x10\x2e\x62\x64\xaf\x78\x2f\x9c\xd4\x58\x3e\x7d\x37\xd4\x11\xa2\x24\xe2\x09\x4a\xf2\x74\xde\xef\x70\x56\x91\x4e\xe7\x38\x9a\x4d\x70\xe2\xf7\xc7\xb8\x8b\xb2\x64\x86\x4d\xd5\x9f\x3f\xc1\x69\x61\xaa\x6e\x29\x9f\x37\x14\x06\xe5\x2d\x92\x72\x78\xa7\x73\x92\x78\xa7\x5a\x16\xef\xd4\x91\xc6\x5b\x2f\xf2\x5a\xd1\x88\x89\xe6\xeb\x3c\x7b\x3f\xed\x84\x3d\xbb\x4b\xdc\xff\xc3\x83\xf2\x1e\x1d\x32\xd6\x17\x02\xdf\x4f\x6f\xa2\xc1\x3b\xd8\x6f\x88\xc8\x0b\x5d\xa8\x9e\x29\x39\xd1\x37\x59\x91\x8a\xe4\xa6\xa1\x55\x53\x26\x09\x40\xa8\x2c\x03\x6e\x97\xd1\x12\xe0\xb0\x32\x18\xf9\xc9\x66\x56\xa9\x55\x57\xb2\xf8\xd3\x74\x8a\x93\x9e\x9f\xe2\x4a\x95\x7f\x86\xac\xca\x95\x7a\xd5\xb9\xf1\xf0\x99\x75\x67\x1e\xcd\x37\xee\x3c\x8d\x2a\x8f\x88\xc6\x6b\x5c\x90\x0e\x99\x2b\x46\x08\x28\x4a\x82\x6d\xf1\xd6\x96\x62\x5b\x55\xf4\xf0\xcc\xf6\xa2\x0a\xdd\xee\x25\x8d\x4d\x9e\x8a\xbb\xa8\x83\x7c\xd4\x17\xeb\x65\x7e\xd7\xef\x0e\x02\x86\x72\x33\x27\x6b\x87\x68\xda\xf3\x05\x7b\x55\x32\x3d\xba\x9a\x12\xdd\x3e\xd8\x66\x52\xf4\x5b\x35\x7b\xf9\x05\xce\x16\x4c\x5e\x7e\x81\x5d\xdb\xc9\xf7\x9d\xbb\xdc\x42\x1c\xe5\xb3\x97\xeb\x66\x73\x5d\x59\x1e\x35\x95\xe4\xa7\x67\xaa\x7a\x9d\x4c\x13\xab\xa2\x6d\x56\x25\x13\xa1\xcb\x53\xf6\x58\xe9\xd0\xf9\x00\x49\x07\x73\x2d\x66\x0f\x39\x62\x77\x9e\x8e\xd8\x4f\x47\xec\x7f\xf6\x11\x5b\xd2\x67\x32\x0e\x31\x61\x2c\x5d\x3d\x69\xff\x17\x1e\x0e\x13\x7c\x83\x7e\x0d\xc7\x83\xcf\x18\xbd\xf9\x03\x0f\x87\xae\x68\x3d\x0b\x85\xf6\xd9\xf7\x13\x72\x84\x3f\xf0\xa3\x01\xf6\xa1\xac\x2d\xa8\xcf\x1d\xe2\x00\xb1\x2a\xef\xfc\x4b\xf4\x6b\x1c\x07\xe8\xcd\x85\xf3\x90\xdf\xca\x0f\xf9\xff\xc5\xb8\xa9\xe2\x3c\xcc\x58\x6c\x51\x4a\x5b\x4b\xa0\x3a\x3d\x0b\xad\x2d\x05\x2d\x4e\x92\x58\x0b\x1e\xb4\x4a\xdf\x51\x1b\x04\xba\xed\xec\x65\x2f\x52\xb2\x31\x4e\xe3\x28\x0d\xfb\x63\x4a\x60\x53\x1f\x9c\x48\xd0\x84\xdd\xf9\x90\xbd\x68\x9a\xc4\x97\x61\x80\x93\x54\xd4\xf2\xc7\x69\x6c\x56\x8d\xc7\x63\x52\x95\x50\x1b\xb7\x1e\x47\x51\x1c\xd0\xaf\x61\x34\x88\x27\x32\x64\x02\x8c\xe5\x14\xa0\x57\xae\x59\x38\xc1\x64\xb1\x85\x29\xaa\xa3\x14\x0f\xe2\x28\x80\xdd\x31\x8c\x2e\xc6\x38\x8b\x23\x18\x4e\xd2\xbd\x82\x83\x3e\x47\x55\x39\xee\xf3\x97\x68\x43\x74\x45\xd2\x33\x90\xb6\x41\x03\x7c\x2b\xbd\xe4\xb8\xc8\x5a\x07\xe7\xe1\x8f\x48\x28\xa3\x24\x8e\xe2\x59\x3a\xbe\x81\x30\x18\x8e\x7d\x98\x7c\xb2\x9c\x47\x50\xe0\x67\xbe\xf3\x84\xac\xf6\x56\x51\x79\x44\x81\xd2\x79\x02\x46\x3e\xa9\xfd\xa0\xf4\x5e\x49\x6e\x18\x47\x69\x4c\xb6\x2e\x42\x14\x15\x4a\x1a\x2b\x7b\xd1\xa5\x3f\x0e\x83\x43\x56\xbe\x22\xcb\x3c\xdc\x0b\x1b\x06\x43\x92\xf0\xd5\x3d\x9e\x91\xf9\x4a\x16\x1f\xd2\x77\x80\xd2\x0a\xed\xbd\x07\xdd\x64\xc6\x16\xd2\xf9\x85\x9d\xca\x37\xd4\xb9\xa2\xc2\x2c\x03\xcd\xaf\xca\xa1\x53\xbc\x91\x30\xfd\x85\xa0\x7b\x44\xa9\x10\x0b\x41\x4d\xea\x66\x36\x4a\xe2\x2b\xa4\x76\x4f\x2f\xaf\x74\x87\x75\x93\x7e\x5a\x29\x75\xf2\xf7\x17\x9a\x7d\x90\x66\x0b\x49\x40\x3f\x97\x0a\xe9\x67\x3e\x31\x00\x70\x83\x22\xa4\xe0\xb9\xa5\x68\x83\xa7\xce\x94\x64\xe3\x22\xea\x78\x18\x42\x30\xe7\x9e\xca\xfd\x0c\x64\x09\x79\x9e\x74\x0a\x27\x89\x2e\xe2\x5b\x7a\x53\xd5\xcd\x6d\xc8\x9f\x02\x67\x11\x1a\x9b\x3f\x64\x46\x6d\xb9\x7d\x43\xc8\x65\xd9\x5e\x15\x12\xd4\x83\x73\xba\x8f\x0d\x36\x6a\x2c\x3a\x19\x90\x02\x6f\xc9\x77\x8b\x92\x89\xd6\x7b\x08\xc2\x84\x16\xbe\x33\xc2\x04\x9c\x64\xea\xe4\x4c\xe6\x6e\xa4\x98\x3e\x00\x2d\xaa\x34\xc8\xf5\x6c\x30\x1b\x15\xde\xca\xbd\x48\x2f\x9d\x47\x7b\x4a\x87\x04\xd1\xa1\x39\xdb\x1f\xce\xc4\xbe\x4a\xa4\x4d\x7e\x26\x64\x22\x9f\x41\x71\x19\x9f\x2a\xbb\x6a\xae\x90\x96\x44\x5d\x75\xd7\x77\x6e\xf7\xf3\x76\xee\x8c\x1c\xa9\x98\xe0\xa2\x23\x4a\xbe\x1d\x8a\x4f\x73\x39\x36\x8d\xfd\x7f\x0b\xd0\xf6\x82\xb9\x4b\xc6\xf2\x55\x98\x25\x71\x4c\xb2\x38\x88\xd1\x60\x8c\xfd\x68\x36\x45\x11\xc0\x27\x03\x2c\x8e\xed\x45\x43\x25\x61\x6f\x59\x79\x14\x49\x39\x20\x8a\x68\x5c\x1d\x4b\x22\x1c\x9d\xd2\xd2\x67\x44\x48\x22\xd5\xbb\x88\x02\x09\x83\xae\x01\xa8\x6b\x03\xd9\xcd\x7f\xde\xf2\x9c\xd8\xab\xab\xfa\xe8\x2b\x0c\x80\x09\x60\xea\x6e\xce\x10\xaa\x88\x15\x3e\x67\x72\xe3\xa9\x10\x4a\x89\x08\xca\xcc\x68\xe1\x74\x73\x11\x92\x23\x5d\xa8\xeb\x8e\x49\x1d\xcb\x9c\x1b\x73\x5b\x38\xf2\x02\x84\x4a\xa4\x50\x97\x77\x88\x5a\x96\x59\x06\xf9\xb5\x34\x3c\x39\xfe\x6c\x74\x2a\x4c\xa3\xfa\x19\xdf\xa4\x95\xbc\x6e\x95\x6b\x79\x21\x59\x3c\xfa\xe9\x27\xe4\x1a\x43\x42\x4c\xc9\x09\x7d\x5f\x51\x0a\xbd\x56\xc7\x59\x17\x80\x0b\xc6\x3b\xdf\x7d\x12\x4c\x78\x01\x91\xff\xf9\xb0\x4f\xf0\x60\xe4\x47\x61\x3a\xe1\xc7\xd0\x62\xe6\x00\x00\x8a\x87\x97\xb6\x21\x0f\xec\x67\x8c\xa7\x22\x7f\x00\xef\xec\xea\xcb\x3f\xd2\x51\x18\x91\x86\xae\x07\xf1\x64\x3a\xc6\xd7\x61\x76\xd3\x6d\xc3\x91\x8c\x14\x20\x04\x51\x21\x9b\xc3\x67\x7c\x43\x35\x05\x62\x34\xa5\xf1\x5a\x5d\x45\x09\x9e\xc4\x97\x18\xf9\xe3\x31\xf4\x2a\xf5\x10\xbe\x1e\xe0\x69\x06\x62\x3f\x7b\x25\x97\xcf\x46\xf8\x06\x45\x98\x8e\x48\x1f\xb3\xfa\x01\xe9\xf1\xcc\x1f\x8f\x6f\x50\xff\x06\x86\x8c\x0c\x0f\x4b\x05\x00\x34\xf3\x2b\xd9\x90\xc2\xe8\xa2\x52\x95\xf6\x81\xca\x0f\x4a\xef\xd0\xd7\xaf\x04\xdf\x95\x30\x0a\xf0\xf5\xc1\xb0\x02\x6e\x8a\x84\xd8\xce\x5f\x54\x61\xf2\x97\xeb\xfa\x06\x21\x51\xd8\x67\x7c\x73\xb6\x22\x56\xa2\x6e\x0e\x6d\x52\x24\x29\x6f\x98\x26\xff\x8d\xc9\x13\x4e\x99\x64\xde\x07\xd4\x36\x17\xc5\x51\x19\x9e\x40\x4d\x6a\x8b\x68\x92\x59\x0c\x9b\x2a\x50\x07\x15\xa2\x0e\x01\x67\xe9\x4c\x8a\x33\xa5\xf7\x04\xb0\xa4\x8a\xf4\xd0\x60\x65\xfb\x64\xf7\xfc\xf0\xe0\xc3\x87\xbd\x8f\xef\xce\x4f\xf6\xf6\xb7\x0f\x3e\x9d\xc8\xc7\xa3\x32\x33\x60\x0a\x55\x8a\xc4\xf4\x28\x47\x47\x53\x26\x23\x78\x6d\xf9\x99\x8f\x36\xd0\xe9\xd9\x6b\xf5\xfd\x1e\xb8\x1b\xf3\xd7\xe5\x96\xaa\x00\xb8\x32\x9d\xa5\xa3\x8a\x4e\xf7\x4c\xc4\x53\x4a\xef\x05\x29\x2d\xfc\x19\xdf\x54\x8d\x31\xc8\x01\x2e\x30\x78\xa5\xc4\x4d\x01\x99\x35\xca\x97\xd4\xc4\x9f\x2a\x4c\x32\x04\xb2\x05\x86\x02\x24\x46\x48\x53\x1d\xa6\x7d\x7f\x2a\xa9\x2e\x24\xbd\xb6\xea\x29\x4e\x05\x57\xe0\x1a\xd5\x3f\xf5\x31\xd8\xf7\xa7\xa7\x50\x2d\x84\x2d\x9e\x8f\xcc\x29\x14\x3f\x93\x3c\xd2\x45\xe3\x8a\xdf\x3c\x5a\x58\x66\x8e\x55\xa9\x59\x09\x6f\x72\x72\xb0\x75\xd0\xe5\x44\x86\xc6\xf1\xc5\x7f\xe8\x52\x75\xec\x90\xab\xef\x2b\x49\x97\x50\x16\xa4\xd6\xa3\x23\xfb\xb6\x32\xf1\xa7\x15\x97\xb1\x02\xff\x03\xfb\xc5\x20\x1f\x65\x32\xf6\xec\xa8\x17\x06\xb2\xe3\x8d\xa0\x88\xcf\x18\xa5\xb3\x04\xf4\xc4\x9c\x59\x85\x29\x4a\xb3\x90\xd0\x03\xe5\xe4\x38\x40\xfe\x10\x1c\x84\x92\x24\xbc\xf4\xc7\xda\x5e\xab\xc0\x24\x03\x02\x6e\xff\x74\x69\x84\xc1\x99\x8e\x62\xde\xa5\x95\x41\x6e\x0f\xa0\xd6\x11\x5f\x9c\x0e\x33\x5c\x77\x22\x7f\xba\x45\x78\xcc\xf4\xcc\x96\x1a\x43\x7f\x9c\x62\xf9\x96\x8d\xb9\x3d\xcd\x1d\x53\x91\xce\x9f\xb5\x89\xee\x00\x83\xcc\x0b\xcc\xb8\xb4\x68\x1d\x87\xff\xd7\xc6\x78\xfe\x00\x35\x4b\x8c\x63\x79\xc5\x00\x52\x28\x4c\xea\x25\x54\x54\x47\x49\x5b\xec\xee\x61\x52\x71\x71\xeb\x19\x90\x7c\xc9\xe9\xca\xb8\x74\xa4\x07\xd5\x50\x6f\xbc\xb4\xd4\x4b\x66\xea\x0a\xa6\x90\xfe\xb1\xdb\x80\xd0\x3e\x4c\x19\xfe\x63\xb7\x09\x6e\xa8\x6b\x65\xee\xc8\x58\xcc\x4d\x9c\x65\x61\x74\x61\xf7\xec\x05\xc6\x14\x48\x99\x6b\xd1\x86\xf0\x59\x7b\x6d\x94\xc8\x23\x3d\x0b\xfb\x20\x57\xd0\x22\xd6\x28\xeb\x37\x41\x79\xfd\xe9\x5a\xef\xe9\x5a\xef\x1f\x7e\xad\xc7\x22\xfa\xb2\x53\xcb\x5d\xa2\xfa\xce\x33\x87\x75\xe4\xbe\xd0\x52\x5f\x2c\x62\x38\xcb\x97\x74\x9d\x1d\x0e\x36\x83\x20\x85\xa1\x13\xbb\x9b\x1f\x81\x5a\x2a\x45\x33\x2a\x7e\x31\xa7\x37\x8f\x08\x5f\x61\x06\x91\xf2\x10\x24\x05\xa0\x9b\x2a\xdd\xed\x9f\x3f\x97\xcf\x07\xec\x7c\xf6\x5c\x57\x12\x91\x6d\xf3\x39\xbb\xb6\x92\xca\x49\xbc\x8a\xc6\xe9\xe1\xae\x74\xa4\x5c\x1c\x31\x87\x2b\x85\xa3\x31\xb9\x89\x8c\xbd\x45\xd5\xe8\x12\x8a\xe8\xbe\xcd\x7b\x9a\x5a\x36\x0b\x9b\x3d\x0e\xff\x53\xf7\x2d\x7d\x7b\x72\xe9\x2e\x85\x85\x20\x0f\x44\x04\x28\xff\xf4\x13\xe0\x4e\x15\x53\x61\x74\x01\xdc\xb8\xaa\x40\xe4\xd7\x17\xf3\x52\x9a\x52\x88\xb2\x97\xf2\x5d\x3b\x29\xa4\xa1\xb1\x9f\x42\x33\xc7\x19\x99\xec\x1f\x36\x36\x8c\x81\xe6\x7f\xc6\x8b\xd5\x55\x9a\xb9\x5d\x21\x29\x58\x6a\x59\x32\x23\x32\x5b\x92\x66\x28\x8d\xa9\x9d\xe3\x74\x0a\xac\x1b\xce\xce\x7e\x74\x93\x91\x03\xbf\x87\xfa\x78\x48\x18\x00\x5d\xe2\xfc\x0a\x15\x46\x83\x2a\x19\xb5\xbf\x70\x58\xf9\xc1\x82\xf5\x4f\x3f\x21\xdb\xc8\x57\x8d\xfa\xc8\xbc\x6e\x20\xa8\x5a\xdc\xa3\x9d\x9d\x8d\x29\xdf\x8c\xf0\x75\x86\x7a\x87\x9f\xd0\xe0\x66\x30\xc6\x9e\xe8\x26\x0c\xbb\xd8\x6c\xa0\x27\xd0\x65\x66\xb3\x34\x4d\xe2\x01\xe1\x59\x29\x1d\x1d\xa3\x15\xe9\x18\x2c\x96\x89\x6d\x2e\x2c\x1d\x61\xa4\xa1\x97\xba\xf5\x50\xad\x4c\xff\x2c\xc3\x4a\x49\xc1\x25\x9a\x49\xc6\x60\xcf\x05\x00\xdd\x8c\x4d\xd2\xc5\x56\x4c\x3b\x28\x47\xba\x5f\xdd\x12\xea\xd6\xcb\x85\xf0\xbd\xc0\xcb\xd9\x04\x7b\x2f\xeb\x90\xa8\xce\x00\x38\x0b\x59\x27\xdc\x4e\x72\xcf\x9a\x96\xd3\x99\x6b\xb3\xd8\x64\x5e\x93\xff\x90\xac\x6b\xda\x23\x72\xb4\xa4\x9c\x5a\xa2\x5c\x78\x69\x49\x2a\x27\xd6\xab\x74\xd2\x87\x0f\x7e\x10\x08\xdb\x2e\x29\xef\xa7\xf8\xae\x4f\x8f\x74\x70\x90\x58\x2c\x37\xde\x82\xf7\x92\xad\x38\x15\xe8\xc4\x48\xc8\x96\xbe\x79\xbb\x85\x16\x8b\xe1\x30\x7f\xa5\x6a\xa5\x72\x16\x04\x5a\x05\x0d\xf9\x52\x48\xc8\xb3\xe8\x96\x68\x0d\x02\x13\x2a\xe7\x8a\x34\x07\xd5\x82\xd1\xb6\x4a\xb5\x02\x21\xb7\x01\x1b\x91\xd5\xd5\x6c\x17\x44\xf6\x7d\xca\x51\xfa\x24\xfb\xfe\xd3\x65\xdf\xdc\xa4\x8d\x27\xec\x7d\x28\x1f\xdd\xbd\xbe\x1f\xa9\xd2\x6e\xd8\xf7\x85\xeb\x2d\xbe\xa6\xea\xea\x22\xd7\xdd\xe3\x89\x9f\x64\xdb\xac\x60\xee\x76\xeb\xbc\x1a\x03\xb5\x12\x34\xcb\xfb\xa2\xe9\xbc\xa5\xd7\xe2\x12\xec\x38\x4b\xc2\xe8\xe2\x16\x5c\x5b\x6c\xef\x89\xb4\xdc\xf7\x23\xf9\xd3\x2f\xfe\x78\x86\x6f\xd1\x25\xf9\x0f\xbb\x0e\x21\x90\x87\x38\xc1\x73\x6e\x48\x3d\xd5\xbc\x00\x82\xd4\x30\x9c\x54\xb1\x38\x1b\x79\x80\x11\x91\xd6\x3d\xda\x92\xb9\x85\x81\xda\x8d\x8e\x32\x64\x9b\xee\xfb\x51\x25\x8b\xab\x4c\x55\x04\x3a\x1c\xf2\x99\xab\x7c\x2a\x16\x2b\x22\x52\x0f\xd2\x44\x54\x5e\x84\x54\x7d\x43\x21\x32\x3f\xdd\x17\xa6\xfe\x98\x41\xdc\x0a\x13\x22\x8b\xd9\x1c\x62\x78\x8f\x4e\x62\xe6\xd9\x2b\x77\x07\xaa\x33\xe8\x95\xaa\xd9\x35\xde\x9e\x90\x63\xa0\x1b\x36\x49\x17\x5c\x24\x84\xa7\x34\xce\x46\x72\x4a\xf0\x4a\x15\x1a\x61\xd8\x46\x69\x16\x66\x33\x2a\x70\x99\xe6\x5f\x01\x9e\xc6\x69\x98\xc9\x58\x32\xb8\x02\x3d\x00\x33\x18\x87\x38\xca\x74\x4b\x8c\xd2\x0d\x1b\x26\x16\x3c\xd5\xb8\x39\x82\x8b\x62\x64\x8e\x1f\x57\xc1\x17\x5e\x25\x0b\xd2\x1b\xce\xa2\x00\x6c\x22\x07\x38\xc9\xfc\x50\x4c\xbf\x63\xf9\x88\x89\x5d\x6c\x1d\x3d\xfa\x12\x12\x78\xdd\x61\x2d\xb1\x91\x27\xb3\xa9\x65\xfc\x92\x64\x5b\xe1\xbd\x9e\xc5\xb9\x44\x4b\x40\x77\x69\x03\x12\x6d\x8e\x67\xb8\x4b\xff\xc3\xc5\x5c\x2d\xd9\xbb\x73\x56\xd8\xe4\xe7\x93\x02\x71\xed\xc3\x01\xe2\x9c\x10\x71\x0e\x89\x2a\x93\x59\x9a\xc1\x56\x87\x27\x38\xca\x04\xdd\xf4\x6f\x32\x9c\x36\x1b\x55\x26\x8c\xff\x50\xd5\x26\x92\x95\x7b\xf0\xe9\x23\x93\xa0\x4d\x20\xaf\x4f\x49\x15\xcd\xa2\xf0\xbf\x67\x18\x85\x01\x8e\xb2\x70\x18\xaa\xac\xb8\xd4\x64\xf3\xe1\x29\x31\xc5\xd0\xa4\x9d\x6d\xfa\xb0\xed\x48\x9b\xd0\x6b\x9d\x0a\xf8\x20\x57\xfc\x7e\x58\x5d\xf1\x33\xc2\x59\x57\xf8\x00\x73\xd0\x7f\xde\x97\x0a\x0c\x61\x95\x0f\xa3\x35\x0a\x82\xb9\x21\xfe\xd8\x6d\x12\xd9\x95\x67\xee\xbf\x3d\xf3\xda\xa5\x72\x25\x33\xf5\x6e\xbb\x54\xc2\xb6\xd7\xb2\x16\x3e\x26\x02\xc6\xd0\x1f\x64\x71\x72\xe3\x51\x8d\x32\x19\xd8\x67\x84\x4f\x13\x59\x3f\x1e\x22\xd1\x9b\x8d\x0d\xf4\x23\x8d\xc8\xf4\x23\x94\x79\xb6\xba\x8a\x7a\xf1\x64\x12\x47\xff\x75\xfc\xfc\xd9\x33\xa3\xf3\xf9\x2f\xd6\x00\xc7\xa9\xf2\x23\x19\x86\x04\xff\x58\xf5\x90\xf4\x0a\x47\x83\xe5\xbe\x9f\xe2\x4e\x4b\xfb\x30\x09\xda\x7a\xd1\xcb\xe9\xe7\x60\xa8\xbd\x1c\x84\xd3\x11\x4e\x96\x29\xe4\xea\xeb\xe7\xcf\x6e\x9f\x3f\xc3\xe3\x14\x23\xa9\x33\x54\x63\x4e\xfb\xc2\x87\xe1\x47\xf4\xd3\x4f\xec\xc3\x8a\x3f\x09\x44\xdf\x36\xf7\xb7\x9e\x3f\x7b\x46\x3f\x54\x4e\x39\xce\x1e\x52\x51\x85\x67\x82\x21\xfd\x40\x11\x83\xdf\x32\x3e\x67\x62\x94\x65\xc4\x58\x43\x34\x1c\x06\xaa\xf4\x93\xf8\x2a\xc5\x49\xf5\xf9\xb3\x67\x62\xc4\xe2\x38\x5b\xe9\x25\x37\xd3\x2c\xfe\xaf\x63\x5a\xf5\x16\x8e\x4f\xf2\xfe\x23\xbe\xa3\x3f\x9f\x3f\x7f\x56\x51\xcf\x63\xcf\x10\x55\x89\x1c\x8f\xe2\x24\x1b\xcc\xb2\x94\xbe\x21\xcb\xa6\x87\x36\x10\xaf\xfb\x5a\x7a\x7d\x3e\x0e\xfb\xe4\xd3\xca\x38\xec\x4b\xef\x41\x1b\xd6\x83\x4e\x91\xaf\xa4\xd4\x8a\xf4\x4e\x81\xe0\x8f\x2f\x62\x00\x41\x7e\xbc\x7e\x2e\xb0\xf8\x10\xc7\x9f\x67\x53\x94\xf9\xfd\x31\x96\x30\x39\x7e\x7b\xf0\x1b\x3b\xf4\x89\x77\x7b\x1f\x7f\x39\xb7\xbd\x3f\xfe\xf4\xf6\x7c\x7f\xef\xb7\xf3\x9a\xeb\x43\xdd\xf5\xa1\xe1\xfa\xd0\xb4\xb6\xed\x6a\x47\xfe\x68\xb4\x25\x7f\x34\xda\x93\x3f\xf2\x36\xc5\xd0\xf4\xe2\xc9\x94\x9c\x14\xc7\xe6\x10\xd9\xa6\x54\xab\x15\xc4\xb3\x3e\x11\xfb\x49\xad\xbc\x00\xb0\x58\x19\x0b\x24\x9b\x2a\x84\x10\x4e\x10\x85\xe8\x0d\x6a\xb4\x3b\xaf\x51\xb8\xb4\xa4\x80\x17\x42\x22\x7a\x83\xea\x8d\x75\xe3\x1b\xf9\x0b\x4e\xc3\x33\xb4\x41\x60\xbc\x41\xf5\xd7\xea\x77\x7a\x97\x5a\x50\xab\x42\xab\x55\xd1\xef\xa8\x76\x5d\xaf\xf7\xf5\xfa\xf9\xe3\xed\x73\xa5\xd7\xbf\xfa\xe3\xcf\xe8\xdd\x4e\xa5\xf1\xfb\x7a\x55\xed\xed\x35\x0d\x91\xa8\xbe\x0b\xb5\x97\x0b\x8d\x80\x34\xc8\x69\x3f\xbe\x56\x3f\x82\xa5\x01\x69\xf3\x3a\x44\xbf\xa3\xca\x75\xde\x21\xf6\xbb\x21\xfd\x6e\x4a\xbf\x5b\x55\xad\xb3\x00\xa5\x92\x5e\xa3\x9f\x7f\xfe\x19\xad\x43\xc9\xf4\x1a\xfd\x84\x6a\xd7\xc3\x21\x1d\xa0\x4e\x53\xab\x42\x56\xc7\xe9\x35\x19\xc8\xf4\x5a\xfb\xc4\x17\xcf\x69\x0a\xdf\xaf\x5f\x3f\x77\x76\x6a\x32\x1b\x67\xe1\x74\x1c\x0e\x40\x4d\x60\x76\xef\x9a\x90\x71\x70\x7a\x7d\xf6\xda\xf2\xad\x45\xbf\x35\xac\x1f\xd7\xe9\xc7\xd6\x59\x41\xeb\xe9\xac\x8f\x40\xc0\xf1\xd0\x24\xbc\x46\x83\x78\x3c\x9b\x44\xa9\x42\xfd\x32\x4c\x22\x29\x54\x02\xe8\xd5\x4b\x42\x33\xb5\x3a\x1f\x29\xf6\x58\xab\xd7\x6a\xfa\xd0\x8a\x95\x4c\x07\xab\x92\xc1\xc4\xb4\xaa\xe8\x2b\xf9\x4d\xc7\xdb\x51\xa5\x2e\x57\xa9\x77\xa4\x2a\xf5\x8e\xab\x4e\x43\xae\xb3\x5e\x45\x79\x9d\x86\x31\xeb\x82\x1b\xd0\x3a\x59\xc1\x48\x85\xd1\xa5\x3c\x5a\xe4\xb1\xf4\x88\x5d\xaf\x4b\xe3\xc3\xc8\xb3\xc5\x5e\xd5\xf8\x8b\x86\x32\xa4\x85\x23\xaa\xf0\x47\x46\x63\x65\x86\x55\x61\x9d\x4a\xbd\x39\x63\xab\xb0\x55\xa5\xe2\x9c\x01\x56\x58\x2e\xab\x58\x34\xca\x70\x5b\x00\x8a\x60\x9c\x98\x9c\xf0\x87\x6b\x2b\x13\x64\x0c\x60\x63\x01\x0e\x08\x55\x1a\xe8\x77\x14\x9c\x92\xff\x5d\xaf\xa3\xdf\xd1\x75\xe3\xec\x4c\x5f\x48\x50\x36\x44\xbf\x6f\x40\xc1\xeb\xd0\x28\xa0\x30\x49\xf8\x79\x0b\x87\x5a\xb1\xaf\x1c\x26\x78\x40\x3b\x17\xa0\xa3\x41\x1c\xb1\x0d\x26\xdf\x95\x8e\x7a\x07\x1f\xc9\x1e\x51\xbb\xae\xd5\x3c\x54\xbb\xae\xd5\xe1\xdf\x06\xfc\xdb\x82\x7f\xd7\x3d\xa0\x05\xf2\x6f\x03\xfe\x6d\xc1\xbf\xeb\xf0\x6f\xbd\x4f\xfe\x6d\x76\xf2\xcd\xec\xe5\x4b\x86\xd4\x4b\xb4\xb9\x7d\x4c\x03\xb2\x23\x2a\x0e\x21\x22\x10\x24\x61\x36\x9a\xac\xf0\x32\xab\x39\x2a\xa4\xf4\x06\x13\x1f\x56\xe8\x83\x24\x61\xac\xe0\xeb\x8c\x86\x0f\x10\x5d\x3e\x0f\xe2\x23\x9c\xe2\xac\x8b\x1c\x5b\x24\x1b\x84\xe3\xcf\xe1\x94\x99\xfe\xc6\x43\x14\x1d\xc5\x70\x1c\x1b\xf9\x29\xea\x63\x1c\x81\x7b\x00\xbb\xe0\xf2\xa3\x00\x6c\xf8\x82\x30\x40\x51\x9c\x31\x3b\x4c\x93\x14\x68\x36\x17\x0e\x89\xdb\x8b\x9e\x7f\xc6\x37\x87\x49\x18\x27\x47\xd4\x04\x78\x63\x23\x7f\x6f\x25\x1d\x6e\x17\xa6\xcd\xa9\xd9\x01\x55\x7c\xe3\x7f\xdc\xe2\x70\xc3\xde\x7c\xfe\xd6\xc2\x9f\x3f\xe3\x9b\x5f\xe3\x04\xac\x18\x3f\xe3\x9b\x95\x2b\xf2\xdb\x5e\xec\x38\xfc\x82\x59\xa9\x34\xbc\x78\x4b\x18\x10\x5a\x45\xad\xa2\x65\x24\x1c\x01\x12\x18\x20\x13\x2c\x1f\x39\x8e\x63\xfe\xcc\x1b\x5c\x42\x9d\x52\x2d\x90\xfe\xa7\x83\x11\x26\xc7\x0f\x44\x44\x68\x4b\x1f\xd2\xa3\xf8\x8a\xc0\xae\xf0\x66\x96\xc8\x2e\xfd\xb2\xb0\x0f\x32\x5c\xfb\xb0\xf0\x46\xa5\x71\x96\xde\x9d\xea\x4b\x35\xb7\x11\x25\xe8\x50\xd1\x83\xfe\x7c\xc3\x30\x64\xcf\x16\x29\x04\x31\xb2\x13\xe5\xe9\x20\x59\xcb\x91\x3f\x09\x95\x53\xa8\x73\x46\x47\x16\x66\x9c\xbd\xb1\xb0\x1a\x37\xc3\x42\xd2\x7e\x62\x00\x87\x70\x3a\xfa\x50\xca\x68\xff\xc0\x10\xff\x97\x40\xdc\x89\x39\x9b\x85\xa3\x38\x43\x84\x24\xdd\x85\x32\x79\x0f\x50\xb7\x80\x42\xc8\xc7\xb3\x7e\x19\xc8\x20\x3e\x71\x98\x67\xd2\xde\x06\x1f\xf2\x9d\x8a\xc9\x68\x67\xd2\x2e\x26\x97\x58\x57\x0a\x00\xa6\x0c\x32\x7b\x3d\x07\xdb\xfd\xf0\x1a\xd8\x76\x11\xb6\xbf\x6f\x00\x13\x3f\x65\x83\xbc\x9a\x53\xc7\x57\x54\x63\xa8\x5b\x26\x1b\xe5\x13\x0e\xa4\xc5\xd6\xdd\xcf\xa8\x43\xf8\x99\x36\x61\x68\x63\x03\xb5\xe6\x4d\xda\x77\x37\xb4\xf6\x3e\x3b\x46\xdc\xb5\x66\x0c\x5a\x67\x43\x72\x86\x7e\x27\xb2\x84\xb9\x88\xe6\x72\x73\x59\xa6\x2b\x66\x33\x61\x74\xf9\xde\xc2\x69\x8c\xd7\x6e\x66\x43\x8a\xe6\xfc\x46\x3c\xe5\x2c\x87\xbf\x72\x70\x1d\x99\x61\x31\x3e\xba\x2c\xea\xd8\x88\x17\x8e\x8c\xbc\x99\x7f\x15\x10\x8d\x93\x9d\x3c\x2c\x67\x6a\x59\xc1\xcd\x43\xfc\x0d\x6a\x81\x27\x0b\x7d\x28\xa2\x7d\x75\x2e\x4e\x39\x04\x26\x69\x2e\xd8\x91\x02\x60\xaa\xd0\xad\xae\x21\x42\x8a\xaa\x70\xed\x58\x4a\x67\xe8\x77\xf7\xe2\x74\xfc\xa9\xc2\xb7\x7d\x05\xea\x08\x34\x4f\xd5\xa5\x68\x9f\x03\xa7\x24\xeb\x49\xd3\x83\xa3\x41\x72\x33\xa5\xa6\xb1\xb2\x9c\xb7\xef\xa1\x78\x38\x4c\x71\x66\xcc\x0c\x5d\x23\x41\xdc\x13\xf5\xf2\xc2\x9e\xb9\x57\x7b\xf9\x09\x31\xff\x59\xcf\x7f\x36\xf2\x9f\x4d\x0f\x58\x8c\x7c\xca\x50\x70\x0d\xf0\xa2\xb8\x12\xae\x79\xe5\x4f\x51\x23\x0a\x40\xf6\x6c\x65\x23\x87\x10\x43\xe8\x7b\xff\x94\x82\x21\xf2\x8b\x3e\xa4\xca\x37\xb5\x6c\xb3\xa0\x6c\xd3\x7a\x24\x2a\x33\x84\x2a\xad\x7a\x2a\x81\xaa\x8f\x75\xf5\xb1\xa1\x3e\x36\x3d\xa1\xb0\x30\x36\xef\xd5\x55\xb4\x47\x4e\xbe\xdf\xc5\x18\xd9\x27\x5d\x19\x26\xeb\xac\x7b\xe8\x7e\xe4\x66\x23\x1a\x76\x20\x28\x2d\x59\x5b\x06\xf6\x1d\x66\xc1\x42\xe1\x46\x92\x8a\xea\x04\x53\x8b\x8e\xab\x26\x0d\xd6\x19\xbc\xfe\x5d\x61\xb6\x35\x9b\x06\x28\xad\xeb\xd3\xa1\xd5\x32\xe6\x07\x6a\x35\xd4\x5a\x0d\xbd\x96\x55\xdb\x94\x36\xf5\xe9\xd4\x6a\x35\x6d\x6a\xa8\xf7\xda\xd9\xc1\x7e\xf4\x97\xb7\x40\xdb\x89\xe1\xc8\x72\xc6\x11\xfb\x2f\x1d\xd5\x0d\x54\x7f\xcd\x7e\xbe\xe1\x33\xc4\x5e\x38\xf6\x5d\x98\xe3\x70\x98\x01\xa5\x7b\x0e\x45\x59\xe1\xc4\x71\xd4\x33\x32\x79\x92\xba\xa6\x26\x24\xaf\xdf\x25\x45\x57\x25\xad\x1b\x72\xd7\xef\x92\x52\xab\x92\x36\x74\xa9\xeb\x77\x49\x7f\x95\x36\xa5\xd7\xc6\x36\xbc\xb4\x64\xdb\x00\x00\xb9\xba\x8a\x5c\xdd\x81\x5c\x63\x0e\x72\xcd\x42\xe4\x6a\x77\x44\xae\xa1\x22\xd7\x70\x20\xd7\x9c\x83\x5c\xad\x10\xb9\xfa\x1d\x91\x6b\xaa\xc8\x35\x1d\xc8\xd5\xe6\x20\x57\x2f\x44\xae\x31\x17\x39\x2b\xe9\x7e\x9a\x82\x11\x51\x9a\xf9\x19\x36\x0b\x00\x3b\xc9\x6a\x96\x8e\x01\xcb\xc8\x74\x3d\x1a\x7c\x21\x73\x91\x35\x6c\x5f\xc8\x40\x64\xba\x76\xdc\xaa\x44\xb1\xae\xa7\x39\xbc\x0f\x96\x4f\x85\x9e\x3c\xa4\xb5\xa3\x9f\x5a\x2c\xcb\x47\x3f\xb6\x98\x2b\x48\x39\xb7\xe4\x4b\xa8\x5a\x8e\x12\xc4\xfa\xe1\xd8\xd5\xdd\xd8\x99\xeb\xc7\xc0\xce\x58\x42\x2a\x76\xb5\xbb\x60\xd7\x90\xb0\x6b\xb8\xb1\x33\x17\x90\x81\x9d\xb1\x86\x54\xec\xea\x77\xc1\xae\x29\x61\xd7\x74\x63\x67\xae\x20\x03\x3b\x63\x11\xa9\xd8\x35\xe6\x63\x67\x52\x2b\xe6\x91\xad\xed\x72\x09\xdd\x86\x2d\xeb\x48\x17\x72\x8c\xe5\xa4\x6e\xae\x96\x55\x65\x88\x3e\x4d\x97\xec\xc3\x8e\xc2\x5d\xd4\x68\x77\x56\x9b\x0d\xa6\x81\xae\xda\x54\xc1\x5c\x62\x11\x02\x52\xca\x3c\x87\x99\x6a\xf8\x45\xca\x12\x3e\x21\xc8\xe1\x3d\xf4\x07\x58\xe8\x88\x05\x90\xff\xc4\xd7\xfe\x64\x2a\x4e\xca\xf9\x07\x3e\xa7\x14\x56\x86\xaf\x33\xe9\x76\x7b\x65\x73\xfb\x78\x85\x9d\x23\x2a\x13\x6e\x92\xfe\x19\xdf\x78\x68\x30\xbc\x10\xd2\x7c\x0e\x65\x3a\xf6\x09\x12\xd7\x19\xd2\xa1\x30\x09\xbf\x92\xb7\x63\x03\xc4\x74\xda\x3d\x8b\x12\xfb\x9c\x86\x4d\xdd\xc5\xe3\x29\x4e\x2a\x9b\xdb\xf4\x5a\x9f\xea\xec\x9f\x3f\x63\x36\x2b\x72\x93\xaf\x9f\x3f\x87\x10\xb8\x60\x40\xa2\x58\x15\x74\xdb\x0d\x8f\xdb\x25\x74\xdb\x60\x3b\x22\x59\x26\x74\xdb\x2d\x2f\x37\x49\xe8\xb6\xc1\x87\x71\x12\xb4\x7f\xec\x76\xea\xb7\x67\x5e\xbb\x71\x2f\x6b\x91\x6f\x69\x26\xf2\x68\xc6\x1c\xdf\xd0\x2c\x83\xae\x84\x97\x88\x19\x50\x90\xe6\xd1\x20\x9e\x4c\xe3\x08\x62\xae\x93\x6f\xab\xcf\x9f\x89\x79\x1f\x87\xfd\x15\x56\xf4\xeb\x57\xd9\x00\x40\x78\x7d\x3e\xb0\x71\x87\x9f\xe2\xdc\xaa\xc3\x4f\xb1\xf4\xed\xd7\x38\x09\xc0\x2f\x5d\x14\x10\x6f\x64\x08\xb3\x21\x18\xfc\x01\xad\x6f\xf2\x5b\x9e\x1c\xa6\xf5\xb3\x82\x19\x06\xd7\xaa\x1e\x59\xa8\xd2\xfb\x4f\xd9\x70\x1d\xa0\xe0\x68\xb0\x42\x1e\x34\xac\x3b\x2d\xf1\x95\x3e\x16\x19\xa2\x88\x2f\xdb\x97\xd3\xf7\x5b\x3b\xf9\x65\x13\x7d\xb6\xde\x60\xf5\x53\x6a\x9f\x47\x96\x15\xbf\xc5\xca\xf0\x64\x3a\xf6\x33\x1b\x83\x12\x51\xa6\xff\x8c\x58\x44\x1e\xae\x41\x05\xaf\x02\xc1\xeb\x40\xef\x17\x7e\xc1\x2b\x3c\xc2\x64\x17\xb5\x50\xa5\xde\x58\x47\xfd\x30\x4b\xab\x45\x00\xc3\x4b\x0b\xbc\xbd\x5f\xee\x0a\xee\x7c\xfb\x63\xef\xfc\xb7\x9d\x83\xa3\xfd\xf3\xfd\x83\xad\x6d\xb4\x09\xb1\x0d\x32\x3f\xca\x50\x82\xa7\x09\x4e\x71\x94\x85\xd1\x05\x57\xc4\x10\x32\x9c\xc4\x41\xde\x77\x2b\xcc\xad\xed\x52\x30\x19\x3b\x35\x60\x4a\x97\x82\x9a\xc9\x91\x78\xb4\x53\x94\xe5\x92\x30\x9f\x4d\x8a\x6e\x0f\xfc\xbe\x67\x09\x18\x3c\x88\x24\x1f\x72\x11\xa5\xb8\xd4\x3b\x41\xf7\x64\x0e\xd0\xc9\x08\x93\x51\xcf\x62\x34\x63\x7e\x02\x84\x05\x20\x52\x18\x40\x2b\x20\x57\xf3\x87\xc1\xf0\xa2\x0b\xa4\xcb\x71\xad\xca\x3b\xaa\x81\x2d\x6c\x17\x29\x85\xcd\xc8\x2f\x8c\x5c\x93\x61\x43\x9f\xda\x63\x4a\xb8\x13\xd2\x23\xc8\x7f\xc6\x37\x2b\xd6\xb2\xdc\x35\x74\x30\xbc\x40\x95\x03\x68\xc5\x1f\x57\xa1\xce\xc0\x36\x78\x25\xc7\x40\x6d\x8b\x07\x12\xa5\x13\x7a\x4b\x48\x84\xf7\x8e\x10\xca\xa0\xa8\x4f\xe4\x5c\x11\x0e\xdc\xdf\x55\x29\xc1\x2c\x80\x14\x69\x41\xde\xe3\xf9\xd5\xf3\x0a\xdd\xa6\xb7\xe9\x30\xc7\x49\x85\x5d\x9e\xc1\x10\x7a\xe8\x4f\x14\x5e\x76\x51\x78\x99\xf3\xc6\x5b\xc5\xf4\x40\x99\x6f\x15\x52\x57\x89\x0b\xc5\x24\x07\x5d\x03\x20\x67\x0e\xa1\xf5\xd9\x8d\xb3\xba\x56\x2d\xb2\x87\x2e\xa1\x95\xa4\x27\xc7\x42\x7c\xa2\xa7\x87\xa5\xa7\x2d\xfc\x50\xf4\x24\x20\xdd\x8f\x9e\x54\x3e\x7d\x07\x7a\xda\x8b\xc2\x2c\xf4\xc7\xe1\x17\x9c\x22\x1f\x45\xf8\x6a\x7c\xc3\x30\x0c\xd8\x70\xcc\xa7\x25\xbe\x6b\x5c\x0f\xe3\x64\xb2\x1f\x07\x18\x6d\x53\x67\x35\x88\xd3\x9c\x73\xba\x38\x91\xe9\x14\xac\xab\xc1\xcf\x8f\x53\xad\xd8\x64\xec\x64\xf8\xdd\x91\xec\x83\x91\x55\xc5\xfc\x60\xe3\x14\x77\x24\xb8\x30\x0a\x15\x0b\x1b\x31\x4d\x12\xb9\x58\x54\xd4\x9b\xd3\x29\xa1\x05\x18\x2d\x9e\x6e\x3a\xb5\x5c\x33\x90\x21\xde\x10\x3f\xf9\xa6\x48\x69\xd0\x3c\x15\x67\x44\x72\xa6\x86\xf5\x71\x32\xa1\xd3\xee\xdb\x74\x37\x94\xbe\x73\x92\xda\xc8\xc9\xeb\xb5\xad\x24\xb5\xa3\x01\x5b\x19\xeb\x59\x3c\xa4\x84\x4e\x3d\x00\x6c\xfd\x00\xfb\xa2\x4a\xe9\x85\x03\x36\x3a\x2a\x1f\x86\x60\x0e\xa9\x68\x09\xb4\x67\xf7\x24\x1f\xb6\x04\x4d\xdc\x94\x19\x4e\xca\x18\x51\x51\xa3\xa2\xc0\xcf\x7c\xd4\x07\xd9\x4b\x2d\xe1\x90\xc7\x00\x34\x4d\x75\xc1\xdd\x9d\x75\xc0\x87\x38\x81\xb9\x1c\xc4\xd1\x20\xc1\x19\x5e\x66\xc3\x31\x8e\x2f\x14\xa6\x2c\xdd\x4b\x1d\x2d\x36\xd6\x10\x50\x03\x30\xa7\xfe\x2d\x8c\xa7\xe0\x40\x62\x29\x38\x58\x60\xd3\xfb\x9a\x32\x57\x18\x02\x94\x29\x3b\x09\x6f\xe0\x6d\xb0\x06\x24\xf0\x25\x76\x2e\x89\x3f\x09\x58\x34\x6a\x16\x8b\x46\x10\x46\x17\x0f\xc0\x4d\xf2\xce\x6f\x70\xf2\x60\xf0\x2b\x2f\x48\x9b\x2f\x54\x32\x29\x53\xef\x8a\x63\xee\xa4\x30\x56\xb2\xab\x85\x79\xa5\x43\xe7\xe0\x1e\x38\x0a\x6c\xb3\xef\xc3\x17\xb9\xba\x8d\xa6\x68\x7b\xc8\xbf\xf4\xc3\xb1\xdf\x1f\x63\x6a\x86\x98\xba\xb7\xc5\x73\xde\x99\xd2\x54\xb5\x13\x46\x6c\xe3\x2b\xdc\xa7\x18\x5c\x75\x9f\xf9\x18\x67\xcc\x3d\x9a\x46\x4d\xa3\x90\xf2\x5d\x03\x85\x29\xc2\xc3\x21\x1e\x64\xe1\x25\x1e\xdf\x20\x1f\x05\x38\xcd\x92\x19\x3c\x7b\x28\xc1\x7e\xb0\x1c\x47\x03\x5c\x6a\x9f\x29\x4b\xbd\x80\xc6\x63\xd1\x30\x05\xfe\xd8\x94\xcc\x47\xb2\x52\x9e\x88\x45\x95\x45\xa9\x5f\x54\x9c\x4f\xfe\xbc\x68\x79\xfa\xdf\xc9\xe7\x62\x06\x85\xd4\x12\xe1\xb0\x10\x00\x2a\x5c\x2d\x4a\x51\xcb\x45\xc9\x02\x0c\x19\x02\x22\x11\x54\xd9\x82\xc3\x01\x0b\x98\xc9\x39\xf5\x8e\x34\x21\xd6\xc5\x67\xd6\x9e\xab\x6c\xae\x37\xd6\x57\x9b\x0d\xf9\x13\x55\x89\xd8\xbe\x68\x72\x50\x17\xd5\x95\xaf\xaa\xfc\xdb\x45\x8d\x32\x67\xa7\xd4\xaa\xca\xf6\xe7\x2b\xb2\x91\x73\x6d\xf2\x53\x0b\x1b\xe9\x93\x11\x96\x84\x02\x96\x69\xcb\x47\x23\xd0\x1a\x13\x21\xb3\xc4\x52\xe4\x22\xec\x66\xc4\xf1\x81\x08\x03\x7c\x59\x13\xa1\x89\xad\x6b\x4b\x87\xbe\xc1\x61\x89\x59\x7b\x9b\x2a\x4f\x4d\x47\x6e\xc8\xb6\xce\x55\xa6\xd4\xeb\x3a\xfd\xa6\xc8\x9f\xf8\x94\xe2\x31\x1e\x64\xb4\xe1\xe3\x2c\xf1\x33\x7c\x71\x53\x71\x99\x6b\x4b\xda\x67\x10\x17\x37\xd0\x0b\xca\x4a\x5f\x38\xcd\xc3\xd8\x6c\x1c\xfa\x69\x4a\xd8\xc4\x5b\x3f\xc5\x81\xe2\x31\x27\xff\x15\x1b\x87\x31\x50\xc7\x38\x81\x03\x17\xd9\xd5\xdc\x90\x8a\x17\xb9\x9e\xdc\x8f\xdd\x67\x14\xd8\xa8\xbb\x90\x62\xe4\x24\x33\x36\xf3\x86\xa5\xc8\x6e\x34\x8f\x02\x66\x9f\x07\x71\x71\x43\x51\xf4\x90\xfb\x02\x47\x1f\x03\xcf\x61\xe9\xc9\xc8\xbe\x6b\xf4\x5f\xbb\xcf\xb9\x17\xda\xea\x4d\x91\x87\x0a\x6f\x8c\x74\xcc\x2d\x13\xaa\xb3\x6d\x99\x4b\x56\xaa\x4c\xc3\x6b\xbf\x7a\x53\x75\xd8\x69\x96\x60\x7f\x72\x27\x55\x36\xc8\x50\x4c\xf9\x2c\xdb\xe0\x37\x1b\xcb\xfd\x90\x1a\x6c\xab\x27\x1a\x2a\x9d\x40\x1c\x6b\x49\x33\x5d\x47\x95\x66\x43\x55\x4c\x4b\x0a\xdf\x63\xc0\x4f\x53\xfb\xea\x2f\x0b\x3c\x42\x76\x2c\x7b\xad\x6d\x87\xe5\x22\xe2\xd4\x4f\xe0\xb8\x65\x13\x10\xcd\xed\x0d\x8e\x37\xb9\x75\x15\x17\x1a\x7f\xf8\xe1\xc5\x70\x3c\x4b\x47\x2f\xca\x6d\x73\x14\x8a\x6b\xa3\x13\xc3\xdc\x45\xf5\xa2\x79\x85\x73\x2d\xa4\x35\x9d\xca\xb7\xa5\xb2\xf2\xfc\x7c\x42\xcf\xbe\xbd\x15\xf6\xe3\xcf\xdb\xf9\x14\xa2\x78\xec\x40\x3d\x83\x4a\xa4\x36\xa4\xdb\x4d\x76\xd0\x36\x9c\x83\xd9\x7b\x59\xe9\x5d\xa4\xa0\x97\x55\x94\x13\x9e\x9d\x2b\x97\xaf\x17\xde\x4d\x37\xd5\x1e\x59\x15\x82\x7a\x6a\x99\x5c\xc1\x0f\x54\xfd\x0d\xf6\x43\x3e\x53\x7c\xbb\x03\x3d\x6c\xef\x6d\xcf\x50\x45\x73\x8e\x12\x5e\x52\xaf\x9d\xbb\x68\x9e\x73\x18\x85\xba\x42\x51\x97\x2b\x9a\xa4\x7a\x77\xd2\x38\x8b\xe9\xcc\x0f\x48\xff\x33\xa7\x33\xd7\x04\x2f\x38\x9d\x56\xc5\x6f\xc9\xe9\x14\x75\xef\x31\x9d\x45\x0a\xdf\x72\x57\x07\xdf\x74\x3a\xef\x3d\x5d\x05\x4b\x60\xce\x7c\xe9\x7a\xd3\x82\x49\xa2\x9b\x89\xd0\xf3\x0e\x6c\x62\x1d\xb3\xba\xbe\x44\x1b\x28\xbc\x94\x67\xab\x68\x8b\x60\x3b\x26\x0d\x2c\xdd\x1b\xf9\x61\x04\x39\x4f\x5c\x77\xad\x6f\xc1\x6e\xe0\x9c\x77\x1e\x6d\xb8\x83\x0f\xe8\x2a\x36\x65\x07\x21\x75\x0d\x62\x90\x86\x26\x6f\x4c\xdb\x25\xc4\x9d\xe8\xeb\x22\x8e\xf2\xb6\xc7\xb7\x03\xed\x24\x24\x35\xa1\xcc\x1d\xe9\xd5\xdb\x9e\x65\xef\x31\xc1\xd3\x26\x0e\x45\xfc\xcf\x8c\xab\x31\x28\x95\xfa\x19\x33\xea\x5e\xd1\xeb\x18\x30\x34\x9a\xa5\xd2\x91\xd0\x8a\x30\x61\x29\xe6\x32\x12\x52\x39\x21\xb2\xde\x90\x30\xbb\x2c\x02\x84\xfd\xbc\x1a\x61\x16\x7a\x9f\xe2\x07\x91\x3c\xd3\x12\xc8\x99\x0b\xc3\x5e\x90\xfc\xc1\x54\x32\x51\x87\x7a\x03\x40\x7e\x3c\xe8\x82\x70\x6d\xd0\x65\x59\x79\x32\x50\xae\x02\x34\xcc\xe4\x55\x28\x4e\x5b\x68\xab\x03\x2c\xd2\x6f\x48\xe4\x85\xe4\x30\x9c\xcd\x85\x58\xa1\xc9\x11\xaf\x1c\xe6\xac\xbf\x1d\x1c\xc1\x79\x99\x11\x9d\x59\xe6\x3a\x4e\xa0\x5f\xb9\xa2\xdb\x43\x4a\xbf\xbc\xbc\x59\x9b\xd0\xcf\xf0\x90\x7d\x5d\x2a\xfa\xe8\x5a\x31\x3b\xc2\x13\x0c\x52\x38\xec\xae\x94\x04\xd8\x55\x14\x9c\xf6\xc1\xa1\x1d\x5e\x9b\xd5\xb9\x04\x8b\x2f\x79\xdc\x79\xca\x4c\x69\x42\x79\x8e\xb7\x30\x05\x74\x76\x40\xf6\xdc\x99\xbb\x6e\x03\x5c\x62\xdd\x8a\x7d\xea\x69\xdd\x3e\xad\x5b\x74\xf7\x75\x7b\x9f\xd5\x01\x16\xc2\xa3\x30\x5d\x78\x6d\x58\x31\x61\x14\x0d\x5c\xe4\xb7\x83\x23\x27\x07\x90\x3d\xc8\x0c\x0e\x70\x5f\xb6\x63\xc5\xec\x24\x1f\x9a\x3e\x1e\xc4\x13\xb6\x74\x08\x5b\x08\xe3\x59\x5a\x9e\x79\x88\xc1\x2a\xcb\x1e\x04\x29\xf1\x6e\x54\x9c\xb8\x2f\xe4\x01\x05\x22\x12\x97\x96\x6c\x1e\xfe\xa3\x38\x4e\x31\x9a\x84\xd7\x44\x16\xb2\xf4\x0f\x3c\x41\x4d\x21\x0d\xc9\x84\xc8\xa4\x30\x17\xd9\xc5\x97\x20\x9d\x92\x93\x4e\x3a\xeb\xa7\xf8\xbf\x67\x38\xca\xac\x2a\x06\xa4\x8a\x76\x52\x5a\x0f\x75\x14\x9d\xaa\x41\x19\x25\x6d\x56\xe6\xab\xfa\xc9\xce\x66\xc3\xca\x16\x23\x29\x5f\x6d\xd6\x48\x49\xe4\x0f\x26\x30\xb7\x1e\x0f\xcf\xd0\xef\x1b\xb4\xde\x69\x58\x18\xba\x24\xff\xcd\x4d\xa0\xdf\xf6\x58\x79\x25\xa0\x89\x24\xda\x1e\xfa\x41\x40\x26\x70\x8e\x02\x64\x0a\x69\xae\x7a\x2b\xf4\xbf\x76\xf5\xc7\xe1\xfb\xde\x31\xfa\x5f\xed\xd5\x35\x34\x65\x40\x53\xa6\xcb\xb3\xc1\x3c\xfc\x3c\x48\xd7\x40\x4e\x9e\xfa\xc1\x0a\x7f\x2a\x90\x8d\x0f\x7d\x7e\xfd\x3c\x4b\x79\xec\x7c\x11\x08\x85\x99\x2b\x43\xe0\x64\x81\xc7\x42\xf6\x57\x00\x59\xbe\x7d\x26\x68\x59\x2b\xd9\xf5\x78\x2c\x04\x94\x74\x1f\x09\x80\x52\x11\xcd\x92\x0c\x0a\xc4\xb3\x7c\xe4\x63\xb3\x38\x7c\x89\x71\x25\xbf\xf2\xeb\x35\x4f\x8b\x9b\xa5\x5c\x30\xfb\x81\x7e\xb9\x76\x67\x06\x22\xaa\xd1\x58\x27\x1b\xd2\x78\xb9\x62\x86\xcc\xa2\x4c\xd0\x0e\xf8\x15\x99\x50\x23\x46\xb0\x06\x50\xfa\x62\x99\xe6\x9c\x16\x11\x56\xfe\xa5\x15\xb0\x35\x4b\xef\x85\x78\xbb\x66\xe8\x05\x9a\xea\x0d\xbe\x12\x7a\x81\x08\x28\x0a\x16\xb9\xaf\x8b\xf1\x9e\x39\xb8\x18\xef\xc1\xad\x45\x79\x3b\x17\xb3\x42\xa4\xd2\xe2\xf0\x05\x39\xfb\x51\xdb\x44\x21\x5a\x72\xb9\xe5\xcb\xd0\x69\x9c\x7b\xe9\x4d\x81\xf4\xaa\x61\x87\x36\x72\xdb\x77\x7e\xf8\x97\x41\x7b\x2a\x4a\x36\x33\x84\xcd\x20\xb0\x0f\x02\xcc\xf5\x20\x8e\x06\x7e\xc6\x61\x96\xd6\xc0\x7c\x8a\xa6\x82\xa1\xc0\x92\x1d\xf9\x01\x0d\x64\xc4\x16\xea\xb7\xe1\x32\xb3\x48\xe7\x33\xdf\x84\x23\x40\xb3\x25\xae\xdc\xa1\x9c\xce\x12\x6c\x7c\xe0\x1d\xce\x94\xcc\xc5\xd2\x22\x86\x18\xb0\x68\xec\xa7\x19\x3c\xcf\x5f\xd3\xb9\x78\x7d\x5a\x51\x97\xf3\x32\xaa\x57\xa9\x8b\xd9\x19\x73\x06\xb3\x79\x12\x53\xc1\xc1\x4d\x31\x39\xb8\x0d\x7d\x0d\x4a\x9b\x29\xdd\x36\x17\xd4\xf3\xff\x15\x17\x41\x36\x17\x05\xfb\xcd\x82\xed\x56\xa1\xe8\x1e\xe8\xe1\x8c\xfe\xf7\xe3\x00\xdf\x52\xf5\xe0\x89\x38\xad\xd1\x4b\x11\x38\x49\x48\xdd\xe9\xbd\xed\xb9\xa0\xb0\xb9\xba\x15\xf4\x45\x60\xe9\xc2\x86\x09\x11\x48\xde\x41\xe0\xe0\x47\xc0\x06\x40\x32\x9c\xd4\x08\x9c\x60\x0a\x98\x79\xda\xa9\x8e\xb6\x6d\x34\x71\xab\x78\x23\x2c\x60\x18\x48\x27\x5a\xfd\xd8\x93\xac\x0f\x8b\x6d\x00\x0b\x02\x9c\xa9\xf6\xa1\x16\x3f\x4e\x90\x9b\xc9\x08\x28\x6a\x51\xa4\x2a\x76\xc9\xf7\x09\xd8\x7e\x3a\xf0\xcf\x27\xd6\x3c\x0c\x18\xb6\xa4\x5c\xd2\x56\x8d\x4b\x9c\x27\x06\x02\x15\xb6\x44\xd0\x68\xc0\xa9\x5c\xbb\x9b\xb1\x4b\xfb\xab\x2f\x8b\x9b\x57\xad\x57\xaa\xe8\xe5\xea\xc2\x18\x08\x55\x8b\xe3\x2c\xf3\x1e\xe3\x29\xf2\x33\x34\xc6\x84\x0b\xc6\x11\x5f\x01\x2c\xcd\x07\xb5\x04\x85\xfd\x1a\x18\xae\xc9\xb7\x90\x38\xdf\x4c\xc2\x88\x1a\x89\xb2\x43\xbc\x11\x2e\x51\x7d\x64\x95\xe8\xf4\x49\xf8\x53\x42\x9a\x80\xfd\x31\x3d\xf2\x86\x97\xe8\xa7\x9f\xac\xfa\x78\x3d\x50\xc7\xe1\x9d\x74\x19\x39\x26\xaa\x32\xc5\x79\x3e\xd7\x9b\x2d\x7b\x25\xed\x16\x49\x73\x91\x44\x18\x4a\xb3\x57\x16\x82\xe6\xcd\x3d\x2c\x21\xaf\xae\x92\x83\x0c\xcd\xf7\xe5\x12\xb9\x40\x5e\x67\xa6\x5f\x20\x81\xc3\xef\xb9\x3a\x08\x7e\x15\x4f\x6d\x04\x5d\xa7\xe4\x3b\x5d\xc6\x3f\xde\xb2\x7a\x5c\xbc\xad\xed\x81\xe4\x37\x67\x06\xa8\x7c\x64\x6b\x6f\x9e\xe5\xdf\x3d\x2d\x15\xc0\xf4\x8e\xc9\x1e\x76\x33\x14\x34\x88\xc7\x63\x4c\xe9\x3f\x1e\x72\xd1\x00\x44\x4d\x0c\xc9\xf4\x8a\x44\x0f\x49\x14\x95\x9c\xbc\xc9\x36\x9a\xf8\x57\xd2\x2b\xab\x5f\xa2\xdd\xf5\x83\x3a\xa0\x0b\x21\xa5\x4c\xed\xfc\xe2\x11\x52\x3c\x30\x2e\x48\xeb\x93\xf5\x69\x98\xe3\xba\x00\xa5\xfe\x98\x62\x0f\x3f\x00\x18\xa8\x24\x7d\x1a\x7e\x14\x27\xe1\x25\x95\x55\x38\xc7\xb0\x02\xe4\x57\xa9\xb9\x9c\x2f\x59\x0e\x9a\xb1\x56\xcb\xc9\x35\x77\xe9\x59\xb1\x7c\x33\x18\xe1\xc9\xdd\xe0\xda\x05\x4e\xa6\x32\x07\x8b\xe9\xa1\x04\xcf\x0a\x82\x26\x65\xbc\xcd\x93\x36\xd2\x53\x0c\x15\xb1\xf8\x5b\x5d\x0c\x1b\xc4\xd1\x25\x4e\x32\x45\x86\xa5\xe9\xee\xb8\x31\x25\x58\x7c\x52\xeb\x3f\xb7\xdb\xea\x21\xad\xa2\x3a\xaf\x8a\x97\x25\xed\x61\xe6\xbb\x58\xa9\xa8\xcd\x3f\xd6\x09\xef\x26\x19\x1f\xcd\x4e\xd4\x8f\x44\x16\xab\x69\x9c\xa6\x61\x7f\x8c\xdd\x2b\xd6\xd2\xd4\x62\xce\x4d\xf9\x40\x99\xf6\xa0\xf4\x1b\x3f\x81\xff\x69\x40\x41\x42\x7d\x4e\x56\x70\x57\xfa\x9d\x3b\x3c\x59\x2b\x7d\xc6\x37\x5d\xd5\x2f\xca\x5a\x4c\xf3\x94\xb2\x17\x22\xcb\xb8\x0b\xff\xce\x29\x28\x56\x65\xd7\x74\xe7\xb2\xd7\x60\x22\xbc\x6e\x99\x60\x2f\x2c\xe4\x7a\xf5\xe8\xfc\xbe\x77\xbc\x66\xaf\x20\xb1\xf0\x96\xbd\x84\x58\x38\x12\x50\xfa\x6e\xe5\x60\x8a\xa3\xe3\xe3\x0f\x46\xb5\xf2\xce\x64\xf2\xf4\xdb\x05\xaf\x49\x78\xbd\x17\xa9\xe5\x4a\x9b\x1e\xd1\x55\x9c\x2e\xb6\x8c\x91\x73\xdd\x98\xac\x44\xf3\x0d\x74\x70\x13\x72\xa8\x73\x03\xe7\x06\xb6\xdc\x2b\x03\x76\x05\xf8\x1d\x0e\x43\x7d\x8d\x17\xc0\x81\x2c\x60\x29\x4d\x01\x06\xe9\xe3\x70\xee\x45\x99\x63\x1c\xc5\xf4\x8d\xc6\x00\x59\xd2\x7e\x5c\xc4\x3d\xca\x2e\x69\x8a\xbc\xb8\xa6\x63\x6b\x7b\x09\xbd\x78\x61\xf7\xad\xb0\x96\x5f\xc9\x62\x9a\x6f\xc8\xe5\xca\x31\xa7\x96\x83\x54\x9d\x84\xc9\x2b\xca\xc4\x29\xc6\xc6\x65\x55\x95\x97\x40\x5f\xbf\x52\x72\xcd\xeb\xac\xf0\x49\xbc\xe1\xc7\x5e\x43\x47\x63\x95\x93\x28\x95\xcd\xbb\xd7\xa0\xed\xc0\xd5\x86\xf8\x69\xbf\xdd\x60\x3d\xb7\x11\xa7\x0d\x34\x2b\x2e\x72\x19\xc3\xee\xa5\x0e\x62\xf1\x75\x87\x58\x75\xbe\x7b\xc9\x45\xbc\x99\xe5\x41\x3c\x99\xfa\x19\x6c\x2f\x65\x97\xa1\xbc\x2d\x68\x9b\x98\x24\xfe\x94\xdd\x13\x6d\xcb\xef\x2e\xc8\x3d\x94\xe1\x60\x4c\xdb\x3e\xe6\xe4\xed\x20\x64\x89\xba\x5c\xbc\x51\xa1\x6f\x51\xbc\x34\xf7\x9d\xa3\x96\x91\x23\x2d\x29\x4b\x30\xff\x62\x0b\xd4\x48\xc4\x5d\xad\x02\x79\x67\x3b\xc6\x42\x7f\xcd\x43\x2c\x29\xee\x54\xb5\x5c\x49\xd1\x6a\x0c\xed\xfd\x69\xed\xba\xdd\xec\xd4\x3b\x83\x35\x48\x6c\xd0\x69\x77\x5a\xed\x61\x7b\x78\x56\xe5\xaa\x78\x00\xcd\x1f\xf2\x7e\x38\xce\x91\x25\x50\x70\x8e\x85\xe3\xf0\x25\xea\xe6\x8c\x8c\x86\xb5\x59\x7c\xcf\x2b\x5a\x63\xb2\xbf\xd2\xa2\xc2\x23\x5f\x27\x39\x9d\xde\x79\xc9\xa8\x31\x1b\xf8\x82\xbe\xc3\x1a\x7e\xd8\x00\x0e\xa6\x30\xaa\x2d\xbd\xa9\x9f\xa4\xb8\xa2\x2c\xd4\x82\x8b\xc9\x24\x55\x14\x3f\x79\x35\xab\x57\x02\x29\x8e\x68\x0c\xaf\x39\x8b\x8e\x12\x86\x81\x4c\x91\x7a\xb5\x08\x22\xbf\x8c\x93\x0e\xc3\x2c\x29\x84\x01\xee\x04\xa7\x19\xb5\x6d\xf0\xc7\x96\x05\xaa\xc1\x3c\xad\x9d\xa1\x8d\x0d\x94\xaf\x3d\xf4\xd3\x4f\x7a\xbb\xa7\x75\x56\x86\xaf\x49\x97\x0a\x6a\xfb\x9a\x5e\x60\x98\x2d\x23\x95\xc3\x18\x8b\x5f\x6b\x91\x99\xf2\x34\x3c\xd4\xaa\x16\x58\xd7\xc5\x97\xec\x88\x0e\x57\x41\x39\x0c\xb3\xbc\x01\x7f\x0a\x0d\xd4\xf4\x5b\x6b\xa3\xb8\x72\xab\x53\xef\x94\x63\x14\xd6\xa3\x91\xe3\x18\xe4\x49\xa7\x13\x55\x34\x2f\xbc\x2b\xe2\x8b\xf0\x2a\xf1\xa7\x53\x90\x23\xfd\x8c\x35\x2f\xab\x4c\x90\x4f\x76\xfa\x54\xf2\x4a\x2b\x5c\xbd\x8a\xab\x8f\xe1\xca\x96\x3b\xfc\xd8\x3e\x95\x75\x20\xb9\xf3\x65\x8f\x10\x7a\xb8\x8c\x5f\x24\xd5\x73\x1d\x81\xdc\x5b\xd6\x59\xea\x10\x1a\x05\x94\x6a\xc4\x01\x23\xbf\xd8\xb1\x1c\x9c\x8a\x42\x44\xe9\xde\x8b\x80\x50\xd7\x10\xd5\xa4\x89\x2d\x0c\x2a\xc5\xae\x1d\xc8\xbc\x31\x6f\xba\xfb\x78\xa8\xe6\xca\x27\xcb\x51\xa7\xc0\xfb\x9c\x35\x4d\x6d\x50\xd8\xef\xdc\xef\xfc\x6f\x12\xc3\xc5\xbe\x85\x6d\xfe\xb5\x1b\x18\x59\x96\x76\x8d\x8a\xb9\xac\x84\x7f\xa5\xa9\x8d\x50\x5c\x2d\x1d\xa7\xb0\xc7\x6b\x30\x0f\x52\xa3\xab\x13\xbe\x69\xe3\x9e\x58\x6d\x0e\x69\xa0\x40\xd9\x61\x71\x8e\x75\x7b\xb1\xde\x2d\x84\xce\x42\xd1\x73\xb6\x6d\xf6\xeb\x52\x74\x83\x38\x77\x3e\xb1\x05\x40\xb3\xfa\xac\x1a\x62\x49\xee\x99\x21\x02\x24\xb0\xce\xde\x45\x32\xe9\x41\xff\x72\x98\x70\x05\x6c\x40\x61\xf6\x46\x84\xe3\x0a\xc7\x5c\xd7\x7e\x54\x7e\x3b\x2d\xda\xb4\x95\xfd\xd5\x2c\xc8\x55\x8b\x96\x4f\x84\xac\x44\xdf\x56\xc2\x4b\x4b\x11\x49\x47\xc8\xe8\xc5\x2c\x43\xb5\x82\x39\x20\xb8\x10\x35\x8b\x09\x7d\x60\x5e\x92\xbd\xb2\x14\x96\x74\x81\xba\x85\xb5\xa5\xb4\xa4\x17\x24\xa4\x37\xb4\x1c\xd7\x6e\x4b\x1f\x5b\xd8\x3d\x74\x2a\x26\x4e\x28\xbe\xe4\x6b\x19\xf4\x68\xdb\x93\x4c\x00\x62\x87\xd2\x2e\x9a\xa4\x47\xc8\xed\xfd\x77\xdc\xa7\xb4\x00\x2d\x22\xd2\xf1\x37\xd8\x9b\xf2\xa8\xca\xf3\xd9\x34\xf7\x9e\xb7\xb0\x69\x4e\x76\x2c\x8c\x82\xe4\x51\x7f\x67\x96\xfd\xd0\x28\xea\xfb\xd2\x03\x6e\x29\xce\xd8\x05\x8e\x08\x03\xdf\x60\x57\x61\x1a\x07\x49\xb5\x20\x2f\x26\x0d\xb0\xbc\x53\xb0\xdb\x6f\x38\xbf\xca\xc8\xe7\xdc\xc4\xd6\x1c\xe3\x14\xe6\x86\x21\x4f\x9e\xb2\x89\x29\x51\x17\xe9\xb0\xe4\x7b\x93\xc4\x64\x14\x85\x8f\x75\x9b\x10\x4d\x2c\xac\x8d\xb1\xb2\x35\x7d\xac\xd4\xfb\x17\xd0\x31\xf9\x69\x3a\x9b\xe0\x40\xbd\x4f\xf4\xc7\x09\xf6\x83\x1b\x69\xbf\x53\x0e\x64\xb3\x88\xa6\xad\x2c\x11\xd1\x6c\x31\xb6\x67\xe7\x5f\x0b\x1d\x9a\x08\xe3\x02\x13\xf5\x24\xc5\x0b\xf3\x7a\xb7\xbe\x68\x16\x2d\x0a\xeb\x2f\x94\xb8\x0d\x92\xa7\x2a\xa4\x03\x4e\x05\x48\x10\xbf\x9d\x07\x9c\x1b\x3a\x25\x79\xf5\xb0\xca\xb6\x54\xde\x2c\x76\x8d\xbc\x08\xe7\x84\xb0\xe1\x36\x21\x94\x3d\x99\x4b\x55\xbf\xd8\x40\x85\xda\x51\x06\xad\x40\x29\x6a\x68\x26\xac\x37\x24\xef\xed\x26\x12\xf3\xae\x4c\x3e\x07\x43\xb8\x2f\xa1\xff\x2d\xbe\x2c\x99\x67\x85\x61\x5e\x98\xbc\xa7\xd0\x49\x2b\xe5\xee\x49\xb6\x08\x78\xb8\xd3\x27\x8d\x91\xb5\xbc\xf7\x0b\x57\x18\x4c\x59\xbc\xa0\xf2\xea\x58\x5e\x83\x59\x5e\xb0\x07\x90\x53\x48\x33\x00\xb8\xd8\x2b\x24\x0f\x54\x8e\xa9\x6d\x45\x18\x31\x4b\x5e\x66\x07\xc0\x4c\x66\x2e\x70\x04\xc6\xbc\xc5\xd0\x44\x94\x72\x07\x30\x1a\x3a\xbb\x18\x96\xa9\x33\x00\x15\x96\x24\x24\x6d\xa2\x4e\x0b\x4c\x8e\xe1\x03\xb7\x9f\xdd\x1b\xa2\x78\x12\x12\x19\xc1\x43\x3e\xfd\x74\x15\x8e\xc7\xa8\x8f\x45\x83\x01\x4a\xfc\x28\x88\x27\xe3\x9b\x07\x3a\xdc\x53\xab\x09\x36\x4c\x1e\xda\xfb\xc5\x83\x29\x25\x8d\x7f\x03\x2e\x44\x27\x39\x30\x59\x90\x44\x8d\x2b\xf8\x1a\x0f\x66\x19\xae\xbc\xe0\xd1\xa8\x5e\x78\x2c\x71\x87\xc7\xcc\xb7\x1c\x62\xd1\x03\x41\xf7\xd0\x0b\x32\x1c\xe4\xff\x5f\xb8\xcf\xcc\x14\x8c\xcc\xdd\x38\x35\x7b\x9c\x44\x3d\x46\x5d\x54\xb1\x69\x37\xea\xa7\xd3\xcc\x66\xd9\xa1\xa8\xfe\xc1\x79\x95\x64\x28\x91\x29\x9c\x4a\xa7\xb5\x6a\xa4\x35\xb7\xb8\xd5\xd1\xa5\x2d\xad\x6b\x53\x5a\xa1\xf1\x66\x69\xe2\x81\x5c\x81\x2b\x62\xdc\xe5\x69\x90\xd9\x42\xba\xad\xae\xb0\x44\xde\xd2\x78\x00\xfe\xd6\x80\xb5\x84\x36\xb3\x62\x0c\xc0\x6e\xda\x50\x93\x8b\x64\xd0\x4c\x41\xce\x93\xc9\xf2\x31\x47\x2f\x4d\x7d\xb6\x92\x1a\x3a\x4f\xe1\x6c\x77\x96\x3a\x62\xa2\xd4\x82\x87\xf1\xfc\x48\x2d\xa4\xe8\xbb\x69\xb5\x6d\x9a\x01\x45\xc5\x1d\x30\xbe\xcc\x59\x9e\xc6\x92\x3d\x01\xcb\x21\x7e\xdd\x5d\x1f\x6e\x89\x12\x27\x14\xe2\xf6\x6f\x36\x0d\xd7\x23\xea\xc7\xdf\x6f\xed\xdc\x22\xb2\x7d\x72\x0b\x4a\xdb\x2e\x9c\x4b\x79\x9c\xd9\x16\x6f\x71\x0b\x69\xc5\x2d\x1d\x76\x3b\x3f\x7c\x0e\x86\x5d\x69\x7b\x96\x28\x64\x41\xf5\x38\x73\xa9\x5a\x64\x5f\xfe\x3e\xf4\xe5\x85\xd2\xc1\x77\xa0\x8e\xf8\x9b\xa8\xcd\x2d\x8b\xaf\x94\x26\xf9\x05\x1f\x6a\x57\x58\xd9\xc7\x6f\xd8\x43\x7f\x3e\xb2\x06\x3b\xdf\x8e\xbe\x91\xc2\x41\xdb\x5d\xe3\xcc\xa5\xdc\xb5\xc9\x2e\x04\x3c\x11\x5b\xb8\xb8\x22\x61\x4f\x87\x57\xc8\x18\xec\x99\x6e\x7b\x2e\xef\x4e\x2a\xc6\xd2\xbe\x19\x5d\x5a\x81\x2d\x56\xc1\x60\xc5\x1a\x92\xc0\xa9\x98\x57\xf4\x25\xee\xeb\x0c\x39\x00\x84\x31\x3f\x6a\xfb\x92\x1e\xdf\x40\x63\x3f\xbc\xa6\xc9\x40\xa0\x82\x75\x48\xa5\xb3\x35\x35\xcc\x54\xa0\xbb\xf4\x26\xd6\x13\xdf\x3d\xf4\xc1\x7f\x01\x3f\x7e\x60\x05\xf1\xf7\xce\x98\xbf\x47\x3d\xb1\x8d\x19\x2e\xaa\x28\xbe\x17\x63\x7c\x70\x14\x4d\x45\xf1\x43\x31\xee\x92\x7a\xe2\x6f\xce\xbb\xbf\xb9\xb2\xf8\xdb\x6f\x15\x9e\x62\xdb\xe3\x38\xa1\x3d\xdc\xde\x51\x4a\x1f\xee\xbe\xbf\xb0\x6d\x1d\xf2\xf8\x96\xdc\x3d\x8a\x14\xe4\xb9\x2a\x4f\x64\xba\x94\x53\x5a\xb2\xfc\x95\xb7\x67\x5e\xbb\xf9\xbd\x26\xa5\x7c\xf0\x1c\x94\x8b\xe6\x9e\x54\x72\x4e\x1a\x88\x99\xe9\x27\xb5\xb4\x93\xbc\xa2\x23\xf1\x24\xe8\x47\x73\xe0\xe2\xa7\x9a\x7c\x72\xdf\xcf\x46\x1e\xb2\xa4\xa0\xcc\x8f\xd7\x1f\xe2\x81\x3f\x46\xd3\x78\x7c\x33\x0c\xc7\x28\x1e\x22\xba\x69\xb1\x53\xbc\xe5\xc8\xcb\x62\xdb\x6f\xa8\x05\xb5\x86\x15\xc6\x24\x5e\xef\x90\xf7\xb7\xaf\xcd\xd8\x41\x92\xad\x65\xff\x0f\x83\xa9\x81\x8d\xe0\xac\x4f\x66\x50\x27\xe2\x9d\x95\x69\x12\x67\x31\xf9\x84\x36\xc8\xe9\x43\x2f\xc0\xea\xa1\x0d\x14\xe1\x2b\x82\x40\x31\x84\x68\x36\x1e\x3b\x16\x8a\xc0\x20\x5f\x26\x52\xbc\x23\x5b\x24\x4f\x3e\x27\xc5\x4a\x6e\xa7\x62\xfb\x43\xd8\x4f\xfc\xe4\x66\x9e\x8e\x5c\xca\x0f\xea\x04\x05\xd9\x42\x99\xd6\x93\x08\x17\xbc\xcb\xfe\x18\x85\xd1\x08\x27\xa1\x12\xc0\x55\x89\xe8\xa0\xe7\x19\x35\x23\x8c\x9a\xd3\x59\x22\xec\x1f\x8f\x31\x0c\xee\x71\xc2\xcf\x60\xe4\x67\x1c\x21\x16\xca\x83\x8a\x41\xc6\xa9\x12\xa1\xa2\x38\x80\x5c\xee\x8a\x2f\x71\x92\x84\x01\x4e\xd1\x21\x55\x88\x84\x38\xa5\x0c\x7c\x7a\x83\xc2\x88\x65\x33\xce\x11\x28\xd1\x82\x9e\xab\xe1\x64\x51\x00\x86\xcc\xe5\x28\xb7\x48\xd4\x40\x32\x51\xfb\x37\x27\x94\x84\x15\xe9\xa6\xc0\x24\x51\xf6\x17\x0b\xf1\x38\xe8\xa2\x17\x90\x29\xeb\x85\x6e\x38\x62\x6f\x93\xfc\x4d\x70\x36\x8a\x83\x42\x1f\x79\xa9\xb4\x1e\x23\xdf\xe6\x78\x86\x90\x19\xce\x90\xa2\xaf\x18\x64\xf3\x79\x75\x06\x31\x9c\xfa\x57\x91\xf9\x45\x62\x24\x44\x58\xc8\xd3\xea\xb9\xcc\x89\x37\x67\x17\x13\x1c\x59\x4c\x87\xc9\x8e\x52\x8c\x05\xca\x99\x0f\x3b\x77\xe5\xe5\xad\xe9\x1f\xac\x08\x30\x33\x29\xee\xfa\x15\x0a\xc7\xd2\xc4\x8e\xd3\x0f\xbc\xc9\x91\x9f\x1e\x5c\x45\x8c\xec\x6f\x2a\x2f\x48\xcd\x17\x55\xe1\xf3\x44\x1e\x61\x13\xe4\xe5\xc9\x8b\xb9\xfd\xa0\xb5\x0a\xa7\xdb\x52\xeb\xff\x49\x67\x53\x22\x6a\x45\x61\xb6\xe2\x13\xe1\x94\x6d\x7d\x7e\x72\x31\x23\xa3\x6b\x1d\x0f\x64\xc9\xa0\x50\x30\x4e\xb9\xc7\x6d\xf2\x22\x45\x39\x47\x0f\xa9\x52\x98\x4f\x3a\x5d\xa5\x26\x04\xb9\x83\xca\x7e\xe0\xd8\x76\x10\x57\x8c\x0f\x71\x82\xa3\x01\x69\x00\xc6\x79\xaa\xaf\x57\x63\x18\x98\x5c\x6c\x03\xe8\xdc\x67\x90\x2d\x35\x86\x8d\xa9\x6e\xc3\x4a\x49\x65\xa6\x49\x55\xde\xb3\x88\x8e\x03\x4c\x20\x5d\xb5\x66\x08\xd4\x4d\x3e\x1f\x79\x06\x9b\x4a\x55\x5c\xc3\x11\x51\x1a\x42\xca\x01\x90\x4a\xf5\xef\xcc\x2b\x79\xc4\x72\xb4\xc1\xd8\x26\xbf\xb3\x98\xcb\x8b\x68\xb9\x62\x8e\x67\x36\x02\x4b\x2e\x8f\x93\x6d\xae\x5c\x1e\x41\x5d\x5a\x23\xfc\x9d\xba\x4e\x9c\x54\xc3\x8b\xdf\x85\x6c\x8a\xdc\xd5\x1d\x73\x85\x0e\x18\x33\x63\x49\x02\x80\xa4\xc0\x84\x3e\x08\x50\x1a\x4f\x30\x4d\x3d\x85\xae\x46\x38\x42\x37\xf1\x2c\x11\x66\xf6\x3e\x11\x67\x29\xf0\x07\x8e\x9d\x7b\xdf\x5d\x50\x77\x74\x2e\xda\xcb\x10\x65\x00\x2b\x2b\xe6\xc8\x88\xa1\xbf\xe3\x76\x37\x17\x8d\x52\x73\xda\x8b\xa7\x44\xd8\x99\xe6\x72\x0f\x93\x77\xee\x21\x4e\x49\xc0\x40\xc3\xa4\xc8\x54\x13\xd0\x44\x3e\xf0\x94\xb2\xd5\x49\xf7\xcf\xb2\xf2\xcb\x1d\xc7\x1d\x1a\x51\x2e\xb1\x45\xff\xac\x6b\x5c\x44\x3c\xe4\x97\x6d\x1f\xfd\x09\x18\x4d\xcc\xa9\x87\xd8\x56\x9d\x17\xd3\x37\x6b\x19\x60\xb5\x70\x8b\x25\xd3\x79\x2a\x17\x3f\x43\x1b\x52\xfb\xea\xa7\x05\x52\x17\x39\x36\xd9\x6d\x74\x15\x47\x2f\x32\x2a\x3f\x73\x77\x47\x29\x78\xe1\x38\x8e\xa7\xc8\xef\xc7\x97\x96\x6d\xb0\xb8\xcb\x2f\x38\xb4\x17\xee\x0e\x03\x17\x15\xad\xca\xfd\x14\x6f\x4b\xe4\xd5\x2a\xb5\x78\xc4\xe1\x04\x7a\x0a\xf6\x2f\x8b\xac\x1b\xdb\xc6\x37\x18\xc7\x11\x7e\x04\x8e\x07\x70\xd1\x46\xbe\x87\xc0\x8b\x12\x3b\x19\x29\x36\x77\x23\x93\x73\x91\xa8\xc2\x11\xe7\xa7\x56\x7b\x32\xfb\x19\xd9\x7a\xbb\x1f\x21\x1f\x3c\x6f\xb5\x58\x84\x85\x91\x85\x8c\x38\xef\xc5\x20\x6c\xe1\x69\x84\xf1\x83\x1a\x0e\x31\x0d\x2f\xa2\x70\x18\x0e\xfc\x28\x63\x01\x25\x43\xda\x7b\x00\x49\xdb\xb1\x1d\x93\x7f\x95\x3c\x88\xe9\x59\x59\x7e\xf3\x00\x61\x63\xcc\xe6\x75\xb2\x70\x84\xc1\x97\x4d\xaf\xe6\x8c\x35\xb2\x9a\x85\x89\x91\xd2\x6e\x30\xe6\x0e\x1a\x7e\xb0\x54\x2f\xb2\x7f\xb6\xb2\xb1\x1b\xb6\x30\x0e\xed\x7f\x79\x00\xa7\xb5\xeb\x5a\xad\x56\xaf\x35\x6a\x4d\x0f\xd5\xae\x6b\xad\x5a\xbb\xd6\xa9\xad\x9d\x3d\x1a\x60\x0f\x75\x4a\x87\x5e\x61\xe1\xeb\xf8\x8c\x18\x2b\xf6\x8a\x39\x04\xc3\x72\xe5\x0f\xf4\xbf\x5f\xbf\x42\xcc\x5e\x4d\xd4\x18\xa2\x8a\x98\xde\x1f\x36\x2c\x8a\x42\xf9\x0f\xa0\x4a\x46\x43\xfc\x67\x69\x63\x52\x1d\x00\x25\x8f\x31\x8e\x2e\xb2\x11\x35\x3d\x72\x72\x91\xf2\x31\x63\xf2\x85\xb2\x58\xa4\x98\xed\x68\x10\x07\x84\xde\x31\xfd\xa1\x93\x3b\xbc\x2e\x8e\xfd\x29\x08\x00\x47\x83\x95\x5d\x7c\xed\x6e\x73\x5e\x00\x99\x52\xab\x7d\xe1\xe0\x2e\x39\xb1\x96\x88\xec\x62\x89\x6b\x30\x2f\xac\x8b\xa5\x8a\x32\x24\x9f\xb2\xe1\xfa\x42\xd1\x5c\xd8\x54\x38\x63\xb9\xf0\xa9\xfa\xfa\x15\xed\xe2\xeb\xc2\xf0\x2d\x73\x08\x68\xe0\x67\x38\x62\x7b\xbe\x4a\x41\x0e\xe6\xef\x26\x24\xe9\x1e\x36\x1f\xf0\x13\xc6\x0d\x25\xca\x84\x34\xbf\x8b\xde\xeb\x96\xc5\xa5\x0c\x6d\x08\xec\xea\x3c\x7e\x86\x78\xd3\x70\xa7\x34\x83\x92\x3a\x53\xa2\x81\x9d\x17\x0b\x47\x42\x06\xf6\x57\x83\x61\x59\x7c\x15\xb3\x91\x2f\x42\x1d\xe4\x24\xe6\x2e\x1d\xa6\xc7\x39\x8f\x51\x78\x8e\x03\xf8\xb1\xca\x92\x28\xfc\xbc\x8e\xd1\xa9\xde\xd8\x9f\x4c\x11\xbe\x86\x48\x92\xfd\x50\xef\x1c\xbd\x57\x25\x65\xcc\xdb\x06\x7a\x9f\x3a\xb0\x05\x49\x51\x10\xff\x97\x23\x50\x3a\xd4\x27\x22\x69\x84\x61\xab\x45\x7e\x86\x7c\x94\x85\x13\x8b\xc4\x6d\x0b\xc9\x2e\x77\xd7\x9d\x14\x42\x1e\x1c\x52\x14\x6d\x10\xf4\xd8\x2c\x9c\x86\x3c\x2a\x36\xf9\x4f\xa5\xd1\x42\xcb\xa8\x12\x52\x8c\x5f\xa2\xf5\x6a\x55\x44\xcb\x76\x4a\xf1\x14\x8e\xda\xe3\x25\x14\x8a\x70\xdb\x5f\x37\xf2\xa6\xdf\xbc\xe1\x6d\x58\xca\x8b\x46\x4b\x08\xfe\xce\x6d\x49\x1e\x53\xba\xb8\xee\x35\xa6\xee\x28\xf7\x65\xbb\xbf\x81\xcc\xc1\x2e\x93\x31\xd8\xa4\x42\xb1\xd9\x2e\x6d\xa8\x68\xda\x72\xac\xf8\x61\xe4\xf7\xf5\x93\x87\x74\x00\x28\xcb\x4e\x69\x0c\x0e\x22\x04\x2a\x82\x61\x98\xdd\x57\x14\xcc\x17\xa7\x58\x5d\x0e\x26\x45\x3e\x97\x0d\xdd\x6b\x61\x4d\xa6\x1c\x65\x8b\x8b\xe4\x64\x32\x76\x86\x61\x11\xd5\x4e\x05\x0c\x1e\x67\x7e\x03\x96\x0e\xfd\x03\xd2\x6f\x36\x08\xe9\xa7\x0a\x5f\xb0\x10\xbc\x22\x4a\x6d\xa0\x7d\x3f\x1b\xad\x0c\x70\x38\xce\x6b\xae\xa2\x05\x22\x12\xd9\xcf\xbf\xa5\x76\x1e\x87\x39\x92\x71\xfc\xbd\xab\xdd\x27\x3b\xee\xca\xb4\x60\x9c\x77\x55\x5a\x98\x77\xce\x95\xc1\xc2\x49\x8d\xe2\x2a\x47\x3f\x37\x4f\xce\x2b\x26\x8d\x30\xf3\xfb\x9a\xd3\xa4\x8e\xd4\x5b\x7c\x0a\x24\xb1\x61\x18\x8e\xc7\x3c\xec\x2c\x73\x93\x80\xf3\xd6\x7c\xa1\x84\x1f\xe6\x22\xdb\xa1\x57\x06\xe5\x74\xf1\x29\x35\xcb\x0c\x52\x29\x42\x79\x28\xe3\xb3\x12\x47\x30\xe6\x0a\x52\x77\x9f\xb4\x68\x09\x99\x4c\x22\xfb\x11\x4b\x66\x0f\xe6\x81\x8a\x7c\x4d\xd4\x1b\xf2\xc9\xf9\x95\x3b\xca\xfc\xf9\x15\xda\x20\xff\x3a\x12\xa8\x4d\xce\xbf\x90\x6d\xe6\xba\xe9\x07\xb8\xb3\xde\xd7\xc3\xaf\x8b\x62\x7e\xfa\x19\xc9\x9c\xa3\xe0\x9e\xa0\xc4\xdd\x1d\x6d\xb5\x52\xbb\x7e\x55\xeb\xbc\x42\x2f\x49\x17\xbe\xc0\x9e\xbe\xb3\xb3\xb3\x53\x45\x4b\xf4\xc5\xcf\x3f\xa3\xda\x75\xbd\x06\xdb\x3d\x41\xc0\xb1\xdd\xd3\x2e\x56\x6a\xd7\xad\x4e\xbb\x46\x81\x5d\xe9\xc0\xae\xca\x02\x83\xe1\xc5\xe9\x0c\x3c\x7d\x2a\x80\xc6\x9b\x37\xb4\x26\x5a\x42\x30\xd2\x85\xf5\x59\xdd\xd5\x0d\xa8\xc3\xfe\x8a\xcb\x2e\x6d\xa0\xda\x4a\xdb\x59\x06\xc6\x94\x15\x7d\x49\xed\x6d\x38\xb5\x55\xd1\xcf\x68\xa5\x8d\xfe\x03\xd5\x51\x17\x2d\xd7\xcb\x88\x28\x06\xe7\x50\xc5\x0d\x0f\x25\x03\x7f\x30\xc2\x2c\xbb\xce\x7c\x81\x83\xd4\x3c\x27\xf4\x98\x54\x2a\xb4\x2a\x39\x2a\x29\x48\x92\xdd\x44\x1a\x0c\xfb\x15\x13\xad\xba\x81\xce\x93\x0a\x2d\x0f\x04\xb9\xd6\x5f\xb3\xf4\xe9\x2a\xcf\xe1\x53\x11\xe5\x73\xf8\xe8\x2b\xaa\x95\x0c\x6b\x1e\xe1\x2b\xc9\xd9\x09\x6e\x1d\x99\x02\x24\xe2\xe9\x7b\x9e\x69\x23\x69\x77\x3e\x65\x47\xfb\x79\x86\x34\x38\x1a\x80\x21\x0d\xfd\xaf\xdd\x90\x66\x17\x5f\x9b\x9a\x00\x1b\x38\x52\x70\x83\x02\x5d\xa1\xbf\xcb\xc5\xdf\xd4\xd5\x17\x23\x7c\x5d\x5a\x85\x51\xe2\xe4\xb9\x60\x54\xcd\x52\xad\x3f\x14\x23\x1f\xe1\x6b\x33\x84\x26\x1b\x3f\xe9\x68\x3f\x3f\x91\x90\x35\x70\xe6\x5d\x8f\xa9\x57\xa5\x4f\x9e\xe9\xa2\xc7\x48\x3a\xeb\x26\xa0\x11\xbe\xee\x8d\xfc\xa4\x74\x9e\xad\x74\xee\x81\x0e\x72\xa4\x85\xf4\x20\x77\x75\xcf\x43\x1c\xc7\x8e\xad\x71\x00\x4b\x80\xb4\xaa\xb9\xda\xa7\xde\xa9\xda\xf8\x9d\xad\x2a\x69\xa7\x36\x2c\xae\xeb\x60\x10\x02\xdc\x1f\x71\x18\x55\x5e\xbc\xb8\x43\xc4\x4d\x89\xc2\xe9\x7a\x5b\x44\xd3\xc3\x57\x0a\x25\xdc\xf2\x0b\xc6\x21\x3c\xfd\xf5\x52\x13\x5f\x6c\xd4\x66\x5b\xac\xc7\xf2\x91\x32\x69\x95\xc5\x12\xa5\xd0\x3a\x1f\xf8\xd1\x85\x3e\xb2\xa3\xcc\x22\xab\xe6\x6a\x91\xd4\x74\x72\xa3\x6c\x0b\x6d\x14\xe4\xc7\xa4\xab\xa5\x09\x9a\x09\xe8\xf4\x5e\x94\xb1\xce\xae\xa4\xb3\x7e\x9a\x25\x95\xd0\x43\x8d\xaa\x07\x49\xf8\x72\x95\x05\x59\x51\xeb\x55\x9b\x03\xee\xc2\x7b\x9e\x32\x4c\xab\xa8\x51\xd6\x7d\xf6\x83\x9f\x85\x51\xbd\xdc\xa6\xc5\xca\xf2\x7d\x4b\x3c\xde\x6d\xeb\x62\xd5\xff\xba\xdd\xab\x2c\x02\x0f\xb5\xa6\xc6\xd0\x9e\x7d\x0f\xa3\xb8\xfc\x8f\xda\xc6\xe8\x70\x7c\xc7\x3b\x99\x84\x20\xdd\x91\xe8\xd4\xad\x0c\x93\x78\x42\xde\xf6\xe2\x00\xc3\x26\x55\x76\x43\x92\x01\xde\x63\x4f\x52\xe8\xf6\xee\xdb\x92\x20\xc7\x85\x16\xc3\x77\xbd\x39\xb1\x55\x44\xf7\x27\x79\xb9\x95\xdf\xa2\x44\xad\xc5\x76\x29\x51\x4d\x6c\x54\xe2\xcd\x63\xef\x55\x5a\xd3\xf3\x72\x39\x87\x92\x16\x3d\xef\xed\xca\x80\x11\xf4\x66\x56\x09\xf9\x9a\xd0\xb7\x2a\xbb\x6e\x71\xe1\xad\x4a\x43\xb8\xec\x4e\xf5\xe9\x64\x67\x79\xbd\xdc\x46\xf5\x29\x1b\xae\x8b\x6d\x8a\x3d\xdc\x6d\x93\xa2\x8d\xfe\x75\x7b\x54\xc9\xf6\x1f\x6a\x65\xcd\xb2\xe1\xba\x7d\x83\x22\xa3\xf8\x98\xdb\x53\x96\xdc\x14\x18\x18\x05\x98\x1c\xd1\x3f\x1d\xed\xf5\xb8\xa7\x53\x05\xa7\x03\x7f\x8a\x2b\x05\x1b\xa7\xc9\x96\xd1\xc0\xcf\x06\x23\x54\x31\xd3\x47\x03\x0a\xa3\x24\xbe\x02\xba\x85\x8c\x2b\x95\x17\xfb\xfe\x78\x18\x27\x13\x1c\xb0\x69\x08\xfc\xcc\x37\x53\xd0\x2d\xce\xc0\xe5\x49\xbd\x3b\xff\x66\x73\xb5\x08\x99\x7c\xd7\xcc\x1b\x28\x8c\xb2\xee\x9c\x0c\xcb\x33\x6e\x56\xc7\x65\x0c\xa0\x6c\x0d\xb3\x88\x51\x0f\xb5\x10\x50\xe8\x8a\xc3\xa9\x96\x0e\x40\x23\x52\xf0\x42\x2e\x4c\x1c\xb0\x6c\x66\x92\x17\xba\x33\x13\xaf\x64\x27\x7b\x23\xa5\x44\x9b\xcc\xd2\x0c\xf5\x31\x0a\xc9\x88\x4e\x70\x94\xd1\x3c\x6b\x3e\x5c\xaf\x27\x38\x13\x1e\x0b\xa5\x72\xfb\x6a\x79\x3a\x55\xe5\x3e\xcd\x71\x48\x5d\xab\xf2\x04\xf1\x9f\xf1\x34\x43\xb3\x68\xca\x93\x06\xaa\xd9\x41\x25\x9b\x96\x9a\x85\xfb\xbe\x65\xe3\x00\x99\x06\x37\xc5\x28\x08\x2f\x31\xd7\xe7\x92\x66\x70\x90\xdd\x95\x59\xf3\x68\x23\xfd\x82\x25\xd1\x66\x49\x4c\xb3\x18\x85\x59\xca\xbd\x62\x10\xa1\xe0\xfb\xde\x31\xf5\xad\xc8\xd3\x84\xb8\xee\x4b\xa6\x52\x59\x77\x99\x79\x1f\x02\x2b\x65\x9b\xcd\x00\x64\xe0\x64\x9e\x8a\xda\xce\xaa\x33\x25\x5a\x3e\xda\xf2\x33\x9f\x0b\xeb\xb5\xb2\x92\xe6\x66\x10\xa4\xd0\x06\xcf\x0b\xee\x18\x69\x46\x0b\xe5\x37\x45\x11\x64\xc1\xc8\x3c\xce\x8c\x5d\x10\x5d\xf3\xcc\x09\x80\xf2\x4b\xea\x53\xe2\x4b\x16\x94\xd4\x9e\x18\x38\xde\xe3\x4c\xe6\x39\x45\xa7\xf2\xc2\xe4\xf7\xa5\xea\xcd\xdf\x1b\x59\xc9\x32\xc9\xcc\x4d\xf7\xfa\x3c\x1d\x9d\x1c\x50\x54\x1a\x20\x16\x4c\x54\x05\x25\xfb\x38\x03\x19\xcd\x89\x13\xc9\x68\x4d\x62\xca\x80\xe1\xfc\x48\x69\x9b\xd0\x35\x17\xf9\x72\x53\x22\x1b\x30\x83\x68\x97\x36\xd4\x24\xe9\x65\x29\x98\xe7\x3a\x4d\x91\x7f\xe9\x87\x63\x88\xd8\x45\xf9\x02\x30\x3b\x37\xd5\x9c\x48\xce\x2a\x61\x74\x19\x7f\xc6\xa9\x9e\x64\xb8\xc2\x92\x03\x7b\xe8\x6a\x14\x0e\x46\x56\x56\xdd\xbf\x29\x60\xd5\x66\xab\x7c\xa1\xf4\xe3\x78\x8c\xfd\xe8\x16\x05\xf1\xce\x78\x96\x8e\xd0\xaf\x23\x9c\xd1\x78\x26\x3c\x17\x2d\xb8\x6b\x4d\xfd\x04\x18\x05\x7b\x95\x73\x6d\xc1\xae\xef\x10\x0e\x44\x70\x7a\x18\xf1\xfb\x6f\xf3\x02\xe0\x16\x25\x24\xd7\x9a\xe1\xa9\x72\x5d\x71\x39\x16\x04\x63\xcf\x14\xac\xc6\x5a\xa5\x45\x95\xc5\x47\x07\x7c\x41\x9d\x09\x5b\x22\x39\x71\x5b\xb4\x25\xe4\x35\x37\x4e\x83\x91\x75\xa9\x55\xc8\x47\xc9\xd0\xcc\x45\xf7\xbc\x78\x2e\x2b\x6c\x68\x29\x99\x8b\x0a\x73\xe8\x79\x6d\x7b\x44\xbf\x5e\x3c\x8b\x32\x4e\x5f\x16\x66\x42\x80\x46\x34\x91\xf0\x11\xc4\x2d\xde\x50\xf1\x5f\xd5\x9a\x7c\x6d\xf2\x22\xd7\x90\x33\x0c\x8e\xe2\x59\x14\xa0\xd9\x94\x3a\x14\x0e\xc6\xb3\x00\x6b\x74\x6f\x56\xd3\x30\xca\x8d\x5c\xe4\x0f\xe5\x63\xdb\x0a\x2c\x82\xf8\x2a\x92\xf1\x88\xa3\xf1\x0d\x1a\xce\xc4\xa2\xb4\x44\xd2\x5f\x5d\x45\x63\x9c\x52\xa7\x4a\xbb\xac\x05\x7c\x23\xc1\x13\x3f\x8c\x54\xe1\xaa\x5c\xbf\x26\xfe\x75\x45\xe9\x17\x5c\x9c\xa2\x65\x5b\x66\x76\x6f\xfe\x95\xaa\x98\x73\xaa\x79\x70\x4d\x39\x50\x32\xc7\x43\x69\xfd\x25\x92\x08\xd0\x45\x4f\x40\x1b\x4e\x72\x22\x5f\xd5\x3e\x86\x51\x45\x6e\xf2\x25\x6a\x79\x0a\x9d\xd9\xcc\x27\x79\x06\x6f\x1b\x91\x10\xba\x93\x00\x16\xbb\x6d\x51\x3e\x4f\xd5\x2c\xec\xf7\x1b\x79\x04\xc4\xdb\x25\x69\x3d\x39\x8d\x26\x08\x66\x38\x21\xa7\x49\xb1\x31\x2c\xe7\x07\x04\x70\x86\xb4\x57\x64\xdc\x45\xdd\x83\x04\x57\xb1\xe5\xaa\x77\xcd\x31\x52\x52\x60\xe5\x0c\x1f\xa6\xdc\x2c\xaa\x70\x5f\x99\x85\xe9\xc9\xb0\xe4\x11\xb5\xa0\xa1\x70\x32\xb4\xbc\x21\xcf\xf4\x7c\xaa\xe4\xb1\x45\x8b\xb0\x75\x2b\x9c\x54\xfc\x3d\xb9\xe9\xfb\x1a\xbb\x95\xce\x42\x59\xe8\xe4\x75\x4f\x2b\x37\xc7\x6e\xf8\x17\x99\xbc\x9d\x1b\x1b\x62\x8e\x89\x75\xc6\x0a\x2d\xde\x54\x1e\x26\x4e\x9a\x8e\x4c\xf4\xfc\x0c\x3e\xf2\x53\xc8\x90\xeb\x3c\x71\xcf\x4d\x45\x9e\xb3\x6b\xd9\x07\x8a\x4e\x3a\x83\x4e\xc3\xae\xe1\x14\xc5\x91\x74\x14\xae\x77\x50\xa5\x5d\x6f\x80\x25\x6b\xd5\x72\x2c\xde\xa5\x95\xf9\x31\x58\x3c\xda\xcf\xc3\x0f\x12\xf5\xb5\x28\x03\x59\x61\xc0\xd4\x22\x57\x33\x3a\x08\x0b\xe4\x24\xbf\x6b\x74\x3b\xd2\x10\xa2\x21\x92\xe7\x05\xb9\x2b\x6d\x43\x22\xe6\x40\x09\xdd\x76\xbc\xbb\xd9\x68\x77\xec\x4e\x62\x45\xa9\xae\xef\x1c\x61\x8d\xc7\x56\x2b\x1f\x66\xed\x18\x8b\xf0\x1e\x6e\x0d\x81\xa9\x86\x98\x63\x89\x9d\x6b\x52\xf8\xc2\x79\x78\x95\x09\xa3\x97\x87\x50\x91\x00\xc2\xb2\x8a\x47\x2d\xe1\x58\x49\x00\x5a\x61\x5e\xa6\xd4\xa0\xef\xcd\x6c\x38\x2c\x1b\x33\xdf\x90\x8f\x16\x1b\xeb\x4f\xd3\x00\x58\x86\x3c\xd8\x34\x2d\x7f\xf9\x8c\x7d\xce\x08\xc2\x14\xb8\x1e\x47\xb8\xb4\x0b\x11\x65\x45\xcc\x7f\x68\xee\xf2\x5e\x60\xce\x67\x80\x57\xe5\x05\x43\xca\xa6\x4b\x51\x4b\xce\x57\x9d\xd0\x82\x32\xa1\x28\x63\xe0\x58\x8f\x0e\x8d\x04\x53\xd8\xa8\x10\x2c\xe4\xc1\xc6\x97\x08\xe9\x04\x5f\x1b\x28\xe9\x1c\x6b\x8a\xbf\x0f\xe6\x3b\xb1\xc3\x92\xdc\xa4\x02\x17\x27\x83\x44\x1f\x63\x40\xd9\xcf\x68\xbe\x78\x56\x33\x8f\x19\x8a\xc2\x14\xe1\xe1\x10\x0f\xb2\xf0\x12\x8f\x6f\x90\x8f\x02\x9c\x66\xc9\x0c\x9e\x3d\x90\xd3\x97\xe3\x68\x80\x4b\x45\x19\x2d\x49\xa1\x4a\xa2\x07\x40\x29\x0f\xc8\x0d\x25\x16\xd7\x5c\x90\x41\x78\xa0\x9d\x01\x6d\x70\x72\x14\xc9\x84\x1c\x6a\x09\x47\xe9\x22\x42\x2f\xa8\x36\x9f\xea\x79\xd1\x85\xe8\x7e\xc7\x32\xbe\xe6\x81\xa8\x18\x0c\x9a\xb7\x56\xe6\x09\xf0\x0b\x70\x56\x69\x84\x38\x93\xdd\x91\xe6\xc1\xba\x78\x48\x79\xd7\xe2\x91\x92\xdf\xb5\xeb\x8d\xd5\x66\xa3\x9c\x98\x9f\x32\x8d\x8f\x12\xff\xde\x67\x93\xf6\x42\x04\x4e\x0a\xa3\x0c\x27\x43\xc9\x5a\x18\x39\x57\x05\xe7\xaf\xac\xeb\x9c\x6a\xe9\x76\xcb\xe2\x23\xfa\x68\x84\xc7\x53\x9c\x10\xf1\xa7\xc4\x22\xd8\x61\xb8\x31\xdf\x60\x1d\xe5\x6f\x70\x8f\x47\x65\x26\xdd\xa9\x82\x76\x75\xe5\x9c\xf6\x6a\x17\xba\x54\xb1\x09\x5b\x6e\xfd\x9c\x5c\x55\x31\x1e\x04\xd0\xae\xfb\x3d\x63\x5d\xd8\x03\xe0\x22\xf5\xbc\xc8\x56\x22\x1c\x16\xd5\x2c\x62\x79\x86\x4b\x95\xc2\x17\x3f\x36\x5a\xe9\x89\xb0\xe4\xdd\xfd\xcd\xde\xc3\xd3\x13\x11\xa1\x79\x50\x0a\xd2\x02\xa3\xab\xbf\x05\x4d\xed\x4e\xfc\x41\x29\xba\x9a\xf8\x83\xfb\xd0\x96\xa8\x7e\x2f\xfa\xfa\x8c\xed\x2a\x24\x89\xbe\x7a\xe7\x80\x16\x99\x07\x4a\x64\xb4\x11\x5a\x77\x31\x62\x2b\x3c\xfe\x0a\x4d\xd2\x1c\x1f\x06\x82\x0d\x38\x31\xb0\x1f\xb9\x17\x03\xcf\xd4\x02\x21\x7d\xf7\xfd\x6c\x44\xc3\xfa\x3e\xe3\xef\xd9\x30\xbf\xce\x23\xfd\xde\x9e\x79\xed\xd6\xf7\x1a\xde\x97\x21\x53\xe1\xe1\x88\xab\x0f\x1e\xef\x97\x43\x5e\x34\xee\xaf\xc0\x50\x8e\xff\xeb\x0a\xfa\x2b\xbe\x43\xf0\x5f\x5b\x00\x5d\xf3\x8a\x82\x47\x8d\xcd\xa7\x4c\x22\x00\x29\x1a\xac\xf4\xbe\x20\x3c\x8d\x52\x5b\x72\x81\x71\x85\x91\xed\xb4\xca\x99\x68\xb1\xb2\xdc\x48\x4b\x3c\xde\xcd\x4c\x8b\x55\xff\xeb\xec\xb4\xca\x22\xf0\x50\x9c\xb2\x0f\xed\xd9\x4d\xb5\x28\x2e\xff\x00\x5b\x62\xa3\xfc\xc4\x9f\x0a\xe1\x70\xe2\x4f\x17\x8f\xbd\x60\x71\x11\x37\x41\xb8\xac\x32\xe9\x98\xdf\xd5\x60\x19\x2d\x6d\xa0\xa6\xdb\x66\xf9\x26\xc3\x75\x8b\xd1\x32\xfd\x73\x99\x2e\xd3\x3f\xa7\x01\x33\x07\xdc\xc8\x01\x57\x42\xb4\x84\xea\x55\x8b\x4d\x34\xff\x52\xc6\x32\x9a\x03\x6e\x6a\x80\x1b\x4e\xc0\x0d\x2b\x60\x3b\xe4\x2c\x09\xa7\x63\xb8\x7a\xa9\xd0\x61\x79\xf3\x06\xfc\x26\xbe\xd2\xe7\x06\x79\x5e\x27\x8f\x80\x82\x0d\x8a\x98\x8a\x3f\xe8\x54\x54\xfe\x40\x6f\x48\xeb\x3f\xfd\x84\x00\x9b\x3f\xd0\x4b\x54\x5b\x59\x6b\x4b\x33\x54\x7d\x8d\xfe\x28\x08\x77\x21\xcd\x3d\xb5\x05\x9f\xf8\x53\xb0\x99\xdd\xcc\x2a\x15\x8e\x30\x74\xba\x83\x5e\xa2\x4a\x13\x2d\xa3\x3f\xaa\xac\xa7\xcd\xa1\xd5\xdb\xc9\x88\xcf\x60\x2a\x2e\x82\x80\xa7\xfb\x36\xa9\x91\x7d\x20\x28\xa1\x0d\x24\xa1\xd3\x31\x9c\x49\x20\xb6\x5e\x5e\xdc\x6e\x1c\x3c\x0a\xc7\x18\x55\xe4\x7e\xb2\x70\x01\xae\x58\x23\xd6\x61\x91\x9b\x59\xbc\xcf\x8c\xb3\xca\x50\xef\x61\x27\xaf\xf0\xe4\xbb\xdb\x59\x0a\x56\xbb\x10\xa3\xff\xae\x4d\x2d\xd9\x0e\x41\xed\x7a\xe4\xad\xa4\xbc\xb9\xa5\xa8\xb5\xe0\xe6\x20\xea\x09\x43\x79\xf1\x46\x18\xca\xcf\xe7\xfb\x46\x89\x04\x5f\xe2\x24\xc5\xfb\x52\xc1\xfc\x95\x2d\xae\xd9\x0f\xf9\x67\x27\x75\x17\x02\xb5\x6d\x01\xfc\x4f\xe7\x3f\x84\xfd\x90\x15\xca\x3a\x58\xc8\x69\xd4\x86\x4f\xf9\xc2\x66\xb6\xf9\x7f\x54\xcf\xd0\x06\xfa\xa3\x5c\xac\x4e\x0b\x4b\xd9\xbb\x88\xe2\x04\x7f\x33\xae\x22\x81\xdc\x8b\x02\xf0\x73\xce\xa7\x3b\x24\x6f\x0e\x86\xf3\x78\x86\xd4\x0e\x85\xf1\xc3\xc6\x06\x5a\xae\xcf\xe1\x49\x32\x85\xc9\xb5\xef\xc4\x88\xad\x22\x41\x22\xd2\x5e\xa6\xf8\x43\x1c\x4f\xf3\x25\xe1\xe9\x38\x78\xd2\x8c\x2a\x22\x87\x76\xe3\xe9\x4f\xbb\xe8\xc5\xe6\xdb\xde\xd6\xf6\xce\xbb\xdd\xbd\xff\x7a\xff\x61\xff\xe3\xc1\xe1\xff\x3e\x3a\x3e\xf9\xf4\xcb\xaf\xbf\xfd\xfb\xff\xf8\xfd\x41\x80\x87\x17\xa3\xf0\x8f\xcf\xe3\x49\x14\x4f\xff\x3b\x49\xb3\xd9\xe5\xd5\xf5\xcd\x97\x5a\xbd\xd1\x6c\xb5\x3b\x6b\xeb\xaf\x96\x56\x37\x58\x84\x5b\x71\xb4\x13\x8b\x76\x61\x54\xf3\x21\x76\x78\xa5\xe4\x96\x1b\x8a\x85\xa9\x4d\x14\xd2\xda\xb1\xb9\xa9\x90\x99\x0e\x1d\xfb\x0d\x73\xec\x4a\x89\x90\x24\x2d\x8f\x9c\x9a\x64\x07\x16\xb4\x8c\xea\xd5\x33\xf0\x5e\xc9\x05\xa6\x86\x49\x5c\x1c\x68\xa3\x0c\xd0\xea\x19\xdf\xe0\x65\x31\xcc\x02\x95\x0a\x44\x91\x12\xb9\xe7\x2b\x11\x66\x00\xfd\xaf\xb4\x45\xd9\xb7\x26\x2a\x0e\xde\x83\xd8\x10\x2f\x2d\x29\x1f\x04\xd9\x8a\x1f\x8c\x22\x8d\xd8\x92\xd6\xb0\x08\xb7\x79\xee\x1e\xfd\x90\x2f\xed\x11\xaf\x9d\x99\x7d\xda\x4f\x47\xff\xa7\xa3\xbf\x38\xfa\x7f\x3a\xd9\x59\xae\x77\xd0\xdb\xed\xd2\x0e\x5a\xf5\xce\xdb\x6d\xd9\x47\xab\xde\x51\x9f\xe0\xeb\xdd\x9d\xb6\x28\x32\x7f\xad\xe3\x56\x49\x1c\x1e\xd0\x79\xab\xde\x71\x7a\x6f\xd5\x3b\xff\x00\x8d\x40\xf9\xc3\x3a\x0c\xc6\x7d\xce\xea\x76\x7f\x7f\xb0\x8c\x8a\x03\x7c\x18\x87\x51\xe6\x72\x32\xae\x77\x1c\x4e\xc6\xd6\xc3\x74\x8e\xa9\xdb\xcb\x58\x34\x59\xd6\xd5\x58\x02\x7a\x8f\x13\x94\x4e\xc4\xf7\x72\x56\x03\xda\x5c\x74\x6d\x7c\xd7\xc7\x28\xba\xaa\x84\xcb\x1a\x5f\x7c\x0b\xf9\xac\x41\xa5\xc5\x7c\x8d\x79\x2d\x21\xdf\xf2\x17\x8f\xed\x69\xac\x36\x5c\xce\xd1\xb8\x0e\xb2\x8f\xc0\x50\x75\x33\x26\x22\x50\xbe\x58\x1a\x64\xb1\x68\x41\xd8\xdc\x14\xee\x92\x72\xb4\xd1\x79\x59\x3e\x14\x06\x23\xcb\x0f\x25\xf6\x30\x69\x9f\xfa\x70\xef\x7d\xea\xc3\x77\xb0\x4f\x95\xc1\xe1\xa1\xf7\x29\xeb\x72\xfa\xb0\xfd\xb4\x4d\x89\xbf\x07\xdb\xa6\xd2\x2b\x7f\xba\x1d\x05\xa1\x1f\x55\x16\xdd\xb1\x6c\x47\xf2\xef\x7f\xcb\xfa\xf0\x38\x5b\x56\x99\x65\xf2\xfd\x6f\x59\x1f\xb6\xb5\x4d\xeb\x69\xc7\x32\x76\x2c\x69\xc5\x2c\xb4\x79\x7d\xd3\xdd\x4b\xcc\x8b\x84\x2d\x01\xa4\xf4\x91\x47\xc3\x87\x2f\xec\xee\x84\x2e\xee\x5a\x8d\xfc\x3f\x5c\xac\xd0\x8f\xa4\xfb\xec\x2b\xfd\x96\x2f\xff\x79\xea\x02\x20\x2c\xb7\xb6\xa0\x73\x2f\x6d\x01\xcb\x51\xfb\x2d\x95\x06\x1e\x92\x5e\xa5\x23\xbf\xae\xbd\x1a\x4d\xfc\xc1\x23\xaa\x16\x3c\xc4\x9b\x85\x5f\xd0\xda\x3f\x41\xdd\x60\xe4\x8b\xbd\x83\x2a\x42\x31\x62\x91\xbe\xec\x6f\xb5\xa1\x26\x98\xdc\xec\x6f\xb5\x6d\x32\x1e\x98\x38\x7f\xc6\x37\x34\x0b\x36\xb5\x83\x15\x7d\x05\xe7\x5f\x3f\xca\x78\x12\xef\x38\x99\x50\x1b\xed\xed\x5f\x0e\xcf\x61\xd3\x3d\x89\xdf\xe3\x5c\x18\x44\x57\x57\x57\x2b\xf1\x14\x47\x69\x3a\x5e\x89\x93\x8b\xd5\x20\x1e\xa4\xab\x90\x84\x3b\x5e\xd5\xea\x8c\xb2\xc9\xd8\xa2\x08\xd9\xbe\x9c\xbe\xdf\xda\xc9\xd1\x16\xcf\x25\x83\x21\xcc\xf7\x01\xd1\xf6\x38\xc3\xfb\x85\xa5\x3c\x87\x3d\x8a\x0c\x4c\x4a\x1e\xc2\x88\xbb\xbd\x48\xe1\x9e\x73\x57\x97\x16\xaa\xd4\x1b\xeb\x8a\xa7\x8b\x01\xdf\x61\xa4\x26\x87\xc5\xd0\x13\xa4\xec\x6f\xb5\xe7\x61\x1b\x66\xcc\x16\x59\x0f\x52\x2d\x7d\xc8\x62\x34\xa5\x56\xa7\xb2\x77\x8e\x63\x87\x33\xfc\x62\xb4\xdd\x81\x0d\x4f\x17\xd5\x1b\xeb\x60\x42\xaa\x7c\xa5\x9d\x03\xcc\xb5\x2f\x39\x3e\x4a\xdb\xb7\x77\x76\xbb\x71\x10\xed\x63\xfb\xe1\x60\xa9\xd1\x07\x30\xb3\xfe\x1c\x0c\x0d\xef\x1b\x4a\xf3\x73\x52\x34\xcd\xaf\xf8\x67\x3e\x57\xeb\x5a\x3e\xbf\xbb\x82\xf1\xd4\x69\xac\xd5\x6a\x3a\xe0\x05\xbd\x83\xe6\xfa\xfd\x94\x93\x77\xb7\x20\x85\x3f\xa1\x11\x42\x15\x90\x08\xdb\x87\x0c\xac\x64\xd1\xde\xc5\x4a\x9f\xd7\xa5\xb1\x00\x6c\x80\x0a\x2a\xa7\xfe\x38\x43\x9b\xf0\x9f\xc5\xc5\x62\xa0\x2e\x4a\xde\x0f\x41\x5e\x98\x6c\x1e\x9f\x83\xe1\x0a\x75\x8b\xc0\x15\xde\x19\x0f\xf0\x2b\xc8\x5b\x03\xc5\x95\xfc\x8e\x6a\xcd\x85\x04\x5e\x75\x8a\x2d\xe2\x2d\x59\xe9\x8c\x7b\x98\xb5\x85\x97\x1a\x21\x0f\x66\xa2\x9c\xaf\x0e\x2b\x2c\x97\x5b\x18\x84\x16\xa0\x43\xfc\x1e\xc6\xc6\x96\x12\x6d\x91\x33\x72\x0e\x4c\xf8\x04\x8b\x37\xce\xe3\x32\xdf\x63\x68\x8f\xd8\x93\xa5\x9c\xc4\xc4\x69\xd1\xfc\x85\x05\xcb\x77\x6c\x63\x22\xe0\xd5\x8f\xcc\x98\x45\xc3\x95\x1b\xb4\xbc\xe1\xf8\x58\x8f\x02\x44\x8c\x03\xcf\x01\xe7\x05\xb3\xea\xb2\x44\xcb\xce\xbf\x56\x46\x72\x30\x86\xdc\x09\x84\x41\xe1\xc4\x26\x19\x05\x1b\xf4\xaa\x36\x2f\xfc\xe9\xcc\x12\x84\x26\xc4\xc0\x99\x9f\x95\x83\x52\x9d\x1e\x94\xa4\x81\x2e\x4c\xfb\xa3\x61\x2f\x90\x75\x8e\x82\x0d\x63\xcb\x50\x99\xef\x24\xb2\x62\x31\x63\xac\x6d\x68\xa3\x2c\xd5\x92\x74\x34\x9c\xfe\x2c\xd1\x2e\x44\x80\x39\x5e\xaf\xac\xcd\x75\x29\x1e\x2c\xfb\x1d\xdf\x89\xf7\x2e\xc8\x77\x1f\xd0\xfb\xd6\xe2\x57\x26\xf5\xa6\x3c\x37\x97\x2a\x29\xda\x0d\xe9\xbd\xca\xdd\xf3\x0f\x48\xe1\xea\x62\xd3\xa6\xfb\xb5\x8b\xb3\x2f\x56\xcd\x43\x0e\xb1\xe1\x3e\x60\x0a\xc5\x06\xa1\x42\xce\x65\x7d\xd7\x9e\x63\xba\xb0\xb0\x61\x57\x25\x16\x70\x5c\x29\xde\xef\x6e\x5f\x17\x1c\xdf\x29\x34\xfb\xd9\xdd\xe3\x87\xcf\x6e\x7b\xdd\xe3\x47\xd2\xee\xda\x1a\x39\xd3\xaf\xfd\xad\xcf\xf4\x83\x70\x3a\xc2\xc9\xf2\x23\x9b\x08\xc0\xe9\x5d\x6e\xea\xaf\x39\xc4\x9b\x99\x3b\x1f\xe4\x34\xdf\x83\x8e\x1d\x12\x8e\x93\x8a\x43\xbb\xfc\xd2\x6d\x42\x20\xde\x6b\x99\x30\x94\x1a\xe4\x0c\xe7\x67\x50\x89\xfe\xe4\x8c\x98\x55\xdc\x81\x97\x19\x8b\xaa\x40\x8b\x2c\x90\x4e\x83\x9c\x6e\xe8\xdc\x64\xf8\x3a\x23\xa7\x48\x9f\x3d\xa3\x29\xed\x13\xf3\xcd\xe2\xa9\x36\xfc\x00\x0f\xc2\x89\x3f\x1e\xdf\xb0\x34\xa0\x41\xe9\x9b\x1b\x79\x54\x6e\x59\x2b\x6c\xe0\x4e\x04\x1a\x6a\xb3\x8b\x27\xe3\xb8\x0b\x7e\x8f\x9a\x9e\x23\x9f\x12\xe9\x56\x47\xee\xfc\x62\x17\x3b\x4a\x4d\x87\xa3\x96\x5c\x66\x25\x9f\xdd\x3c\x81\xc4\x2e\xbe\xbe\x63\x26\x08\xcb\xf0\x4a\xe4\x23\xdf\x37\x2c\x38\x9d\xda\xcd\x43\x18\x4d\x67\xd9\x7d\xe6\x94\x93\x87\x4a\x74\x77\xa0\xb3\x87\x22\x8e\x81\xc6\x28\x2c\xf4\x71\xe7\xa4\x12\x30\x5a\xf6\x10\x36\xf9\xe4\x6c\xa0\xbc\x0d\x5a\xe1\xb5\x95\x7a\x7a\x0a\xf5\x70\x8d\x40\x0e\xa8\x2b\x03\xbd\xb5\xeb\xe6\xdd\x3b\x6d\xde\x5d\x6d\xb7\x95\x36\x88\x6e\xbb\xe1\x69\xca\xf3\xf5\x27\x53\xbb\x7f\xba\xee\xdb\xb5\x3b\x1a\x91\xcc\x8b\x34\xe1\xe6\x21\x05\x1c\x80\x85\xc6\xd5\x9a\x88\x8a\x94\xd8\x90\x1d\x55\x1f\x26\x21\x3d\xb8\xbc\xce\xe5\x78\xa5\x95\xc4\x25\x55\x51\x44\x56\x07\xe7\x65\x3c\x48\x70\xf6\x40\x4a\x25\x22\xff\xee\xda\x03\x07\x41\x2f\x19\x9b\xb0\x79\x22\x53\x47\xdf\xb2\x1a\x43\xd9\x39\xd8\x11\x20\xd8\xaa\x33\x12\xfa\x22\xea\xa3\x20\x1e\x75\x0f\xf7\x02\x6f\xb7\x87\x8c\x2f\x0b\x07\xa6\x39\xe1\x65\xe9\xa1\x4a\x8a\x2e\xab\x8f\x93\xdd\x10\xbf\x40\x31\x45\x3b\xfa\x56\x8a\x8b\xc9\xba\x5e\x14\x19\x53\xab\xc4\xf5\x05\x3a\x2c\x7b\x94\xcc\xcd\xf1\x38\xbe\x42\x7e\xd2\x0f\xb3\xc4\x4f\x6e\x10\x53\x2f\x7d\xc6\x37\x96\xb8\x83\x9f\x65\x8d\xc4\xcf\xd6\x86\x0b\x06\x4a\x57\xb7\x94\x1b\xad\x39\xce\x90\x04\xa5\x02\x37\x48\x88\xff\x06\xba\x8d\x38\x41\x61\x14\xe1\x04\xa2\xcf\xc6\xb3\x0c\x04\x08\x3d\x0a\x1f\xc4\x4c\xa4\x3a\x46\x4a\x86\xec\x81\xb6\x62\x04\xa4\xe3\x1a\x3f\xb9\x46\x68\xa9\xb1\x08\x09\xc4\x92\x56\x32\x2e\xd2\x47\x86\x52\xc1\x50\x2a\x68\x34\xf6\xdb\xc1\x11\xcc\x27\xbd\x06\x9c\xfa\x01\x1a\xc4\x51\x9a\xf9\x91\xde\xbc\x35\x89\x94\x3a\xc7\x6e\xc5\x9a\xc0\xfb\x34\x3c\x43\xbf\x6f\xa0\xda\x75\x7b\x40\xff\x67\x73\x87\x31\x0a\x37\x3b\xf4\x7f\xc5\x9a\xb1\x58\xd3\x89\x85\xda\xb3\x8d\x22\xff\x82\x38\x64\xb0\x03\x3d\x46\x14\x32\xc1\xc4\x1f\x24\x12\x59\x41\xbe\x32\x1b\x33\xb6\x0c\x24\x74\xda\xc6\xc7\x1d\x7a\x52\x55\x5f\x9c\x2f\x98\xbb\x45\x20\x83\x61\xfe\x6e\xe2\x8f\xed\x6f\xf6\x58\xf4\x31\xc0\x2b\x84\x25\x56\x18\x09\x65\xc1\x29\x2f\x13\x88\xcc\x28\xfd\xf0\xc1\xc8\x64\x92\xe0\xad\xcc\x0d\x3e\xf6\x58\xd1\xc3\x60\xa8\xff\xa7\x47\x0f\x9b\x23\xa6\x2e\x22\x22\x12\x1e\x9a\xd3\xd0\xdc\x08\x62\xee\x1a\x73\xa3\x88\xb9\xab\x3e\x52\x24\xb1\xfb\x73\xbb\x1e\x55\x4f\xc3\x78\x5b\xf6\x63\x22\x5d\xec\xda\x83\xa3\x15\x06\x1c\x2b\xe4\x98\xf2\x58\x69\x40\x73\x09\x85\x4b\x1a\xfc\x92\x49\xa0\x52\x75\x86\x1c\x9b\xf8\x03\xfb\x25\x91\x38\xf8\x3b\x8c\xe0\x5e\xfd\xad\x15\xe6\xd7\x9d\xd6\xb2\xe5\xf5\x38\xec\x2f\x13\x54\x02\xb0\x6d\x4d\xb5\xaf\x38\x1a\x2c\x83\x4d\xa3\xe5\x3d\x75\xb3\xd4\x3e\x4c\x82\xf6\x7c\xe3\xbb\x74\xe4\x37\xda\x3a\x48\xf2\xb2\xa1\x83\x4b\x47\x7e\xbb\xde\x30\x5f\x36\xd7\x2d\x25\x9b\xda\xab\x24\x9c\xe2\x49\x50\xef\xd4\xac\xb6\x7f\xca\xab\x69\xff\x73\x30\xd4\xdb\xc1\x97\xd3\xcf\xc1\xb0\xe8\xde\x41\xed\x7a\x1c\xe0\xe5\xc1\xb0\x6f\x7d\x9d\x25\x8e\xd7\xcb\x17\x63\x3f\x98\xf8\x91\xed\x73\x6c\x07\x86\x07\xfa\xeb\xa9\x1f\x2c\xfb\x51\x1a\x5e\xbf\x6a\xe8\x83\x40\x3e\x85\x69\x5c\xaf\xd5\x1b\xfa\x88\xb3\x4f\xaf\xd6\x5e\xad\xe9\x33\x44\x3e\x7d\xc1\x49\xcc\x5c\xaf\x2d\x5f\x23\xc7\x37\xaa\x23\x5b\x1e\xe1\x6b\xed\x83\x8f\x75\xe2\xa2\x71\x37\x02\xe3\x7d\x32\xd0\x27\x37\xf1\xfb\xfd\x30\xb3\xbe\x5c\x1e\xe3\x0b\x7f\x70\xf3\xd8\x77\x40\x62\xf5\xc0\x93\xbe\x68\xe0\x65\xbe\x56\xc4\x23\x5b\x22\xf0\x4c\x56\x86\x66\x16\xca\xd6\x81\xf8\xdd\x68\x89\xdf\x84\xea\xf9\x6f\x42\xec\xe2\x37\xfd\x95\x93\x76\x6e\x5f\x0a\xbf\x18\x21\x53\x0c\x28\xfd\x1a\x77\x58\x14\x1d\x4e\xad\xd2\x53\x96\xa8\x4f\x82\x36\xf3\xb7\xb1\x52\x83\x50\x22\x6d\x56\x26\x40\xf1\x46\xd0\x9d\xfc\x86\x92\x9b\x78\x23\x53\x99\x78\x19\xa9\xaf\x24\x9a\x82\x67\x42\x4a\xf0\x23\xa7\x20\x3a\x2a\x03\x36\x50\x8c\x5e\xa4\xdf\x9c\x4c\x16\x55\x44\x2a\x0a\x48\x99\xd7\x2e\xae\x98\x74\x87\x62\x63\x5d\xea\xb6\xeb\x5e\xb1\x36\xd9\x53\xe9\xaa\xdb\x6e\x79\x0a\xe1\x75\xdb\x6d\x2f\x9f\xf8\x6e\xbb\xe3\xa9\xa3\xd7\x6d\xaf\xe9\x37\xc2\x3a\x29\x77\x3b\x35\x8f\x51\x6b\xb7\x03\xf8\x08\x4a\xe9\x76\x1a\x9e\x4c\x2b\xdd\x4e\xcb\xb3\x51\x4b\xb7\xd3\xf4\x64\x0a\xe9\x76\xda\x9e\x4c\x3f\xdd\x0e\xe0\xa5\xd0\x4c\xb7\xb3\xe6\xe9\x54\xd3\xed\xac\x7b\x3a\xdd\x74\x3b\xaf\x3c\x83\x48\xba\x6b\x35\xcf\x42\x4e\xdd\x35\xc0\x9f\x2d\x89\xee\x1a\x60\xcf\x48\xa3\xbb\xd6\xf2\x0c\xe2\xe8\xae\x01\xe2\x84\x8c\xba\x6b\x80\x73\xbe\xce\xba\x6b\x1d\xf9\x02\xdd\xcb\x97\x6c\x77\x8d\x5f\xad\x93\xc5\xdc\x5d\x7b\xe5\xf1\xa5\xda\x5d\xaf\x79\xf9\x12\xee\xae\xd7\xbd\x7c\x71\x77\xd7\x01\x9d\x9c\x82\xbb\xeb\xd0\xb8\x60\x34\xdd\xf5\xd6\xed\x99\xd7\xa9\x3d\x5d\x1e\xfc\xf5\x97\x07\xbd\x11\x1e\x7c\x26\x9d\x82\x95\x42\xdd\x80\x68\x9a\xb3\x74\x36\x25\x03\x83\x59\x7c\x6a\xa9\xdf\x20\xc7\xd3\x90\xe6\xe8\x87\x0d\xf4\x82\x43\x7e\x61\xb1\x08\x11\x4e\x1a\x0f\x78\x5d\x51\x68\x8e\x2f\xda\x39\xc2\x43\x9c\x60\x38\xe8\x25\xe1\x05\x9c\xc9\xc2\x28\xcc\x72\x30\xe9\x6c\x8a\x13\x50\x5d\x6f\x68\xe9\x39\x24\x28\x9b\xb3\x8b\x09\x8e\x32\xad\x00\xca\x62\x34\xf2\xa3\x60\x8c\x95\x71\x93\x61\xf7\xad\x90\x15\x9b\x1a\xa8\x6a\xba\x03\x4a\xba\x6f\x1a\x4b\x9e\x9a\x40\x85\x51\xb6\x2e\x69\xe8\x87\x72\x7d\xa1\x98\x50\x67\xc7\x3c\xe6\xe7\x35\xa8\x12\xfe\x13\x81\x0a\x2f\x64\x6c\x94\x43\x84\x15\xb1\x98\xa6\xff\x02\x48\x97\x21\xbe\x72\xa1\xe8\x6c\x5e\x42\x78\x8f\xa3\x80\xbe\x7e\x55\xcb\x73\x82\x03\x2c\x41\x67\xcc\xab\xff\x40\xd6\x9c\xb0\x1d\x81\x45\x67\x07\x6e\x54\xad\x1a\xad\x38\xb1\xaa\x77\xec\x68\xb9\x5b\x5a\xac\xc6\x5e\x94\x35\x1b\x8b\x36\xb1\x58\x8d\x9d\x71\xec\xdf\xa5\x4a\xa7\x05\xef\xf3\xf2\x77\x24\xa5\x15\x4a\xc1\x1e\x92\x5f\xdd\x64\xf8\x00\x92\x03\x19\xaf\x6d\x79\x97\x15\xfa\xdb\xa5\x8b\x2e\x6f\xab\xcc\x8a\xc8\x4b\x2f\xa6\x42\xc8\xa1\xbd\x15\xb8\xa1\x0d\x3b\xce\x16\xcd\xc2\xf6\x35\xcb\xbe\x7a\x93\xd9\x8c\x9f\x17\x72\x17\xb4\xa1\xb2\x48\x3e\xed\xbc\xfe\x69\x78\x76\xa7\xe4\xd9\xb9\x39\x77\xf8\x05\x53\x55\x6d\xee\x38\xaa\x16\x15\x8c\x35\x4f\x6d\xe1\x21\xe6\x46\x68\xeb\x88\x32\xdf\xd6\xac\x67\x64\x34\xc9\x6b\x02\x0f\x45\x44\xea\x93\x99\xb9\xd9\xae\x3f\x9d\x8e\x6f\x58\xc3\x7e\x72\x31\x23\x2c\x3c\x2d\xf2\x57\x64\xfc\x7a\x65\x9a\xc4\x59\x4c\x70\x94\x39\x77\x91\xe1\x84\xbe\xfb\xd8\x15\x2c\x9d\xfa\x93\xac\xf3\xd7\xc8\x3a\x10\x30\xfa\x2f\x88\x4b\x64\xcd\xa9\x54\xc2\x44\x02\xb6\x58\x7a\x8f\x87\xb2\x5c\xb7\x4e\xaa\x9c\x30\x66\x21\x95\xa4\xaa\x4b\xed\xe6\xcf\x26\xe9\xb9\xf8\x4a\xa7\x65\xe7\x22\x27\x84\x4d\x6c\xd0\xe1\x5b\xf1\xfb\x29\xfd\x91\x86\x11\x0b\xc6\x4a\x58\x46\xed\xba\x5e\x63\x7f\x55\xf4\x55\x4d\xe3\xcb\x96\x57\xa5\x6a\xb5\x50\xdf\xdf\x6a\x6b\xd6\x14\x36\x03\x10\xdd\x6b\x12\x6d\xb0\x51\xb5\x18\x80\xf0\xb4\x37\x85\xb7\x63\xb9\x26\xd8\x9e\xab\xf8\xd4\xe4\xa4\xb5\xeb\xce\x5a\xab\xdd\x68\xd6\xea\x1e\xaa\x5d\xe3\xe1\x20\xf0\xfb\xeb\xaf\x2c\x79\x15\x6b\xd7\xaf\xd6\xfb\x7e\x30\x18\x62\x0f\x06\xa6\xd9\x68\xb7\xd6\x3a\x6a\xb9\x33\xe7\x8d\x98\x96\x46\x4f\xee\xc5\xbe\xc8\xa4\x67\xdb\xbb\xae\xfc\x29\xc2\xe0\x5e\x3d\x7f\x0f\xa9\x77\xdc\x3b\x86\xfb\xfa\x9a\xcf\x06\x45\xe2\x9c\xc0\xe3\xe9\x05\x51\xe8\x88\xc0\xbb\x7f\x2e\x95\xde\x3f\xe5\x0f\x67\x36\x97\x10\xe9\x33\x21\x38\xb3\x00\xf9\xab\x54\x2a\x12\x4c\xea\x29\x8e\xbe\x22\xf9\x25\xec\x75\xad\xaa\xe6\x23\x8e\xbe\x96\x04\xd8\x68\x55\x2d\x00\x21\x94\xb1\xe2\x92\x6e\x82\xbb\x9f\x71\xc8\xae\x72\x43\x61\xbf\xee\x57\x86\xb4\x86\xa4\x31\x45\x4b\xa8\xa6\x8b\x0f\x4a\xe9\xba\x56\xba\x5e\x58\xba\xa1\x95\x6e\x14\x96\x6e\x6a\xa5\x9b\x85\xa5\x5b\x5a\xe9\x56\x61\xe9\xb6\x56\xba\x5d\x58\xba\xa3\x95\xee\x14\x96\x5e\xd3\x4a\xaf\x15\x96\x5e\xd7\x4a\xaf\x17\x96\x7e\xa5\x95\x7e\x55\x3c\x3b\x35\x6d\x76\xe6\x4c\x66\x5d\x2b\x5e\x3c\x9b\xf5\x86\x56\xbc\x78\x3a\xeb\x4d\xad\x78\xf1\x7c\xd6\x5b\x5a\xf1\xe2\x09\xad\xb7\xb5\xe2\x6d\x83\x1b\xac\xae\x12\x86\xfc\x39\x8c\x2e\x48\xd5\xd0\x1f\xf7\x6d\x62\xb3\x4f\xb6\x81\x53\xeb\x40\xf5\xe1\x93\x75\x50\x06\xf0\xc9\x3a\x00\x01\x7c\x6a\xda\xd0\xe9\xe5\x77\xd0\xea\x37\x82\xc4\xce\x4e\xc5\xf7\x50\xdf\x43\x03\x0f\x05\x9e\xb4\x40\x3d\x84\xd6\x3c\xb2\x85\xd6\xce\x74\xde\x10\xd0\x7a\x81\x87\x44\xd5\x7c\x84\x3c\x84\xea\x0d\x0f\x9d\x9c\xd6\x8d\x7a\x03\x5a\x8f\xb6\x44\xab\xe6\x8b\x96\xd4\x5b\x23\xf5\x1a\x46\xbd\x3e\xad\x27\x90\xf4\xa5\x7a\x4d\x0f\xa1\x06\xb4\xd7\x34\xea\x15\xf5\xaf\x25\xfa\xd7\x5a\xa8\x7f\x6d\xd1\xbf\xf6\x42\xfd\xeb\x88\xfe\x75\x16\xea\xdf\x9a\xe8\xdf\xda\x42\xfd\x5b\x17\xfd\x5b\x5f\xa8\x7f\xaf\x44\xff\x5e\x2d\xd4\xbf\x7a\xcd\x63\xfd\xab\x9b\x04\x53\xd4\xc1\x7a\xdd\x63\x1d\xac\x9b\x14\x53\xd4\x43\x82\x25\xed\x61\xdd\x24\x99\x42\x12\x6d\x7a\x9c\x44\x4d\x9a\x29\xec\x63\x4b\xf4\xd1\x24\x9a\xc2\x3e\xb6\x45\x1f\x81\x6a\xcc\x4e\xbe\x7b\xe7\xe8\xa4\x87\x50\x9b\x76\xd2\xa4\x9b\x80\x56\xb4\x76\x92\xd0\xdb\x2b\x5a\xd1\x24\x9c\x01\xad\x68\xef\x64\xdd\x43\xa4\xa3\x27\xa7\x75\x93\x72\xfa\xb4\xa2\xb5\x93\x84\x63\x34\x6a\x50\xd1\x24\x9d\xa2\x3e\xb6\x45\x1f\x1b\x76\x5e\xe3\xea\x23\xa1\x39\xda\xc7\x86\x9d\xd9\x38\xfb\xd8\xe6\x7d\x6c\xd8\xb9\x8d\xab\x8f\x2d\xd1\xc7\x86\x9d\xdd\xb8\xfa\xf8\x2a\xef\xa3\x9d\xdf\x38\xfb\xd8\x12\x7d\xb4\x33\x1c\x57\x1f\x09\x63\x64\x7d\xb4\x73\x1c\x57\x1f\xd7\xf3\x3e\xda\x59\x8e\x93\x56\x9b\x1e\xef\xa3\x9d\xe7\xb8\xfa\xd8\x10\xb4\xda\xb0\x33\x1d\x57\x1f\xd7\x44\x1f\x9b\x76\xa6\xe3\xea\x23\x59\xfe\xb4\x8f\xcd\xba\x7d\x41\xee\xee\xba\x89\xb5\x05\xb8\x36\xed\x5c\x67\x77\xd7\xde\x49\x32\xac\x64\x6d\x9d\x9c\x36\xed\x5c\x67\x77\xb7\x60\x41\x76\xa0\xa2\x9d\xeb\xec\xee\x3a\x3a\xd9\xf2\x50\xa3\x09\x15\x4d\xd2\x29\xea\x63\x3d\xef\xa3\x9d\xe9\xb8\xfa\xd8\xca\xfb\x68\x67\x3a\xae\x3e\xc2\x44\xd2\x3e\xda\x99\x8e\xb3\x8f\x35\xd1\x47\x3b\xd3\x71\xf6\xb1\xe9\xb1\x3e\xb6\xec\x4c\xc7\xd5\xc7\x9a\xe8\x63\xcb\xce\x74\x5c\x7d\x6c\x8a\x3e\xb6\xec\x4c\xc7\xd5\x47\xc2\xca\x69\x1f\x5b\x76\xa6\xe3\xea\xe3\x2b\x31\x8f\x2d\x3b\xd3\x71\xf5\x91\x2c\x0f\xd6\x47\x3b\xd3\x71\xd2\x6a\x9b\xd3\x6a\xcb\xce\x74\x5c\x7d\x6c\xe4\x7d\x5c\xb3\x2f\xc8\xbd\x3d\xb7\xa0\xda\xa1\x9d\xb4\x73\x9d\xbd\x3d\x7b\x27\x81\xe6\x80\x07\xb4\xec\x5c\x67\x6f\xaf\x40\x0c\x68\x83\x08\x68\xe7\x3a\x7b\x7b\xf6\x4e\x12\xde\xd1\x80\x61\x6d\xdb\x45\x1d\x57\x1f\xc9\x7c\xd0\x3e\xb6\xed\x4c\xc7\xd5\xc7\xa6\xe8\x63\xdb\xce\x74\x9c\x7d\xac\x89\x3e\xda\x99\x8e\xab\x8f\xf5\xbc\x8f\x76\xa6\xe3\xea\xe3\xba\x98\xc7\xb6\x9d\xe9\xb8\xfa\x08\x34\x47\xfb\x68\x67\x3a\xae\x3e\x82\x48\x4e\xfb\x68\x67\x3a\xce\x3e\x36\x3d\xde\x47\x3b\xd3\x71\xf5\xb1\x25\xfa\xd8\xb1\x33\x1d\x67\x1f\xeb\xbc\x8f\x1d\x3b\xd3\x71\xf5\xb1\x21\xfa\xd8\xb1\x33\x1d\x57\x1f\x5f\x89\x79\xec\x34\xcd\x05\x09\xd7\x28\x19\x4e\x26\x38\x08\xfd\x8c\x39\x95\x81\xbb\x82\x5a\x8e\x1c\x71\xd1\x06\xaa\xc0\x7f\x97\x90\xaf\x6b\x58\x69\x99\x3a\x2b\x53\x27\x65\xfa\xf6\x32\x0d\x56\xa6\x41\xca\x0c\xec\x65\x9a\xac\x4c\x93\x94\x09\x0c\x6d\xae\xa6\xaa\xdc\xb1\x58\xea\x2e\x18\xd0\x16\x32\xa5\x8b\x6c\xba\x7e\xe6\xdb\x0e\xe6\x7e\xe6\x8b\x50\x3e\x7e\xe6\xbb\x95\x63\xd1\xdb\x30\x4b\x4f\xe2\xcc\x1f\x0b\x98\xd1\x96\x9f\xf9\xd4\x83\xe4\x25\x5a\xb7\x40\x87\x3a\x1f\xf0\x30\xe3\xd0\x85\xc7\x09\x94\x37\x3a\xe3\x4c\x79\x25\xd0\x3c\xcd\x41\xfe\xfc\xf3\xcf\xa8\x0d\x17\x6f\xb5\xeb\xf5\x5a\x7e\xdf\x96\x97\xf8\x17\x6a\x36\x0c\xe2\x50\xfb\xb2\x8b\x36\x10\xa8\xdd\x87\xe3\x38\x4e\x2a\x52\x27\x57\x15\xdd\xbb\xab\x73\x50\xf6\x03\xda\x90\x9e\xf4\x85\x23\x50\xaf\x54\x2a\x39\x6e\x4b\xa8\xd3\xa2\xf9\xd2\x5e\x41\x30\xd1\x56\x95\x2a\x6c\xec\xfa\x59\x5e\x95\xe1\x9c\x2b\x67\xe5\xb7\xe5\xb5\xb3\x26\x38\xa6\x9a\xd5\xc1\xcd\xd3\xcd\x1a\x5c\x62\x91\xce\xb6\xca\x74\xf6\x83\xb5\xb3\x1f\xee\xda\xd9\x0f\xd6\xce\x7e\x28\xdb\x59\xb3\xb7\xb2\x13\x55\x45\x74\x9f\x07\x9b\x82\x9c\x7a\x76\xff\x41\x30\x78\xa7\x6e\x0c\xe0\xa3\x68\xf3\xa4\x2a\xcc\x2b\x3f\xc7\x1b\x52\xd1\x79\x5b\xc8\x77\x97\x19\xc6\x3b\xbd\xdf\x16\xba\xf7\x70\x5c\x71\xa1\xa2\xeb\x7f\x81\x09\x5c\x61\xec\x9e\xda\xef\x2e\x76\xd9\x2d\x59\xa5\xb2\xab\x5c\x4b\xec\x2e\x7c\x1f\x41\x69\x61\x57\xb9\x8b\xd8\x75\x5e\x42\xcc\xbf\x71\x38\x62\xb9\x81\x61\x0e\x59\x04\x9e\x00\xc6\x54\x2d\x5a\x22\x59\x39\xb8\x21\x14\xb2\x7a\x50\xb0\x82\x53\xa6\xb8\xa1\x83\xc7\xfc\xfa\xdf\xd8\x78\xe1\xf3\xb9\x41\x0b\x2e\xef\x4a\x1e\x41\x83\x7c\xb5\x7b\x38\xd0\x5f\x02\x49\x4d\xf5\x75\xed\xa1\xd4\x43\xea\x15\x1a\xf0\x49\xb4\x81\x7c\xb4\x84\x2a\x95\x3e\xfa\x89\x6e\x8e\x95\xff\x4b\x7e\x06\x55\xc2\x06\xae\xd1\x12\xca\xa4\xf6\x44\xc0\xe2\x88\x4c\x53\x4a\x57\x2a\x8d\x53\xde\x6c\xa0\x65\x94\x56\xa1\x5a\x5f\x33\x7a\x13\x58\x69\xe7\xff\x72\x58\xc1\x76\x5c\x19\xa0\x9f\xd0\xff\x7d\x1c\xac\xb4\x43\xd0\x5c\xac\xfa\xe8\x77\x34\x40\xbf\x13\xc4\x1e\x1e\x19\x4d\x00\x9c\x8b\x0c\x41\xa4\xd2\x47\x5f\x1f\x78\x70\xe4\xdb\xea\x63\x57\x9a\xf4\xb9\x89\xf7\xcb\x04\x59\xe3\x7e\x62\x9a\x8b\x22\xac\x06\x13\x8c\xc3\x59\xcc\x51\xfa\xae\x61\xcd\xd8\xba\x14\x46\x2e\xfb\x5b\x6d\x8b\xef\x57\x71\x79\xd3\xe1\x2b\x8f\x2f\xa6\x5c\xe6\xab\x19\xf9\xf7\xb7\xda\x56\x93\x01\xe7\x24\xcc\xc9\x55\xff\x50\x53\x70\xa7\xd0\x0e\xf3\x27\x4e\xf6\xf2\x7b\x88\x89\xa3\x4e\x65\x62\x22\x76\x27\xfe\x80\x4c\x86\x92\x19\xde\x9c\x0f\x56\xcc\x9c\x93\x3c\x9b\x3d\x9d\x97\xc2\x0c\xec\x2c\xb2\xb5\xc3\x02\xaa\xf1\xb7\x76\x31\xfb\xe7\xc7\x64\xa3\x8b\xed\x25\x8b\x33\x84\x76\x30\x0e\xfa\xfe\xe0\x33\x8b\xab\x39\x89\x03\x58\x52\x84\x66\xc4\x7c\xc3\xcb\xde\xce\x5b\x22\x02\x59\xc4\x03\x30\x73\x82\xaf\x8a\xb5\x1c\x58\xb8\xd0\x56\xf6\x09\x00\x66\xcc\x23\x56\x7d\x6f\xe7\xed\xca\x76\x44\x63\x95\x83\x01\xd5\xce\x5b\x8b\xc1\xcf\xd4\x61\x2e\xc3\xcc\x0c\x0b\x4c\x66\xdc\xa2\x29\x0b\x41\xc5\x05\x12\xfa\x68\xbb\x67\x96\x42\x79\xd0\x42\x72\x28\x0f\xb5\x3c\x8f\x51\xfe\x1e\xdf\xa4\x59\x82\xfd\xc9\x66\x14\xb0\xde\x59\xac\x23\x63\x66\x16\x2b\xc0\x79\xac\x01\x9b\x90\x7d\x84\x27\x18\x82\x8c\x83\x31\x26\x9d\x27\x16\x2b\x13\xfc\xe7\x23\x7c\x9d\xd1\xd7\x76\xf1\x1d\x5f\xbe\x65\x31\x53\xa1\xf5\x95\x74\x1c\x0e\x70\x85\xa3\x20\x6e\xea\x05\x2e\x36\xfb\x49\x65\xd6\xb6\xf0\x3f\x65\xd6\xee\x31\xba\x60\x38\x3c\x0a\xd3\x85\xc7\xf6\x9b\xd1\xcd\x49\xde\xa1\x3e\x1e\xc4\x13\xe6\x75\x4f\x08\x22\x8c\x67\x69\x39\x92\x11\x5d\x2c\x25\x8e\x17\xf4\xa6\x32\xb7\x0b\x9a\x6f\x84\x79\x60\x83\xf3\xde\x65\x1e\xac\xe5\xf2\xb5\x6a\x34\x2e\x87\x63\xa6\xcd\xe7\x9f\x21\xb3\xeb\xa5\xf5\x48\x23\x4a\xa3\x0d\x14\x5e\xb2\x29\xac\x39\x56\x62\x7c\x89\xd1\xde\x2f\x70\xfe\x4c\x67\xfd\x14\xff\xf7\x0c\x47\x59\xc1\xe9\x19\xf0\x15\x0e\x0c\x73\x0d\xa0\x75\x7c\xb4\x09\x31\x27\x81\xfc\x31\x2a\xc7\x74\xa0\xa1\x60\x45\x00\xf1\x90\xda\x95\xd5\x55\xc4\x66\x24\x7f\x67\xcd\x96\x5b\x1c\x35\x86\x9a\x9e\xe7\x16\x82\x10\x09\x46\x34\x0a\xe7\x68\x83\x5e\x18\x16\x5c\x9c\xd8\x79\x5b\x64\x70\xcd\x37\x9d\x45\xe2\xd4\x75\x9a\x4f\xc2\xc7\xf7\x2e\x7c\xa0\xff\x9c\x26\x38\xc5\xc9\x25\xa6\x62\x48\x3c\x23\xa2\xbc\x24\x7e\x80\x1a\xc3\xcf\xc2\xfe\x98\x71\x60\xb4\x95\xa0\xb7\x49\xe8\x47\xe8\x1d\x75\xcf\x44\xc3\x70\x8c\x71\x34\x58\x19\x00\x08\x1e\xf2\x19\x22\x60\x6b\xf4\x73\x72\x04\x45\xfe\xcb\x8f\xd0\x6e\x32\xeb\xdf\xa0\x3f\x46\xe4\x3f\x2b\x57\xb8\xff\x9f\x17\x13\x3f\x1c\xaf\x0c\xe2\x89\x5d\xde\x39\x39\xe2\xcd\x15\x88\x3d\x72\xa1\xd2\xd2\xcf\xb3\x3c\xdf\x4b\x34\x20\x07\x05\x9a\x32\xe9\xf9\xb3\x67\x64\xd0\x81\xf4\x44\x3a\x24\x50\x12\x51\xa5\x50\x15\x66\x9d\xfe\xfa\x13\xad\xae\xc6\x97\x38\x19\x8e\xe3\x2b\x52\x07\x36\xbe\x3a\x4f\x07\x4a\xea\xd5\x3b\xd5\x9f\x48\xd9\xd7\xe2\x73\x43\xfe\xbc\xae\x7f\x6d\xb2\x3d\x8c\x35\x06\x78\x02\x2a\x04\xac\x68\x77\x75\x15\xf1\x66\x51\xbf\x4e\x8a\x00\xca\xd0\x74\xed\xb5\xa8\xd2\xc8\xab\x88\x32\xcf\x00\x01\x5a\x88\x96\x6a\xaa\xa5\x58\xb1\x67\x80\x0a\x2b\x77\x0b\xff\x12\x82\x94\x4b\x2c\x2d\xf5\x9b\xd2\x77\xf8\x87\x97\xa1\x45\x96\x96\xfa\x8d\xd7\xcf\xdd\x05\x96\x96\xfa\x75\xf6\x9d\xfc\x0b\x1d\xe7\x8d\xc2\xc3\xd2\x06\xf4\xfc\xcd\x1b\x96\x0f\x52\x7e\xdd\xa0\x2a\x40\xe5\x2d\x43\xc8\x6c\x49\x54\xab\x5d\xd7\xea\x4c\xeb\x97\x17\x65\x5c\x8f\x14\x22\x2f\x6f\x75\xea\x60\xcb\xa3\x32\xa0\xff\x55\x69\x84\xbd\xa4\x37\x48\x9c\x94\xf2\x97\x55\x46\x30\xd2\x14\xac\xae\x22\xb2\x4b\xc0\x4d\x0c\x0a\xa5\x85\x44\x17\x8f\xb1\xd2\x5e\xa4\x08\xe0\xa5\x28\x8e\xc6\x37\x74\x39\x6e\xfd\x7a\x70\xb4\x85\xfe\x40\x6f\xd0\x3a\xc0\xe4\x0d\xd6\x6d\x58\xd0\xbb\x38\xb5\xb3\xec\x1b\xef\x2f\x5f\x4b\xca\x59\x40\xac\xab\x15\xc7\xeb\xbf\x50\xe6\x5c\x54\xe4\x34\x8a\x6b\x32\x8c\xd9\x2a\xe3\x89\xa2\x59\x3e\x60\x06\xea\x45\x12\x0f\x72\x4b\x3d\x20\x34\xd8\x1b\x29\x96\x81\xd0\x1d\xe4\x20\x34\x5f\x16\xe2\xd2\x01\x21\x6c\x93\xe6\x29\x2b\x7a\xa6\x8b\x46\xec\xb3\x84\xab\xaa\x7a\x5e\x44\x28\x42\x0e\xc1\x08\xdd\x4d\x38\x42\x0b\x0a\x48\x48\x95\xe7\xcc\x43\x57\x4e\xf7\xf2\xd9\x4b\x2c\x8d\xd7\x9a\x64\x25\x8a\x4b\x02\x96\x53\xc4\x92\x0a\x2f\x20\x69\xb5\x9e\x24\xad\xef\x5d\xd2\x72\xc8\x57\x0e\xf5\xce\xc9\x51\xb1\x9c\xb3\xa8\x7a\xc7\xc2\xd2\x75\x5e\xfe\xc4\xc4\xff\x79\x4c\xbc\xf0\x34\xfb\x08\x2c\x7b\x2f\x1a\x24\x18\x22\x37\x30\xe0\x1a\x48\x26\x87\xe4\x93\xbb\x8c\xa8\x31\x8d\xe3\x0b\xdc\x96\x7f\x45\xb5\xbf\xd5\xe6\x50\x76\x57\x98\x7f\xde\x26\x65\x16\xd8\x05\xda\x4f\xbb\xc0\xdf\x62\x17\xd8\x1e\xe3\x41\x96\xc4\x51\x38\x40\xbd\x38\xc0\xfd\x38\x9e\xaf\xf0\xdf\xee\x15\x29\xfc\xe9\xd7\x85\x76\x84\xed\x9e\xaa\xf0\x27\xcf\x0f\xb5\x03\xc8\xac\x5d\x65\x20\x6a\xbd\x22\x2d\x26\xc1\x47\x59\x48\x8f\x85\x5f\x80\xef\x84\x1f\x4f\xbd\xd4\x9b\xaf\x37\x83\x32\x0b\xac\xe3\xbf\x77\x72\xe4\xff\x39\xeb\xf8\x60\x96\x4d\x67\x59\xf9\x4b\xbb\x83\xc2\x4b\xbb\x83\xc5\x2f\xed\x74\xa9\xee\x40\xbb\xc4\x3b\xf8\x6b\xaf\x83\x1e\x5d\xaa\x33\x75\xf3\xe2\xcd\xc3\x4a\x76\x05\x0d\x7d\x2f\xd2\xdd\x3f\xe9\x84\x7d\xa0\x5d\x6b\xba\x84\xa8\x83\x12\x97\x16\x07\x0b\x5e\x5a\x3c\x65\xb1\xfb\x7b\x30\xdf\xcd\x8f\xc7\x7b\xe8\xb7\x95\x57\x8d\x26\x37\x10\x47\x69\x46\x96\xf7\xc5\x8d\xc1\x7d\xa7\x7e\xb0\xb2\x19\xa5\xe1\x6f\xa4\xb4\xc8\x05\x37\xf5\x03\x99\xfd\x05\x7e\xe6\x4b\x17\xa1\xae\x0b\xd0\x54\xbd\x01\x25\xb5\x8e\x73\x83\x5f\xc5\x00\xf8\xb5\x5a\xb4\xaf\xa7\x15\xe9\xbb\x12\x8a\x00\x51\xcc\xa2\x4c\xf4\x4c\x0b\x66\x05\xb6\x78\x87\xf4\x9b\x01\x8c\xbe\x58\x56\x31\xfb\x97\xf6\xdd\x68\x8d\xc6\xb4\x19\xfb\x29\x8d\x9c\x85\xa6\x71\x1a\xaa\x1e\xf8\xa4\x51\xf2\x9d\xd4\x3f\x8c\x79\x67\x45\x0b\x4b\x1a\x46\xcb\xa8\xae\x35\x72\xe8\x07\xf9\x33\x0c\x94\xc8\x36\xa2\xbe\xa6\xac\x44\x6e\x2b\x0f\xa9\xa5\x36\x92\x87\xd4\x92\x4b\xdb\x82\x6b\xa9\x96\xd9\x4b\x1a\x20\x6e\x87\xc8\x2d\x70\x67\x91\x85\x38\x74\x8a\x78\x87\x33\x29\xe1\xbc\x32\x55\x54\x81\x2f\x46\xb3\x78\xe6\xa4\x3e\x57\x54\x34\x97\xc9\xf1\x97\xf5\x3d\xbf\x08\x92\x50\x60\xfb\x8a\xe1\x21\xa1\x81\x71\xf4\xf6\xf9\xb3\x5b\x2b\xdf\xe4\xcb\xe5\xfa\x55\xa3\xb9\x10\xef\xbc\x5f\x62\xb2\x27\xde\xf9\xad\x78\xe7\xde\xf1\x01\x82\x90\xb8\xe5\x58\xe7\x1e\x0b\xa0\x7b\x5f\xd6\xf9\x97\xb3\xc3\x7c\x49\xcc\xe1\x87\x16\x56\x45\xd3\x01\xd8\x23\xd0\xad\x24\x7e\x14\xc4\x93\x8a\xc1\x01\xab\xd5\x15\x4d\x52\x2a\x86\xc3\x52\x87\x9d\x1a\x5c\xae\xd1\x3a\xf3\x08\xb8\x27\x46\xa5\x33\x2a\x4e\x9c\x0b\x31\xaa\xbf\x77\xe6\x85\xff\x51\x8c\x6a\x75\x6f\xbb\x87\x5e\xad\xbd\x5a\x5b\xae\x23\x46\x1b\x68\x1f\x67\xa3\x38\x40\x0d\x17\xb7\x82\xd0\xde\x77\xe5\x56\x9b\x41\x40\xfd\x07\xd5\x05\x51\x82\x0b\xf0\xd5\x4b\x6a\xd3\x3f\xbe\x68\x95\x06\xfe\x0f\x4e\x62\xc8\x1d\x96\x8d\x30\x4a\x70\x2a\xf1\x45\xa5\x23\xa4\x1c\xeb\x31\x79\x36\xf0\xbe\x13\x2f\x60\x0b\xf1\x0b\xc3\x41\x5d\x8d\xce\xe6\x01\x34\x85\x67\x5f\xd8\x71\x84\xd1\x24\x4e\x30\x15\x1e\x97\x97\xa1\x6f\xae\x51\xe4\xeb\x7d\x79\xb9\xe4\x02\x87\xf9\x5c\x64\x81\xaf\xdd\x2f\xca\xf9\xd3\x02\xff\x66\xa7\x38\x14\xc5\xf1\xb4\x9c\x18\xf2\x91\x93\xa3\x73\x65\x0b\x62\x77\xaf\x89\xbc\x48\x11\xcd\x89\xa6\x16\x22\xba\xfb\x85\x9b\x7d\x22\xba\x6f\x45\x74\xff\x47\x62\x7e\xc5\x24\x27\xf1\xc0\xbf\x50\xf8\x2d\x7d\x70\x96\xcf\xb7\x86\x00\x5c\xa9\x14\x8b\xc0\x55\xf4\xf5\xab\xfe\xea\x4e\x5b\x8c\xbd\xc7\xf3\xe3\x0a\xac\xae\xa2\x4f\x04\xbe\x5a\x2f\x34\x22\x05\x80\x66\x41\x94\xb9\x1a\x85\x63\x8c\x2a\x3f\x54\x72\x5f\xeb\x3c\x06\x37\x78\x1c\x1a\x31\xb7\x85\x09\xa7\xa1\xc8\x0c\xc5\x96\x84\x54\x15\xa5\xee\xd8\x0d\xf1\x78\xcb\xec\x5e\x12\x05\x2d\xc4\x4b\xfe\xde\x8e\x5b\x96\x1c\x5d\x34\x49\xd6\xe3\xf2\x95\x3c\x13\x12\xb4\xf6\xd7\xe7\xf9\x78\xdc\x24\xe1\xe5\x62\x62\x1b\x31\xaf\xc5\x97\xe3\xdd\xcd\x7a\x1e\xeb\x99\x3c\x49\x1f\xcd\x44\xe0\x36\x07\xd1\x43\x3f\x4d\xc9\x42\x5e\x26\xa8\x05\xe8\x3d\xbe\x41\x5b\x38\x09\x2f\x69\x4e\xc8\x1d\x3e\x28\x8d\xe2\x98\xd3\x87\x6f\xdf\x6f\xed\x34\xf2\xd6\xc4\x73\xc9\xc4\xe3\xbd\x38\x1a\x86\x17\x33\x96\x89\x32\x86\xac\x90\x69\x51\x7e\xc9\x24\x9e\xe2\x24\xbb\x41\x7f\xd2\x63\x31\x78\x93\x02\xf3\x3d\x19\xd1\x1c\xc7\x29\x79\x08\x23\x96\x2e\x20\x8b\x85\x2f\xcd\x0a\xda\xc2\x43\x7f\x36\xce\xba\xa8\x85\x2a\xf5\xc6\x3a\x24\x52\xae\xba\xe0\x3b\x12\x9a\xe3\x84\x27\x32\xcf\xc1\x91\xf1\x9f\x87\x66\x98\xb1\xe4\x99\x29\x80\xca\x0f\xf5\xd2\x87\x2c\x46\x53\x9c\x0c\xe3\x64\x22\x01\x57\x20\x4b\xe9\x1f\x07\xc3\x8b\xae\x6b\x94\x11\xbd\xf8\x3a\x86\x98\x33\xf5\xc6\xfa\x6a\xb3\xa1\x85\xe0\xa6\x5d\xa1\xa8\x6b\x9f\x72\x84\x94\xc6\x6f\xab\x45\x09\x49\x8b\x12\xc8\x93\x59\x09\x72\xd2\xe2\xeb\x6d\x7e\x16\xd1\x03\xe0\x73\xb7\xa4\xab\x72\xc6\x50\x32\x7e\x03\x1b\xdd\x70\x7f\xb3\x61\x9c\xc0\x29\x26\x6f\xf4\x01\x12\x83\x7e\x0e\x86\x46\xd2\x78\x4a\xed\xfc\xf4\xa8\x98\x61\x2d\x52\xf1\xcf\x7c\xb2\xd6\x69\xfa\xc9\x7b\x83\xf1\xd4\x69\xac\xd5\x6a\x3a\xe0\x82\xec\xf5\x83\xe1\x85\xdd\xf0\x82\x4c\xc4\x86\xf8\xc9\x09\x8f\x14\x77\x05\xc3\x30\xd7\x3b\x5c\x57\x50\x0f\xba\xb2\x2c\xe8\x2e\xf9\x66\xa7\x0c\x36\x50\x0b\x7f\x58\x29\x59\x39\xf5\xc7\x19\xda\x84\xff\x2c\x9e\x88\x96\xbb\xd1\x48\x7e\xed\xf7\x21\x3b\x9a\x48\x3d\x18\xae\xb0\xa8\x24\x15\xde\x19\x0f\xf0\x73\x4e\x2a\x2b\x2e\xcf\xab\x56\x73\xa1\xdc\x2e\xea\xd4\x5b\x0d\x08\xc3\xcc\x91\x14\x96\x79\xd9\x83\xef\x3e\xa3\x55\x42\x3e\x94\x07\x79\x62\x76\xec\x66\x89\xee\x04\xe5\x20\x9b\xd2\xc1\xa6\xe9\xe6\x0d\x7d\x8e\x2d\xd4\x13\xc8\xc9\x7b\x51\x80\xaf\x6d\x35\x4e\x6b\xd7\x4c\x01\x64\x89\xd6\x39\x27\x44\x97\x40\x45\x08\xcb\xe2\x8d\x33\x7f\x7d\x8e\x0d\xaf\x94\xbf\x71\x56\xe2\x5b\xde\x06\x99\x95\x15\xf6\x64\x33\xc2\xc8\xb7\x16\x5a\x34\x7f\x31\xc7\xc8\x42\xfd\xc8\x04\x75\xad\x83\x3c\x2e\xd2\x1b\x8e\x8f\xd5\xb8\x40\x74\x92\xe5\x39\xe6\xc9\xb2\x81\x02\xf3\x34\xbe\x79\xaf\xf5\x39\x43\x2c\xa3\x77\x9e\x1a\xd8\xfc\x3e\x3f\x1b\x03\xc0\x57\x86\xd8\x3a\xba\x66\x71\x91\xc5\x28\x7f\xc5\x3a\xee\x40\x64\x4f\x8c\xb1\x1d\x74\x28\x47\xb3\x63\x60\x2d\x58\x28\xb6\x1c\x75\x6a\xcb\x21\x4d\x9f\xd3\x98\x03\x01\x3f\x57\x9a\x80\xd1\x13\x23\x2d\x7f\xb4\x8d\x75\x99\xf1\x46\xf3\x42\x41\xd9\x3a\xcb\x47\x5f\x7e\x67\x0f\x58\x25\x35\xf1\xdb\xc1\x91\xda\x1d\x70\x9d\xb2\x78\x5c\x1b\xe3\xf6\x07\xb5\x81\xf9\x83\xdb\xc0\x48\xb3\xf9\x1a\xfd\x51\x30\x7a\xe4\x2f\xaf\x71\xfa\x07\x98\xc3\x18\x1d\x39\xfd\x43\x37\x8b\xe1\x7f\xb7\xe6\x6b\x3d\xe0\x14\xf9\x93\x98\x03\xd3\x4d\x43\xa3\xb6\x29\xd1\x98\xc4\x69\xed\x6c\x69\xa9\xd8\xa4\x48\x02\x2e\x1d\x7d\x39\xdf\xb0\x04\x31\x63\x7b\x59\x5e\xaf\xc8\x80\x52\x3e\x46\xdc\x6b\x43\x2f\x13\x6c\xa6\x70\x23\x5f\x70\x13\x7f\x28\xd1\x32\x4c\x6d\xe9\xf6\xe7\x47\xaf\xb1\x88\x06\x0f\x10\xc4\x86\x8a\x08\x42\x32\xa4\x42\xa1\x4b\x4c\x58\xac\x9a\x87\x1c\xb2\xe9\x7d\xc0\x14\xca\xa6\x79\x90\x1d\x71\x94\x74\x09\x30\x1e\xd2\x05\x55\x36\xec\xaa\x58\x4c\x0a\xcd\x11\x9e\x6e\x8b\x6c\xd1\x28\x34\x7b\xa0\x1e\x3d\x85\x2e\xcf\x09\x7b\x7b\xe6\xad\xfd\xbd\x7d\xe8\x17\x48\xeb\x3e\x3f\x39\xfa\xe3\xea\x8e\x9c\xe9\xb5\x5d\x59\xaf\xff\x09\xda\xa5\x63\x30\xce\xec\x71\xe3\x5d\xaa\x44\x92\x5f\x16\xe9\x91\x04\x1e\x47\x78\x96\xfa\xfd\x31\x66\xe1\xc0\x24\x74\x8e\x91\x9c\x6a\x91\x42\xd1\xdf\xbc\x43\x6a\x86\x35\x69\x5b\x38\x82\x6c\xca\x88\x19\xda\x32\x1b\x63\x53\x93\x24\xca\x43\x8c\x95\x30\x45\x3e\xa2\x09\x98\xd1\x25\x4e\x52\x88\x5a\x36\xf2\x33\x14\xe1\x8b\x31\x1e\x64\x38\x20\x6c\x78\xc0\x52\xaa\x66\x4c\xe1\x93\xc5\x68\x1c\x66\xd9\x18\x2f\xd3\x00\x97\x2b\x2a\x50\x9c\x24\x71\x82\x82\x18\xa7\xd1\x8b\x0c\xf9\xc3\x21\x1e\xd0\xba\x14\xa9\x17\x29\x4a\xf1\x60\x96\x84\xd9\x8d\x27\x2a\xf6\x67\x19\x0a\x33\xa8\xc4\x6b\x84\x59\x2a\x02\x2a\x84\xe3\x30\x63\x4e\xdc\x34\xaf\x6b\x48\xf8\xf3\x04\x47\x74\x3f\x48\x6d\x8a\x32\x3a\x20\x1f\x68\xe7\x84\xba\x4c\x7b\x2b\xcf\xdf\x5d\x93\xb6\x15\x1f\x52\xde\xcb\x66\xd0\xce\x03\x46\x6e\xbd\x0d\xa7\x86\xcb\xa2\xd3\x42\xc8\x4e\x68\x64\xf7\xc2\xce\x73\xda\x6f\xa2\x5d\xf2\xcb\x92\x38\xee\xfd\x69\xed\xcc\x43\x95\xf7\xa7\xcd\x33\x16\x2c\x00\x7d\x25\x8f\xec\x2a\xa0\xde\xa9\x5a\x92\xc8\xbd\x3f\xad\xd3\x4a\x35\xb5\x52\xb3\xb8\x52\x83\x56\xaa\xab\x95\x6a\xc5\x95\x9a\xb4\x52\x43\xad\x54\x17\x95\xd4\x3a\xb6\xec\x48\xc6\x90\x71\x2f\x43\xd7\xa0\xf5\xc4\xa0\xf5\xec\x83\x66\xe2\x23\x0d\x17\xeb\x13\xbd\x30\x19\x0e\x79\xda\x41\x8a\x34\x0d\xb2\x5a\xab\x91\x2f\xb6\xfe\x9a\x13\xd1\x54\x21\xd7\xad\x90\x1b\xa5\x20\xd7\x9c\x03\x2f\xc1\xd0\x20\x37\x4b\x41\xae\xbb\x66\xc7\x93\x60\x68\x90\x6b\x1a\xe4\xf9\x13\xd9\xf3\x93\xe4\x06\xf5\xf5\x74\xaa\x74\xaa\xfa\x34\xfe\x85\xa9\xc9\xc8\xe8\xe4\x13\xd6\x93\xde\xa4\x19\x9e\xa0\x61\x3c\x4b\x50\x16\x4e\xf4\xb9\x5f\x30\x28\x6f\x84\xaf\xb3\x63\xb2\xfa\xdc\xf1\x63\x2d\x11\x6f\xf7\xe3\x20\x1c\xde\x50\x4e\x48\xe9\xb0\x04\x16\xeb\x6e\x2c\x7a\xa7\xd4\x71\xe0\xb7\x53\x48\x79\x09\xd1\x56\x8c\x4c\x71\xb6\x24\xb9\xbf\xa0\x14\x67\xb3\xa9\xfa\xa1\xc0\xa3\x63\xfe\x61\x7f\xef\x17\xea\xda\x51\x74\xc2\xdf\xfb\xe5\xbc\x86\x36\xd0\xde\x2f\x66\x6a\x34\xa9\x48\x9d\x16\xa9\x5b\xa3\x19\xcb\x4b\x1a\xa6\x32\x9d\xf5\x2f\x31\x11\x15\x5c\x47\xff\x1a\x0d\x7e\x0c\x6d\xd3\xe8\xc7\x5f\x11\x7d\x72\x45\x3f\x96\x8b\xb3\x30\xc7\xa2\x7c\x7e\x1d\x6a\x0f\x73\x2c\x9a\x6d\x88\x66\xeb\x4a\xb3\xf5\x79\xcd\xd6\xd5\x66\xeb\x8b\x35\x0b\x61\x74\xc2\x1a\x5f\x82\x04\x48\xd8\x50\x57\xa0\xab\x6a\x13\xaa\x36\xf8\x62\x86\xaa\x35\x75\x99\x3a\x66\x84\x91\x75\x11\x6b\x45\x40\xad\x35\x7a\xae\xd7\x63\xfb\xd3\x8f\x75\xfa\xb1\x6e\xfd\xd8\xa0\x1f\x1b\xd6\x8f\x4d\xfa\xb1\x69\xfd\xd8\x2a\x6a\xb3\x5d\xd4\x66\xa7\xa8\xcd\x35\xd1\x66\x81\x46\xaa\x14\xe7\x41\x8b\x73\x1f\x54\x8e\x03\x21\x53\x49\x21\xfb\x11\x3d\x48\x72\x57\xa7\xf2\x5a\x92\x3e\x4a\x71\x66\xb5\x88\xbd\x77\xee\xed\x1d\x06\x37\xf7\x32\x03\x2e\xa4\x96\x3e\xa6\xa1\x86\x7e\x03\x22\x44\x95\xdf\xc8\xdc\xf3\x55\x02\xcf\x62\xef\x7d\xad\x57\xac\xd3\x8a\x0d\x56\x71\x4d\xab\xd8\x76\x56\x6c\xd0\x8a\x2d\x56\xb1\xae\x55\x5c\x73\x56\x6c\xd2\x8a\x9d\x33\x81\x9a\x52\xb1\x9e\x57\xbc\xd7\x2e\x56\x14\xa5\x9e\x22\xc2\x63\xc7\x1f\xb3\x94\xec\x2c\x78\x3c\x3c\xde\x25\x7a\x3c\x87\xc3\x18\x9c\x80\x63\x8b\x1f\x6f\xc5\xd7\xea\x84\x87\xa4\x1c\xbd\xc2\x9b\xee\xb8\xd8\x8b\x4e\xa6\x7e\x61\xc7\x93\xdf\xdc\xe6\x1f\xc3\x4b\xfa\xa5\xd3\x5a\x6d\x36\x74\xb5\x9c\x58\x26\x82\x60\x2b\x25\x5d\xa1\x94\xf5\xa1\x7c\x91\x44\x50\xcd\xe0\xe7\xd8\xbf\xc4\x28\x1e\x07\x4e\x56\xbb\x80\xfc\xd0\x3b\xa7\x93\xdb\xd3\xe3\x1d\x2a\x2d\xf6\xfc\xf1\x60\x36\x26\x2b\x2c\xc2\x57\xce\x66\x7b\x2c\x11\x4c\x8f\x26\x82\xa9\x5d\xb7\x82\x26\xfc\x1f\x5a\xe2\x12\x9a\x9e\xaf\xa5\xc7\xf2\xc2\xf4\x68\x5e\x98\xda\x35\xab\xd1\x84\x98\xf2\x3d\x2e\xa0\xd6\xaa\xe8\x0d\xaa\xf4\xce\xa5\xe7\xff\x40\x75\xd4\x45\xb5\xaa\x09\xb1\xc1\x20\x36\x28\x44\x06\xb0\xc5\x20\xd6\x35\x88\xf5\x12\x10\x9b\x0c\x62\xd3\xe8\x56\x85\xb6\xa3\x40\x6c\x94\x80\xd8\x62\x10\x5b\xd6\x5e\x37\x35\x88\xcd\x12\x10\xdb\x0c\x62\xdb\xda\xeb\x96\x06\xb1\x55\x02\x62\x87\x41\xec\x58\x7b\xdd\xd6\x20\xb6\x4b\x40\x5c\x63\x10\xd7\xac\xbd\xee\x68\x10\x3b\x73\x21\xe6\x62\x3f\x05\xaa\x54\x5f\xd3\xab\xeb\xde\x31\x82\xa6\xc9\xee\x73\xb1\x7c\x8f\x45\x44\x4a\x5d\x5c\x03\xaf\x0e\x49\xd7\x7a\x96\x24\x1c\x3c\x5d\x7e\x32\x1b\x64\x68\x14\x5e\x8c\x90\x1f\x05\x68\x1c\x5f\x21\x3f\xb9\x98\x41\xf8\x17\x70\x73\xfe\xef\x99\x9f\x18\x89\x7b\xa0\x01\x1f\x6d\x90\x56\xb8\x14\x67\x51\x1e\x5c\xf4\x69\x11\xba\x4b\x58\x8f\x4f\xbc\xcf\x0a\x06\x09\x4e\x67\xe3\x0c\xc5\xc3\xa2\xe6\x47\x74\x0b\xa8\x5c\xf8\xe8\x25\xba\xf0\xa9\xeb\x4a\x7d\xad\x8a\x96\x10\x7d\xd5\x67\xaf\xda\xf0\xaa\x0f\xaf\x6c\x48\x8e\x29\x20\xa9\x2b\xf4\x48\xf8\x12\x5d\x5c\xc3\x0c\x57\x81\x20\x78\x01\x21\x76\x4a\x05\x6c\x89\x60\x48\x87\x7e\x3b\x38\x42\x10\x4e\x52\xfe\xf8\x8e\x72\xb8\x8b\x11\xfa\x1d\x5d\x8c\xcb\x32\x39\xbb\x52\xe5\x37\xc6\xe2\xde\x51\x16\x57\xa9\xbc\xcb\xb7\x6f\xb2\x93\xbd\x93\xc4\x82\x2a\x2b\xd0\x51\x0b\x74\xf2\x02\x3a\x3d\xff\xc6\xb8\xe1\x3b\xca\x0d\x2b\xb4\x99\x7c\xbf\x7d\xc7\xf9\x1f\xec\xb7\x4b\x88\xb4\x66\xc2\x68\x30\x18\x0d\x0e\xa3\xae\x22\x50\x37\x30\xac\xa9\x05\x6a\x45\x18\x36\x19\xf4\x26\x87\xde\x50\x31\x6c\x68\x18\xd6\x2d\x18\xb6\x18\x8c\x16\x87\xd1\x54\x11\x68\x1a\x18\x36\xd4\x02\x8d\x22\x0c\xdb\x0c\x7a\x9b\x43\x6f\xa9\x18\xb6\x34\x0c\x9b\x16\x0c\x3b\x0c\x46\x87\xc3\x68\xab\x08\xb4\x0d\x0c\x5b\x6a\x81\x56\x11\x86\x6b\x0c\xfa\xda\x99\x42\x22\x02\xc3\x8e\x86\x61\x5b\xc1\xb0\x54\xe2\x8f\x94\x27\x9d\x10\xba\xd6\x12\x69\x27\xe6\x5d\x77\x51\x58\x19\xbe\xce\xe4\x7b\x27\x59\x93\xca\x43\x29\x28\x69\x1c\xe8\x6d\x91\x79\x7f\x35\x1d\xfb\x04\x9b\xeb\x0c\x39\xc1\xb1\x38\x33\x95\xbc\x65\x1b\x44\x71\x71\x55\xa4\xd4\x55\x93\x77\xc8\x25\xab\x45\x77\x50\x72\xc1\xd2\xc6\xc8\x9e\x7a\x37\xd2\x6d\xb7\xbc\xfc\x52\xa4\xdb\xee\x78\xec\xae\xa4\xdb\xa9\xdf\x9e\x79\x6b\x7f\xef\x48\x84\x4f\xf7\x55\x4f\xf7\x55\x8f\x76\x5f\xa5\x2d\xf1\xfc\x3e\x47\xbf\xc9\xf9\x7b\xdd\xe1\x3c\x54\x56\xb8\xf7\xe2\x68\xfe\x5e\x3d\x9a\xbf\xbf\xeb\xd1\xfc\xbd\x7a\x34\x7f\x5f\x74\x34\x9f\xa7\x60\x7e\xba\xa9\x7a\xba\xa9\x7a\xba\xa9\x52\xbe\x3c\xdd\x54\x3d\xdd\x54\x3d\xdd\x54\xe5\xcd\x3e\xdd\x54\xe9\x1f\x9f\x6e\xaa\x1c\x8f\x4f\x37\x55\x4f\x37\x55\x4f\x37\x55\xf0\xf7\x74\x53\x55\x4e\x89\xfb\x74\x53\xf5\x74\x53\xf5\x74\x53\x25\xfd\x3d\xdd\x54\x3d\xdd\x54\x3d\xdd\x54\x3d\xdd\x54\xfd\x4f\xbe\xa9\x7a\xb0\x3b\xaa\xbb\xdd\x4e\x95\xb9\x97\x2a\x71\x23\xf5\x58\x77\x51\x7f\xef\x7c\x28\x4f\x77\x51\xff\xfc\xbb\x28\xf9\xee\xa8\xd7\x9a\xeb\xe8\x24\xdf\x1c\xf5\x5a\xd2\xb5\x11\x3c\x3c\xfe\x9d\x11\xf5\xd2\x14\xb7\x46\xf6\xa0\x02\xdc\x43\xbb\xe8\x5a\x09\xdc\x38\x65\x8f\x62\x29\x66\xba\xa9\xaf\x88\xc2\x0c\xa5\xfd\xf8\xda\x84\x73\x2c\xd0\x39\x96\xaf\xe9\xf8\x9f\x4d\x9a\x6c\xb4\x3b\xee\x43\x39\x3b\x74\x87\xf3\xd5\xb8\xef\xf1\x8d\x4d\x8f\xab\xb6\xe8\x71\xff\xf1\xb9\x0d\xb3\x41\x21\x43\xc0\xa3\x4a\x84\xe8\x5f\xf2\x38\x39\x54\x87\xac\x12\xd9\xda\xf8\xd8\x9f\x2a\x80\xcc\x48\x68\xca\x67\x23\x28\x9a\xed\xec\x4f\x7a\x51\xf9\x03\x2d\xd1\xf1\x59\xe2\x8d\x56\xd1\xbf\xa0\x57\x8e\x58\x0a\x57\xfe\xd4\x8e\x33\xec\x1b\xa6\x86\x40\x9a\x80\x63\xbb\x63\x3c\x79\x4d\x66\x7c\xfe\xf4\xf4\xac\x2a\x7e\x96\x55\x43\x10\xcd\x1f\x2c\xcb\xac\x00\x74\x6f\xb5\x1c\xd7\x84\x80\x16\xc4\xc8\xbf\x4e\xa6\xc7\xae\x32\x54\x5a\x16\x4e\xce\x8d\x76\xc7\xa1\x10\xa9\x39\x95\x21\xd6\x46\xcb\x2a\x46\xa4\xf5\xa4\x29\x46\xf2\x41\x0b\xb5\x2f\x7f\xe4\xc3\x39\x37\x03\x3c\x28\x07\xd5\xea\x9f\x65\x3c\xb5\xf9\x10\xab\x29\xa2\xcb\x28\xa2\x2a\xb5\xc8\xb2\x88\x42\xd0\xa0\xd3\x84\x71\x8c\x2a\x95\xef\x0a\x09\x3b\x08\xd7\x4a\xb4\x05\x04\xeb\x26\xd6\x9c\x50\xd5\xf7\x6a\x67\xbf\x92\xba\x15\xb6\xa6\x48\x15\x86\xd7\x59\x9e\xd7\x20\xd2\xf3\x18\x68\xc7\xa7\x4f\x10\x07\xc5\x72\xa3\x95\x93\x7a\x68\x9c\xdd\xc9\x58\x28\x73\xc5\xc4\x32\x05\xbb\xef\x55\xee\xed\xb5\x1e\x42\xe8\xed\xb5\x16\x96\x78\xcd\x3d\x56\x13\x77\x7b\x2d\x6b\x6c\x0b\xb8\xa1\x09\x71\x70\x87\x1d\x7e\x2b\x89\xa7\xca\x2e\xcf\x5e\xc0\x20\x7c\x83\xa8\x78\x01\x69\x4e\x0d\x34\xa7\xe9\xf9\xc9\xc4\x93\x52\x22\xd4\x1c\xaa\xbf\x6a\xc8\x60\xf5\x58\x73\x04\x75\x29\xea\x97\xb6\x8a\x09\xa8\xae\x0a\x42\x8d\x18\x57\x4a\x88\x21\x6d\xf0\x82\xc5\x77\x18\x64\x3c\x0b\x36\x70\x61\xf8\x42\xf0\x22\xbb\xf8\xcf\xb0\x99\x2f\x2f\x5b\xf7\xf0\x05\xd8\x3d\x9a\x93\x00\xe9\x3b\x5a\x6d\x64\x88\x1e\x66\xc5\x01\xa4\xc5\x57\x1d\xa3\xf9\xe2\x95\x47\x0a\x15\x9f\x34\x7b\xad\xc7\x3a\x66\xde\x2f\x5d\xdf\xb7\x3c\x5f\x3e\xda\x29\xf0\xdb\x06\x71\x26\xac\x0a\xa7\x38\xb9\xc4\xcf\x9f\x55\x06\x55\xd4\xa8\xd5\x1b\xa8\x7f\x83\x7a\xff\xdf\xff\x1b\x24\xe1\x00\xed\xe3\x34\x0a\xc7\x2b\x68\x73\x3c\x46\x49\x78\x31\xca\x52\xc4\xca\x07\x2b\xcf\x9f\x3f\x3b\xc2\x41\x98\x66\x49\xd8\x9f\x01\x7c\x3f\x0a\x20\x28\x4f\x18\xa1\x34\x9e\x25\x03\x0c\x6f\xfa\x61\xe4\x27\x37\x84\x1d\x4c\x52\x8f\x45\x69\x48\xe0\xbf\xf1\x2c\x43\x13\xe0\xe9\x03\xe0\xac\x1e\xf2\x13\x8c\xa6\x38\x99\x84\x59\x86\x03\x34\x4d\xe2\xcb\x30\xc0\x01\x0d\x3a\x41\xd6\xe9\x30\x1e\x8f\xe3\xab\x30\xba\x40\x83\x38\x0a\x42\xba\x86\x49\xa5\x09\xce\xba\x6c\xc5\x2f\x23\x15\xad\x14\x14\xc3\x14\x9f\x41\x1c\x60\x34\x99\xa5\x19\xd9\xa8\xfd\x30\x02\xa0\x7e\x3f\xbe\x24\x9f\xa6\x37\xd0\x45\x14\xc5\x59\x38\xc0\x1e\x8d\x2b\x34\x0e\x53\xd0\x2c\xcb\xed\x45\x81\x86\x4c\x10\xa6\x83\xb1\x1f\x4e\x70\xb2\xe2\xc2\x21\x8c\xe4\x81\xe0\x38\x4c\x93\x38\x98\x0d\xf0\x83\xa3\x81\x58\xd7\x82\x78\x30\x13\x71\x30\x48\x8d\xd5\x38\x61\x31\x32\x26\x7e\x86\x93\xd0\x1f\xa7\xf9\x30\xc3\xdc\x40\x35\x09\x75\x32\xcf\x27\xbb\x7b\xc7\xe8\xf8\x60\xe7\xe4\xd7\xcd\xa3\x6d\xb4\x77\x8c\x0e\x8f\x0e\x7e\xd9\xdb\xda\xde\x42\x6f\xff\x8d\x4e\x76\xb7\x51\xef\xe0\xf0\xdf\x47\x7b\xef\x76\x4f\xd0\xee\xc1\x87\xad\xed\xa3\x63\xb4\xf9\x71\x0b\xf5\x0e\x3e\x9e\x1c\xed\xbd\xfd\x74\x72\x70\x74\x8c\x7e\xdc\x3c\x46\x7b\xc7\x3f\xc2\x87\xcd\x8f\xff\x46\xdb\xbf\x1d\x1e\x6d\x1f\x1f\xa3\x83\x23\xb4\xb7\x7f\xf8\x61\x6f\x7b\x0b\xfd\xba\x79\x74\xb4\xf9\xf1\x64\x6f\xfb\xd8\x43\x7b\x1f\x7b\x1f\x3e\x6d\xed\x7d\x7c\xe7\xa1\xb7\x9f\x4e\xd0\xc7\x83\x13\xf4\x61\x6f\x7f\xef\x64\x7b\x0b\x9d\x1c\x78\xd0\xa8\x59\x0d\x1d\xec\xa0\xfd\xed\xa3\xde\xee\xe6\xc7\x93\xcd\xb7\x7b\x1f\xf6\x4e\xfe\x0d\xed\xed\xec\x9d\x7c\x24\x6d\xed\x1c\x1c\xa1\x4d\x74\xb8\x79\x74\xb2\xd7\xfb\xf4\x61\xf3\x08\x1d\x7e\x3a\x3a\x3c\x38\xde\x46\xa4\x5b\x5b\x7b\xc7\xbd\x0f\x9b\x7b\xfb\xdb\x5b\x2b\x68\xef\x23\xfa\x78\x80\xb6\x7f\xd9\xfe\x78\x82\x8e\x77\x37\x3f\x7c\xb0\xf6\x92\xe0\xae\xf4\xf1\xed\x36\xfa\xb0\xb7\xf9\xf6\xc3\x36\x6d\xe9\xe3\xbf\xd1\xd6\xde\xd1\x76\xef\x84\x74\x27\xff\xd5\xdb\xdb\xda\xfe\x78\xb2\xf9\xc1\x43\xc7\x87\xdb\xbd\x3d\xf2\x63\xfb\xb7\xed\xfd\xc3\x0f\x9b\x47\xff\xf6\x18\xcc\xe3\xed\xff\xfd\x69\xfb\xe3\xc9\xde\xe6\x07\xb4\xb5\xb9\xbf\xf9\x6e\xfb\x18\x55\xe6\x0c\xc9\xe1\xd1\x41\xef\xd3\xd1\xf6\x3e\xc1\xf9\x60\x07\x1d\x7f\x7a\x7b\x7c\xb2\x77\xf2\xe9\x64\x1b\xbd\x3b\x38\xd8\x82\x81\x3e\xde\x3e\xfa\x65\xaf\xb7\x7d\xfc\x1a\x7d\x38\x38\x86\xd1\xfa\x74\xbc\xed\xa1\xad\xcd\x93\x4d\x68\xf8\xf0\xe8\x60\x67\xef\xe4\xf8\x35\xf9\xfd\xf6\xd3\xf1\x1e\x0c\xda\xde\xc7\x93\xed\xa3\xa3\x4f\x87\x27\x7b\x07\x1f\xab\x68\xf7\xe0\xd7\xed\x5f\xb6\x8f\x50\x6f\xf3\xd3\xf1\xf6\x16\x8c\xee\xc1\x47\xe8\xea\xc9\xee\xf6\xc1\xd1\xbf\x09\x50\x32\x06\x30\xf8\x1e\xfa\x75\x77\xfb\x64\x77\xfb\x88\x0c\x28\x8c\xd4\x26\x19\x82\xe3\x93\xa3\xbd\xde\x89\x5c\xec\xe0\x08\x9d\x1c\x1c\x9d\x48\x7d\x44\x1f\xb7\xdf\x7d\xd8\x7b\xb7\xfd\xb1\xb7\x4d\xbe\x1e\x10\x28\xbf\xee\x1d\x6f\x57\xd1\xe6\xd1\xde\x31\x29\xb0\x47\x9b\xfd\x75\xf3\xdf\xe8\xe0\x13\x74\x99\xcc\xd1\xa7\xe3\x6d\xfa\x53\xa2\x58\x0f\x66\x12\xed\xed\xa0\xcd\xad\x5f\xf6\x08\xda\xac\xf0\xe1\xc1\xf1\xf1\x1e\xa3\x13\x18\xb2\xde\x2e\x1b\xee\x95\xe7\xcf\x5e\xae\xaa\x3a\xaf\x7d\x3f\x1b\x3d\xac\xde\xab\x5c\xd4\x69\x1a\xf8\x58\x14\xa1\x8f\xa5\xac\xb3\xe1\xc2\xce\x8f\xb2\x14\x65\x7e\x9f\x4b\x2c\xa4\xca\xf9\x97\xb1\x35\xd8\x66\x2e\x47\xd5\x3c\x84\xea\x1e\x42\x0d\x0f\xa1\xa6\x87\x50\xcb\x43\xa8\xed\x21\xd4\xf1\x10\x5a\xf3\x10\x5a\xf7\x10\x7a\xe5\xa1\x7a\xcd\x43\xf5\xba\x87\xea\x0d\x0f\xd5\x9b\x1e\xaa\xb7\x3c\x54\x6f\x4b\x16\x96\x6b\xb4\x2e\xf9\x46\xe0\x91\xf2\x04\x46\xbd\x4d\xe1\x92\x7a\xd0\xd6\x2b\x06\xbf\xc1\x60\xd4\xa1\x8d\x1c\x4e\x93\xb5\xd5\x62\xb8\xbc\x62\x30\xd6\x25\x3c\xd7\x18\xac\x0e\xc3\xa5\x4e\x61\xd6\xe5\x58\xcb\x75\x56\x97\xe3\x52\xa3\x30\x00\x0f\x8e\x67\x93\xc2\x22\xf0\xeb\x72\xbf\x65\x38\x2d\x56\xb7\xcd\x70\x5f\x63\x30\x1a\x12\x9e\x75\x06\x6b\x9d\xe1\xc2\xfa\x5d\x6f\x9e\x55\x5f\xcb\x73\x91\xcc\x99\x0b\x8e\xc7\x9a\x34\x56\x0d\x06\x93\xe3\xdc\x51\xc7\x03\xfa\xd6\xd4\xfa\xde\x61\x75\x9a\x39\x2c\xa8\xdb\xce\x71\xe6\x30\xf8\x78\x40\x5b\x75\xad\xef\x50\xa8\x2d\x75\x70\x8d\x21\xd8\xc9\x07\x57\x00\x69\x48\x03\x4d\x91\xcd\x01\xad\xb3\x3a\xd2\x60\xc1\xc4\xb4\xf3\xc1\x15\x30\x9a\xd2\x40\x53\x64\x25\x84\x1a\x6c\x64\x6b\x12\x30\x3e\x1a\x6b\x62\xf6\x04\x85\x22\x36\x3a\x14\x59\x75\x36\xd2\x79\x2b\x83\xa2\xc8\xc6\x0a\xd0\x93\x5b\xe2\xb4\xd5\x94\xc6\xb3\x93\x7f\x53\x68\x7a\xcd\x83\x4f\x30\x54\x9c\x5e\x5f\xe5\xb4\xc7\x69\xaa\xde\x96\x86\x75\x8d\x95\x55\xe6\xa3\x9e\x13\x81\x98\x8b\x57\xac\x20\x27\x9e\x75\xa9\x0c\x47\x7c\x0d\x7e\xcb\x67\x29\xb1\x96\x5b\x79\x55\xde\xbe\x58\xf3\xf2\x9a\x58\x57\x40\xe6\xa0\xf8\xfa\x6c\xe7\xb4\x2f\xfa\xd9\xc8\x51\x10\xe3\xc4\x48\x86\xc2\x45\xda\x94\xcc\x5b\x20\x0c\x31\x65\xf0\xdb\x39\x02\xd0\xcf\xb5\x7c\x21\x42\x83\x2d\x86\x48\x47\x43\xba\xa9\x0e\xbe\xe8\x74\x3d\x87\x23\xc6\x4e\x2c\x68\xf8\xae\xc0\x11\x0c\xa4\x2e\x0d\x52\x27\x6f\x57\x2c\x3c\xb6\x80\xeb\x4d\xcb\x7c\x88\x0e\x68\x88\x73\x40\x62\xc1\x35\xa4\xff\xb6\xc5\x2a\x56\x07\xa8\x6d\x29\xd7\x52\x67\x46\xcc\x64\xde\x29\x54\xaf\xa3\x33\x25\x4b\xf6\xf9\x88\xac\x10\xcb\x7c\x20\x11\xaa\xb9\xe6\xa1\xda\x75\x7b\x73\xbd\xb1\xf6\xea\xd5\x2b\xf2\xbb\xb3\xbd\xf5\x6a\xfb\xed\x66\x9d\xfc\x5e\xdf\xa9\xbf\x7d\xdb\xdb\xea\x91\xdf\x9b\xaf\xda\xcd\x9d\xad\xd6\xb6\x3a\xdf\xa3\xc4\xd9\x40\xbb\xb6\xd9\x58\x7f\xbb\xdd\x81\x06\x7a\xad\xad\xad\x7a\xa3\x05\x0d\x6c\xad\xd5\x9a\xdb\x3b\x4d\xf2\x7b\x6d\xb3\xb3\xb5\xd6\xd9\x86\x86\x39\x42\x67\x56\x7d\xc0\xd1\xde\xe1\xf6\xfe\x56\xbd\x53\x83\xf0\xfb\x73\x74\x48\xa2\x6c\xae\x45\x92\x5e\xd1\x5d\xf9\xae\x77\x45\x54\x99\x08\x48\x38\x82\x60\x77\xd6\x5a\xed\x46\xb3\x06\x23\xb8\xbd\xd3\xdb\xda\x7c\xbb\x0e\x1d\x7c\xb5\xfe\x76\x73\xab\xb7\xb3\x4d\x7e\xd7\x6b\xcd\x46\xbb\xb5\x06\x83\xd3\x6b\x6e\x35\xb6\xeb\x3b\xb5\x33\xa7\x6a\xbc\xac\x52\xde\xaa\xd8\x2d\xed\xa5\x54\x2f\xb8\xa9\x99\x6f\x8e\x4f\xb1\x00\xdd\x6b\x6e\x16\xe9\xb8\xbe\xd9\x3f\x97\x4a\xf3\xcb\x83\x73\xd3\x90\x09\x15\xdd\xa9\x48\xf5\xd0\x06\xaa\x98\x05\x10\x35\x00\x95\x1a\xcb\x0d\x1f\xa4\x97\x8b\x19\x95\x1a\x00\x99\x5d\xa9\x06\xd0\xb4\x2e\x35\xc1\x15\xa8\xc6\xd0\x3c\x5b\xe7\x5d\x24\xee\x1f\x08\x29\x3a\xaf\x1c\x81\x01\x9c\x8f\xc6\xee\x02\x09\x14\x48\x9c\x05\x40\xfc\x3c\xff\xe2\x86\x00\x32\xd1\xf9\x17\x37\x04\xd8\xa6\xcf\x53\x37\x04\xd8\x34\xce\xd3\xc4\x1e\xd1\x7a\x75\x95\xac\xb2\xcf\xe4\xd0\x7c\xe9\x27\x21\x91\x8e\x2d\x97\xb4\xfe\xd8\x43\xfd\xb1\x87\x06\x63\x0f\x05\x63\x0f\xe1\xb1\xa5\x21\x3f\xf1\x50\x3f\xf1\xd0\x20\xf1\x50\x90\x78\x08\x27\x7a\x63\x3e\x41\xc5\x27\x08\xef\x9a\x2e\x23\xfd\x04\x82\x8e\xc3\xc7\xba\xfe\x71\x40\x3e\x0e\xe8\xc7\x86\xfe\x31\x20\x1f\x03\xfa\xb1\xa9\x7f\x84\x03\x03\xa6\x1f\x5b\xfa\x47\x91\xa6\xda\x57\xf3\x52\xf3\x2e\xe9\xb7\x82\x56\x53\x42\xf8\xef\xd2\x06\xaa\x5b\xd7\x76\x46\x96\x8f\x3f\x46\x4b\xf9\x9a\x5a\xfa\x32\x3e\x0d\xcf\xce\xaa\x5f\x6d\x4e\x0c\xe0\xb5\xf3\xa6\xde\xa9\xfe\xf9\xfc\x99\xca\x1a\x49\x1b\x68\x58\xaf\xf4\xc7\xde\x60\xec\x05\xe3\x2a\x5a\x42\xa3\xb1\xdd\xf7\xe6\x16\x09\x85\x5c\xf8\xa6\xd9\xa0\xaa\x36\x0b\xb4\x86\x0e\xcd\x18\x79\x03\x5a\x6b\xdd\x09\xad\xa9\x43\x33\xa6\xca\x80\xd6\x69\x39\xa1\xb5\x74\x68\xc6\xdc\x4a\xd0\xfe\x5c\x5d\x65\x10\xd7\x6b\x4e\x88\x6d\x1d\xa2\x41\x10\xc8\x1e\x26\x9d\x4c\x62\x66\x9d\x2e\xf2\x05\x25\x71\x36\xae\x64\x5e\x4a\xa6\xd5\xe6\xb4\x01\x34\x90\x2d\xe1\xb1\x7d\xca\x61\x45\x18\x4b\x8a\xfc\x01\xdd\x06\xb6\x2f\x40\xee\xd0\x2e\x59\x93\x75\xab\x1b\x10\xac\x97\xbe\xad\x36\x2c\x33\xe3\x26\x51\xa0\xea\x27\x68\x49\xa2\xd6\xe4\xee\xd4\xda\xae\xf4\x13\x6f\x90\x78\x41\x02\x23\x9e\xdc\x8f\x5a\x5b\x3a\xb4\xfb\x52\xab\x0a\xed\x5e\xd4\xda\xd0\xa1\xdd\x9b\x5a\xeb\x3a\xc4\x07\xa6\xd6\x04\x6e\xad\x0b\xc8\x35\x71\x90\x2b\x70\xd4\xc4\x46\xae\xc0\x88\x6d\x5f\x80\x45\x53\x72\x4d\x9c\xe4\x0a\x1b\x80\xad\x36\x6c\x0d\xa6\x85\x86\xce\xca\xf7\xe4\x74\x0c\x20\x43\x82\xd5\xaf\x26\x61\x92\x7f\x36\x50\x65\x97\x9a\xe6\x0e\x08\x67\x0e\x2c\x3d\xdd\x65\x26\xbc\xbb\xd4\xfc\x36\x20\xe5\x6c\x23\xb2\xcb\xcc\x74\x77\xa9\x21\x2d\x26\xe5\x7c\x6b\xb9\x26\x2b\x07\xc6\xb2\xb0\x23\xf4\xad\xe5\x5a\xac\x1c\x18\x26\xf7\x49\xb9\x81\xb5\x1c\x18\x30\x2b\xc3\xa2\x8b\xb5\x3b\x2c\xb5\xc6\x3d\xcc\xb3\x02\x3f\xf3\x85\x30\x44\x1e\x2c\x1b\xff\xfc\x34\x8c\xbc\x64\xf4\x36\xcc\xd2\x93\x38\x03\x8e\x47\x61\x46\x5b\x7e\xe6\x53\xab\xad\x97\x68\xdd\x02\x1d\xea\x7c\xc0\xc3\xcc\x48\xda\x08\xe5\x8d\xce\x6c\x06\x81\x99\x85\x18\xb1\x7c\x8b\xd4\x98\x29\x07\x49\xa4\xc9\xf6\x19\xfa\xba\x41\x13\x0b\xe7\x36\x12\xa2\xc4\xbf\x50\xb3\xa1\x53\x6b\x0e\xa9\x52\xa9\xe4\x45\x97\x10\xe1\x0f\x04\xe4\xab\x2a\x01\xd5\x22\xeb\xb6\xde\x72\x08\xd0\xbc\x2a\x1d\x8e\x5c\x78\x96\x5e\x96\x17\x9e\x0d\x60\x4c\x70\xd6\x80\xcd\x13\x9c\x6d\x1d\x95\xf3\x74\xe4\xf9\x30\x79\x8e\x1d\x30\x8e\xb1\xa4\xed\x58\x5d\x85\x93\x20\x82\xec\x2e\xd4\x21\xcb\x6a\x38\x35\xa5\x27\x2f\x33\x9b\x4b\x31\x59\xc2\xea\x96\x65\x74\x0b\xe1\xec\xa2\x0d\x24\x8b\xef\xf7\x3b\xbf\xb5\x4b\x1d\xdf\xec\x27\xb2\x5d\x38\x8a\xed\x5a\x9c\x49\x50\xd1\x19\x6c\x57\xb8\xeb\xed\x2a\xc7\xab\xdd\x85\xcf\x55\x94\x42\x76\x95\x33\xd5\xae\xf3\x30\x35\xdf\x14\xee\x88\xde\x84\xd3\xc9\x65\x19\x2c\x02\x18\x6c\xb5\x28\xbb\x31\xd7\x26\x48\x61\x53\x83\x71\x1c\x15\x33\x28\x30\x25\x20\xa5\x72\xed\x02\x3c\xba\xcd\x20\xe8\xe7\x73\x83\x48\x68\x3d\x93\xd6\x18\x9a\xf0\x55\xb1\x8b\x82\x9f\xb7\xf4\xf6\x1f\xc9\x16\x71\xc3\x7a\xe5\xda\x43\x37\x1e\xfa\x62\x4b\xf3\x51\xa9\x5c\x83\x67\xe7\x0d\xfc\xfb\x25\xcf\xd6\x7e\x6b\xc0\x69\x14\xc3\xa9\x5c\x57\x7f\xaa\xdc\x54\xa9\x3b\xf9\xff\x25\x0f\x5f\xaa\xd5\xea\xff\xcf\xde\xdb\x6f\xc7\x6d\x23\x0b\xe2\x7f\xc7\x4f\x81\x99\xdf\x19\xbb\x15\xb5\x24\x02\xfc\x02\x6d\x2b\xbf\x4d\x14\xfb\x3a\x1b\x3b\xf6\xb1\x9d\xb5\xef\xf1\xb1\x33\x20\x09\xaa\x19\xb7\xba\x75\xbb\x29\xab\x35\x13\xcf\xd9\xd7\xd8\xd7\xdb\x27\xd9\x83\x02\x48\x82\x24\x00\x76\xcb\x72\xee\x64\xc6\x9a\x33\x4e\x77\xb3\x50\x55\xa8\x2f\x14\xf1\x51\xb8\x67\xc3\xe6\x8f\x62\x13\x88\xfe\x21\x30\xb6\xac\x59\x70\x05\xe3\xb8\x6e\x03\x06\xe0\xed\x6a\xef\xf6\xe4\x1f\xc0\x9c\x1d\x63\xb8\x8d\xcc\x84\xd0\x7e\x6b\x51\x59\x70\x41\x2a\xb1\x99\x2e\x8c\x98\x36\xf7\xef\x2f\x80\xab\xcd\x37\xdf\x7c\x33\xf1\xc9\xc1\x42\x67\x4a\x7e\x70\xee\x86\xa9\x37\xc3\xc8\x7b\xe0\xb6\xdb\x0c\x63\xbd\xed\x47\xed\x6f\x81\x3d\x4f\xf5\xe7\x6a\x29\x23\xd3\x10\x8d\xe5\x7e\x1e\x0b\xf4\x75\x2f\xe6\x51\x9e\xd1\xee\x64\xa9\x27\xf0\x26\x77\x14\x8b\x77\x0c\xbb\x70\xec\xad\x2e\x6b\x6e\x4d\xdb\x6d\x86\x93\x83\xbd\xad\x36\x35\xc0\x76\x5b\x95\x6a\xe5\x3c\x7a\xf2\xed\xc9\xef\xa0\x1a\x47\xf3\xf7\xfc\x0a\x9a\xae\x79\xb6\xe2\x95\xe5\xee\x24\x8b\x42\xe1\xca\xc1\x1b\x54\xa8\xbc\xc8\xb0\x51\xcd\xa3\x33\x96\xb5\xea\xd1\xb7\x58\x19\x34\xd4\x01\x1e\x6a\xe9\x8c\x65\x06\x4d\x7d\xf5\x51\xae\x03\x5b\xb6\x46\xd5\x90\xe6\xdb\x89\x3e\xbe\x9d\xc6\xf1\x97\x2d\x4e\xff\x0a\x47\x56\x3e\xf7\xd2\x7d\xaf\xb0\x9a\x46\xd8\x5a\x32\xed\xc5\xa3\x6f\x0f\xf0\x16\x2b\x19\xc3\xbb\xaa\x6f\x72\xfd\xe2\x18\x4e\x9f\xb6\x4b\x18\xe5\xa2\xac\x26\x86\x02\x54\xdd\x25\x0d\x5e\x64\x39\x4b\x69\x62\xa8\xcd\xe4\x6d\x12\x9a\xb2\x3c\x2b\x78\x67\x8d\xc3\x04\x98\xf9\x39\xe1\xb8\xf0\xba\xcf\x3e\x7d\x09\xc4\x96\xa1\x9b\x93\xef\xe1\x0c\xfa\x00\xc1\x36\x73\xcf\xe6\xe9\x62\xf1\x28\x35\x4f\x16\x43\xc2\x68\x9e\x2a\x86\xd7\x55\xf3\x44\xb1\x78\xc4\x9b\x69\xe2\x01\xa7\xd6\x79\x62\xeb\x9c\xb0\xe5\x6d\x01\xe6\x7d\x90\x3c\x61\x6a\xa9\x05\xf3\x4a\x26\xfe\xdd\x12\x18\xdd\xb3\xa7\xf5\x5f\x3d\xa1\x64\x46\x54\x9f\x73\x78\xf5\xa6\x44\x07\xc8\x7f\x8b\xde\xa9\x8f\xb4\xfd\x88\x03\xed\x73\x64\xbb\x3b\x52\xb1\x34\x59\xc0\xe1\x58\xf9\x6e\x09\xaf\x0f\x3e\x36\x97\xa9\x31\xbf\x09\xc1\xd4\xd2\x84\x09\x24\x21\x20\x61\xf2\x4d\x26\x86\x03\xb2\x1c\xed\x03\x21\xdb\x44\x23\xba\x8f\x88\x67\x95\x1a\x4c\x9b\x4d\x26\x29\xba\x8d\x32\x99\xe7\x8a\x8f\x39\x60\xf6\x36\x21\x93\xab\xb0\x23\x53\x7c\xe8\x3e\x0a\xc6\x48\xa4\xe8\x1d\xca\xd0\x3b\x94\x4b\xcc\x11\xcf\x13\x9e\x32\x53\xd1\xa1\x1e\xe6\x68\x07\xe6\x25\xef\xe2\x53\xa6\x7a\x71\x80\xbc\x4d\xec\xf1\x20\xf0\x49\x60\xa7\x75\xf4\x75\x43\x8e\x7a\x7b\xe8\xeb\xa3\xad\xfb\x22\xf0\xfb\x61\x92\xfb\x9c\xf4\x67\x79\x90\x45\xa5\xc2\x5f\x72\xd3\x74\x1f\x3a\x46\x99\x69\x8a\x0f\x01\xc9\xfb\xf7\x91\xef\xa9\x5e\x82\xfa\x8d\x77\x8b\xa2\x63\x64\xe2\x83\x6d\x77\x5a\x6b\xab\xc9\x40\x35\x89\x56\x4f\xb6\xb1\xfe\x09\x6f\xd4\x99\x08\x84\x09\xc3\x41\xe5\x13\xd4\x99\x04\x84\xc9\xc2\xcc\x0c\xe3\xeb\x13\x85\xb9\x19\x26\xd0\x27\x09\x79\x1f\xe6\xcb\x04\xdf\x3f\xeb\x04\x9f\xc8\x85\x0f\x8b\xf9\x72\xb9\xd2\xe7\xdc\x8e\x60\xa0\x56\x7f\x9f\x44\x04\x6a\x21\xb4\x98\x47\xe6\xe9\x06\xd3\x74\x9f\x69\x86\x6e\xc7\x79\x20\xe3\x74\xdd\x1f\x71\x36\xe8\xcb\x14\xc2\x60\x32\x40\xa4\xcf\x3b\xcd\x1e\x40\x03\xd7\xc4\x41\x37\x21\xef\xce\x19\x88\x67\x5f\xa6\x0b\x6e\x74\xba\x00\xf4\xb1\xc5\x4c\x81\x59\x2d\xed\x24\x81\x52\x8d\xfd\xd8\x94\x00\xb0\x4f\x0b\xd0\x3f\x74\x81\x8d\xf5\x8c\x91\x30\xfa\xdc\xb5\x31\x14\x95\x7f\x9f\xe9\x83\xc1\xf4\x80\xfe\x0e\x4f\xc2\xa8\xf3\x16\xaf\x9d\xc2\xee\xcf\x0a\x10\x12\x6c\x37\x2f\x20\x00\x3b\x38\xe1\xbb\x44\xfe\xbb\xce\x0d\x64\xd8\x0b\x13\x9e\x53\xf1\xca\xef\x47\x71\x96\x87\x5e\x0c\x9f\xbd\xd8\xcb\x73\x0c\x9f\x8b\xd8\xe3\x61\xe2\x9b\xe7\x0c\x8a\x22\xf3\xbc\xd4\x87\xc9\x85\x88\x86\x14\x87\x58\x7e\x0e\x8a\x84\x16\x0c\x10\xa4\xbc\x60\x41\xc1\x82\x1d\xa6\x0b\xb6\xca\x3c\xb5\xb0\xaf\x44\xa7\xb5\x74\x9c\xa2\x85\x88\xda\xa4\x33\x07\xc7\xc3\xe4\xc5\xb2\xb0\xf4\x65\x88\x1e\x19\x71\x09\x09\x76\x1d\xa4\x45\x93\x91\x61\xba\xe3\x1d\x83\x81\x9a\x10\xf3\x21\xf6\x2f\x43\xf5\x27\x0c\xd5\x42\x2b\xdb\x0d\xd6\x46\xe5\x74\x86\x6b\xa9\x20\xe7\x80\x4d\x48\xff\xa8\xb3\x76\xae\x59\x0d\x47\x77\xe3\x44\x0c\xe0\xc9\x97\x79\xfd\xff\x9e\x81\xf9\x8f\x77\x2c\xef\x07\x79\x89\x43\xf9\xb7\xe6\x54\x2e\x5a\x2d\x2f\x16\x39\xca\xba\xe7\xf5\xb4\x1e\x3c\xea\x5f\x9d\xf2\x63\x77\x19\xa0\x9e\xa8\xe5\x2d\x0e\xf9\xc4\x94\xc1\x20\x7d\x49\xb9\x5c\x3f\x5b\x95\x67\x7c\xb2\x30\x0e\x63\xeb\xff\x5a\x55\x3f\xd5\xef\xf9\xe2\xcb\x64\xd1\x7f\xcf\x6c\x26\x82\xa5\x3a\xd1\x31\x22\xf7\xea\xcf\xf7\x8f\x25\x86\xfa\x07\xc7\xdc\xf0\x9f\x26\x0b\xf4\x17\x05\xb6\x67\x9d\x2f\x54\x3e\x5a\xb0\xf9\x9a\x8f\xef\x0a\xec\xcf\x8f\xd5\xef\xe3\xab\x8b\xee\x1b\xae\x41\x2c\xa7\xbc\x7a\xb8\x62\xf0\x99\xcd\xbf\x2b\xab\xb5\x41\x40\xcd\x12\xfe\x02\x1d\xa0\xc9\x02\x2a\x7b\xee\xa1\xaf\x3b\x93\x1f\xfd\x99\x2c\x8d\x56\x3d\x4b\xad\x57\x66\x87\xdf\x40\x21\xbd\xfa\x3d\x97\xb3\x72\xce\xd1\x44\x3d\xbb\x8f\xd4\x96\xcc\xbe\x14\x5b\x6d\x5a\x05\xdd\xa0\xa0\x56\x29\x3f\x7a\x23\x81\xa0\xec\xe8\x40\x10\x60\x0b\xe7\xcb\xcb\xc9\x62\x8a\x30\x3a\x42\x64\x6f\x8b\x8a\xed\x08\x6e\x42\xd9\x05\xad\xbf\x67\x2c\x9e\x2d\x51\xec\xef\x8f\x4c\x85\x2e\x3a\x10\x75\x86\x34\x69\x71\x5e\x7f\x8d\x4d\x24\xde\xdb\x65\xd3\xc3\x0c\xfd\xb3\xaf\xb4\x3d\x3a\x5c\xcf\xcb\x8c\x4f\xbc\xbd\x2f\xab\x5e\x5b\xaf\x7a\x0d\x1e\x15\xf0\x28\x34\x3d\x3a\x85\x47\x83\x05\x23\xc8\x59\xe0\x51\xfc\xc9\xcb\x68\x91\xa3\xd6\xfd\xef\xbd\x8c\x76\xca\xce\xce\x98\xb7\x69\x16\xd3\xf0\x40\x28\x43\x68\xd8\x68\x3c\xa9\x5b\xde\xbf\x8f\x88\x5c\xf4\xaa\x7f\xf9\xe6\x9b\x6f\x50\xbc\xb7\x87\xd0\x3b\x33\xa6\xee\x5f\x07\x13\x0e\x06\x98\x30\xdd\xdb\xdb\x0e\x53\xb7\x9d\x6f\x0c\x2f\x9d\x9e\xe0\xb6\xdf\xc6\x4d\xf2\x5d\x60\xad\xdb\x58\x32\xab\x75\x1b\x6f\xea\x7a\xd3\x5b\x32\xdb\xc5\xe4\x0f\x31\x25\x3b\x76\xbb\x6e\x67\xbe\x93\x00\xb5\x86\xa3\x94\xb8\xaf\x7a\x0e\x45\x7e\x55\x0f\xf7\x9d\x0b\xa6\xb6\xd5\xcf\x0c\x4e\x35\x4e\x38\xba\x8d\x0a\xd8\xec\xf6\x0f\xf1\xf1\xd4\x76\x85\xcb\x19\x83\x0a\x73\x0c\xdd\x46\x29\x80\x33\xb9\x3a\xf8\x0e\xa9\x75\x42\x13\xff\x90\xac\x94\xa7\x82\xf1\x66\xa9\x55\x2d\xb6\xa9\xb5\x56\xb9\xf5\x4f\x3e\xc1\x89\xf6\x04\xfb\x9d\x47\x9d\x46\xe6\xb1\xad\x21\x06\xf7\xd4\x4c\x38\xd8\xb8\xac\x9c\xcc\xa1\x5d\xa4\x30\xca\x27\x58\x7b\x82\xb1\xfe\x28\x96\x3b\x5b\xe5\x23\x12\x9a\x47\x3c\x58\x40\x16\x94\x66\x68\xbf\x26\xbb\x2f\x84\xba\x2f\x2f\x7a\xb3\x2e\x1e\x43\x43\x82\x8e\x6b\xc1\xec\x0b\xd1\x9a\x28\x88\xc0\x75\x6a\x40\x20\x62\x5d\xbf\x4e\xbb\xf8\x13\xe1\xd1\x94\x7e\x41\xed\x4c\xb8\x2d\x01\x9b\x96\xf9\xd0\xc8\x12\x69\xbf\xda\x3a\x1a\x59\x0e\x9d\x54\x42\x10\x15\x31\xd1\xfa\x77\x59\x1a\x95\x30\xa1\x82\x81\x92\xe1\x85\x19\x26\x52\x30\x50\x12\xfc\xd4\x0c\x13\x2b\x18\xf0\xf9\xd9\x97\x65\xd8\x2f\xcb\xb0\x5f\x96\x61\x87\xd9\xe6\x97\x65\xd8\x7f\xca\x39\xde\x30\xda\x79\x8e\x37\x8c\x46\xe7\x78\xf5\x77\xb6\xe1\x1c\x6f\x18\x7d\x99\xe3\xbd\xf1\x39\xde\x30\xda\x76\x8e\xd7\xa4\x9c\xee\x1c\x2f\x28\xc8\xbd\x69\xbb\x59\x3b\x33\x2f\xcd\x52\xef\x0f\xbd\x34\xbb\x89\x82\xdf\xe5\xe2\x82\x86\xce\x97\x59\xe0\xee\x2c\xf0\x26\x82\x35\xd5\xc3\x4d\x14\x68\xbf\xbf\x8e\x02\x55\xa5\x1b\x20\x0e\xb5\x3a\xd1\x3b\xd5\x74\xd3\xfa\xf7\xfc\xd1\xd3\x5f\x9e\x3e\x7c\xf8\xe2\xc1\xcb\x17\xfd\xd9\xe2\x67\x3f\xfc\xf2\xc3\x4f\xdf\x3f\x78\xfd\x60\x78\x2b\xf7\xf3\xa7\x3f\xff\xf4\xfd\x2f\x27\x4f\x7f\x7a\xf1\xf2\xdb\x9f\x9a\x96\x1a\x39\x39\xad\x7c\xb2\xdd\xb4\xb2\xd6\x62\x35\x5b\xd6\x45\x5b\x7a\x73\xd2\x35\x69\xf1\x76\x8d\xa7\xe8\xca\x56\xaa\xbc\x92\x53\x22\x15\xba\x8f\x48\x70\x0f\x55\x86\x29\x11\xad\xcf\x6f\x36\x68\x1f\x85\xe8\x6b\x74\x25\x4f\x0f\x56\xf5\x21\x4d\xf8\x44\xf6\x60\xa6\x12\xfd\x05\x45\x83\x5c\x04\xd2\x40\x7e\xf9\x1a\x1d\xa3\x2b\xf4\x17\x14\x9a\xb2\x44\x7e\xf9\x9f\x02\x2b\x41\x5f\x23\x41\xc7\x17\x74\xf6\x0c\xc0\x1b\x39\x2d\xf7\xba\xf7\xf3\x95\xfc\xf9\x3f\x2d\x53\xc1\x9a\xd8\xce\x4b\x54\xc2\x75\x02\x06\xa1\x35\x92\xd9\x48\xc9\x6c\xe4\x01\xcd\x8d\x41\x30\x0d\xa8\x94\x2e\xba\x92\xa0\x57\x96\x69\xa5\xd6\x40\xba\x62\xbc\x82\x0b\x7e\x86\xbd\x16\x72\xed\x77\xfd\xe3\x68\xdf\x7a\xab\x1c\x5d\x6b\x78\xfc\xf0\xc5\x73\xc1\xeb\xc6\xc3\x26\x63\xd0\xef\x9d\xb0\xcc\x8f\x09\x30\x20\x51\x1b\xeb\x93\xf5\x65\xcf\xb6\x8c\x60\x8f\x6b\x30\x8b\x08\xd5\xcd\x13\xbf\xa2\xfb\x28\xbe\x87\x7e\x75\xcc\xcc\x41\x1f\xe0\x68\xaa\xb9\x2a\x4a\x4d\x3e\x2d\xab\x67\xcb\x35\xd4\x71\x15\x56\x05\x97\xe5\xfe\xba\x87\x0e\x90\x69\x37\x75\x8d\x5c\x6f\x74\x1f\xa9\x7a\x11\x26\x60\xf1\x37\xe8\xe0\xbb\x63\x04\x64\x34\x2c\x16\x5a\xdd\x1d\xd5\x3a\xd5\x6f\x8e\x81\xac\x7d\x73\xf5\x80\xf2\x13\x8d\x72\x07\xd5\x81\xe1\xbd\xa7\x61\x60\xbb\xa9\x25\xcd\xb0\x16\x7c\x53\x81\x01\x8d\xa8\x85\xda\x77\xa2\x1f\x1d\xa1\x67\xab\xf2\xac\xac\xca\x0f\x1c\x9d\x2f\xe7\x57\x8b\xe5\x59\xc9\xe6\x68\xf9\x81\xaf\xd0\x7f\x3c\x9c\x90\xbd\xbb\x68\xf3\x8e\xa2\x7d\xb4\x79\x17\xc1\xbf\x21\xfc\x1b\x88\x30\x63\x46\xa9\x2c\x5a\x92\x97\xe7\x07\xde\x21\x6f\x13\x3b\xb6\xcc\x5b\x98\x53\x18\x8e\x8d\xf6\x31\xb2\xe8\xd5\x0b\xf0\x72\x8e\x4f\x0d\x3f\x75\x81\xb1\xbe\xce\xa6\x03\xfb\xd9\xdb\x75\x35\x65\x0d\xfe\x53\xf1\xb3\xf3\xe5\x8a\xad\xae\x3a\x37\xd1\x09\x17\x78\xa9\x0f\x44\xd6\x55\x4a\xe3\xad\x33\x66\xef\x7f\x69\xec\xd9\x18\xdf\xbd\xb5\x1d\x7f\xbb\x95\x1d\xbf\xb3\xae\xe3\xbb\x56\x75\x6e\xfe\x2a\x81\xe5\x45\x75\x7e\x51\x3d\x86\x57\xeb\x0e\x2c\x82\x24\x3d\xe7\xeb\x72\xc5\x73\xed\xa2\x81\xb4\xac\xd6\x75\x41\x68\xd9\xb8\xf3\xb6\x50\x37\x7e\xba\x98\xd7\x6a\xd2\x6a\x70\xb3\x15\xbf\x8b\x08\x09\xa6\x88\x84\xd1\x14\xf9\x34\x98\xa2\x10\x93\x7e\x63\x75\x67\xc1\x5d\xf1\x4c\x7f\xd4\xbf\xb4\xa0\x7e\x69\xb6\xde\x5b\xa0\xf7\xae\x87\xed\x1a\xf7\x17\xc0\x4c\x2d\xdc\x84\x58\xbf\x7b\xd7\xdf\xde\xbc\xb5\x44\xfb\x2d\x4c\x4d\xfc\x01\x1e\x69\x72\x0b\x7e\xd9\x98\x1d\x2c\xc2\x8d\x95\x12\x00\x4e\x9a\xdb\x7a\x61\x04\x88\x3c\x0f\x1d\x20\x31\xd0\x36\x37\x25\xe8\x92\x10\xd9\x8b\x4f\x3e\xd7\x8a\x9e\x61\x62\xce\x20\x34\xe3\xe4\x59\xdd\x89\xc7\x6c\x01\x73\x3f\xbd\xae\x1d\x21\x62\x9a\x43\x4b\xd7\xcb\x55\x3a\x2e\xff\x1e\xfa\x4f\xa9\x24\xf8\x84\x94\xa8\xbb\x28\x26\x64\x6d\x9d\x36\x7f\x42\xe0\x0e\xfa\x3e\xb8\x88\xf5\xae\x62\x16\xd6\x2b\xa8\x05\x79\x67\x3d\x41\xd2\x29\x24\x48\xae\x53\x41\x90\x74\x4a\x07\x92\xeb\xd7\x0c\x54\x0c\xe3\x31\x8e\x71\x97\x65\x7c\x2d\x9e\x71\x97\x69\xbc\x0b\xd7\x46\x3d\x48\xe3\x6a\xa6\x46\xca\x45\xb5\x94\xd6\x6c\xd6\xf4\x9c\xc1\x64\x5e\xed\xce\x06\x51\x08\x88\x43\xb8\x6f\xf6\xdd\x31\xc8\xc5\x06\x33\x5f\x5e\x22\x05\x33\xbe\x1a\xf1\x5c\x0c\xb0\x6b\x8b\x0f\xc8\x44\x19\xfc\x40\x7e\x94\x49\x2f\x7c\xb6\xbb\xc0\xcb\x19\xaf\xd8\xf0\xc9\x0e\x6f\x0d\x1a\xb2\x27\xa5\x78\x05\x99\x5f\x9c\x2d\xa0\x73\x06\xb7\xaa\x25\x58\xa7\xd9\x53\xd4\x66\xd2\x46\xe0\x1d\xdf\x49\x74\x1a\x1d\x2d\xb5\x6f\x28\x16\x42\xe2\xaf\x4e\x3d\x1b\xed\xb9\x60\x1f\x6b\xb0\xf3\xe5\xa5\x35\x2f\xb5\x4a\xeb\xa5\x31\xcf\x31\xf5\xe4\xa5\xd0\xc2\xcb\x37\x1b\x1b\xef\x2f\x37\xd2\xd6\x8e\xa1\x07\x76\x20\x30\xb6\x63\x60\x7d\xbb\xdd\x37\xd7\x33\x03\x47\x58\x6d\x7b\x14\x40\x97\x26\x42\x2f\x01\xbc\x1e\xba\x16\xcb\x5f\x6e\x70\x0b\x8e\xb7\x01\x97\xf6\xf5\x72\x83\x5d\x7a\x54\xb0\x8f\x1b\x58\xd0\xa3\xd3\xbc\xd7\x17\x2b\xf0\x28\x79\x9d\x88\x30\xf5\x71\x2b\x7f\xb9\x09\x54\x2c\x40\x93\x89\xe2\xad\x39\x1a\xac\xe8\xab\xf3\xc1\xb6\xd7\x1b\xc0\xf6\xb8\xc1\x26\xa3\x86\xc4\xf6\xb8\x87\xed\xc9\x38\xb6\xdf\xd5\xa9\x3a\xa1\xd0\x61\x9f\xa8\x1f\x12\x2d\x66\x8a\x76\x7a\xdb\x7b\x3e\x5b\xa2\x67\xa5\xc3\xb2\x05\xc9\xfa\xce\x47\x7c\x4f\xfb\x2a\x53\xb9\xe6\xfb\x27\x9b\x7c\x47\x72\x0d\x5a\x97\x19\x0b\x20\x69\x41\x63\x01\xa9\x86\x7e\xdc\x42\xdb\x43\x12\x0c\x16\xb3\xe5\x53\x99\xa5\x1c\x77\xe6\xc3\x74\xbe\xac\x9d\x7d\xbe\x84\x44\xcf\x11\xe2\xc5\x0b\x74\x4b\x62\x74\xe2\x41\xf3\x95\x49\xdd\xe9\xfb\xf7\x5b\x26\xc1\xb4\xeb\xfe\xc1\x55\x9a\x3e\x41\x07\xda\x73\x9b\xa1\xa3\xae\xeb\x34\x38\x8c\xc8\x9f\xec\x88\xbc\x3b\xe7\xd1\x76\x77\xab\x19\x8f\x7e\x97\x15\x57\x1a\x1a\x98\xed\x18\x32\x17\x05\xd7\xee\xf9\x93\x11\x1a\x8f\x77\xa4\xe1\x1a\xdb\x56\x6c\xb1\x3e\x5f\xae\x9d\x56\x02\xe1\xf7\x59\xf9\x58\x3a\xc6\xcb\x37\xda\x84\x62\x6b\x87\xd6\x31\x4f\x36\xdc\x66\xe0\x53\x90\x63\xa3\x9f\x35\x7e\x9c\x97\x88\x55\x30\x04\x42\xbc\x34\xe7\x84\x2f\x3d\xe8\x83\xb1\x68\x6b\xf3\x72\xe4\x35\x01\xc0\x08\xf7\xd2\xab\xbb\x23\xa1\x6d\x2e\xff\xd2\xab\x3b\xa3\xe0\x2c\xe3\xd6\xd1\x11\x3a\x99\xb9\x82\xdf\xf6\xc3\xfa\x35\x87\x8c\xf1\xd0\x88\xb4\xf0\x55\xc7\xe1\x66\x5c\x19\x31\xee\xdd\x42\x6a\xdd\xea\x65\x63\x70\xdb\x37\xd9\xe0\xa6\xd1\x44\x4b\x42\xf6\xb6\x19\x00\x25\x02\xd2\x43\x40\x06\x08\x9c\x52\x14\xb9\xc7\x6a\x79\xe9\x10\xe2\x5c\xf3\x86\x97\xad\x6b\xbc\x43\x93\x7f\x28\xf6\xe5\x0f\xb7\x6b\x66\xe0\xab\x2b\x7e\xcc\x35\xaf\x79\xd9\xba\x90\x8e\x11\x7e\x68\x31\xce\x97\x97\x9f\x3e\x41\xfb\xc3\xd2\xf4\x46\x32\xd0\xb7\xd5\xd3\x3a\xd3\x90\x62\x7c\xeb\x4d\x66\xc2\xf3\xd1\x97\xb6\x0e\x16\x9b\x23\x76\xf2\x95\x6e\x0b\xe1\x92\x8e\xc5\x8e\x7f\xae\x6d\x51\x86\x49\x9a\x1b\xdf\x15\x35\x80\x6f\x66\x7c\x44\xbb\xe1\x34\xd0\xd7\x30\x79\x35\x9c\x07\xba\xee\x5e\x2a\x7c\x9d\xad\x54\xb0\x49\x2a\xe3\xe5\xbc\xbb\xdf\x09\xef\xa1\xa3\x2e\xff\x7b\xe8\xeb\xfe\x0f\x40\x1c\x16\x68\x9a\xdd\x5c\xff\x24\x9b\xa0\x3e\x79\x0e\x4f\x9f\x66\xac\x99\x37\xce\x41\xa2\x23\xa3\xea\x75\x90\x7a\x16\x70\x88\xf3\xc8\xb8\x99\xee\xc5\x7f\x5d\x70\xfe\x37\x3e\x44\x3a\x63\xeb\x59\x6d\xdc\x5b\xdd\x45\x3f\xe0\xe2\x53\x26\x0b\xc7\xe7\x84\xb6\x4f\xe9\x6d\xe9\xfc\xee\x73\x88\x2d\x3d\xfb\xac\x9c\x96\x1a\xaa\x89\x39\x3d\xe1\xdc\x69\x6e\x4e\x43\xa5\xa6\xe7\x74\x54\xd7\x9d\x57\x6c\x45\xe1\xee\xc4\xe3\x41\x27\x1e\x5f\xb7\x13\x8f\x07\x9d\x78\xbc\x5b\x27\xcc\xaa\x92\xa6\xab\x9c\xac\x5a\xa2\x15\xaf\x56\x25\xff\xc0\x0d\x1b\x10\x91\x3a\xdc\x2d\xe3\xc1\xf9\xc5\x7a\x56\xb3\x61\x12\x91\x01\xf2\xc9\x10\xf2\xd3\xcb\x13\x1b\x4e\x0f\x35\xa4\xa7\x43\x17\xb6\x9e\x27\xba\xa1\x5d\x93\xf6\xf8\xa5\xb6\x50\x1a\xc2\x59\x73\xd8\x69\x8b\x08\xb1\xe5\x62\x4e\xfd\xb1\xdd\x9f\xe9\x14\xfb\x97\xed\x9a\xd7\xdc\xae\xe9\xef\xba\x59\xd3\x1f\xdb\xaa\xe9\x3b\x36\x6a\xfa\x5f\xb6\x69\xde\xf4\x36\x4d\x7f\xcb\x4d\x9a\x06\xb5\x74\xb6\x68\xfa\xdb\x6c\xd0\xf4\xed\xc7\xf0\x9b\x8d\x87\x77\x69\xf0\xf1\xed\x94\xe2\x7f\x91\xed\x9a\xfd\x02\x3b\x21\x26\xbf\xdb\x1e\xce\xba\xdc\x8e\xa0\xf9\xc7\x2a\xb7\x73\xad\xdd\x96\xea\x71\xbb\xdb\xb3\x86\xd9\xa9\x20\x4f\x88\x49\x67\x5b\x48\x88\x89\x75\x9b\x09\xdd\xb2\x20\x8f\x00\xec\x6c\x35\xa1\xaa\xaa\x45\x88\xc9\x8d\x1d\x21\xd6\xbb\x6f\xad\xc9\x33\xd8\xe4\xe0\x6d\xb2\x34\x4d\x93\x3c\xcc\xa7\x5a\xc1\x9e\xbd\xa9\x09\x32\x22\x09\x23\x09\x61\x7a\x39\x9f\x3d\x43\xdd\x1e\x43\xd3\x04\x87\x89\x87\x43\xa6\x57\xff\x31\x13\xc1\x21\x29\x78\x26\x6b\x06\xd5\xb5\x81\xb6\x24\x12\xc5\xbe\x4f\xa2\x48\x96\x15\x52\x95\x83\xcc\x44\x28\x4f\x83\x80\xd1\x58\xaf\x2b\xb4\x25\x91\x3c\xf5\x32\xc2\xbd\x5c\x2f\x43\x64\x26\x12\xc4\x69\x18\x50\x9c\xeb\x45\x8a\x7a\xa9\xe9\x4d\x57\x29\x12\xf6\x74\xcd\x2a\x45\x38\xfa\x52\xa6\xe8\x86\x72\x22\xba\x73\x99\x22\xd1\x64\x2c\x2f\xd2\x63\xc6\x30\x33\xa2\x5f\xca\x14\xdd\x7c\x6e\x44\xb7\x2d\x53\x64\x54\x4e\x37\x3f\xa2\xa3\x65\x8a\x7c\xea\x2e\x53\x24\x86\xf1\xbb\x94\x98\xb2\x25\xf2\x2f\x92\x2d\xfd\x4b\x1f\x6e\xb9\xd9\x83\x2d\x9f\xe9\xc8\xca\xf5\x93\x28\xf9\xa8\xe9\xae\x42\xf4\x4b\xbd\x83\xd7\x70\xd7\x4d\x77\x93\xef\x21\x3b\x3f\x9f\x5f\x4d\xd4\x8f\x53\xc4\x56\xa7\x17\x67\x7c\x51\xad\xfb\x77\xf2\xe8\xc7\x67\x5a\x7e\xa0\x94\x52\x4b\xa2\x47\xde\xdb\x04\x84\x32\x52\x24\x90\x57\xe4\x31\xa1\x8c\x13\xb2\x37\x1d\xc2\xc5\xd8\x8f\x83\x20\x81\x32\x83\xc4\xe7\x45\x14\x66\xb9\x9e\x1a\x0c\x1a\xa4\x61\xe6\x15\x69\x56\xc0\x05\x08\x59\x90\xfb\x29\x29\x4c\x88\x79\x92\x86\x79\xca\x42\xb8\x3d\x1b\xd3\x24\x4f\xd3\xcc\x89\xd8\x4f\xc2\x28\x23\x61\x0a\xe9\x8c\x1f\xd0\x34\xf4\xa9\x09\x71\x98\x14\x18\xe3\x02\x38\x4e\x23\x2f\xcc\x3d\x9c\x38\x11\x27\xc4\x2f\x28\x61\x70\xe5\x36\x2b\x70\x12\x14\x49\x6a\x42\xcc\x52\x9c\x85\x3c\x07\x8e\x73\x16\xe5\x14\x63\xea\x44\x9c\x53\x2f\x66\x4c\xca\x98\xf9\x9e\xef\x91\xc0\x28\x63\x4c\xa8\x1f\xa6\xf2\xce\x88\x20\x8c\xbd\xa8\x48\xb9\x13\x31\x09\x7c\x4c\xc3\x14\xee\x8e\x08\x38\x0f\x52\x42\x33\xa3\x28\x42\x2f\x8b\xf3\x0c\x2e\x10\xcf\xc3\xa2\x48\x03\x4e\x9c\x88\x63\x92\xf2\x30\x8f\x41\x14\x05\x89\x53\x9a\x44\x46\xe5\x51\x2f\xe7\x29\x96\x97\x57\xf8\x29\x8e\x92\x28\xc5\x6e\x19\xa7\x79\xe6\x45\xb2\x42\x25\x09\xb3\x18\x13\x3f\x34\x21\xce\x70\x92\x16\x58\x32\x90\x15\x51\x42\xa2\x24\x70\x22\xe6\x41\x92\x46\x49\x06\xb2\x4b\x78\x81\x03\x96\x1b\x65\xcc\x8b\x94\x07\x31\x85\x6b\xc4\x7d\x1a\x14\x24\xe4\xbe\x13\xb1\x57\x64\x38\xc9\x33\x68\x40\x53\x9a\xe5\x61\x6a\xe4\x98\x04\x5e\xc6\x70\x96\xc1\x25\xed\x31\xcb\x92\x2c\x0a\xdd\xca\xcb\x79\x42\xb2\x08\x1c\x24\x4c\x48\xea\x91\xd8\x88\x38\x60\x71\x40\x03\x06\xef\x08\x11\x67\x11\x0f\xa8\x9b\xe3\x30\x4b\x3d\x96\xe4\xc0\x49\x9a\x07\xb8\x48\xf3\xc0\xe8\xd2\x51\x91\x50\x9a\x03\x62\xea\x63\x1c\xfa\xa9\x9b\xe3\x84\xfa\x3c\xc4\x21\x01\x97\xe6\x51\x94\x17\xcc\xec\x20\xd4\xc7\x59\x14\x41\x86\x4f\xf2\x34\xf0\x09\xf6\xdc\xb1\xc2\xf3\x7c\x12\x67\x54\xde\xf9\x5e\xa4\x04\xfb\x46\x73\x4b\x8b\x30\x89\x8b\x4c\xd5\x37\xe5\x85\xc7\xb9\xdb\x2a\xb2\x88\x7b\x5e\x5a\x80\xe1\xfb\x39\xa3\xb4\xc8\x8c\x56\x91\x87\x2c\x4e\x70\x00\x88\x13\xdf\x63\x2c\x26\x6e\x51\x78\x51\xc6\x22\x3f\x94\xd7\xbb\x78\x9e\x4f\x89\xd9\x41\x70\x40\x12\x92\xc8\x77\x2f\x8f\x79\x3c\xe2\xb1\x5b\x14\x24\x4e\x63\x8f\x51\x08\x2e\x41\x94\x13\x52\x14\x46\x97\x26\x1c\x0b\x31\x81\xc8\xc2\x8c\x44\x59\x42\x22\x27\xe2\x20\x27\x59\x94\x17\x60\x15\x21\xcb\x02\xc2\x78\x6e\x8c\x15\xbe\x4f\xbd\x1c\x83\xc8\x92\x3c\x09\x53\x3f\x2f\x9c\x88\xa3\xd0\x63\xb1\x1f\x06\xd2\x41\x58\x11\xf9\x39\x37\x9b\x5b\xc4\x3c\x96\x42\xdc\xf6\xb3\x38\x4e\x09\x73\x87\x4d\x8a\x33\x92\x25\x44\x46\xb7\x98\xe7\x8c\xf3\xc8\x84\x38\x21\x31\x21\x99\x14\x19\x0e\x28\xf1\x43\x3f\x75\x22\x66\x24\x2d\x38\x65\x32\xce\x66\x05\xf6\xfc\xc8\xe8\x20\x8c\x62\x16\x45\x01\x70\x9c\x66\x01\xf1\x3d\xcf\x1d\xdd\x32\x12\xa4\x34\x8d\x3d\x88\xb3\x5e\x41\x93\x38\xc1\xc6\xe8\x16\x47\x59\x88\x19\xc8\xd8\x8b\xc2\x20\xe5\xbe\xdb\x2a\x72\x9c\x10\x4e\x71\x02\x88\x23\x5e\x84\x04\x1b\xc7\xbc\x3c\x4a\x12\x2f\x22\xa0\x8b\x30\x8c\x42\x96\x8c\x78\x5e\x11\x78\xdc\x0f\xa5\xec\xc2\x38\xc6\xc4\x23\xcc\x68\xc7\x5e\xc4\x98\x27\x7b\xe6\x93\x34\xcd\x71\xea\x56\x1e\x4e\x58\x90\x61\x0c\x61\x33\xa5\x39\xc9\xbd\xcc\xc8\x31\xe6\x7e\x1c\x65\x9e\xb4\x63\x1c\x60\x96\x86\xee\xe8\x46\xe2\x80\xc6\x71\x00\x76\x9c\x17\x94\xf3\x34\x49\x4c\x88\xfd\x20\xf5\xd2\x2c\x85\x9e\x71\x9c\xa4\x01\x1d\x31\x37\x3f\xc1\x99\x97\xa5\xa0\x94\x2c\xcc\x92\x90\x45\xbe\x31\x1e\xf3\x9c\x32\x16\x40\xd8\xe4\x7e\x80\x29\xcb\xdc\xe6\x16\xa6\x49\x96\xb1\xa0\x90\x23\x43\xe4\x73\x3f\x36\x22\x8e\x28\xe1\x51\x21\x83\x55\x1e\xa5\x24\xa5\xcc\x2d\x8a\x38\xa0\x05\x25\x1c\x1c\x24\xcc\x79\x91\x12\x73\xac\x88\x29\x0b\x23\x5f\x8e\x34\x81\x8f\x63\x52\x44\x6e\xab\xa0\x41\x46\x63\x8a\x65\x26\x84\x0b\x8f\xa5\xb1\x31\x6c\xd2\x2c\x8b\x3d\x22\x95\x87\x59\x14\xf8\x09\x77\xe7\x6e\x89\x97\xf2\xa2\x28\x98\xcc\x22\x23\x1f\x73\x62\xb4\x0a\x16\x84\x5e\x94\x71\xf0\xbc\x9c\x53\x92\xe6\xdc\x9d\xbb\xa5\xbc\x48\x98\x5f\xc8\x91\x81\x64\x51\x9c\x60\x73\x5e\x11\xc5\x38\xa6\x85\x1c\xc2\xfc\x98\x84\x3e\x71\x2b\x2f\x63\x24\xf6\x79\x06\x32\xe6\x8c\x44\x11\x4e\x8c\x32\xce\x31\x8d\x52\x2a\x87\x26\x22\x0c\x89\x74\x27\x01\x87\x89\x08\xcb\x59\x9c\xe7\xe0\x20\x59\xce\x3d\x9e\x62\x63\xd8\x2c\xc2\x38\x0f\x8a\xb8\x50\x83\x2e\xcf\x71\xec\xb6\x63\x2f\x2a\xbc\x28\x96\xf9\x42\x4c\x70\x1c\x15\xa9\xd1\xa5\x3d\x16\xf9\x71\x9e\x81\x83\x30\x92\xd1\x84\x32\xf7\x08\x82\xb1\x5f\x24\xd4\x0b\xd4\xc4\x5d\xe2\xe5\xcc\xc8\x31\x4e\x63\xec\xa5\xbe\x8c\xc7\x3e\xce\x82\x18\xbb\x65\x4c\x68\x9e\xc6\x71\x11\x4a\xab\xf0\x82\x38\xa7\xc6\x78\xec\x93\x8c\xb1\x34\x06\xab\x08\xbc\x2c\x26\x41\xe2\x76\x10\x3f\x4b\x78\xca\x3d\x10\x05\x0e\xb3\x24\xe5\xa9\x51\x79\x81\x8f\xf3\x28\xce\xa0\x67\x49\x86\x3d\x2f\x0f\xdc\x76\x1c\x64\x59\x98\x07\x32\xf1\xce\x52\x9f\x07\x24\x35\x0e\x4d\x22\x5d\x21\x49\x02\xc1\xaa\xc8\xa2\x30\xe6\x22\xbc\xba\x62\x45\x91\xa5\x51\xc1\xe4\x20\xc9\xf2\xa8\x60\xdc\xc8\x71\x94\x05\x01\x4e\x28\x20\x0e\x58\x10\x87\x14\xc7\x6a\x12\xf5\xad\xe3\xd8\x6a\xfb\x5e\xf8\xea\xba\x27\x54\x6d\xd7\xa0\xbd\xea\x9c\x50\xfd\xe5\x7a\x27\x54\x43\x4c\xb6\x5b\x3a\x30\x2c\x47\xdc\x7c\xf5\xd1\xeb\x2e\x1d\x44\xcc\x4b\x78\x3d\xe1\xee\xa7\x59\x96\x78\x96\xa5\x83\x34\x8d\x62\xc6\xe5\xf0\x4b\x83\x8c\xb1\xb8\x9b\xba\x38\x88\xf8\x59\xc4\x0b\x3f\x86\x48\x56\xf0\x24\x28\xa8\x88\x64\x26\x48\x16\x06\x45\x11\xfa\xe0\x05\x61\x81\x73\x3f\x2a\xb6\x9d\xd5\x0f\xb1\xc7\x43\x22\x83\x0f\xcb\x79\x44\x49\x6e\x59\x3a\x48\x52\x2f\x8c\xa8\x34\x48\x92\xfa\x3c\xca\x70\xb1\x25\x11\x5c\x50\x3f\x4f\xa4\xcd\x17\x69\x80\xd3\x3c\xb2\xf4\x24\x4c\xb9\x97\xe5\x32\x0d\xc2\x7e\xcc\x09\x8e\x93\x5d\x96\x0e\x6e\xfa\x1c\xe9\x36\xa5\x61\x01\xce\xb3\x57\x7e\x7d\x84\xed\xa5\x5f\x1f\x11\x7b\xed\xd7\x47\xbe\xbd\xf8\xeb\xa3\xc0\x5e\xfd\xf5\x51\x68\x2f\xff\xfa\x28\xb2\xd7\x7f\x7d\x14\x5b\x0a\xc0\xca\x0e\x42\x79\x58\xe3\x3e\x70\xf9\x7c\x2e\x9f\x0f\x0f\x7b\x48\x19\x40\x73\xe3\x11\x28\xf9\x7c\x2e\x9f\x5b\x9a\x13\x68\x4e\xac\xcd\xc9\x5c\x3e\xb7\x34\xf7\xa1\xb9\x6f\x6d\xee\xcf\xe5\x73\x4b\xf3\x00\x9a\x07\xd6\xe6\xc1\x5c\x3e\xb7\x34\x0f\xa1\x79\x68\x6d\x1e\xce\xe5\x73\x4b\xf3\x08\x9a\x47\xd6\xe6\xd1\x5c\x3e\xb7\x34\x8f\xa1\x79\x6c\x6d\x1e\xcf\xe5\x73\xc3\xb6\xbe\x2d\x8b\x1e\x4b\xcb\x30\x21\x67\xd2\x28\xfa\x15\xf7\x60\xcb\xad\x34\x08\x53\xab\x54\xda\x82\xa9\x55\x26\xed\xc0\xd4\x2a\x93\x26\x60\x6a\x95\x4b\xf5\x9b\x5a\xe5\x52\xf3\xa6\x56\x5c\x6a\xdd\xd4\x8a\x4b\x85\x9b\x5a\x15\x52\xd9\xa6\x56\x85\xd4\xb3\xa9\xd5\xa9\xd4\xb1\xa9\xd5\xa9\x54\xaf\xa9\xd5\x4c\xaa\xd6\xd4\x6a\x26\xb5\x3a\x37\xd5\x1d\x74\x1d\xdd\xdd\xf2\x3a\x54\x6b\x3d\xed\x9a\xfe\xab\x52\xd6\x1e\xb6\x1d\x37\x7f\x00\x23\x78\xbd\x7c\x36\x04\xd9\xa2\x50\xb4\x24\x23\x44\xf0\xaa\xac\x4f\x1b\xe8\x55\xa3\xd1\xd7\x88\xbc\x05\x48\x73\x2d\xd7\x16\xc7\x5c\xe2\x50\xe7\x0b\xfa\x38\xe0\xd4\xfc\xb5\x2a\x50\x1f\x1d\xa1\xff\x80\x6a\xc4\x76\xe2\x75\x49\xe7\x9d\x2a\x54\x6f\x66\x4d\x9d\xe3\xcd\xd8\x59\x3c\x05\x36\xd7\x5a\xb8\xcf\xe3\x49\xa8\x59\xa7\x0a\xf6\x4c\x16\xff\xd5\x8b\x57\xcf\xa1\x44\x71\x5d\x0e\xb8\x03\x47\x07\x70\xb0\xe9\xf5\x1d\xea\x82\xc5\xae\x13\xa6\x12\x72\xde\xe1\x62\x3e\xe4\x62\x66\xe2\x62\x3e\xe4\x62\xa6\x73\xd1\x85\x8b\x87\x70\x96\x4a\xc6\xba\x4a\x2d\x35\x73\x3e\x68\xb5\xb7\x77\x29\xbe\xdd\x6a\x14\x6f\xa7\x51\xdc\x6a\x14\x6f\xa5\x51\x3c\xeb\x14\xf8\x9e\xd5\x55\xb8\xb5\xc2\xdc\x73\x55\xab\x5b\x13\x12\x56\x12\xee\x82\xc1\x3e\xe6\x44\x53\x69\x8d\x2f\x1a\x55\x29\x9e\x77\xd8\x98\x1b\xd8\x98\x99\xd8\x98\x0f\xd8\x98\x75\xd8\xe8\x22\x8c\x06\xf8\x48\xe4\xd4\xe9\x4e\xb5\xc3\x5d\xa1\x24\x6e\xd5\x1e\xbb\xd4\xfe\xaa\x8c\x65\xe4\x32\x0e\xcc\x3d\xc8\xb9\x82\x74\x9c\x09\x97\x90\x38\xd2\x02\x89\xf5\x56\xe8\x1a\x56\x32\x80\x8d\x99\x45\x1f\x76\x5e\xc3\x8e\xf2\xd0\x46\x9a\xb9\x10\x5a\x19\xf7\x47\xae\x2e\x78\x1b\xca\x66\x12\x7c\x06\x35\xdb\x04\x1e\xa1\x49\x6f\x0f\xdd\xaf\xbd\xb3\xf9\xe5\xff\x47\x18\xdd\x45\x83\x6d\xd3\x43\x3e\xc4\xbf\xb5\x06\xc7\xd9\x10\xff\xee\x37\xde\x62\xe1\x02\x5f\x97\x0b\x90\xe2\x96\x3c\x48\xed\x0c\x39\x90\x9a\x18\xd0\x37\x23\x6d\x47\xc5\x57\xa5\x4d\xbd\xed\xa8\xf7\xaa\x34\x31\x67\xaf\x89\xaf\x8a\xe2\xcf\xd0\x6d\x54\xcc\x54\x59\x7c\xf1\xc5\x7c\x8e\x4f\xb6\x91\xbe\xcf\xe7\xa2\xcd\x5c\xb5\x11\x5f\x4e\xe7\x8e\x62\xfa\x33\xa8\xa6\x2f\x50\xa7\x92\x0e\x7c\xce\xe4\xe7\x54\x7d\xb6\x37\x9f\x43\x73\x41\x25\x95\x24\xe1\x73\x26\x3f\xa7\xea\xb3\xbb\x24\xff\x4c\xd6\xe4\x57\x01\x47\x8e\x2b\x6c\x2e\xcb\x4b\xef\xc9\xe2\x07\x6c\x56\x57\xec\x57\x0f\x3b\x35\xfb\x67\xda\x2d\x12\xac\x1e\x75\x9c\x95\xf9\xe1\x6d\x6a\xd2\x20\x52\x34\x67\x5d\x9a\xf3\x0e\xcd\x59\x97\xe6\x5c\xa7\x39\xdb\x86\x26\x96\xfd\xe4\x6a\x68\x90\xe7\x4d\xb8\x1c\x14\x68\x5d\xf6\x7f\x56\x5f\x5a\xa1\x3d\x0c\xda\x87\x82\xa6\x5f\x3f\x93\x65\xb8\xdd\x34\x65\x3f\x15\x70\x4d\x73\xd6\xa5\x39\xef\xd0\x9c\x75\x69\xce\x75\x9a\xb3\x96\xa6\x31\xeb\x1c\xbf\x87\xc0\xcc\xeb\x8f\x50\x7d\xe9\x47\xfb\x61\xaa\x1f\xc1\x79\x7f\x2c\x5d\xc7\xa8\x7e\x84\x60\xf0\x63\x69\x0b\xa1\x1f\xe0\xa2\x04\x01\x33\x9b\x37\x2c\x9a\x9c\x52\x02\x0a\x82\xb3\xb6\x2f\x32\x5c\x54\x58\x0f\x17\xb3\x6d\x62\x55\x4b\x56\xfc\x2b\x24\xe2\xa6\x59\x01\xa9\x6c\x66\x22\x98\x5d\x8b\xe2\x8f\xc6\xd0\xd3\xa7\xf8\x63\x69\xa2\xf8\x63\x79\x1d\x8a\xe6\x60\xd7\xa7\xf8\xca\x48\xf1\x95\x89\xa2\xd9\xda\xfa\x97\x57\x58\x48\xc2\xe4\x45\xed\xf6\x00\x68\xe5\x0e\xe6\x41\xea\xa8\xb4\x2f\xc3\x23\xb0\x48\x74\x16\x6b\x5c\xdb\xb1\xf9\xf3\x79\xce\x2a\x8e\x2e\xdd\x6f\xfa\xe2\x0f\xde\x37\x8d\xf6\x0d\xaf\x9b\xa7\x26\xb6\x61\x00\x2a\x4c\x6d\xe0\xc5\xb6\x30\xb5\x81\x77\x68\x6e\x6a\x03\xaf\xd0\xdc\xd4\x06\x5e\xc9\x27\xf9\x1c\xae\xef\x98\xdb\xee\xef\x80\x77\xfa\x49\x3e\x03\x28\x29\x3a\xae\x4b\x2e\x1f\x08\xcd\x7a\x13\x88\xc0\x94\x99\x78\x84\x29\x85\xcc\xc4\x23\xcc\x5e\xa4\xa6\x36\x30\x79\x91\x9a\xda\xc0\x3c\x09\x33\xb5\x81\x69\x92\xc1\x6d\x06\xe2\x0f\xa6\x5d\x26\xd2\xd4\x2b\x62\x15\x06\x4c\xdc\x4c\xa4\x1c\x84\x65\xed\xb7\x23\x8e\x94\x46\x35\x4c\x76\x6e\xf4\xb2\x12\x6d\xce\x10\x32\x83\x47\x60\xff\x6c\x90\x0d\x3c\x6a\x8a\x51\x4c\x1e\x81\xdd\x33\xc9\xec\x23\x4f\xe7\x96\x0d\x99\xed\xe3\xd1\x66\x19\x25\x41\x10\x51\x3a\x24\x88\x5b\x82\x20\x9e\x54\x11\xec\x44\x82\x74\x9c\xa0\x36\x2f\x29\x09\x12\x08\xb1\x43\x82\xa4\x25\x48\x66\xf5\xb8\x34\x01\x78\x2d\xbc\x8e\x13\xd4\x66\x32\x25\x41\x5f\x10\xcc\x87\x04\xfd\x96\xa0\x2f\x68\xe5\x8a\xa0\x3f\xe2\x0e\x7d\x3c\xda\xdc\xa7\x24\x18\x08\x82\x7c\x48\x30\x68\x09\x06\x82\x16\x57\x04\x03\x9d\x20\x1f\x27\xa8\xcd\x96\x4a\x82\xa1\x20\x58\x0c\x09\x86\x2d\xc1\x50\xd0\x2a\x14\xc1\x50\x27\x58\x8c\x13\xd4\xe6\x57\x25\xc1\x08\x5e\x2a\x86\x04\xa3\x96\x20\x64\xef\xa7\x8a\x60\xd4\x79\x89\x18\x27\xa8\xcd\xc8\x4a\x82\xb1\x20\x38\x1b\x12\x8c\x5b\x82\xf0\xda\xa4\xc6\x64\x01\xef\x4a\x02\x3e\xf9\xec\xc5\x97\x4b\x71\x6e\xee\x52\x1c\x2c\x92\x7b\x75\xb3\x99\x40\x06\x75\x58\x7c\xef\xa6\xaf\xc5\x31\x93\xc1\xff\x94\x17\xe3\x9c\x2c\x17\x1f\xf8\x4a\x56\xf9\x45\xd5\x12\xf9\xe4\x20\x2d\x2b\x91\xa0\xe4\x88\xc1\xfe\xec\x94\x17\xcb\x15\x57\xdb\xa9\x07\x5a\xd3\xce\x9a\x68\x6b\x77\xd5\xf2\xb5\x4f\x6e\xe2\x22\x9e\x3f\xea\x15\x3c\x3a\x9f\x4d\x7d\x90\xbb\x08\x7b\x24\x38\xf2\x55\x9d\xe2\x2f\xa7\x9b\xac\x47\x95\x42\x4c\x76\x3d\xdd\x24\x9a\x8c\x9c\x6e\xea\x6c\x6b\x18\x9c\x6e\x0a\x31\xf9\x72\xba\xe9\xa6\x4f\x37\x09\xad\x6c\x77\xba\xc9\xa8\x9c\xce\xe9\x26\xa9\x20\xe7\xe9\x26\x79\x8e\x76\xcb\xd3\xdf\xfe\x1f\xfa\x3c\x13\x5f\x64\x07\x29\x5b\xf3\x28\xe8\x3d\x38\xcb\xc3\x3e\xe8\x87\xf3\xf7\x79\xd1\xfb\x31\x2b\xcf\x67\x7c\xf5\xbb\x1c\x89\xd2\x58\x85\xef\x82\x43\xf9\x40\x32\x06\x9f\x75\x7e\xfe\x15\x8e\x4e\xbd\xda\xea\x4e\x20\xd8\x3c\x73\x02\x5d\x6f\xe0\xb4\xdf\xc6\x8f\x42\x1d\x1d\xa1\x67\x7c\x75\x06\xa3\xe8\xc9\x6c\x59\x66\x1c\xe1\xfe\xb5\x29\xa2\xf9\xb3\x13\xdc\x3d\xbb\x14\xc6\x53\x14\x24\x53\x14\xe0\x29\xf2\xfd\x29\x22\xe1\x14\xe1\x78\x8a\x92\x29\x42\x58\xdb\x6a\x14\xd2\x29\x0a\xbd\x29\x0a\xc8\x14\xf9\xc1\x14\x91\x68\x8a\x30\x9d\x22\xec\x4d\x11\xd1\xe1\x92\x29\x0a\xf1\x14\x05\xfe\x14\xf9\xe1\x14\x91\x78\x8a\x70\x32\x45\x58\xe0\xd7\xe0\x22\x6f\x8a\x42\x32\x45\x41\x30\x45\x7e\x34\x45\x91\x3f\x45\x61\x38\x45\x41\x3c\x45\x7e\xa2\x01\xfa\x78\x8a\x88\x3f\x45\x38\x9c\xa2\x78\x8a\x50\x44\xa6\x28\x0c\xa6\x28\x80\xab\x05\x74\x40\xc1\x09\x99\x22\x1c\x4c\x51\x24\x00\xf1\x14\x85\xfe\x14\x05\xe1\x14\xf9\xb1\x06\x48\x92\x29\x22\x78\x8a\xb0\x20\x39\x45\x88\xd0\x29\x22\xde\x14\x61\xc1\x8e\x04\x7b\xeb\x90\x2b\x31\xcb\x95\x74\xe5\x2a\xb8\x10\x72\x14\xfd\x26\xe2\xf3\x14\xa1\x50\xe7\x56\x11\x16\xdd\x12\xdc\x02\x43\x9e\xce\xa5\xaf\x04\x27\xb8\x12\x00\xd1\x14\xe9\xdd\xc5\x91\x94\x87\x10\x30\x70\xef\x77\x15\x21\x14\x2a\x04\x2c\xe4\xe7\xc7\x52\xb0\x61\xd8\x93\x57\xe0\x29\x6d\x85\x52\xfb\x81\x4e\x41\xa8\x46\x98\x86\x2f\x54\x1a\x49\xb5\x87\xba\x0e\x85\x0a\x84\x3d\x08\xbb\x10\x3a\x14\x82\xad\xb3\x9a\xce\x8d\x50\x17\x67\x17\x73\x06\xd7\xa4\x88\xa4\x72\x3d\x2b\x8b\xc1\x0d\x4f\xe0\x05\x3f\xbc\xfc\xe5\xc5\xa3\x1f\x1e\xca\x3b\xa5\x84\xc4\xc8\x14\x41\xe7\x85\x84\xa8\xb0\x48\xa5\x26\x90\xae\xb2\x54\xac\xd4\x49\x94\xf5\x82\x40\xa8\x4e\xff\xc5\x77\x4f\x5f\xf3\x35\x62\x8b\x5c\xd5\x46\x3f\x07\x95\xca\xfb\x34\x0c\x7c\x08\xf8\x5f\x9e\x75\xf5\xd9\x4b\x29\xbd\x8d\x77\x17\x5e\x46\x28\xf1\xbc\x69\xff\x59\xfd\xae\x20\x41\x0c\x00\xa4\x03\x40\x3d\x8f\x0c\x40\x7c\x0d\x64\xf8\x34\xd0\x9f\x1a\x08\x84\x5d\x02\xc4\x40\x20\xea\x32\x69\x02\x89\x7b\xfd\x30\x10\xa2\x1d\x46\x86\x28\x92\x3e\x95\x21\x0a\xa6\x83\x98\x00\xd2\xbe\xb4\x86\x20\x59\x8f\xcc\x00\x20\xef\x77\x65\x08\xc2\x35\x90\x21\x85\xa2\xcb\xe5\xb0\x39\x75\xb5\xc6\x74\x54\x1f\x84\x8e\x10\xf0\xe9\x88\x55\x05\x7d\x22\x06\xbb\xa0\x6e\xbb\x89\xe8\xa8\x61\xc6\xd4\x65\x98\x94\x8e\xea\x3b\xa1\x23\xfa\x66\x7d\x26\x0c\x26\xd1\x27\x33\xe4\x24\xa3\xa3\x1a\xcf\xe9\x88\xd5\x70\xea\xb6\xee\xa2\x4f\xc3\xa0\x79\xab\xba\x54\x94\xc0\x66\x41\x12\xed\xa9\x45\x99\x7e\x07\xc4\x48\x3d\xe8\x62\x31\xf5\x31\xd4\x41\x8c\x36\xa1\xf3\x69\x78\x1e\x77\xd9\x70\xf8\x06\x76\x98\x7f\xd2\xe7\xd4\x1a\x28\xb0\x43\xa3\x69\xb7\x33\x06\xab\xe8\x74\xc6\x1a\x27\xb0\xc3\x7e\x79\x0f\xc4\x16\x2a\xb0\x39\x14\xd0\x51\x51\x60\x3a\x2a\x0a\x42\x47\x55\xef\x53\xb7\xda\x82\x1e\x0a\x5b\xac\x70\x89\x3b\xa2\x2e\x13\x8e\xe9\x88\x32\x28\x1d\x91\x64\x42\x47\x4d\x8b\x51\xb7\x42\xd3\xbe\xbc\x0d\x83\x47\x9f\xca\x10\x24\xa7\x2e\x95\x72\x3a\xe2\x42\x45\x5f\xa3\xfa\x1d\x55\xd3\xb1\x2c\x23\xf0\x3c\x1a\x78\xd8\x1a\x41\x14\x8c\x35\xcd\x68\x14\x68\x8b\x20\x35\x11\xcf\x44\x24\xe8\x12\x31\xc2\x84\x5d\x3c\x46\x66\xa2\x2e\x1e\x23\x4c\xdc\xc2\x18\xa8\xe8\xc1\xd6\xd8\x3c\xe9\x93\x30\x20\x61\xfd\xee\xd8\x13\x0e\x45\xc8\x80\x24\xeb\x08\xd6\x00\x90\xb7\x00\xd6\x00\x22\x59\x30\x34\x2e\xfa\x5a\xb1\xe6\x5d\x4e\x61\x62\x3a\xd2\x0b\x42\x5d\xd2\xf6\xfb\x24\x4c\xb6\x41\x7b\x7a\x37\xd9\x06\x1d\x17\x78\x44\x47\x0c\x35\xa6\xe3\x86\x4a\xe9\x88\x52\x12\xea\x50\x0a\xa3\x6e\x5f\x4a\xfb\x1c\xd8\x03\x89\xd3\x55\x72\x3a\x62\xc4\xbc\x2f\x53\x7b\x3c\xb1\x5a\x90\xfe\x02\x62\x78\x8a\xb7\x70\x7b\x4c\xb6\x70\x26\xec\x6f\xe1\xf8\x38\xd8\xc2\x9e\x71\xe8\x74\x7d\x1c\x8d\xb9\x24\x8e\x47\x82\xa1\x9e\x82\x9b\x31\x24\x63\xe1\x12\xb3\x31\xbf\xc7\xe9\x16\xd1\x12\x67\x63\x81\x0c\xe7\x5b\x04\x4b\xcc\xb7\x08\x65\xb8\xe8\x6b\xc8\x68\x2e\x63\xa1\x02\xe3\x31\x0f\xc5\x64\x0b\x07\xc1\xfe\x88\x97\xe1\x60\x9b\xc0\x16\x6e\x11\x76\x70\xe4\x8c\x6e\x38\xde\x22\x2c\x61\xba\x85\x2f\xe2\x64\x0b\xaf\xc7\x6c\x8b\x68\x8a\xd3\xb1\x08\x86\x33\x57\x08\xc3\xf9\x58\x58\xe0\x5b\x84\x51\x5c\xf4\x22\xd4\x2e\xa9\x0a\xf6\x02\x4b\x30\x32\xb3\x4c\x3a\x52\xc1\xd6\x14\x45\xe2\x36\x61\x0f\xb4\xe7\x9e\xe1\x79\xd8\x53\xce\x10\x22\xea\x08\xcd\x44\x23\xee\x40\x8c\x0f\xc7\xf6\xdc\xa4\xa5\x62\xcb\x4c\xea\x9e\xda\xb2\x92\x96\x8b\x21\x9f\x59\x4f\x9a\x43\x88\xbc\x23\x2d\x5b\x6a\x02\x18\x2c\x69\x89\x6a\x6b\x96\x80\xab\x7b\x98\x8e\xb1\x4f\xa8\xdd\x50\x7c\x3a\x66\x28\x01\x1d\x53\x74\x48\xdd\x9d\x8f\xa8\xdb\x94\x62\xed\xf9\xf0\x29\xa5\x76\xd1\x25\xd4\x25\x3a\x46\xc7\xcc\x2b\xa5\x6e\x27\xc8\xa8\xdb\x74\x72\x3a\x66\x18\x9c\x8e\x39\x41\x41\xc7\x4c\xbc\x93\x56\x58\x8c\x00\x8f\xb8\x2b\x26\x23\x16\x8a\xfd\xd1\x90\x81\x03\xa7\xa5\xe2\x70\xd4\xe1\x71\x34\x1a\x35\x70\xec\x8a\xc4\x74\xd4\x13\x71\x32\x1a\x32\x30\x73\x78\x23\x4e\x47\xc2\x05\xce\x46\xa3\x16\xd6\xc3\x81\x81\x04\x1f\x89\xbd\xb8\x18\x0d\x49\x2a\xb5\x70\x76\x13\x3b\xfd\x0a\x93\xf1\xd0\xe2\x3b\x22\x07\x0e\x46\xdc\x1a\x87\xa3\xb1\x05\x47\x4e\x07\xc6\xf1\x68\x6c\xc3\x74\x24\xf8\xe0\x64\xd4\x03\x31\x1b\x09\x03\x38\x1d\x8d\x81\x38\x1b\x0d\x05\x38\x1f\x8d\x47\x98\x3b\x82\x1d\x2e\xba\xd1\x68\x97\xfc\x81\x7a\x92\xa4\x39\xb6\xd4\xd9\x27\xf6\x02\x4b\x2a\x51\x33\x6d\x78\xee\xb7\x18\x02\xb3\x21\x06\x76\x23\x0a\xbb\x12\x31\xe7\x10\x4d\x72\x6c\x22\x1f\x7b\x9d\xf4\xcf\x3e\x7e\xd6\x2b\x2a\xe6\x0c\xa2\xd5\xad\x39\x7f\x90\xcf\xcd\xb9\x43\x2b\x3e\xdb\x0a\x4a\x2b\x1e\x03\x8e\x5c\xf3\x52\x4b\xe6\x50\x9b\xb7\x39\x77\x68\x15\x6c\xe9\xbf\x53\xbf\x98\xda\xbb\x47\xe8\x18\xf3\x3e\x1d\x13\x40\x40\xdd\x2a\x0e\xe9\x58\x17\x22\x6a\xb5\x9f\x98\x8e\x19\x1f\xa5\x2e\xf9\x25\x5d\xe2\xb6\x24\xc2\x61\x1d\x29\x75\x69\x2f\xa3\x63\xd6\x97\x53\xb7\xfd\x72\xea\x76\xbf\x82\x8e\x79\x08\xf6\x46\x5c\x04\xe3\x11\x2f\xc4\x64\xd4\x0d\xb1\xef\x1a\x29\x9c\x16\x8e\xc3\x51\x17\xc1\x91\x37\xa6\x27\x1c\x8f\x46\x32\x4c\x47\xbd\x05\x27\xa3\xe1\x02\xb3\xd1\x80\x87\xd3\x91\x98\x89\xb3\xd1\xb8\x81\xf3\x91\xb0\x84\xb9\x23\x2e\xe1\xc2\x19\x36\x64\xf6\xe0\xee\x03\x1e\xf5\x4b\x4c\xec\x8e\x89\xfd\x11\xb7\xc7\xc1\x88\xe1\xe3\x70\xd4\x77\x70\x34\x1e\xdd\x62\x47\x78\xc3\x74\xdc\x79\x12\x67\xfc\xc0\x6c\x34\xfe\xe1\x74\x34\x88\xe2\xcc\x19\x44\x70\x3e\x1a\xa5\x30\x1f\x09\x53\xb8\xe8\xc6\x91\xdd\x92\x07\x63\x4c\xa9\xf9\xb5\xad\x90\x34\xdc\x18\x53\x86\xbb\xda\x76\x0d\x63\xc6\xa0\x00\x60\x3e\xc5\x98\x37\x34\x39\x9f\xe1\x79\x54\x23\xb0\x01\xc4\x2d\x83\x86\xa7\xba\xce\x6d\x29\x43\xcb\x9f\x25\x67\x68\x7b\x68\xa0\x90\xb6\x0c\x9a\x59\xc8\x3a\x00\xa6\x81\xc3\xea\x7b\x5c\x57\x8e\x01\x75\xd1\x11\x8e\x79\xce\xc1\xd5\x1e\xd3\x11\xe1\x12\xea\xd9\x0c\xc7\xa7\x6e\xc3\x09\xa8\xcb\x70\x42\x3a\x62\x17\x11\x1d\x91\x5a\x4c\x47\x4c\x8f\xd2\x11\xd5\x26\xd4\x26\x77\x46\x47\x74\x9a\x52\xb7\xd5\x66\x74\xc4\x6a\x72\x3a\xa2\x39\x4e\xdd\x86\x5b\x50\x97\xd9\x63\xcf\xe9\xb6\x18\x7b\x56\xbd\x62\x32\xe6\xd3\xd8\x1f\xf3\x49\x1c\x8c\x78\x35\x0e\xc7\x9c\x02\x47\x63\x91\x03\xc7\x23\xbe\xdd\x8c\x7b\x56\x35\xe2\x64\xcc\x81\x30\x1b\x89\x8f\x38\x1d\x8b\x20\x38\x73\x46\x28\x9c\x8f\x45\x18\xcc\xed\x83\x73\x31\x12\x21\x20\x3f\x70\xeb\x0a\x8f\x58\x1a\x26\x23\x9e\x8e\xfd\x31\x67\xc6\xc1\x98\xb3\xe2\x70\x2c\x54\x45\xf6\x50\x84\xe3\xb1\x60\x81\xa9\xdb\x5d\x92\x31\x87\xc7\xcc\x1a\x2c\x70\x3a\xe6\xcb\x38\x1b\x09\x17\x38\x77\x06\x4b\xcc\xc7\x42\x19\x2e\x7a\x01\x67\x97\xac\x40\xb1\x4d\x4d\x51\xa4\xc6\x69\xca\x0b\x64\x5b\x62\xee\xb3\xdf\x3e\x27\x26\xdc\x41\x2b\x11\x23\xfe\x50\xef\x8f\x29\x2b\x68\x9e\x0e\x71\xc7\x1d\x83\xb6\x8e\x8a\xc6\x6c\x40\x63\x6a\x88\x98\xd5\x64\x8d\x2c\xa7\xca\x40\x4d\x19\x80\x26\xab\xe1\xf3\x5c\x43\x3b\x7c\xca\x9b\xbe\x0e\x9f\x15\x1d\x29\x9b\x7a\xea\x54\x12\xa6\x6e\x25\x11\x6a\xe9\x91\x4f\x5d\xda\x09\xa8\xab\x3f\x21\x75\x5b\x5d\x44\xdd\x96\x11\x53\xbb\x3c\x28\x75\xd9\x45\x42\xed\xf6\xcc\xa8\x5b\xf5\x29\x75\xeb\x30\xa3\x16\x9b\xca\xa9\x5b\x45\x9c\xba\x6c\xaa\xa0\x6e\x53\xc6\xde\x88\x1f\x61\x3c\x62\x7c\x98\x8c\x78\x2a\xf6\x1d\x06\x88\x03\xa7\x9f\xe2\x70\xc4\x15\x71\xe4\x8d\xc4\xa0\xd8\xe9\x73\x4d\x06\x6b\xe1\x3d\xb1\x46\x6d\x66\xf3\x56\x9c\x8e\x84\x36\x9c\x39\xe2\x22\xce\x47\x62\x08\xe6\x23\x3e\x8b\x0b\x67\x70\x13\x23\xba\x85\x71\xec\x34\x25\x4c\x9c\x4e\x8b\xfd\x11\xbf\xc4\xc1\x88\x63\xe2\xd0\xe1\x99\x38\x1a\x89\x35\x38\x1e\x0d\x56\x23\x9e\x84\x93\x11\x1f\xc5\xcc\x11\x00\x70\xea\x8c\x5a\x38\x73\x86\x16\x9c\xdb\xfc\x1f\xf3\x31\x17\x2e\xba\xa1\x67\xf7\xa1\xdb\x60\x23\x35\xab\x81\x87\x0d\x43\xb7\x4a\x35\x0c\x83\xb6\x42\x6a\x6a\x16\x34\x49\x8e\xe9\x69\x68\xe9\x7e\x24\x51\x1a\xc6\xe8\x36\x65\x1a\x3e\xa5\x5a\x07\x4c\xc3\x74\xd3\xf7\x61\x53\xa6\x19\xf9\xf0\x69\xaa\x75\xc2\xf4\xaa\xae\xe5\x71\x86\x61\x5a\xca\x6d\x88\x95\xb7\x72\x33\xbd\xa4\x6b\x99\xef\xb0\xa7\x2e\x31\x60\x6a\x16\x2a\xa1\x2e\xfd\xfa\xd4\xd5\xc7\x80\x3a\x0c\x27\xa4\x2e\xe1\x45\xd4\xd5\x93\x98\xda\xc4\x43\xa9\xc3\xac\x12\xea\x52\x35\xa3\x2e\x8d\xa4\xd4\x61\x08\x19\xb5\x99\x79\x4e\x5d\x96\xcc\xa9\xd9\x62\x0b\xea\x50\x32\xf6\x9c\x5a\xc6\xd8\xe9\xae\xc4\xe9\xaf\xd8\x77\xfa\x0a\x0e\x5c\xee\x80\x43\xa7\x2b\xe1\xc8\xe9\x10\x38\x76\x45\x04\x35\xde\x18\x1f\x25\xce\x68\x81\x99\xcb\x63\x70\x6a\x09\x1a\x38\xb3\x05\xd9\xdc\xe9\xb9\x98\x3b\x83\x02\x2e\xac\x11\x11\x7b\x4e\xad\x63\xa7\x23\x62\xe2\xf6\x6e\xdf\x62\x69\x38\x70\x3a\x1a\x0e\x5d\x2e\x8c\x23\xab\x1f\xe2\xd8\x19\x19\x30\x75\x7a\x3f\x4e\x9c\xbe\x88\x99\x25\x58\xe1\xd4\xe9\x6e\x38\x73\x45\x07\x9c\x5b\xbd\x18\x73\x67\xe4\xc0\x85\x16\x1c\x76\x19\x53\xa9\x18\xe0\x89\x01\x61\x23\x9c\x61\x3c\xbe\xdb\x2e\x6e\x0c\xc3\xb1\x6c\x37\x0c\xc4\x0a\x9f\xe1\x51\x28\xf1\x11\x23\x1f\x51\xf3\xd0\x14\x84\x15\x27\xe6\x71\x86\x7a\x66\xfe\x93\xa6\xdf\xa6\x10\x2c\xf9\x34\x3d\x4a\x1b\xa4\x06\x3e\xb3\xbb\xf2\xb0\xc7\x30\xfc\x9a\xed\x84\x37\x42\x34\xb4\x29\x14\x13\x86\x47\xf5\xa2\x92\xb5\xe7\xf2\x31\x76\xc9\x54\xc1\x10\x97\xfe\x15\x8c\xef\xd2\xb5\xfa\x3d\x70\x09\x5b\xc1\x84\x76\xb1\x2a\x88\x68\xb4\xcf\xb1\xc5\xb4\xd4\x63\xea\x92\xa8\x82\x49\x6c\x5a\x52\xcf\x99\xdd\x4a\x15\x44\xea\xb2\x47\x05\x93\x99\x55\xae\x9e\xe6\x2e\x33\x52\x30\xdc\x65\xa2\x0a\xa6\xb0\x7b\x68\x9d\x11\x1b\x1d\x1b\xbb\x7a\x80\x89\x45\xc8\xd8\xb7\x59\x1c\x0e\x5c\xcc\xe2\xd0\xa5\x16\x1c\xb9\x84\x81\x63\x47\x17\x6d\xf1\x37\xb1\xab\x10\x33\x97\xa5\xe2\xd4\x19\x0f\x33\x97\x47\xe1\xdc\x6e\xdf\x98\xdb\x8c\x0e\x17\xe3\xde\xd5\xbe\xdc\x58\x21\xb0\x3b\x16\x60\x32\x6e\x70\xd8\x1f\xf3\x3e\x1c\x38\xbd\x0f\x87\xe3\x41\xa0\x56\xb6\xb3\xbb\xf1\x78\x50\xc2\x74\x3c\xb8\xe1\x64\x3c\x1a\xd4\xe6\xe0\xf2\x32\x69\x14\xd6\xa7\xd9\x58\x58\x93\x86\xe1\xe0\x93\x8f\x45\x9c\xda\x48\x80\x8a\x36\xb2\xcb\x8f\x7a\x5d\x83\x27\x6c\xfd\x7e\x8d\xaa\x19\xab\xd0\x9a\xcf\x79\x56\x41\x3d\xa2\x17\xdf\x3d\x7d\x8d\xca\xc5\x79\x7d\x4d\x44\x53\xd1\xe0\xc9\xb7\x2f\x7a\x17\x17\xb7\x07\x13\xa7\xa8\xdd\xf8\x0f\x17\x28\xaa\x2f\xf0\x59\x7d\x99\xea\x0d\x3d\xf5\xab\x04\x90\x5f\xea\xcf\xe2\xcb\x54\xeb\x4f\x9f\x73\xad\xaa\xd2\xf7\x0f\x5e\xc8\xc2\x58\x48\x16\x7e\x71\xdf\x51\x25\xa0\x9b\x0b\xaa\xe4\x17\xad\x4a\xca\x75\xaf\xa8\x72\x97\xd6\x7b\xcf\xaf\x9a\x12\x60\xef\xf9\x95\xa1\xf4\xdd\x7b\x7e\x55\xd7\xd5\x7b\xcf\xaf\xcc\x65\xf5\x04\x0d\xa9\xa2\x30\x42\x69\x59\xad\x11\xcb\xb2\xe5\x2a\x2f\x17\xa7\xa8\x5a\xa2\x67\x27\xd8\x88\xf7\xbb\x12\x4a\x01\xbd\xe9\xd7\x40\x36\xdd\x1d\x12\x46\xf6\xbb\x43\x5a\x74\xcf\x96\x02\xe1\xb3\x13\xfc\xa6\x7c\x8b\x0e\x10\x36\xd4\x28\x55\x74\x65\x79\xfe\x49\xdd\xbb\x37\x6d\x7b\x55\x8e\x4f\xfc\x67\xe2\x63\x74\xa0\xa1\x86\x3a\x7c\x7b\xe8\xf6\x00\xb1\xa1\x60\xe9\xb7\xeb\x35\x3f\x4b\xe7\x1c\xe1\x08\xad\x2f\xd2\xf7\xfc\xca\x20\xfe\xf5\x45\xfa\x23\xbf\x5a\x37\x2a\x68\xbf\xdb\x85\xb2\x78\x01\x40\x52\x34\xf5\x97\xfb\x08\x47\xcd\x37\xfb\x15\x2b\x27\x50\x71\x4a\xf1\x63\x16\xe4\xba\xc6\xae\x78\x79\xa3\x90\xbe\x55\x4c\x19\xf1\xba\xaf\x6e\x49\xcb\xea\x05\x54\x45\x39\xd6\x8a\xa0\x34\x78\x6d\x28\xa5\x41\x05\xd4\x68\x50\x64\xd8\xc6\x64\x35\x24\xb0\x5b\x4d\x97\x4e\xb1\x5a\x9e\x41\x80\x99\xf3\xa2\x42\x84\x82\x67\x08\xca\xe6\x86\x52\x38\x6f\x26\x25\x3a\x92\x77\x43\x78\x50\xc0\xb1\x36\xae\xc9\xe4\xd9\x09\x51\x36\xb8\x87\xf6\x1b\x09\xec\xa1\xbf\x20\x42\xdf\x42\x8d\x47\xb0\xad\x12\xfd\x05\xee\xb8\xd8\x9a\xbd\x55\x79\x3a\xdb\x9e\xbf\x00\xca\x77\xb6\x4c\xee\x75\xb8\x24\x14\x1e\x4b\x5e\xd1\x3e\x22\x81\x85\xe1\x3d\x03\xc7\x03\xb2\xa6\xca\xfe\xa2\x03\xe5\x22\xe3\x88\xb3\x6c\xa6\xcc\x0e\x95\x6b\xc4\xce\xcf\xe7\x25\xcf\x85\x2e\xd9\x02\xf1\xcd\x39\x5b\xe4\x3c\xaf\xeb\x32\x42\x78\x9f\x1a\xb1\x09\x11\x28\x34\x19\x5b\xa0\x94\xa3\x74\xb5\x7c\xcf\x17\xa8\x5c\x54\x4b\x44\x65\x51\xe0\x35\x5a\x67\x6c\x2e\xd1\x4b\x94\x6b\x33\xb6\xcb\x59\x99\xcd\x10\x9b\xcf\x97\x97\x6b\x40\x2d\xf0\x56\x4b\x81\xf6\x62\xcd\x73\x74\x59\x56\xb3\xe5\x45\x25\x19\x5c\x97\xcb\xc5\x10\x8b\x12\x34\x94\xd7\x9c\xb4\x5f\xee\xdf\x57\xd7\xca\xb4\x3f\x89\x80\xe2\x63\x93\xe4\x3a\x96\x8b\xa5\xe5\xc6\x6e\xc3\x55\x68\x21\x88\xb5\x9f\x21\x66\x4d\x4a\xa9\xc4\xaf\x91\xd0\xbe\x6f\x56\x95\xad\x1f\xb1\xde\x8f\xf8\xad\x2a\xec\xf9\x9b\xfe\x13\x5c\x0a\x30\xb8\x6a\xc7\x10\x01\x4f\x64\xe1\x4b\x54\x2e\x3e\xf0\xd5\x9a\xdb\xa3\x60\xb9\xf8\xf0\xa2\x17\x08\x3b\x3f\x6d\x35\x40\x60\xc7\x00\xd1\x62\xd3\x25\xb6\x7e\x83\x43\x61\xd0\x7d\xec\x1f\x3b\x13\x0e\xed\x17\xbe\xc8\x56\x57\xe7\xd5\x0e\x57\x01\xaa\x8a\xb5\xcb\x93\xa6\x5d\x0b\x3c\xed\x86\x7c\x6b\x09\xdd\x9c\x7f\x0e\xaa\xad\x44\x5c\xb5\x7b\x4f\xdc\x94\xa7\xb5\x20\x4d\x49\xc7\x7f\xf0\x4a\xcf\xd3\xba\xcc\xcd\x01\xa9\x76\x35\x56\x5f\x07\x12\x6c\xd5\x07\x83\x9b\xb3\x0c\xd9\xc7\x0f\x8b\xb2\x2a\xd9\x5c\x2f\x7d\xd5\x85\xe1\x9b\x6c\xc6\x16\xa7\xfc\xf1\xf3\xb6\x2c\xaa\xac\x3c\xe6\x6d\xbc\x42\xfe\xaf\x6f\xd2\xe6\x36\xf2\x7e\x6a\x78\x63\x2d\x0a\x6b\x9b\xe7\x8f\xf5\x36\x04\xe8\xf8\xea\x6f\xbb\x36\x54\xf2\xe6\x15\x85\xf8\xff\x96\xbc\x41\x9b\x50\xfd\x19\x2b\xd3\xba\xae\x6a\x93\xe5\xc3\xc0\xa3\xe4\x47\xe9\x55\xf0\x79\xfc\xda\x36\xc3\x48\x64\xcc\x27\x00\x9d\xed\xda\x8b\xc6\x30\x74\x3b\xb1\xc0\xae\xba\xb0\x2b\x05\x6b\x64\xf2\x21\x2f\xd7\x15\x9f\x37\x56\x6c\xc6\x58\x40\xe7\xb7\x4b\x2d\xa8\x3b\x40\x17\x62\xa0\x95\xa5\xd6\xde\x94\x6f\xdf\x4c\x26\x8a\xdb\x77\x6d\xb8\x16\x89\x64\xf3\xea\x02\xdf\xa1\xac\xb6\x49\x34\x86\x80\xdd\x73\xa4\x95\x4d\x52\x3d\x4f\x9a\xd7\x6c\x14\xe3\x01\xfc\xe7\x45\xbe\x44\xeb\x4b\x76\x2e\xd3\x8f\x39\x5b\x57\xd2\x18\x86\x21\xbc\x72\xab\xac\xc7\x6c\x57\x61\x2e\xc7\xaf\x0c\x36\x0c\x15\xc5\x77\x75\xf5\x81\x6b\xdc\x98\x0b\x5e\xc7\xd5\xaf\x13\x52\x46\x42\x97\xe1\x8d\xac\x42\xcb\x8b\x6a\x10\x81\x9b\x90\xeb\x56\x59\x27\xe4\xda\x75\xd6\x19\x32\xde\xf3\x2b\x59\x02\x3a\x0a\x8e\x7c\xa2\x3f\x29\x3f\x58\x1e\x68\x75\xa3\x23\x63\xd5\xe8\x23\xf4\x42\x58\xa0\x7a\x09\x58\x2d\xd7\xeb\x36\x4d\x87\x9a\x87\x90\x10\xc3\x6b\xa9\x6c\xd1\x0c\x54\xad\xe0\x26\xf5\x78\x75\xc6\xd6\xef\x3b\x2e\x5b\xdb\xee\x64\xd2\x31\x51\xe1\x88\xf5\xe8\xfa\xae\xd3\x75\xe1\xb4\x02\x8b\x26\x82\x8e\xc9\xbe\x03\x9b\xfd\xca\x68\xf8\xe2\x99\xc8\xa8\x24\x66\x05\x55\xfb\xdd\x80\xed\xe7\x8f\xb7\x67\x7b\x65\x67\x7b\xee\x66\x7b\xee\x60\x7b\xb5\x05\xdb\xce\x22\xd2\xeb\xba\x8a\xb4\x9c\xfe\xd8\xae\x8e\xf4\x58\x11\x66\x89\xab\xe2\x9b\x4a\x2f\xc5\xfc\xfd\x83\x17\x87\x2a\x41\xeb\xd4\x62\x9e\xa2\xac\x38\x35\x14\xd7\x3e\x9f\x33\xc1\xc4\xa6\x42\x7d\x2c\x2a\xe1\x9a\xb4\x74\x4c\x88\x9a\xca\xce\xc3\x89\x9a\x6e\xd1\xed\xef\x1f\xbc\x30\x56\xdc\x7e\xb9\x2a\xcf\xe7\xfc\x60\xb7\x29\x22\xd9\xa8\x33\x51\xa4\xff\xf4\xc7\x99\x2e\x52\x13\x11\x82\xed\x12\x2a\x94\x66\xfd\xeb\x81\x54\x16\xcb\xd7\x18\x1d\x0b\xb8\x43\x29\xd5\x07\x52\xc7\xcb\xd5\xa4\xbd\x67\x5d\x5d\x1c\x5f\x93\x3e\x5c\xcf\xcb\x8c\x4f\xbc\x29\x22\x7b\x83\xbb\x30\x1a\xb4\xe4\x9a\x68\xc9\x14\x05\x0e\xb4\xfe\x35\xd1\x06\x53\x14\xed\xd9\x2f\xd2\xb8\xf6\xbb\x07\x5f\xe3\x43\xbd\xb1\xd6\xc2\x2a\x99\x43\xfd\x9d\x63\x8b\x06\xfe\x16\x14\x6e\xe6\x9d\x46\xd0\xda\x91\x39\xb2\x6b\xf7\xf1\x16\x14\xcc\xa3\x1e\x4e\xc8\x8d\x0d\x7b\xff\x24\x61\xb5\x89\x2e\x37\x10\x5c\x5b\x5c\x3b\x86\x58\x5b\x88\xeb\x06\xda\x06\xca\x59\x3f\xbf\x81\xea\x95\xd0\xd7\x0a\xb3\xdf\x0d\xc9\xb4\x57\x55\x5f\x2b\xee\x7e\x37\x0c\xa6\x6d\x55\xf7\xbb\x61\x34\x55\xc5\xde\xef\x46\xf8\xe3\xdb\x29\x0d\x3e\xa9\xe0\xfe\xef\x59\x69\xff\xb3\xd5\xc3\xff\xef\xa9\x6c\x0f\x37\x15\x94\x0b\x9e\xdf\x6c\x89\xfb\xef\xd8\x9a\xb7\x55\xeb\xd9\x9a\x6b\xcf\x5e\xfb\xc4\x59\x01\x7f\xe8\xcb\x9b\x28\x40\x0b\x76\xc6\xd7\xe7\xba\x97\x1e\xe9\x6c\x08\x10\xc1\x86\xfc\xef\xdf\x3f\x9a\xd0\x7c\x8b\xa2\xa0\xb9\xc2\xc6\x84\xe6\x75\x14\x08\x3e\x80\xa9\x4d\x14\x1c\xaa\x2f\x82\x7f\x43\x66\xd0\xa2\x96\xe8\xd5\x74\x4a\xf9\x37\xbe\x46\x0c\x2d\xf8\xe5\xfc\x0a\x49\x5f\xcb\x4d\x84\xf5\x80\x82\x3a\xb7\x79\x2c\x2e\xce\x52\xbe\xfa\x88\xe0\x56\x29\xb8\x55\x45\x7c\xf0\x09\xa4\xf3\x87\xce\x26\xf3\xe5\x25\xb4\x10\xff\x35\x35\xe8\x36\xee\x46\xb7\x21\x40\x2d\x97\x4d\x2b\x97\x3a\x22\xd4\xe2\xa9\x07\x66\xb9\xfa\xe7\x11\xcf\x87\xb7\xb2\xc0\x0b\xbd\xc8\xeb\xce\x77\xd6\x92\x86\x10\xbf\x28\x3b\x19\x95\xe8\xe1\x54\x70\x6d\x1e\xc3\xd4\xfd\x5a\x86\x5b\x3d\xe1\xb1\xe8\xed\x31\xea\xde\xbe\xad\xbf\x99\xf7\x35\xf5\x5d\x59\x5d\x96\x6b\x8e\x7e\x7a\xfa\x72\x0d\x18\xc6\x14\x53\x5f\x94\xa2\x0c\xe4\x23\xfa\x56\xe8\x57\xc8\xe5\x00\x04\xa3\x46\x12\x56\x54\x7c\x85\x16\xfc\x94\x55\xe5\xe2\xf4\x06\x04\x0f\xa8\xb8\x10\xbc\x52\xc1\xe1\x62\x59\x4d\xac\x52\x3d\x3a\x42\x8b\xe5\x68\xa6\x0a\x77\xb2\x48\x81\xfe\xa3\x91\xee\x3d\x23\x98\x14\xec\x3f\x6a\x21\x1b\x52\x52\x25\x19\x25\x98\xda\x1a\x5a\x75\xde\xeb\x70\xd7\xc9\x00\x6c\x5a\xf9\xf6\xa7\xef\x35\xad\xc0\x72\x02\x8c\xdb\xe7\x6c\x0d\xcb\x0b\x5b\xf9\x50\xa3\x29\xc0\x21\x5c\xa2\x51\x56\xb5\x14\x24\x6a\xbc\x37\xac\xfc\x6f\x7f\xfa\xfe\x66\x54\x2f\xd7\x76\x5a\xc5\xb3\x45\x3e\x61\x8b\x65\x35\xe3\x2b\xc5\x88\xcb\x0c\xd8\x22\xd7\xcd\x40\xf4\x70\xc4\x14\x5a\x3f\xbb\x2d\x05\x32\x66\x15\x8d\xe7\x29\xf8\xdf\xcd\x3e\x9e\x3e\xff\xdc\xe6\xf1\xf4\xf9\x67\xb2\x8e\xa7\xcf\x6f\xc6\x38\x96\xab\x8e\x6d\x2c\x57\x3b\x98\xc6\x72\x75\x6d\xcb\xf8\x6d\x47\xcb\xf8\xed\x77\xb6\x8c\xd7\x9f\xdf\x34\x5e\x7f\x36\xdb\x78\x7d\x53\xc6\xb1\xe9\x59\xc7\x66\x27\xf3\xd8\x7c\x82\x7d\xbc\xdb\xd1\x3e\xde\xfd\x4e\xf6\x01\x8b\xf2\xba\x65\x2c\xe4\xcc\xa8\x7a\x21\x9c\xf3\xa2\xda\x3e\x2b\x5b\x80\x4d\xc8\x6f\x68\x59\x34\x98\xe0\x0a\x9b\x9b\x32\x06\x40\x76\x33\xe6\x00\xa8\x3a\x06\x01\xbf\x3c\x9e\x90\xd0\x65\x07\x12\x48\x37\x85\x85\xc9\x0e\xc4\x2b\xd0\x02\xdd\x47\x3e\xb1\xad\x74\x69\x96\x32\x69\x4d\xe5\xfe\x7d\xb4\x80\x25\xf2\xc6\x18\xe4\xd6\x21\x82\x0e\xd0\xc2\x78\x59\xbd\xd9\x84\x04\x9e\xa1\xad\x7d\x44\xf5\xcb\x93\x9b\x21\x1d\xcd\x64\x81\x0e\x0c\x37\x86\x0e\x48\xf7\x97\xba\x04\xb9\xff\x4e\xeb\x85\xa9\xfc\x7f\x3b\xf3\x7d\x3e\xb1\xbf\x5c\xd4\xd6\xfb\xfc\x86\xac\x57\xea\xbd\x6b\xa9\x9a\xf1\xd6\xf6\xbc\x85\xf1\x0e\x22\x26\xa0\xba\x86\xfd\x6a\x5e\xd0\xe0\x19\x37\x60\x45\xfe\x77\xb7\xe0\xe7\xcb\x8a\x55\xfc\x73\x07\xe0\x15\x50\xb9\x29\x13\x06\x6c\x37\x63\xc2\x92\x31\xdd\x84\x57\xcb\xd1\xf8\x2b\x40\x46\xed\x57\xf5\x08\xec\x40\x45\xf5\xc5\x9e\x48\x07\xdb\x5f\x9e\x4f\xa2\x60\x60\x96\x9f\xaa\xb0\x1b\x8a\x39\x7f\x2c\x8d\x8d\x84\x1c\x01\xb1\xbb\xc2\x9e\x0f\x14\xf6\xf8\x3a\x0a\xfb\x36\xcf\x3f\x77\xe6\xcb\xf2\xfc\x33\x65\xbe\xf2\xca\xef\x9b\x78\x67\xce\x7b\xef\xcc\xf9\x4e\xef\xcc\xf9\xd6\xef\xcc\xfd\x11\x61\xbf\x49\x64\x61\xc3\xa8\x39\xf9\xcd\xd8\x6a\x75\x25\x9a\xd5\x63\x88\xbc\x18\xbe\x33\xac\xb4\xd7\xc3\x9b\x71\x0c\x13\xa9\xfd\x36\xe7\x46\xfb\x92\x86\xe2\xe1\x53\x23\xba\xfc\x66\x5e\x5d\xf9\x76\xa1\xae\x00\x5f\x16\xfa\xdc\xe6\xda\x74\xc3\xf1\x6a\x79\xce\x57\xd5\x15\xfa\xbb\xba\x62\x18\x00\xc1\xbc\x1a\x14\x83\x69\x45\x65\x20\xeb\x43\x13\x9e\x3a\xac\x34\x77\xa2\x77\xa3\xcb\xba\x3c\x5d\x94\x45\x99\xb1\x45\x85\x52\x78\x5e\x2e\x34\xdf\x00\xa2\x8e\xd9\xdf\x76\x5e\xba\x66\xa6\xfe\xe5\x06\xe6\x81\x87\x1c\xd8\xdd\xb1\x23\xae\xc9\xd3\x73\x61\x96\x6c\xbe\xd7\x91\xfd\xa8\xe0\x90\x31\x20\x37\x92\xd3\xd0\x6e\x25\x44\xde\x55\xf3\x27\xf8\xea\xa5\x2e\xea\x7e\x2f\x3a\x6b\xbe\x5d\x9f\xfd\x44\x64\x6f\x06\xed\xc5\xdf\xae\xd3\xda\xd3\x5d\xb1\x60\x8a\x13\xcc\x70\x0a\x67\x6a\x32\x9c\x63\x8e\x8b\xbd\x01\x92\xb7\xff\x46\x5d\x9d\x22\xec\x6d\xbd\x3c\x00\x46\x37\x6d\xcc\x76\x10\x96\x2f\xd5\xe6\x09\x08\x8b\xf5\x17\xf9\xdf\xdf\x7e\x33\x1c\xc0\x10\x79\x7f\xe3\x03\x7f\x3a\x46\xc3\x55\x30\xfd\x4f\x8e\xcd\x35\xf8\x71\xc3\x46\x7f\x2f\xa0\x35\x69\xef\x23\x90\x3e\x34\xe7\x8b\xd3\x6a\x86\xbe\x46\x74\xcb\xad\xd4\xfd\x40\x73\xb2\x5c\x7c\xe0\xab\xfa\xd5\x50\x0b\xc3\x2a\x3e\x88\x41\xbb\x3e\x1d\xb0\x55\xe0\xa9\x47\xed\x46\xbb\x9d\x95\xb9\x8f\xe8\x65\x37\x88\xde\x59\xa3\x9c\x55\x0c\xb1\xf5\x8e\x74\xb6\x9e\xc9\xea\xae\x14\x6e\xb4\x00\x7d\x58\x2d\x5f\xfb\xc4\xbe\x14\x02\x8f\x3f\x61\xcf\x8e\xa2\xd5\x35\x2a\xc3\xce\x9d\x1a\xee\xb1\x54\x66\xc3\x64\xad\x5e\xd3\x2e\x1e\xa9\x36\x03\x2e\xd9\xdd\xad\x37\xef\x77\x69\xbb\x4f\x7a\xb5\x4b\x78\x75\xab\x37\x83\x2d\xfc\xe2\xaf\xe6\xe1\xf0\xfc\x62\x3d\x9b\xd4\x89\x94\xc8\x11\x4c\xef\x95\x66\xe8\x5e\x2e\x81\x0c\xfb\x64\xeb\x54\x44\x53\x70\x1d\x41\x6a\x9c\xd3\xae\xdb\x58\x37\x92\x0c\xbc\x02\xd0\x08\x93\xcc\x96\xe7\x30\x48\x5a\xc6\x7e\x34\x9a\xb6\x36\x66\xcf\x51\x36\x5f\x2e\x5c\x6f\x2a\xdb\x9a\x34\xe0\xe9\xdb\x32\xfc\x68\xb7\x65\x78\xec\xb4\x65\x1d\x33\x64\x29\x92\xdd\x66\xe7\xab\x69\xa7\xeb\x09\xc0\xff\x19\x0c\xfb\xcf\x52\x32\x43\xa4\x75\x2c\x95\xf8\x86\x61\xb6\xde\x35\x66\x27\x00\x67\x98\xea\x85\x75\x99\x9c\x58\xc8\x34\x2e\x74\xd9\xf1\x9f\x51\x37\xb8\xdc\xc6\x07\x2e\x95\xc9\xd7\xe8\xdf\x94\x6f\x4d\x62\xb7\x9b\x2a\x00\x77\xd6\x97\x9b\xf4\xd8\xba\x6f\xa6\xb7\x5b\x46\x6d\x8d\xf9\xf8\x76\x4a\xc3\x6d\xf6\xbb\x1c\x7d\xfd\x27\x34\xab\xaa\xf3\xf5\xdd\xa3\xa3\xb3\x6a\xb6\x3e\x4c\xf9\xd1\x45\x55\xd0\x5f\xd7\xe8\x03\x39\xc4\x87\x04\xa5\x57\xe8\x7f\x9c\xb1\x6a\x56\xb2\xb5\xb0\x98\x76\x83\x0c\xec\x0a\x91\x9b\x3d\x8e\x8e\xd0\xf7\xbc\x92\xc7\xe1\x38\x17\xe2\x2e\x59\x3a\xe7\x6b\xf4\x57\x45\xe9\xaf\xb7\xbe\x82\x6d\xfc\x2b\xce\x1f\x34\xfb\x5f\x06\x3b\x69\xd0\x1d\xa9\xbc\x3b\xe8\xf6\xed\xfa\xe7\x7b\x76\xf4\xe8\xaf\xb2\x3b\x1a\xf2\x27\xf0\x43\x8b\xfb\x4c\x7d\xef\xa2\x56\xbf\xde\xbe\x6d\xd8\x9f\x73\xdc\x61\xb2\x01\x76\xb2\x71\x0a\x3b\x67\xfe\x3a\x95\xbb\xf1\x7f\x5a\xe6\xfc\xf0\xd7\x35\x5a\xae\xd0\x77\x72\x2b\x4d\x59\x94\x3c\x47\xd9\x32\xe7\x53\xc0\xc2\x16\x39\xba\x58\x73\x54\x56\x62\x5c\xfb\xab\x90\xa3\xd6\x07\xb5\x0f\xa7\xe9\xc3\xa9\xfa\xde\xed\x83\xfc\xf5\x9e\xdc\x93\xd4\x36\x3b\x6c\xa0\x8f\x75\x64\xbf\xfd\xa6\x7d\x3b\xbc\x2c\x17\xb9\x78\xbb\xec\xc0\xc8\xad\x43\x82\x17\xa4\xff\x0c\x9b\x7d\x6e\x7d\x75\xf4\xf5\xc1\x8d\xfd\x7d\x7d\x74\x4b\xf6\x76\x5d\xad\xca\xc5\xe9\xc3\xd5\xf2\xec\x64\xc6\x56\x27\xcb\x5c\x68\xee\x05\xfc\x78\x58\x68\xbf\x2a\xe1\xbf\x64\xef\xf9\x42\xca\xb8\x6f\xb2\xe7\x17\x8b\x2b\x21\xdf\x5b\x5f\x35\x11\xec\x22\x5b\x93\x9c\x8b\x1f\x27\x92\x8e\xec\x20\x2c\x6d\xc2\xe6\xfb\x7a\x08\x84\x9f\xb2\xe5\xc5\xa2\xe2\x2b\x35\x73\x09\x3f\xcd\xeb\x58\x21\x9b\xb7\xc1\x02\x9e\xc2\x79\xc6\xfa\x0b\xdf\x54\x2b\x26\xbe\x5c\xce\xca\x39\x47\x93\x1a\xdb\x7d\x85\x44\x92\xfe\x0a\xda\xb4\x08\x33\xd5\xbd\x6f\xab\xba\xc1\xfe\xbe\x70\xf5\xaf\x40\xa7\x12\xf8\x9b\x63\xe4\x6d\xbe\xa7\x9e\x27\x74\x2e\x7f\xba\x0f\x3f\x7d\xf7\xf0\xa1\xf8\xc9\x42\x49\x88\x0b\x5e\xd7\xd7\x17\xab\xd5\xf2\x94\x55\x7c\x0a\x56\x57\xcd\xf8\x8a\xc3\x39\x4f\xb4\xe0\x9b\x0a\x09\x16\x58\x56\xf1\x15\x34\x82\x6e\x6c\xc3\x1f\x30\x38\x91\xe0\xb7\x91\xb7\x79\x78\xe2\x79\x7b\xc2\x42\xbd\xcd\xf7\xf0\xf1\xef\x22\x38\xcf\x97\x97\x2d\x7d\x68\xf6\x95\x94\xbc\x1c\xca\x27\xaa\x8b\x02\x81\xff\xf0\xe1\x1e\x1c\xcd\xf4\xf6\xd0\x3e\xd2\x30\xc3\x83\xfd\xba\xe2\x90\xa2\xde\x66\xc1\xaa\xab\x17\x8b\x33\x56\x65\x33\x9e\xb7\xf4\xee\xa1\xe5\x62\x7e\x85\xd8\xf9\x39\x87\x7e\x97\x6b\x70\x40\x74\xb1\x28\xab\xa9\x78\xd1\xcc\xd8\x9a\xc3\xdb\xa6\x10\x44\x83\xa9\x81\x11\x42\xaa\xea\x7d\x51\x0d\x56\x31\xd4\x33\xed\xeb\x39\x2b\x57\xc3\x9e\x41\xbf\x14\xaf\x5f\x29\xd1\x1d\x1c\x28\xde\x6f\xf5\x3b\x60\x69\x29\x00\xc5\xff\x55\xbc\x97\x50\xb5\x37\x5e\xc7\x19\xf8\x02\x9c\x01\x46\xe1\xd6\x17\x1a\x2b\x97\x79\x4b\xd7\xc8\xcb\x45\xce\x37\xe8\x18\x1d\x60\xa3\xd9\x37\x7e\x74\xe7\x8e\x66\xfc\xfb\xfb\xb2\x99\xc5\xf8\x81\xce\x1b\x00\x79\xdb\x37\x76\x61\x4a\x0f\x85\xc6\xa5\x64\xe4\xaf\x07\xc7\xb5\xfa\xef\x69\xf2\x42\xfb\xc7\x86\xf8\x51\x23\xfa\xe6\x1b\x84\xbd\xda\x80\xd0\x6f\xca\x87\x94\x4a\x6a\x4e\xa4\xb1\xa2\xdf\x50\xc7\x0e\x1b\xe1\x6f\x41\x08\x10\xda\x94\xd4\x08\x3f\x9b\xf1\xec\xfd\x8b\x8c\xcd\xd9\xea\x7f\x89\x56\x13\xa1\x87\x67\xcb\x72\x21\x77\x53\x83\x00\x9a\x9f\xba\x1e\xdf\xfe\x2c\xbd\xbe\x15\x4e\x35\x5b\x2d\x2f\xd1\x83\xd5\x6a\xb9\x9a\x40\xaf\xee\x3c\x16\xa9\x50\x6b\x9a\x3f\xef\xdf\x41\xfb\x2d\x82\xc3\x6a\x29\x23\xeb\x04\x47\x7b\x87\xd5\xf2\xe7\xf3\x73\xbe\x3a\x61\x6b\x3e\xd9\x43\xfb\x12\x81\x30\xf9\xc5\xb2\x12\x06\x0e\xcc\x4a\xb9\xdc\x11\x0f\xeb\x8e\x7e\xfc\x0c\x23\x41\x2b\x27\xc8\xaa\x45\x26\xde\x8a\x63\x2a\x97\xd9\xd4\xe0\x24\xa5\x6c\xd0\xc6\x44\x17\xe0\x37\x75\x1b\xa9\x51\x98\xaa\xdc\x50\x6f\xaf\xaf\x17\xe9\x10\x27\x75\x43\x93\x5a\x34\xb4\xb7\x95\x71\x3e\x7c\x48\x55\xac\x53\x61\x0e\x1f\xa4\x57\x15\x47\x6b\xfe\x5f\x17\x7c\x91\x41\xa0\xb3\x33\xda\xd2\xa8\x4d\x07\x06\xc2\xab\xb3\x74\x39\x6f\x1c\xc9\x46\x99\x7a\x5d\xca\x64\x48\xb9\xc1\x34\x2e\xa4\x48\x0a\x08\x2b\x01\x9d\x78\x0d\x4b\xcd\xc6\x63\x03\x13\x10\x86\x75\x26\xfc\x21\x13\x0e\x83\xbf\xb7\x23\x93\x98\x48\x2e\x3d\xc5\xe5\x03\xaf\x83\x62\xff\xd8\x62\x35\xd1\x16\x9d\x79\xe0\x0d\x3a\x13\x7c\x92\x44\x31\x55\xcc\xc6\x92\xd9\x87\x5b\x32\x8b\xc9\xae\x9d\x6a\x21\x4d\x5c\x75\x3b\xda\xf5\x80\xc6\x36\x01\x43\xdf\x25\x44\xea\xaf\xc6\x89\x7e\xd2\xd4\x20\x15\xa9\xfb\x30\xb9\x1a\x64\x4d\x2d\xfc\xe8\xa0\xd2\x80\xd6\x3f\x08\x25\xc8\x68\xb5\xe5\xe0\xd2\xf6\x58\x27\xac\x8f\x32\x1a\xca\xfd\x63\x87\xeb\xf7\x22\x7a\xdb\xec\x73\x25\xc2\x8d\xec\x57\x9c\xe5\x27\xcb\x45\x55\x2e\x2e\xe0\xf0\x2c\x68\xbf\x0d\x45\x82\x93\x1f\xa0\xef\xdf\x1c\x03\x5b\x27\x22\xb1\x30\x8c\x06\x77\x7e\x58\x7c\x60\xf3\x32\x07\x20\x29\xed\x3b\xaa\x5b\x8d\xbc\xbb\x54\x90\x44\x08\x13\x05\x6f\x1a\x3a\x6f\x95\x9b\x88\xa6\xcd\x8f\xfb\xfb\x22\x19\xaf\x23\x54\x0f\xcd\x6d\x19\x46\x64\x22\x28\xa2\xe4\xdf\xb5\x60\x68\x84\xf6\x1f\x36\x8c\x1d\x1d\xa1\x1f\x0a\x74\xc9\x91\xc8\xd7\x2e\xce\x91\xc8\x54\xa7\xa8\xac\xfe\xef\xff\xfe\x3f\xf5\xb0\xa4\xa3\x00\x8e\x6f\x59\x7a\x3e\x00\xbc\x33\x08\xfe\xd2\x7a\x5f\x80\x17\x4c\x5a\x2b\x17\xc0\x58\x37\x43\xa2\x7f\xf1\xf5\x2f\x81\xc1\x7c\x87\xba\xfa\x04\x55\x75\x31\x1d\x0f\xb5\xae\x24\x5b\xb0\x39\x1c\x7e\x68\xe4\xf8\x9c\xb3\x1c\x15\xe5\x6a\x5d\xd5\x52\x82\x6e\xed\xae\xe6\xe1\xe8\x86\x26\x8b\xe5\x50\xbc\xeb\xbd\xda\x26\x24\xa1\xdb\x4a\xff\x2a\xb2\x6a\xbc\x36\xf2\xad\x79\x1d\x8e\x61\x3d\x3c\x0f\x6a\x83\x3a\xa9\x51\x81\x5a\xd0\xb1\xc5\x61\xee\xf5\xe3\x81\x8e\x0c\xcb\xd7\x0c\xa8\xb9\xd3\x68\xd7\x94\x80\x35\xd6\xdb\x9a\xaf\x16\xa3\xba\x09\xfc\x0e\x26\x58\xa7\xf5\xb2\xef\x7e\x5f\xb6\x67\xec\x0a\x95\x8b\x6c\x7e\x01\x2f\x21\xe2\xe5\x42\x7f\xa5\x31\x49\xf9\x61\x2d\x9d\x07\x3b\x48\x07\x4c\xf9\x7a\x02\xf4\xd4\x7b\x1a\x81\xbd\x49\x92\x96\x2e\x50\xdf\x26\x50\x0f\x92\x17\x29\xb0\xb1\xfc\xe0\x73\xca\x7c\x38\xc2\xf7\x25\x4a\x95\x44\x1f\xde\xac\x44\x21\x64\x5c\x53\xe8\x31\x08\xdd\xdb\xf4\xc5\xee\x6d\xbc\x93\x3d\xf4\x1b\x48\x64\x22\x79\x90\xbf\x36\xfa\x08\xac\xfa\x80\x37\x2a\xc3\x3b\x06\xf6\xf4\x57\x30\xb3\x26\x6a\x79\x1a\xb5\xf0\xf3\xcb\x87\x07\x14\xe5\x30\x53\xc6\xf3\x26\xf2\xd6\x61\x53\x9d\xc0\x6a\xbe\x43\x40\xd3\xbe\x43\xfc\xb9\xd7\xcb\x49\x54\xae\xd1\x8e\xc6\x92\xbf\x06\x5f\x37\x25\xd1\xc0\xea\xa8\x06\x54\xf4\x00\xa8\x25\x25\x5a\x8c\x6d\x67\x7f\x3a\xe9\x4e\x3b\x4f\x54\x9d\x9d\x6b\xd9\xc8\xa4\x3a\x3b\x47\xc7\xbd\xb1\x64\x0f\xfd\xe9\xf8\x58\x06\xe5\x7e\x76\xa2\x16\x31\xaa\xb3\xf3\x7e\x9e\xa1\xbd\xa0\xb7\xd0\x7b\x9f\x73\xf2\x4d\x88\x15\x1d\x03\x83\x77\x3e\xf0\xd5\xba\x5c\x2e\xee\xdc\x45\x77\x60\xd2\xf7\xce\x54\xfc\x2a\xf9\xb9\x73\x57\xcb\x0a\xe1\x77\xd9\x5d\xf5\xbb\xfc\x72\xeb\xab\x8f\x6a\x92\xee\xc5\xf2\x8c\xa3\x6f\x9f\x7c\x8f\xd2\x8b\x72\x9e\xa3\xe5\x79\x55\x9e\x95\x7f\xe3\xab\xf5\x14\xcd\xcb\xf7\x1c\xad\x0e\x7f\x5d\x4f\xe5\x2b\x31\xcc\xb4\xaf\xcf\x79\x56\x16\x65\x26\x9c\x37\x2f\x41\xe1\xe7\xac\xaa\xf8\x6a\xb1\x06\x7c\xd0\xa8\x9a\x71\x54\x2c\xe7\xf3\xe5\x65\xb9\x38\xbd\x2b\xe7\x3c\x85\xf9\xf5\xce\x45\xa2\x3b\xb5\xd1\xdc\x91\x93\xbb\x1d\x80\x43\x76\x96\xf7\x66\x51\x9b\x23\x92\xe2\xd9\xad\xaf\xa4\xba\xd4\xa1\xc9\x66\x9a\xbb\x3b\x80\x89\x3e\x83\xee\x40\x39\xed\xdb\x45\x6f\xd6\xf8\x4f\xda\xf7\xc3\xc5\x32\xe7\x2f\xaf\xce\x79\x9b\xcc\xb5\x73\xd5\xea\xc5\xa3\x5c\xe8\xf3\xc6\xcf\xcb\xc5\xe9\xf2\x7f\xbe\x40\x1f\xbc\x43\x7a\xe8\xc1\xeb\x79\xdb\x42\x3b\x4b\xda\x30\xa3\x42\x63\x8d\x89\xad\x2e\x67\x6c\xde\xc3\x14\x1f\x7a\x07\x72\x22\x66\x55\xef\x8d\x92\xa7\x18\xd5\x6f\x33\xb6\x7e\x7a\xb9\x78\x56\x6f\x81\x39\x56\x40\x87\xdd\xdf\x01\xbc\x59\x22\x81\xaa\x71\x52\x28\x75\xc4\xe8\x82\xcb\xf5\x21\xf1\x1c\x0e\x12\xef\x09\xd9\xe8\xb2\x7a\xf3\x5e\x16\x30\x14\x10\xf0\xb9\x33\xf9\xd5\xeb\xd7\xf3\x59\xb9\x58\x8a\x5e\x31\x74\xc9\x53\xa4\x0e\xaa\xaa\x59\xeb\x43\x65\xd0\x4a\x26\x1f\x6f\xa9\x23\xaa\xb0\x6c\xf2\x71\xfa\xf7\x8f\x6f\xa7\x34\xda\x66\x49\x64\x70\x62\xf7\xf5\x93\xc7\x8f\xaa\xea\xfc\xb9\x18\x32\xd6\x55\x83\xed\xcf\x69\x79\x2a\x37\xb3\x1c\xfe\xba\xfe\xf3\x36\x98\xef\x5c\xac\x39\xbc\xb0\x65\xd5\x9d\x7b\xb7\x86\x84\xbe\x2b\x4f\x7f\x02\x84\xf7\x44\x87\x7f\x5d\xcf\x44\x50\x2e\x4f\x17\xcb\x15\xbf\x3b\x2f\x17\xfc\x56\x43\xfa\x92\xa7\xfe\x56\x24\x85\x92\x5e\xf1\x54\x8e\x4d\xf2\x98\xf1\x9d\xc3\xa3\x79\x99\x1e\x09\x14\x22\x38\xdf\x3a\x3a\x42\xf9\x72\x71\xa7\x42\xcb\x0f\x7c\xb5\x2a\x73\x5e\xaf\x38\xd4\x0b\x1c\xb7\xb4\x33\xc8\x6a\xe9\x40\x44\xb8\x3b\xcd\x8e\x06\x58\x90\xe8\x00\x1c\x4a\x9a\x5d\x28\x61\x21\xb0\x4e\xa6\x83\x00\x77\xf7\x6e\x7d\x34\x88\x43\x3e\x51\x2b\x5b\x35\xcb\x7f\xbe\x4b\xc8\xc7\xb7\x42\x0c\xd3\x37\x52\x0c\x6f\xf7\x6e\x1d\x1d\xfd\x7f\x68\xbd\xbc\x58\x65\xfc\x09\x3b\x3f\x2f\x17\xa7\x3f\x3f\x7f\x7c\x2c\x1e\x1e\xcc\x61\x17\xe9\xaf\xeb\xc3\x33\x76\x7e\xeb\xff\x05\x00\x00\xff\xff\x73\x19\x9e\x3a\x8a\x21\x06\x00") +var _web3Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x6b\x7b\x13\x39\xd2\x38\x0e\xbf\xcf\xa7\x50\xfc\xdc\x0f\xb6\x89\xb1\x73\x60\x18\xc6\x99\x0c\x1b\x02\x33\x64\x6f\x20\x5c\x40\x76\x76\xef\x6c\x96\xab\xed\x96\xed\x1e\xda\xdd\xfe\x75\xb7\x73\x18\x92\xef\xfe\xbf\x54\x3a\x95\x0e\x7d\x70\x12\xe6\xb4\xc9\x0b\x70\x4b\xa5\x53\xa9\x54\x2a\x95\x4a\x55\x19\xfd\x7f\xcb\x28\xa3\x7b\x9d\xc9\x32\x19\x17\x51\x9a\x10\xda\x29\x7a\x49\x2f\xeb\x7e\x51\x29\x79\x27\xed\x2d\xbb\x5f\xa2\x49\x67\x3d\x39\x49\x4f\xf9\xaf\x02\x7e\x9d\x05\x19\x09\xf6\x8a\xcb\x05\x4d\x27\x44\xd6\xb5\xd7\x92\x45\x5b\x0f\x1e\x88\xc4\x5d\x56\x66\xf9\xe0\x41\xd0\xcd\x68\xb1\xcc\x12\x12\x74\xd2\xde\xfa\x66\x97\xa5\x47\x32\x2d\x12\x69\xac\xd6\xc9\x5e\x42\xcf\xc9\xcb\x2c\x4b\xb3\x4e\xeb\x20\x48\x92\xb4\x20\x93\x28\x09\xc9\x3c\x0d\x97\x31\x25\xed\xd6\x46\xba\xd1\x6a\xb7\xba\xbb\xc5\x2c\x4b\xcf\xc9\xa4\x3f\x4e\x43\xba\xd7\x7a\x73\xf4\xe2\xf8\xf5\xcb\x4f\x6f\x8f\x3e\x7e\xfa\xf1\xe8\xf8\xed\x8b\x56\x6f\x72\xcd\xea\x8b\xf7\x58\xdf\xf7\xbe\xd0\x8b\x45\x9a\x15\xf9\xf0\xcb\xf5\xf5\x2e\x1b\xc3\xc9\xe6\x69\x7f\x1c\xc4\x71\x27\xee\x8b\xac\x9e\xec\x7d\x87\xf2\x01\x26\x7b\x00\xb8\x75\x7a\x42\x4f\x77\x45\x57\xf3\x4e\xf2\x2c\x19\xd2\xee\x75\x2f\xee\xe9\x92\xb4\xc7\x71\x77\x2d\xa0\x58\x93\x32\x13\x7a\x11\x35\xc2\xd5\x24\xcd\x3a\x0c\x3a\xdd\xdb\xdc\x4d\xbf\xcf\xfa\x31\x4d\xa6\xc5\x6c\x37\xdd\xd8\xe8\xe6\x9d\x8c\x21\x5e\x75\xe3\xba\xdb\xf9\xb2\x35\x3c\x51\x5d\x16\x55\xf4\x38\x96\x7a\xa2\xed\xee\x97\x35\x9e\x20\x3b\xb3\x77\xb2\x46\xc8\x97\x35\x42\x08\x69\x8d\xd3\x24\x2f\x82\xa4\x68\x0d\x49\x91\x2d\x69\x8f\xa7\x46\xc9\x62\x59\xe4\xad\x21\x39\x81\x6f\x09\x0d\x79\x49\x30\xa7\xad\x21\x69\x7d\x4a\xcf\x13\x9a\xb5\x7a\x3a\x87\x8d\x8e\xe5\x04\x61\x98\xd1\x3c\x6f\x89\x9c\x6b\xf8\xff\x54\x54\x2d\x8b\xc3\xff\x22\x2d\x5d\x16\xf5\xed\xa5\x9f\x50\x11\xa3\xbd\xd1\x65\x41\xf3\x9d\x6d\x7f\x7b\x12\x48\x61\x7a\x8d\x90\xeb\xde\x9d\x20\xe0\x46\xfd\x51\xc3\x41\xd8\x6b\x86\x80\x95\x51\xfd\x47\x1d\xfa\x38\x4d\x0a\x9a\x14\xb7\x1e\xfc\x9f\x72\xde\xd9\x8c\xfd\x61\xa6\x7d\x12\xc4\xf9\x6f\x37\xf4\x8c\xe6\x34\x3b\xf3\xad\xfa\x3f\xfa\xa4\xe5\xcb\xd1\x7b\x3a\x8d\xf2\x22\x0b\xfe\x0b\x26\xaf\x57\x55\x07\x3d\x3f\xba\x15\xdf\x2f\xb2\x20\xc9\x27\x5e\xd6\xf7\x67\xc1\x41\x66\x91\xc2\xea\x48\xc8\x69\xf1\xa1\x9a\xa4\xee\x0c\x17\x76\xd3\xbf\x49\xa3\x5f\x79\x02\x82\x26\x88\xaf\xaa\x60\x91\x45\xf3\x20\xbb\xf4\xf6\x23\x4d\xe3\xda\xc9\xdb\x17\x6d\xfd\x79\x51\x68\xee\xc1\x95\xd5\x94\x21\xe1\xa0\x74\x1b\xff\x23\x21\xc1\xdb\xfb\x30\xca\xd3\xf3\xe4\x16\x3d\x0f\x92\x34\xb9\x9c\xa7\xcb\x7c\x85\xae\x47\x49\x48\x2f\x68\x68\xec\x5d\x77\x36\xb1\xba\x72\xd4\x1d\xb3\xf6\xf3\x28\xb9\x0d\xe3\xde\x5f\x02\x26\x5e\x26\x21\x0d\x5b\x16\x9a\xe8\x19\x23\x84\xbf\x00\x8e\x46\x51\x18\x36\xc3\xd1\xcd\xea\x3f\x0b\xe2\xa5\xb7\xfb\xcb\x28\x29\xb6\xbf\x79\x52\x3d\x05\x6f\xe9\xf9\xf3\xe8\x77\x44\xfe\xad\xd6\xdc\xc1\x2c\x48\xa6\xbf\x27\xe9\xdc\x09\xe5\x94\xd4\x8d\xa4\xfa\x4a\xaa\xf1\x62\xe6\x1d\xdf\x8d\x6a\x11\xb4\x76\xba\xb6\x76\xdd\xfb\x72\x7d\xda\xdb\xfe\xdd\x0e\xfd\x7f\xa1\x33\xef\xef\x24\x3b\x4e\x96\x49\x78\x63\x52\xb9\xf5\xc6\x75\x7f\xec\xfd\x73\x1f\x7b\xef\x0f\x7d\x7f\xe4\x33\x87\x77\xf0\xe2\xbc\xf0\x47\x93\x36\xbf\xee\x66\xae\xf7\xaa\x9d\x3b\xdb\xab\x56\x9d\xf7\x49\x96\xce\x6f\x39\xed\x45\x7a\xcb\xa3\xe6\xed\x04\xbe\xdf\x77\xdd\xfc\x11\xf0\x17\x25\x61\x94\xd1\x71\x71\xe8\xdd\x33\x57\xe8\xc9\xed\x26\x22\x1a\x07\x8b\x8f\xbf\xeb\x64\xf8\x31\xd9\xec\xb4\x4b\x17\x69\x1e\x55\x1d\xd4\x17\xc1\x65\x30\x8a\xa9\x29\x14\xfc\x2e\x5c\xa9\x8c\xe6\xee\xe4\xf8\x75\x3b\x1a\xd8\x97\xe3\x7d\x61\xe2\xf3\xb7\x3f\xc9\xdc\x09\x92\x4a\xea\x6e\x46\x67\xbf\x03\xfa\xff\xb0\x58\xbf\x8b\xf3\xe3\x8d\xf9\xe4\xd7\xc6\xba\xcd\xf4\xee\xd1\xde\x10\xed\xb7\xde\xb8\xbe\xf6\xcc\x1e\x7a\xb6\xb4\x2a\x39\xee\x71\x13\x39\x0e\x8c\x37\xc8\x9e\xb4\x70\xe8\xb4\xfb\x83\x49\x9a\xcd\x83\xa2\xa0\x59\xde\xee\xee\x02\xc0\x87\x34\x8e\xc2\xa8\xb8\xfc\x78\xb9\xa0\x26\x2c\x6b\x9f\x41\xad\x0d\x1e\x3e\x5c\x23\x0f\x0d\x48\xa1\x73\x27\x51\x4e\x02\xb2\xc8\xd2\x94\x01\x93\x62\x16\x14\x24\xa3\x0b\x76\xc8\x4a\x8a\x9c\x88\xb9\x23\x2c\x93\xd5\x70\x58\x90\x79\x50\x8c\x67\x34\x1f\xb2\x4f\x91\x8d\x7e\x9e\x9c\xe2\x8f\xc7\xc6\xd7\xa9\x99\xb9\x63\x7d\x9f\x9e\x3c\x39\x3d\x39\xed\x91\x7e\xbf\xbf\x46\x1e\x0e\x9c\xb1\xc9\x1e\xef\x11\x65\x4d\xd3\xe9\x8a\x29\x2e\x66\x51\xde\xff\x04\x0b\xe3\x47\x89\x20\x06\xd8\xe7\xe8\x3a\x64\x19\x87\x49\xb1\x8b\x80\xf9\xbe\xed\x83\x3e\x82\x1c\xd1\xdc\xee\xda\xf5\xee\xda\x9a\xa7\x1f\xfd\x45\x96\x16\x1c\x6b\x7b\x24\xa1\xe7\x46\x5f\x3b\x5f\xae\xbb\xbb\xd5\xa5\xfa\x20\xbd\x64\xcb\x71\x91\xb2\xc6\x3d\xb0\x75\xed\xf6\xa3\x5c\xcc\xb9\x46\x08\x23\x47\x89\x14\x61\xd7\xb2\xbe\xce\x12\xfb\x30\x6f\x9d\x81\xc0\x76\xe7\xdf\x27\x9d\x93\xcd\x47\xdf\x9d\x3e\xec\xfe\xfb\xb4\xfb\x6c\xd0\xe5\xe3\x34\x0f\x0e\xa5\xdd\xba\xee\x7d\x69\x61\x52\x6c\x0d\xbf\xeb\xb5\x38\xbd\xb5\x86\x5b\x8f\xaf\x4f\x7b\xdf\xfc\xce\xe4\xfd\x3c\x4d\xe3\x1a\xda\x1e\x31\x90\x12\xc2\x66\x79\xf2\x7f\x4e\xa5\xf0\xeb\xb1\xfe\x79\x8a\x92\x77\xf0\x47\x1d\x19\x43\xcf\x6e\x4a\xc3\xac\xf0\x2a\x44\xcc\xe1\x6d\x0a\x66\xa9\x2b\x92\xaf\x59\xa4\x82\x76\x79\x8b\x55\x65\x6f\x42\xb5\xff\x61\xa8\x35\x69\xf6\xe1\xff\x34\x22\x5a\xd1\x9f\x7a\x8a\x7d\xf2\x7b\x53\x2c\xdb\xc3\x14\xc9\x16\x7e\x9a\x2d\x66\x94\xc0\x66\x07\x84\xdb\xf7\x51\x2e\xcb\x55\x3f\x04\x5d\xc2\xcf\xc7\xe8\xf7\x29\xce\xd8\x31\xbe\x4c\xfa\x25\x62\x6b\x55\x3f\x9f\x1a\xf5\x88\xa2\x1e\x2a\x87\x4e\xde\x98\xcc\x59\xe9\x95\xe8\x9c\x17\x70\x08\x9d\x25\xaf\x4a\xe9\x66\x99\x2a\x52\xe7\x8d\x56\x96\xbe\x19\xb1\xb3\x4a\x38\xa9\x7f\xd9\xea\x5d\x77\x6f\x46\xf8\xa2\x77\xf5\x94\xff\x6d\x13\xca\x1f\x3c\x84\x0e\x7f\x9c\x45\x39\x99\x44\x31\x65\x94\xba\x08\xb2\x82\xa4\x13\x72\x4e\x47\x3b\xfd\x5f\xf2\xfe\x1a\x80\x88\x2f\x06\x30\xc9\x28\x25\x79\x3a\x29\xce\x83\x8c\x0e\xc9\x65\xba\x24\xe3\x20\x21\x19\x0d\xa3\xbc\xc8\xa2\xd1\xb2\xa0\x24\x2a\x48\x90\x84\x83\x34\x23\xf3\x34\x8c\x26\x97\x50\x47\x54\x90\x65\x12\xd2\x0c\x08\xbe\xa0\xd9\x3c\x67\xed\xb0\x8f\x9f\xde\x1e\x93\xd7\x34\xcf\x69\x46\x7e\xa2\x09\xcd\x82\x98\xbc\x5b\x8e\xe2\x68\x4c\x5e\x47\x63\x9a\xe4\x94\x04\x39\x59\xb0\x94\x7c\x46\x43\x32\xba\x14\x54\x44\xc9\x8f\xac\x33\x1f\x44\x67\xc8\x8f\xe9\x32\x09\x03\x36\xe6\x1e\xa1\x51\x31\xa3\x19\x39\xa3\x59\xce\x66\x68\x47\xb6\x25\x6a\xec\x91\x34\x83\x5a\x3a\x41\xc1\xc6\x90\x91\x74\xc1\x0a\x76\x49\x90\x5c\x92\x38\x28\x74\x59\x17\x05\x7a\xa4\x21\x89\x12\xa8\x76\x96\xca\x95\x1d\x15\xe4\x3c\x8a\x63\x32\xa2\x64\x99\xd3\xc9\x32\xe6\x82\xe3\x68\x59\x90\x9f\x0f\x3f\xbe\x3a\x3a\xfe\x48\xf6\xdf\xfe\x8b\xfc\xbc\xff\xfe\xfd\xfe\xdb\x8f\xff\xda\x25\xe7\x51\x31\x4b\x97\x05\x61\x12\x25\xd4\x15\xcd\x17\x71\x44\x43\x72\x1e\x64\x59\x90\x14\x97\x24\x9d\x40\x15\x6f\x5e\xbe\x3f\x78\xb5\xff\xf6\xe3\xfe\xf3\xc3\xd7\x87\x1f\xff\x45\xd2\x8c\xfc\x78\xf8\xf1\xed\xcb\x0f\x1f\xc8\x8f\x47\xef\xc9\x3e\x79\xb7\xff\xfe\xe3\xe1\xc1\xf1\xeb\xfd\xf7\xe4\xdd\xf1\xfb\x77\x47\x1f\x5e\xf6\x09\xf9\x40\x59\xc7\x28\xd4\x50\x8f\xe8\x09\xcc\x59\x46\x49\x48\x8b\x20\x8a\xe5\xfc\xff\x2b\x5d\x92\x7c\x96\x2e\xe3\x90\xcc\x82\x33\x4a\x32\x3a\xa6\xd1\x19\x0d\x49\x40\xc6\xe9\xe2\xb2\xf1\x44\x42\x65\x41\x9c\x26\x53\x18\xb6\xa2\x32\x42\x0e\x27\x24\x49\x8b\x1e\xc9\x29\x25\xdf\xcf\x8a\x62\x31\x1c\x0c\xce\xcf\xcf\xfb\xd3\x64\xd9\x4f\xb3\xe9\x20\xe6\x15\xe4\x83\x1f\xfa\x6b\x0f\x07\x92\xd9\xfe\x0d\xc8\x76\x9c\x86\x34\xeb\xff\x02\x2c\xf2\x6f\xc1\xb2\x98\xa5\x19\x79\x13\x64\xf4\x33\xf9\xdf\xb4\xa0\xe7\xd1\xf8\x57\xf2\xfd\x9c\x7d\xff\x8d\x16\xb3\x90\x9e\xf5\xc7\xe9\xfc\x07\x00\x0e\x83\x82\x92\xed\xcd\xad\x6f\x80\xe1\xd5\x6f\x05\x15\x02\x2c\x2a\x23\xe4\x31\xdf\xde\x21\x24\x05\x04\xcc\x76\x41\x1f\xe4\x61\x52\x98\x80\x51\x52\xf8\xe0\x8e\x1d\xc0\x65\x09\xe4\x8b\xcb\x24\x98\x47\x63\xc9\xc6\x51\x89\x90\xe7\x00\x8f\xf2\x95\xfc\x50\x64\x51\x32\x35\xcb\xe4\x90\xe6\x83\x7e\x4f\x03\x6b\x8c\x19\x0d\xbc\x63\x3c\x76\x41\x97\x65\xb0\x9e\x6e\xab\xfe\x02\x70\x94\x8b\x01\x1a\x9c\x39\x47\x55\xf4\x60\x87\x15\x7c\x5a\x5a\x88\xa3\xfc\xbe\xaa\x02\xb6\x11\x0e\x7c\x75\xa5\x4e\x8f\xa4\x04\x7a\x3f\xcb\x82\x4b\x0e\xce\x99\xb8\x25\x0a\x1c\x30\xfa\x44\x12\x80\x58\x49\x9c\x43\x84\xa4\x48\x09\x4d\x18\x0d\x0f\x42\xca\xfe\x53\xad\x30\x66\x1c\x70\x36\xc9\xb8\x92\x90\x6b\xcd\x8d\x99\xd7\x8d\x47\xcc\xc0\x72\x73\x67\x86\x24\xb2\x07\x35\xe4\x46\x17\x81\xf7\xcf\x69\x31\x4b\x43\x4f\xb7\xb8\x72\x3d\xcd\xe6\x84\x4b\x2e\xa9\x31\x23\x6b\x84\xaf\x41\x51\xfc\x93\x98\x19\x91\x45\xfe\x06\xbd\x27\x5f\x38\xf1\x5c\x2b\xb1\xfc\x6f\x1c\xf3\x39\xf9\x82\x2b\xbb\x86\x2c\x78\xab\x90\x93\x2f\xf0\xae\xe1\x9a\x88\xcf\x88\xf1\x06\x2e\x11\x31\x32\x84\xbe\xb0\x9d\x88\xb1\x7b\x40\x88\x81\x0c\xb4\x53\xe3\x2e\x39\x38\x92\x28\x62\xd8\xcc\x4d\xf1\x0e\x61\xad\x3f\x89\xe2\x82\x66\x1d\x54\xb6\x8b\x74\x10\x82\x8a\x0a\x21\x14\x48\x22\x00\x9d\x42\xf7\x64\xf3\x74\x97\xf3\xcf\x68\x42\x3a\xeb\xb8\x11\x5c\x07\x7f\xa0\xc1\x9f\x72\xb4\xa3\xe4\x2c\x88\xa3\x50\xd3\x00\xab\x71\x7d\x48\xda\x64\x83\xe0\xca\xd7\xb0\xac\x81\x6b\x36\x29\xb0\x84\xd2\xc8\x22\x0e\xa2\x84\xd3\x97\x35\x8d\x1c\xe0\x9d\xc8\x29\x9f\x45\x91\x7e\x34\xfa\x85\x8e\x8b\x6b\xab\x42\x39\xc9\xba\x1c\xaf\x36\xb4\xe0\xca\xa7\x0e\x75\xc3\x99\xb9\x1e\x2f\x6f\x09\x5c\x30\x69\xa8\x58\xde\x39\x61\xc0\xa7\x3d\x72\x02\xe0\xa7\xdd\x66\xa8\x89\xa3\x1c\x24\x20\xbe\xf8\xca\xb1\x93\x63\x34\x00\x0b\xe0\xd8\xf1\xa5\x2f\x74\x81\x32\xc4\x38\xcd\x36\xc2\x4d\xee\x2e\x7d\x81\x9d\xbc\x8c\xbe\x73\x49\xe0\x53\x5a\xe0\x15\x98\x0b\xce\x21\x48\x96\x15\x13\x7d\x63\x25\x8c\x1a\xfa\xf3\x60\xd1\x29\xe3\xb1\xa0\x95\xf3\xac\x11\x83\x77\xf2\x9a\x3b\xbc\xa7\x27\x50\xe4\x94\xb3\x67\xf9\xa5\x56\x11\xea\x8f\xd8\xa7\x8e\x26\x93\x9c\x16\x4e\xa7\x32\x1a\x2e\xc7\x14\xf5\x2b\x18\x8f\x7b\xa4\xa6\x73\x80\x9d\x22\x28\xa2\xf1\xbb\x20\x2b\x5e\xc3\x4b\x22\xab\xe6\xbe\x9d\xdf\xf1\xf4\x53\xd6\x95\x31\xa6\x44\xc3\x0f\x6e\x95\x6f\x82\x62\xd6\x9f\xc4\x69\x9a\x75\x3a\x4e\x8b\x1b\x64\x67\xab\x4b\x06\x64\x67\xbb\x4b\x1e\x92\x9d\x6d\x31\x68\x84\xbe\x60\x3c\x26\x1b\xa4\xa3\x36\x1d\x03\xeb\x25\x28\x24\xcf\xd0\xde\x45\xc8\xce\x36\x19\x1a\x09\x25\x9d\x95\xa8\xef\x91\x4d\x8c\xfd\x8c\xe6\xcb\xb8\x90\xd4\xc3\x67\xf0\xcd\x32\x2e\xa2\x9f\xa3\x62\xc6\xe7\x44\x52\xa0\xd1\xb7\x9e\xa2\xa3\x9e\x39\x83\xb2\x72\x31\x42\x5e\xbf\x79\xe2\xf3\x93\xbe\xd5\xaa\x6f\x0d\x34\xec\x01\x5a\x23\x6a\x78\xad\xd6\xae\x5e\x38\x34\x9e\x88\x11\x8b\xce\x8a\x5d\x21\xcd\x5e\x06\xe3\x59\xc7\x66\x4c\x11\xa6\x2d\xc6\xf5\x4b\xe7\x4b\xcf\xd5\x69\x17\x17\xe2\x08\x81\xae\x6c\xb8\xda\xce\x8e\xd9\x7d\xb9\x8e\x10\x11\xaa\xb5\xcb\xa8\x98\xc6\x13\x01\x62\xcf\x11\x74\xc0\xed\x92\xc4\x13\x7c\xd8\x93\x85\x9b\x30\x97\xe2\xc6\x1e\xa1\xe2\x19\x1e\x19\x90\x6d\x0d\x7a\x4d\x68\x9c\x53\x6b\x78\x83\x01\x09\xd3\xa4\x5d\x90\x20\x0c\x89\x28\x55\xa4\x66\x95\x7d\x12\x15\xed\x9c\x04\x71\x46\x83\xf0\x92\x8c\xd3\x65\x52\xd0\xb0\x04\x4b\x5f\x69\x9c\xd7\x7a\x11\x0e\x06\xe4\xe3\xd1\x8b\xa3\x21\x99\x44\xd3\x65\x46\x09\x3b\xb0\x25\x34\x67\x27\x40\x76\x4a\xbb\xcc\x4d\x66\xf5\x5b\x10\xc9\x1f\x67\x92\xcd\xc9\xa0\x18\x81\x12\x2b\x25\xcb\x5c\xa1\x35\xa3\x93\x00\xd4\x31\xe7\xb3\x34\xa6\xbc\x87\x51\x32\x5d\xaf\x61\x04\x15\x3c\xc0\xe6\xfc\x62\xd0\x3d\x92\x3a\x2b\xdf\x58\xe4\x72\x4e\x6a\x45\x7d\xcf\x16\xd7\x71\x55\x63\x88\x80\x78\xc3\xe4\x3c\xd0\x64\x9d\xd3\xc2\x99\x53\x4e\x56\x6f\x83\x39\xb5\xf7\x21\x9d\x83\xe5\x4c\xb7\xac\x67\xf3\xa9\xde\xcf\x74\xc5\x9e\x3a\x15\x5f\x14\x18\xd4\x52\xad\xfc\xab\x18\xb6\xac\x64\x91\xd1\xb3\x28\x5d\xe6\xaa\x43\xdb\xbb\x0c\x25\x51\x42\xa2\xa4\x70\x4a\xd4\xe1\x1f\xf5\xd7\xd7\x20\xfb\x9b\xa4\x19\x81\x47\xc2\x11\xd9\x23\x5b\xbb\x24\x22\xdf\xcb\x01\xc8\xf7\xc2\x24\xda\xd8\x28\x2b\xce\xfe\xac\x3e\x6f\xec\x91\x8d\x8e\xc4\x41\x44\x1e\x91\xad\x53\x26\xe1\x93\xab\x2b\xb2\xb9\x5b\x5a\x49\x05\x2b\x17\xf4\xb0\x41\x22\xf2\xb0\x6c\xe6\x36\xec\x5e\x30\xe1\xa0\x8c\xed\xcb\xbf\x6b\x27\xd5\x4c\xb9\xee\x76\xba\xd6\x14\x0e\x06\x64\x12\x65\x79\x41\x68\x4c\xe7\x34\x29\xd8\xf9\x8a\xa3\xa9\x47\xf2\xcf\xd1\x82\x44\xc5\x2a\x53\x6e\x60\x7f\xd3\x87\x7d\x86\xbf\xca\x19\x80\xa7\xf3\x61\x18\xb1\x46\x82\x58\x2d\x72\x81\x4f\x87\xff\xb8\xf8\xf6\xf3\x45\x4d\x3a\x25\x0c\xe2\x24\x22\x1b\x64\xeb\x54\xf2\x09\xb2\x41\x9c\x6e\x78\xd0\x5e\x8b\x60\x8b\xf9\x79\x20\xc5\x56\xe9\xa1\x7d\x4e\x15\x37\x66\x3d\x7f\x68\xa6\xc2\x84\x2d\x13\x53\xb7\x5c\xfc\x35\x94\x49\xca\x18\xd2\x66\x15\x43\x22\x8d\x68\xba\x96\xa3\x0c\x06\x64\x1c\xc4\xe3\x65\x1c\x14\x54\x0a\x3e\xec\xc8\x27\xfa\x42\xa2\x82\xce\x6f\xc1\x8e\x18\x2b\x3a\xf9\x13\x31\xa5\xae\x0d\x7b\xbd\xd2\xbe\x72\xcb\x09\xf9\xfd\x18\x0c\x66\x2e\x5f\x9d\xb7\x10\x47\x5b\x24\xfa\x51\xa3\x0d\x11\xba\x48\x71\x33\x99\x56\x68\x8c\x38\x64\x63\x8d\x91\x4c\x57\xb7\x9a\x4a\x25\xe2\xd7\x25\x95\xeb\x41\x50\xc3\x1e\xf1\x0f\xea\xf7\xe9\x88\x50\x31\xad\x23\xe2\xd0\x20\xdb\x34\x41\x4b\xa5\x92\xa8\x04\x21\x65\x3a\xa2\x72\x84\x88\x12\x70\xc2\x80\xd6\x34\x62\xaa\x35\x44\x78\x88\xbe\xd3\xb1\x81\x9b\xd5\x15\x44\xb2\x14\xa7\x62\x0c\xcf\x89\x38\xf7\x9e\xc2\xad\xe3\xfe\x1d\x6b\x94\xf8\x90\x3b\x30\x32\xb9\xbe\xb4\x5a\xc4\xd0\x8b\xc8\x1a\xb5\x86\xa9\x4a\xe5\xa0\x47\x55\xab\x67\xc0\x18\xe5\x1c\x88\x95\xb9\xeb\x91\x36\x51\x47\xa9\x93\xa8\x4f\x0e\x16\x5d\x2b\x65\x92\x83\x01\xc9\x97\x73\x7e\x43\xe7\xd9\xa5\x84\x88\xa8\xe0\x45\x75\x27\xd1\x29\xe3\x8a\xea\x0b\xb6\x24\x1f\xff\x91\xcd\x9b\x88\x90\xd2\xa6\x83\x82\xc1\x80\x64\x74\x9e\x9e\xc1\x35\x26\x19\x2f\xb3\x8c\xc9\xa7\x4a\x38\x4d\x21\x59\x74\x33\xca\xa1\xe7\x9e\xde\xe6\xab\x68\xfc\x24\x32\x1b\x6b\xfe\x8c\x91\x91\x47\x4e\xfd\x8d\x29\xed\x83\xb5\x0e\x4b\xae\x75\xbc\xa7\x56\xc9\xe3\x3c\x54\x56\x58\x57\x0e\x92\xac\xd8\x0e\x86\x2f\x49\xcc\xfb\x0b\xde\x5b\xd6\xd6\x58\xdc\x32\x61\x53\x0b\xe8\x7d\x87\xdb\xab\xda\x26\x18\xe2\x5a\xb4\xd3\xed\x79\xb3\x9f\xa7\x69\x5c\x96\xc7\x84\x90\x92\xac\xe3\x8a\x3c\x7c\xb9\x59\xda\x6c\x55\x26\xe7\xc2\x65\xb9\xef\x69\x50\xda\xe3\x63\x9e\xb9\xc6\x08\xc2\xb5\xdf\x00\xd4\x29\x9b\x0d\x69\x38\x3b\x7c\xdc\x6b\xf1\xbb\xdf\xd6\xf0\x1b\xf8\xc9\xfa\xd6\x1a\x3e\x61\xbf\xf1\x75\x6c\x6b\xf8\xb4\xe7\xb3\xf5\x88\x92\xa2\x35\xdc\xda\x64\x3f\x33\x1a\xc4\xad\xe1\xd6\x36\xfb\xcd\x6f\x65\x5b\xc3\xad\x1d\xf6\xb5\xe4\x50\xd0\xc0\x52\x80\x3d\xb9\x3e\xed\x3d\xfd\x2d\xed\xa2\x6a\xae\xa1\x6f\x66\x4d\x84\x2b\x59\xc5\xa8\xc8\x2c\x67\xdb\x16\xe1\xdc\x15\x4d\x8c\xfc\x45\x2b\x2c\x8d\xcc\x9e\x34\xa9\xeb\x16\x76\x47\x25\xc6\x46\x8d\x1a\x45\x57\xe2\xde\xe9\x92\x6c\x27\x5b\xd2\x06\x26\x4c\xd6\xb0\xeb\x2d\x99\xbe\xbb\xb7\x64\xba\xb7\x64\xfa\x6f\xb1\x64\xd2\x0b\xe1\xae\xcc\x99\x9e\x47\xd3\xb7\xcb\xf9\x08\x58\xa1\xe2\xce\xa3\x68\x9a\x40\x62\xff\x17\xc5\xc9\x97\x45\x14\x9b\xf6\x35\xfd\x01\xa4\xf1\x7f\x25\xd8\xd8\x0b\x32\x4e\x93\x49\xe4\x18\x03\xc9\x93\x19\xda\x15\xe0\xec\x02\xdb\x82\x1c\x38\xe7\xd5\x39\x01\x7e\x4f\xe0\xc1\x06\x3b\x67\x31\xbe\xa5\xad\x64\x61\x29\xb0\xb9\x01\xe5\xcc\x43\x86\x63\x0e\x19\xe5\x24\xa1\xd3\xa0\x88\xce\x68\x4f\x72\x22\xb8\x38\x2a\xce\xd3\x76\x4e\xc6\xe9\x7c\x21\xa5\x55\x28\xc5\xe6\x56\x95\x9c\xc4\x69\x50\x44\xc9\x94\x2c\xd2\x28\x29\x7a\xfc\x3a\x94\x91\x7d\x98\x9e\x27\xd6\x99\xce\x54\x93\xb8\xc7\xb7\x2b\x8e\xe5\x2b\x85\xef\x6b\x39\x16\xb6\x94\x12\x4a\x43\x38\x45\x8f\xf4\x1c\x87\x7e\x63\x18\x40\xda\xb5\xb2\xf3\x31\xdb\x35\x18\x30\xd4\x2f\xb9\xb0\x6a\xb7\xcf\xe7\xa2\x33\xee\xbf\xfc\xf8\xea\xd3\xf3\xc3\x9f\xde\x1e\xbf\x79\xfe\xf2\xfd\xa7\xf7\x47\xc7\x6f\x5f\x1c\xbe\xfd\xe9\xd3\x9b\xa3\x17\x2f\xd1\x19\x4e\x69\xe2\x60\x26\xfb\x8b\x20\x7c\x4d\x27\x45\x87\x7f\x15\xe9\xc7\xf3\x34\x3f\x50\x58\x14\x6d\xf6\x8b\x54\x88\x4b\x5b\x4f\xba\x3d\xf2\xe4\xb1\x79\xc3\x83\x77\x4b\x18\x4e\x87\x37\x62\x1a\x60\x98\x13\x2f\x0f\xbf\x25\x38\x7f\xae\xce\xc6\xe6\xa1\x79\x55\x1c\xba\x52\x87\x81\x45\x0f\x42\x8a\xf4\x15\xbd\x90\xe3\xce\x97\xa3\xbc\xc8\x3a\xdb\x08\x7f\xb1\x75\xb5\xcf\x8b\x4b\x2d\xf7\x06\x79\xb2\xd3\x25\x03\x8c\x22\x1b\xdd\xef\xa3\xe9\xac\x10\xc5\x7a\x24\x26\x0f\xbf\x32\x3e\xc5\x0e\x7c\xa7\x68\x2d\x95\xe9\x6e\x8d\x5d\x79\x3c\x33\xd1\xaa\xb4\x73\xbf\xdb\x0c\x58\x6a\x53\xde\x58\xb7\xcf\xd7\xfc\x06\xa9\x9f\xa0\x3a\x4e\xc7\x25\xf9\xf2\x15\xf1\x41\xe6\xdf\x76\xee\x94\x71\x67\xf3\x59\x9b\x64\xe9\xfc\xb8\x98\x3c\xbd\x9f\x38\xcf\xc4\x89\x77\x46\x65\x8c\x4c\xbc\x42\x92\x93\xc6\xbe\x69\x90\xac\xce\xc8\xec\x27\x47\xe5\x73\xd6\xde\xbc\xdd\x5f\x9b\x6c\x88\xea\xc9\x33\x42\xda\x5b\x6d\x32\x24\xed\xcd\xf6\xed\x79\x54\x1d\x26\xd9\x89\x95\x95\xfa\x07\x83\xcb\x09\x13\x8c\xe7\xcb\xb8\x88\xb8\x50\x39\xba\x24\xdb\xff\x99\x33\xf1\x5c\xd9\xd0\x05\xac\xe6\x82\x4e\x69\x56\xb1\x95\xbc\x17\xb5\xd6\xed\xdf\xab\xce\x88\xb0\x65\x2e\x99\x11\x81\x26\x8b\xfa\x18\xd6\x54\x8b\x6a\x73\x8d\xe6\x34\xb7\xb2\xb6\xbb\xfd\x45\x7a\xde\xd9\xda\x7e\xda\xed\x9a\x28\x3d\x98\xd1\xf1\x67\x12\x4d\x0c\x9c\x22\xb1\xc8\x42\x44\x1e\x4d\x13\x1a\x1e\xe6\x6f\x75\xb6\xa3\x88\x56\x75\xcc\xe8\x85\xe8\xb1\x89\x0c\x49\xb4\x70\xe8\x83\xb6\x0b\x53\x12\x4b\xd9\x91\xe5\x3c\x62\x62\x78\x10\xe7\xda\x6a\xd9\x6e\xbd\x16\x5f\x3e\x0c\x49\x76\xb3\xd9\x23\x5b\xdd\x1e\xd9\x7a\x82\xe4\x91\xed\xae\x91\xdb\x25\x7b\x7b\x7b\x8c\x64\xbd\x54\x98\x31\xf6\xf1\x28\x88\xa1\x53\x84\xab\x0e\xf4\x85\x07\x17\x35\x5d\x22\xe2\x8a\x04\x5b\x08\x34\xc8\xc3\xb1\x83\x65\x38\xd3\x82\x61\x45\xbb\x4a\x38\x84\x65\x11\x4d\x09\x97\xd3\x2d\x7a\x53\x5d\x30\xf0\x67\x18\xc5\x32\x60\x3e\x8f\x7b\xbc\x37\x48\x97\xd9\xe9\x92\xab\x2b\xd2\xda\x6c\x09\x1d\xf1\x60\x40\xc6\x8a\x8a\x98\xf0\x2c\x27\x52\xb5\xce\x81\x60\x96\x95\x98\xed\x4a\xd8\xf2\xf2\xd6\x9a\x64\x31\xb1\x1e\xfd\xa3\x67\x72\xf9\x7c\xce\xa3\x64\x69\x2f\x81\xf6\xe4\x96\x7f\x6d\xa8\x5b\x56\xbe\xa5\xee\xc6\x1a\x74\xe8\x06\xe4\xb3\xac\xa6\x9f\xe3\x4a\x02\xf2\x91\x0e\x5d\x89\x76\x44\xf3\x2e\xd5\x1c\xdf\x05\xd9\x7c\x1d\x94\x09\x7e\x5f\x86\x32\x87\x71\xd7\xa2\x0c\x30\x86\xe4\x61\x13\x45\xa2\x39\x17\x45\x0e\x27\xf7\x99\x9b\x5b\x2b\x51\xc0\xf4\xc3\xe8\x2c\x0a\x69\xf8\xfc\xb2\x82\x81\xdf\x84\x9a\x6a\x70\x73\x7c\xd7\xc8\x59\x96\x62\xe7\x78\x65\xf4\x1c\xdf\x06\x3f\xee\x15\x2c\xaf\x5a\xa1\xa8\x4c\xdc\xd2\xaf\xa5\x1b\xe3\x45\x6e\x6b\xe6\x5c\x94\xe2\x48\x34\xed\xa2\xc8\x11\xce\x7c\x18\xf2\x2c\x2f\xd8\xac\x6e\x29\xad\x6d\xb5\xc9\x33\xbe\x2f\x0b\xb7\x18\xab\x61\xb3\xf4\xd8\x88\x1e\xe5\x56\x6c\x7c\x31\x9d\x68\xc4\x31\xf1\xa1\xe2\x60\xe3\xc8\x1d\x49\x30\xa7\xfc\x75\x0f\xfb\x65\xc9\x5f\x02\x86\xd5\xa9\x6a\xf0\x60\xde\x39\x81\x42\x1b\x3d\x82\x35\xe5\xac\x90\x78\x5f\x4d\xf6\x48\xd9\x33\xdd\x87\xdd\x01\x3a\xcf\xe4\xd1\xaf\x82\x27\xe6\x70\x45\x25\xca\x9f\x6c\x9d\x9a\x72\x70\x7b\xf3\x82\xc9\xcb\xee\xe4\xf6\xf3\x38\x1a\x53\x26\x96\x6c\x93\x87\x50\xdd\x8a\x74\x5e\x33\x33\xf8\x08\x7e\x67\x13\xb4\x2a\xfa\x4b\xf5\x00\xce\x26\xa3\xce\x87\x16\x1f\xe0\x88\x13\x37\x60\x36\xe6\x9e\x3c\xee\x8a\x3d\xbc\x48\x05\x7c\x97\x3c\x94\x47\x4a\xdf\x0c\x58\x15\x71\xd1\xf0\xc9\xe3\x9e\x68\x7f\xb5\x29\xa8\x38\x92\xf3\xe1\x7b\xce\xe4\x77\x8a\xfd\x20\x1f\x47\x51\x15\xfe\x3d\x67\xf9\xdf\x10\xf3\x52\xa5\x03\xaa\x81\x66\xf8\x5f\x6d\x02\xb4\x6f\x9a\xb2\x19\xd8\xd7\xde\x6b\x4a\xa6\xa0\x94\xb7\x97\xa0\x5c\x55\xe8\x62\xdb\xe7\xbd\x66\x05\x69\xca\xc0\x5d\x6b\xf3\xa2\x45\x36\x88\x38\xe0\x00\xda\xf9\x6f\x65\x53\xf0\x78\xb3\x47\x70\x52\x99\xc3\x80\x2f\xd2\xee\x03\x1d\x34\x87\xd6\x77\xcf\x86\x81\x15\x3b\x74\x52\x1c\x38\xbc\xc0\x87\x65\x19\x4e\x29\x8e\xcc\xa1\x9b\xe4\xf6\x23\x4d\xe3\xa1\x9d\xe0\x40\x31\x09\x64\x68\x27\x60\x28\x25\x96\x0d\xed\x04\x17\xea\xd8\x01\x3b\xf6\xc2\xe1\x46\x75\x8a\xa7\x3e\x17\xf0\xd8\x0f\x89\x07\xab\x53\x3c\x70\x18\xdb\x28\xc9\x85\xf4\x4d\x8f\x9b\xe3\x96\x33\x27\x08\xa7\xb9\xb0\x82\xea\x87\xde\x75\x77\x2d\xef\x74\xcd\x9b\xa1\xd6\x70\xeb\x69\xaf\x65\xde\x28\xb5\x86\xdb\x60\xbe\x00\x0b\xa3\x35\xdc\xda\xea\xb5\xf0\xbd\x54\x6b\x68\x7e\x5e\x9f\xf6\xb6\x36\x7f\x67\x7f\x2e\x87\xdc\x30\xbe\xc2\x01\x51\x94\x14\x65\xfe\x87\xc4\xd5\x55\x94\x14\xdc\x35\x0b\xfb\xf1\x58\xfd\x3a\xd5\x89\x3b\xe8\xb7\xe5\xb9\x25\x4a\x0a\xee\xb7\x25\x4a\x8a\x27\x8f\x15\xd8\x53\x5d\xd1\xf6\x37\x4f\x4a\xea\x62\xf0\x35\x7e\x8c\xec\xa3\xe1\x57\x74\xc5\x05\xe0\xb6\x0d\xc2\x61\x52\xac\x68\x76\x61\x94\xa8\xb0\xb6\x80\xe6\x2a\x4a\xde\xc8\xb6\x22\x4a\x0a\x29\x2a\x3e\xbb\x91\x3f\x17\xde\xab\x7a\x1b\x88\xad\x46\x21\xec\xee\x8d\x20\xee\x8d\x20\xfe\xbc\x46\x10\x44\x5b\x41\x70\x51\xe9\x8e\x0c\x20\x1a\xd8\x35\xd8\xac\x9e\xdb\x2d\xa4\x60\x8d\xae\xdd\x76\xf4\x3d\x12\xea\xf9\x8c\x26\xea\xb1\x62\x8f\x1b\x7e\x33\x01\x5c\x79\x6f\x90\x92\xe5\xc0\x6b\x18\x61\xe9\xbe\xed\xb7\x89\xc0\x49\xa5\xfc\xc8\xff\xbf\xba\x22\xed\x36\xe2\xb3\xa9\x7c\xb6\xc0\x7f\xec\xa2\x77\x86\x51\x22\x5a\x6f\xec\xee\x63\x4a\x0b\x6c\xef\x0b\xd6\xe3\xed\x5c\xbe\x02\x05\x5e\xc2\x2a\x31\x4c\xdd\xb5\x7c\xcf\x2d\x5d\x4d\x29\x5a\xaa\x99\x74\xad\xb8\x32\xd2\x91\x7d\xec\x1a\xd6\xec\x80\x1e\x6c\xcd\x6e\x37\x52\x69\x87\x06\x26\xfe\xc6\xb1\x03\xdf\x3d\x36\x46\xc6\x38\xa3\x8c\x98\xe4\x7a\x30\x7d\xb2\x70\x72\x0f\xa3\xc9\x84\x82\x35\x32\x47\xb9\x75\x2e\x39\x57\x8f\x42\xf0\x71\x44\xa2\x44\xcc\x92\x34\x5c\x4e\xbc\x87\x10\xf3\xe8\xc2\xb6\x43\x5f\x3f\x82\x05\xe7\x30\xaa\x17\xe5\xa8\x3c\xf7\x3f\x98\x35\xe9\xae\xf4\x4a\x4f\x13\xa4\x22\xd5\x55\x30\x9a\xce\x47\x51\xe2\xba\xb7\x29\xd2\x29\x65\xdc\x9d\xd5\x40\xa7\x7d\xbe\xa8\x82\xc5\x82\x26\xb0\x96\x82\x84\x3f\x80\xb0\xb0\x2b\x6a\xab\xbb\x84\x11\x8c\x69\x16\x8d\x19\x7b\x92\xbd\xaa\x2f\x2c\x6e\x4f\xd3\x89\x80\x85\x7d\xa8\x12\xb5\x72\x78\x75\x7a\xbf\x2a\xb4\x2a\xbd\x05\xbf\x32\xd9\x25\xf5\xd8\x1d\x07\x71\x2c\xf0\x2b\xef\x70\xf8\x88\x66\x81\x5e\xba\x79\xf4\xab\xf0\x2c\x08\x77\x75\xb3\x20\xef\xb1\xff\x25\xa1\x81\xef\x5f\xcf\xa5\x1d\xc6\xb7\x32\x04\xf5\xeb\x4c\x2b\x51\xe3\x77\xcd\xe4\x5b\xb8\x62\x55\xac\xef\xed\x81\x74\x31\x89\x12\xeb\xa1\x52\x1d\x12\xb4\xcb\x22\x51\x95\xb8\x5e\xb6\x95\x06\x3c\x77\x3f\x7f\x5e\x7e\xf4\xe7\x1a\x5f\x57\x43\xd3\x60\x99\x19\xb5\x57\x0d\x7a\x1d\x46\xad\xdf\xff\x77\xc9\x33\xd2\x6e\x93\x61\x33\x6b\x2c\x84\x32\xaf\x4d\xd6\x0a\x78\x63\xbc\x9f\x2b\x27\x94\xcc\xe8\x7b\xeb\xa5\xf5\x17\x7e\x9c\xc9\xbd\x47\x5e\x09\x07\x98\xe1\x07\x73\x4c\x64\x40\xe2\x95\x58\xd4\x8d\x79\x51\x08\x4e\x95\x6c\xfc\xf9\x9c\x33\xa9\xe5\xb5\x4b\xf8\x95\x1f\x29\xa1\x3b\x31\x61\x9d\xd5\x51\x67\x6c\x6b\x25\xb8\x43\x9b\x92\x1f\x79\x32\x21\x90\x37\xf0\x0d\xb0\x48\xe7\x8b\xe2\x12\xab\x04\x1b\x6c\xa2\xb5\xab\xd0\xa4\x47\xc4\x9e\x86\x20\x7d\xac\x80\x1b\xe9\x6e\xaa\xd4\xd1\x94\x17\x13\x95\x03\x11\x55\xd6\x8d\xc1\xb8\x58\xd9\xf0\x88\x05\x37\x19\x87\x7e\x89\x57\xee\x1c\xea\x75\x94\x17\xce\xb3\xbf\x13\x63\x34\xa7\x1e\x8f\x50\x95\xa3\xd7\x35\xbb\xdb\x8b\x7a\x14\x24\xaf\xe9\x97\x8b\x90\x9b\xb5\x8a\x47\x70\x4a\x15\x59\xa4\x05\x7a\xe8\xca\x0b\x4b\xe1\x88\x3b\x1d\x22\xc6\xc3\x3e\xf5\x7e\x50\x80\x9a\x6f\x8a\x8c\xbd\x4d\xad\x47\xbe\x7d\x95\x2c\x48\xfb\xf6\xcb\xf6\x14\x62\x36\x4f\xf6\x70\x8f\x35\x2c\x1e\xc6\xc6\x9e\xab\xe8\x17\x4f\xb5\xdc\xe7\x59\x1c\x52\x8b\x40\x9d\x14\x3f\xb9\x55\x4f\xe6\x06\x03\x39\xdd\xf4\x8c\x66\x97\xc5\x0c\x1c\x91\xa0\x7a\x30\x76\x5c\xaf\x53\xd2\x1c\xcd\xc1\x8f\xf1\x4c\xd7\x7f\x43\xa1\x1c\x2f\xdd\x69\x13\xae\xd2\xf9\xba\x47\xda\x6d\xa9\x7c\xaf\x50\x52\xbc\xe3\xb3\x64\xe9\xf4\x94\xfa\xee\xfa\xb4\xb7\xd5\x28\xd0\xde\x57\xd4\xc9\xc1\x6d\x74\xb5\x52\x2e\x63\x20\x25\x5a\x39\x69\x63\xc6\xfe\xe7\xaa\x32\xf8\xf5\x58\xff\x3c\x45\xc9\x3b\xf8\xc3\xd2\xcd\xb1\x34\xae\x9c\x63\xbf\xa4\x76\x8e\xfd\x7e\x8a\xaa\x43\xfa\x39\xa7\xc6\x06\x1a\x3a\xe7\xee\x7d\x15\x15\x1d\x2b\xbc\x8a\x8e\x8e\xc3\xdb\x4a\x3a\x96\xba\xa2\x96\xce\x2c\x52\xa1\xa6\xe3\x2d\x56\x95\xbd\x89\xa2\x8e\xe1\xb6\x44\x51\xd7\xcc\x4b\xbe\xe8\x56\x03\x45\x5d\xa3\x50\x5e\x5f\xeb\x65\x9d\xe7\xf6\x6f\x15\xf2\xe0\xc5\x57\x21\x10\x59\xc2\x26\x11\x9e\xbe\x22\x91\xd8\x85\x2a\xc8\x44\xb6\x5b\x5d\xfe\x46\x3a\x5d\x2e\x49\x35\x79\x30\xe7\x69\xef\x6e\x9f\xca\xa9\x51\x36\xa0\xbb\xbb\x0f\x3d\x52\xf9\x78\xc7\xc3\x87\x91\x7f\xdb\x28\x6f\xee\xd8\x76\x4c\xb3\x22\x88\x12\xbf\x73\x5b\x07\x91\xfc\x36\xa9\x86\xa8\x39\x50\xdf\x4c\xaf\x26\x6b\x51\xc4\xca\xa8\x75\x05\x51\xd0\x6c\xce\x8e\xfc\xd1\x04\x6a\x36\xfb\x1d\x0a\x97\xb5\x64\x1a\x9d\xd1\x44\x9a\xb4\x98\x47\xea\x32\x5f\xb9\x96\xfd\x0b\x3f\x66\x6b\x73\x5b\xc0\x32\xaf\xdc\x69\xd7\x6f\x7c\x8b\x21\x9a\x2f\x11\xee\x99\xb6\x55\x78\x85\xe3\xf4\x8c\x66\xd9\x79\x16\x15\x05\x05\x73\x2f\xde\xab\x16\xd9\x80\xde\x37\xc6\xdd\x39\x68\xd9\x73\xfc\x8a\x1f\xac\x20\xf4\x51\x34\x4a\x04\x0a\x0b\xd7\xe9\xb0\xfd\xd0\xbe\x11\x32\x5d\xad\xa4\xd5\x9c\xd6\xda\x96\xe0\xcd\xe3\x3f\xc0\x8f\xc1\xc1\x00\x54\xe1\xc1\x9c\xad\x0a\x70\x79\x28\xb4\x59\x6c\xbc\x8c\x13\x50\x7e\xc7\x10\x47\x9f\x29\x09\x48\x1e\x25\xd3\x98\x2a\x27\x5c\x00\xd9\x37\xec\xa1\x81\x82\xb9\x8f\x19\xee\x93\x83\xb7\x76\x75\x45\x4e\xda\x27\x5b\xa7\xed\xd3\xae\x12\x06\x6b\x7c\x00\x88\xee\x99\x78\x67\x5f\xd8\xaf\x61\x89\xe8\xce\x6d\xa0\x38\x2a\xc0\x56\x61\xab\x47\x1e\x81\x31\xf6\x26\xf4\x65\x0b\x7b\xa1\xd1\x1d\x72\x04\x59\xe9\xa5\xa1\x27\xfd\x3a\x94\x9d\x16\xa4\x37\x87\x87\x12\x50\x37\x30\x18\x90\x20\x8e\xc9\x28\xc8\xa3\x31\x77\x7e\x00\x2f\x05\x76\xb6\x85\x02\x27\x4e\xd9\xc9\x58\xf6\xa6\x47\x76\xb6\xeb\x8c\x4e\xcc\x85\x2d\x38\x9a\x3c\x81\x4b\x5d\x24\xa1\x53\x10\x20\x21\x22\xd4\xc9\x69\x8b\xec\xfd\x00\xeb\x53\xa7\x3d\xe6\x89\x95\xca\xb4\x7d\x59\xdb\xaa\x1c\x60\x46\x4b\x7b\x56\xb1\xda\x71\xab\xa5\x34\xab\x7d\x7e\x19\xde\x60\x1c\xa2\xdb\xb5\xb6\x51\x54\xe4\xc1\x03\x82\xbf\x4f\xd0\x6f\xe4\xff\xed\x54\xee\xba\x2a\x2c\xc6\x60\x7a\xa3\xb9\x11\xcb\xb7\x6a\x6a\xe4\x2c\x98\x73\x23\x26\xcc\x9c\x1a\xe4\x6e\xed\x96\x33\x63\xf5\xab\x62\x62\x50\x9b\x5f\x7b\x5e\xee\x72\x62\x4c\xbf\x27\x9a\x91\xa2\x99\x80\xb3\x51\x0b\x6c\x11\xb6\x39\xd2\xf9\x21\xa9\x25\x8c\x15\xb6\xc4\x54\x6c\x3d\x56\x80\xdb\xa7\x27\x3b\x02\x54\xa6\x71\x10\x05\xb1\x75\x6a\x25\xe8\x6f\x77\x77\x00\xac\xde\x60\x7b\xc0\x63\x11\x43\xac\xdf\x13\x50\x63\x77\x34\x91\xd1\x84\x74\x50\x16\xe2\x90\x36\x3f\xbe\xe1\xc4\x02\xc3\xf6\xbd\x86\xd8\xaa\x98\x72\xb1\x49\xc8\x53\xb5\x6f\x9e\x61\xde\x7c\x53\xdd\x52\xc1\xf7\x9c\x09\x17\x9f\x2d\x63\xde\x8d\x8a\x4e\xcc\xca\xf1\x74\x6b\xd7\x6b\x8d\xe6\x59\x65\xf0\xa1\x88\xfc\xd2\xf9\x35\x5c\x28\x96\xee\xf6\xc2\x55\x51\x1c\xe4\x05\x39\x39\x65\xc2\x04\xaf\xf7\x46\xd3\xbe\xee\x9f\x77\x35\x07\x20\x67\x11\xc7\xc1\x12\x1c\x68\xf4\x33\x28\xf8\x54\x34\xd0\x84\x48\x2a\x8c\x63\xd1\x11\x46\x71\x60\xfb\xa6\x89\x8c\x2e\x49\x48\x27\xc1\x32\x06\x45\x68\xbe\x64\x72\xaa\xda\x98\x5b\xc2\x47\x4d\x4f\xc4\x78\xb4\x67\xd1\x38\x46\xdd\x80\x01\xeb\x1d\x71\x45\x51\xb8\xe1\xe9\xad\xd4\xa8\x5e\x3a\x6a\x97\x3a\x62\xb4\x44\x72\x7b\x8d\x00\xc5\x0b\x52\x3e\x69\x31\x8a\xef\x91\x16\x5b\x04\xec\xbf\xd3\xd6\xa9\xa6\x76\x01\x81\xd2\xa0\x50\xb2\x8c\xed\x67\x0f\x68\x36\x1b\xa1\xcd\xf6\x2e\x67\xf5\xb7\x66\x21\xb8\x1e\xaa\x9c\x95\xc0\xf7\x06\xe1\x29\x8f\xcf\x7a\x0e\x37\xbc\x6c\x38\xc6\x78\xd9\xbf\xb0\xea\x2d\x22\x16\xdc\xaa\xf3\xef\x13\x7e\x1a\xff\xf7\x69\xb7\x5e\x44\x10\xca\x5b\xe5\xea\xa1\xfc\xde\xc1\x8a\x61\x21\xa1\x9b\xb3\x0e\xf9\xf0\xd4\xbd\xcb\xb2\x70\xe6\xb9\xb4\x10\xf7\xe8\xf6\xc6\xe0\x75\x46\x6d\xde\xca\x08\x3f\xa8\xd2\x03\xaa\xcd\x16\x6a\x5c\xc1\x2a\xfb\x6f\x6c\x4c\xbc\x4b\x4a\xff\xfc\x5e\x51\x5d\xa7\xb2\x34\x9e\x60\x67\xb2\x82\x95\x39\x85\xd4\xb3\xe4\x93\x53\x9f\x07\xf1\xfe\x62\x99\xcf\x3a\x8e\x5b\x52\xf9\x4c\x5b\xfa\x18\x75\x6b\x66\x63\x71\x1d\xae\x9f\xf9\xbc\x7f\xe2\x96\x90\x13\xcf\xce\x59\x8f\x60\xe7\xb2\x96\x6f\xd2\x5b\x79\xf4\x15\x13\x88\x3d\xf9\xde\x7a\xfe\xa0\xeb\x8e\xd4\x21\x10\xff\xdb\xcf\x9f\xcf\x1d\x6b\x8d\x1b\xd6\xd2\x89\x60\xb3\x09\x7e\x52\x2b\xe6\x63\xe5\xd9\x58\x73\xee\x08\x2d\xdd\x91\xb1\x24\x91\x3b\xdb\x26\x0e\x41\xf9\xfd\xe8\x24\x4b\xe7\x5e\x73\x03\x0e\xe5\xe3\x2d\x23\xfb\xc1\x8e\x65\x20\x64\x58\x06\xad\xf0\x60\x4a\x32\x35\xde\x72\x03\x16\x25\x06\x82\x59\x94\xe1\x4c\xb3\x86\x55\x7d\x15\x5e\x05\x7b\x13\xbe\xb1\xe4\x82\xae\x78\xe2\x03\xdd\x93\x82\x8e\x40\xd7\x43\xb2\x0d\xc6\x0f\x5d\xe9\xce\x59\x20\xaf\x6c\x11\x55\xd6\x89\x9b\x77\x2a\xf6\xad\x28\x28\xf0\xa1\xe0\x77\xec\xb8\xf4\x06\xd9\xe1\x1e\xef\xf9\x6e\x9b\x33\x90\x9c\x04\x93\x82\x66\x6a\x91\xe0\xfe\xde\x68\xad\xfa\xcb\xf8\x1c\x77\x6b\xce\x51\xe2\xb0\x9b\x54\x62\x4f\xc4\x8d\x79\x5b\x56\x3f\x76\xea\x51\xea\x43\xda\x0e\x78\x53\xc9\x68\x1a\x72\x1a\xf2\xb0\xba\x6f\x0c\x76\x63\xaf\x1a\xa6\x11\xa3\x32\xbd\xcd\xa2\x69\xdf\x20\xd1\xdd\x72\xad\x3f\xc4\x1e\x82\xff\x1a\x52\xbf\x34\x48\x6d\xf8\xf7\x87\x22\xfe\x7b\xda\x47\x7f\xbf\x0b\xed\x13\x2f\xe9\xe3\xe8\x8c\x37\x25\x7d\x3b\x86\xd8\x8a\x9b\x8a\x43\xac\x76\xfd\xcd\x76\x16\xb3\x17\xab\xd4\x2f\xe6\xcf\x4b\x6f\xb1\x43\x5f\xfe\xf5\x57\xbe\x84\x17\xe2\xd6\xcf\x35\x52\xad\xeb\x7e\x87\x6c\x91\x0d\xb3\x77\x5d\xee\x90\x89\x87\x11\xf3\x4c\x3d\x77\x3f\x6c\x5d\xba\x19\x0f\xb6\x2b\x9c\xd9\x1b\xb8\xb6\x2c\xbe\x0c\x2e\xb6\xb6\xe2\xd8\xf0\x9c\xab\x95\xb5\xdd\x35\xd5\xaa\xde\x8b\x44\xab\xeb\xb5\x17\xbc\xe5\x57\xbb\xea\x4d\xdc\xf5\x69\x6f\xeb\xf7\x8e\xbb\x7f\x5c\xff\xec\x6d\x59\xf1\xee\x4d\x78\x22\x81\xff\xb9\xad\xcb\x52\x3f\x7d\x5b\xa2\xb7\x6f\x4b\xfc\x60\x6d\xe9\x79\xfd\xb6\x54\xcf\xdf\x96\xe8\xfd\xdb\x12\x3d\x80\x5b\x9a\x2f\xe0\x9c\x1a\x1b\x58\xd8\x38\xfe\x51\xbe\xe2\x23\xb8\x63\xef\x2b\xb8\xe3\xd5\x9f\xc1\x1d\x37\x7d\x07\x77\xec\x3e\x84\x3b\xbe\x83\x97\x70\xcb\x5b\x3f\x85\x3b\x6e\xfc\x16\xee\xf7\x0e\xea\x7f\xdc\xc0\xe2\x6c\x59\x65\x72\x26\x5d\xab\xf0\x1f\x82\x38\x91\xd5\xd9\x12\x9b\x9d\x2d\x0d\x2b\xb1\xa5\xcf\xf0\x6c\xa9\x2d\xcf\x96\xd8\xf4\x6c\x89\x6d\xcf\x96\x96\xf1\x99\xa7\xde\x26\x8b\xe3\x37\xb5\x3f\x3b\xf6\x1b\xa0\x1d\xdf\xc0\x02\xed\xb8\xb1\x09\xda\xb1\xc7\x06\xcd\x2e\x7d\xb3\x35\x52\x61\x86\xd6\x74\x91\x34\x37\x44\xfb\xb6\xc9\x2a\x69\x2f\x73\x0a\x8a\xd9\x71\xd1\xe6\xd1\xf8\xa6\x29\xa1\xc9\x19\x09\x53\x0a\xd6\x0a\xf0\x3a\x30\x48\x42\x70\x60\x4b\xfe\xf9\xe6\xf5\xab\xa2\x58\xbc\xa7\xff\x6f\x49\xf3\x62\x0d\x04\xb3\xcb\x05\x4d\x27\x56\x0e\xf7\x63\xa3\xde\x6f\xb4\x25\x5e\x44\xc3\x7d\x1b\x9a\x7c\xb9\xde\x5d\x33\x22\x45\x96\x42\x9a\x09\x20\xa9\xff\x92\xcf\xd8\xee\x13\x4d\x93\x34\xa3\xc3\x38\x4a\xe8\xda\x35\xb7\x58\x65\x78\x68\xe4\xea\xfe\xfe\xe5\xec\xfd\xcb\xd9\x3f\xf1\xcb\x59\xfe\x6a\x56\xd8\xb0\x19\xcf\x66\xf9\x86\x43\x6e\xf6\x7a\x56\xec\x7d\xc7\x45\x14\x43\x9d\x5c\x9f\x09\x6b\x87\x3f\x4f\x72\xc0\xa2\xe2\x52\xb1\x44\x5d\x64\x1c\x07\x79\x4e\x4e\xa0\xc8\xa9\xe8\x26\xcf\xd0\x4c\x98\x57\xb5\x36\x80\x7b\x23\x58\xa5\x42\xb9\xca\x38\x08\xa9\xf0\x64\xdd\xdc\xc9\x39\x40\xb2\x9a\x8e\xdf\x1e\x7e\xfc\xc0\xce\xd6\x30\x09\xed\x73\x1a\xb5\x39\x69\xb6\x3f\xa3\xdf\x6f\xd0\xef\x9f\xd0\xef\xfc\xd7\x60\x94\xca\x8f\x49\x94\x24\xf4\x52\x7d\xd1\x79\x91\xc2\x53\x46\x99\xb2\x88\xc6\x66\x42\x12\x24\x66\xc2\x3c\x1a\x67\x76\x4a\x1c\x47\x4e\x21\x03\xde\x00\x95\x1f\x46\x91\x69\x16\x24\xa1\x1a\x8a\x91\xf5\x93\xf1\xf5\xd1\xf8\x7a\x67\x7c\xbd\x34\xbe\xfe\xcf\xf8\xfa\x97\xf1\xf5\xd6\xf8\x7a\x61\x7c\xfd\xc3\xf8\x3a\xe6\x5f\x6b\xa7\xe5\xae\x6b\xd8\x1c\xbd\xdb\x7f\xc1\xa6\x78\x48\x76\xb6\x7b\x2a\xf1\xc3\xe1\x4f\x6f\xf7\x3f\x1e\xbf\x7f\xf9\xe9\xf5\xcb\xb7\x3f\x7d\x7c\x35\x24\x8f\x75\x26\xcc\xea\x50\xff\xd4\x39\x25\x94\x33\x24\x5f\x88\x95\xa0\x9d\xa8\x43\xc6\xa7\x17\x47\x3f\xbf\x25\xd7\xba\xa6\x77\x47\xaf\x5f\x33\xe8\x8f\x87\x6f\x5e\x1e\x1d\x7f\x1c\x92\xad\xcd\xcd\xcd\x81\xe8\xa1\xb8\xf1\x7e\x1e\xa7\xe3\xcf\x43\xd2\x66\xac\x33\x2f\xda\x46\xde\xfe\x18\xe2\x18\x0f\xf5\xdb\x46\xfe\x00\x83\xed\xe7\x75\xbe\x4f\xee\xe3\x60\xdc\x6f\x64\x7f\xf5\x8d\x6c\x4d\xb9\x80\xc8\x67\xc1\xce\x5d\x79\x80\x38\xc8\x2e\x17\x45\xfa\xf7\x0f\x78\x73\x18\x43\xda\x23\x1d\xfe\x82\x35\xe8\x05\x18\xb0\x9c\xb6\x37\xb4\x93\xeb\xbe\x01\x28\x2e\xc7\x0f\x54\x45\x12\x79\xf0\x40\xe6\xf6\xa5\xbf\x08\x2e\x26\xcf\xe8\x45\xdb\x7e\x45\x67\x78\xfe\xfa\x81\x6c\xb3\xd2\xb6\xeb\xe3\x6d\xe9\x2e\xd2\x2c\x4e\xe4\x65\xb8\xba\xe0\xb7\x9c\xb3\x13\xeb\xb5\x1d\x07\x95\x38\x62\x9d\xeb\xbf\xa2\x17\x7d\xd0\x5e\x0a\xcf\xbd\x3e\x1b\x23\x86\x15\x39\x6c\xdd\x3a\x3f\xd1\x71\xf5\xdb\x90\x6c\x7f\xf3\x84\x97\x44\x8f\x93\xe5\x9b\x33\xc6\xf2\x14\x8e\x5b\xc3\x6f\xbe\xeb\xb5\x4c\x94\xb7\x86\x4f\x37\xaf\x4f\x7b\xdb\x8d\x7c\x3e\xdd\xf3\xbd\x7b\xbe\xf7\xe7\xe5\x7b\x9a\xed\xf1\x77\xfe\x77\xc0\xf7\x2c\xd9\x7d\x75\xd1\xdd\x23\xb9\xcb\x82\x3e\xc1\x7d\xa5\x50\x43\x36\xaf\xed\x0f\x04\xbb\xd7\xb1\x88\x26\x4f\x31\x00\xfb\x56\x22\xfc\x32\x89\x8a\x37\xc1\x42\x89\x8b\x6d\x29\x51\x0f\x39\x0f\x6a\x6f\x4a\x59\x93\x49\xed\x43\xcd\x16\xdb\x5b\x86\x9c\x3f\x44\x19\x9b\x9b\xaa\xd0\xff\x56\xe4\x8d\x82\xd1\x28\x98\x52\xd5\x12\xce\x43\xc2\xff\xd0\xce\x9b\x7b\xea\x44\xd9\x6f\xaa\xb3\xe3\xf4\x8c\xc6\xc1\x58\x36\x6b\x67\xeb\x33\xc6\xd0\x97\x3d\xf5\x57\x8e\x20\x7e\xaa\x85\xc8\x67\x41\x92\xa4\x89\x31\x6e\x13\x42\x9f\x6b\x86\x15\x10\x35\xad\xc0\xc9\x6a\xe8\x81\xc0\xa8\xd4\xe7\xa5\x61\x35\x50\x5d\x4d\xe2\xec\x36\xf4\x02\x19\x95\xa9\xf3\x98\x3d\x36\x0f\xa0\x7f\x88\x26\xa0\x41\xae\x1e\x38\x04\xfa\xd9\x84\xf5\x81\xe2\xb9\x86\x53\x5f\x65\xc5\xb8\xbf\x8d\xea\xc6\xd5\x37\x2d\x80\xca\x14\x2b\x94\x61\xc5\xfc\xc6\x56\xda\x11\xc3\x22\x08\x85\x29\x29\x98\x7a\x5e\x2c\xe8\x98\x6d\x5e\xca\x3c\x1f\x1b\x5d\x09\xef\x29\x3e\xcb\x29\x5d\xc5\x88\x32\xb8\x50\x84\xe3\xb2\x6c\xb0\xc6\xb3\x20\x0b\xc6\x05\xcd\x72\xa9\xe2\x87\x7b\x79\x51\x1a\xed\x23\xde\x36\xa2\x69\xd2\x43\xb6\xd0\x64\x73\xcd\xef\xf6\x23\x9a\xce\x0a\x22\x3d\xd2\x5a\xde\x7d\xc5\x18\x0c\x69\x93\x83\xf4\xa0\x77\x79\x0f\xda\xf1\xf8\x18\xe2\x16\x22\x00\x03\x11\x69\xe1\xb5\xaa\xba\x21\xde\xea\xf6\x7f\x49\xa3\x04\x82\x35\x90\x67\x50\x07\x19\x92\xd6\x66\xab\x4b\x36\x04\x70\x89\xe1\xdb\x8d\xe7\x02\xa2\xf5\xfc\xd9\x27\x03\x06\xb1\xe2\x6c\x88\x1e\x6e\x70\x8f\xcb\x37\x9d\x97\x32\x43\x44\xd3\x11\x0d\x6c\x9d\x60\x86\x08\x91\x3c\x5c\x1f\xd3\xd6\xbc\x70\x6f\xcd\x15\xb3\x12\x25\xac\x12\x3f\xb2\xb0\x3f\x6a\x8f\xa3\x24\xd6\xb8\x36\x3b\xe4\x1e\x48\x8e\xf9\xd6\xae\x44\xfa\x19\x0f\xf6\x3c\x18\x90\x1f\xa3\x24\x24\xfc\x71\x97\xe8\xa8\x0a\xd6\xcc\x24\x8a\x56\x4b\xdf\xe4\x83\xed\x4b\x0f\xe2\x47\xcd\xe8\x85\x34\x61\x56\x67\x2e\x96\xc6\x4f\x3d\xec\xc4\x51\x7e\x56\x62\xd5\x6c\xe3\x77\x2f\x60\x5c\x23\x6c\x6a\x76\x49\xb4\xb1\xb7\x8d\xc1\x65\x20\x64\x6c\xdb\xa1\x9b\xea\x44\xac\x1d\x11\xfa\x42\xb5\x30\x21\x1d\x5e\x64\x6f\x8f\x6c\x76\x8d\x53\xda\x28\xa3\xc1\x67\x0d\xca\x46\xb9\xb1\x47\xc4\xab\x72\x36\x83\x07\xb3\x20\x3b\x48\x43\x0a\x35\x78\x0f\x61\x6c\xb2\xa5\x39\x4e\x5e\x64\xcd\x28\x84\x4f\xda\x4a\x24\xb2\xcf\x8a\xfc\x76\x34\x02\xcd\xfd\xf7\x10\xc9\x4d\x66\x3e\x2f\xca\x5e\xa7\x9b\x93\xed\xf1\x31\xdf\x59\x64\x74\x12\x5d\xf0\x08\x5a\x9b\x17\x5d\x36\x0b\xc0\x35\xfc\xee\xed\x45\xa8\xb7\xf2\xd9\xf7\xda\x2e\xc3\x11\x34\x88\x81\x9b\x57\x06\x13\xf0\x85\xf8\x34\x7c\xed\x0b\xb7\xeb\xa2\x1b\x98\x2a\x18\xc5\x0b\xcc\xf3\xd9\x87\xe5\x20\xcc\xb6\xf9\x72\x90\x33\xc2\x5a\xd2\xd4\x31\x49\x33\xdb\x84\x2e\x2f\xb2\xb2\x70\xf8\x68\x46\x19\xd4\x58\xcc\xcd\x7e\xd1\x89\x6e\xb6\xd2\xc1\x3a\x51\x44\x06\x37\xbc\xb6\x69\x10\xd6\xdf\x8d\x3d\x92\xc8\x7d\xe1\x7b\xb2\x4d\x9e\xb1\x93\x0d\xd9\x20\x6c\x3f\x48\x7c\x34\x21\x5c\xc8\xcf\xe8\xc5\x5d\x92\x86\x15\x73\xc0\xa6\x8d\x1a\xd6\xf0\x9b\x11\x87\xc3\x33\x10\x75\xfc\x36\x14\xf0\xbb\x4d\xab\xe5\xb1\x74\xb2\x8c\x63\x85\x86\x01\x3d\xa3\x49\xc1\x1f\x0a\x00\xcb\xff\x25\x4f\x13\x12\x8c\x22\x9b\xc7\x4b\xb7\x89\x1f\xd3\x1f\x97\x71\x6c\xbf\xa1\x94\x8f\x09\x58\xe9\x47\xbc\xb4\xfb\x18\x8a\x37\xec\xb4\xab\x19\xbb\xdb\x86\x21\x48\xb1\xca\xb1\xea\x94\x7d\xf7\xc1\x84\x22\x4a\x42\x7a\x71\x34\xe9\xb4\x3b\xed\x2e\xf8\x86\x7c\xb4\xe5\x79\x0e\xa9\xe0\x1d\x3b\xc1\xe2\x72\x41\x45\x73\x00\x04\x54\x64\xfa\x33\xeb\x44\xdd\x2f\x32\x7e\x70\x9f\xc1\xef\x92\x6b\x21\x8a\x99\x96\x7f\xaa\x15\xb2\x41\xda\x1d\x36\x73\xaa\xf6\x0d\xd2\xee\xb6\x1b\xad\xbd\x30\xca\x17\x71\x70\xc9\xe7\x05\x7c\x8c\x26\x05\x93\x6d\x15\x36\xec\x37\x6b\x17\x90\xfd\x82\x17\xab\x7a\xe1\xca\x6a\x33\x27\xdf\xbf\xbc\x8c\x1e\xb0\x2d\xcd\xa2\x18\x3a\xed\xcb\x60\x8b\x97\x1d\x61\x56\xd7\x25\x8f\x7e\x50\x89\x6a\x5a\xdd\xbe\x55\x3e\x7c\x56\x36\x9b\xce\xcc\x1a\x68\x16\x60\x7c\xb2\xc9\x33\xfb\x4d\xab\x78\x0f\xc6\xd6\x8c\x76\x36\x32\x18\xe8\x81\xa6\x67\x34\x8b\xd3\x20\xa4\xa1\x52\x04\x7b\xd6\x04\x1e\xc0\x47\x4d\x24\x65\x6f\x1a\x07\xe4\xe3\xd1\x8b\xa3\x21\x99\x07\x9f\x41\x35\x1c\x25\x67\xcb\x38\xa1\x59\x30\x8a\xe9\x5d\x0e\x50\x9f\x06\xec\xd7\xbb\x5b\xe4\x11\x41\xd9\xdd\x6e\x3f\xa3\x8b\x38\x18\xd3\x4e\x9b\xb4\xc1\xa9\x1b\x3b\x2d\xb4\xcc\x08\x91\x69\x72\x46\xb3\x22\xd7\xf1\x36\x41\xee\x0b\xe9\x38\x9a\x07\xb1\xcd\x64\xa3\xc4\xcf\xec\x8b\xf4\x05\x2f\xe0\x52\x5e\x65\xec\x4c\xd3\xad\x21\x17\xf0\x44\x4d\xb5\xd1\x1f\x8b\xd4\x0d\x8e\xa9\xc2\xcf\x34\x19\x63\xad\x6c\xcb\x78\xe2\x5d\x8d\x0b\xd5\x55\x1d\x99\x35\x91\x5a\x52\x77\x7c\x9e\xb8\xdc\x42\x7d\x6a\xee\x28\xc6\x61\x9f\x03\xc4\x34\xcf\x3f\xce\x82\xa4\xb3\x09\x4e\x64\x1f\x71\xab\x73\x61\xbd\x2f\x08\x6b\xab\x0b\xb1\x5b\x51\x8e\x81\xc5\xfd\x25\xb8\x69\x16\xa8\x0c\x92\x4b\xe1\x78\x47\xb8\x23\x4d\xca\xd1\xda\x17\x78\xdd\x4f\x42\xae\xfe\xe7\x34\x14\x4d\x2e\x73\xe1\x48\x3d\x27\x23\x3a\x49\x33\xda\x77\xe8\xea\x95\x38\x3a\x54\xe3\xfe\x4a\xec\x41\x35\xa4\xf5\x0a\xf6\x79\x03\xf9\x6a\xfd\x3e\x14\xa6\x62\xf3\xe0\x82\x87\xad\xbc\x88\x8a\xcb\x21\x79\x0a\x2a\x6c\xb9\xeb\x44\xb9\x70\x69\x0c\x45\xbb\xf6\x26\x83\x26\xb9\xb3\xc1\x20\x76\x8d\xa2\x78\x3a\xab\x0b\x5b\x65\x85\x21\xdd\x19\xa3\x1d\x76\x0a\xe1\x48\x6b\x7b\xab\x80\xf8\x4a\x7f\xff\x70\xf4\xb6\xaf\xb0\xcc\xdb\xd3\x0e\x2c\xc1\x75\x6c\x4e\x02\x3b\x94\x67\x8f\x2c\x82\x3c\x67\xbc\xab\x98\x65\xe9\x72\x3a\x33\x57\x80\x1a\x88\xa0\x35\xa8\xd5\xbd\x9c\xd4\x5c\xed\x11\x9c\x96\x3c\x32\x6f\xe9\x88\x25\x80\x78\xdb\x61\x56\x57\x53\xdb\x99\xb4\x1f\x45\x15\x90\xce\x7a\x94\xff\x18\x25\x51\x41\x2d\xa4\x5b\xdd\x00\x09\x11\x75\xc2\x94\xb2\xdc\x8e\xa2\x75\xf1\x5e\x6c\x2a\x7c\x1d\xb0\xf3\x52\x02\xdc\x9f\xfc\x4c\x6d\x41\x6a\x4a\x0b\x08\x57\x7c\x34\x39\x4e\x22\xaf\xb6\x0b\xca\x16\x33\x2a\x7e\xa8\x05\x47\x8a\xb4\xa7\xb4\x53\xca\x21\xba\x37\x6a\xa3\xea\x87\xaa\xa6\xc3\x3b\xd3\x85\x22\xe0\xb6\x2b\x27\x34\xcb\xd2\x4c\xba\xa4\xe1\x3d\xce\x49\x92\x16\x64\x9c\x66\x19\x1d\x17\xc3\x73\xb5\x6e\xcc\x5e\x1b\x0b\x88\x15\x94\x24\xb0\xe4\x99\xf0\xdf\x33\xf8\xaf\x5f\xa4\xaf\xd3\x73\x9a\x1d\x04\x39\xed\x00\x73\xe1\xfa\x5e\xcd\xc7\x18\xd4\x3f\xc4\x2d\xb3\xb8\xba\x39\x61\xff\x9f\xea\xa3\x38\x02\xc1\x7e\xbf\x31\xe1\x71\x4f\x64\x09\x3d\x27\x2f\xd9\xa8\x3a\x6d\xb8\xea\x85\x8e\x80\xad\xea\xbf\xdb\x05\xa1\x17\x51\x5e\xe4\x3d\xb2\x88\x69\x90\x83\x58\x0c\x23\x4f\x13\x85\xaa\x49\x1a\xc7\xe9\x79\x94\x4c\xa1\x64\xce\xb8\xa0\xb5\x8c\x44\x0f\x7b\xe0\x5f\xa1\xa7\x9f\x7d\x54\x44\x89\x55\xbd\x07\xef\x57\xa6\x57\xe1\xe0\x33\x85\x45\xc8\x19\x3e\x5c\x46\x47\x60\x4f\xab\x98\x2c\x27\x01\xc6\x6a\xc1\x57\x05\x9f\x78\x8e\x5a\x41\x59\xef\xd2\x3c\x8f\x46\x31\x9f\x42\x70\xa1\x21\x8c\xfa\x3e\x1c\x32\xf9\x32\x2b\xf8\x4f\x26\x52\x4b\x6c\xbd\x9c\x4c\xa2\xe9\xa5\xf8\x38\x92\xa4\xf4\x88\x7c\x66\xcd\xf3\x3f\x7d\x5d\x05\x9f\xe2\x66\x8b\x83\xcd\x35\x98\xba\x5c\xe2\x9f\xf2\x2a\x8a\xc3\x4d\x35\x9c\xba\xff\xe1\x9f\xe2\xc2\x48\xe7\xf1\x02\x8f\x1e\xa9\x85\xa9\xef\x71\x78\x81\x5f\x83\x51\x6a\xe4\x79\x4a\xc8\x7b\x18\x3e\x00\xb8\xbe\xc1\x79\xbc\x04\xea\x05\x2a\xcc\x3f\x05\x16\x10\x08\xb1\x20\xd0\x07\x5c\xa6\x08\x84\x50\x8d\xc3\x29\xfa\x5d\xc8\xdf\xb6\x48\xc1\xf9\x82\x75\xf2\xbd\x52\x72\x3a\x27\x87\x71\x90\xb0\x93\x41\xa0\x58\xb3\x48\x17\xba\xb2\x34\x23\x01\x79\xf5\xf2\x9f\x70\x08\x97\xd2\xda\x9d\x31\x14\xb5\xcf\xca\xa3\xdd\xcf\x33\x2a\xfd\xec\x05\xe8\x2a\x57\x44\x41\x41\xc1\x02\xd8\x7a\x0a\x72\x72\x4e\xd9\x02\xd1\x0e\x56\xe4\x30\xd6\x90\x34\xf4\x33\x35\x8e\xe4\x72\x9c\x98\xa5\x70\x51\x87\xd5\x2c\x99\x04\x16\x8a\x78\x09\x1c\x35\xd6\xe4\x54\x9c\x3b\x59\xf2\x10\xde\x86\x45\x05\xe4\x99\xd1\xc8\x10\x7f\x21\xc9\xaa\x76\xf9\x06\x1c\xc7\x9e\x15\x7c\x4e\xa3\xfb\x05\xfb\xdf\xb2\xc4\x8b\xb4\x6a\x81\xa3\xf3\xc2\x6f\xb6\xd4\xd9\x6a\xfb\x1d\x17\x3b\x20\xe4\x6e\x96\x7a\x11\xcd\x69\xfe\x7b\x2c\xf3\x44\x28\x17\xd9\xe2\x56\xaa\xaa\x9c\x1f\xf3\xd9\x16\x4d\x94\x29\x8b\x43\x0d\xaa\x23\x8d\x68\x42\x53\x81\xbc\x3a\x64\x53\xaf\x49\xc1\xac\x4d\x39\xb9\xd2\x15\x68\x00\x85\x7e\x6c\x7b\x63\x4d\x42\xcd\xf1\xe7\x1b\x26\x03\xc2\xaa\x97\xe5\xc5\x8f\xab\x2b\xb2\xb9\xeb\x3d\xdb\x88\x7a\x9d\xb3\x09\x4f\x37\x0e\x44\x02\xe5\xb2\x27\x0f\x1e\x10\xf1\xdb\x27\xf3\xb3\x26\xed\x5c\x7c\xc0\xf0\xb9\x40\x33\x44\x31\x51\x58\xa9\x44\x36\x2f\xda\xbd\x76\x1b\xdf\xb7\x58\x8e\xd2\x7c\xa5\x31\x9d\x94\x8a\x74\x89\x0c\x1d\xeb\xa1\x14\x45\x27\x1c\x4c\x06\xf1\x50\x27\x31\x61\x35\x09\xb0\xc5\x79\xda\xce\xc9\x58\x85\x74\x71\x48\xcb\x8c\xf8\xd2\x84\xbe\x4a\xa8\x06\x9d\x91\xcd\x3a\x4d\x7d\x97\x41\x32\x0c\x7c\x84\x28\xcb\xb7\x5e\xe1\xc5\x77\x07\x39\xad\x53\x05\xb0\x46\xa2\x76\xea\x5a\x93\x5b\xfe\xb5\x60\x96\xfb\x8b\x78\x99\xeb\x2e\x88\x6f\xaf\x77\x43\x05\x64\x2a\x92\x66\x74\xfc\x39\x97\xa7\x26\xce\x22\xe5\x2d\x67\x2e\xde\xca\xc5\x97\xe0\xc6\xd7\x1b\x8c\x98\x93\xfc\xd8\x1b\x88\xd8\x0c\x29\x8c\x1a\x60\xeb\x3f\x40\x05\xb0\x63\x3b\x08\xae\x24\xa6\xce\xaa\xdc\x98\x39\x51\xde\xd2\xa0\x0d\xfe\xb3\x79\x71\xb2\xf9\xe8\xbb\xe0\xd1\xe4\xf4\xcb\xe3\xcd\xeb\xff\x19\x44\xfd\x82\xe6\x85\x02\x5f\x61\xf0\x15\x63\xfe\x4a\xa3\x6d\x30\x4e\x50\x00\x0c\xfe\xd3\xd9\xbc\xe8\x3e\xab\x1c\x28\xa6\xc0\xc1\x40\x07\xcb\xe2\xe1\xb0\xa0\x7b\xdc\x85\xb0\xb0\x3a\x9c\xc3\x43\x5e\xb6\x21\xa3\x61\x9b\x14\x2c\x3c\x01\x12\xd3\x57\x85\xb7\x33\x66\x5f\x18\xa3\x43\x60\xfb\x8f\x7e\xf4\x82\x59\x5d\x86\xd8\x5d\xed\x1c\xbc\x1d\xe7\x73\xf6\xef\x38\x58\xe4\x20\x3c\x88\xdf\x3d\xec\x9e\xd1\xee\x2d\xf7\x3a\x8f\x3a\x6b\x54\x7e\xa4\xf6\x76\x8e\x19\x1a\x8c\x67\x64\x1c\xe4\x4e\x35\x51\xce\xa9\x64\x39\x17\xb3\x83\x48\x89\xaf\xb1\xe6\x04\xc5\xdb\xca\x97\xf3\x39\x0d\x4b\x69\xcb\x6a\xee\xae\x69\xcc\xaa\xbe\x8a\xd6\x06\x03\x3e\x20\x0b\x39\x81\x2a\x29\x7e\x39\x1b\x90\xd6\x86\x08\x88\x57\x41\x0e\xae\x68\x66\xc1\x8e\x6c\xc4\xd4\xa4\x48\x59\xc7\xe7\xee\xe5\xf1\x26\xdc\x50\x12\x8b\x3c\xc0\x75\x77\x31\x23\x31\x85\xc7\xd4\x28\xfe\xde\x62\x41\x33\xd6\x5b\x39\x0f\x09\xc4\x2e\x9c\x46\x3c\xbc\x5d\x90\xd3\x79\xb0\x60\xf3\xb1\x65\xe8\xf9\x3a\xca\x7e\x01\x75\x1a\x9c\xb2\x6d\x3d\xe9\x92\x1f\xc8\xb7\x6c\x37\x17\x59\x27\xd1\x69\xbf\x48\x8f\x59\x43\x42\x13\xb4\xbe\xb7\x87\x32\x81\xe2\xab\x2b\xfc\x7e\xcf\x53\x23\xd6\x2d\x59\x35\x96\x78\x0a\x47\x6b\x52\x73\x7c\x83\xef\xeb\xe8\x0b\x8a\x4c\xdf\x88\x83\x9e\x24\xc7\x12\x5a\x2c\xd2\x3b\xa5\x45\xa9\xbc\x56\xfb\xf2\x0a\xa4\x88\x54\xc6\x8a\xfc\xec\x47\xd7\xa2\x9d\x76\x5b\xd0\x92\x4b\xa7\x06\x82\x6f\x44\xb5\x08\x68\xec\xf4\x9e\x55\x54\x41\xc7\xb2\x17\xe8\xd6\xdd\xa6\x69\x60\x79\x33\x6d\xf9\xc7\xa8\xf4\x3b\x76\xee\x99\x70\xff\xf9\xf2\x22\x4e\x91\xb8\x41\xc1\x75\x04\x6c\x92\x90\xdd\xff\x8d\xbd\x52\xea\x46\xf4\x65\xb3\xd2\xda\x9a\x2a\x69\xd3\x2a\x69\x4a\x9e\x5a\xd2\x34\x18\x69\x91\x32\x89\x32\x0a\xc9\xf6\x26\x77\x19\xf4\x48\xdc\x0f\xf2\x36\xf9\xf3\x84\xcd\x0b\xc2\x6d\x3b\x5c\xdb\xae\x5a\x52\xf6\x5f\xf6\x0b\xe7\x03\x98\x6f\x2b\xfb\xad\x66\xf4\x6b\x49\x33\xde\x6d\x4f\xfa\xd4\x95\xf8\x40\x32\x3c\xdf\x6b\xab\xb6\x59\x4f\x45\xe2\xee\xcb\x57\x9f\x09\x21\x23\x2f\xc2\x8d\x92\xaa\x51\x3f\xa6\xea\x91\xc7\x9b\xfe\x4b\x02\xe9\x87\x58\x1e\xa6\x73\x2d\xe5\xd6\xc7\xd8\xf4\x9e\x24\x7d\x37\x5f\x46\xdc\x4d\xbe\x93\xf9\xce\x80\xa4\xc3\xbb\x61\x89\x85\xb2\x6f\x49\x5e\x04\xc9\x98\x71\x11\x5d\xf8\xea\x4a\x21\x4d\x14\x86\xc7\x6b\xf0\xcb\xf0\x9b\xe1\x4d\xe5\xa6\x11\xc0\x8b\x54\x95\xed\xa6\x88\x92\xe7\xe1\x3a\x2c\x7d\x70\x6c\x8b\x1a\xa2\xc8\x13\x21\xc9\x8b\x1f\xc1\x5a\x45\xcf\x60\x34\xbc\x6f\xed\xbb\x43\x0f\xef\x4b\x63\xdc\xc8\x1e\xd7\x63\xe7\x47\x6d\x43\xb2\x2a\x7e\x64\xd1\x1b\x61\x48\x96\x68\x37\x1c\x11\xeb\x53\x51\x3f\x1c\xde\xf5\x1b\x0c\xe6\x48\xf4\xad\xe1\x62\x60\xf2\x45\xb2\x8c\x63\x08\x92\xd0\x71\x57\x08\xd8\x6d\x83\x0a\xc3\x33\x76\x71\x5d\xdb\x70\xe4\x23\xde\xd9\x06\xec\x80\x03\xde\x84\x19\xf0\xa4\x1b\x4d\xa4\xe8\x5e\xd3\xd1\x80\x07\xc0\xfa\xb1\x38\x01\x35\x1a\x8e\xc4\x0d\x8a\xd1\x90\xa5\x41\xc1\xca\x31\xd8\x07\x12\xbe\x8f\x82\x89\x5c\x2a\xa9\xce\x1c\xc4\xdf\x73\x73\x5d\x69\x03\x84\xca\x31\xb0\x62\xf6\xa3\x01\xe5\x39\x29\xbb\x74\xf7\xa9\xf5\x75\xb8\x98\xe4\xaf\x70\xb5\x2d\xeb\x35\x19\x43\xd4\xa7\x0e\xf5\xec\x6d\xf8\x38\xba\xca\xa8\x03\x31\xee\x97\x6c\x02\xe9\x72\x4e\x46\x71\x3a\xfe\x4c\x66\x34\x08\x69\xc6\x3e\xd2\xb9\x6d\xb4\x11\xe5\xcf\x59\xb2\x4f\x68\x98\xd1\x0b\xe5\x16\x1d\xca\x92\x49\x14\x17\xb6\x32\xd3\x43\xb0\x00\x6b\x78\x1f\x66\x29\x95\xe7\xfc\x6f\xb6\xb6\xf5\x41\x9f\x83\xd7\xe0\xa5\xfc\x98\xce\xeb\xc2\x55\xf9\x4e\xe9\x2e\x94\x2f\xe0\xb0\x3e\x69\xaf\xb9\xfd\xb8\xc1\xcc\xc4\x29\x13\xf3\x16\xd1\xd8\x9d\x87\x8f\x2c\xb9\x6e\x1e\x0a\x05\x54\x31\x01\x50\x93\x31\x01\x50\xac\x72\x02\x9e\x3c\xd6\xf8\xe7\xd0\x37\xc6\x3f\x54\x85\x6b\xf2\xa1\xdf\x01\xba\x11\xf6\x4b\xfc\x8e\x08\x91\x6f\x28\x7f\xf4\x64\x2a\xbc\xf9\x19\xaa\x5f\x3c\x1d\x04\xc3\x21\xff\x4f\xa6\x08\x03\x92\xa1\xfe\xc9\x73\x90\x71\xc9\x10\x7f\xc8\x72\xc7\xc5\xe4\xe9\x50\xfc\x2f\xd3\xc0\x5c\x65\x28\x7f\xe8\x7a\x38\xac\xfc\xa5\xd3\x05\xbc\xfa\x29\xea\x71\x6d\x6e\x87\xbe\x44\x0e\xed\x9a\x72\x0e\x3d\x69\x06\xac\xb4\x9a\x1c\xda\x09\x72\x1c\x3f\x53\x18\xc5\xcf\x14\x8d\x01\xd2\xc4\x0f\x09\xa7\xa4\xc5\x21\xfe\x90\xb9\xa6\xca\x7a\xe8\xa4\x28\xac\x71\x41\x7d\xa8\x7f\xf2\x1c\x24\x1d\x0f\xf1\x87\xcc\x35\x4e\x22\x43\x3b\x41\x42\xa1\x7c\x2b\xc7\x3a\xba\x0f\xdd\x24\xd9\x43\x07\xd2\x49\x92\x75\x4a\x61\x6c\x88\x7e\xe3\xfe\x26\xd3\xa1\xfa\x25\xd3\xf9\x9e\x3a\x54\xbf\xd4\xe8\xf9\x7a\x1f\xea\x9f\x6a\x4c\x6c\x97\x1c\xca\x1f\x32\x95\x6d\x58\x43\xf1\xbf\xaa\x83\xf1\xbb\xa1\xfc\x21\x53\x81\x6d\x0c\xe5\x8f\x1e\x2c\x30\xee\x9f\x4e\x3c\xea\x6e\x0d\xb7\xbe\xeb\x55\xba\xb7\xe9\xb5\x96\xc5\xe4\x69\x6b\xf8\xf4\x9b\xeb\xd3\xde\xf6\x56\x13\x87\x0f\xe6\x12\xde\xe3\x0b\xb8\x25\xfc\x1c\xb4\x86\xa4\xb5\xd9\xdf\xde\xec\x6f\xb5\xd6\xae\xa5\x27\xb8\xed\x46\x81\x8a\xef\x1d\x49\xdc\x3b\x92\xf8\x2b\x38\x92\x10\xb5\xac\xb9\xae\xe0\xfe\x4e\x27\x93\x8c\x5e\x92\x9f\xa3\x78\xfc\x99\x92\xef\x7f\xa1\x93\x89\xed\x4d\xa2\xa1\xc3\x38\x00\x8b\x82\x84\x1c\x31\x89\x3b\x00\xa8\x28\x48\x5c\xb0\x1f\x83\x11\x03\xfb\x47\x3a\xa5\x71\x5e\xd0\x38\xa6\x19\xf9\x7e\x02\x89\x2e\xf0\x4f\xc1\x19\xf9\x39\x4d\x43\xf2\xfd\xb4\xd4\xcb\xc5\x63\xed\xdd\x47\xb8\x82\x7c\x13\x24\xc1\xd4\x74\x3d\xd1\x1f\x30\x2c\x0c\x32\x0e\x30\xe7\x00\xd2\xc5\xc4\xe1\x08\x0e\x47\x36\x70\x34\x0a\x12\x09\xf2\x12\xac\xf8\x6d\x08\x2e\x79\xe5\x03\x5a\xcc\x24\xe0\x8b\xe7\x15\x70\xe1\x48\xb9\x9b\x9d\x55\xd5\x97\xcf\x54\x7d\x6f\xc1\x31\x79\x19\x60\x42\x0b\x09\xf8\x8e\x66\x39\xbc\xa4\x2a\x87\x5e\x08\x10\xd5\x89\xf3\x20\x9b\x57\x75\x83\xe5\x2b\x60\x5a\x14\x10\xb4\xc9\x85\xcf\x45\x96\x04\x95\x5c\xc5\x80\x94\xec\x82\x9d\xa8\xb4\x6f\x8f\x28\xb6\x2a\x44\x51\xe5\xcb\x5d\x84\x70\x20\xe9\x8c\x49\xbc\xdb\xa0\x49\xe8\xe9\x1b\xcf\x90\x60\xcf\xe1\xc4\xe4\x42\x8d\x58\xba\xc2\x64\x96\x2e\x68\x56\x5c\x7a\xe0\x16\x22\x4b\x82\xbe\x2a\x8a\xc5\xbb\x2c\x3d\x8b\x42\x2f\xb9\xb1\x85\xba\x10\xd9\x8a\xd8\x16\xe3\x8a\x12\xd1\x62\x6c\x17\x68\xe6\xd0\x70\x6d\x4d\xc9\xea\x3f\xd3\xd1\x0e\xe9\xc8\x6a\x4c\xa7\xbc\x99\xbd\x42\x12\x7a\x6e\x2d\x1b\x5d\x12\xf9\xe7\x15\x91\x56\x51\xcf\x25\x14\x02\xa2\xfc\xa9\x0b\x3d\x67\xcb\x05\xfc\xf4\xe3\x2a\xc2\x91\xc8\x7c\xf1\xdc\xc9\xcb\x67\xb2\xe4\x87\x99\x5b\x32\x81\x35\xc0\x72\xdf\xd2\xc2\xc9\x5d\x68\xc2\x67\x20\x72\x1d\x38\x70\xa3\x5f\x7f\x95\x6d\x30\xba\x76\xfb\xa0\x09\x1c\x80\xc4\x67\x07\xc3\x68\xca\xd6\x47\x8d\x60\x11\x0d\xd5\x66\x28\xfe\xe7\x47\x0e\xdc\x49\x81\xad\xdc\x28\x8a\xc9\x67\x68\x7c\xf5\x14\x0c\xa2\x97\x21\xfe\x70\x9a\xf8\xa4\xd6\x00\xff\xe1\x0c\x50\x00\x74\x74\xfb\x82\x9c\x23\x9a\x0f\xd1\xef\x0e\x37\xe6\xb9\xee\xee\x32\x89\x69\x30\x00\x0f\xbc\x39\x25\x7a\x0c\x29\xdf\x89\xc1\x25\xd0\x1a\x23\x37\xcf\xf8\xea\xc6\x56\x3a\x2e\x26\x34\xca\x3a\x65\x38\x4d\x8a\x29\x0f\x87\x0c\xae\xa7\x71\x5c\x78\x65\xd2\xf6\xf4\x25\xa3\x3c\x56\x84\xee\xc5\x67\x4a\x17\x87\xf9\x87\xcb\x64\x1c\x25\xd3\xca\xae\x40\x59\x0b\xbe\x19\x05\x7a\x3a\x82\xf9\xc2\x73\x6d\xbf\x62\x41\xc9\x57\x30\xdc\x9b\x14\x7c\x79\x60\xe4\x8b\x59\x09\x05\xdf\x1e\x38\xf1\xec\x5a\x82\xb1\x4f\x07\x0a\xbf\xc0\xe5\x80\x2a\xc5\x0b\x6b\xd4\x29\x13\x3c\x6d\xeb\xe7\x54\xb2\x79\x91\xe2\xad\xd5\x86\x46\x69\x9e\xba\x31\x2e\x65\xed\x55\x38\xe5\x16\x8e\x12\xf2\x67\xea\x1f\x19\x86\x12\xdf\x0e\x1c\x36\x6c\xe1\x90\x2a\xc5\x03\xeb\xde\x0a\xcb\x32\x07\xf6\x6d\xa1\xd3\xe7\xb2\xb2\x4e\x8e\xa7\xdd\xc3\xe7\xfb\x6f\x51\x63\xec\xd3\x81\xd2\xde\x69\x38\x98\xf8\xf6\xc1\x49\xc7\x29\x0a\x10\x12\xd8\x2e\x66\x2f\x7c\xbe\xf5\xe3\x87\xdc\xfc\x52\xc8\x74\xae\x68\x5e\xd7\xc1\x9d\xb4\x0d\x59\x76\x7d\x1a\x46\x19\xa8\x8a\xc7\xc1\x02\x1e\x5f\xa0\x0b\x4c\xcf\x8c\x1e\x1e\xec\xbf\x33\xd6\x3e\x2b\x87\x2d\xe4\x22\x2e\x4a\xb2\xe5\xcb\xa4\x4a\x9e\x6f\xbc\xf5\x64\x10\x7d\xd1\x8c\x5c\xd9\xe0\x4f\x46\xf1\xdf\xaa\x80\xa3\x27\x8a\x77\xc3\x5e\x27\xc4\x91\x8e\x79\xe7\x9c\x80\x0e\xa6\x2d\xf7\xa4\x24\x0d\x69\xbb\x67\x40\x4c\xc1\x2e\x64\x48\xda\x4c\xe8\xf8\x34\x8e\x23\x9a\x14\xff\xe0\xe0\x6d\x7d\x27\xdd\xed\xdd\xa4\x35\x5a\x9c\xa7\xd9\xe7\xb2\x06\x13\x5a\x7c\x12\xa0\x16\x88\x19\x2f\x60\x68\xaf\xf2\x5b\x76\x8b\x0a\x85\x76\x59\xbf\x68\x31\xfb\x04\x73\x3d\x4e\xe3\x7f\xfc\x0e\xfd\x3b\x9f\x45\xf9\x42\xb9\x46\x76\xba\x97\xcf\x66\xb7\x46\x1b\xfc\x3c\xf5\xee\x25\x51\x7e\x90\x26\x09\x77\xd9\x84\x96\x5b\xd7\xa0\xbd\x8e\x77\xbb\x7c\xf0\xc0\xbb\x8d\xe2\x2a\x3b\x5d\xff\x0e\xc6\x9d\x14\x48\x99\xbc\x94\xe6\xc1\x38\x14\x02\x27\x08\x89\xc6\xab\xb7\x65\x75\x4b\x67\xa2\xf8\x84\xc0\x55\x4e\xc6\xc1\xa2\x35\xdc\xde\x64\x49\xf8\x48\xd2\x1a\x6e\x6f\xb1\x34\x7d\x1c\x68\x0d\xb7\x1f\xab\x14\x2e\x3a\xb5\x86\xdb\x4f\x55\x12\x16\xee\x5b\xc3\x9d\x6d\x95\xc1\x56\x78\x6b\xb8\xb3\xa3\x13\xb4\x50\xdf\x1a\xee\xe8\x4a\xf5\xb1\xb0\x35\xdc\xf9\xd6\x49\xa6\xc5\xac\x35\xdc\x79\xea\xa4\x27\xb4\x68\x0d\x77\xbe\x73\xd2\xa5\x20\xdc\x1a\x3e\xde\x74\x32\xf3\xd9\xac\x35\x7c\xbc\xe5\xa6\x33\x59\xb8\x35\x7c\xac\xbb\x2f\xcf\x38\xad\xe1\xe3\x6f\x54\xa2\x79\x70\x6e\x0d\x1f\x3f\x51\x59\x52\x6a\x69\x0d\x1f\x7f\x5b\xad\xdb\xbb\x3e\xed\x6d\xef\xdc\x6b\xde\xee\x35\x6f\xff\x2d\x9a\xb7\x20\x8e\xc1\xbf\xc4\xed\xdc\xb8\x22\x05\x97\xa3\x0a\xf1\xe9\x42\x64\x94\x98\x97\x67\xdc\xa2\x1f\xe9\x18\xa0\x37\x12\x4e\xc7\x8c\xa9\x0b\x8e\xe4\xea\x69\xbc\x8a\x9a\x1f\xe1\x72\xd7\xaa\x0c\xd2\x24\xc4\x39\x0f\x7d\x64\x82\x48\x56\x24\x32\x95\x73\xd7\xfd\x38\x36\x86\x62\x0a\x46\xe6\xd1\xaa\x07\x37\xf5\x3d\x62\x99\x96\x95\x28\x3d\xcc\x04\x7c\x44\xfe\x85\x5f\xce\xb3\xff\x70\xb2\x63\x2e\xc9\x37\x21\xa7\x87\xd5\x51\xbe\x2d\xa9\x55\xba\x03\xdf\x53\xbf\xae\xae\x20\xfc\x0d\xb1\xdd\x3e\xb0\x44\x48\x3d\x69\x33\x29\x14\xc2\x0a\xb4\x7b\xa4\x5d\xa4\xfc\xe7\x69\x9f\xa3\x19\x85\x3b\x9c\x78\x6e\x43\x45\x33\x27\x93\x53\x30\x70\x51\xf6\xa1\xe2\x86\xb4\xeb\x89\x99\x6d\x55\xc3\xfa\xc3\x8a\xef\x21\xe2\xe1\x1e\x74\xa0\x23\xfc\xbc\xa4\x63\xe0\xe9\x06\xa5\xcd\x82\x7e\xb7\x05\xae\x28\x34\x5e\x0d\x3c\x9b\x8f\xbb\xb0\x73\x8a\x2a\x8c\x7b\x82\x16\x87\x41\x11\xc8\x11\xb0\xdf\x7d\xf6\x0f\xd9\x43\xbf\xaf\xae\xc0\x28\x56\x01\xc0\x55\x72\x2e\x41\xc4\xd7\xd5\x95\x0e\xbe\x09\xda\x46\xd6\xb4\xbc\x23\x47\x80\x27\x9b\xa7\xfd\x9c\x31\x04\xe5\x61\x9d\x41\xcf\x85\x80\xa3\x29\xcc\x9d\xae\x5f\x3c\xd3\x85\x5b\xd9\x13\xa6\xb6\x42\xba\x73\x2f\x6d\x3b\xbf\xa8\xe7\xe9\xdd\x93\xcd\x53\xf4\xf0\x6a\x1d\xda\xef\x92\x2f\xf0\xd4\x21\x48\x92\xb4\x20\x93\x28\x09\x79\xbf\xa2\x64\xca\x1b\x7a\xa6\x9a\x1f\xa7\x49\x9e\xc6\xb4\x7f\x1e\x64\x49\xa7\x8d\x4b\x70\x67\x39\x8c\x15\xc7\xe9\xb4\x8d\x4c\x5f\x45\x8f\x19\x2a\x1c\x87\x4b\x54\xb0\x21\x1c\x98\x0b\xe6\xae\xe3\x5b\x9d\x3d\xde\xad\x9e\x49\x10\xe6\x11\x0a\x6a\x94\xbe\x0e\x61\x8a\x1b\x2c\xc7\x0b\x3a\x66\x12\x80\x67\x3d\xf6\xc0\x21\xd3\x28\x18\x7f\x56\x21\x44\xc1\x13\x81\x38\xec\xca\xeb\xd6\x4e\x90\x4d\x97\xf0\x12\xe4\x44\xfd\x42\xce\x78\x4c\x2b\x74\x59\x23\x84\x7e\xae\x2c\x86\xdd\xc6\x75\x1c\x08\x36\xf1\x5b\xa6\x1b\x0b\xcd\x36\x92\x65\x1c\x3b\xe8\x4e\x25\xa5\x09\xe7\x77\xfa\x00\x2c\x21\x26\x28\xc8\x1a\xd7\xcc\x02\x26\xfb\xa3\xc8\x54\x1a\x22\xf1\x9b\x73\xf6\x4e\xda\x83\x83\x52\xbb\xe7\x65\xac\x3d\xc9\xde\xd9\x61\xab\xd3\xed\xe9\x86\x10\x86\xeb\x67\x2a\x28\x8a\x60\x3c\xfb\x98\x1e\x48\x3f\x58\x78\xca\xa4\x73\x2c\x7c\xe6\xd6\x53\xcb\xc7\xcd\x3f\x9d\xe1\xc8\xa2\xfd\x20\x8e\xd5\x7e\x22\x80\x4b\xce\x14\x4e\x37\xd5\x01\xc3\x73\xc2\xf0\x1e\x31\x80\x54\x5b\xc3\x6d\x90\xee\xf9\xaa\x6f\x0d\xb7\x41\x76\xc7\x21\xdb\x76\x00\xd8\xda\x08\x5b\xc3\xc7\x3b\x4c\x64\x7e\x7c\x2f\x32\xdf\x8b\xcc\x7f\x6d\x91\x19\x45\x7b\x81\xb3\xf7\x5d\x85\x7b\xf9\x7b\x9e\x26\xd9\x62\x6c\xca\x9b\xbf\xf0\x44\x75\x75\x98\x65\xa9\x2d\x02\xf3\x34\x25\x89\xba\x2a\x0a\x36\x58\x43\xc8\x74\x64\x4c\x40\xc7\xa7\x52\x49\x53\x64\xe4\x22\xae\x77\x8d\x9f\xc0\x20\x0c\xa5\x4b\x47\xc6\x8e\x45\x61\xf0\x92\x0d\x5d\x13\x09\x96\x45\x60\x10\x86\x1e\x1b\x5b\x22\xc6\xcf\x0b\x15\xda\xba\x75\xb0\x06\xe3\xc4\xac\x38\x0c\x7d\x32\xb7\x6f\xe0\x39\x0f\x0a\x2e\x21\x6a\x47\x24\x99\x76\x55\xff\x05\x8c\xb7\x6b\xbe\xfd\xdc\x74\x2e\xa0\xf0\x6b\x74\xd3\x9d\x02\x7d\x4f\x94\x84\x5c\xcd\x24\x61\x7b\xa8\x6e\x9a\x65\x3d\x21\x89\xe6\xae\x4c\xcc\xc9\x87\xff\x12\xc2\xa2\x06\x10\xf8\xc1\x1e\x26\x15\x2a\x7b\x04\x5e\xb7\x97\x3c\x60\x13\x55\x9e\x00\xcc\x29\x3e\x1e\x94\x0a\xec\xbc\x48\x49\xb5\x4c\xac\x91\xfd\x11\x95\xf6\x1d\xd9\xc7\x2e\xb0\x2e\x16\x51\x3f\xca\xff\x11\xc4\x51\xf8\x9e\xe6\x8b\x34\xc9\xa9\x68\xca\x79\x7c\xe7\x8c\xc1\xdf\x5e\x87\xaf\xb1\xfe\x61\x72\xe6\xad\x75\xd7\xa9\xf4\xda\xed\x5f\x69\xe5\xdc\x65\x93\x33\x58\xbe\xe7\x82\x6b\x08\x5f\x86\x68\xbc\x2f\xfa\x00\x4e\x23\x70\x82\x13\xc4\x5e\x4f\x85\x3a\xdf\x10\xbf\x28\x01\x94\xa5\xf5\x93\x7c\xf0\xad\xe1\x36\xe8\xd1\xc4\x8a\x6c\x0d\x77\xc0\xea\xad\x51\x90\xef\xfb\x0d\xff\x7e\xc3\xff\xf3\x6e\xf8\x7a\xbf\x57\x62\xf9\x1d\xa9\xc8\x1a\xea\xaa\xd8\x89\x27\xb3\xc0\x72\x21\xeb\x0f\x20\x73\x55\x75\x9a\x84\x43\xef\xa6\xb0\x1e\x4c\x3e\x88\x12\xd0\xfb\xe8\x10\x82\xc0\x94\xc6\xd0\x88\x38\xee\xdb\x3f\xb9\x7a\x09\x3f\x32\x83\x6d\xde\x7e\xa7\xcc\xe1\x0e\x34\xd8\x3b\x09\xa5\xe4\x02\x30\xf6\xbd\x26\xd2\x95\xb3\x99\xea\x6d\x40\x38\xfb\xf5\x57\x6d\x3e\xf5\x1c\x45\x3d\x51\xce\xba\xd5\x09\x46\x91\x47\x0d\x82\xdc\x3e\x13\xcb\xcf\x32\x8f\xef\xbd\xb7\x47\xda\xa8\x4f\x6d\xf2\xe0\x81\xe1\xc7\x19\x9d\x9b\x79\xb3\x86\xb3\xff\xeb\xae\xb5\x0d\x57\x35\xe8\xf1\x0c\x4d\x3a\x90\x58\xb2\x5d\x43\x1e\x77\x18\xed\xd9\x19\xac\x8a\x18\x58\xee\x69\x1a\x68\x4f\x1c\xde\x39\x42\x39\xa8\x42\x23\xd2\xf2\x48\xed\x55\x03\xe9\x51\xc5\xf3\x12\x9e\xa2\xf8\xd1\xda\xfb\xb2\x29\x08\x43\x49\xc3\xb9\x3e\x86\x63\xda\x90\x69\xd7\xaa\xa6\x52\x7a\xe2\xa4\xe2\xaf\xb2\xf2\x64\xaf\x8f\xeb\x37\x27\x14\xf4\x0a\x71\x95\xd9\xc7\x9a\x2a\xa5\xfd\x51\xfd\xf9\x44\x8b\x99\x54\x37\xeb\x4e\x9a\x7e\x2f\x6a\x55\xa9\x13\x47\xcd\xa1\x11\xa0\x55\xa5\x0d\xe6\x95\x73\x8b\x46\x93\xca\xf9\xcd\xdd\xcd\xa8\x5d\x5f\xbd\xa2\x46\x32\xbc\xbb\x98\x5b\xce\x7b\x2d\xb5\xb2\xe0\xac\x42\xdb\xa8\x78\xac\x39\x79\xae\xde\x8a\x77\xac\x74\x3a\xf7\xe3\xb8\x72\xba\x00\x48\x5c\xf4\xac\x4c\x60\x5c\x15\x5a\xd3\xc1\xd5\xa9\xcd\x78\x14\xe8\x2a\xd5\xca\xa8\xad\x8a\xdc\x94\x9b\x1c\xb0\xfd\x93\x93\x3e\xa5\x45\x2e\x8c\x57\xe2\x4b\x12\xd2\x45\x9c\x5e\xd2\x50\x9a\x08\xc2\xf3\xc1\xf1\x2c\x88\x12\xfb\xb9\x1a\xd4\xf6\x63\x9a\xc9\x1e\x79\x7c\x0f\xc8\x03\xab\x8f\x24\xe5\xba\xbc\x56\xaa\xc5\x35\xc3\x43\xee\xb1\xbc\xdc\xd0\xcf\xda\x4a\x5a\xc4\x06\x0f\xb2\x25\xa4\xb0\xd4\xe4\x0b\xf1\x9a\x21\x90\x8c\xa3\xe6\xfd\x11\x82\x94\xef\xc9\x87\x65\x90\x3f\x18\x90\xf3\x20\xe2\xea\x72\x10\xb9\x16\x85\x56\xc1\xca\x9b\x32\x73\xde\xc5\x52\x50\xf1\xa2\x75\xc7\x68\xd7\x74\xbc\xbc\x4e\xe1\x69\xb2\xd1\xbe\xbd\x2b\x41\x7f\x37\x36\x76\xcd\x63\xd3\x60\x40\xf2\x22\x5d\x70\x5d\x6d\x94\x4c\x49\x30\x61\x5d\xf9\x66\x93\xcf\x55\x4e\x3a\x45\x34\xa7\xe9\xb2\xe8\x3a\x47\x47\x8e\x80\x1f\xc8\x37\x9b\xde\xc3\x22\xef\x7d\x9f\xd5\xfe\xb3\xa8\x5c\x87\x54\xe8\x92\x2f\xd7\x9e\x33\x9d\x8d\x40\xfe\x60\xcf\x7b\x0e\x55\x33\xe2\x3d\x6d\xea\x93\x9f\xf6\x0b\xac\x18\x13\xdc\x97\x04\x7c\x65\x8c\x19\x61\x83\x8f\xe0\x11\x93\x98\x97\x49\x68\x63\xa0\xed\x3b\x7c\xd2\x18\x39\x14\xc1\x7f\x8e\x37\xe2\x1b\xb7\xca\x96\x1f\xae\x59\xf9\x13\x71\xb1\x66\x50\xcd\x94\x16\x1f\x75\x53\xef\x39\xa9\x69\x8e\x82\xba\xf1\x2a\xc8\x67\x98\xa8\x7a\x92\x30\xbb\xfe\x23\x7c\x34\xe9\x08\x00\x3f\xb5\x79\x0b\x79\x3b\x08\x11\x8c\x44\x5d\xfd\xb1\xb9\x00\xcd\x1e\x41\x98\x23\x7f\x77\xe4\x5f\x99\xf3\xf6\x27\xca\x79\x7b\xd9\x5f\x34\xe9\x98\x14\x77\x75\x45\xd6\xa1\xc5\xca\x62\x44\xb1\x6e\x0f\x6d\xe2\xbf\x9b\x2c\x01\xfc\xd7\x70\x39\xd8\x43\x4a\x43\x14\x21\x7a\xa7\x72\x66\xe4\xdf\x60\xa0\xee\xf9\xe2\x74\x8a\xa8\x16\x8e\x15\x92\x8d\xaf\xb7\xbb\x35\xcd\x13\x43\x54\x53\x1c\xb5\x64\xaa\x1b\x54\x36\x18\x10\xbe\x59\x49\x71\x21\x48\x42\x22\x6e\x46\x48\x30\x0d\xa2\x44\xac\x9c\x73\x2a\x02\xfc\xd5\xfc\xf9\x65\x4f\x7b\x03\xac\xa9\xc1\x96\x75\x9c\xed\xbf\x66\x48\x63\xee\x96\x4d\x5c\x0a\xb2\x2d\x81\xed\x8e\x39\x1d\xa7\x49\x48\x18\xc3\xad\xad\x04\x91\x6e\x3d\xb1\x12\x83\x23\x82\x2e\xac\x69\x87\xbd\x5e\x8c\xee\xb8\x43\xd8\x75\x3b\x12\x25\xc4\x89\x16\x71\xca\xbc\x48\x33\x1a\x2a\x37\xee\x5c\x02\x01\x8d\xcf\x34\xc8\x49\x30\x67\x1b\x52\xdf\xcb\xaf\xed\xbf\x52\xfe\x6d\xff\x79\xbc\xcb\xdf\x45\x17\xab\x7b\x78\x5d\x9a\x5b\xc6\x31\xdc\x12\x36\x24\xd2\x4e\x36\x3d\x50\xa0\x2b\x06\x49\xe8\x3f\x06\xec\x98\x7d\xa9\x7c\x69\x58\x52\x9c\x05\x56\x73\x68\xb0\x2b\xc5\x07\x06\x38\x55\x05\xa3\xc8\xb8\x5c\xe0\x2f\x8a\xa8\x3c\xbe\x43\x5a\x30\x8a\xc8\x1e\x83\x94\x72\xd6\x43\xae\x09\xad\x1f\x93\x3e\x21\x25\x24\x40\xa2\xa9\x28\x2e\x6b\x91\x63\x4b\xe8\xb9\x4a\x92\x63\x4a\x2e\xaf\x31\x31\x58\xba\x91\x2d\x69\x53\x10\xc4\xdd\x15\x8b\x6e\x57\x14\xb5\xe5\x60\x43\xb2\x10\xbe\x4e\xa4\xa2\x38\x74\x4a\xfb\x24\x65\x01\xa1\xa4\x65\x7d\xfc\x93\x49\xaa\x2d\x3d\xf1\x50\x68\xa0\x27\x82\xa1\xd4\x77\xfd\x42\x2a\xb6\xe8\x6f\x65\x0d\xec\x4f\xfd\xe0\xd2\xb5\x3a\x45\x62\xfa\xeb\x48\x3a\xe8\xa9\xd9\xc7\x1c\x6c\x30\xe0\xa1\x15\xb5\x95\x85\x51\xa9\xb6\x95\xf8\x72\xbd\xcb\x80\x25\x96\xd6\xcd\xb6\x05\x62\x50\xc5\x70\xc6\xcd\xe0\x2d\x0e\x10\x32\x7e\x94\x10\x47\x63\x0a\x57\x0d\xda\x5e\xc3\x8a\xfe\xe7\xb3\x1d\x01\xfb\x8f\x72\x8b\x11\xe2\x58\x8d\xe4\xfd\x45\xba\x30\x1c\xcc\x99\xdd\x8b\x83\xbc\x10\x90\x4e\xd5\xfe\xee\x70\x42\xea\xb0\x82\xe0\xbc\x68\x5d\xbd\x38\x81\x38\xb4\x90\x6e\xf7\x49\xa3\xb0\xa6\x4b\xac\x21\x01\xdc\xe7\x41\x49\x7e\x20\x9b\x76\x6d\x62\xa6\x25\xed\xef\xcb\xb5\x5c\xaf\x05\x90\x7f\xb7\x52\x09\x22\x34\x59\xcc\x52\xaa\xd3\x94\xa9\x1d\x1e\xd6\xba\xd9\xe5\xfe\x22\xb8\x0c\x46\x31\xf5\x75\xcf\x3d\x0e\x70\xfb\xa9\x9c\x26\xa1\x0e\x48\x95\xa4\xc9\x23\x51\x09\x46\x87\xbd\x4d\x5c\x97\x4d\x3d\xf8\xf6\x63\x9c\xd1\xaf\x82\xed\xc8\xa5\xd2\x83\x11\xa3\x5a\xe5\x04\x81\xed\xdb\xc6\x1e\xaf\x68\xd7\x9c\xc4\xd2\x1b\x41\x7c\xa2\x35\x74\x00\x52\xee\x83\x58\x08\xa6\x96\x20\xa4\xe4\x3c\xc8\x95\x40\xb9\x66\xe2\x8a\x2f\x6d\xb8\x7a\x45\x47\x18\x6d\x98\x65\xdd\xbf\xce\x82\x7c\xe6\x43\x3a\xeb\x35\xcd\xb2\xb2\x9b\x48\x7c\xe5\xe8\xbb\x57\xac\x92\x78\x98\x38\x1a\x86\xfc\xda\x0b\x71\x5d\xd6\x13\x7f\x5b\x25\xc7\x2e\xb2\x07\x65\x4a\x84\xaf\x52\x09\x71\x12\x65\x79\x51\x2e\x20\xae\x28\xe3\x95\x68\x40\x7c\x6a\x0f\xdf\xf5\xab\xf1\x55\xe7\xf8\x12\x02\x6d\xf2\x81\xd7\xcd\xb3\xd5\x58\x53\x94\xd7\xa2\x7a\x95\xa1\xfb\x79\x9a\xd2\xc9\x73\x20\xa1\x2b\x13\xd8\x95\x9b\x20\x3b\xdf\xbe\xe0\x76\xa5\x90\x24\x3e\x0d\x03\xb4\x1b\x0b\x5e\xb6\xd6\xac\x4e\x3b\xeb\xd9\xd4\x45\x4d\xd7\xa6\x0c\x34\x51\xf5\x0f\xd6\x06\x03\x6b\x07\x36\x2e\x70\xb4\xcb\x63\xa4\xbe\xb4\x2a\xef\xf0\x7d\x79\x30\x30\x7c\xe9\x96\x86\x9d\x1e\x8f\xc1\x2d\x6e\xca\xe3\x34\x45\xc9\xb4\x42\x36\x33\xd5\xd8\xe6\xc8\xf9\x24\x5e\xbb\x9c\x08\x8b\x43\x55\xa2\x10\xf9\x82\xa4\xae\xa6\x12\xd1\x84\x24\xa9\xae\x81\xb1\xb7\x45\x90\xe7\x34\xec\xb1\x2a\xb4\xeb\x3b\x06\x91\xa3\x25\x6d\xf2\x32\x45\x78\x30\x03\x16\x3a\x0d\x73\x48\x9f\xef\x54\xd3\x66\x95\xac\x2c\x43\x69\x4b\x79\xad\xad\x2c\x66\xc8\xb5\x24\xc4\xaa\x81\x08\x61\xd2\xa8\x40\x75\xa9\x27\x0b\x8c\xe8\x38\x58\xe6\x94\x9d\xc4\xc3\x34\x69\x17\xe4\x3c\x48\xc0\x28\x29\x5f\xa4\x51\xcc\xaf\xc3\x93\x82\x66\x93\x60\xac\xbc\x63\x37\x38\x8a\x37\x39\x6e\xdb\xfb\x54\x3d\x43\x24\x8e\x7f\x5d\xb5\xa8\xd1\xe2\xfc\x89\x16\xdc\x5d\x33\xdb\x20\x7b\xe4\x7c\x16\x8d\x67\x60\x35\xc0\xd6\x77\x91\x8a\x7d\x8c\x2c\xe2\x65\x5e\x7f\xf7\x2a\x18\x41\xcd\x04\x6b\xee\xe1\xb7\x64\xaa\x91\x61\x57\x17\x54\x55\xb1\x7a\x01\xf2\x36\xc2\x63\xb9\xe0\x88\xac\x95\x6f\x24\xc8\x54\x09\x31\xe6\x53\x87\x3e\xb7\x48\x6f\xce\x7d\x3d\xc7\x1e\xef\x79\xb7\xc1\xfd\x79\x19\x6f\x72\x4e\xc3\xde\x63\x70\xc9\x53\x16\xdf\x81\xd8\xdd\xfe\xb4\x61\x38\xc7\x9f\xfb\x7a\x85\x78\x4e\xd3\x5e\xbb\x25\x8b\x6e\x77\x95\xfd\xb3\x69\x2c\xd1\x1a\x7e\x5b\x66\x02\xad\x4c\x1a\x5a\xc3\xed\x1d\xd7\x26\x5a\x8c\xbc\x35\xdc\xd9\xba\x3e\xed\x6d\x3f\xb9\xb7\x7d\xba\xb7\x7d\xfa\x6b\xdb\x3e\x21\x63\x67\x61\x03\x79\x07\xd6\xce\x25\x7e\x2c\x85\x75\x25\x7f\x98\x75\x34\x91\x97\xce\xfb\xd9\x34\x1f\x96\xa8\x6e\x90\x90\x27\x8e\xb0\xa2\x12\x1c\xfb\x4e\x6e\x27\x8c\x7d\xca\x4a\x09\xb6\x71\x02\x3e\xdf\xf3\xf5\xe1\xfd\xbb\x03\xce\xdc\x6f\xd3\x01\x1e\x70\x09\x58\x2d\x85\x17\x8c\x45\x4a\xde\xbf\x3b\x10\x17\x05\xfe\x0e\x88\xf7\xe8\xe0\x45\x51\xb7\x3c\x4b\x73\x7c\xfd\xe5\x36\x7e\x70\xf4\xf6\xed\xcb\x83\x8f\x87\x47\x6f\xc9\xcb\xf7\xef\x8f\xde\x0f\xc9\x81\xd2\xff\x8e\x79\x95\xfc\x48\x1f\x52\xd2\xde\x20\xac\x3e\xb2\xd1\xee\xfb\xfb\xa0\x5d\xde\x34\x1d\xbb\x7a\x68\xcf\xb5\x08\x05\x5b\x3d\x11\xaf\xcc\xdf\x84\xb4\xa4\x1d\x12\xdb\x2a\x18\x0d\x13\xde\xa5\xd1\x3c\x0f\xa6\x94\xec\x91\xf5\x75\xf1\xd4\x90\x6d\xeb\xe2\x77\x9f\x87\x8c\x75\x52\xfa\xb2\xd8\x33\xe2\x4d\x1e\x12\x35\x5d\x7f\xff\x70\xf4\x16\x66\x25\x53\x5d\xf2\x84\x59\x15\x7d\x73\x1e\x93\x69\x1c\x88\xaa\xcd\xd1\xea\xd9\xfc\xc8\xef\xab\xf1\x78\xe7\x79\xd3\x29\xfd\x78\xf8\xe6\xe5\xd1\xf1\xc7\x21\x11\xb7\xde\x8c\xb8\x58\x27\xe7\x39\xd9\x20\x6d\xf6\x5f\x30\x9e\x31\x8e\xd1\x36\x62\xda\x08\x3f\x92\xdf\xde\xef\x56\xf7\xbb\xd5\x5f\x7b\xb7\x42\x9b\x15\x3c\xbb\xfc\xa3\x9a\xe9\x36\x7f\xcd\xde\xe8\x11\xfd\x1d\xbe\x65\x97\x4e\x87\xd8\xfa\x57\x87\x33\x1c\x93\x29\x37\x8e\x21\xe2\x91\x2d\xb4\xa5\x0f\x0b\xb6\x15\xf2\xd7\x7e\x08\xbf\x90\xb6\xbc\x48\x93\x8e\xf3\x79\xec\x0a\x52\xf1\x1e\x39\x4f\x93\x6e\xcd\x1b\x7a\x94\x99\xa4\xc9\xe5\x3c\x5d\xaa\x16\x55\x42\xc9\xe9\x4d\x22\x6d\x4a\x25\xae\x68\xc8\xe5\x01\x88\x62\xe0\x84\x6b\x12\x69\xea\x78\xf6\x3c\x4d\xe3\x6b\x08\xaf\x1a\x82\x0f\x72\xbe\x49\x50\x0e\x19\xa2\xd9\x81\x07\x22\x34\x34\x3c\xa6\xcb\x13\x1f\x44\x23\x60\x8b\x52\xd4\x3e\x58\x33\xa6\x09\xbb\xdf\x62\x10\xa6\xe7\x28\x5e\xaf\x1d\x81\x01\x21\xdf\xbd\x13\x89\x3c\xa2\x42\xd4\x17\x35\xc1\x05\x87\xf8\x5d\x62\xef\xea\x2f\xaf\x0d\x96\x4b\xaf\x88\x31\xb6\x39\x7d\x86\xdc\x07\x38\xb8\x31\xb2\x70\x1d\x6a\xf7\xe0\xde\x70\x41\xde\x0a\xca\x51\x87\xaa\xab\xf2\x12\xc4\x29\xd1\xf5\x50\xde\xd1\xf4\xda\x7c\x74\xb0\x42\x3d\x43\x2b\x84\x43\xf3\x8a\x71\xe1\xa2\xd5\xf4\xb0\xd2\x88\xa4\x2b\xf5\x1b\x0d\x27\x8f\xa6\x49\x50\x2c\x33\x7b\x38\x38\xbd\x6c\x3c\x18\xa6\x7c\x3c\x0a\xaa\x6a\x40\xe0\xc1\xa0\x79\xff\xc5\x13\x07\x49\xde\x82\x23\x05\x49\xa8\x54\x4b\x45\x0a\x41\x89\x27\x51\x12\xc4\x7e\xb3\x67\x5e\x87\xcf\xa8\x14\xaf\x6b\x2b\x4b\x54\x6f\x20\x45\xe6\xd1\x33\x9a\x5d\x16\x33\xae\xb2\x9e\x8f\x22\x60\x19\x29\x8f\x12\x0d\x7d\x13\x71\x16\x2a\xb1\xe5\xf1\x0d\x22\xba\xe3\xb8\xb6\x53\x8b\x5b\xfd\x42\xaf\x00\xef\x3d\x88\x68\x7f\x1d\xca\x41\x47\x9d\x6b\x11\xa9\xd7\x5c\xb7\x76\x1e\xb7\x9f\xa2\x72\xfe\xb2\x55\x38\x17\xe4\x8e\x3a\x25\xb5\x77\xba\xae\x4a\x53\xcc\xd3\x47\xd9\xb1\xdb\xb2\x74\x14\xc3\xa2\x92\x9f\x83\xe7\x65\x11\x4c\x5b\x94\x3f\x89\x20\xc6\x94\x65\x0d\x20\x80\xf0\xfc\x31\xba\xd1\xc9\xc9\x32\x8e\x4b\x9e\xb8\x68\xcd\x22\x71\x6f\xff\x4d\x85\x30\xd4\x57\x16\x9a\x11\x32\xad\xd1\x9c\x55\x5c\xf7\x0b\xec\x3b\x8f\x63\x3a\x7c\xfb\xea\x91\x33\xfb\xea\xbc\x6b\x07\xd7\x5b\xa9\x36\xe8\x7b\x0d\xc5\x99\x44\x32\x4e\x93\x71\x50\x74\x8c\xd9\xef\x96\x3b\xb2\x29\xe5\x7a\xc2\x8b\x4d\x39\xd7\xb3\x77\x5b\x5a\xc6\xe1\x42\x7e\xf7\xe0\xf2\x30\xc1\x15\x84\xe5\x10\x9c\x10\x78\x2d\xa1\x6a\xf6\xc1\x03\xd0\x37\x98\xbd\xa8\xde\xa6\xcb\xbd\xef\x00\x0e\xee\xd0\xfd\x4e\x90\x4d\xad\xd5\xa5\xc5\xc7\x67\x46\xc9\x21\xfe\x12\xae\x79\xb6\x90\x2b\x14\x31\x3e\x71\xff\xa2\xea\xb5\x9f\x6a\xf1\xc9\x24\x5f\x94\x94\x86\xeb\xdb\xee\xee\xb2\x95\xf9\x4b\x1a\x25\x9d\x56\xcb\xad\x5c\xbd\x8a\xe3\xe4\xc6\xf1\x84\xaf\x37\x40\x36\xec\xb0\x65\xde\xed\xe1\x1e\xe1\xab\x9a\x24\x2d\x0e\x8d\xbe\x2a\x14\x7a\x1c\x0e\x69\xe0\x86\x6d\xc3\xb3\x85\x6e\xcf\x6a\x05\xb7\xaf\x36\x12\xc4\xb5\xd3\x65\xb1\x58\x16\xaf\xd3\xa9\x66\xd7\xc2\x19\x0f\x5a\x2d\xd2\xfb\x0f\x77\x34\x83\xc4\x32\x13\x4c\x73\x6b\x18\x93\xed\x07\x8a\xc3\xf0\x5b\x2e\x83\x9f\x66\x34\x5c\x8e\x29\x9a\xab\x60\x3c\xee\x11\xe1\x8b\x12\xf3\x93\x60\x3c\x3e\x11\xc9\x9c\x27\x32\xa4\x88\x6f\x49\xe5\xcf\xcc\x29\xeb\xe7\xb3\x68\x52\x74\xba\x64\xe8\x60\x54\x66\x39\x4a\xab\x60\x3c\x96\x5a\x2a\x6e\xed\xcd\x49\x9b\xc6\xb4\xa0\x72\x1c\xda\x4b\x92\x99\xce\xa9\xea\x06\x2c\x03\xdd\x5f\x89\x87\x25\x62\x69\xb3\xad\x9e\x8b\x71\xa5\x9e\x15\xee\x4a\x2e\x32\x1a\xae\x16\x7e\x3c\x9e\x1b\x6c\xe9\xe7\x8f\xee\x92\x69\xbb\xde\x25\x53\x55\xf1\xad\x72\x23\x3b\xb3\x02\x62\x48\x80\x86\xf3\x07\x5b\xec\xb0\xfd\x3e\x39\x02\xe5\x1f\xca\x01\x54\x29\x2d\x63\xdb\xff\x06\xaf\x1a\xad\x67\x6d\xde\x27\x8d\x95\xd4\xf8\xb5\xbc\x4d\x31\x50\xf3\xe4\x5a\xc6\x01\xa5\x81\x21\xb4\x74\x82\x00\x4e\x0d\xea\xf5\x01\x60\x07\x56\x9a\x28\xbc\xa0\x27\x8a\xdd\xf3\xb6\x4f\x4b\x07\x60\x58\x4d\x78\xef\x84\x0d\x5c\x22\x97\x58\x55\x57\xc2\x75\x8e\xb2\x6e\xe8\x1b\xeb\x69\x13\x05\xfc\x6d\x9d\x5d\x0e\xfc\xba\xc9\x37\x9c\x06\x3d\xfa\xbf\xea\x48\x22\x38\x88\xc8\xda\x60\x40\x3e\x1e\xbd\x38\x1a\x92\x8c\x72\x8b\xac\x1e\xc9\x53\x61\x3a\xa3\xae\xb8\xb4\x31\x4e\xc0\x35\x5d\x7d\x56\x2e\x2a\xda\x39\x49\xe8\x98\xe6\x79\x90\x5d\xb2\xc5\x02\x21\xb0\x73\x46\x6e\x6d\x70\x58\x0c\xee\xa2\xc9\x79\x9a\x7d\xe6\x52\xde\x7c\x19\x17\xd1\x22\x46\xa1\x1c\xcc\xe0\x29\x7e\xff\x46\x83\x87\xc4\x6b\xcc\xfd\x8d\xb4\xe5\xe6\x75\x98\x66\x0c\xb2\x79\xc3\x88\x54\x37\x46\x43\xbe\x71\x98\x27\x13\x55\xaa\x2f\x71\xe4\xf3\x60\xb3\xce\x3a\x77\xe2\xc2\x9e\xfa\xce\x0f\x65\xb0\x16\x3b\x25\x8e\x81\xa3\xd9\x4f\xe1\xd0\xc9\x57\x53\x8d\x1d\xa4\xb7\x9e\xd2\x23\x94\xae\x5f\x10\xbc\x3d\x26\x07\xc0\x73\xe4\xe6\x39\x3e\x6c\xf0\x1c\xc5\xf4\x84\x49\x8f\xd9\x45\x8f\xe9\xa7\x28\x96\xd3\xc2\x0a\x15\xe3\x73\x72\x55\x79\x10\xab\x9e\xee\x88\x56\x8c\x57\xc3\x78\x86\x5c\x46\x2f\x44\x47\x39\xb9\x5c\x79\xd8\xaa\xe0\x1d\x0c\x9c\x20\xc3\x51\x7a\xd1\x37\xd8\x91\xfe\xd8\x25\x12\x40\x72\x21\xf8\x7f\x57\xa6\x2a\x96\xc3\x7f\xa8\x74\xc4\x68\xe4\x4f\x53\x8e\xa4\x17\xe2\x7d\xb7\xcb\xcd\x39\x1a\xb4\x6b\xa2\x12\xfe\x5c\xc2\x91\x5b\xc3\x1d\x70\x61\x84\xbd\x86\x33\xc6\xfc\xdd\xfd\xcd\xe8\xfd\xcd\xe8\x5f\xfb\x66\x54\x5c\x8b\x8a\x37\xbf\xff\x15\x01\xf6\xee\xd4\x65\x38\x1c\x02\x1e\x92\x83\x34\x39\xa3\x8c\x15\x05\x22\xe6\x31\x9c\x83\xe1\x2c\x00\x81\x8b\x65\x24\x17\x46\xc0\x41\x9c\xa7\x24\x88\xe3\xf4\x3c\xe7\xf1\xd9\x41\x51\x97\xf7\xd7\x58\x45\x52\xf0\x7f\x13\x5d\xd0\xf0\x9a\x67\xad\xb9\xf7\x1a\x6b\xe2\x46\xb5\x48\xed\x28\xc7\x42\x65\xa9\x0e\x9c\x1d\x53\x25\x4a\xae\xae\x64\x84\x74\x9d\xd1\x56\x3a\xd4\x76\xd7\x56\x06\xf0\xb3\x9c\x10\x91\xb8\x62\x96\xf7\xa1\x23\xf5\x8b\x46\x43\x5c\x0f\x71\x34\x01\x55\x73\x17\x6a\xdf\x74\xea\x04\x48\xc1\xf7\xf1\x93\x56\xe3\xce\x48\x46\x51\x52\xed\xc0\x91\x8b\x89\x9a\x8c\xd3\xca\xcb\x1f\xdb\x12\x36\x55\xfa\x7d\x71\xd8\xea\xb1\x49\x38\xa3\x59\x34\x01\xc7\x1e\x19\x1d\x07\x8c\xe3\xa0\x48\x35\x0f\x1e\x90\x38\xf8\xf5\x92\xc4\x69\x10\x92\xf0\x32\x09\xe6\xd1\x98\xa4\x09\xcd\xa1\x35\x31\x21\xba\x21\x11\xcd\x3a\x55\x7a\x02\x80\x92\x06\xf6\xb2\x71\x07\x8a\xcd\xd6\x94\x16\x47\xea\x90\xec\x71\xe1\xcc\x26\x46\x0b\xac\x75\xfe\x01\xb0\x32\x41\x4c\x89\x3c\x26\x97\xdf\x7a\x18\x9a\xfe\xd2\xab\x17\x9e\x9d\x9f\x47\x10\xb0\x04\xf5\x8a\x80\x0e\x22\xa7\xfc\x04\x3d\x74\x9e\x56\x71\xe1\x7d\x99\x51\xa1\x5e\xec\xc1\x05\xde\x98\xaf\x0e\x7e\x38\x9e\xd1\x0b\x9f\xda\x40\x6b\x4d\xad\x04\xcb\x15\x65\x83\x22\x86\xe6\x53\xc4\xd5\x2e\x55\xca\x5b\x0a\x7f\x19\x85\xfb\x99\x88\x4f\xce\xaa\x12\x8b\xac\x4b\x86\x72\xbd\x09\x30\x57\x56\xf2\x5d\x13\x78\xde\xd7\x41\x37\x87\x56\xb7\x7b\x0e\x1c\x5b\x02\x1a\x8a\x7d\xb9\x30\x45\x8a\xeb\x71\xf3\x03\x19\x96\x59\x02\x05\x38\x28\xb3\xdd\x1a\xdc\x5f\x0d\x57\xba\xd6\xea\xab\x72\x5d\x5f\xef\x6e\x52\xa3\x28\x65\xea\xa7\xd0\x41\x87\x53\x60\x3e\x63\x14\xe8\x41\xb8\x45\xea\x52\x55\xb3\x1f\x86\xfc\x59\x84\x52\xa2\x05\x49\x48\x72\x5a\xe4\x64\xb9\x80\x0c\x71\x1a\x01\x96\x11\x15\x34\x63\x7b\x47\x7a\x26\x84\x2d\xe1\xc7\xb4\xbf\xb6\x86\x9e\x46\xbc\x4e\xa7\xf9\x7e\xf1\xa1\x08\xb2\x62\xcd\xd6\x34\xe6\x34\x9e\xa8\xc4\x89\xfb\x80\x59\xb0\x70\xb3\x16\x23\x50\x18\x8d\x27\x8e\x13\x1f\xf9\xca\x6e\x4a\x0b\xae\xcf\x62\x85\xad\xa7\x76\xa0\x5f\xd0\xc3\xcc\xa1\x7b\x44\x9e\x3c\x2d\x9e\xc1\x5a\xe9\xfb\x18\x07\x64\x4c\x69\xd1\xb1\x1e\xfd\x08\x4b\x46\xe7\x94\x33\x18\x88\x17\x34\xf0\x4c\x94\xf5\x51\xa0\x0d\xcc\x26\xe1\xa2\x5b\x26\x4a\xb3\x23\x70\x85\xd1\xef\xf7\xc9\x2f\x4b\xee\x09\x98\xb5\xc9\x78\xaf\x73\x5e\x2e\x79\x19\x59\xf1\x2a\xf2\xda\x7e\x02\x6b\xad\x74\x35\x0c\xff\x19\x93\x67\x7a\x0f\xa6\xdc\x90\xb3\xee\x9d\x26\x7f\xbc\x63\x9a\x7d\x1a\xfd\xab\x77\xc4\xfa\xf5\x48\x77\x91\xc6\x31\x27\x1f\x3f\xd9\x0a\xda\xd4\x60\x36\x5d\x2a\x95\x08\xa8\x6d\x93\x37\xca\x0c\xd7\x20\x96\xb4\x84\x5c\xc4\x8c\xa6\xce\x9c\x4a\x23\x0b\x46\x7a\x72\xac\xbe\x49\xf0\x3d\x9b\xf2\xd1\x44\xda\xf8\x24\xdf\x94\x3a\x6e\x46\x19\xda\x4c\x19\x86\xa6\x95\xd7\xcf\xac\x04\x5d\xc9\x50\x16\x72\x49\xe7\x56\xe8\xb9\x1d\x91\x96\xea\x03\xa0\x4f\xb6\x37\x6a\xc6\x78\xde\xa5\x71\xcc\xf8\x8c\xee\x09\xa7\xc1\x21\x2f\xc2\xce\x69\x74\x4e\x93\x02\x8e\x9c\x7d\x46\x71\x30\x34\xbd\x97\x2c\x84\xa1\xfd\x09\xc7\x14\x90\xe3\x61\x78\xda\x93\x57\x54\x46\x72\x4f\x13\xa3\xc8\xc1\x7e\x8c\xb8\x82\x18\xe8\x97\x6d\xd6\x32\x6c\xa1\x43\xe2\x96\x4c\xd6\x23\x4e\x7c\x0f\xb9\xdc\x3c\xb7\x03\x3d\x71\x9a\x3a\xc8\x28\x8c\x09\xec\xb5\x0f\x3c\x2f\x1d\x81\xd9\x71\x0d\x36\xba\x70\x35\xf0\x81\x34\x7c\xab\xa8\xca\x4a\x75\x5d\xa5\xca\x1e\xbf\x52\xcd\xec\x0c\xb2\x25\x20\xa5\x2e\xe3\x4b\xad\x31\xb5\xb0\xa9\xc5\x60\x4b\xf4\x45\xd0\x0e\x1a\xcc\x04\x04\x29\x67\xde\x7d\x32\xa6\x56\x88\xb0\xac\x51\x19\x62\xcb\x3d\x28\xcb\xd7\x6c\xcf\xc9\xc2\xd7\x4e\xea\x77\x69\xbf\xfb\x09\x3d\x17\xb7\x4e\x18\x07\xd8\x59\x18\x67\x92\x51\x68\xf8\xc6\xf3\x33\xc7\x9a\x65\xdf\x19\x8f\x3c\x62\xee\x78\x54\xcb\x07\x89\xe0\xc8\xe2\x5c\x58\x41\xbd\x96\x47\x52\x97\xbd\x54\x94\xf5\x77\xa3\x5a\xef\x6c\x2c\x6d\x46\x04\xa1\xeb\x08\x10\xfb\x6a\xc8\x28\x5c\x32\xb0\x33\xc7\x82\x26\x21\x18\xb8\xa9\x49\x0e\x72\x50\xb4\x24\x39\xa3\x50\xe5\x0c\x46\x57\x94\x4e\x00\x98\x15\x62\x52\x4f\x97\x2b\x57\x54\xeb\xcb\x24\xc8\xf3\x68\x9a\xd0\xb0\xef\xf6\xd1\xa6\x28\x1f\x4f\xf6\xcd\x8e\x92\xb1\xc6\xa3\x9a\x09\xf2\x36\x83\x4d\xc6\xd0\x48\xb4\x3d\x31\x89\xb1\x74\x18\xc4\x19\x0d\xc2\x4b\xfd\x60\x5d\x0b\x8a\xf9\xed\x29\xcd\x14\x64\xa5\xf4\x5a\x37\xae\x68\xd2\xb1\x5a\x53\x4e\xe0\x36\x5d\x97\x5c\x7a\x65\x72\x2e\xee\xf3\x0b\xc9\xa4\xe8\x22\x15\x63\x8b\xe6\x73\x1a\x46\x41\x41\xe3\x4b\xbb\x59\x41\xee\xe3\xa6\xb4\x6d\x4a\x27\x50\x7d\xa7\xc4\xd5\x84\xcf\x6d\x15\xd6\x64\x73\x96\xcf\xb6\x1f\x3e\x18\x74\x97\x7b\xee\x84\xe9\xb0\x37\x73\x93\xb7\x71\xc3\x3e\xd4\x0f\xa9\x8e\x31\x98\x23\x1e\x8d\x35\x4f\xe2\xba\xd4\x1d\x08\xc2\x35\xba\x13\xbe\x6e\x3a\x10\xbc\xef\xd6\x8f\xc7\x91\x1c\xd2\x85\x14\x1c\xcc\x81\xd4\xf0\x77\x78\x5a\x3e\x4f\xcf\xa4\x4a\x93\x04\xf9\x65\x32\x56\x87\x1f\x9f\x60\xe4\xe3\xdb\xcb\x04\xde\x4e\x1b\x08\x40\x32\x86\x85\x2d\x87\x77\x61\x43\xf8\x55\x6a\x36\x04\x7f\x07\xa3\x53\x2b\x66\xbb\xcf\x7b\x82\x23\x53\x78\x4d\x4e\x54\x49\x5b\x28\xb7\x76\xd4\x12\x3b\xca\xc1\x80\x1c\x4e\x34\x67\x8c\x72\xf5\xae\xef\x92\x0a\xff\x2b\x24\x2a\x88\x76\xd3\xa5\xcb\x9d\xcf\x28\x18\x63\x88\xd1\x77\x09\x67\xaa\x39\x89\x0a\x93\xad\x7a\x37\x6a\x87\xd8\xd5\x32\xf3\xed\x1e\x3e\xf4\x8b\x1a\xed\x09\xc5\xfb\x31\x84\x48\xf1\xf0\xb7\xaf\xe8\xa0\xc7\x92\xc7\x33\x6a\x5b\xef\xc5\xe9\xb4\xac\x5d\x62\x31\xa6\x8a\xb3\x05\xd4\x32\x64\x7b\x42\x89\x3f\x3e\x7f\xc4\x12\x13\xc4\x39\x00\xd8\x03\x6b\x4e\x47\x8e\x9f\x29\x21\x88\x1f\xbe\xe0\x09\x43\x41\x63\x9d\x6e\x9f\xef\xc8\xe3\x40\x7a\x2c\x04\xbf\x2a\x34\x24\x6c\x75\xcf\xb2\x34\x49\x97\xb9\x72\x5f\x28\x0c\x03\xd8\x6e\x6f\xbb\x22\xe2\xd5\x08\x61\xb7\xed\x35\xaf\x05\xa7\x12\xa9\xb6\xd2\x6b\x42\x40\xae\x0d\x1d\xab\xa1\x7e\x0e\x6f\x31\x6f\xd7\x35\xfc\xd8\xb9\x22\xe5\xb8\x75\x82\xbf\x55\x5c\x90\x5e\x9f\xf6\x76\x36\x9b\x5c\x81\xb6\x97\x39\xd7\x8b\x8f\x8b\xf6\xda\xfd\x85\xe8\xfd\x85\xe8\x9f\xf8\x42\x54\x3f\x15\x45\x2a\xeb\x9b\xbc\x17\x15\xc0\x2b\xdc\x64\xfa\x82\xbf\x35\x7e\x62\x9a\x4c\xa2\xa9\x17\x8e\x67\x49\xc0\xc3\x51\x60\x05\x75\x89\x46\x41\xe2\x09\xd4\x02\xda\x64\x1e\x69\x8a\xdb\x48\xf3\xcb\xcc\x51\x34\x15\x1e\x0c\x2c\x2b\x46\x0e\xf4\x3c\x9a\x5a\x4a\x7d\x6c\xcd\xc8\x35\xce\x57\x1c\xe2\x4a\xc1\x5e\x9b\x5e\xab\x74\x3a\xb6\xc4\x05\x3d\x63\x49\x1b\x86\x54\xc4\x7b\xe7\x7d\x86\x56\xa4\xaa\xac\x04\xdb\x55\x4a\xa0\x28\x7f\x97\x51\x71\x0d\x8a\x6e\x27\x8c\xba\x47\x3a\xdd\x6a\x60\x84\x4b\xb0\x83\x84\x70\x7f\x4f\xae\xae\xdc\x3c\x71\x36\xf5\x67\xd2\x20\x8b\x23\x56\x14\x75\x2d\x59\x2c\x8b\x17\x74\x12\x2c\x63\xef\xc5\x49\x5d\x1f\xd9\x8e\x6c\xb7\xa3\xae\x7c\xbd\xe1\x5b\x18\xc9\xf4\x43\xd4\xa2\xc7\xf7\x54\xf9\x3d\x0e\xee\x82\x35\x8a\xdf\xa2\xfb\xf6\x8b\x2e\x2e\xa0\xb0\x5a\x4a\xe6\xd8\x68\xd4\x53\x21\xca\xf6\xe0\x41\xd2\xd6\x2b\x7a\xe1\x19\xb9\x58\x55\x7c\xb0\x39\x32\x8a\x4c\x27\x24\x30\x7c\x03\x82\xe3\x49\x65\x47\xa0\xec\x02\xd8\xba\x7b\xf5\xf2\x9f\xd6\x72\x83\x3a\x98\x5c\xec\x5d\x68\x52\x97\x6f\xf8\xd8\x75\x0c\xdf\xe5\x15\xb9\xd4\xf6\xbb\x75\x7a\x23\x7f\x7f\x31\x2e\x8f\xe1\xfa\x0f\x5d\xc1\xc2\xe7\xd5\x95\x45\x43\xfb\x63\x88\xbb\x80\x1c\x9f\x61\x78\x8f\xc7\x2d\x59\x2d\xf4\x49\xb8\xa1\xf2\x5f\x3d\x9a\x72\x10\xae\xba\x48\x45\xc0\xe8\xa8\x20\xf3\x68\x3a\xe3\x82\xa3\xf2\x5e\x2c\x94\x54\x4e\xcb\x45\x5a\xdb\x6e\x91\x9a\xad\x9e\xb4\xe7\xc1\xc5\x8f\x94\xbe\xa3\xd9\x4f\x41\xde\xee\x11\xf6\xfd\x2e\x8b\xd2\x2c\x2a\x2e\x8d\xf4\x69\x90\xbf\xcb\xa2\x31\x15\xbf\xd9\x7f\x30\xcd\xec\x47\x92\x26\x63\xea\x7b\xc5\xf8\x99\x5e\x56\xbc\x63\xfc\x4c\x2f\x9b\xbe\x64\x84\x9a\x1c\x5c\xf3\x1a\xf6\x90\xdd\xc5\x0b\x3a\x8e\xe6\x41\xdc\xc1\x00\xee\x4b\x32\xf3\xb2\xf5\x6b\x13\x3b\xf2\xb9\x79\xd7\x34\xef\xab\xfa\xee\x49\xff\xa6\xd4\x7d\x4f\xd7\x7f\x44\xba\x16\x42\x91\x43\xd8\x70\xff\x2a\x83\x09\x09\xaa\xf6\x8a\x4a\x8d\xe9\xf9\xc2\x14\x8f\x44\xfa\x9a\x21\x13\xd5\x52\x70\x71\xd1\xfd\xa2\x34\x83\x17\x7d\xbc\x9d\xae\xcb\xd3\xb9\xd6\x88\x99\x00\xca\x43\x46\x2a\xf1\x67\x02\xa8\x37\x20\x2c\x1d\xe1\x02\x5e\x9b\xf9\xab\x77\xa0\xbc\x6d\xd8\x50\x52\xf9\x77\xd1\x07\x92\xf2\x17\x82\x2c\x0d\x39\x0d\x72\x3f\xdc\x34\xc8\x0d\x28\x20\x5f\x04\xaa\x45\x45\x94\x6f\x0c\x15\xaf\x0d\x93\x50\x35\x55\xdb\x60\x25\xf5\x63\x18\xc3\xcf\xa7\x6a\xc9\x59\x75\xd5\x2d\xba\xe0\xe5\x2d\x3b\xb0\x46\x0f\x8a\x8b\xbe\x34\xfc\xf3\x56\x80\x9f\x19\x4b\x2d\xc4\xc5\xca\xcb\x46\x86\xe8\xb9\xc9\xf2\x11\xd1\x82\x2a\x57\x91\x0a\x5e\xb5\xca\x52\xb2\x2b\xb6\x5c\xd6\xe0\xa8\x43\x3a\xca\x50\xcd\xda\xf2\x41\xb9\xf4\xe9\x81\xd2\xa4\x27\x33\x1b\x2c\xb5\x52\xd0\xf2\x26\x4b\x16\x9d\x0a\x86\xb3\x9c\x2f\xe3\xa0\x88\xce\xe8\x4f\x41\x7e\x9c\xc3\x9b\xbe\xb2\xaa\x1c\x58\xab\xae\x69\x6d\x0d\x53\xa3\x1c\x1a\x3b\x9d\x4c\xe8\x58\xd4\xcc\x57\x6e\xc9\x72\x28\x2f\xe0\xa3\xe7\x52\x68\x7b\x51\x9a\xd6\x22\xb2\x58\x9c\x4e\x6d\xe3\x4b\x9d\x81\x22\x0a\x39\xda\x41\x50\xf1\x79\x75\x83\x9e\x07\xca\x0c\xb6\x4e\x11\x28\x5a\x6a\xb4\x0e\x81\xc8\x9a\xaf\x3c\x38\x38\x55\x2e\x36\xa8\xb0\xc1\x52\x33\x6b\xc2\x26\x46\x50\x83\xf6\xd9\x4d\x94\x73\x26\xf0\x52\x28\xf4\x0f\xf8\x68\xd8\x1f\x05\x39\xad\xe5\x8d\x3e\x50\x1f\x19\x78\xe0\x0c\x02\xe0\xf9\xd3\x20\x7f\x1d\xcd\xa3\xc2\x43\xbf\x26\x80\x28\xab\x12\x4b\x88\xde\xc8\x37\xca\xe4\xd1\xaf\xbe\xdd\x4e\x67\x1a\xd0\x45\x34\xa7\x79\x11\xcc\x17\xa5\x45\x14\x84\x5e\x58\x3c\x23\x29\x63\x5b\x46\x76\x59\xb5\x4a\xa7\x82\x3a\x13\x46\x93\x49\x34\x5e\xc6\xf0\xae\xa7\x0c\xd3\x1a\xc8\x1c\x48\x5a\x04\xf1\x8b\x26\x15\x58\x90\x58\x6a\x36\x57\xaa\x00\xd7\x3c\xce\x5c\xaf\x6e\xb6\x2b\x6b\x46\x05\x9d\x77\xed\x17\x7d\x8e\x59\x25\x40\xb9\x17\xd8\xc6\xaa\xf6\x49\x6d\xbc\x60\xdd\xf2\x1e\x71\x9d\x4c\x83\xc5\x1d\xa7\x53\xef\x2a\xc6\x1c\xc5\xb7\x86\xe3\x74\xaa\xd5\x6f\xee\x42\x86\x7a\x8d\xc5\x8c\x2b\xc4\x4b\x19\x5d\x7b\x44\x13\xf6\x65\x6c\x6a\x6a\x9c\x56\x86\x87\xc6\xec\xa2\xbb\xb8\x4e\x67\xd7\x32\x2a\x6e\xb0\xfd\x79\x2b\x31\x9a\x88\xd3\xa9\xa7\x6a\x99\x5a\x52\xa5\x2a\x64\x9e\x2e\xe0\x2a\xa7\xfe\xc4\x7c\x3e\x8b\x72\xc6\x8d\x17\x69\x5e\xdc\xe0\xc8\xfc\x2e\xcd\xab\xa5\x22\x37\x06\x53\x25\xd7\x76\x2b\xc5\x13\xcd\x3a\xa9\xcc\x42\x07\x03\xe8\x74\x7f\x11\x5c\xc2\xbb\x8a\x3d\x43\x4d\x86\xb3\x04\x92\x21\xa9\x28\x62\xef\x79\x4d\x66\x62\xd8\xf3\x34\xfb\xfc\x31\x7d\x97\xa5\x67\xb4\xbc\x0c\x02\xc2\x65\x17\x42\xe2\x2d\x2f\x28\x21\x50\x68\x81\x09\x8e\x3c\x65\x58\x52\x73\xd6\xc2\x3b\xc9\xdd\xac\x60\x9e\x82\xd2\xc9\x9e\xf1\xf5\x8c\x9c\xa0\xcf\x53\x32\x54\x66\x0c\xd7\xba\x55\xae\x83\xe7\xea\xf8\x38\x4e\xcf\xe1\x59\x89\xd4\x6b\x54\x55\x5f\xfd\x0c\x82\xc7\x4e\x64\xc4\x44\xd2\x24\xbe\xe4\x01\x21\x0a\xe3\x75\x86\x7c\x21\xc1\x5f\x42\xf8\x1e\xf6\xc8\x67\x12\x64\x68\x3f\xda\xc1\x0f\x24\xec\xa3\x35\xeb\x63\x23\xde\xa5\xee\x83\x80\xfe\x85\x95\xaa\x97\x9b\xd5\x51\xba\x9f\xac\xcd\x7b\x85\x6a\xc2\x16\x74\x0d\xf8\xa5\x17\x8b\x28\xbb\xf4\xac\x78\x94\x8b\xc9\x2d\xe7\xee\x63\xbc\xd0\x2c\xaf\x6c\x09\x58\xa0\x9e\x05\x00\x94\xed\x93\x25\x2c\x88\xee\xae\x6f\x55\xbe\x0f\xce\x25\xc9\x88\x14\x2f\x18\xaa\x7e\x3f\x1f\x47\x91\xbd\x7c\x65\x19\xbc\xdb\xfe\x3d\x17\x88\x53\x70\x48\x9a\xd3\xeb\x50\x35\x00\xfe\x94\x21\x0a\x9a\x8f\x39\x0c\x06\xab\xac\x08\x58\x9b\x78\x35\x96\x2e\x46\xbd\xdc\x6e\xb1\x92\xac\x6b\x00\x8e\xa2\x66\xf4\xaf\x98\xaa\xad\x91\xf3\x85\x4b\xc1\x66\x3e\x11\xbf\xc6\x4b\xe8\x39\xdc\xe8\x75\xcc\x60\xda\x70\xd5\x31\x0a\x92\x7e\x94\xff\x23\x88\xa3\xb0\x03\xb1\x2e\x44\xca\x8b\x28\xa3\xe3\xa2\xe3\xbb\xe7\x10\x2e\xc5\x00\x50\xd4\xd8\xe9\x3a\x97\x28\x58\xd0\xd1\x31\x88\x64\x0f\x3c\xd5\x1a\x5e\xeb\x3c\x15\x35\xa8\x42\xf4\xcc\xac\x89\xab\x27\x6c\x1b\x12\xe1\x57\x5c\xc2\xb6\x65\x50\x70\xbd\xd0\x3f\x5c\x26\xe3\x28\xf1\x4b\x2b\xc2\x91\x37\xba\x79\x5a\x37\x93\x88\xeb\xd7\xc8\x10\xcf\xc0\x0b\x12\x18\x21\x46\xc9\x14\x0e\x38\xde\xe3\xad\x0b\x66\xfa\x92\x12\x6e\x9d\x6a\x2a\xc0\x50\x66\xf9\x59\x34\x9d\xd1\xbc\xae\x3c\x86\x32\xcb\xe7\x97\xc9\x98\x86\x42\x77\xed\xd3\xa0\x79\xe1\x2a\xea\x78\x2e\x82\xa4\x34\xa9\x07\x60\x7d\x75\xb1\x8c\x71\x1a\xd6\x57\xa4\x00\xab\x6a\x69\xd4\x25\x03\xd8\x57\xdb\x87\x22\xcd\xf8\xd3\x8e\xca\x7a\x04\x58\x45\x0d\x8d\xba\x83\x61\xad\x19\xa7\x41\x4c\xc3\x8f\x59\x44\x93\x6a\x0c\x59\x80\x55\xb5\xd4\x74\xc9\x03\xec\xab\xad\xc1\xac\x59\x80\x55\xb5\x34\xea\x53\xc5\xac\x31\x80\x28\x99\x36\x44\x14\x86\xf4\xd6\x23\x5b\xaa\xad\x46\x02\x56\xf8\x53\x2c\x89\x7f\x51\x77\xfd\x3f\xac\x85\xe8\xe9\x7a\x2a\x2a\xa8\x29\x69\xdc\x0c\x0f\x3d\x69\x08\xd6\x77\xb2\x1d\x96\x67\xa1\x92\xf6\x4e\x38\xf4\x27\xa3\x12\x86\xdc\x36\xf4\xa4\x71\xd8\x32\xe3\x91\x61\x69\x0e\x2e\xe7\x1f\x50\x79\x5e\x49\x59\x5b\xa3\xec\xa9\xc2\x06\x31\x7a\x6f\x28\xcc\x86\xde\x54\x0c\x8f\xcf\xe4\x43\x4f\x1a\x86\xb5\xd0\xe8\x49\xc4\xd0\xf6\x1e\x3a\x2c\x49\xe7\x7b\xaf\x61\x71\xc8\xaf\x47\x5b\xc3\xad\xa7\x65\x7e\xb4\x98\x00\xd2\x1a\xee\xec\x5c\x9f\xf6\x76\xb6\xee\x7d\xb0\xdc\x9b\x1c\xfe\xd7\x98\x1c\x0a\x4a\xbf\x8b\x60\x4a\xab\x45\x9e\x68\x68\x67\xc8\x63\x3d\x99\x06\x84\x3c\xed\x2b\x84\xb0\x68\x1e\x74\x22\x88\xe3\x81\x15\x97\x15\x5e\x93\xdb\x41\x9d\xdc\x50\x14\xf2\x49\x84\x1b\xbf\xae\x22\x04\x85\x2f\x80\xdd\x27\xbe\xb9\x89\x10\x09\x38\x74\xf3\xea\xe1\x0b\x74\xa5\x62\x77\xc0\xb5\xf2\xa4\xdb\x55\x0b\x51\x1f\x03\x50\x4a\x40\x9d\xf2\x1b\xc3\xc8\xd0\xce\x02\x44\x7c\x62\x88\x3b\x09\x9f\xc1\xf6\x07\x7b\x32\x0c\x47\xab\x60\xd5\xa2\x1f\x20\xe2\x73\x71\x36\xcd\xf1\x49\xed\x06\x01\xcb\xe5\x09\x55\x07\x77\x04\x2f\x26\xc0\xeb\xf9\x93\xb9\x6c\x9a\xf3\x18\x19\xeb\xe2\x9c\xd7\xac\xc3\x58\x18\xab\xec\x34\xee\xde\x0f\x0e\x29\xc9\x1c\x1c\xaa\x52\xbc\xcd\x75\x07\xe7\x1f\x9b\xed\x79\xa3\x42\x4c\xec\x68\x3c\x34\x44\x44\x55\x40\x4a\x1c\x43\xdb\x17\x75\x2d\xca\xc9\x38\xcd\x32\xd7\x21\x2a\x9c\xdf\x83\x82\xee\x67\xd3\xdc\x17\xa3\x52\x47\xc9\x7f\x48\xfe\x06\xe7\xff\x9c\x7c\x81\xd3\xff\x35\x6b\x2f\x2a\xc4\x8b\x24\xc3\x67\xaa\x67\xaa\x70\x3b\xa5\x73\xa4\x55\x34\x1c\x0a\x50\xe4\x58\x32\x05\x1a\xf1\x83\x81\x7c\x7a\x06\xea\x4c\xc3\x19\x11\x6c\x9e\xe0\x02\x53\xc7\x9e\x63\x5b\x6d\x00\x0f\x57\xb3\xe0\x52\x3e\xc3\x14\x73\xb7\xde\x71\x42\x97\x06\x5d\xe5\x50\xbf\x4b\x1e\x3c\x70\x2e\x98\xac\xfb\x2e\x01\xce\x9d\x63\x57\xc2\xeb\x2b\x35\xa3\x8c\x55\xc0\x7a\x03\x0e\x9a\x26\x89\x1d\x49\x88\xeb\x7b\x7b\x65\x84\x6c\xbe\xbb\xcb\xd2\x73\x19\x3d\xb0\x22\xc6\x5f\xc7\x71\x87\x51\xe5\x40\x5a\xaa\x14\xc1\xc2\x0e\x93\x8a\x11\x02\x25\x7d\xc7\xc1\x3c\xe4\xe5\x6c\x1a\xda\x71\x7c\x89\x33\xe9\x20\x56\xad\x6a\x83\xc3\x4a\xca\x53\xed\x57\x92\x9d\x11\x34\x77\x75\x86\xb1\x2a\xbf\x30\x63\xdd\x96\x04\xd3\xbd\xd6\xdc\x1c\x2f\x9f\x8e\x27\xb2\x6d\x91\xfa\xe3\x56\x18\x91\x6f\xf7\x48\x49\x4c\x0a\x5f\x68\x03\xf1\xea\x0a\x0d\xd7\x08\xa9\x5b\x61\xb9\x57\x12\x73\x49\xa2\xfe\x66\xb1\x65\xbc\xc5\x2b\xe7\xfd\x46\x11\x66\x84\x73\xfc\xcd\x1e\x79\x2a\x75\x99\x15\x4d\x2c\x93\x45\x30\xfe\x7c\xc4\x6f\x51\x0c\x1b\x56\x48\x32\x34\x9b\x66\x92\xee\x82\xa1\xd2\x4c\x65\x55\xfc\x87\x22\xbd\x3d\xb2\x4d\x9e\xc9\x44\xe9\xbf\x9f\xc8\x73\xa0\x76\x68\xa1\xbc\xee\x97\xb9\xef\xc7\x42\x4e\x4f\x14\x37\x67\x54\x28\x52\xb0\xf3\x71\x15\xb9\xf1\x64\xf3\x94\x0c\x7d\x2e\xe6\x0f\x20\x72\x79\x80\x82\xc5\x4b\x64\xd9\xe1\xe8\x83\x38\xc6\x8b\xbb\xdf\xef\xcb\xf5\x7d\x60\x97\xb5\x36\x1f\xc7\xb9\xd3\x21\xdf\xee\x20\x28\xb5\x04\x65\xbb\x51\xa0\x6a\xe8\x71\x3f\x3e\x76\xc5\xdc\x93\x21\xbc\x9c\x95\x87\xae\xc0\x78\xdb\x18\x24\xa1\xe9\x01\x48\x82\xf1\xb0\xed\xfc\x64\xc4\xea\xe0\x21\x2f\x19\xb8\x40\x9b\x97\x76\xc5\xac\x42\xd4\xe9\x3a\xaa\x85\x5e\x95\x45\xf6\x5e\x25\x6c\xb7\x7f\xdf\x94\x32\x98\x65\xeb\xab\xf6\x18\x38\xc8\x68\xf9\x4f\x38\xfc\x36\xc4\x42\xcc\x7e\xc0\x69\xb9\x29\x7d\xe1\x22\x58\xfc\xb1\x8b\xe9\xeb\x28\xee\x68\x5c\x72\x69\x09\xa7\x8d\x4a\xd6\x7d\x0f\xc9\xf5\x0d\x83\x62\x7c\xb4\x98\x71\x24\x88\xaa\x7b\x46\xd7\xdc\x67\xa3\x50\x0a\x2f\xe1\x8e\xb1\x1e\x90\xab\x7c\xe7\xa1\x77\x93\x06\x7b\xae\x7b\x26\x97\x07\x20\xe7\x4c\xf2\x3d\x90\xe1\xc2\xa3\xc7\x4d\x83\x76\x4d\x97\xd8\xbc\xd3\x34\x74\xdc\xff\x17\xd9\xa5\xf5\xea\x15\x81\xc2\x43\xd7\xf2\xf1\x12\xe3\x65\xee\x18\x5c\x23\x74\x1c\x07\x4b\x9c\xe2\xf7\x08\xc5\x85\x50\x29\xb3\xf3\xb2\x75\x24\xc9\x54\x6e\x14\x4d\xce\x95\xf6\xb6\x61\x16\xa9\xdd\x15\xac\x16\xfe\x54\x4b\xad\x76\xcd\x48\x92\x12\x80\xc2\x98\xf8\x07\xb2\x09\x87\x1a\xe3\xac\xe9\x4a\x87\x38\xde\x6d\x90\x70\x2f\x03\x49\x28\x3c\x91\x42\xc0\xe2\xe4\x91\x3c\xa8\x3a\x91\x9b\x6b\x96\xab\x11\xad\x90\xad\x1b\x6b\x1e\x3a\xe6\x1d\xb4\xa8\xae\x16\xbc\x79\x98\x08\x9a\x17\xd1\x3c\x28\xe8\x4f\x01\x28\x10\xeb\xa8\x0a\x81\xd7\x51\x14\xae\xf9\x2e\xa8\xe9\xeb\x53\x47\xb3\x19\x42\xe3\xaa\x9b\x1d\x0f\x68\xd9\xcc\xbc\x97\xcd\x50\x19\xf8\x0e\x02\xf8\x48\x5d\xa0\x90\x0f\xf0\x54\x4c\x69\xf1\xc2\x0e\x4c\x25\x77\x56\xbb\x9a\xba\xb9\x12\x75\xdd\xf1\x3c\x35\x42\xbc\xb4\x47\x10\x2b\x93\xc7\xf4\x69\x2e\x35\xdf\x22\x7c\x26\x2e\x2a\xf1\x8c\xc8\xbe\x12\x61\xbf\x6d\x2c\x4d\x55\xff\x8d\xc2\x69\xaa\x42\xab\x0e\xf2\x6b\xc6\xd6\xd4\x3a\x1a\x36\xc0\x6c\x31\x96\x4e\xdb\x72\x7e\x6a\xae\x63\x44\x02\xba\xdc\xa6\xaa\x62\x5c\xa2\xec\x1f\x9b\x2b\x11\x23\xc6\x90\x04\xc3\x62\x8a\x11\x2a\x07\xcf\x89\xeb\xe5\xd0\xd2\xb8\x3e\x03\xd7\xc9\x9f\x58\x8f\xdb\x64\xc8\x3f\xac\x9d\xa4\xdd\x73\x84\x97\xa1\xf6\x2e\xa8\xf2\x94\x5f\x44\x31\x9c\x53\x9d\xc5\x3b\x2e\xbd\xee\x72\x06\x59\x4b\x0c\x32\x28\x50\xd9\xf6\xa3\xc2\x6d\x55\x6f\x3d\x9e\xc8\x58\x78\x82\x0b\x43\xd0\x59\x37\xb1\xa3\x6d\xc9\x60\x9b\x2f\xb0\x0c\x25\x3d\xcb\xe8\xb4\xb2\xad\xc2\x42\x67\x3f\x58\x2c\xe2\x4b\xe1\xe7\xaa\x11\x61\x75\x6d\x23\x4c\xbe\x05\x58\xcd\xb0\xc4\x1b\xd5\x5d\x33\x0f\x22\x7a\x94\x66\x3c\x3a\x80\xd4\xad\x23\x47\x79\x26\xec\x6b\x05\x8f\x92\xe9\x7a\xc5\x63\x47\x5f\xa5\xe0\xe2\xb0\xa9\x31\x5c\x06\xe8\x4a\xcd\xde\xc9\x2f\x2b\x6e\x8a\x48\x7c\x24\x3a\xa9\xb4\x98\xde\xad\xa5\x83\x2a\xf6\xf9\xa7\x8c\x9c\x25\xcb\x02\x81\x47\xd9\x78\x19\x07\xd9\xfa\xfa\xfa\x7a\x75\xbc\x2c\x49\x41\xbb\x77\x12\x31\x8b\x6b\x7f\x5b\xc3\xed\x27\x7e\xf7\x43\xdb\xf7\xb7\xff\xf7\xb7\xff\x7f\xed\xdb\x7f\x71\xf5\xcf\x60\x65\x44\x33\x7f\x1c\x96\xdf\x2d\xc2\x8a\xcf\xb2\xa0\xda\x10\x60\x6d\x30\x80\x88\x6d\x41\xc6\x48\x99\xed\x60\xcb\xdc\x1c\x22\x23\xb8\x30\x9a\x4c\x68\x46\x93\x82\xd0\xe4\x2c\x87\x42\xa3\x2c\x3d\xcf\x69\xb6\x86\xdc\xd1\x9e\x47\x49\x98\x9e\x83\xc6\x02\xc5\x29\x21\x0f\x1e\x88\x9c\xfe\x3f\xdf\xbc\x7e\x55\x14\x0b\xe1\xe9\x98\x73\x4d\x33\x8d\xec\xf9\x61\x81\xf5\x89\x30\x1b\xd1\x34\x49\x19\x23\x88\xa3\x84\xb2\x9e\x24\x69\x48\xd7\x90\x6f\x3b\xa7\x46\x35\xf0\x8b\x79\xcc\x46\x26\x36\xb6\x76\xb7\x69\x23\xd7\x1c\x93\xff\x7c\xf5\x7e\xdb\xa8\x6e\x96\x6d\xb7\xbb\xa5\xa5\xa4\xe4\xc0\x5a\x78\x27\x91\xe9\x9a\x44\x80\xfc\xc4\x44\x7b\x70\xee\xca\x5d\xc1\xb3\x5e\x2a\x03\x08\xa3\x3c\xde\xf2\x67\x69\x5e\xf4\x48\x11\xcd\x69\xba\x2c\x7a\xac\xc2\xac\x07\x4a\xe6\xf3\x34\x13\x4f\x29\x61\x33\x61\x70\x64\x8f\xc0\x7f\x57\x57\xa4\x2d\x88\x3d\x4e\xc7\x41\xcc\x12\x87\x4f\xbf\x79\xfc\x0d\x84\x45\xe6\x7b\x0f\xaf\x90\xed\x84\xe2\xd7\xd5\x15\xd9\x54\xd9\xac\x19\xb2\x07\xad\xa9\x34\xd9\x28\xd9\x53\xed\xd7\x0a\x4f\x8b\x8c\x2e\x20\xce\x20\x3d\xb7\xa6\xcc\x92\x9d\x04\xe0\x7b\x74\x96\x11\x92\xd3\xf3\x34\x8d\x69\x90\x5c\xc3\x1d\x2b\xdb\x9f\xa5\x04\xa3\xb1\x2c\x9c\x8a\xa2\x03\x9f\xd9\x96\xe1\xb9\x0a\x63\x1a\xc9\x5d\x66\x07\xcc\x8b\x40\x56\x3d\x47\x35\xbf\x41\xe1\x84\xc4\x78\x18\xdc\x00\xea\x6c\x42\xb4\x78\x05\x43\x7e\xf5\x7e\x5b\x47\x25\xe6\x92\x16\xc2\x3c\x9a\x08\x06\x63\xb8\x6e\xb4\x2a\x32\xc6\xc3\xab\x04\x79\x58\xd6\x9a\x2e\x68\xd2\x69\xbf\x3b\xfa\xf0\x51\x06\x52\xe5\x84\xc3\x3b\xb7\xbb\x86\xfc\x40\xc2\xdc\x3e\x78\x60\x4e\xaa\x71\xe8\x5b\x82\x41\x4d\xfb\x79\x90\x47\x63\xd2\x26\x1b\xd0\x85\xe7\x4b\xc6\x1e\x50\x15\x1b\xa4\x3d\x54\x57\x85\xaa\x9e\x7e\x91\x8a\x27\x97\xed\x51\x90\xd3\x27\x8f\xdb\xd6\xf8\xb5\x17\xf4\x57\x34\x08\x69\xd6\x69\xef\x03\x5f\x8d\x7e\x0d\xf8\x69\x0b\xda\xe7\x23\xac\x28\xc4\xe4\x63\x9a\x14\x8f\xd8\x41\xbb\xdd\x23\x6d\x26\xf9\x47\x63\xa8\x62\xf0\x4b\x2e\xd5\x8e\xea\xc6\x4a\x4c\x59\x0d\xb9\xf2\x78\x39\x97\xc9\x18\x1d\xaa\x6d\x4d\xb2\xef\xe2\x79\x81\xae\xaf\xfd\x91\xd1\xab\x48\x2f\xb7\x23\x65\x4a\x5d\x9a\x4d\x72\x92\x66\x4c\x5a\x15\xa1\xb6\x81\x1e\xb5\x76\x5f\x63\x2e\x09\x3b\xf0\x9c\x87\xc7\x40\x8a\x26\x97\xaa\x7e\x81\x64\xa9\xc8\xc7\x4e\xce\x7d\xd6\x00\x07\x69\x92\x50\xf1\xe8\x46\x52\x98\xa6\x44\xe3\x72\x51\xb6\x2e\xc3\x8d\x7c\xa4\x17\x85\xd3\x41\x01\x8b\xde\x1a\x89\xb7\x1d\x66\xb7\xaa\xba\xf4\x5e\xd4\xdf\xf1\x35\x88\x57\x49\xf3\xc8\xd7\x40\x03\x41\x0d\x11\xec\x2b\x8e\x53\x41\x09\x22\xeb\x47\x27\xd6\x0c\x29\xb2\x68\x3a\xa5\x19\x0f\x90\xc5\x66\x1f\xc4\x16\xe5\xed\x96\xe1\xa0\x8e\x60\xa0\x07\x3e\xaa\x31\xe3\x5c\x37\xa1\x1f\x30\x5e\xd9\x35\xb8\x49\x02\x9e\xc9\xf3\x22\x28\xe8\x78\x16\x24\x53\xbf\x02\x81\xb1\x14\x8d\xf8\x20\xbc\xfc\xc0\x0a\xc0\x8d\xf0\x63\xc6\x61\x6c\x96\xb7\x6e\xc6\xa9\x6e\x40\x31\x1a\x50\xde\x2a\xa1\x00\x68\xf6\x65\x56\x0d\x45\xc1\x99\xcc\x7b\x6b\xa5\x6e\xac\x56\xa4\x2d\x82\xaf\xb6\xec\x8b\x2d\xa3\x65\x76\x16\xbc\xb6\x50\xac\x37\x02\x17\xb3\x66\x65\x79\x5f\x2f\xbd\x8f\xbc\x54\x07\x6f\x1e\x62\x21\xdf\x2d\x07\xb0\xbb\x50\xc5\x04\xc4\x4a\xc3\xeb\x4a\x5f\x96\xc7\x97\x8c\xde\xf9\xcb\x68\x58\x5c\x8c\xaa\x4b\xd6\x56\x94\x8b\xfa\xa9\xc9\x4c\x95\x10\x20\x15\x9c\xb6\x30\xc0\xce\x0f\x49\xbb\x20\x93\x20\x8a\x69\xd8\x27\x47\xec\x9c\x76\x1e\xb1\xb3\x47\x00\x31\xed\xca\x57\x13\x6a\xd3\x33\x17\x1a\x9f\x4a\x9f\xa1\x62\xa7\x44\xe1\x90\x7c\xa7\xfe\xa4\xbe\x8f\xed\x3e\xd9\x62\x3c\x24\xed\xed\xfe\xa6\x52\x1e\x4a\xfd\x63\x3b\xa1\xc5\xa7\x38\xca\x0b\x9a\x44\xc9\x54\x65\x2b\xed\xe1\xa9\x61\xd0\x25\x15\x5c\x19\x0f\xd0\xe7\x92\xaf\xb4\x2a\x64\x83\xd4\x93\xe0\xa8\x0b\xf0\xd0\xa5\xaa\xc0\x38\xed\x33\x31\xb7\x35\x7c\xca\x7e\x19\xf2\x73\x6b\xb8\xf5\x2d\x3b\xf9\xef\xdc\x9f\xfc\xef\x4f\xfe\x7f\xf1\x93\xbf\x36\xfc\x87\x27\xb7\x77\x64\xf4\xaf\x0c\x39\xf1\xa9\x72\x14\x4d\xb9\x0d\x6e\xff\x17\x7e\x42\xe7\xf7\x20\xe1\x6b\x3a\x31\x37\x04\x15\xa9\x14\x5e\xce\x21\x21\x5d\x6d\x76\x1c\x82\xb3\x8b\xf3\x19\xeb\x7d\xc7\x34\xd0\xfa\x9e\x17\x26\x0f\xc9\x76\xd7\xb7\x5b\xb6\x37\x99\x14\x6f\xbe\x9e\x25\xfe\x17\x71\x82\xb9\xbf\x13\xa7\xba\x20\x21\x87\xcf\xf7\xdf\x8a\x49\x0e\xc9\x77\xdf\x92\x71\x3a\x5f\x2c\x45\x94\xa0\xd1\x25\x99\xa7\x67\x51\x32\x45\xb1\xf0\x1e\x93\xf1\x2c\xc8\x60\x2f\xe0\x37\xb3\x21\x37\xa5\x92\xe6\xea\x12\x3a\xa6\xfc\xd1\x42\x91\xb2\x06\x39\xae\x72\xd2\xd9\x27\x7b\x64\x6b\xb3\x47\x9e\xb3\xff\xb7\x7a\xa4\xdf\xef\xf7\xc8\xff\x91\x3d\xb2\xf3\x4d\x97\x1d\x76\x48\xbe\xa0\xe3\x68\x12\xf1\x85\x74\xf8\xe1\x68\x6b\xe7\xc9\xd6\x13\xdb\xc4\x2c\xca\x53\x48\x17\xe3\x70\x7d\x22\x5f\xf3\x17\xdd\xac\x23\x6c\x80\xe6\xd5\x1a\xbe\x59\x16\x92\x54\x28\xc1\x84\x63\x0e\xb3\x7e\x63\x42\x59\xc5\x78\x1e\xd9\x88\xda\xfb\xed\x3e\x43\xcb\x41\x1a\xd2\xfd\xa2\xb3\x89\xb4\xd6\x6c\x6c\xed\xff\x73\xb2\x39\x03\xe4\xaf\xce\x81\x58\x8b\xf4\x78\xb1\xa0\xd9\x41\x90\x6b\x55\x36\xca\xce\x97\xa3\xbc\xc8\x3a\x8f\xbb\xf2\x3d\xb9\x48\xd8\xec\x3d\xb6\x6e\xcc\x78\xee\x22\x8e\x8a\x4e\xbb\xdd\x35\x9f\xda\x27\x5d\xd3\xba\x4a\x3c\xb4\x4c\x7c\x9d\x97\xf2\x21\xc0\xfc\xb0\x47\xf6\x99\x40\x08\x1f\xdf\xef\x91\xff\xeb\x3a\x11\x2c\x3c\x33\x2b\x26\xd6\x80\x54\x2e\x91\x43\x4a\x1e\x91\x7d\xb2\x41\xb6\x36\x91\x9d\x91\x2f\xaa\x83\x8c\x9c\x6b\xdb\x30\x5d\x77\xfb\xbf\xa4\x51\xc2\x86\x69\x5b\x2a\x8e\x97\xe0\x2f\x1a\xa6\xf8\xcd\xd1\x0b\x46\xd8\x5b\x9b\x92\x29\x09\x0b\x3f\xa0\x7c\x0f\xc5\x7d\xbb\xf9\xe4\xb1\x4d\x70\xf3\x34\xfc\xee\xdb\xad\xcd\x32\x42\x33\xe9\x4b\x7b\xe1\xe6\xd4\x24\x0a\x57\x52\x51\x46\xe7\x41\x94\x70\xdd\x11\xcb\xd3\x77\x8f\xc2\x35\x91\xc9\x1e\x04\xb0\xb6\x5b\xde\xee\x5a\x4e\x97\x80\x59\x49\x30\x65\xf1\xfa\x9d\x61\x22\xa7\x9b\x04\x59\xfb\x30\x29\xb8\x3f\xa7\x1e\xd9\xda\xec\x92\xff\x3f\xc3\xda\x86\x53\x0b\x77\xe9\x24\xcc\xcf\x7d\xbe\x94\x54\x5d\xaa\xa4\xae\xcf\x98\xa7\xfa\x77\x48\xdc\x04\x1d\xd6\x81\x30\xf8\x87\x0b\x75\x48\x10\x6f\x1d\x04\xfb\x94\xf3\xe5\x9f\x9c\x01\xf6\xd5\xee\x9f\x04\x61\x09\xad\x97\x9c\xdb\x55\x27\x46\x72\x5d\x3f\x29\x84\xd0\x5a\xce\xe5\xeb\x1c\x8b\xa8\x18\xcc\xbe\xca\x71\xfa\x1e\xa0\x2c\x29\x46\xb3\x21\x5c\x2b\xb6\x86\xb5\x62\x2c\xa7\x8f\x6a\xac\xf3\x78\x11\xe4\xcf\xa5\xc7\x0b\xf4\x52\x41\xc4\xb2\x25\x5b\x4f\x10\x0b\x1b\x05\x39\xdd\x79\x42\xf6\xa0\x8c\x56\x0f\xed\x3c\x31\x4c\x00\xc2\x90\x72\xcd\x22\xec\x81\x1d\x5e\xa8\x47\xb6\xbe\x31\x25\x61\xd5\xcf\xe7\xa3\x20\xe9\xf0\x62\x26\xf3\xb3\x16\xb3\x70\xaa\x83\x16\xee\x73\x36\xf4\x22\x35\x76\x2f\x36\x7d\x04\x9c\x1d\x64\x97\x72\x45\x73\x65\x12\xd8\xeb\xbe\xe3\x91\x4c\x92\xb4\x10\x42\xd9\xf7\xd1\x0f\xad\x29\x48\x24\xdc\x59\xd3\x44\x23\x35\x9f\x05\x5c\x5a\x83\xfd\xed\x62\x1c\x2f\xf3\xe8\x4c\x05\x5e\x8d\x46\x51\x1c\x15\x4a\xc0\x19\x05\xc9\xe7\xc1\x28\x0b\x92\xf1\x8c\xe4\x34\x3b\x8b\xc6\x72\x03\x0c\xb8\xef\x85\xd6\xf7\x83\xe8\x87\xbe\x4d\x43\x2a\x08\x4a\x2e\x77\xa1\x09\xcd\xd8\x36\x14\xc4\xd3\x34\x8b\x8a\xd9\x9c\x84\x34\x1f\x67\xd1\x88\xb3\x25\x21\xff\xd0\xa4\x7f\x1e\x7d\x8e\x16\x34\x8c\x02\x10\x82\xd8\xd7\xe0\x30\x29\x68\x96\x04\xfc\xe9\xc4\xa7\xe7\x41\xf2\xf9\x93\x70\xfc\xf0\x89\xcf\xeb\xff\xef\x27\x31\xd2\x64\xfa\x89\x0d\xf1\x13\xbc\x25\xfa\x14\x46\xd3\xc8\x79\xca\x21\xa7\xc6\x47\x91\x23\xb9\xa7\xca\x19\x90\x1e\x8f\x8a\xd4\xb3\xcd\x36\xa0\xd5\xe7\xf6\x8a\x1c\x59\x6c\x51\xcc\xe8\x01\xdf\xa7\xda\xff\x7c\xd9\xde\x5d\xf3\xf2\x4c\xc1\x63\x3b\xd6\xce\xdd\xc1\x15\x6c\x90\xf6\x26\x88\x4a\xd0\x0a\x36\x77\x61\xe8\x78\xc1\xb0\x41\xf6\x48\x87\x8b\x53\x9d\xef\x9e\x92\x47\xba\x89\xae\x7c\x36\xf0\x68\xdb\xda\x6f\x95\xcf\x18\xb3\x29\x54\xa7\x68\xb0\x46\x6d\x25\x98\x08\xc2\x15\x10\x36\x0f\x7f\x1f\x25\x79\x11\x15\xcb\x42\xba\xfa\x8e\x42\x9a\x14\x6c\xd3\xb2\xc3\x46\xf0\x5a\x0e\x93\x30\xca\xa8\x69\xc0\x60\xbe\xb1\xc9\x7b\x52\x96\x55\x8f\x6c\xe0\xd5\x54\x0b\xb5\xd4\x82\xa6\x5a\xba\xad\xd6\x2a\xbc\xc8\xec\x89\xd7\xfd\xb7\x79\x04\x36\x39\x43\xfb\xe5\xc7\x57\x6c\x1e\xe4\xeb\x16\x8c\x01\x94\xaa\xfa\xd6\xb5\xf8\x75\x5a\xc5\xaf\xe5\x53\x3a\x8e\x5c\x11\x5b\x3e\xca\xf9\x4b\x39\xcc\xc7\x1d\xb9\x13\xfc\xff\x94\xca\x9b\x6a\x2f\xf2\x28\x3e\xa4\xc2\x83\x3f\xa7\xe3\x2d\x29\xa1\xf3\x10\x79\x17\xaa\x94\x13\x22\xec\xa5\x28\xe2\x64\x85\x85\x3f\xed\xa2\xa8\x56\x57\xae\xb0\x00\x5d\x2f\x7d\x3d\x88\xc7\xac\x63\x8a\x78\x47\xd5\x23\xa9\x47\x6b\x03\x63\xc3\xda\x1a\x77\x94\x16\x25\x0c\xfe\xf3\xcf\x97\x27\x9b\x8f\xbe\x3b\xfd\xb2\x7d\xdd\x79\xf9\xf1\x15\xfb\xbd\xff\xe8\xff\x4e\xbf\x6c\xed\x5c\x5f\xa9\x8f\x9d\xcd\xde\xce\xd6\x75\xf7\x7f\x06\xfd\x02\x94\xa0\x6a\x03\x37\xde\xe5\x95\x31\x06\x04\xce\x9f\xe7\x6d\xad\x88\x30\xf1\x04\x13\x4e\xff\x5e\xb4\xbd\xd0\x4b\xf0\x6e\xf0\xf6\xc2\x5d\x49\x16\xe2\xf4\xa0\xf0\xe3\x9e\x9d\xc7\xe4\xea\xaa\x2c\xef\x9b\x1b\x0e\x7b\x42\xa2\xa4\x64\xe0\x06\xf7\xb9\x9b\xa1\x7b\xd9\x48\xa3\xc1\x6f\x6f\x36\xb2\xda\xe4\x22\x25\x1b\x69\xbe\x9c\x33\xc0\xe3\x5c\x1c\x1f\xe6\x69\xf8\xe8\xbb\x6f\x1f\x6d\x6d\xaa\x6c\x38\xe3\x42\xef\xc6\x69\x4c\x3a\x87\x1f\x8e\x06\x87\x2f\x0f\x08\x3b\x37\x0c\xb7\x37\x37\x77\xba\x36\x4f\x46\xd5\xba\xa7\x50\x94\xeb\x0c\x5c\xe6\x35\x1c\xb6\x38\x13\x6e\xf7\xc8\x76\x33\x5b\x55\xcc\x54\x8d\x2d\x85\xd0\x69\x9f\xfc\xf3\xfd\xcb\x9f\x1c\x37\x98\xaa\x80\x7f\x34\xa5\x35\xba\x93\x8a\x20\xeb\x86\xa7\x09\xa0\x03\x3e\x12\x9d\x21\x7f\xdb\x23\x8f\xbb\x64\x48\xda\xed\x46\xe3\x1e\xc7\x11\x3c\x24\x53\x1d\x04\xe5\x53\x94\xd8\xe3\x63\x58\xf8\x69\xff\x1f\x47\x3f\xfe\xeb\xe8\xfd\xff\xda\xb3\x0a\x75\x94\xcc\xa9\x5d\xbf\x77\x72\x39\xd0\xad\xc7\xbe\xb5\xb5\xfa\xc8\xc5\x6a\xf2\x9f\x4b\xdc\x83\x87\x3b\x34\xa7\x02\x67\x78\x81\xe7\x1c\x82\xef\x9d\xc4\xe0\x7c\xee\xf3\x8c\x43\x87\x3b\xe0\xc7\xe8\x10\x5b\x7a\x94\x91\xe7\x0f\x75\x4a\x31\x4e\xa8\xfc\x8c\x62\x9e\x67\xb6\x9e\x74\x7b\x64\x7b\x53\x39\xe8\x33\xa4\x3c\x89\x5e\x6b\x90\xb2\x70\xb3\x05\x5a\xe2\x95\xea\x10\xb2\xb8\x52\x1f\xeb\x15\x5b\x43\xf3\xf3\xfa\xb4\xb7\xf3\xf8\x5e\x8d\x7f\xaf\xc6\xff\x8b\xab\xf1\x85\x0a\x7f\x31\xae\xb6\xdf\xbb\x85\xc5\x5d\x4b\x07\xd8\x6c\xed\xae\x14\x18\xb0\xc6\x4e\x8f\xeb\x99\x16\x63\xaf\x25\xd8\x22\x28\x66\x3d\x92\x50\xc3\xfa\xfb\x13\x68\x2e\x9c\x87\xa7\xf2\xaa\x1a\x87\x26\x97\x5e\x0b\x84\xbd\x0e\xd8\xf8\xb0\xff\x78\xaa\xce\x1a\xab\x1b\x5e\xe0\x8a\x85\x4c\xe8\x7c\x61\xd0\x43\x5d\x5e\x39\x52\xb5\x8a\xf5\xd3\xa4\xd3\x86\x51\xb5\x71\x28\xdf\xae\x61\x3f\x9d\xa7\x8c\x89\xf1\xb7\x84\x87\xef\x0e\x88\xbe\x57\xe6\x2f\x0c\xdb\x3d\x42\x11\xeb\xfd\xc4\xd9\xa0\xb8\xf0\xee\xd8\xae\x5c\xbd\x3d\x48\x42\xdc\x3e\x6a\xbe\xb4\x32\xb2\xa6\xde\x18\xbc\x3e\xfc\xf0\xf1\xe5\x5b\x58\x41\x07\x47\x6f\xdf\xbe\x3c\xf8\x78\x78\xf4\x96\xbc\x7f\xf9\xe1\xdd\xd1\xdb\x0f\x2f\x3f\x94\xb6\x1a\x06\x45\x80\x9b\x65\xdf\x78\x73\x1a\x3c\x14\x66\x84\xf3\xe0\x62\x9c\xce\x17\x31\xbd\x88\x8a\xcb\x21\x79\x02\x94\x65\xf5\x10\x74\xa1\xca\x0e\x81\x55\xa5\xf7\x9b\xae\x27\xee\x92\xb0\x39\xf8\x62\x86\x61\x87\x83\x5f\x68\xdb\x4e\x88\xee\xf0\xf0\xf4\xc0\x5f\x42\x72\x3e\x8b\xc6\x33\x32\x0f\x8a\xf1\x4c\x88\xaf\x7c\x13\x62\x0c\x2d\x34\xca\x79\x62\x6e\x40\xd3\xfe\x38\xed\x70\x1d\xe5\xf4\x16\x2c\x10\xfc\x51\x94\xa3\x49\xe7\x93\x9f\x90\x4f\xe0\x6d\x5c\x14\x9e\xba\xce\xfc\x55\x61\x36\x56\x01\xb6\xeb\x40\xd9\x11\xef\x4b\x23\x41\x43\x35\xa2\xef\x76\x45\xd7\x0e\x16\x27\x51\x46\x0d\x8f\x00\x36\xba\xca\xc6\xc3\x86\xe2\x69\xbd\x02\x5c\x87\xa5\xc6\xa6\x2d\xfa\x2f\xa4\x31\x2d\x68\x55\x0d\xf6\x60\x6c\xdc\xe0\x57\xd8\x3f\xb3\x5d\x0b\x08\x51\x10\x04\xaf\x0f\x94\x3b\xdc\x56\x2a\xe1\xce\x72\x48\xca\x1d\x85\x47\x45\x7f\x6d\x4d\x0a\x83\x26\x09\xaf\xd9\x6a\x0f\x78\x91\xc9\x84\x3f\xcd\xf3\x90\x78\x64\x16\xc6\x7e\x81\xf1\xaa\xb2\xd9\x60\xcf\x92\xd7\xfe\xc1\xfd\xf2\x6b\xf7\xd2\x72\x89\xbf\x78\xf9\xe8\xe0\xd5\xf1\xdb\xff\x7d\xf9\x5e\xd5\x13\xd2\xf1\x6c\x99\x7c\xa6\xa1\x78\x55\xc2\x5f\x8c\x8a\xbf\x7e\x46\x17\x71\x30\xa6\x9d\xc1\xbf\xaf\x4f\xfe\x9d\xfc\x3b\x3b\x7d\xf6\xef\x2f\x83\x69\xaf\x7d\x7d\xf5\xe8\xd1\xd5\x97\x76\x17\x1c\x63\x7f\xf1\xc2\xff\xfb\x54\x96\x38\x11\x65\x4e\x59\xa1\x13\x59\xea\xf4\xc4\x5f\xce\x2e\x65\x14\x2a\x29\xa3\xdb\x42\x2d\xa9\x86\x50\x19\x71\xcd\xc7\xb2\xdb\x92\x93\x1a\x18\x70\xd7\x2c\x20\x1e\xf1\x97\xc1\x00\xee\x40\xa9\x70\x87\x01\x9e\x36\xa0\x82\x35\x87\xf4\x59\xde\x01\xcb\x32\x57\xae\xf0\x3b\x63\xc1\x90\x0d\xc2\xdf\xbf\x1a\xa2\xba\xba\xb3\xb6\x38\x99\xeb\xd4\xc0\x67\x0b\x06\x7d\x47\xa5\x84\x35\x0d\x37\xa6\x59\x73\x17\x9f\xee\xcc\x9e\xdd\x19\x31\x74\xf0\x18\xad\x2c\xa8\xc1\xf5\x5d\x32\xa6\x31\x84\x83\x90\x8f\x38\x8d\x32\xe3\x98\x06\x99\x34\xe1\xb2\x5a\x11\xc9\xd6\x82\xf6\x03\x81\xaf\x86\x42\x56\xe4\xdb\xe3\xcc\xf2\xf6\x5e\x87\xff\x2a\xed\x2a\x05\xce\x30\xfc\x75\x8f\x6c\x6d\x6e\x6e\x92\x87\xfc\x72\xc6\x73\xd7\xea\x75\xfc\x00\xef\xf6\x00\x3b\x12\x5f\x8c\x83\xe4\x54\xd0\x0b\x8f\x24\x24\xde\xf5\xad\x8e\x2a\x77\xc6\x2c\x12\x81\x60\x55\xc2\xb2\xd2\xe9\x30\x67\x11\xfd\xc5\x32\x9f\x99\x16\x83\xb6\xaf\x78\x0c\x2e\x9c\xff\x30\x1e\xf9\x93\xd8\x42\x83\x30\xcc\x71\x9c\x7b\x61\xe5\xe0\x4a\x63\x5c\x3d\xdc\x5b\xe3\x1b\xae\x3c\x18\x88\xb3\x76\xc4\x83\x2d\x08\xae\x07\xbb\xb1\xbc\x15\x52\xa9\x87\x21\x2f\x15\x64\x59\x74\x46\x31\xc3\x0d\x42\x35\x7b\xb2\xbd\x0a\x0e\xeb\x81\x36\x62\x2d\xf8\x6d\x4a\x91\x4c\x21\x5f\xab\x47\x21\xb9\xba\x92\x5f\x27\x9b\xa7\x6a\xcb\x84\x2b\x6c\xde\x37\x0d\x2d\x12\xcc\x12\x3c\x11\x4b\x74\xde\xcd\x8b\xec\xa9\xde\x54\x49\xbc\x0c\xb4\xaf\x1a\x96\x75\xcb\x5d\x4d\xae\x23\xbc\x52\xc9\xf9\x8c\x4a\xbf\x03\x21\x17\xcb\xe1\xf4\x05\x1a\x77\xb6\xbf\x87\x08\xcd\x82\x88\x2b\x50\xeb\xda\x77\xaa\xa3\xfd\x24\xcd\x3a\x0c\x2f\x9f\xe9\x25\x3f\x29\xfa\x06\x60\x3a\x81\xe9\xf8\x81\xfa\xb3\x20\x3f\x3a\x4f\xde\x41\x98\xb0\xe2\x12\x22\x6f\x5a\x5c\xa0\x04\x3d\x9f\xe9\xe5\x69\xb9\x6d\x67\x3b\x4d\xc8\xe1\xbb\x83\x76\xd7\x5a\xfc\x42\xb6\xa8\xa8\xd3\x31\xb3\xd0\xcb\xe4\x00\xfb\x20\x14\xce\xf2\x09\x3a\x6e\x44\x39\xc9\x8b\x88\x87\xd2\x89\x42\x44\xd4\xd8\x2c\xb4\x14\xe1\x7e\x3b\xce\x4e\xf9\x69\x49\xca\x01\x6c\xf7\xc8\xa8\xe8\x47\x8f\x53\x81\xd9\xab\x69\x9a\x50\xa1\x79\xea\xac\x7f\xb2\xc5\xfe\xf3\x2c\x2a\xc0\x5f\x8a\xc5\x8d\x10\x88\x75\x84\xfa\xe4\x9e\xa1\xa4\x8b\xc1\xf5\xb2\xda\x85\x02\xc9\x3b\xf4\xaa\x17\x04\x6b\x98\x7e\xac\x7a\xe9\x07\xf4\x74\x85\x18\x9b\xec\xae\xc1\xb9\x57\x40\x91\x44\x53\x3d\x96\x88\xe7\x08\x55\x7b\xd6\x94\xbd\x0c\xd1\xb3\x5f\xdf\xa8\x2a\x2c\x9e\x6f\x26\x36\x28\xaa\xc6\x52\x83\x39\x94\xda\x7d\x94\x58\x7f\xbe\x7d\xd2\x32\xbb\x13\xda\x44\xeb\x8c\xe2\xb8\xe3\xf9\x57\xba\x04\x2b\x6b\xfd\xda\xac\xd5\xde\xb0\xd9\xed\x46\xbb\x45\x72\x6c\x98\xdd\xc7\x76\xda\x9a\x0f\xc2\x8b\xad\xb4\x20\xf9\x72\xb1\x48\xb3\x02\x74\x6b\xfc\xa6\xf6\xdd\x01\x51\x5a\x95\xb6\xe1\x08\xb2\x9c\x30\x1b\xbf\x54\xb8\xc9\x62\xac\xa7\xb2\x95\x28\xcc\x7b\xac\x07\x9a\xaa\xb4\xa0\x47\x0e\x75\xed\xdd\xb4\xd4\xdb\x8d\xab\xc7\xd5\x18\x74\x9c\xb4\x97\xbc\xd2\xbe\x3e\xed\xed\x7c\x73\xaf\xd2\xbd\x57\xe9\xfe\x57\xa8\x74\xc5\xc3\x8a\x5b\x3d\xc7\xde\x0f\xb2\x34\x21\xff\xbb\x9c\x07\x67\x51\x4e\xbe\x0f\xd8\xe7\xdf\x3e\xf3\xcf\xfe\x9c\x7a\xd5\xbd\x83\x01\x39\x4c\xa2\x22\x0a\xe2\xe8\x57\x4a\xfe\xce\x7b\xc1\x08\x35\x20\x39\x58\x62\x49\x83\x1b\x18\x28\x5b\xaa\x86\x93\xf3\x3e\x68\x75\x65\x31\x19\x45\x44\x84\x4f\x3b\x0c\x87\x64\xb3\xee\xe6\x8d\x5b\x7b\xb0\xe1\xdb\x6e\x75\xbd\x66\x26\x5e\x77\xba\xfa\x15\x9a\x8c\xd4\x36\x91\x08\x85\x96\xb4\x41\x8f\xc7\x09\x2f\x7f\x9d\xd2\x43\xaa\x9e\x89\xac\x46\x66\x49\xdf\xbb\x5e\x37\x44\x68\x04\xac\x3d\xa7\xf7\x83\x35\x81\x9e\x12\x57\xbc\xbc\xad\x9e\x68\xcc\x70\x9a\xca\xb3\xba\x65\xaa\x65\xd9\xa4\x63\xcc\xa3\xcc\x76\xd7\xdb\x28\x66\x5a\x10\x9e\xb1\x33\xaa\x9c\x1d\x72\xf8\x02\x72\x64\xef\xd4\xa4\x6d\x6c\x94\xf9\x19\xf2\xbf\xfe\xe1\x6f\x85\x9c\x6a\x74\xb6\x7c\x1e\x24\x46\xaa\xd2\xe5\xbb\x20\xfe\x3f\x3b\x30\xc9\x17\x42\xcd\x0d\x2f\x24\x0e\xd4\xe1\x51\x1a\x10\xf9\x4d\x75\x94\xb2\xae\x2e\x6e\x9f\xe7\x65\xb6\xd5\x80\xdf\x3c\x43\xa2\xc1\x6a\xcf\x0a\xe7\xcd\x13\xad\xcb\x50\xee\xd3\x07\xe9\x9c\x05\xd0\x33\xd5\x76\x9f\x9e\xd1\xec\xb2\x23\xbd\x21\x7f\x88\x92\x69\x4c\xdf\x70\x84\x77\xc9\x90\x78\x33\x74\x4d\x62\x5a\x55\x47\xfc\xe0\x62\x02\xd5\x41\x4b\x09\xef\x92\x6e\x90\x05\x91\x4c\xe3\x14\x69\xd8\x16\x89\x0c\x39\x3f\x7b\x7b\x7b\x9c\x6a\x30\x90\x70\xbb\x20\x61\xd9\x99\x9b\x81\xf1\x6b\xdd\xb6\xaf\x3a\x21\xc3\x5a\x3e\x25\x07\x03\x1e\x58\x52\x25\x09\xaf\xec\x98\xb9\xc8\xf5\xd8\xc8\x9f\x3c\x67\x44\x23\x78\x8f\x56\xc3\x8e\x9e\x33\xa0\x72\x17\xdf\xa2\xe3\x16\x7f\xe1\x75\xe5\x9c\xa9\x8a\xaa\xa4\x80\x13\x76\x41\x79\x24\x16\x45\x47\xf2\x9e\x2e\x99\x44\x34\x0e\x2d\xd3\x03\xd1\x8a\xd1\x53\x8b\xe7\xe0\x0e\x5a\x8c\x87\x77\xcd\x22\x43\x99\x6c\x45\x7d\x90\x64\xe1\x3a\xc2\x72\xd8\x9b\x84\xed\x4b\xd6\x26\xbf\x05\x8b\x33\xf5\xf0\x8e\xac\x28\xea\x13\x72\x22\x13\x03\x9f\xdc\x8b\x81\xf7\x62\xe0\x5f\x5b\x0c\xd4\xef\xf3\xf8\xa2\xb9\xab\x17\x7a\x77\x73\x77\xcf\x40\xde\x48\x75\x63\xa9\xb1\x32\x9c\x13\x45\xa4\x16\x69\x85\xcc\x3e\xd1\x29\x52\xb8\x5c\x93\xb9\xec\xd3\xb8\xb8\x07\x9e\xa7\xf3\xb5\x64\xb0\x89\xc0\xc0\x27\x3f\x0e\xa9\xa9\x0d\xa1\x71\x06\x2a\xc1\x3d\x3d\xfb\x8a\x58\x39\x86\xd2\x15\x34\x06\x6f\x82\x24\x98\x52\xfd\x3a\x9f\xb1\x2c\x8e\x0a\x43\x15\x20\x5d\x78\x68\x70\xb4\xdf\xcf\x0d\x0c\x39\x15\x67\xf3\x1a\xfb\xf7\x90\x32\x0e\x13\x25\xa6\x7f\x4f\x4b\xfc\x1b\x05\x39\xf7\xb9\x50\x16\x89\x62\x4a\xc1\x4b\xa5\x67\x93\x32\x3d\xcd\xdb\x8e\x45\x65\x9b\x66\x7b\x40\x62\x0e\x22\x44\x1b\xa5\xb1\x26\x0c\x77\xa2\x28\x7c\x8e\x22\x0e\x65\xc7\x27\x7d\x19\xe6\x4c\xb0\x51\x29\x75\x6e\x8e\xb9\x33\x4e\x7d\x49\x21\x42\x73\x88\x6d\x57\x8d\xb3\x4f\xde\x30\x56\x1e\xd1\x5c\x84\x40\x07\x7c\x38\x5e\x28\x0d\xcf\x9e\x8d\xf1\x26\x07\x75\xf5\x76\x19\xc7\xda\x31\x46\x8f\x49\x91\xf4\x22\x82\x6b\x33\x1f\xee\xfe\x98\xf1\x87\xee\x2c\xec\x0e\x59\xfb\x5a\x71\x77\x1c\x4c\x36\x8a\xb6\x63\x07\x38\x51\xa1\x64\xcc\x83\x18\xa9\x09\x1f\xf3\xfe\xdd\x81\x88\x30\x51\x1d\x3b\x46\xa3\x4d\xb8\x7a\xe5\x84\x07\x48\x57\x27\x4e\x1b\x4d\x1c\xf4\x90\x41\xba\x58\x32\x88\x4e\x25\x79\xd0\x81\x6a\xa9\xc4\xc6\xba\x87\xbb\x96\x50\x90\xef\x71\xa3\xa7\xb4\x25\x43\x2a\xa7\x8b\x3d\x02\x21\xde\xab\x42\x48\x91\x67\xfa\x37\xa7\x6e\x28\x72\xca\xd8\x01\xfa\xac\xf1\xac\xef\x60\x9d\xf3\x7b\x15\x7b\x19\x63\xde\x45\x3c\x77\xc0\x5b\x7d\x56\x34\xdd\x11\x97\xe0\xde\x13\x23\xc5\x0c\xf5\x8c\x51\x68\x6f\x56\xe0\x6c\x06\x8e\x3d\xcf\xbc\x00\xaa\x2a\x6f\x6c\x12\x81\x0b\x5f\xc8\x22\xf9\x7e\x4a\xd2\xe1\x0a\x91\x8b\x02\xb9\x6e\x1b\x21\xa1\x59\x0c\x22\xec\x8e\x55\xec\x23\xb6\x97\xe4\x95\x9d\x2f\x0b\x79\x02\x80\xd1\x32\xc0\x80\x90\x67\x04\x18\x52\xc7\x14\xbf\x16\x44\xaa\x33\x40\xb3\x54\xa2\xcc\xa8\x72\xab\x8c\x55\x1c\x0e\xaa\xa4\x8b\x5c\x8e\x4f\x53\xda\x1a\xfd\x82\xd1\xc5\x32\xe4\xd0\x46\xcb\x28\x0e\x01\x61\x62\x50\x2c\xd3\xf1\x6f\x0b\x0c\xff\xe3\xd1\x8b\xa3\xf5\xf5\x75\x10\xef\xdb\x39\x59\x4e\xe3\xcb\xbe\x88\x22\xc6\x0e\x04\xcb\x9c\xed\x89\x85\x6a\x25\x41\x2e\x65\xd9\x6f\x69\x57\xa3\x6e\x48\x18\xe3\x80\x0c\xf5\xde\x7a\xcb\x88\xf4\x34\xfa\xe5\x84\x65\x9f\x6c\x9e\x9e\x32\xb1\x0b\x7f\x5e\x5d\x29\xbb\x4d\x1b\x94\xff\xd8\x82\x32\x6c\x2c\xbb\xfe\xab\x22\xab\x76\x80\x24\x88\x0b\x3b\xe8\x55\x88\x2a\xbb\x45\x55\x97\xea\xda\xe8\x94\x87\x40\x49\xfc\xcf\xb2\x88\xe3\xe7\x5b\xc8\xef\xfa\x34\xbc\x8a\x1f\x68\x62\x45\xb0\xf0\x85\x2a\x30\xce\xea\xd0\x96\x29\x51\xea\x8b\x29\x7d\x3f\x63\xc4\x62\x51\xe6\x75\x1e\xd3\x3c\xbb\x61\x0e\x2f\xda\xc1\xcc\x4c\x19\x45\x5a\x06\x34\xde\x70\x2a\x66\x77\x8d\x6a\xca\x87\x60\x5f\x43\x09\x52\x61\x59\x4d\x3d\x3d\xcb\x30\x57\x34\xa9\x77\xe7\x28\x39\xe4\x32\xa3\x70\x43\xfa\xfe\xdd\x81\xf2\xc0\xc4\x4d\x59\xc6\x41\xa2\x84\xcd\x28\x11\x4a\x17\xbf\xaf\xa7\xcc\xf5\xf5\xd8\xef\xf7\xaf\x71\x7c\x37\xdb\x97\x9e\xd6\x64\xca\xa2\x1e\x4e\x5a\xe7\xd3\xbe\xd4\xdd\xfc\x2a\x44\x28\x69\xc0\xf4\x49\x8f\x67\xad\x0c\xd1\xa2\x64\x89\x62\xe7\x8d\xb4\x81\x69\x7a\xfd\xf7\xed\xbd\xde\xe7\x5e\xef\xf3\xd7\xd6\xfb\x08\xa5\x4f\x38\xba\xc5\xcd\x9f\x4f\xef\xa3\xb4\x35\x58\xf1\xc3\x99\x93\xd2\xe8\xbc\x78\x6e\xf0\x11\x36\x0c\xd3\xe5\x87\xa3\xa9\x80\x91\x5a\xc9\xbb\x15\x81\xc2\xd6\xb4\xbc\x94\x77\x3c\x36\xfd\xe2\x82\x8b\x7c\x21\x96\x74\x65\xc9\x41\x1d\x56\x33\xda\x59\x04\x90\xa3\x76\xe9\xf8\x3a\x68\xe9\x9b\xf5\x2e\x5f\x1e\xb0\x68\xb1\x2c\xd4\xe3\xb5\x84\x9e\x0b\x6c\x76\xf4\x76\xc9\x84\x8e\x21\x69\x2b\x38\x2b\x8e\xc6\x90\xb4\xc3\xd1\x27\x5f\xae\x14\x13\x77\x54\x9f\x54\xa3\x53\xda\xac\x51\x05\xe7\x6d\xd4\x97\x2b\x1b\xdd\x76\x1b\x5d\x2c\x8b\x57\xf4\xa2\x7e\x98\xaf\xe8\x45\xd9\x18\xcd\xac\xea\x01\xd6\xb7\xc5\x81\xca\x86\xe6\x6f\xcb\x1a\x97\xd8\x8c\x4e\x34\x9c\x9c\x88\x9e\x46\x72\x4f\x0c\xbd\x27\xba\x05\xc0\xa7\x25\x3b\xd7\x8b\xe7\x7a\xd7\xe2\xb4\xd3\x1a\xee\xc0\x16\xf5\xf4\x7e\x8b\xba\xdf\xa2\xfe\xda\x5b\x94\xbe\x9a\xa0\xc5\xec\x46\xf7\x12\x02\xf8\x6e\x5f\x25\x96\x44\xff\xf7\x85\xff\xf7\x5d\x82\xf8\xef\x41\x6a\xb6\x4d\x06\x22\xcd\x91\x2d\xa0\x85\x48\x96\x60\xe3\xb2\xf6\xc6\x69\x32\x89\xa6\x12\x0c\x85\xc2\xc1\xd0\x32\xb2\x8a\x04\x3b\x17\xcf\xd6\x8c\x0b\x1a\x91\x28\x61\x7e\xe4\xa1\xc0\x2d\x64\x40\xa2\x04\x39\xcc\x3f\x5c\x26\x63\xbe\xc5\x60\xa8\x9c\xa7\x4a\x30\xc6\x8a\x33\x6a\x03\x89\x54\x55\x17\x77\x50\x84\x21\xa2\x51\x90\xc8\x6c\xee\xf5\xd0\xe9\x8f\x4c\x56\x42\x08\xf8\x4c\x6b\x72\x67\xa0\x74\xde\xe2\x8d\x20\x28\x01\x37\x4f\xbb\xe4\xc1\x03\x22\x7e\xf7\x41\x27\x78\x34\xe9\xb4\x37\x2f\xda\xdc\x75\xc9\x66\x97\x3c\x23\x2d\x5a\xcc\xd8\xee\x01\x81\x49\x9f\x5f\xbe\x0a\xf2\x59\x8b\x0c\xed\x64\xae\xd1\x6d\x69\x29\x01\xc5\x7f\xfa\x31\x4b\xe7\xcf\x7f\x83\x9e\xb6\x45\x97\x50\x58\xa1\xe7\x97\xd0\x30\xeb\xf4\x7e\x12\x1e\xb2\x72\x2a\x9a\x97\x17\x92\x8f\x43\xc1\xea\xf1\x2c\x93\x71\x4c\x7f\xa3\x01\x1c\xb3\xb6\x6a\xba\x8e\x61\x4a\x3b\x2d\xe7\x07\x8d\xf3\x20\x5d\x26\x8d\xae\x99\xee\x60\x1c\xde\xb6\x39\x09\xe1\xa1\x94\x80\xf1\x51\x39\x53\xf0\x1b\xf6\xff\x58\x35\x88\x26\xc3\x99\x04\x0c\x60\xf4\x59\x75\xef\x65\x31\xbb\xeb\x03\x42\xe3\xc3\xc1\x1d\x9d\x0d\x20\x00\x70\xf9\xd9\x80\xab\x3e\x38\x17\x8f\xa8\xb7\x47\x0b\xdc\x99\x45\x4d\x3f\x16\x37\xe8\x02\xba\xe3\xe6\xdc\x95\xfb\xbf\x20\xd8\x43\xf7\xe1\xf3\xfd\xb7\x56\x30\x32\xc1\x53\xb9\x56\x86\x3f\xa0\x15\xba\x99\xeb\xb5\x35\xde\xbb\x3e\xb7\x8c\x52\x6f\x69\x5e\x16\x33\xad\x0d\xea\x91\x36\x0e\xdd\xdc\xee\x89\x61\x4e\x69\x31\x2c\xd1\x79\x4a\x5f\xa5\x7d\x5c\x50\x8c\xa4\x27\xf4\x74\x46\xe1\xb3\x20\x36\xa2\x8c\xf5\xad\xc0\xd9\x67\x41\xec\x38\x23\x51\x69\xd7\x6b\x80\x9e\x95\x86\x22\xfc\xfc\xdd\x64\x30\xa2\xe8\x4d\x86\x23\x8a\x36\x1c\x50\x93\xb3\x28\xe3\x2e\x41\x0c\x96\x9b\xb5\x27\x27\x01\xe8\x9e\x9e\x24\x9b\x72\xf2\xd5\x11\x0a\x59\x73\x1a\x57\x78\x43\x72\xa2\x05\x2a\x7e\xbd\x27\xdc\x68\xfe\xa8\x6f\xf3\x6c\x08\x1c\xf9\x9c\xf3\x13\x05\x8c\x42\x47\x5a\xf7\x58\x43\x5c\x0d\xcf\x53\x3e\x6b\x14\x50\xc9\xb1\x39\xcd\x82\x29\xdd\x2f\x9a\x9c\x9c\x05\x68\x29\x8e\x7c\x10\xea\x50\x5b\x81\x25\xbe\xee\x38\xc7\x2e\x52\x38\x59\xae\x82\x16\xef\xc0\x84\x73\xc7\x9a\x31\x31\xa8\xd2\xe1\x58\x99\xbf\xfd\x7c\x7b\x07\x26\x57\x7d\x1d\x3d\x73\x76\x64\x0d\x4d\x89\x8c\xb7\x1b\x96\xaf\xb7\x3d\x67\x89\x6b\xfb\x57\xb6\x78\xc9\xf5\x6a\xf4\xcb\x9a\xa8\xa6\x5d\xd8\x7f\xeb\x31\x01\x60\x0e\x26\x94\x44\xf7\x35\x30\x81\x48\xf9\x16\x83\xee\xad\x95\x50\xf6\x7c\x11\xc5\xfc\xf0\x56\x4b\xde\x02\xb4\x82\xc6\x5d\x08\x89\x87\xcd\x72\xfa\xb3\xe5\xb5\x86\xf4\x68\x17\x73\xba\x55\x25\xb2\xba\x1d\xdc\xba\xe5\x44\x55\xcd\x8d\x9c\xc2\x17\x74\x1c\xcd\x83\xb8\x1c\x15\x5a\x0a\x6c\x88\x04\x5d\xa0\x84\x28\xff\xb8\x03\x36\x85\xa7\x9a\xc1\x96\xc7\x4a\x2e\x39\x82\x81\x7c\x5d\x39\xe8\xfa\x15\x84\x2a\xac\x66\x1e\x1f\x3d\x27\xd4\x95\xc6\xa4\x4a\x39\x83\x2b\x3b\xfc\xfe\x91\x38\xcd\x4d\xf0\xf4\x9e\x8e\x69\xb4\x68\x40\xe6\x6e\x99\x26\x04\xe0\x82\xde\x96\x02\x44\x8d\x8d\x07\xd8\x70\x15\xd7\x72\x31\xcf\xe0\x6c\xc0\x26\x14\xc0\xc5\xa2\x3b\x12\x10\x6b\x97\x37\x3b\x20\xbd\x0f\xce\x9b\x2f\x71\xb7\x80\x1f\x11\x95\x70\x4d\x38\x1b\xc3\x83\x47\x16\x72\x43\x4b\xd7\xf5\xb6\x51\x57\x6f\xde\x4f\x7b\xa6\x7c\x6b\xcc\x37\x8e\x68\x9a\xac\x30\x0e\x13\xba\x64\x1c\xa5\x40\x5f\x79\x1c\x0d\x3a\x5f\xde\xe3\x3b\x97\xb5\x4b\x08\x47\x18\x77\x55\x75\x14\x02\xff\x7b\x3b\x6a\xe5\xdc\xa4\xa3\x6c\x2f\xb8\xb3\x13\x81\x19\x21\xbd\x6a\x4c\x08\xd2\x3f\x34\x3f\xc0\x4d\x28\xc6\x18\xe1\xad\xb8\xd2\x98\xcb\xa7\x32\xae\x79\xdd\xb4\x71\xe8\xbe\x0c\x76\x5e\x32\x85\x66\x9d\xbe\xb1\x96\x76\xe4\xf5\xeb\xd7\x0d\xfb\x10\x97\x52\x90\xaa\x69\xa5\x96\x3f\xd0\x6c\x41\x6b\xb7\x27\x85\x01\x0e\x5d\x8d\x00\x07\xa6\xa2\x17\xf9\x72\x34\x8f\x8a\x9f\xd3\xac\x4e\x4a\xd2\x80\x25\x2b\xdd\x97\x5f\x7d\xf5\xdd\xa0\x55\x01\x55\xba\x15\x97\xb4\x67\x1d\x71\x9c\xeb\x6f\xad\xf8\xe9\xe1\x34\xa5\xe8\x30\x52\x0f\xd2\xd0\x04\x83\x25\x6c\xa4\x80\xec\x6f\x15\x12\x07\x30\xb7\xa4\x2d\x3e\xb8\x10\xfa\x28\x61\xe4\xa1\x82\x65\xe9\x4a\x30\x2d\x03\x10\xb2\x53\x59\xb6\xd5\xa8\x69\xd0\x8b\x18\x89\x4e\x74\xc5\x00\x94\x67\xee\x57\x66\xa1\xd2\x12\xa8\x79\x73\x45\x3b\x19\xaf\x5f\xbf\x76\x81\x39\xf5\xa3\x2a\x15\x61\x1a\x83\x66\x09\xf0\x2d\x2c\x1c\x78\x4c\x36\xa5\xec\xae\xf2\xd1\xac\xe9\x88\x91\xae\xd2\xba\x9a\xa6\xa3\x6a\xe1\x46\xc9\x28\xc8\xb1\xa2\x42\x74\x00\x18\xa5\x58\xb7\x02\x46\x81\x5c\x77\x7b\x2b\xb4\x31\x8f\x12\xd3\xba\xc5\x69\x41\x40\xdc\xb0\xfe\x59\x90\xcf\xb2\xa0\xa8\x1c\x43\x09\x4c\xa3\x9d\x61\xf5\x1e\xc9\xdb\xd9\x8a\x0e\xf9\x41\xea\xcf\x19\xe2\x3a\xd8\x3c\x5c\xac\xde\xc3\x69\x90\xbf\xcb\xa2\x71\x25\xce\x4a\x60\x6e\xac\x04\x5e\xbd\x97\x22\xec\x50\x5e\xd5\x4b\x05\x73\xc3\x36\x46\xe8\x0a\xad\xa2\x99\x72\xb0\xaf\x44\x43\x32\x26\xc3\x3f\xb8\xad\x4d\x55\xdf\x6c\x50\xd4\x22\x66\x21\xc6\xb5\x4b\x7f\xac\xed\x18\xd0\x95\xe6\x28\x32\x5e\x2c\x04\xe3\x22\xcd\xa4\xf8\x23\x2d\x1f\xc0\x8c\xb8\x47\x18\xac\x61\x4b\x2c\xa0\x7d\x8d\x4d\xa4\xa5\x83\xf3\x14\xb5\x87\x9e\xdb\x71\xa8\x83\x8c\x82\xa5\x12\x3c\x1e\x3b\xb0\xcc\xe9\x51\x9c\x22\x6e\x3d\xa1\xeb\x61\xa8\x90\xe1\xe6\xad\x1b\xbb\x9e\x34\xc7\xe8\xd3\x62\xd6\xe9\xf6\x5c\x92\x7d\x9d\x4e\x91\x6c\xdc\xac\x4b\xbe\x81\x6a\x43\x8c\x6a\x17\xfa\x12\xfb\x1d\x51\xa0\x3f\x8d\xd3\x51\x10\xf7\x19\x52\xfb\x81\x9b\x2c\x62\x9e\xf9\x9a\x8c\xc6\xc1\xff\xc7\xde\xbb\xef\xb7\x6d\x33\x8b\xa2\x7f\x27\x4f\x81\x66\x9f\xaf\x91\x63\xc6\x16\x75\xb3\xa3\xc4\x5d\xcb\x91\xed\xd8\x2b\x71\xec\x6d\x3b\x6d\xbf\xed\x9f\x9b\x1f\x25\x42\x16\x1b\x89\xd4\x22\x29\x5f\xda\x78\xbf\xcf\x79\x8e\xf3\x62\xe7\x87\xc1\x85\xb8\x52\x94\x2f\x69\xda\x65\x7f\x6b\x35\x22\x09\x0c\x06\xc0\x60\x30\x18\xcc\x65\xfa\xf1\xb6\xcd\x92\xca\x46\xa3\xf4\x65\x59\x93\x92\x55\x4a\xd1\xa0\xee\xfa\x20\xe7\x94\xe2\x15\x6a\xf6\xe9\x99\x17\x8d\x6a\x3b\x1f\x15\xf6\x7c\x9a\x61\xce\xb3\xae\xbf\xee\x3d\x33\x0c\x84\x98\x81\x7a\x61\x99\xf3\xac\xdb\x68\xc3\x0b\x3a\xa7\xcf\xba\x8d\x57\xf4\x51\xd0\xc2\xb3\x6e\x93\x56\x89\xfa\x41\xfc\xac\xdb\x6c\x7a\xaa\xf9\x20\x3c\xb2\x41\x7a\xd6\x6d\xb5\xe0\x99\x9b\x11\x3d\xeb\xb6\x28\x78\xc6\xd9\x9f\x75\x5b\x14\x2d\x7e\xd9\xfb\xac\xdb\x22\x0d\x72\x23\xa0\x67\xdd\x56\xf3\xe6\xcc\x6b\xbe\x7a\xb4\x47\x7c\xb4\x47\xfc\x67\xdb\x23\xba\x8c\x11\xef\x6c\x33\x5f\xdd\x4c\xb0\x82\x0d\x20\x94\xfb\x88\xf3\x87\x34\xb1\x87\xb7\xf3\x4d\x56\x0a\xe3\xfa\xdb\xd8\xac\x54\x30\xa9\x5f\x5d\x5d\x2d\x62\xd2\xd8\xe2\xdc\xb0\x84\x8d\x84\xc5\x03\x38\x9c\x8f\x50\x30\x8d\x24\xdc\x1f\xe8\x40\x62\x26\xa3\xd7\x04\x1e\x35\x63\xfd\x6d\x85\x2b\x8c\x53\x5d\x37\x6e\xb4\xe2\x2a\xb4\x80\xc0\x27\x8b\x5f\xc6\xa6\xf6\x11\xe7\x96\x4d\x4d\xdd\xbc\xe4\xdd\xe5\xe6\xcc\x6b\xd5\x1f\x77\x8b\xc7\xdd\xe2\x9f\xbd\x5b\x7c\xa7\xd6\xeb\xf7\x67\x68\x5e\xd1\x0e\xbe\x30\xe5\x3c\xc4\x69\x96\xc4\xc1\xf8\xd1\x9e\xf3\xa1\xed\x39\x6f\xaa\x59\xf8\xc5\xf8\xb2\x30\x1b\x2c\xd3\x0f\x17\x05\x4d\x15\xf1\x94\xcd\xea\x67\x6b\xa1\x3b\xdc\x50\x46\x13\xb2\x11\x1c\x05\x97\xef\xf1\xbc\x9b\x0b\xb9\xe8\x73\xef\xe9\x93\x27\x3a\x6e\x46\x81\x12\xcf\xb4\xea\x37\x71\x66\x3b\xe2\x83\x64\xb8\xf7\xe4\x49\xc5\xbb\xe9\xca\x57\x70\x78\x70\x84\x07\xc9\x05\x0d\x0e\x55\x76\x67\xc5\xcb\x59\x71\x55\xbf\x96\x0c\xc8\x2c\x1e\x27\x83\x2f\xd5\x28\x45\x29\x5b\x42\x2c\xae\x72\x55\x0c\x1e\xab\x8d\x9b\x73\xf4\xee\xf9\xe6\xbb\x98\xfb\xb9\xd7\xdf\x8b\x5c\x73\xda\xae\x8d\x9d\x5d\xaa\x3e\x3f\xd5\x66\xa7\x7c\x6e\x16\xb9\xcb\xd4\xe7\x46\x43\xde\x26\x59\xb3\x86\xa5\x46\xa4\xc5\x5b\xbc\x55\x28\x48\xba\x3d\xe1\x54\xed\xba\xed\x70\x5e\x8a\x48\xe0\x64\x79\xf7\xe1\xce\x07\x9b\x73\xd4\xc2\xc5\x74\xc8\x85\x1d\x62\xb9\x29\x97\xf3\xed\xb6\x10\xce\x2d\x2a\x22\x4d\x2b\xa4\xcb\xe9\xfe\xa3\x9c\xfe\x28\xa7\xff\xb3\xe5\x74\x26\xa4\x67\x23\x87\x56\x67\x8e\xf8\x8d\x53\x3c\x9b\x10\xd0\x3f\xcd\x51\x02\x0d\x92\x14\xaf\x44\x89\x2a\xa7\xaf\x55\x0e\x9c\x50\xd1\xd1\x72\x9e\xbf\x26\x14\x3a\x1e\x8d\x1e\x5c\x3b\xf4\xfd\xc8\xe3\x84\x3b\x1e\x8f\x94\xdb\x0d\x7c\xc9\x82\x4e\xef\x7c\x8b\x0b\x9d\x6c\x34\xff\x42\x27\x1b\xc1\x85\x0e\x15\x5c\x16\xb9\xb7\x29\x93\xf3\xdd\x9b\x93\x21\x1e\x48\x5b\xd3\x85\xf5\xa6\x8e\x89\x08\xd9\x68\xf4\xd9\x5e\x40\x35\x0a\x41\x16\x5d\x56\x59\xa3\x51\x3c\x4c\xdc\x2d\x5a\xbe\xde\xad\xb9\x0c\xe7\xfb\xc1\x15\x23\x82\xe3\xe8\x0f\xfd\x72\x58\x6a\x7b\x5e\x51\xd5\xea\xe7\x36\x88\x44\xf1\x61\xf2\x4b\x39\x02\xb6\x22\x77\x6b\x78\x12\xa4\x5f\x4e\xd2\x59\x96\xe3\xf0\x10\x1b\x97\xc1\x52\xf3\xe5\x05\xef\x86\x44\x8c\x89\x4c\x77\x18\x44\x25\xed\x3b\xcb\xdc\x8d\x02\x82\x30\x3c\x4c\xa3\x8b\x20\xc7\xf4\x48\xe8\x68\xbd\xac\xd8\xdd\xfa\x4e\x93\x7e\xcd\xed\x7e\x59\xb1\xbb\x21\x30\x0a\xb2\xb9\xad\x3b\xcb\xdc\xad\xe9\x73\x9c\xd3\x0d\xbd\x74\xec\x4b\x4a\xdd\xbd\xf9\x0a\x73\x5f\x56\xec\xce\x74\x7f\x7c\x3d\x29\x6d\xdc\x55\xe4\xce\x54\x3f\xaf\x61\x57\x91\xbb\x0e\x39\x91\xe3\x72\x4c\x41\xef\xa4\xc9\xe4\x30\xc8\xb2\xcb\x24\x0d\xcb\xc6\xbf\x62\x9d\x3b\xaf\x83\x79\x63\xe2\x2a\x72\x67\x32\x9c\xd7\xb0\xab\xc8\x7d\xb0\x9e\x79\x6d\x97\x94\xb2\x37\x2f\x1e\x56\x57\x51\x36\xeb\xc3\xcd\x1b\x4d\x69\x3c\x8b\x8b\xe7\x49\x94\x65\x51\x7c\xfe\xb4\x32\xb6\xd3\x24\xd3\xaf\xae\x24\x2c\x2d\x5f\x2d\x7a\x0a\x54\xae\x77\x44\xf3\x6f\xb9\x8e\x47\x23\x29\x81\x98\x66\x7b\xa1\x9c\xa2\x35\xcb\x88\x56\xe3\xf1\x0c\xfd\x78\x86\xfe\x67\x9f\xa1\x8b\xbb\xae\xfe\x1f\x7f\x68\x77\x5d\x9b\x63\x7c\x85\xde\xe2\x14\x9f\x67\x7f\x04\xd9\x1f\x11\x7a\x13\x8c\xf1\xd5\x7f\xa6\xf9\x30\x5b\x19\xcd\xd4\xe3\x70\x87\x45\x33\x3d\xc2\x43\x9c\xe2\x78\x80\xbb\x88\xb4\x9f\x75\x57\x57\xcf\xa3\x7c\x34\xeb\xaf\x0c\x92\xc9\x2a\x3f\x75\xaf\x9e\x27\x2f\xc5\xef\xfe\x38\xe9\xaf\x66\x97\x41\x3a\x59\x8d\xe2\x1c\xa7\x71\x30\x5e\x25\x5d\xc2\x57\x39\xff\x77\xe5\x3c\xf9\x5f\x1f\x9a\xcd\x07\xbe\x1a\x2b\xee\xbb\x8e\x09\x36\xff\xf0\xc3\x35\xfc\xf8\x5b\x5c\x76\x51\xcb\x57\x9c\x5f\x26\xe9\x97\x23\x0c\xa1\x6a\xcb\x14\xe5\x7a\x71\x53\x5b\xde\xff\xe3\x8f\xcf\x25\xa5\xee\xe2\x9b\x77\x1d\x0f\xb6\xe3\xa0\x3f\xc6\xf3\xb0\x94\x4a\xda\x11\xb4\x17\xb8\x0b\x6e\x97\xc1\xb4\x22\x6e\x45\x49\x07\x6e\xd6\x02\x77\xc0\x2d\x4c\x2e\x63\x16\x85\xb8\x0c\x31\x5e\xcc\x8e\x95\xe5\x6b\x75\x77\x53\x07\x62\xb3\x69\x05\xb4\x68\x21\x3b\x52\xc6\xb7\x3b\xa3\x94\xe2\x3c\x8d\xf0\xc5\xbc\x08\x10\xbc\x98\x1d\x2d\xcb\xd7\xbb\x90\x56\x4e\x76\xbb\x39\x44\x45\xca\x38\xc8\x49\xfb\x74\xe7\x21\x3a\xc7\x15\xdc\x99\xed\xb8\xa8\x1f\xee\x30\x26\x34\x7b\xc3\x9c\x18\xa9\x76\x1c\xd4\x0f\x77\x1e\x0d\x96\xb0\xa5\x1c\x19\x5a\xc8\x8e\x8f\xf1\x8d\xa3\xd4\xaa\x84\x52\xc9\xad\xae\xa1\xe2\xd4\xd9\xb2\x74\xfb\x57\xf0\x43\xe9\x65\xc1\x88\x8a\x97\x9c\x0f\x48\x37\x8e\x53\xf5\x99\x53\xbf\x04\x88\x90\x60\xf1\x78\x8e\xa5\x8b\xc9\xe9\x4c\x7a\x90\x64\xf1\x07\xbd\x66\x1c\x45\x17\x4e\xdf\x18\x32\x27\xf0\xdd\x79\x86\x2c\x87\x6d\x51\xca\x2a\xb0\xe1\xbb\xe3\x78\x65\x39\x5f\x11\x61\xc9\x16\xec\xd6\x7a\x2f\xd9\x7c\x3c\x53\x3d\x9e\xa9\xfe\xd9\x67\x2a\x76\xa0\xe2\x17\x44\xdf\x36\x4a\xfb\x6d\x0c\xab\xb9\x77\x54\x30\x8d\xb8\x30\x4e\x53\xfc\xe5\xa3\x32\x0b\x34\x7a\x5d\x56\x1a\x95\x92\x97\xce\xaf\xa7\x44\x3e\x60\x11\x28\x5f\x3f\x95\x18\x78\x94\x0f\x46\x35\xf2\x5d\xcf\x2d\x32\x08\x32\x8c\x9e\x13\x8a\xcf\xf2\xe7\x5d\xe5\x13\x4c\x56\x7a\x9e\xad\x64\xa3\x68\x98\xd7\xb4\x84\x22\xc8\x48\x0e\x58\x37\x0b\x30\x96\x0c\xee\x6b\x31\xbe\xa4\x11\xa8\xe8\x85\xec\x6b\x0b\x1a\x53\x1c\x87\x51\x7c\xfe\xe0\x78\x1c\xd2\x76\x64\x1b\x22\x1b\x52\x2c\x74\xa0\x89\x8d\x06\xce\xa8\x4c\x13\xac\xdc\x48\xd2\x81\x28\x35\xdf\x92\x90\x41\xd3\x65\x04\x85\x14\x2c\xb2\x93\x45\xaa\x8e\xa3\x38\xcb\x83\xf1\xb8\x52\xcb\x5a\x69\xbb\xaf\xbb\xbb\x50\x09\x1e\xe7\x38\xff\x90\x9c\x57\x08\x28\x46\x4a\x39\x7d\xec\x69\x8b\x5a\x91\x92\x56\xa7\xc9\xdc\x38\x1c\xa4\xc8\x9c\xf6\x7a\xa3\x20\x3e\xc7\x15\x9a\xb4\x09\x1f\x14\x84\x6c\x92\xa5\x8c\x9e\x22\x08\x91\x8e\x49\x8d\x24\xe3\xb1\x2c\x0f\x2c\xcc\x6f\xb2\xd1\x68\x05\x58\xa3\xc1\x6e\xb2\x91\xc9\x6e\xdc\xe2\xd3\x9c\x5b\x1a\x83\x0c\x90\x71\x4b\xa3\x58\x12\xdc\xab\x9a\xde\x4d\x8c\xc8\xa6\xa9\x7f\x38\x44\x4c\xd2\x45\xc6\x35\x05\x6d\x96\xe1\xa0\x17\xbd\x5b\xf3\x1a\x19\xdf\x43\xdb\x32\xe9\x19\x92\x28\xc5\x01\xe7\xa3\x2e\xf9\x0f\x05\x96\x8d\x46\x5d\xf2\x1f\x2a\xbc\xda\x12\x32\xb4\x5a\x8f\x22\xe9\xa3\x48\xfa\x0f\x17\x49\x0b\x3d\x3f\xf7\xb1\xbe\xa7\x6c\xd1\xd4\x3f\xfc\x08\x9f\x93\x79\x0e\xd2\xcd\x7e\xe4\xc8\x4b\x90\xad\xbe\x53\x8b\x42\xa2\x7f\xae\x9e\x8f\x06\xc1\x54\x06\xe2\x82\xb1\xd7\xdb\x3c\x34\x21\x48\x98\x30\x47\x74\x66\xbd\x8c\x36\xd0\xf3\xfa\xd5\xa0\x13\xbe\x0a\x1b\x83\xb0\xd5\x7a\x15\xac\xb5\x5b\x83\xd6\xab\x56\xa3\xd3\xc2\xfe\x7a\xfd\xd5\xa0\x5d\xc7\xcd\x56\xd8\x69\xb5\x3b\x8d\xfe\xf3\x02\x17\x1b\x98\xc0\x0f\x7c\xdf\xef\x0f\xea\x6b\xad\xc1\xab\xc1\x30\x58\x5b\xf7\x87\xf5\x41\x73\x1d\x77\x9a\xfd\xb0\xed\x0f\x5e\xf9\xfd\xf5\x60\x58\xaf\x3f\x77\xf3\x26\x8a\x63\x57\x92\x74\x83\x7e\xd4\xb5\x0c\x62\xc1\x09\x99\x1b\x7c\xd7\xda\x3f\xba\xd3\xd3\xc2\x04\x6d\x03\xb2\x3e\xae\x16\xb8\x66\x77\x29\x54\x85\x63\x96\xcf\xe2\xb3\xae\xef\x3d\x9b\x33\x4f\xcf\xba\x0d\xc2\x6c\xdb\x8f\xcc\xf6\x91\xd9\xfe\xb3\x99\x6d\xc1\x6b\xb9\xf2\x4b\x63\xb6\x65\x86\xc9\xc3\x34\xf9\x03\x4f\x82\x78\x25\xc4\x3f\x7d\xab\x74\xfe\xfa\x05\xe9\x5d\xd2\xf5\x53\x45\xa2\xf4\x9d\xbe\x50\x32\x12\x68\x25\x32\xbd\xc4\x6d\x72\xef\x2f\x9e\xe1\xbf\x24\x5b\x3f\x1f\x8b\x87\xcf\xd7\x5f\x35\xc7\xf7\x9d\x53\x7c\x5b\xba\x54\x92\xe4\xdb\x16\xba\x45\x1b\xe1\xff\xb0\xbd\xa5\x75\x21\xdd\xf6\x77\x92\x5e\xdb\xd9\xef\x7b\x4a\xb0\xfd\xc3\x06\x25\x1c\xed\x15\xd9\x50\x86\x51\x8c\xc3\xbb\x64\xe0\xe6\x99\x63\xf3\x04\xb1\xb4\xd5\x45\x3a\x6b\x48\xc7\x2d\x0e\xdb\x22\x21\xeb\x0a\xda\x27\x1b\x5b\x84\x33\x46\x49\x30\x4c\xda\x58\x6a\x39\xb0\xe7\x66\xd3\xe7\xe3\xba\xc3\x46\xea\xeb\xc7\xd9\x78\x7c\x23\x19\xbb\x47\x43\x84\xaf\xa2\x0c\x8a\x5b\x87\x5c\x6b\xb1\x34\xa7\x3c\xcf\x60\xc3\x5b\xa3\x39\x6c\xe4\xbc\xfb\x2f\x91\x7f\xb6\xe4\x48\x33\xbf\x32\x4d\xa6\xb5\x25\xc8\x5b\xcd\xee\xbd\x08\xff\x87\xf5\x04\xa3\xf5\x83\x2c\xdc\xa8\xc3\x4d\xed\x1b\x0a\xcc\xf2\xc4\x4e\x8a\xaa\x1d\x84\x8b\x18\xd9\x2b\xde\x0b\x27\x35\x56\x4f\xdf\x0d\x75\x84\x28\x89\x78\x82\x92\x22\x9d\xf7\x3b\x9c\xd7\xa4\xd3\x39\x8e\x67\x13\x9c\x06\xfd\x31\xee\xa2\x3c\x9d\x61\x53\xf5\x17\x4c\x70\x56\x9a\xaa\x5b\xca\xe7\x0d\x85\x41\x79\x8b\xa4\x1c\xde\xd9\x9c\x24\xde\x99\x96\xc5\x3b\x73\xa4\xf1\xd6\x8b\xbc\x56\x34\x62\xa2\x79\x9f\x67\xef\xa7\x9d\xb0\x67\x77\x49\xfa\xbf\x7b\x50\xde\xa3\x43\xc6\xfa\x42\xe0\x07\xd9\x75\x3c\x78\x07\xfb\x0d\x11\x79\xa1\x0b\x4b\x67\x4a\x4e\xf4\x4d\x56\xa4\x26\xb9\x69\x68\xd5\x94\x49\x02\x10\x2a\xcb\x80\xdb\x65\xb4\x0c\x38\xac\x0c\x46\x41\xba\x99\xd7\xea\x4b\x2b\x79\xf2\x69\x3a\xc5\x69\x2f\xc8\x70\x6d\x89\x7f\x86\xac\xca\x35\x7f\xc9\xb9\xf1\xf0\x99\x75\x67\x1e\x2d\x36\xee\x22\x8d\x2a\x8f\x88\xc6\x6b\x9c\x93\x0e\x99\x2b\x46\x08\x28\x4a\x82\x6d\xf1\xd6\x96\x62\x5b\x55\xf4\xf0\xcc\xf6\xa2\x0a\xdd\xee\x25\x8d\x4d\x91\x8a\xbb\xac\x83\x7c\xd4\x17\xeb\x65\x71\xd7\xef\x0e\x02\x86\x0a\x33\x27\x6b\x87\x68\xda\xf3\x05\x7b\x55\x31\x3d\xba\x9a\x12\xdd\x3e\xd8\x66\x52\xf4\x1b\x35\x7b\xf9\x39\xce\x17\x4c\x5e\x7e\x8e\x5d\xdb\xc9\xf7\x9d\xbb\xdc\x42\x1c\xd5\xb3\x97\xeb\x66\x73\x5d\x59\x1e\x35\x95\xe4\xa7\x67\xaa\x7a\x9d\x4c\x13\xab\xa2\x6d\x56\x15\x13\xa1\xcb\x53\xf6\x50\xe9\xd0\xf9\x00\x49\x07\x73\x2d\x66\x0f\x39\x62\x77\x1e\x8f\xd8\x8f\x47\xec\x7f\xf6\x11\x5b\xd2\x67\x32\x0e\x31\x61\x2c\x5d\x3d\x69\xff\x17\x1e\x0e\x53\x7c\x8d\x7e\x89\xc6\x83\x2f\x18\xbd\xf9\x1d\x0f\x87\xae\x68\x3d\x0b\x85\xf6\xd9\x0f\x52\x72\x84\x3f\x08\xe2\x01\x0e\xa0\xac\x2d\xa8\xcf\x2d\xe2\x00\xb1\x2a\xef\x82\x0b\xf4\x4b\x92\x84\xe8\xcd\xb9\xf3\x90\xdf\x2a\x0e\xf9\xff\xc5\xb8\xa9\xe2\x3c\xcc\x58\x6c\x59\x4a\x5b\x4b\xa0\x3a\x3d\x0b\xad\x2d\x05\x2d\x4e\xd3\x44\x0b\x1e\xb4\x4a\xdf\x51\x1b\x04\xba\xed\xec\xe5\xcf\x33\xb2\x31\x4e\x93\x38\x8b\xfa\x63\x4a\x60\xd3\x00\x9c\x48\xd0\x84\xdd\xf9\x90\xbd\x68\x9a\x26\x17\x51\x88\xd3\x4c\xd4\x0a\xc6\x59\x62\x56\x4d\xc6\x63\x52\x95\x50\x1b\xb7\x1e\x47\x71\x12\xd2\xaf\x51\x3c\x48\x26\x32\x64\x02\x8c\xe5\x14\xa0\x57\xae\x79\x34\xc1\x64\xb1\x45\x19\xf2\x51\x86\x07\x49\x1c\xc2\xee\x18\xc5\xe7\x63\x9c\x27\x31\x0c\x27\xe9\x5e\xc9\x41\x9f\xa3\xaa\x1c\xf7\xf9\x4b\xb4\x21\xba\x22\xe9\x19\x48\xdb\xa0\x01\xbe\x91\x5e\x72\x5c\x64\xad\x83\xf3\xf0\x47\x24\x94\x51\x9a\xc4\xc9\x2c\x1b\x5f\x43\x18\x0c\xc7\x3e\x4c\x3e\x59\xce\x23\x28\x0c\xf2\xc0\x79\x42\x56\x7b\xab\xa8\x3c\xe2\x50\xe9\x3c\x01\x23\x9f\xd4\x7e\x50\x7a\xaf\x24\x37\x4c\xe2\x2c\x21\x5b\x17\x21\x8a\x1a\x25\x8d\x95\xbd\xf8\x22\x18\x47\xe1\x21\x2b\x5f\x93\x65\x1e\xee\x85\x0d\x83\x21\x49\xf8\xea\x1e\xcf\xc8\x7c\x25\x4f\x0e\xe9\x3b\x40\x69\x85\xf6\xde\x83\x6e\x32\x63\x0b\xe9\xfc\xc2\x4e\xe5\x1b\xea\x5c\x51\x61\x96\x81\xe6\x57\xe5\xd0\x29\xde\x48\x94\xfd\x4c\xd0\x3d\xa2\x54\x88\x85\xa0\x26\x75\x33\x1f\xa5\xc9\x25\x52\xbb\xa7\x97\x57\xba\xc3\xba\x49\x3f\xad\x54\x3a\xf9\x07\x0b\xcd\x3e\x48\xb3\xa5\x24\xa0\x9f\x4b\x85\xf4\x33\x9f\x18\x00\xb8\x41\x11\x52\xf0\xdc\x4a\xb4\xc1\x53\x67\x4a\xb2\x71\x19\x75\xdc\x0f\x21\x98\x73\x4f\xe5\x7e\x06\xb2\x82\x3c\x4f\x3a\x85\xd3\x54\x17\xf1\x2d\xbd\x59\xd2\xcd\x6d\xc8\x9f\x02\x67\x11\x1a\x9b\x3f\x64\x46\x6d\xb9\x7d\x43\xc8\x65\xd9\x5e\x15\x12\xd4\x83\x73\xba\x8f\x0d\x36\x6a\x2c\x3b\x19\x90\x02\x6f\xc9\x77\x8b\x92\x89\xd6\xbb\x0f\xc2\x84\x16\xbe\x33\xc2\x04\x9c\x64\xea\xe4\x4c\xe6\x76\xa4\x98\xdd\x03\x2d\xaa\x34\xc8\xf5\x6c\x30\x1b\x35\xde\xca\x9d\x48\x2f\x9b\x47\x7b\x4a\x87\x04\xd1\xa1\x39\xdb\x1f\xce\xc5\xbe\x4a\xa4\x4d\x7e\x26\x64\x22\x9f\x41\x71\x39\x9f\x2a\xbb\x6a\xae\x94\x96\x44\x5d\x75\xd7\x77\x6e\xf7\xf3\x76\xee\x9c\x1c\xa9\x98\xe0\xa2\x23\x4a\xbe\x1d\x8a\x4f\x73\x39\x36\x8d\xfd\x7f\x03\xd0\xf6\xc2\xb9\x4b\xc6\xf2\x55\x98\x25\x71\x4c\xf2\x24\x4c\xd0\x60\x8c\x83\x78\x36\x45\x31\xc0\x27\x03\x2c\x8e\xed\x65\x43\x25\x61\x6f\x59\x79\x14\x49\x39\x20\x8a\x68\x5c\x1d\x4b\x22\x1c\x9d\xd2\xd2\x67\x44\x48\x22\xd5\xbb\x88\x02\x89\xc2\xae\x01\xa8\x6b\x03\xd9\x2d\x7e\xde\xf0\x9c\xd8\xab\xab\xfa\xe8\x2b\x0c\x80\x09\x60\xea\x6e\xce\x10\xaa\x89\x15\x3e\x67\x72\x93\xa9\x10\x4a\x89\x08\xca\xcc\x68\xe1\x74\x73\x1e\x91\x23\x5d\xa4\xeb\x8e\x49\x1d\xcb\x9c\x1b\x73\x5b\x3a\xf2\x02\x84\x4a\xa4\x50\x97\x77\x88\x5a\x96\x59\x06\xf9\xb5\x34\x3c\x05\xfe\x6c\x74\x6a\x4c\xa3\xfa\x05\x5f\x67\xb5\xa2\xee\x12\xd7\xf2\x42\xb2\x78\xf4\xe3\x8f\xc8\x35\x86\x84\x98\xd2\x13\xfa\xbe\xa6\x14\x7a\xad\x8e\xb3\x2e\x00\x97\x8c\x77\xb1\xfb\xa4\x98\xf0\x02\x22\xff\xf3\x61\x9f\xe0\xc1\x28\x88\xa3\x6c\xc2\x8f\xa1\xe5\xcc\x01\x00\x94\x0f\x2f\x6d\x43\x1e\xd8\x2f\x18\x4f\x45\xfe\x00\xde\xd9\xd5\x17\xbf\x67\xa3\x28\x26\x0d\x5d\x0d\x92\xc9\x74\x8c\xaf\xa2\xfc\xba\xdb\x86\x23\x19\x29\x40\x08\xa2\x46\x36\x87\x2f\xf8\x9a\x6a\x0a\xc4\x68\x4a\xe3\xb5\xba\x8a\x52\x3c\x49\x2e\x30\x0a\xc6\x63\xe8\x55\xe6\x21\x7c\x35\xc0\xd3\x1c\xc4\x7e\xf6\x4a\x2e\x9f\x8f\xf0\x35\x8a\x31\x1d\x91\x3e\x66\xf5\x43\xd2\xe3\x59\x30\x1e\x5f\xa3\xfe\x35\x0c\x19\x19\x1e\x96\x0a\x00\x68\xe6\x17\xb2\x21\x45\xf1\x79\x6d\x49\xda\x07\x6a\x3f\x28\xbd\x43\x5f\xbf\x12\x7c\x57\xa2\x38\xc4\x57\x07\xc3\x1a\xb8\x29\x12\x62\xfb\xfc\x7c\x09\x26\xff\xa5\xaf\x6f\x10\x12\x85\x7d\xc1\xd7\x67\x2b\x62\x25\xea\xe6\xd0\x26\x45\x92\xf2\x86\x69\xf2\xdf\x98\x3c\xe1\x94\x49\xe6\x7d\x40\x6d\x73\x51\x12\x57\xe1\x09\xd4\xa4\xb6\x8c\x26\x99\xc5\xb0\xa9\x02\x75\x50\x21\xea\x10\x70\x96\xce\x64\x38\x57\x7a\x4f\x00\x4b\xaa\x48\x0f\x0d\x56\xb6\x4f\x76\x3f\x1f\x1e\x7c\xf8\xb0\xf7\xf1\xdd\xe7\x93\xbd\xfd\xed\x83\x4f\x27\xf2\xf1\xa8\xca\x0c\x98\x42\x95\x22\x31\x3d\xc8\xd1\xd1\x94\xc9\x08\x5e\x5b\x41\x1e\xa0\x0d\x74\x7a\xf6\x5a\x7d\xbf\x07\xee\xc6\xfc\x75\xb5\xa5\x2a\x00\xae\x4c\x67\xd9\xa8\xa6\xd3\x3d\x13\xf1\x94\xd2\x7b\x61\x46\x0b\x7f\xc1\xd7\x4b\xc6\x18\x14\x00\x17\x18\xbc\x4a\xe2\xa6\x80\xcc\x1a\xe5\x4b\x6a\x12\x4c\x15\x26\x19\x01\xd9\x02\x43\x01\x12\x23\xa4\xa9\x0e\xd3\x7e\x30\x95\x54\x17\x92\x5e\x5b\xf5\x14\xa7\x82\x2b\x70\x8d\xa5\x3f\xf5\x31\xd8\x0f\xa6\xa7\x50\x2d\x82\x2d\x9e\x8f\xcc\x29\x14\x3f\x93\x3c\xd2\x45\xe3\x8a\xdf\x3c\x5a\x58\x66\x4e\x54\xa9\x59\x09\x6f\x72\x72\xb0\x75\xd0\xe5\x44\x86\xc6\xc9\xf9\x7f\xe8\x52\x75\xe2\x90\xab\xef\x2a\x49\x57\x50\x16\x64\xd6\xa3\x23\xfb\xb6\x32\x09\xa6\x35\x97\xb1\x02\xff\x03\xfb\xc5\xb0\x18\x65\x32\xf6\xec\xa8\x17\x85\xb2\xe3\x8d\xa0\x88\x2f\x18\x65\xb3\x14\xf4\xc4\x9c\x59\x45\x19\xca\xf2\x88\xd0\x03\xe5\xe4\x38\x44\xc1\x10\x1c\x84\xd2\x34\xba\x08\xc6\xda\x5e\xab\xc0\x24\x03\x02\x6e\xff\x74\x69\x44\xe1\x99\x8e\x62\xd1\xa5\x95\x41\x61\x0f\xa0\xd6\x11\x5f\x9c\x0e\x33\x5c\x77\x22\x7f\xba\x41\x78\xcc\xf4\xcc\x96\x1a\xc3\x60\x9c\x61\xf9\x96\x8d\xb9\x3d\xcd\x1d\x53\x91\xce\x9f\xb5\x89\x6e\x01\x83\xcc\x0b\xcc\xb8\xb4\x68\x1d\x87\xff\xd7\xc6\x78\xfe\x00\x35\x2b\x8c\x63\x75\xc5\x00\x52\x28\x4c\xea\x25\x54\x54\x47\x49\x5b\xec\xee\x61\x52\x71\x71\xeb\x19\x90\x7c\xc9\xe9\xca\xb8\x74\xa4\x07\xd5\x50\x6f\xbc\xb4\xd4\x4b\x66\xea\x0a\xa6\x90\x7e\xd6\x6d\x40\x68\x1f\xa6\x0c\x7f\xd6\x6d\x82\x1b\xea\x5a\x95\x3b\x32\x16\x73\x13\xe7\x79\x14\x9f\xdb\x3d\x7b\x81\x31\x85\x52\xe6\x5a\xb4\x21\x7c\xd6\x5e\x1b\x25\x8a\x48\xcf\xc2\x3e\xc8\x15\xb4\x88\x35\xca\xfa\x4d\x50\x5e\x7f\xbc\xd6\x7b\xbc\xd6\xfb\x87\x5f\xeb\xb1\x88\xbe\xec\xd4\x72\x9b\xa8\xbe\xf3\xcc\x61\x1d\xb9\x2f\xb4\xd4\x17\x8b\x18\xce\xf2\x25\xed\xb3\xc3\xc1\x66\x18\x66\x30\x74\x62\x77\x0b\x62\x50\x4b\x65\x68\x46\xc5\x2f\xe6\xf4\xe6\x11\xe1\x2b\xca\x21\x52\x1e\x82\xa4\x00\x74\x53\xa5\xbb\xfd\xd3\xa7\xf2\xf9\x80\x9d\xcf\x9e\xea\x4a\x22\xb2\x6d\x3e\x65\xd7\x56\x52\x39\x89\x57\xd1\x38\x3d\xdc\x95\x8e\x94\x4b\x62\xe6\x70\xa5\x70\x34\x26\x37\x91\xb1\xb7\xa8\x1a\x5d\x42\x11\xdd\xb7\x79\x4f\x33\xcb\x66\x61\xb3\xc7\xe1\x7f\xea\xbe\xa5\x6f\x4f\x2e\xdd\xa5\xb0\x10\xe4\x81\x88\x00\xe5\x1f\x7f\x04\xdc\xa9\x62\x2a\x8a\xcf\x81\x1b\x2f\x29\x10\xf9\xf5\xc5\xbc\x94\xa6\x14\xa2\xec\xa5\x7c\xdb\x4e\x0a\x69\x68\x1c\x64\xd0\xcc\x71\x4e\x26\xfb\x87\x8d\x0d\x63\xa0\xf9\x9f\xf1\x62\x75\x95\x66\x6e\x57\x48\x0a\x96\x5a\x9e\xce\x88\xcc\x96\x66\x39\xca\x12\x6a\xe7\x38\x9d\x02\xeb\x86\xb3\x73\x10\x5f\xe7\xe4\xc0\xef\xa1\x3e\x1e\x12\x06\x40\x97\x38\xbf\x42\x85\xd1\xa0\x4a\x46\xed\x2f\x1a\xd6\x7e\xb0\x60\xfd\xe3\x8f\xc8\x36\xf2\x4b\x46\x7d\x64\x5e\x37\x10\x54\x2d\xee\xd1\xce\xce\x26\x94\x6f\xc6\xf8\x2a\x47\xbd\xc3\x4f\x68\x70\x3d\x18\x63\x4f\x74\x13\x86\x5d\x6c\x36\xd0\x13\xe8\x32\xb3\x59\x9a\xa6\xc9\x80\xf0\xac\x8c\x8e\x8e\xd1\x8a\x74\x0c\x16\xcb\xc4\x36\x17\x96\x8e\x30\xd2\xd0\x4b\xdd\x78\xa8\x5e\xa5\x7f\x96\x61\xa5\xa4\xe0\x12\xcd\x24\x63\xb0\xa7\x02\x80\x6e\xc6\x26\xe9\x62\x6b\xa6\x1d\x94\x23\xdd\xaf\x6e\x09\x75\xe3\x15\x42\xf8\x5e\xe8\x15\x6c\x82\xbd\x97\x75\x48\x54\x67\x00\x9c\x85\xac\x13\x6e\x27\xb9\x67\x4d\xcb\xe9\xcc\xb5\x59\x6e\x32\xaf\xc9\x7f\x48\xd6\x35\xed\x11\x39\x5a\x52\x4e\x2d\x53\x2e\xbc\xbc\x2c\x95\x13\xeb\x55\x3a\xe9\xc3\x87\x20\x0c\x85\x6d\x97\x94\xf7\x53\x7c\xd7\xa7\x47\x3a\x38\x48\x2c\x96\x1b\x6f\xc1\x7b\xc9\x56\x9c\x0a\x74\x62\x24\x64\x4b\xdf\xa2\xdd\x52\x8b\xc5\x68\x58\xbc\x52\xb5\x52\x05\x0b\x02\xad\x82\x86\x7c\x25\x24\xe4\x59\x74\x4b\xb4\x06\x81\x09\x95\x73\x4d\x9a\x83\xa5\x92\xd1\xb6\x4a\xb5\x02\x21\xb7\x01\x1b\x91\xd5\xd5\x6c\x17\x44\xf6\x7d\xcc\x51\xfa\x28\xfb\xfe\xd3\x65\xdf\xc2\xa4\x8d\x27\xec\xbd\x2f\x1f\xdd\xbd\x7e\x10\xab\xd2\x6e\xd4\x0f\x84\xeb\x2d\xbe\xa2\xea\xea\x32\xd7\xdd\xe3\x49\x90\xe6\xdb\xac\x60\xe1\x76\xeb\xbc\x1a\x03\xb5\x12\x34\xcb\xfb\xa2\xe9\xbc\xa5\xd7\xe2\x12\xec\x38\x4f\xa3\xf8\xfc\x06\x5c\x5b\x6c\xef\x89\xb4\xdc\x0f\x62\xf9\xd3\xcf\xc1\x78\x86\x6f\xd0\x05\xf9\x87\x5d\x87\x10\xc8\x43\x9c\xe2\x39\x37\xa4\x9e\x6a\x5e\x00\x41\x6a\x18\x4e\xaa\x58\x9c\x8f\x3c\xc0\x88\x48\xeb\x1e\x6d\xc9\xdc\xc2\x40\xed\x46\x47\x19\xb2\x4d\xf7\x83\xb8\x96\x27\x4b\x4c\x55\x04\x3a\x1c\xf2\x99\xab\x7c\x6a\x16\x2b\x22\x52\x0f\xd2\x44\xd4\x9e\x47\x54\x7d\x43\x21\x32\x3f\xdd\xe7\xa6\xfe\x98\x41\xdc\x8a\x52\x22\x8b\xd9\x1c\x62\x78\x8f\x4e\x12\xe6\xd9\x2b\x77\x07\xaa\x33\xe8\xb5\x25\xb3\x6b\xbc\x3d\x21\xc7\x40\x37\x6c\x92\x2e\xb8\x48\x08\x4f\x69\x9c\x8f\xe4\x94\xe0\xb5\x25\x68\x84\x61\x1b\x67\x79\x94\xcf\xa8\xc0\x65\x9a\x7f\x85\x78\x9a\x64\x51\x2e\x63\xc9\xe0\x0a\xf4\x00\xcc\x60\x1c\xe1\x38\xd7\x2d\x31\x2a\x37\x6c\x98\x58\xf0\x54\xe3\xe6\x08\x2e\x8a\x91\x39\x7e\x5c\x05\x5f\x7a\x95\x2c\x48\x6f\x38\x8b\x43\xb0\x89\x1c\xe0\x34\x0f\x22\x31\xfd\x8e\xe5\x23\x26\x76\xb1\x75\xf4\xe0\x4b\x48\xe0\x75\x8b\xb5\xc4\x46\x9e\xcc\xa6\x96\xf1\x4b\x92\x6d\x85\xf7\x7a\x9e\x14\x12\x2d\x01\xdd\xa5\x0d\x48\xb4\x39\x9e\xe1\x2e\xfd\x87\x8b\xb9\x5a\xb2\x77\xe7\xac\xb0\xc9\x2f\x26\x05\xe2\xda\x47\x03\xc4\x39\x21\xe2\x1c\x12\xd5\x26\xb3\x2c\x87\xad\x0e\x4f\x70\x9c\x0b\xba\xe9\x5f\xe7\x38\x6b\x36\x96\x98\x30\xfe\xc3\x92\x36\x91\xac\xdc\xbd\x4f\x1f\x99\x04\x6d\x02\x79\x7d\x4a\xaa\x68\x16\x47\xff\x3d\xc3\x28\x0a\x71\x9c\x47\xc3\x48\x65\xc5\x95\x26\x9b\x0f\x4f\x85\x29\x86\x26\xed\x6c\x33\x80\x6d\x47\xda\x84\x5e\xeb\x54\xc0\x07\xb9\x16\xf4\xa3\xa5\x95\x20\x27\x9c\x75\x85\x0f\x30\x07\xfd\xe7\x5d\xa9\xc0\x10\x56\xf9\x30\x5a\xa3\x20\x98\x1b\xe2\xb3\x6e\x93\xc8\xae\x3c\x73\xff\xcd\x99\xd7\xae\x94\x2b\x99\xa9\x77\xdb\x95\x12\xb6\xbd\x96\xb5\xf0\x09\x11\x30\x86\xc1\x20\x4f\xd2\x6b\x8f\x6a\x94\xc9\xc0\x3e\x21\x7c\x9a\xc8\xfa\xc9\x10\x89\xde\x6c\x6c\xa0\x67\x34\x22\xd3\x33\x28\xf3\x64\x75\x15\xf5\x92\xc9\x24\x89\xff\xeb\xf8\xe9\x93\x27\x46\xe7\x8b\x5f\xac\x01\x8e\x53\xed\x19\x19\x86\x14\x3f\x5b\xf2\x90\xf4\x0a\xc7\x83\x97\xfd\x20\xc3\x9d\x96\xf6\x61\x12\xb6\xf5\xa2\x17\xd3\x2f\xe1\x50\x7b\x39\x88\xa6\x23\x9c\xbe\xa4\x90\x97\x5e\x3f\x7d\x72\xf3\xf4\x09\x1e\x67\x18\x49\x9d\xa1\x1a\x73\xda\x17\x3e\x0c\xcf\xd0\x8f\x3f\xb2\x0f\x2b\xc1\x24\x14\x7d\xdb\xdc\xdf\x7a\xfa\xe4\x09\xfd\x50\x3b\xe5\x38\x7b\x48\x45\x15\x9e\x09\x86\xf4\x03\x45\x0c\x7e\xcb\xf8\x9c\x89\x51\x96\x11\x63\x0d\xd1\x70\x18\xa8\xd6\x4f\x93\xcb\x0c\xa7\x4b\x4f\x9f\x3c\x11\x23\x96\x24\xf9\x4a\x2f\xbd\x9e\xe6\xc9\x7f\x1d\xd3\xaa\x37\x70\x7c\x92\xf7\x1f\xf1\x1d\xfd\xf9\xf4\xe9\x93\x9a\x7a\x1e\x7b\x82\xa8\x4a\xe4\x78\x94\xa4\xf9\x60\x96\x67\xf4\x0d\x59\x36\x3d\xb4\x81\x78\xdd\xd7\xd2\xeb\xcf\xe3\xa8\x4f\x3e\xad\x8c\xa3\xbe\xf4\x1e\xb4\x61\x3d\xe8\x14\xf9\x4a\x4a\xad\x48\xef\x14\x08\xc1\xf8\x3c\x01\x10\xe4\xc7\xeb\xa7\x02\x8b\x0f\x49\xf2\x65\x36\x45\x79\xd0\x1f\x63\x09\x93\xe3\xb7\x07\xbf\xb2\x43\x9f\x78\xb7\xf7\xf1\xe7\xcf\xb6\xf7\xc7\x9f\xde\x7e\xde\xdf\xfb\xf5\x73\xdd\xf5\xc1\x77\x7d\x68\xb8\x3e\x34\xad\x6d\xbb\xda\x91\x3f\x1a\x6d\xc9\x1f\x8d\xf6\xe4\x8f\xbc\x4d\x31\x34\xbd\x64\x32\x25\x27\xc5\xb1\x39\x44\xb6\x29\xd5\x6a\x85\xc9\xac\x4f\xc4\x7e\x52\xab\x28\x00\x2c\x56\xc6\x02\xc9\xa6\x0a\x11\x84\x13\x44\x11\x7a\x83\x1a\xed\xce\x6b\x14\x2d\x2f\x2b\xe0\x85\x90\x88\xde\x20\xbf\xb1\x6e\x7c\x23\x7f\xe1\x69\x74\x86\x36\x08\x8c\x37\xc8\x7f\xad\x7e\xa7\x77\xa9\x25\xb5\x6a\xb4\xda\x12\xfa\x0d\xd5\xaf\x7c\xbf\xaf\xd7\x2f\x1e\x6f\x9e\x2a\xbd\xfe\x25\x18\x7f\x41\xef\x76\x6a\x8d\xdf\xd6\x97\xd4\xde\x5e\xd1\x10\x89\xea\xbb\x48\x7b\xb9\xd0\x08\x48\x83\x9c\xf5\x93\x2b\xf5\x23\x58\x1a\x90\x36\xaf\x22\xf4\x1b\xaa\x5d\x15\x1d\x62\xbf\x1b\xd2\xef\xa6\xf4\xbb\xb5\xa4\x75\x16\xa0\xd4\xb2\x2b\xf4\xd3\x4f\x3f\xa1\x75\x28\x99\x5d\xa1\x1f\x51\xfd\x6a\x38\xa4\x03\xd4\x69\x6a\x55\xc8\xea\x38\xbd\x22\x03\x99\x5d\x69\x9f\xf8\xe2\x39\xcd\xe0\xfb\xd5\xeb\xa7\xce\x4e\x4d\x66\xe3\x3c\x9a\x8e\xa3\x01\xa8\x09\xcc\xee\x5d\x11\x32\x0e\x4f\xaf\xce\x5e\x5b\xbe\xb5\xe8\xb7\x86\xf5\xe3\x3a\xfd\xd8\x3a\x2b\x69\x3d\x9b\xf5\x11\x08\x38\x1e\x9a\x44\x57\x68\x90\x8c\x67\x93\x38\x53\xa8\x5f\x86\x49\x24\x85\x5a\x08\xbd\x7a\x41\x68\xa6\xee\xf3\x91\x62\x8f\x75\xbf\x5e\xd7\x87\x56\xac\x64\x3a\x58\xb5\x1c\x26\xa6\xb5\x84\xbe\x92\xdf\x74\xbc\x1d\x55\x7c\xb9\x8a\xdf\x91\xaa\xf8\x1d\x57\x9d\x86\x5c\x67\x7d\x09\x15\x75\x1a\xc6\xac\x0b\x6e\x40\xeb\xe4\x25\x23\x15\xc5\x17\xf2\x68\x91\xc7\xca\x23\x76\xb5\x2e\x8d\x0f\x23\xcf\x16\x7b\x55\xe7\x2f\x1a\xca\x90\x96\x8e\xa8\xc2\x1f\x19\x8d\x55\x19\x56\x85\x75\x2a\xf5\xe6\x8c\xad\xc2\x56\x95\x8a\x73\x06\x58\x61\xb9\xac\x62\xd9\x28\xc3\x6d\x01\x28\x82\x71\x6a\x72\xc2\x1f\xae\xac\x4c\x90\x31\x80\x8d\x05\x38\x20\x54\x69\xa0\xdf\x50\x78\x4a\xfe\x77\xb5\x8e\x7e\x43\x57\x8d\xb3\x33\x7d\x21\x41\xd9\x08\xfd\xb6\x01\x05\xaf\x22\xa3\x80\xc2\x24\xe1\xe7\x0d\x1c\x6a\xc5\xbe\x72\x98\xe2\x01\xed\x5c\x88\x8e\x06\x49\xcc\x36\x98\x62\x57\x3a\xea\x1d\x7c\x24\x7b\x44\xfd\xaa\x5e\xf7\x50\xfd\xaa\xee\xc3\x7f\x1b\xf0\xdf\x16\xfc\x77\xdd\x03\x5a\x20\xff\x6d\xc0\x7f\x5b\xf0\xdf\x75\xf8\xaf\xdf\x27\xff\x6d\x76\x8a\xcd\xec\xc5\x0b\x86\xd4\x0b\xb4\xb9\x7d\x4c\x03\xb2\x23\x2a\x0e\x21\x22\x10\xa4\x51\x3e\x9a\xac\xf0\x32\xab\x05\x2a\xa4\xf4\x06\x13\x1f\x56\xe8\x83\x24\x61\xac\xe0\xab\x9c\x86\x0f\x10\x5d\xfe\x1c\x26\x47\x38\xc3\x79\x17\x39\xb6\x48\x36\x08\xc7\x5f\xa2\x29\x33\xfd\x4d\x86\x28\x3e\x4a\xe0\x38\x36\x0a\x32\xd4\xc7\x38\x06\xf7\x00\x76\xc1\x15\xc4\x21\xd8\xf0\x85\x51\x88\xe2\x24\x67\x76\x98\x26\x29\xd0\x6c\x2e\x1c\x12\xb7\x17\xfd\xfc\x05\x5f\x1f\xa6\x51\x92\x1e\x51\x13\xe0\x8d\x8d\xe2\xbd\x95\x74\xb8\x5d\x98\x36\xa7\x66\x07\x54\xf1\x8d\xff\x71\x8b\xc3\x0d\x7b\xf3\xc5\x5b\x0b\x7f\xfe\x82\xaf\x7f\x49\x52\xb0\x62\xfc\x82\xaf\x57\x2e\xc9\x6f\x7b\xb1\xe3\xe8\x0f\xcc\x4a\x65\xd1\xf9\x5b\xc2\x80\xd0\x2a\x6a\x95\x2d\x23\xe1\x08\x90\xc2\x00\x99\x60\xf9\xc8\x71\x1c\x8b\x67\xde\xe0\x32\xea\x54\x6a\x81\xf4\x3f\x1b\x8c\x30\x39\x7e\x20\x22\x42\x5b\xfa\x90\x1d\x25\x97\x04\x76\x8d\x37\xb3\x4c\x76\xe9\x17\xa5\x7d\x90\xe1\xda\x87\x85\x37\x2a\x8d\xb3\xf4\xee\x54\x5f\xaa\x85\x8d\x28\x41\x87\x8a\x1e\xf4\xe7\x1b\x86\x21\x7b\xb6\x48\x21\x88\x91\x9d\x28\x4f\x07\xc9\x5a\x8e\xfc\x49\xa8\x9c\x42\x9d\x33\x3a\xb2\x30\xe3\xec\x8d\x85\xd5\xb8\x19\x16\x92\xf6\x13\x03\x38\x84\xd3\xd1\x87\x52\x46\xfb\x07\x86\xf8\xbf\x04\xe2\x4e\xcc\xd9\x2c\x1c\x25\x39\x22\x24\xe9\x2e\x94\xcb\x7b\x80\xba\x05\x94\x42\x3e\x9e\xf5\xab\x40\x06\xf1\x89\xc3\x3c\x93\xf6\x36\xf8\x50\xec\x54\x4c\x46\x3b\x93\x76\x31\xb9\xc4\xba\x52\x00\x30\x65\x90\xd9\xeb\x39\xd8\xee\x47\x57\xc0\xb6\xcb\xb0\xfd\x6d\x03\x98\xf8\x29\x1b\xe4\xd5\x82\x3a\xbe\xa2\x3a\x43\xdd\x32\xd9\xa8\x98\x70\x20\x2d\xb6\xee\x7e\x42\x1d\xc2\xcf\xb4\x09\x43\x1b\x1b\xa8\x35\x6f\xd2\xbe\xbb\xa1\xb5\xf7\xd9\x31\xe2\xae\x35\x63\xd0\x3a\x1b\x92\x33\xf4\x1b\x91\x25\xcc\x45\x34\x97\x9b\xcb\x32\x5d\x39\x9b\x89\xe2\x8b\xf7\x16\x4e\x63\xbc\x76\x33\x1b\x52\xb4\xe0\x37\xe2\xa9\x60\x39\xfc\x95\x83\xeb\xc8\x0c\x8b\xf1\xd1\x97\xa2\x8e\x8d\x78\xe1\xc8\xc8\x9b\xf9\x57\x09\xd1\x38\xd9\xc9\xfd\x72\xa6\x96\x15\xdc\x3c\xc4\xdf\xa0\x16\x78\xb2\xd0\x87\x32\xda\x57\xe7\xe2\x94\x43\x60\x92\xe6\x82\x1d\x29\x01\xa6\x0a\xdd\xea\x1a\x22\xa4\xa8\x0a\xd7\x8e\xa5\x74\x86\x7e\x73\x2f\x4e\xc7\x9f\x2a\x7c\xdb\x57\xa0\x8e\x40\xf3\x54\x5d\x8a\xf6\x39\x70\x4a\xb2\x9e\x34\x3d\x38\x1e\xa4\xd7\x53\x6a\x1a\x2b\xcb\x79\xfb\x1e\x4a\x86\xc3\x0c\xe7\xc6\xcc\xd0\x35\x12\x26\x3d\x51\xaf\x28\xec\x99\x7b\xb5\x57\x9c\x10\x8b\x9f\x7e\xf1\xb3\x51\xfc\x6c\x7a\xc0\x62\xe4\x53\x86\x82\x6b\x88\x17\xc5\x95\x70\xcd\xcb\x60\x8a\x1a\x71\x08\xb2\x67\x2b\x1f\x39\x84\x18\x42\xdf\xfb\xa7\x14\x0c\x91\x5f\xf4\x21\x55\xbe\xa9\x65\x9b\x25\x65\x9b\xd6\x23\x51\x95\x21\x54\x69\xd5\x53\x09\x54\x7d\xf4\xd5\xc7\x86\xfa\xd8\xf4\x84\xc2\xc2\xd8\xbc\x57\x57\xd1\x1e\x39\xf9\x7e\x17\x63\x64\x9f\x74\x65\x98\xac\xb3\xee\xa1\xbb\x91\x9b\x8d\x68\xd8\x81\xa0\xb2\x64\x6d\x19\xd8\x77\x98\x05\x0b\x85\x1b\x49\x2a\xaa\x13\x4c\x2d\x3a\xae\xba\x34\x58\x67\xf0\xfa\x37\x85\xd9\xd6\x6d\x1a\xa0\xcc\xd7\xa7\x43\xab\x65\xcc\x0f\xd4\x6a\xa8\xb5\x1a\x7a\x2d\xab\xb6\x29\x6b\xea\xd3\xa9\xd5\x6a\xda\xd4\x50\xef\xb5\xb3\x83\xfd\xe8\x2f\x6f\x81\xb6\x13\xc3\x91\xe5\x8c\x23\xf6\x5f\x3a\xaa\x1b\xc8\x7f\xcd\x7e\xbe\xe1\x33\xc4\x5e\x38\xf6\x5d\x98\xe3\x68\x98\x03\xa5\x7b\x0e\x45\x59\xe9\xc4\x71\xd4\x73\x32\x79\x92\xba\xa6\x2e\x24\xaf\xdf\x24\x45\x57\x2d\xf3\x0d\xb9\xeb\x37\x49\xa9\x55\xcb\x1a\xba\xd4\xf5\x9b\xa4\xbf\xca\x9a\xd2\x6b\x63\x1b\x5e\x5e\xb6\x6d\x00\x80\x9c\xaf\x22\xe7\x3b\x90\x6b\xcc\x41\xae\x59\x8a\x5c\xfd\x96\xc8\x35\x54\xe4\x1a\x0e\xe4\x9a\x73\x90\xab\x97\x22\xe7\xdf\x12\xb9\xa6\x8a\x5c\xd3\x81\x5c\x7d\x0e\x72\x7e\x29\x72\x8d\xb9\xc8\x59\x49\xf7\xd3\x14\x8c\x88\xb2\x3c\xc8\xb1\x59\x00\xd8\x49\x5e\xb7\x74\x0c\x58\x46\xae\xeb\xd1\xe0\x0b\x99\x8b\xbc\x61\xfb\x42\x06\x22\xd7\xb5\xe3\x56\x25\x8a\x75\x3d\xcd\xe1\x7d\xb0\x7c\x6a\xf4\xe4\x21\xad\x1d\xfd\xd4\x62\x59\x3e\xfa\xb1\xc5\x5c\x41\xca\xb9\xa5\x58\x42\x4b\xd5\x28\x41\xac\x1f\x8e\x9d\xef\xc6\xce\x5c\x3f\x06\x76\xc6\x12\x52\xb1\xab\xdf\x06\xbb\x86\x84\x5d\xc3\x8d\x9d\xb9\x80\x0c\xec\x8c\x35\xa4\x62\xe7\xdf\x06\xbb\xa6\x84\x5d\xd3\x8d\x9d\xb9\x82\x0c\xec\x8c\x45\xa4\x62\xd7\x98\x8f\x9d\x49\xad\x98\x47\xb6\xb6\xcb\x25\x74\x1b\xb6\xac\x23\x5d\xc8\x31\x96\x93\xba\xb9\x5a\x56\x95\x21\xfa\x34\x5d\xb2\x0f\x3b\x0a\x77\x51\xa3\xdd\x59\x6d\x36\x98\x06\x7a\xc9\xa6\x0a\xe6\x12\x8b\x10\x90\x32\xe6\x39\xcc\x54\xc3\xcf\x33\x96\xf0\x09\x41\x0e\xef\x61\x30\xc0\x42\x47\x2c\x80\xfc\x27\xbe\x0a\x26\x53\x71\x52\x2e\x3e\xf0\x39\xa5\xb0\x72\x7c\x95\x4b\xb7\xdb\x2b\x9b\xdb\xc7\x2b\xec\x1c\x51\x9b\x70\x93\xf4\x2f\xf8\xda\x43\x83\xe1\xb9\x90\xe6\x0b\x28\xd3\x71\x40\x90\xb8\xca\x91\x0e\x85\x49\xf8\xb5\xa2\x1d\x1b\x20\xa6\xd3\xee\x59\x94\xd8\x9f\x69\xd8\xd4\x5d\x3c\x9e\xe2\xb4\xb6\xb9\x4d\xaf\xf5\xa9\xce\xfe\xe9\x13\x66\xb3\x22\x37\xf9\xfa\xe9\x53\x08\x81\x0b\x06\x24\x8a\x55\x41\xb7\xdd\xf0\xb8\x5d\x42\xb7\x0d\xb6\x23\x92\x65\x42\xb7\xdd\xf2\x0a\x93\x84\x6e\x1b\x7c\x18\x27\x61\xfb\x59\xb7\xe3\xdf\x9c\x79\xed\xc6\x9d\xac\x45\xbe\xa5\x99\xc8\x83\x19\x73\x7c\x43\xb3\x0c\xba\x12\x5e\x20\x66\x40\x41\x9a\x47\x83\x64\x32\x4d\x62\x88\xb9\x4e\xbe\xad\x3e\x7d\x22\xe6\x7d\x1c\xf5\x57\x58\xd1\xaf\x5f\x65\x03\x00\xe1\xf5\x79\xcf\xc6\x1d\x41\x86\x0b\xab\x8e\x20\xc3\xd2\xb7\x5f\x92\x34\x04\xbf\x74\x51\x40\xbc\x91\x21\xcc\x86\x60\xf0\x07\xb4\xbe\xc9\x6f\x79\x0a\x98\xd6\xcf\x0a\x66\x18\x5c\xab\x7a\x64\xa1\x4a\xef\x3f\xe5\xc3\x75\x80\x82\xe3\xc1\x0a\x79\xd0\xb0\xee\xb4\xc4\x57\xfa\x58\x66\x88\x22\xbe\x6c\x5f\x4c\xdf\x6f\xed\x14\x97\x4d\xf4\xd9\x7a\x83\xd5\xcf\xa8\x7d\x1e\x59\x56\xfc\x16\x2b\xc7\x93\xe9\x38\xc8\x6d\x0c\x4a\x44\x99\xfe\x33\x66\x11\x79\xb8\x06\x15\xbc\x0a\x04\xaf\x03\xbd\x5f\xf4\x07\x5e\xe1\x11\x26\xbb\xa8\x85\x6a\x7e\x63\x1d\xf5\xa3\x3c\x5b\x2a\x03\x18\x5d\x58\xe0\xed\xfd\x7c\x5b\x70\x9f\xb7\x3f\xf6\x3e\xff\xba\x73\x70\xb4\xff\x79\xff\x60\x6b\x1b\x6d\x42\x6c\x83\x3c\x88\x73\x94\xe2\x69\x8a\x33\x1c\xe7\x51\x7c\xce\x15\x31\x84\x0c\x27\x49\x58\xf4\xdd\x0a\x73\x6b\xbb\x12\x4c\xc6\x4e\x0d\x98\xd2\xa5\xa0\x66\x72\x24\x1e\xed\x14\x65\xb9\x24\x2c\x66\x93\xa2\xdb\x03\xbf\xef\x59\x0a\x06\x0f\x22\xc9\x87\x5c\x44\x29\x2e\xf5\x4e\xd0\x3d\x99\x03\x74\x32\xc2\x64\xd4\xf3\x04\xcd\x98\x9f\x00\x61\x01\x88\x14\x06\xd0\x0a\xc8\xd5\xe2\x61\x30\x3c\xef\x02\xe9\x72\x5c\x97\xe4\x1d\xd5\xc0\x16\xb6\x8b\x8c\xc2\x66\xe4\x17\xc5\xae\xc9\xb0\xa1\x4f\xed\x31\x25\xdc\x09\xe9\x11\xe4\xbf\xe0\xeb\x15\x6b\x59\xee\x1a\x3a\x18\x9e\xa3\xda\x01\xb4\x12\x8c\x97\xa0\xce\xc0\x36\x78\x15\xc7\x40\x6d\x8b\x07\x12\xa5\x13\x7a\x43\x48\x84\xf7\x8e\x10\xca\xa0\xac\x4f\xe4\x5c\x11\x0d\xdc\xdf\x55\x29\xc1\x2c\x80\x14\x69\x41\xde\xe3\xf9\xd5\xf3\x0a\xdd\xa6\xb7\xe9\x30\x27\x69\x8d\x5d\x9e\xc1\x10\x7a\xe8\x4f\x14\x5d\x74\x51\x74\x51\xf0\xc6\x1b\xc5\xf4\x40\x99\x6f\x15\x52\x57\x89\x0b\xc5\x24\x07\x5d\x03\x20\x67\x0e\xa1\xf5\xd9\x8d\xb3\xba\x56\x2d\xb2\x87\x2e\xa1\x55\xa4\x27\xc7\x42\x7c\xa4\xa7\xfb\xa5\xa7\x2d\x7c\x5f\xf4\x24\x20\xdd\x8d\x9e\x54\x3e\x7d\x0b\x7a\xda\x8b\xa3\x3c\x0a\xc6\xd1\x1f\x38\x43\x01\x8a\xf1\xe5\xf8\x9a\x61\x18\xb2\xe1\x98\x4f\x4b\x7c\xd7\xb8\x1a\x26\xe9\x64\x3f\x09\x31\xda\xa6\xce\x6a\x10\xa7\xb9\xe0\x74\x49\x2a\xd3\x29\x58\x57\x83\x9f\x1f\xa7\x5a\xb1\xc9\xd8\xc9\xf0\xbb\x23\xd9\x7b\x23\xab\x9a\xf9\xc1\xc6\x29\x6e\x49\x70\x51\x1c\x29\x16\x36\x62\x9a\x24\x72\xb1\xa8\xa8\x37\xa7\x53\x42\x0b\x30\x5a\x3c\xdd\x74\x66\xb9\x66\x20\x43\xbc\x21\x7e\xf2\x4d\x91\xd2\xa0\x79\x2a\xce\x89\xe4\x4c\x0d\xeb\x93\x74\x42\xa7\x3d\xb0\xe9\x6e\x28\x7d\x17\x24\xb5\x51\x90\xd7\x6b\x5b\x49\x6a\x47\x03\xb6\x32\xd6\xb3\x78\x44\x09\x9d\x7a\x00\xd8\xfa\x01\xf6\x45\xb5\xca\x0b\x07\x6c\x74\x54\x3e\x0c\xc1\x1c\x32\xd1\x12\x68\xcf\xee\x48\x3e\x6c\x09\x9a\xb8\x29\x33\x9c\x56\x31\xa2\xa2\x46\x45\x61\x90\x07\xa8\x0f\xb2\x97\x5a\xc2\x21\x8f\x01\x68\x9a\xea\x82\xbb\x3b\xeb\x80\x0f\x71\x0a\x73\x39\x48\xe2\x41\x8a\x73\xfc\x92\x0d\xc7\x38\x39\x57\x98\xb2\x74\x2f\x75\xb4\xd8\x58\x43\x40\x0d\xc0\x9c\xfa\xb7\x30\x9e\x82\x43\x89\xa5\xe0\x70\x81\x4d\xef\x6b\xc6\x5c\x61\x08\x50\xa6\xec\x24\xbc\x81\xb7\xc1\x1a\x90\xc0\x57\xd8\xb9\x24\xfe\x24\x60\xd1\xa8\x59\x2c\x1a\x41\x14\x9f\xdf\x03\x37\x29\x3a\xbf\xc1\xc9\x83\xc1\xaf\x3d\x27\x6d\x3e\x57\xc9\xa4\x4a\xbd\x4b\x8e\xb9\x93\xc2\x58\xc9\xae\x16\xe6\x95\x0e\x9d\x83\x7b\xe0\x38\xb4\xcd\x7e\x00\x5f\xe4\xea\x36\x9a\xa2\xed\xa1\xe0\x22\x88\xc6\x41\x7f\x8c\xa9\x19\x62\xe6\xde\x16\x3f\xf3\xce\x54\xa6\xaa\x9d\x28\x66\x1b\x5f\xe9\x3e\xc5\xe0\xaa\xfb\xcc\xc7\x24\x67\xee\xd1\x34\x6a\x1a\x85\x54\xec\x1a\x28\xca\x10\x1e\x0e\xf1\x20\x8f\x2e\xf0\xf8\x1a\x05\x28\xc4\x59\x9e\xce\xe0\xd9\x43\x29\x0e\xc2\x97\x49\x3c\xc0\x95\xf6\x99\xaa\xd4\x0b\x68\x3c\x14\x0d\x53\xe0\x0f\x4d\xc9\x7c\x24\x6b\xd5\x89\x58\x54\x59\x94\xfa\x45\xc5\xf9\xe4\xcf\x8b\x56\xa7\xff\x9d\x62\x2e\x66\x50\x48\x2d\x11\x0d\x4b\x01\xa0\xd2\xd5\xa2\x14\xb5\x5c\x94\x2c\xc0\x90\x21\x20\x12\x41\x95\x2d\x38\x1c\xb2\x80\x99\x9c\x53\xef\x48\x13\x62\x5d\x7c\x66\xed\xb9\xca\x66\xbf\xb1\xbe\xda\x6c\xc8\x9f\xa8\x4a\xc4\xf6\x45\x93\x83\xba\xc8\x57\xbe\xaa\xf2\x6f\x17\x35\xaa\x9c\x9d\x32\xab\x2a\x3b\x98\xaf\xc8\x46\xce\xb5\xc9\x4f\x2d\x6c\xa4\x4f\x46\x58\x12\x0a\x58\xa6\xad\x00\x8d\x40\x6b\x4c\x84\xcc\x0a\x4b\x91\x8b\xb0\x9b\x31\xc7\x07\x22\x0c\xf0\x65\x4d\x84\x26\xb6\xae\x2d\x1d\xfa\x06\x87\x25\x66\xed\x6d\xaa\x3c\x35\x1d\xb9\x21\xdb\x3a\x57\x99\x52\xaf\xeb\xf4\x9b\x22\x7f\xe2\x53\x86\xc7\x78\x90\xd3\x86\x8f\xf3\x34\xc8\xf1\xf9\x75\xcd\x65\xae\x2d\x69\x9f\x41\x5c\xdc\x40\xcf\x29\x2b\x7d\xee\x34\x0f\x63\xb3\x71\x18\x64\x19\x61\x13\x6f\x83\x0c\x87\x8a\xc7\x9c\xfc\x57\x6e\x1c\xc6\x40\x1d\xe3\x14\x0e\x5c\x64\x57\x73\x43\x2a\x5f\xe4\x7a\x72\x3f\x76\x9f\x51\x62\xa3\xee\x42\x8a\x91\x93\xcc\xd8\xcc\x1b\x96\x32\xbb\xd1\x22\x0a\x98\x7d\x1e\xc4\xc5\x0d\x45\xd1\x43\xee\x0b\x1c\x7d\x0c\x3c\x87\xa5\x27\x23\xfb\xae\xd1\x7f\xed\x3e\xe7\x4e\x68\xab\x37\x45\x1e\x2a\xbd\x31\xd2\x31\xb7\x4c\xa8\xce\xb6\x65\x2e\x59\x5b\x62\x1a\x5e\xfb\xd5\x9b\xaa\xc3\xce\xf2\x14\x07\x93\x5b\xa9\xb2\x41\x86\x62\xca\x67\xd9\x06\xbf\xd9\x78\xd9\x8f\xa8\xc1\xb6\x7a\xa2\xa1\xd2\x09\xc4\xb1\x96\x34\xd3\x3e\xaa\x35\x1b\xaa\x62\x5a\x52\xf8\x1e\x03\x7e\x9a\xda\x57\x7f\x59\xe2\x11\xb2\x63\xd9\x6b\x6d\x3b\x2c\x17\x11\xa7\x41\x0a\xc7\x2d\x9b\x80\x68\x6e\x6f\x70\xbc\x29\xac\xab\xb8\xd0\xf8\xc3\x0f\xcf\x87\xe3\x59\x36\x7a\x5e\x6d\x9b\xa3\x50\x5c\x1b\x9d\x18\xe6\x2e\xf2\xcb\xe6\x15\xce\xb5\x90\xd6\x74\x2a\xdf\x96\xca\xca\xf3\xcf\x13\x7a\xf6\xed\xad\xb0\x1f\x7f\xde\xcc\xa7\x10\xc5\x63\x07\xea\x19\x54\x22\xb5\x21\xdd\x6e\xb2\x83\xb6\xe1\x1c\xcc\xde\xcb\x4a\xef\x32\x05\xbd\xac\xa2\x9c\xf0\xec\x5c\x85\x7c\xbd\xf0\x6e\xba\xa9\xf6\xc8\xaa\x10\xd4\x53\xcb\x14\x0a\x7e\xa0\xea\x6f\xb0\x1f\xf2\x99\xe2\xdb\x1d\xe8\x61\x7b\x6f\x7b\x86\x2a\x9a\x73\x94\xe8\x82\x7a\xed\xdc\x46\xf3\x5c\xc0\x28\xd5\x15\x8a\xba\x5c\xd1\x24\xd5\xbb\x95\xc6\x59\x4c\x67\x71\x40\xfa\x9f\x39\x9d\x85\x26\x78\xc1\xe9\xb4\x2a\x7e\x2b\x4e\xa7\xa8\x7b\x87\xe9\x2c\x53\xf8\x56\xbb\x3a\xf8\xa6\xd3\x79\xe7\xe9\x2a\x59\x02\x73\xe6\x4b\xd7\x9b\x96\x4c\x12\xdd\x4c\x84\x9e\x77\x60\x13\xeb\x98\xd5\xf5\x05\xda\x40\xd1\x85\x3c\x5b\x65\x5b\x04\xdb\x31\x69\x60\xe9\xde\x28\x88\x62\xc8\x79\xe2\xba\x6b\x7d\x0b\x76\x03\x9f\x79\xe7\xd1\x86\x3b\xf8\x80\xae\x62\x53\x76\x10\x52\xd7\x20\x06\x69\x68\x8a\xc6\xb4\x5d\x42\xdc\x89\xbe\x2e\xe3\x28\x6f\x7b\x7c\x3b\xd0\x4e\x42\x52\x13\xca\xdc\x91\x5e\xbd\xed\x59\xf6\x1e\x13\x3c\x6d\xe2\x50\xc4\xff\xcc\xb9\x1a\x83\x52\x69\x90\x33\xa3\xee\x15\xbd\x8e\x01\x43\xa3\x59\x2a\x1d\x09\xad\x08\x13\x96\x12\x2e\x23\x21\x95\x13\x22\xeb\x0d\x09\xb3\xcb\x22\x40\xd8\xcf\xcb\x11\x66\xa1\xf7\x29\x7e\x10\xc9\x33\xab\x80\x9c\xb9\x30\xec\x05\xc9\x1f\x4c\x25\x13\x75\xa8\x37\x00\xe4\xc7\x83\x2e\x08\xd7\x06\x5d\x96\x95\x27\x03\x15\x2a\x40\xc3\x4c\x5e\x85\xe2\xb4\x85\xb6\x3a\xc0\x22\xfd\x86\x44\x5e\x48\x0e\xc3\xd9\x42\x88\x15\x9a\x1c\xf1\xca\x61\xce\xfa\xeb\xc1\x11\x9c\x97\x19\xd1\x99\x65\xae\x92\x14\xfa\x55\x28\xba\x3d\xa4\xf4\xcb\x2b\x9a\xb5\x09\xfd\x0c\x0f\xd9\xd7\xa5\xa6\x8f\xae\x15\xb3\x23\x3c\xc1\x20\x85\xc3\xee\x4a\x49\x80\x5d\x45\xc1\x69\x1f\x1c\xda\xe1\xb5\x59\x9d\x4b\xb0\xf8\x82\xc7\x9d\xa7\xcc\x94\x26\x94\xe7\x78\x0b\x53\x40\x67\x07\x64\xcf\x9d\xb9\xeb\x36\xc4\x15\xd6\xad\xd8\xa7\x1e\xd7\xed\xe3\xba\x45\xb7\x5f\xb7\x77\x59\x1d\x60\x21\x3c\x8a\xb2\x85\xd7\x86\x15\x13\x46\xd1\xc0\x45\x7e\x3d\x38\x72\x72\x00\xd9\x83\xcc\xe0\x00\x77\x65\x3b\x56\xcc\x4e\x8a\xa1\xe9\xe3\x41\x32\x61\x4b\x87\xb0\x85\x28\x99\x65\xd5\x99\x87\x18\xac\xaa\xec\x41\x90\x12\xef\x46\xcd\x89\xfb\x42\x1e\x50\x20\x22\x71\x69\xc9\xe6\xe1\x3f\x4a\x92\x0c\xa3\x49\x74\x45\x64\x21\x4b\xff\xc0\x13\xd4\x14\xd2\x90\x4c\x88\x4c\x0a\x73\x91\x5d\x72\x01\xd2\x29\x39\xe9\x64\xb3\x7e\x86\xff\x7b\x86\xe3\xdc\xaa\x62\x40\xaa\x68\x27\xa5\xf5\x50\x47\xd1\xa9\x1a\x94\x51\xd2\x66\x65\xbe\xaa\x9f\xec\x6c\x36\xac\x6c\x31\x92\x8a\xd5\x66\x8d\x94\x44\xfe\x60\x02\x0b\xeb\xf1\xe8\x0c\xfd\xb6\x41\xeb\x9d\x46\xa5\xa1\x4b\x8a\xdf\xdc\x04\xfa\x6d\x8f\x95\x57\x02\x9a\x48\xa2\xed\x61\x10\x86\x64\x02\xe7\x28\x40\xa6\x90\xe6\xaa\xb7\x42\xff\xb5\xab\x3f\x0e\xdf\xf7\x8e\xd1\xff\x6a\xaf\xae\xa1\x29\x03\x9a\x31\x5d\x9e\x0d\xe6\xe1\x97\x41\xb6\x06\x72\xf2\x34\x08\x57\xf8\x53\x89\x6c\x7c\x18\xf0\xeb\xe7\x59\xc6\x63\xe7\x8b\x40\x28\xcc\x5c\x19\x02\x27\x0b\x3c\x16\xb2\xbf\x02\xc8\xf2\xed\x33\x41\xcb\x5a\xc9\xae\xc7\x63\x21\xa0\xa4\xfb\x48\x00\x94\x89\x68\x96\x64\x50\x20\x9e\xe5\x03\x1f\x9b\xc5\xe1\x4b\x8c\x2b\xf9\x55\x5c\xaf\x79\x5a\xdc\x2c\xe5\x82\x39\x08\xf5\xcb\xb5\x5b\x33\x10\x51\x8d\xc6\x3a\xd9\x90\xc6\xcb\x15\x33\x64\x16\xe7\x82\x76\xc0\xaf\xc8\x84\x1a\x33\x82\x35\x80\xd2\x17\x2f\x69\xce\x69\x11\x61\xe5\x5f\x5a\x01\x5b\xb3\xf4\x5e\x88\xb7\x6b\x86\x5e\xa0\xa9\xde\xe0\x2b\xa1\x17\x88\x80\xa2\x60\x51\xf8\xba\x18\xef\x99\x83\x8b\xf1\x1e\xdc\x5a\x94\xb7\x73\x31\x2b\x45\x2a\x2b\x0f\x5f\x50\xb0\x1f\xb5\x4d\x14\xa1\x65\x97\x5b\xbe\x0c\x9d\xc6\xb9\x97\xde\x94\x48\xaf\x1a\x76\x68\xa3\xb0\x7d\xe7\x87\x7f\x19\xb4\xa7\xa2\x64\x33\x43\xd8\x0c\x43\xfb\x20\xc0\x5c\x0f\x92\x78\x10\xe4\x1c\x66\x65\x0d\xcc\xa7\x78\x2a\x18\x0a\x2c\xd9\x51\x10\xd2\x40\x46\x6c\xa1\x7e\x1b\x2e\x33\x8b\x75\x3e\xf3\x4d\x38\x02\x34\x5b\xe1\xca\x1d\xca\xe9\x2c\xc1\xc6\x07\xde\xe1\x5c\xc9\x5c\x2c\x2d\x62\x88\x01\x8b\xc6\x41\x96\xc3\xf3\xfc\x35\x5d\x88\xd7\xa7\x35\x75\x39\xbf\x44\xfe\x12\x75\x31\x3b\x63\xce\x60\x36\x4f\x62\x2a\x38\xb8\x29\xa6\x00\xb7\xa1\xaf\x41\x69\x33\xa5\xdb\xe6\x82\x7a\xfe\xbf\xe2\x22\xc8\xe6\xa2\x60\xbf\x59\xb0\xdd\x2a\x94\xdd\x03\xdd\x9f\xd1\xff\x7e\x12\xe2\x1b\xaa\x1e\x3c\x11\xa7\x35\x7a\x29\x02\x27\x09\xa9\x3b\xbd\xb7\x3d\x17\x14\x36\x57\x37\x82\xbe\x08\x2c\x5d\xd8\x30\x21\x02\xc9\x3b\x08\x1c\xfc\x08\xd8\x00\x48\x86\x93\x1a\x81\x13\x4c\x01\x33\x4f\x3b\xd5\xd1\xb6\x8d\x26\x6e\x14\x6f\x84\x05\x0c\x03\xe9\x44\xab\x1f\x7b\x92\xf5\x61\xb9\x0d\x60\x49\x80\x33\xd5\x3e\xd4\xe2\xc7\x09\x72\x33\x19\x01\x45\x2d\x8a\x54\xc5\x2e\xf9\x3e\x01\xdb\x4f\x07\xfe\xc5\xc4\x9a\x87\x01\xc3\x96\x94\x4b\xda\xaa\x71\x89\xf3\xc4\x40\xa0\xc2\x96\x08\x1a\x0d\x38\x95\x6b\x77\x33\x76\x69\x7f\xf5\x45\x79\xf3\xaa\xf5\xca\x12\x7a\xb1\xba\x30\x06\x42\xd5\xe2\x38\xcb\xbc\xc7\x78\x8a\x82\x1c\x8d\x31\xe1\x82\x49\xcc\x57\x00\x4b\xf3\x41\x2d\x41\x61\xbf\x06\x86\x6b\xf2\x2d\x24\xce\x37\x93\x28\xa6\x46\xa2\xec\x10\x6f\x84\x4b\x54\x1f\x59\x25\x3a\x7d\x12\xfe\x94\x90\x26\x60\x7f\x4c\x8f\xbc\xd1\x05\xfa\xf1\x47\xab\x3e\x5e\x0f\xd4\x71\x78\x2b\x5d\x46\x81\x89\xaa\x4c\x71\x9e\xcf\xf5\x66\xab\x5e\x49\xbb\x45\xd2\x42\x24\x11\x86\xd2\xec\x95\x85\xa0\x79\x73\xf7\x4b\xc8\xab\xab\xe4\x20\x43\xf3\x7d\xb9\x44\x2e\x90\xd7\x99\xe9\x17\x48\xe0\xf0\x7b\xae\x0e\x82\x5f\xc5\x53\x1b\x41\xd7\x29\xf9\x56\x97\xf1\x0f\xb7\xac\x1e\x16\x6f\x6b\x7b\x20\xf9\xcd\x99\x01\x2a\x1f\xd9\xda\x9b\x67\xf9\x77\x47\x4b\x05\x30\xbd\x63\xb2\x87\xdd\x0c\x05\x0d\x92\xf1\x18\x53\xfa\x4f\x86\x5c\x34\x00\x51\x13\x43\x32\xbd\x32\xd1\x43\x12\x45\x25\x27\x6f\xb2\x8d\xa6\xc1\xa5\xf4\xca\xea\x97\x68\x77\xfd\xa0\x0e\xe8\x42\x48\xa9\x52\xbb\xb8\x78\x84\x14\x0f\x8c\x0b\xd2\xfa\x64\x7d\x1a\xe6\xb8\x2e\x40\x59\x30\xa6\xd8\xc3\x0f\x00\x06\x2a\xc9\x80\x86\x1f\xc5\x69\x74\x41\x65\x15\xce\x31\xac\x00\xf9\x55\x6a\x21\xe7\x4b\x96\x83\x66\xac\xd5\x6a\x72\xcd\x6d\x7a\x56\x2e\xdf\x0c\x46\x78\x72\x3b\xb8\x76\x81\x93\xa9\xcc\xc1\x62\x7a\x28\xc1\xb3\x82\xa0\x49\x19\x6f\x8a\xa4\x8d\xf4\x14\x43\x45\x2c\xfe\x56\x17\xc3\x06\x49\x7c\x81\xd3\x5c\x91\x61\x69\xba\x3b\x6e\x4c\x09\x16\x9f\xd4\xfa\xcf\xed\xb6\x7a\x48\xab\xa8\xce\xab\xe2\x65\x45\x7b\x98\xf9\x2e\x56\x2a\x6a\xf3\x8f\x75\xc2\xbb\x49\xc6\x47\xb3\x13\x0d\x62\x91\xc5\x6a\x9a\x64\x59\xd4\x1f\x63\xf7\x8a\xb5\x34\xb5\x98\x73\x53\x31\x50\xa6\x3d\x28\xfd\xc6\x4f\xe0\x7f\x1a\x50\x90\x50\x9f\x93\x15\xdc\x95\x7e\x17\x0e\x4f\xd6\x4a\x5f\xf0\x75\x57\xf5\x8b\xb2\x16\xd3\x3c\xa5\xec\x85\xc8\x32\xee\xc2\x7f\xe7\x14\x14\xab\xb2\x6b\xba\x73\xd9\x6b\x30\x11\x5e\xb7\x4c\xb0\x17\x16\x72\xbd\x7a\x74\x7e\xdf\x3b\x5e\xb3\x57\x90\x58\x78\xcb\x5e\x42\x2c\x1c\x09\x28\x7d\xb7\x72\x30\xc5\xf1\xf1\xf1\x07\xa3\x5a\x75\x67\x32\x79\xfa\xed\x82\xd7\x24\xba\xda\x8b\xd5\x72\x95\x4d\x8f\xe8\x2a\xce\x16\x5b\xc6\xc8\xb9\x6e\x4c\x56\xa2\xf9\x06\x3a\xb8\x09\x39\xd4\xb9\x81\x73\x03\x5b\xee\x95\x01\xbb\x02\xfc\x8e\x86\x91\xbe\xc6\x4b\xe0\x40\x16\xb0\x8c\xa6\x00\x83\xf4\x71\xb8\xf0\xa2\x2c\x30\x8e\x13\xfa\x46\x63\x80\x2c\x69\x3f\x2e\xe3\x1e\x55\x97\x34\x45\x5e\x5c\xd3\xb1\xb5\xbd\x8c\x9e\x3f\xb7\xfb\x56\x58\xcb\xaf\xe4\x09\xcd\x37\xe4\x72\xe5\x98\x53\xcb\x41\xaa\x4e\xc2\xe4\x15\x65\xe2\x14\x63\xe3\xb2\xaa\x2a\x4a\xa0\xaf\x5f\x29\xb9\x16\x75\x56\xf8\x24\x5e\xf3\x63\xaf\xa1\xa3\xb1\xca\x49\x94\xca\xe6\xdd\x6b\xd0\x76\xe0\x6a\x43\xfc\xb4\xdf\x6e\xb0\x9e\xdb\x88\xd3\x06\x9a\x15\x17\xb9\x8c\x61\xf7\x52\x07\xb1\xfc\xba\x43\xac\xba\xc0\xbd\xe4\x62\xde\xcc\xcb\x41\x32\x99\x06\x39\x6c\x2f\x55\x97\xa1\xbc\x2d\x68\x9b\x98\x24\xfe\x54\xdd\x13\x6d\xcb\xef\x36\xc8\xdd\x97\xe1\x60\x42\xdb\x3e\xe6\xe4\xed\x20\x64\x89\xba\x5c\xbc\x51\xa1\x6f\x51\xbc\x32\xf7\x9d\xa3\x96\x91\x23\x2d\x29\x4b\xb0\xf8\x62\x0b\xd4\x48\xc4\x5d\xad\x02\x79\x67\x3b\xc6\x42\x7f\xcd\x43\x2c\x29\xee\x54\xb5\x5c\x4a\xd1\x6a\x0c\xed\xfd\x69\xfd\xaa\xdd\xec\xf8\x9d\xc1\x1a\x24\x36\xe8\xb4\x3b\xad\xf6\xb0\x3d\x3c\x5b\xe2\xaa\x78\x00\xcd\x1f\x8a\x7e\x38\xce\x91\x15\x50\x70\x8e\x85\xe3\xf0\x25\xea\x16\x8c\x8c\x86\xb5\x59\x7c\xcf\x2b\x5b\x63\xb2\xbf\xd2\xa2\xc2\x23\x5f\x27\x05\x9d\xde\x7a\xc9\xa8\x31\x1b\xf8\x82\xbe\xc5\x1a\xbe\xdf\x00\x0e\xa6\x30\xaa\x2d\xbd\x69\x90\x66\xb8\xa6\x2c\xd4\x92\x8b\xc9\x34\x53\x14\x3f\x45\x35\xab\x57\x02\x29\x8e\x68\x0c\xaf\x39\x8b\x8e\x12\x86\x81\x4c\x99\x7a\xb5\x0c\x22\xbf\x8c\x93\x0e\xc3\x2c\x29\x84\x01\xee\x04\x67\x39\xb5\x6d\x08\xc6\x96\x05\xaa\xc1\x3c\xad\x9f\xa1\x8d\x0d\x54\xac\x3d\xf4\xe3\x8f\x7a\xbb\xa7\x3e\x2b\xc3\xd7\xa4\x4b\x05\xb5\x7d\x45\x2f\x30\xcc\x96\x91\xca\x61\x8c\xc5\xaf\xb5\xc8\x4c\x79\x1a\x1e\x6a\x2d\x95\x58\xd7\x25\x17\xec\x88\x0e\x57\x41\x05\x0c\xb3\xbc\x01\x7f\x0a\x0d\xd4\xf5\x5b\x6b\xa3\xb8\x72\xab\xe3\x77\xaa\x31\x0a\xeb\xd1\xc8\x71\x0c\xf2\xa4\xd3\x89\x2a\x9a\x97\xde\x15\xf1\x45\x78\x99\x06\xd3\x29\xc8\x91\x41\xce\x9a\x97\x55\x26\x28\x20\x3b\x7d\x26\x79\xa5\x95\xae\x5e\xc5\xd5\xc7\x70\x65\x2b\x1c\x7e\x6c\x9f\xaa\x3a\x90\xdc\xfa\xb2\x47\x08\x3d\x5c\xc6\x2f\x93\xea\xb9\x8e\x40\xee\x2d\xeb\x2c\x75\x08\x8d\x43\x4a\x35\xe2\x80\x51\x5c\xec\x58\x0e\x4e\x65\x21\xa2\x74\xef\x45\x40\xa8\x6b\x88\x6a\xd2\xc4\x96\x06\x95\x62\xd7\x0e\x64\xde\x98\x37\xdd\x5d\x3c\x54\x0b\xe5\x93\xe5\xa8\x53\xe2\x7d\xce\x9a\xa6\x36\x28\xec\x77\xe1\x77\xfe\x37\x89\xe1\x62\xdf\xc2\x36\xff\xda\x0d\x8c\x2c\x4b\xbb\x46\xc5\x5c\x56\xc2\xbf\xd2\xd4\x46\x28\xae\x96\x8e\x53\xd8\xc3\x35\x58\x04\xa9\xd1\xd5\x09\xdf\xb4\x71\x4f\xac\x36\x87\x34\x50\xa2\xec\xb0\x38\xc7\xba\xbd\x58\x6f\x17\x42\x67\xa1\xe8\x39\xdb\x36\xfb\x75\x29\xba\x41\x52\x38\x9f\xd8\x02\xa0\x59\x7d\x56\x0d\xb1\xa4\xf0\xcc\x10\x01\x12\x58\x67\x6f\x23\x99\xf4\xa0\x7f\x05\x4c\xb8\x02\x36\xa0\x30\x7b\x23\xc2\x71\x85\x63\xae\x6b\x3f\xaa\xbe\x9d\x96\x6d\xda\xca\xfe\x6a\x16\xe4\xaa\x45\xcb\x27\x42\x56\xa2\x6f\x2b\xd1\x85\xa5\x88\xa4\x23\x64\xf4\x62\x96\xa1\x5a\xc1\x02\x10\x5c\x88\x9a\xc5\x84\x3e\xb0\x28\xc9\x5e\x59\x0a\x4b\xba\x40\xdd\xc2\xda\x52\x5a\xd2\x0b\x12\xd2\x1b\x5a\x8e\x6b\x37\x95\x8f\x2d\xec\x1e\x3a\x13\x13\x27\x14\x5f\xf2\xb5\x0c\x7a\xb0\xed\x49\x26\x00\xb1\x43\x69\x17\x4d\xd2\x23\xe4\xf6\xfe\x3b\xee\x53\x5a\x80\x16\x11\xe9\xf8\x1b\xec\x4d\x45\x54\xe5\xf9\x6c\x9a\x7b\xcf\x5b\xd8\x34\x27\x3b\x16\x46\x41\xf2\xa8\xbf\x35\xcb\xbe\x6f\x14\xf5\x7d\xe9\x1e\xb7\x14\x67\xec\x02\x47\x84\x81\x6f\xb0\xab\x30\x8d\x83\xa4\x5a\x90\x17\x93\x06\x58\xde\x29\xd8\xed\x37\x9c\x5f\x65\xe4\x0b\x6e\x62\x6b\x8e\x71\x0a\x73\xc3\x90\x27\x4f\xd9\xc4\x94\xa8\x8b\x74\x58\x8a\xbd\x49\x62\x32\x8a\xc2\xc7\xba\x4d\x88\x26\x16\xd6\xc6\x58\xd9\x9a\x3e\x56\xea\xfd\x0b\xe8\x98\x82\x2c\x9b\x4d\x70\xa8\xde\x27\x06\xe3\x14\x07\xe1\xb5\xb4\xdf\x29\x07\xb2\x59\x4c\xd3\x56\x56\x88\x68\xb6\x18\xdb\xb3\xf3\xaf\x85\x0e\x4d\x84\x71\x81\x89\x7a\x9a\xe1\x85\x79\xbd\x5b\x5f\x34\x8b\x17\x85\xf5\x17\x4a\xdc\x06\xc9\x53\x15\xd2\x01\xa7\x02\x24\x88\xdf\xce\x03\x3e\x1b\x3a\x25\x79\xf5\xb0\xca\xb6\x54\xde\x2c\x76\x8d\xbc\x08\xe7\x84\xb0\xe1\x36\x21\x94\x3d\x99\x4b\x55\xbf\xd8\x40\xa5\xda\x51\x06\xad\x44\x29\x6a\x68\x26\xac\x37\x24\xef\xed\x26\x12\xf3\xae\x4c\xbe\x84\x43\xb8\x2f\xa1\xff\x96\x5f\x96\xcc\xb3\xc2\x30\x2f\x4c\xde\x53\xe8\xa4\x95\x6a\xf7\x24\x5b\x04\x3c\xdc\xe9\x93\xc6\xc8\x5a\xde\xfb\x99\x2b\x0c\xa6\x2c\x5e\x50\x75\x75\x2c\xaf\xc1\x2c\x2f\xd8\x03\xc8\x29\xa4\x19\x00\x5c\xee\x15\x52\x04\x2a\xc7\xd4\xb6\x22\x8a\x99\x25\x2f\xb3\x03\x60\x26\x33\xe7\x38\x06\x63\xde\x72\x68\x22\x4a\xb9\x03\x18\x0d\x9d\x5d\x0e\xcb\xd4\x19\x80\x0a\x4b\x12\x92\x36\x51\xa7\x05\x26\xc7\xf0\x81\xdb\xcf\xee\x0d\x51\x32\x89\x88\x8c\xe0\xa1\x80\x7e\xba\x8c\xc6\x63\xd4\xc7\xa2\xc1\x10\xa5\x41\x1c\x26\x93\xf1\xf5\x3d\x1d\xee\xa9\xd5\x04\x1b\x26\x0f\xed\xfd\xec\xc1\x94\x92\xc6\xbf\x01\x17\xa2\x93\x1c\x9a\x2c\x48\xa2\xc6\x15\x7c\x85\x07\xb3\x1c\xd7\x9e\xf3\x68\x54\xcf\x3d\x96\xb8\xc3\x63\xe6\x5b\x0e\xb1\xe8\x9e\xa0\x7b\xe8\x39\x19\x0e\xf2\xff\xcf\xdd\x67\x66\x0a\x46\xe6\x6e\x9c\x9a\x3d\x4e\xa2\x1e\xa3\x2e\xaa\xd8\xb4\x1b\xf5\xd3\x69\x66\xb3\xec\x50\x54\xff\xe0\xbc\x4a\x32\x94\xc8\x14\x4e\xad\xd3\x5a\x35\xd2\x9a\x5b\xdc\xea\xe8\xd2\x96\xd6\xb5\x29\xad\xd0\x78\xb3\x34\xf1\x40\xa1\xc0\x15\x31\xee\x8a\x34\xc8\x6c\x21\xdd\x2c\xad\xb0\x44\xde\xd2\x78\x00\xfe\xd6\x80\xb5\x84\x36\xf3\x72\x0c\xc0\x6e\xda\x50\x93\x8b\x64\xd0\x4c\x41\xce\x93\xc9\xf2\x31\x47\x2f\x4c\x7d\xb6\x92\x1a\xba\x48\xe1\x6c\x77\x96\x3a\x62\xa2\xd4\x82\x87\xf1\xe2\x48\x2d\xa4\xe8\xdb\x69\xb5\x6d\x9a\x01\x45\xc5\x1d\x32\xbe\xcc\x59\x9e\xc6\x92\x3d\x01\xcb\x21\x7e\xdd\x5e\x1f\x6e\x89\x12\x27\x14\xe2\xf6\x6f\x36\x0d\xd7\x03\xea\xc7\xdf\x6f\xed\xdc\x20\xb2\x7d\x72\x0b\x4a\xdb\x2e\x5c\x48\x79\x9c\xd9\x96\x6f\x71\x0b\x69\xc5\x2d\x1d\x76\x3b\x3f\x7c\x09\x87\x5d\x69\x7b\x96\x28\x64\x41\xf5\x38\x73\xa9\x5a\x64\x5f\xfe\x3e\xf4\xe5\xa5\xd2\xc1\x77\xa0\x8e\xf8\x9b\xa8\xcd\x2d\x8b\xaf\x92\x26\xf9\x39\x1f\x6a\x57\x58\xd9\x87\x6f\xd8\x43\x7f\x3e\xb0\x06\xbb\xd8\x8e\xbe\x91\xc2\x41\xdb\x5d\x93\xdc\xa5\xdc\xb5\xc9\x2e\x04\x3c\x11\x5b\xb8\xb8\x22\x61\x4f\x87\x57\xc8\x18\xec\x99\x6e\x7b\x2e\xef\x4e\x2a\xc6\xd2\xbe\x19\x5d\x5a\x81\x2d\x56\xc1\x60\xc5\x1a\x92\xc0\xa9\x98\x57\xf4\x25\xee\xeb\x0c\x39\x00\x84\x31\x3f\x6a\xfb\x92\x1e\xdf\x40\x63\x3f\xba\xa2\xc9\x40\xa0\x82\x75\x48\xa5\xb3\x35\x35\xcc\x54\xa0\xbb\xf4\x26\xd6\x13\xdf\x1d\xf4\xc1\x7f\x01\x3f\xbe\x67\x05\xf1\xf7\xce\x98\xbf\x47\x3d\xb1\x8d\x19\x2e\xaa\x28\xbe\x13\x63\xbc\x77\x14\x4d\x45\xf1\x7d\x31\xee\x8a\x7a\xe2\x6f\xce\xbb\xbf\xb9\xb2\xf8\xdb\x6f\x15\x9e\x62\xdb\xe3\x38\xa1\xdd\xdf\xde\x51\x49\x1f\xee\xbe\xbf\xb0\x6d\x1d\xf2\xf8\x56\xdc\x3d\xca\x14\xe4\x85\x2a\x4f\x64\xba\x94\x53\x5a\xb2\xfc\x95\x37\x67\x5e\xbb\xf9\xbd\x26\xa5\xbc\xf7\x1c\x94\x8b\xe6\x9e\x54\x72\x4e\x1a\x88\x99\xe9\x27\xb5\xb4\x93\xbc\xa2\x23\xf1\x24\xe8\x47\x0b\xe0\xe2\xa7\x9a\x7c\x72\x3f\xc8\x47\x1e\xb2\xa4\xa0\x2c\x8e\xd7\x1f\x92\x41\x30\x46\xd3\x64\x7c\x3d\x8c\xc6\x28\x19\x22\xba\x69\xb1\x53\xbc\xe5\xc8\xcb\x62\xdb\x6f\xa8\x05\xb5\x86\x15\xc6\x24\x5e\xef\x90\xf7\x37\xaf\xcd\xd8\x41\x92\xad\x65\xff\x77\x83\xa9\x81\x8d\xe0\xac\x4f\x66\x50\x27\xe2\x9d\x95\x69\x9a\xe4\x09\xf9\x84\x36\xc8\xe9\x43\x2f\xc0\xea\xa1\x0d\x14\xe3\x4b\x82\x40\x39\x84\x78\x36\x1e\x3b\x16\x8a\xc0\xa0\x58\x26\x52\xbc\x23\x5b\x24\x4f\x3e\x27\xe5\x4a\x6e\xa7\x62\xfb\x43\xd4\x4f\x83\xf4\x7a\x9e\x8e\x5c\xca\x0f\xea\x04\x05\xd9\x42\x99\xd6\x93\x08\x17\xbc\xcb\xc1\x18\x45\xf1\x08\xa7\x91\x12\xc0\x55\x89\xe8\xa0\xe7\x19\x35\x23\x8c\x9a\xd3\x59\x21\xec\x1f\x8f\x31\x0c\xee\x71\xc2\xcf\x60\x14\xe4\x1c\x21\x16\xca\x83\x8a\x41\xc6\xa9\x12\xa1\xb2\x38\x80\x5c\xee\x4a\x2e\x70\x9a\x46\x21\xce\xd0\x21\x55\x88\x44\x38\xa3\x0c\x7c\x7a\x8d\xa2\x98\x65\x33\x2e\x10\xa8\xd0\x82\x9e\xab\xe1\x64\x51\x00\x86\xcc\xe5\x28\xb7\x48\xd4\x40\x32\x51\xfb\xd7\x27\x94\x84\x15\xe9\xa6\xc4\x24\x51\xf6\x17\x8b\xf0\x38\xec\xa2\xe7\x90\x29\xeb\xb9\x6e\x38\x62\x6f\x93\xfc\x4d\x70\x3e\x4a\xc2\x52\x1f\x79\xa9\xb4\x1e\x23\xdf\xe6\x78\x86\x90\x19\xce\x90\xa2\xaf\x18\x64\xf3\x79\x75\x06\x31\x9c\x06\x97\xb1\xf9\x45\x62\x24\x44\x58\x28\xd2\xea\xb9\xcc\x89\x37\x67\xe7\x13\x1c\x5b\x4c\x87\xc9\x8e\x52\x8e\x05\x2a\x98\x0f\x3b\x77\x15\xe5\xad\xe9\x1f\xac\x08\x30\x33\x29\xee\xfa\x15\x09\xc7\xd2\xd4\x8e\xd3\x0f\xbc\xc9\x51\x90\x1d\x5c\xc6\x8c\xec\xaf\x6b\xcf\x49\xcd\xe7\x4b\xc2\xe7\x89\x3c\xc2\x26\xc8\xcb\x93\x17\x73\xfb\x41\x6b\x95\x4e\xb7\xa5\xd6\xff\x93\xcd\xa6\x44\xd4\x8a\xa3\x7c\x25\x20\xc2\x29\xdb\xfa\x82\xf4\x7c\x46\x46\xd7\x3a\x1e\xc8\x92\x41\xa1\x64\x9c\x0a\x8f\xdb\xf4\x79\x86\x0a\x8e\x1e\x51\xa5\x30\x9f\x74\xba\x4a\x4d\x08\x72\x07\x95\xfd\xc0\xb1\xed\x20\xae\x18\x1f\xe2\x14\xc7\x03\xd2\x00\x8c\xf3\x54\x5f\xaf\xc6\x30\x30\xb9\xd8\x06\xd0\xb9\xcf\x20\x5b\x6a\x0c\x1b\x53\xdd\x86\x95\x92\xc9\x4c\x93\xaa\xbc\x67\x31\x1d\x07\x98\x40\xba\x6a\xcd\x10\xa8\x9b\x7c\x3e\x8a\x0c\x36\xb5\x25\x71\x0d\x47\x44\x69\x08\x29\x07\x40\x6a\x4b\x7f\x67\x5e\xc9\x23\x96\xa3\x0d\xc6\x36\xf9\x9d\xc5\x5c\x5e\x44\xcb\x95\x73\x3c\xb3\x11\x58\x72\x45\x9c\x6c\x73\xe5\xf2\x08\xea\xd2\x1a\xe1\xef\xd4\x75\xe2\xa4\x1a\x5e\xfc\x36\x64\x53\xe6\xae\xee\x98\x2b\x74\xc0\x98\x19\x4b\x12\x00\x24\x05\x26\xf4\x61\x88\xb2\x64\x82\x69\xea\x29\x74\x39\xc2\x31\xba\x4e\x66\xa9\x30\xb3\x0f\x88\x38\x4b\x81\xdf\x73\xec\xdc\xbb\xee\x82\xba\xa3\x73\xd9\x5e\x86\x28\x03\x58\x59\x31\x47\x46\x0c\xfd\x2d\xb7\xbb\xb9\x68\x54\x9a\xd3\x5e\x32\x25\xc2\xce\xb4\x90\x7b\x98\xbc\x73\x07\x71\x4a\x02\x06\x1a\x26\x45\xa6\x9a\x80\x26\xf2\x9e\xa7\x94\xad\x4e\xba\x7f\x56\x95\x5f\x6e\x39\xee\xd0\x88\x72\x89\x2d\xfa\x67\x5d\xe3\x22\xe2\x21\xbf\x6c\xfb\x18\x4c\xc0\x68\x62\x4e\x3d\xc4\xb6\xea\xa2\x98\xbe\x59\xcb\x00\x97\x4a\xb7\x58\x32\x9d\xa7\x72\xf1\x33\xb4\x21\xb5\xaf\x7e\x5a\x20\x75\x91\x63\x93\xdd\x46\x97\x49\xfc\x3c\xa7\xf2\x33\x77\x77\x94\x82\x17\x8e\x93\x64\x8a\x82\x7e\x72\x61\xd9\x06\xcb\xbb\xfc\x9c\x43\x7b\xee\xee\x30\x70\x51\xd1\xaa\xdc\x4f\xf1\xb6\x42\x5e\xad\x4a\x8b\x47\x1c\x4e\xa0\xa7\x60\xff\xb2\xc8\xba\xb1\x6d\x7c\x83\x71\x12\xe3\x07\xe0\x78\x00\x17\x6d\x14\x7b\x08\xbc\xa8\xb0\x93\x91\x62\x73\x37\x32\x39\x17\x89\x2a\x1c\x71\x7e\x6a\xb5\x27\xb3\x9f\x91\xad\xb7\xfb\x31\x0a\xc0\xf3\x56\x8b\x45\x58\x1a\x59\xc8\x88\xf3\x5e\x0e\xc2\x16\x9e\x46\x18\x3f\xa8\xe1\x10\xb3\xe8\x3c\x8e\x86\xd1\x20\x88\x73\x16\x50\x32\xa2\xbd\x07\x90\xb4\x1d\xdb\x31\xf9\x17\xc9\x83\x98\x9e\x95\xe5\x37\xf7\x10\x36\xc6\x6c\x5e\x27\x0b\x47\x18\x7c\xd9\xf4\x6a\xce\x58\x23\xab\x59\x98\x18\x29\xed\x06\x63\xee\xa0\xe1\x7b\x4b\xf5\x22\xfb\x67\x2b\x1b\xbb\x61\x0b\xe3\xd0\xfe\x57\x07\x70\x5a\xbf\xaa\xd7\xeb\x7e\xbd\x51\x6f\x7a\xa8\x7e\x55\x6f\xd5\xdb\xf5\x4e\x7d\xed\xec\xc1\x00\x7b\xa8\x53\x39\xf4\x0a\x0b\x5f\xc7\x67\xc4\x58\xb1\x97\xcc\x21\x18\x96\x2b\x7f\xa0\xff\x7e\xfd\x0a\x31\x7b\x35\x51\x63\x88\x6a\x62\x7a\x7f\xd8\xb0\x28\x0a\xe5\x3f\x80\x2a\x19\x0d\xf1\x9f\x95\x8d\x49\x75\x00\x94\x3c\xc6\x38\x3e\xcf\x47\xd4\xf4\xc8\xc9\x45\xaa\xc7\x8c\x29\x16\xca\x62\x91\x62\xb6\xe3\x41\x12\x12\x7a\xc7\xf4\x87\x4e\xee\xf0\xba\x3c\xf6\xa7\x20\x00\x1c\x0f\x56\x76\xf1\x95\xbb\xcd\x79\x01\x64\x2a\xad\xf6\x85\x83\xbb\x14\xc4\x5a\x21\xb2\x8b\x25\xae\xc1\xbc\xb0\x2e\x96\x2a\xca\x90\x7c\xca\x87\xeb\x0b\x45\x73\x61\x53\xe1\x8c\xe5\xc2\xa7\xea\xeb\x57\xb4\x8b\xaf\x4a\xc3\xb7\xcc\x21\xa0\x41\x90\xe3\x98\xed\xf9\x2a\x05\x39\x98\xbf\x9b\x90\xa4\x7b\xd8\x62\xc0\x4f\x18\x37\x94\x28\x13\xd2\xfc\x2e\x7a\xaf\x5b\x15\x97\x2a\xb4\x21\xb0\xf3\x79\xfc\x0c\xf1\xa6\xe1\x4e\x69\x06\x25\x75\xa6\x44\x03\x3b\x2f\x16\x8e\x84\x0c\xec\x2f\x06\xc3\xb2\xf8\x2a\xe6\xa3\x40\x84\x3a\x28\x48\xcc\x5d\x3a\xca\x8e\x0b\x1e\xa3\xf0\x1c\x07\xf0\x63\x95\x25\x51\xf8\x45\x1d\xa3\x53\xbd\x71\x30\x99\x22\x7c\x05\x91\x24\xfb\x91\xde\x39\x7a\xaf\x4a\xca\x98\xb7\x0d\xf4\x3e\x75\x60\x0b\x92\xa2\x20\xfe\x2f\x47\xa0\x74\xa8\x4f\x44\xd2\x18\xc3\x56\x8b\x82\x1c\x05\x28\x8f\x26\x16\x89\xdb\x16\x92\x5d\xee\xae\x3b\x29\x84\x3c\x38\xa4\x28\xda\x20\xe8\xb1\x59\x38\x8d\x78\x54\x6c\xf2\x4f\xad\xd1\x42\x2f\x51\x2d\xa2\x18\xbf\x40\xeb\x4b\x4b\x22\x5a\xb6\x53\x8a\xa7\x70\xd4\x1e\x2f\xa3\x48\x84\xdb\xfe\xba\x51\x34\xfd\xe6\x0d\x6f\xc3\x52\x5e\x34\x5a\x41\xf0\x77\x6e\x4b\xf2\x98\xd2\xc5\x75\xa7\x31\x75\x47\xb9\xaf\xda\xfd\x0d\x64\x0e\x76\x95\x8c\xc1\x26\x15\x8a\xcd\x76\x79\x43\x45\xd3\x96\x63\x25\x88\xe2\xa0\xaf\x9f\x3c\xa4\x03\x40\x55\x76\x4a\x63\x70\x10\x21\x50\x11\x0c\xa3\xfc\xae\xa2\x60\xb1\x38\xc5\xea\x72\x30\x29\xf2\xb9\x6a\xe8\x5e\x0b\x6b\x32\xe5\x28\x5b\x5c\x24\x27\x93\xb1\x33\x0c\x8b\xa8\x76\x2a\x60\xf0\x38\xf3\x1b\xb0\x74\xe8\x1f\x90\x7e\xb3\x41\x48\x3f\x53\xf8\x82\x85\xe0\x15\x51\x6a\x03\xed\x07\xf9\x68\x65\x80\xa3\x71\x51\x73\x15\x2d\x10\x91\xc8\x7e\xfe\xad\xb4\xf3\x38\xcc\x91\x8c\xe3\xef\x6d\xed\x3e\xd9\x71\x57\xa6\x05\xe3\xbc\xab\xd2\xc2\xbc\x73\xae\x0c\x16\x4e\x6a\x14\x57\x39\xfa\xb9\x79\x72\x5e\x31\x69\x84\x99\xdf\xd7\x9d\x26\x75\xa4\xde\xe2\x53\x20\x89\x0d\xc3\x68\x3c\xe6\x61\x67\x99\x9b\x04\x9c\xb7\xe6\x0b\x25\xfc\x30\x17\xdb\x0e\xbd\x32\x28\xa7\x8b\x4f\xa5\x59\x66\x90\x2a\x11\xca\x7d\x19\x9f\x55\x38\x82\x31\x57\x10\xdf\x7d\xd2\xa2\x25\x64\x32\x89\xed\x47\x2c\x99\x3d\x98\x07\x2a\xf2\x35\x55\x6f\xc8\x27\x9f\x2f\xdd\x51\xe6\x3f\x5f\xa2\x0d\xf2\x5f\x47\x02\xb5\xc9\xe7\x3f\xc8\x36\x73\xd5\x0c\x42\xdc\x59\xef\xeb\xe1\xd7\x45\xb1\x20\xfb\x82\x64\xce\x51\x72\x4f\x50\xe1\xee\x8e\xb6\x5a\xab\x5f\xbd\xaa\x77\x5e\xa1\x17\xa4\x0b\x7f\xc0\x9e\xbe\xb3\xb3\xb3\xb3\x84\x96\xe9\x8b\x9f\x7e\x42\xf5\x2b\xbf\x0e\xdb\x3d\x41\xc0\xb1\xdd\xd3\x2e\xd6\xea\x57\xad\x4e\xbb\x4e\x81\x5d\xea\xc0\x2e\xab\x02\x83\xe1\xc5\xd9\x0c\x3c\x7d\x6a\x80\xc6\x9b\x37\xb4\x26\x5a\x46\x30\xd2\xa5\xf5\x59\xdd\xd5\x0d\xa8\xc3\xfe\xca\xcb\x2e\x6f\xa0\xfa\x4a\xdb\x59\x06\xc6\x94\x15\x7d\x41\xed\x6d\x38\xb5\x2d\xa1\x9f\xd0\x4a\x1b\xfd\x07\xf2\x51\x17\xbd\xf4\xab\x88\x28\x06\xe7\x50\xc5\x0d\x0f\xa5\x83\x60\x30\xc2\x2c\xbb\xce\x7c\x81\x83\xd4\xfc\x4c\xe8\x31\xad\xd5\x68\x55\x72\x54\x52\x90\x24\xbb\x89\x34\x18\xf6\x2b\x26\x5a\x75\x03\x7d\x4e\x6b\xb4\x3c\x10\xe4\x5a\x7f\xcd\xd2\xa7\xcb\x22\x87\x4f\x4d\x94\x2f\xe0\xa3\xaf\xa8\x5e\x31\xac\x79\x8c\x2f\x25\x67\x27\xb8\x75\x64\x0a\x90\x98\xa7\xef\x79\xa2\x8d\xa4\xdd\xf9\x94\x1d\xed\xe7\x19\xd2\xe0\x78\x00\x86\x34\xf4\x5f\xbb\x21\xcd\x2e\xbe\x32\x35\x01\x36\x70\xa4\xe0\x06\x05\xba\x42\x7f\x57\x8b\xbf\xa9\xab\x2f\x46\xf8\xaa\xb2\x0a\xa3\xc2\xc9\x73\xc1\xa8\x9a\x95\x5a\xbf\x2f\x46\x3e\xc2\x57\x66\x08\x4d\x36\x7e\xd2\xd1\x7e\x7e\x22\x21\x6b\xe0\xcc\xdb\x1e\x53\x2f\x2b\x9f\x3c\xb3\x45\x8f\x91\x74\xd6\x4d\x40\x23\x7c\xd5\x1b\x05\x69\xe5\x3c\x5b\xd9\xdc\x03\x1d\xe4\x48\x8b\xe8\x41\xee\xf2\x8e\x87\x38\x8e\x1d\x5b\xe3\x00\x96\x00\x69\x2d\x15\x6a\x1f\xbf\xb3\x64\xe3\x77\xb6\xaa\xa4\x9d\xfa\xb0\xbc\xae\x83\x41\x08\x70\xbf\x27\x51\x5c\x7b\xfe\xfc\x16\x11\x37\x25\x0a\xa7\xeb\x6d\x11\x4d\x0f\x5f\x29\x94\x70\xab\x2f\x18\x87\xf0\xf4\xd7\x4b\x4d\x7c\xb1\x51\x9b\x6d\xb1\x1e\xab\x47\xca\xa4\x55\x16\x4b\x94\x42\xeb\x7c\xe0\x47\x17\xfa\xc8\x8e\x32\x8b\xac\x9a\xcb\x45\x52\xd3\xc9\x8d\xb2\x2d\xb4\x51\x92\x1f\x93\xae\x96\x26\x68\x26\xa0\xd3\x7b\x71\xce\x3a\xbb\x92\xcd\xfa\x59\x9e\xd6\x22\x0f\x35\x96\x3c\x48\xc2\x57\xa8\x2c\xc8\x8a\x5a\x5f\xb2\x39\xe0\x2e\xbc\xe7\x29\xc3\xb4\x8a\x1a\x55\xdd\x67\x3f\x04\x79\x14\xfb\xd5\x36\x2d\x56\x96\xef\x5b\xe2\xf1\x76\x5b\x17\xab\xfe\xd7\xed\x5e\x55\x11\xb8\xaf\x35\x35\x86\xf6\xec\x7b\x18\xc5\xe5\x7f\xd4\x36\x46\x87\xe3\x3b\xde\xc9\x24\x04\xe9\x8e\x44\xa7\x6e\x65\x98\x26\x13\xf2\xb6\x97\x84\x18\x36\xa9\xaa\x1b\x92\x0c\xf0\x0e\x7b\x92\x42\xb7\xb7\xdf\x96\x04\x39\x2e\xb4\x18\xbe\xeb\xcd\x89\xad\x22\xba\x3f\xc9\xcb\xad\xfa\x16\x25\x6a\x2d\xb6\x4b\x89\x6a\x62\xa3\x12\x6f\x1e\x7a\xaf\xd2\x9a\x9e\x97\xcb\x39\x92\xb4\xe8\x45\x6f\x57\x06\x8c\xa0\x37\xf3\x5a\xc4\xd7\x84\xbe\x55\xd9\x75\x8b\x0b\x6f\x55\x1a\xc2\x55\x77\xaa\x4f\x27\x3b\x2f\xd7\xab\x6d\x54\x9f\xf2\xe1\xba\xd8\xa6\xd8\xc3\xed\x36\x29\xda\xe8\x5f\xb7\x47\x55\x6c\xff\xbe\x56\xd6\x2c\x1f\xae\xdb\x37\x28\x32\x8a\x0f\xb9\x3d\xe5\xe9\x75\x89\x81\x51\x88\xc9\x11\xfd\xd3\xd1\x5e\x8f\x7b\x3a\xd5\x70\x36\x08\xa6\xb8\x56\xb2\x71\x9a\x6c\x19\x0d\x82\x7c\x30\x42\x35\x33\x7d\x34\xa0\x30\x4a\x93\x4b\xa0\x5b\xc8\xb8\x52\x7b\xbe\x1f\x8c\x87\x49\x3a\xc1\x21\x9b\x86\x30\xc8\x03\x33\x05\xdd\xe2\x0c\x5c\x9e\xd4\xdb\xf3\x6f\x36\x57\x8b\x90\xc9\x77\xcd\xbc\x81\xc2\x28\xeb\x2e\xc8\xb0\x3a\xe3\x66\x75\x5c\xc6\x00\xca\xd6\x30\x8b\x19\xf5\x50\x0b\x01\x85\xae\x38\x9c\xa5\xca\x01\x68\x44\x0a\x5e\xc8\x85\x89\x43\x96\xcd\x4c\xf2\x42\x77\x66\xe2\x95\xec\x64\xaf\xa5\x94\x68\x93\x59\x96\xa3\x3e\x46\x11\x19\xd1\x09\x8e\x73\x9a\x67\x2d\x80\xeb\xf5\x14\xe7\xc2\x63\xa1\x52\x6e\x5f\x2d\x4f\xa7\xaa\xdc\xa7\x39\x0e\xa9\x6b\x55\x91\x20\xfe\x0b\x9e\xe6\x68\x16\x4f\x79\xd2\x40\x35\x3b\xa8\x64\xd3\x52\xb7\x70\xdf\xb7\x6c\x1c\x20\xd3\xe0\xa6\x18\x05\xe1\x25\xe6\xfa\x5c\xd1\x0c\x0e\xb2\xbb\x32\x6b\x1e\x6d\xa4\x9f\xb3\x24\xda\x2c\x89\x69\x9e\xa0\x28\xcf\xb8\x57\x0c\x22\x14\x7c\xd7\x3b\xa6\xbe\x15\x79\x9a\x10\xd7\x7d\xc9\x54\x29\xeb\x2e\x33\xef\x43\x60\xa5\x6c\xb3\x19\x80\x0c\x9c\xcc\x53\x51\xdb\x59\x75\xa6\x44\xcb\xc7\x5b\x41\x1e\x70\x61\xbd\x5e\x55\xd2\xdc\x0c\xc3\x0c\xda\xe0\x79\xc1\x1d\x23\xcd\x68\xa1\xfa\xa6\x28\x82\x2c\x18\x99\xc7\x99\xb1\x0b\xa2\x6b\x9e\x39\x01\x50\x7e\x49\x7d\x4a\x02\xc9\x82\x92\xda\x13\x03\xc7\x7b\x98\xc9\xfc\x4c\xd1\xa9\x3d\x37\xf9\x7d\xa5\x7a\xf3\xf7\x46\x56\xb2\x4a\x32\x73\xd3\xbd\xbe\x48\x47\x27\x07\x14\x95\x06\x88\x05\x13\x55\x41\xc9\x3e\xce\x40\x46\x73\xe2\x44\x32\x5a\x93\x98\x32\x60\x38\x3f\x52\xda\x26\x74\xcd\x45\xbe\xdc\x94\xc8\x06\xcc\x20\xda\xe5\x0d\x35\x49\x7a\x55\x0a\xe6\xb9\x4e\x33\x14\x5c\x04\xd1\x18\x22\x76\x51\xbe\x00\xcc\xce\x4d\x35\x27\x92\xb3\x4a\x14\x5f\x24\x5f\x70\xa6\x27\x19\xae\xb1\xe4\xc0\x1e\xba\x1c\x45\x83\x91\x95\x55\xf7\xaf\x4b\x58\xb5\xd9\x2a\x5f\x28\xfd\x24\x19\xe3\x20\xbe\x41\x61\xb2\x33\x9e\x65\x23\xf4\xcb\x08\xe7\x34\x9e\x09\xcf\x45\x0b\xee\x5a\xd3\x20\x05\x46\xc1\x5e\x15\x5c\x5b\xb0\xeb\x5b\x84\x03\x11\x9c\x1e\x46\xfc\xee\xdb\xbc\x00\xb8\x45\x09\xc9\xb5\x66\x78\xaa\x5c\x57\x5c\x8e\x05\xc1\xd8\x33\x05\xab\xb1\x56\x69\x51\x65\xf1\xd1\x01\x5f\x50\x67\xc2\x96\x48\x41\xdc\x16\x6d\x09\x79\xcd\x8d\xd3\x60\x64\x5d\x6a\x15\xf2\x51\x32\x34\x73\xd1\x3d\x2f\x5e\xc8\x0a\x1b\x5a\x4a\xe6\xb2\xc2\x1c\x7a\x51\xdb\x1e\xd1\xaf\x97\xcc\xe2\x9c\xd3\x97\x85\x99\x10\xa0\x31\x4d\x24\x7c\x04\x71\x8b\x37\x54\xfc\x57\xb5\x26\x5f\x9b\xbc\xc8\x35\xe4\x0c\x83\xa3\x64\x16\x87\x68\x36\xa5\x0e\x85\x83\xf1\x2c\xc4\x1a\xdd\x9b\xd5\x34\x8c\x0a\x23\x17\xf9\x43\xf5\xd8\xb6\x02\x8b\x30\xb9\x8c\x65\x3c\x92\x78\x7c\x8d\x86\x33\xb1\x28\x2d\x91\xf4\x57\x57\xd1\x18\x67\xd4\xa9\xd2\x2e\x6b\x01\xdf\x48\xf1\x24\x88\x62\x55\xb8\xaa\xd6\xaf\x49\x70\x55\x53\xfa\x05\x17\xa7\xe8\xa5\x2d\x33\xbb\x37\xff\x4a\x55\xcc\x39\xd5\x3c\xb8\xa6\x1c\x28\x99\xe3\xa1\xb4\xfe\x02\x49\x04\xe8\xa2\x27\xa0\x0d\x27\x39\x91\xaf\x6a\x1f\xa3\xb8\x26\x37\xf9\x02\xb5\x3c\x85\xce\x6c\xe6\x93\x3c\x83\xb7\x8d\x48\x08\xdd\x49\x00\xcb\xdd\xb6\x28\x9f\xa7\x6a\x16\xf6\xfb\x8d\x3c\x02\xe2\xed\xb2\xb4\x9e\x9c\x46\x13\x04\x33\x9c\x92\xd3\xa4\xd8\x18\x5e\x16\x07\x04\x70\x86\xb4\x57\x64\xdc\x45\xdd\x83\x04\x57\xb1\xe5\xaa\x77\xcd\x31\x52\x52\x60\x15\x0c\x1f\xa6\xdc\x2c\xaa\x70\x5f\x99\x85\xe9\xc9\xb0\xe4\x11\xb5\xa0\xa1\x70\x32\xf4\x72\x43\x9e\xe9\xf9\x54\xc9\x63\x8b\x96\x61\xeb\x56\x38\xa9\xf8\x7b\x72\xd3\x77\x35\x76\xab\x9c\x85\xb2\xd4\xc9\xeb\x8e\x56\x6e\x8e\xdd\xf0\x2f\x32\x79\xfb\x6c\x6c\x88\x05\x26\xd6\x19\x2b\xb5\x78\x53\x79\x98\x38\x69\x3a\x32\xd1\xf3\x33\xf8\x28\xc8\x20\x43\xae\xf3\xc4\x3d\x37\x15\x79\xc1\xae\x65\x1f\x28\x3a\xe9\x0c\x3a\x0d\xbb\x86\x33\x94\xc4\xd2\x51\xd8\xef\xa0\x5a\xdb\x6f\x80\x25\xeb\x92\xe5\x58\xbc\x4b\x2b\xf3\x63\xb0\x78\xb4\x9f\x87\xef\x25\xea\x6b\x59\x06\xb2\xd2\x80\xa9\x65\xae\x66\x74\x10\x16\xc8\x49\x7e\xdb\xe8\x76\xa4\x21\x44\x43\x24\xcf\x0b\x72\x57\xd9\x86\x44\xcc\x81\x12\xba\xed\x78\x77\xb3\xd1\xee\xd8\x9d\xc4\xca\x52\x5d\xdf\x3a\xc2\x1a\x8f\xad\x56\x3d\xcc\xda\x31\x16\xe1\x3d\xdc\x1a\x02\x53\x0d\x31\xc7\x12\xbb\xd0\xa4\xf0\x85\x73\xff\x2a\x13\x46\x2f\xf7\xa1\x22\x01\x84\x65\x15\x8f\x5a\xc2\xb1\x92\x00\xb4\xc2\xbc\x4c\xa9\x41\xdf\x9b\xd9\x70\x58\x36\x66\xbe\x21\x1f\x2d\x36\xd6\x9f\xa6\x21\xb0\x0c\x79\xb0\x69\x5a\xfe\xea\x19\xfb\x9c\x11\x84\x29\x70\x3d\x8e\x70\x65\x17\x22\xca\x8a\x98\xff\xd0\xdc\xe5\xbd\xc0\x9c\xcf\x00\xaf\xda\x73\x86\x94\x4d\x97\xa2\x96\x9c\xaf\x3a\xa1\x05\x65\x42\x51\xc6\xc0\xb1\x1e\x1d\x1a\x09\xa6\xb0\x51\x21\x58\xc8\x83\x8d\x2f\x11\xd2\x09\xbe\x36\x50\xd2\x39\xd6\x14\x7f\xef\xcd\x77\x62\x87\x25\xb9\xc9\x04\x2e\x4e\x06\x89\x3e\x26\x80\x72\x90\xd3\x7c\xf1\xac\x66\x11\x33\x14\x45\x19\xc2\xc3\x21\x1e\xe4\xd1\x05\x1e\x5f\xa3\x00\x85\x38\xcb\xd3\x19\x3c\x7b\x20\xa7\xbf\x4c\xe2\x01\xae\x14\x65\xb4\x22\x85\x2a\x89\x1e\x00\xa5\x22\x20\x37\x94\x58\x5c\x73\x41\x06\xe1\x9e\x76\x06\xb4\xc1\xc9\x51\x24\x13\x72\xa8\x25\x1c\xa5\xcb\x08\xbd\xa4\xda\x7c\xaa\xe7\x45\x17\xa2\xfb\x1d\xcb\xf8\x9a\x07\xa2\x72\x30\x68\xde\x5a\x99\x27\xc0\x2f\xc0\x59\xa5\x11\xe2\x4c\x76\x47\x9a\x07\xeb\xe2\x21\xe5\x5d\x8b\x47\x4a\x7e\xd7\xf6\x1b\xab\xcd\x46\x35\x31\x3f\x63\x1a\x1f\x25\xfe\x7d\xc0\x26\xed\xb9\x08\x9c\x14\xc5\x39\x4e\x87\x92\xb5\x30\x72\xae\x0a\xce\x5f\x59\xd7\x39\xd5\xd2\xed\x96\xc5\x47\x0c\xd0\x08\x8f\xa7\x38\x25\xe2\x4f\x85\x45\xb0\xc3\x70\x63\xbe\xc1\x3a\xca\xdf\xe0\x1e\x8f\xca\x4c\xba\x53\x05\xed\xea\xca\x67\xda\xab\x5d\xe8\x52\xcd\x26\x6c\xb9\xf5\x73\x72\x55\xc5\x78\x10\x40\xbb\xee\xf7\x8c\x75\x61\x0f\x80\x8b\xd4\xf3\x22\x5b\x89\x70\x58\x54\xb3\x88\x15\x19\x2e\x55\x0a\x5f\xfc\xd8\x68\xa5\x27\xc2\x92\x77\xf7\x37\x7b\xf7\x4f\x4f\x44\x84\xe6\x41\x29\x48\x0b\x8c\xae\xfe\x16\x34\xb5\x3b\x09\x06\x95\xe8\x6a\x12\x0c\xee\x42\x5b\xa2\xfa\x9d\xe8\xeb\x0b\xb6\xab\x90\x24\xfa\xea\x7d\x06\xb4\xc8\x3c\x50\x22\xa3\x8d\xd0\xba\x8b\x11\x5b\xe9\xf1\x57\x68\x92\xe6\xf8\x30\x10\x6c\xc0\x89\x81\xfd\x28\xbc\x18\x78\xa6\x16\x08\xe9\xbb\x1f\xe4\x23\x1a\xd6\xf7\x09\x7f\xcf\x86\xf9\x75\x11\xe9\xf7\xe6\xcc\x6b\xb7\xbe\xd7\xf0\xbe\x0c\x99\x1a\x0f\x47\xbc\x74\xef\xf1\x7e\x39\xe4\x45\xe3\xfe\x0a\x0c\xe5\xf8\xbf\xae\xa0\xbf\xe2\x3b\x04\xff\xb5\x05\xd0\x35\xaf\x28\x78\xd4\xd8\x62\xca\x24\x02\x90\xa2\xc1\x4a\xef\x4b\xc2\xd3\x28\xb5\x25\x17\x18\x57\x18\xd9\x4e\xab\x9a\x89\x16\x2b\xcb\x8d\xb4\xc4\xe3\xed\xcc\xb4\x58\xf5\xbf\xce\x4e\xab\x2a\x02\xf7\xc5\x29\xfb\xd0\x9e\xdd\x54\x8b\xe2\xf2\x0f\xb0\x25\x36\xca\x4f\x82\xa9\x10\x0e\x27\xc1\x74\xf1\xd8\x0b\x16\x17\x71\x13\x84\xcb\x2a\x93\x8e\xf9\x6d\x0d\x96\xd1\xf2\x06\x6a\xba\x6d\x96\xaf\x73\xec\x5b\x8c\x96\xe9\x9f\xcb\x74\x99\xfe\x39\x0d\x98\x39\xe0\x46\x01\xb8\x16\xa1\x65\xe4\x2f\x59\x6c\xa2\xf9\x97\x2a\x96\xd1\x1c\x70\x53\x03\xdc\x70\x02\x6e\x58\x01\xdb\x21\xe7\x69\x34\x1d\xc3\xd5\x4b\x8d\x0e\xcb\x9b\x37\xe0\x37\xf1\x95\x3e\x37\xc8\xf3\x3a\x79\x04\x14\x6c\x50\xc4\x54\xfc\x4e\xa7\xa2\xf6\x3b\x7a\x43\x5a\xff\xf1\x47\x04\xd8\xfc\x8e\x5e\xa0\xfa\xca\x5a\x5b\x9a\xa1\xa5\xd7\xe8\xf7\x92\x70\x17\xd2\xdc\x53\x5b\xf0\x49\x30\x05\x9b\xd9\xcd\xbc\x56\xe3\x08\x43\xa7\x3b\xe8\x05\xaa\x35\xd1\x4b\xf4\xfb\x12\xeb\x69\x73\x68\xf5\x76\x32\xe2\x33\x98\x8a\x8b\x30\xe4\xe9\xbe\x4d\x6a\x64\x1f\x08\x4a\x68\x03\x49\xe8\x74\x0c\x67\x12\x88\xad\x57\x14\xb7\x1b\x07\x8f\xa2\x31\x46\x35\xb9\x9f\x2c\x5c\x80\x2b\xd6\x88\x75\x58\xe4\x66\x16\xef\x33\xe3\xac\x32\xd4\x3b\xd8\xc9\x2b\x3c\xf9\xf6\x76\x96\x82\xd5\x2e\xc4\xe8\xbf\x6b\x53\x4b\xb6\x43\x50\xbb\x1e\x79\x2b\xa9\x6e\x6e\x29\x6a\x2d\xb8\x39\x88\x7a\xc2\x50\x5e\xbc\x11\x86\xf2\xf3\xf9\xbe\x51\x22\xc5\x17\x38\xcd\xf0\xbe\x54\xb0\x78\x65\x8b\x6b\xf6\x43\xf1\xd9\x49\xdd\xa5\x40\x6d\x5b\x00\xff\xd3\xf9\x0f\x61\x3f\x64\x85\xb2\x0e\x96\x72\x1a\xb5\xe1\x53\xbe\xb0\x99\x6d\xfe\xef\x4b\x67\x68\x03\xfd\x5e\x2d\x56\xa7\x85\xa5\xec\x9d\xc7\x49\x8a\xbf\x19\x57\x91\x40\xee\xc5\x21\xf8\x39\x17\xd3\x1d\x91\x37\x07\xc3\x79\x3c\x43\x6a\x87\xc2\xf8\x61\x63\x03\xbd\xf4\xe7\xf0\x24\x99\xc2\xe4\xda\xb7\x62\xc4\x56\x91\x20\x15\x69\x2f\x33\xfc\x21\x49\xa6\xc5\x92\xf0\x74\x1c\x3c\x69\x46\x15\x91\x43\xbb\xf1\x0c\xa6\x5d\xf4\x7c\xf3\x6d\x6f\x6b\x7b\xe7\xdd\xee\xde\x7f\xbd\xff\xb0\xff\xf1\xe0\xf0\x7f\x1f\x1d\x9f\x7c\xfa\xf9\x97\x5f\xff\xfd\x7f\x82\xfe\x20\xc4\xc3\xf3\x51\xf4\xfb\x97\xf1\x24\x4e\xa6\xff\x9d\x66\xf9\xec\xe2\xf2\xea\xfa\x8f\xba\xdf\x68\xb6\xda\x9d\xb5\xf5\x57\xcb\xab\x1b\x2c\xc2\xad\x38\xda\x89\x45\xbb\x30\xaa\xc5\x10\x3b\xbc\x52\x0a\xcb\x0d\xc5\xc2\xd4\x26\x0a\x69\xed\xd8\xdc\x54\xc8\x4c\x47\x8e\xfd\x86\x39\x76\x65\x44\x48\x92\x96\x47\x41\x4d\xb2\x03\x0b\x7a\x89\xfc\xa5\x33\xf0\x5e\x29\x04\xa6\x86\x49\x5c\x1c\x68\xa3\x0a\xd0\xa5\x33\xbe\xc1\xcb\x62\x98\x05\x2a\x15\x88\x62\x25\x72\xcf\x57\x22\xcc\x00\xfa\x5f\x69\x8b\xb2\x6f\x4d\x5c\x1e\xbc\x07\xb1\x21\x5e\x5e\x56\x3e\x08\xb2\x15\x3f\x18\x45\x1a\xb1\x25\xad\x61\x11\x6e\x8a\xdc\x3d\xfa\x21\x5f\xda\x23\x5e\x3b\x33\xfb\xb4\x1f\x8f\xfe\x8f\x47\x7f\x71\xf4\xff\x74\xb2\xf3\xd2\xef\xa0\xb7\xdb\x95\x1d\xb4\xfc\xce\xdb\x6d\xd9\x47\xcb\xef\xa8\x4f\xf0\xf5\xf6\x4e\x5b\x14\x99\xbf\xd6\x71\xab\x22\x0e\xf7\xe8\xbc\xe5\x77\x9c\xde\x5b\x7e\xe7\x1f\xa0\x11\xa8\x7e\x58\x87\xc1\xb8\xcb\x59\xdd\xee\xef\x0f\x96\x51\x49\x88\x0f\x93\x28\xce\x5d\x4e\xc6\x7e\xc7\xe1\x64\x6c\x3d\x4c\x17\x98\xba\xbd\x8c\x45\x93\x55\x5d\x8d\x25\xa0\x77\x38\x41\xe9\x44\x7c\x27\x67\x35\xa0\xcd\x45\xd7\xc6\x77\x7d\x8c\xa2\xab\x4a\xb8\xac\xf1\xc5\xb7\x90\xcf\x1a\x54\x5a\xcc\xd7\x98\xd7\x12\xf2\x2d\x7f\xf1\xd0\x9e\xc6\x6a\xc3\xd5\x1c\x8d\x7d\x90\x7d\x04\x86\xaa\x9b\x31\x11\x81\x8a\xc5\xd2\x20\x8b\x45\x0b\xc2\xe6\xa6\x70\x97\x94\xa3\x8d\xce\x8b\xea\xa1\x30\x18\x59\x7e\xa8\xb0\x87\x49\xfb\xd4\x87\x3b\xef\x53\x1f\xbe\x83\x7d\xaa\x0a\x0e\xf7\xbd\x4f\x59\x97\xd3\x87\xed\xc7\x6d\x4a\xfc\xdd\xdb\x36\x95\x5d\x06\xd3\xed\x38\x8c\x82\xb8\xb6\xe8\x8e\x65\x3b\x92\x7f\xff\x5b\xd6\x87\x87\xd9\xb2\xaa\x2c\x93\xef\x7f\xcb\xfa\xb0\xad\x6d\x5a\x8f\x3b\x96\xb1\x63\x49\x2b\x66\xa1\xcd\xeb\x9b\xee\x5e\x62\x5e\x24\x6c\x09\x20\xa5\x8f\x3c\x1a\x3e\x7c\x61\x77\x27\x74\x71\xd7\xeb\xe4\xff\xe1\x62\x85\x7e\x24\xdd\x67\x5f\xe9\xb7\x62\xf9\xcf\x53\x17\x00\x61\xb9\xb5\x05\x9d\x3b\x69\x0b\x58\x8e\xda\x6f\xa9\x34\xf0\x90\xf4\x2a\x1b\x05\xbe\xf6\x6a\x34\x09\x06\x0f\xa8\x5a\xf0\x10\x6f\x16\x7e\x41\x6b\xff\x04\x75\x83\x91\x2f\xf6\x16\xaa\x08\xc5\x88\x45\xfa\xb2\xbf\xd5\x86\x9a\x60\x72\xb3\xbf\xd5\xb6\xc9\x78\x60\xe2\xfc\x05\x5f\xd3\x2c\xd8\xd4\x0e\x56\xf4\x15\x9c\x7f\x83\x38\xe7\x49\xbc\x93\x74\x42\x6d\xb4\xb7\x7f\x3e\xfc\x0c\x9b\xee\x49\xf2\x1e\x17\xc2\x20\xba\xbc\xbc\x5c\x49\xa6\x38\xce\xb2\xf1\x4a\x92\x9e\xaf\x86\xc9\x20\x5b\x85\x24\xdc\xc9\xaa\x56\x67\x94\x4f\xc6\x16\x45\xc8\xf6\xc5\xf4\xfd\xd6\x4e\x81\xb6\x78\xae\x18\x0c\x61\xbe\x0f\x88\xb6\xc7\x19\xde\x2f\x2c\xe5\x39\xec\x51\x64\x60\x32\xf2\x10\xc5\xdc\xed\x45\x0a\xf7\x5c\xb8\xba\xb4\x50\xcd\x6f\xac\x2b\x9e\x2e\x06\x7c\x87\x91\x9a\x1c\x16\x43\x4f\x90\xb2\xbf\xd5\x9e\x87\x6d\x94\x33\x5b\x64\x3d\x48\xb5\xf4\x21\x4f\xd0\x94\x5a\x9d\xca\xde\x39\x8e\x1d\xce\xf0\x8b\xd1\x76\x07\x36\x3c\x5d\xe4\x37\xd6\xc1\x84\x54\xf9\x4a\x3b\x07\x98\x6b\x5f\x0a\x7c\x94\xb6\x6f\x6e\xed\x76\xe3\x20\xda\x87\xf6\xc3\xc1\x52\xa3\xf7\x60\x66\xfd\x25\x1c\x1a\xde\x37\x94\xe6\xe7\xa4\x68\x9a\x5f\xf1\xcf\x62\xae\xd6\xb5\x7c\x7e\xb7\x05\xe3\xa9\xd3\x58\xaf\xd7\x75\xc0\x0b\x7a\x07\xcd\xf5\xfb\xa9\x26\xef\x6e\x41\x0a\x7f\x42\x23\x84\x2a\x20\x11\x76\x00\x19\x58\xc9\xa2\xbd\x8d\x95\x3e\xaf\x4b\x63\x01\xd8\x00\x95\x54\xce\x82\x71\x8e\x36\xe1\x9f\xc5\xc5\x62\xa0\x2e\x4a\xde\xf7\x41\x5e\x98\x6c\x1e\x5f\xc2\xe1\x0a\x75\x8b\xc0\x35\xde\x19\x0f\xf0\x2b\xc9\x5b\x03\xc5\x95\xfc\x8e\x6a\xcd\x85\x04\x5e\x75\x8a\x2d\xe2\x2d\x59\xe9\x8c\x7b\x98\xb5\x85\x97\x1a\x21\x0f\x66\xa2\x5c\xac\x0e\x2b\x2c\x97\x5b\x18\x84\x16\xa0\x43\xfc\x1e\xc6\xc6\x96\x12\x6d\x91\x33\x72\x01\x4c\xf8\x04\x8b\x37\xce\xe3\x32\xdf\x63\x68\x8f\xd8\x93\xa5\x9c\xc4\xc4\x69\xd1\xe2\x85\x05\xcb\x77\x6c\x63\x22\xe0\xd5\x8f\xcc\x98\x45\xc3\x95\x1b\xb4\xbc\xe1\xf8\x58\x8f\x02\x44\x8c\x03\xcf\x01\xe7\x05\xb3\xea\xb2\x44\xcb\xce\xbf\x56\x46\x72\x30\x86\xc2\x09\x84\x41\xe1\xc4\x26\x19\x05\x1b\xf4\xaa\x36\x2f\xfc\xe9\xcc\x12\x84\x26\xc4\xc0\x99\x9f\x95\x83\x92\x4f\x0f\x4a\xd2\x40\x97\xa6\xfd\xd1\xb0\x17\xc8\x3a\x47\xc1\x86\xb1\x65\xa8\xcc\x77\x12\x59\xb1\x98\x31\xd6\x36\xb4\x51\x96\x6a\x49\x3a\x1a\x4e\x7f\x96\x68\x17\x22\xc0\x1c\xaf\x57\xd5\xe6\xba\x12\x0f\x96\xfd\x8e\x6f\xc5\x7b\x17\xe4\xbb\xf7\xe8\x7d\x6b\xf1\x2b\x93\x7a\x53\x9d\x9b\x4b\x95\x14\xed\x86\xf4\x5e\xe5\xee\xc5\x07\xa4\x70\x75\xb1\x69\xd3\xfd\xda\xc5\xd9\x17\xab\xe6\x21\x87\xd8\x70\x17\x30\xa5\x62\x83\x50\x21\x17\xb2\xbe\x6b\xcf\x31\x5d\x58\xd8\xb0\xab\x12\x0b\x38\xae\x94\xef\x77\x37\xaf\x4b\x8e\xef\x14\x9a\xfd\xec\xee\xf1\xc3\x67\xb7\xbd\xee\xf1\x23\x69\x77\x6d\x8d\x9c\xe9\xd7\xfe\xd6\x67\xfa\x41\x34\x1d\xe1\xf4\xe5\x03\x9b\x08\xc0\xe9\x5d\x6e\xea\xaf\x39\xc4\x9b\x99\x3b\xef\xe5\x34\xdf\x83\x8e\x1d\x12\x8e\x93\x89\x43\xbb\xfc\xd2\x6d\x42\x20\xde\x6b\x99\x30\x94\x1a\xe4\x0c\x17\xe4\x50\x89\xfe\xe4\x8c\x98\x55\xdc\x81\x97\x39\x8b\xaa\x40\x8b\x2c\x90\x4e\x83\x9c\x6e\xe8\xdc\xe4\xf8\x2a\x27\xa7\xc8\x80\x3d\xa3\x29\xed\x13\xf3\xcd\xe2\xa9\x36\x82\x10\x0f\xa2\x49\x30\x1e\x5f\xb3\x34\xa0\x61\xe5\x9b\x1b\x79\x54\x6e\x58\x2b\x6c\xe0\x4e\x04\x1a\x6a\xb3\x8b\x27\xe3\xb8\x0d\x7e\x0f\x9a\x9e\xa3\x98\x12\xe9\x56\x47\xee\xfc\x62\x17\x3b\x4a\x4d\x87\xa3\x96\x5c\x66\xa5\x98\xdd\x22\x81\xc4\x2e\xbe\xba\x65\x26\x08\xcb\xf0\x4a\xe4\x23\xdf\x37\x2c\x38\x9d\xda\xcd\x43\x14\x4f\x67\xf9\x5d\xe6\x94\x93\x87\x4a\x74\xb7\xa0\xb3\xfb\x22\x8e\x81\xc6\x28\x2c\xf4\x71\xeb\xa4\x12\x30\x5a\xf6\x10\x36\xc5\xe4\x6c\xa0\xa2\x0d\x5a\xe1\xb5\x95\x7a\x7a\x0a\xf5\x70\x8d\x40\x01\xa8\x2b\x03\xbd\xb1\xeb\xe6\xdd\x3b\x6d\xd1\x5d\x6d\xb7\x95\x36\x88\x6e\xbb\xe1\x69\xca\xf3\xf5\x47\x53\xbb\x7f\xba\xee\xdb\xb5\x3b\x1a\x91\xcc\xcb\x34\xe1\xe6\x21\x05\x1c\x80\x85\xc6\xd5\x9a\x88\x8a\x94\xd8\x90\x1d\x55\xef\x27\x21\x3d\xb8\xbc\xce\xe5\x78\x95\x95\xc4\x15\x55\x51\x44\x56\x07\xe7\x65\x3c\x48\x71\x7e\x4f\x4a\x25\x22\xff\xee\xda\x03\x07\x41\x2f\x19\x9b\xb0\x79\x22\x53\x47\xdf\xaa\x1a\x43\xd9\x39\xd8\x11\x20\xd8\xaa\x33\x12\xfa\x22\xea\xa3\x20\x1e\x75\x0f\xf7\x12\x6f\xb7\xfb\x8c\x2f\x0b\x07\xa6\x39\xe1\x65\xe9\xa1\x4a\x8a\x2e\xab\x8f\x93\xdd\x10\xbf\x44\x31\x45\x3b\xfa\x56\x8a\x8b\xc9\xba\x5e\x16\x19\x53\xab\xc4\xf5\x05\x3a\x2c\x7b\x94\xcc\xcd\xf1\x38\xb9\x44\x41\xda\x8f\xf2\x34\x48\xaf\x11\x53\x2f\x7d\xc1\xd7\x96\xb8\x83\x5f\x64\x8d\xc4\x4f\xd6\x86\x4b\x06\x4a\x57\xb7\x54\x1b\xad\x39\xce\x90\x04\xa5\x12\x37\x48\x88\xff\x06\xba\x8d\x24\x45\x51\x1c\xe3\x14\xa2\xcf\x26\xb3\x1c\x04\x08\x3d\x0a\x1f\xc4\x4c\xa4\x3a\x46\x4a\x86\xec\x81\xb6\x62\x04\xa4\xe3\x1a\x3f\xb9\x46\x64\xa9\xb1\x08\x09\x24\x92\x56\x32\x29\xd3\x47\x46\x52\xc1\x48\x2a\x68\x34\xf6\xeb\xc1\x11\xcc\x27\xbd\x06\x9c\x06\x21\x1a\x24\x71\x96\x07\xb1\xde\xbc\x35\x89\x94\x3a\xc7\x6e\xc5\x9a\xc0\xfb\x34\x3a\x43\xbf\x6d\xa0\xfa\x55\x7b\x40\xff\x67\x73\x87\x31\x0a\x37\x3b\xf4\x7f\xe5\x9a\xb1\x44\xd3\x89\x45\xda\xb3\x8d\x22\xff\x82\x38\x64\xb0\x03\x3d\x44\x14\x32\xc1\xc4\xef\x25\x12\x59\x49\xbe\x32\x1b\x33\xb6\x0c\x24\x74\xda\xc6\xc7\x1d\x7a\x52\x55\x5f\x5c\x2c\x98\xdb\x45\x20\x83\x61\xfe\x6e\xe2\x8f\xed\x6f\xf6\x58\xf4\x31\xc0\x2b\x82\x25\x56\x1a\x09\x65\xc1\x29\xaf\x12\x88\xcc\x28\x7d\xff\xc1\xc8\x64\x92\xe0\xad\xcc\x0d\x3e\xf6\x50\xd1\xc3\x60\xa8\xff\xa7\x47\x0f\x9b\x23\xa6\x2e\x22\x22\x12\x1e\x5a\xd0\xd0\xdc\x08\x62\xee\x1a\x73\xa3\x88\xb9\xab\x3e\x50\x24\xb1\xbb\x73\xbb\x1e\x55\x4f\xc3\x78\x5b\xf6\x63\x22\x5d\xec\xda\x83\xa3\x95\x06\x1c\x2b\xe5\x98\xf2\x58\x69\x40\x0b\x09\x85\x4b\x1a\xfc\x92\x49\xa0\xb2\xe4\x0c\x39\x36\x09\x06\xf6\x4b\x22\x71\xf0\x77\x18\xc1\xbd\xfa\x5b\x2b\xcc\xaf\x3a\xad\x97\x96\xd7\xe3\xa8\xff\x92\xa0\x12\x82\x6d\x6b\xa6\x7d\xc5\xf1\xe0\x25\xd8\x34\x5a\xde\x53\x37\x4b\xed\xc3\x24\x6c\xcf\x37\xbe\xcb\x46\x41\xa3\xad\x83\x24\x2f\x1b\x3a\xb8\x6c\x14\xb4\xfd\x86\xf9\xb2\xb9\x6e\x29\xd9\xd4\x5e\xa5\xd1\x14\x4f\x42\xbf\x53\xb7\xda\xfe\x29\xaf\xa6\xfd\x2f\xe1\x50\x6f\x07\x5f\x4c\xbf\x84\xc3\xb2\x7b\x07\xb5\xeb\x49\x88\x5f\x0e\x86\x7d\xeb\xeb\x3c\x75\xbc\x7e\x79\x3e\x0e\xc2\x49\x10\xdb\x3e\x27\x76\x60\x78\xa0\xbf\x9e\x06\xe1\xcb\x20\xce\xa2\xab\x57\x0d\x7d\x10\xc8\xa7\x28\x4b\xfc\xba\xdf\xd0\x47\x9c\x7d\x7a\xb5\xf6\x6a\x4d\x9f\x21\xf2\xe9\x0f\x9c\x26\xcc\xf5\xda\xf2\x35\x76\x7c\xa3\x3a\xb2\x97\x23\x7c\xa5\x7d\x08\xb0\x4e\x5c\x34\xee\x46\x68\xbc\x4f\x07\xfa\xe4\xa6\x41\xbf\x1f\xe5\xd6\x97\x2f\xc7\xf8\x3c\x18\x5c\x3f\xf4\x1d\x90\x58\x3d\xf0\xa4\x2f\x1a\x78\x59\xac\x15\xf1\xc8\x96\x08\x3c\x93\x95\xa1\x99\x85\xb2\x75\x20\x7e\x37\x5a\xe2\x37\xa1\x7a\xfe\x9b\x10\xbb\xf8\x4d\x7f\x15\xa4\x5d\xd8\x97\xc2\x2f\x46\xc8\x14\x03\x4a\xbf\xc6\x1d\x16\x45\x87\x53\xab\xf4\x94\xa7\xea\x93\xa0\xcd\xe2\x6d\xa2\xd4\x20\x94\x48\x9b\x95\x09\x50\xbc\x11\x74\x27\xbf\xa1\xe4\x26\xde\xc8\x54\x26\x5e\xc6\xea\x2b\x89\xa6\xe0\x99\x90\x12\xfc\x28\x28\x88\x8e\xca\x80\x0d\x14\xa3\x17\xe9\x37\x27\x93\x45\x15\x91\x8a\x02\x52\xe6\xb5\x8b\x2b\x26\xdd\xa1\xd8\x58\x97\xba\x6d\xdf\x2b\xd7\x26\x7b\x2a\x5d\x75\xdb\x2d\x4f\x21\xbc\x6e\xbb\xed\x15\x13\xdf\x6d\x77\x3c\x75\xf4\xba\xed\x35\xfd\x46\x58\x27\xe5\x6e\xa7\xee\x31\x6a\xed\x76\x00\x1f\x41\x29\xdd\x4e\xc3\x93\x69\xa5\xdb\x69\x79\x36\x6a\xe9\x76\x9a\x9e\x4c\x21\xdd\x4e\xdb\x93\xe9\xa7\xdb\x01\xbc\x14\x9a\xe9\x76\xd6\x3c\x9d\x6a\xba\x9d\x75\x4f\xa7\x9b\x6e\xe7\x95\x67\x10\x49\x77\xad\xee\x59\xc8\xa9\xbb\x06\xf8\xb3\x25\xd1\x5d\x03\xec\x19\x69\x74\xd7\x5a\x9e\x41\x1c\xdd\x35\x40\x9c\x90\x51\x77\x0d\x70\x2e\xd6\x59\x77\xad\x23\x5f\xa0\x7b\xc5\x92\xed\xae\xf1\xab\x75\xb2\x98\xbb\x6b\xaf\x3c\xbe\x54\xbb\xeb\x75\xaf\x58\xc2\xdd\x75\xdf\x2b\x16\x77\x77\x1d\xd0\x29\x28\xb8\xbb\x0e\x8d\x0b\x46\xd3\x5d\x6f\xdd\x9c\x79\x9d\xfa\xe3\xe5\xc1\x5f\x7f\x79\xd0\x1b\xe1\xc1\x17\xd2\x29\x58\x29\xd4\x0d\x88\xa6\x39\xcb\x66\x53\x32\x30\x98\xc5\xa7\x96\xfa\x0d\x72\x3c\x0d\x69\x8e\x7e\xd8\x40\xcf\x39\xe4\xe7\x16\x8b\x10\xe1\xa4\x71\x8f\xd7\x15\xa5\xe6\xf8\xa2\x9d\x23\x3c\xc4\x29\x86\x83\x5e\x1a\x9d\xc3\x99\x2c\x8a\xa3\xbc\x00\x93\xcd\xa6\x38\x05\xd5\xf5\x86\x96\x9e\x43\x82\xb2\x39\x3b\x9f\xe0\x38\xd7\x0a\xa0\x3c\x41\xa3\x20\x0e\xc7\x58\x19\x37\x19\x76\xdf\x0a\x59\xb1\xa9\x81\xaa\xa6\x3b\xa0\xa4\xfb\xa6\xb1\xe4\xa9\x09\x54\x14\xe7\xeb\x92\x86\x7e\x28\xd7\x17\x8a\x09\x75\x76\xcc\x63\x7e\x51\x83\x2a\xe1\x3f\x11\xa8\xf0\x42\xc6\x46\x39\x44\x58\x11\x4b\x68\xfa\x2f\x80\x74\x11\xe1\x4b\x17\x8a\xce\xe6\x25\x84\xf7\x38\x0a\xe8\xeb\x57\xb5\x3c\x27\x38\xc0\x12\x74\xc6\xbc\xfa\x0f\x64\xcd\x09\xdb\x11\x58\x74\x76\xe0\x46\xd5\x25\xa3\x15\x27\x56\x7e\xc7\x8e\x96\xbb\xa5\xc5\x6a\xec\xc5\x79\xb3\xb1\x68\x13\x8b\xd5\xd8\x19\x27\xc1\x6d\xaa\x74\x5a\xf0\xbe\x28\x7f\x4b\x52\x5a\xa1\x14\xec\x21\xf9\xd5\x75\x8e\x0f\x20\x39\x90\xf1\xda\x96\x77\x59\xa1\xbf\x5d\xba\xe8\x8a\xb6\xaa\xac\x88\xa2\xf4\x62\x2a\x84\x02\xda\x5b\x81\x1b\xda\xb0\xe3\x6c\xd1\x2c\x6c\x5f\xb1\xec\xab\xd7\xb9\xcd\xf8\x79\x21\x77\x41\x1b\x2a\x8b\xe4\xd3\x2e\xea\x9f\x46\x67\xb7\x4a\x9e\x5d\x98\x73\x47\x7f\x60\xaa\xaa\x2d\x1c\x47\xd5\xa2\x82\xb1\x16\xa9\x2d\x3c\xc4\xdc\x08\x6d\x1d\x51\xe6\xdb\x9a\xf5\x8c\x8c\x26\x79\x4d\xe0\xa1\x98\x48\x7d\x32\x33\x37\xdb\x0d\xa6\xd3\xf1\x35\x6b\x38\x48\xcf\x67\x84\x85\x67\x65\xfe\x8a\x8c\x5f\xaf\x4c\xd3\x24\x4f\x08\x8e\x32\xe7\x2e\x33\x9c\xd0\x77\x1f\xbb\x82\xa5\xe3\x3f\xca\x3a\x7f\x8d\xac\x03\x01\xa3\xff\x82\xb8\x44\xd6\x9c\x4a\x15\x4c\x24\x60\x8b\xa5\xf7\x78\x28\x2f\x74\xeb\xa4\xca\x09\x63\x16\x52\x49\xaa\xba\xd4\x6e\xfe\x6c\x92\x9e\x8b\xaf\x74\x5a\x76\x2e\x72\x42\xd8\xc4\x06\x1d\xbe\x95\xa0\x9f\xd1\x1f\x59\x14\xb3\x60\xac\x84\x65\xd4\xaf\xfc\x3a\xfb\x5b\x42\x5f\xd5\x34\xbe\x6c\x79\xd5\x96\xac\x16\xea\xfb\x5b\x6d\xcd\x9a\xc2\x66\x00\xa2\x7b\x4d\xa2\x0d\x36\xaa\x16\x03\x10\x9e\xf6\xa6\xf4\x76\xac\xd0\x04\xdb\x73\x15\x9f\x9a\x9c\xb4\x7e\xd5\x59\x6b\xb5\x1b\xcd\xba\xef\xa1\xfa\x15\x1e\x0e\xc2\xa0\xbf\xfe\xca\x92\x57\xb1\x7e\xf5\x6a\xbd\x1f\x84\x83\x21\xf6\x60\x60\x9a\x8d\x76\x6b\xad\xa3\x96\x3b\x73\xde\x88\x69\x69\xf4\xe4\x5e\xec\x8b\x4c\x7a\xb6\xbd\xeb\x32\x98\x22\x0c\xee\xd5\xf3\xf7\x10\xbf\xe3\xde\x31\xdc\xd7\xd7\x7c\x36\x28\x12\x9f\x09\x3c\x9e\x5e\x10\x45\x8e\x08\xbc\xfb\x9f\xa5\xd2\xfb\xa7\xfc\xe1\xcc\xe6\x12\x22\x7d\x26\x04\x67\x16\x20\x7f\xb5\x5a\x4d\x82\x49\x3d\xc5\xd1\x57\x24\xbf\x84\xbd\xae\xb5\xa4\xf9\x88\xa3\xaf\x15\x01\x36\x5a\x4b\x16\x80\x10\xca\x58\x71\x49\x37\xc1\xdd\xcd\x38\x64\x57\xb9\xa1\xb0\x5f\xf7\x2b\x43\x5a\x47\xd2\x98\xa2\x65\x54\xd7\xc5\x07\xa5\xb4\xaf\x95\xf6\x4b\x4b\x37\xb4\xd2\x8d\xd2\xd2\x4d\xad\x74\xb3\xb4\x74\x4b\x2b\xdd\x2a\x2d\xdd\xd6\x4a\xb7\x4b\x4b\x77\xb4\xd2\x9d\xd2\xd2\x6b\x5a\xe9\xb5\xd2\xd2\xeb\x5a\xe9\xf5\xd2\xd2\xaf\xb4\xd2\xaf\xca\x67\xa7\xae\xcd\xce\x9c\xc9\xf4\xb5\xe2\xe5\xb3\xe9\x37\xb4\xe2\xe5\xd3\xe9\x37\xb5\xe2\xe5\xf3\xe9\xb7\xb4\xe2\xe5\x13\xea\xb7\xb5\xe2\x6d\x83\x1b\xac\xae\x12\x86\xfc\x25\x8a\xcf\x49\xd5\x28\x18\xf7\x6d\x62\x73\x40\xb6\x81\x53\xeb\x40\xf5\xe1\x93\x75\x50\x06\xf0\xc9\x3a\x00\x21\x7c\x6a\xda\xd0\xe9\x15\x77\xd0\xea\x37\x82\xc4\xce\x4e\x2d\xf0\x50\xdf\x43\x03\x0f\x85\x9e\xb4\x40\x3d\x84\xd6\x3c\xb2\x85\xd6\xcf\x74\xde\x10\xd2\x7a\xa1\x87\x44\xd5\x62\x84\x3c\x84\xfc\x86\x87\x4e\x4e\x7d\xa3\xde\x80\xd6\xa3\x2d\xd1\xaa\xc5\xa2\x25\xf5\xd6\x48\xbd\x86\x51\xaf\x4f\xeb\x09\x24\x03\xa9\x5e\xd3\x43\xa8\x01\xed\x35\x8d\x7a\x65\xfd\x6b\x89\xfe\xb5\x16\xea\x5f\x5b\xf4\xaf\xbd\x50\xff\x3a\xa2\x7f\x9d\x85\xfa\xb7\x26\xfa\xb7\xb6\x50\xff\xd6\x45\xff\xd6\x17\xea\xdf\x2b\xd1\xbf\x57\x0b\xf5\xcf\xaf\x7b\xac\x7f\xbe\x49\x30\x65\x1d\xf4\x7d\x8f\x75\xd0\x37\x29\xa6\xac\x87\x04\x4b\xda\x43\xdf\x24\x99\x52\x12\x6d\x7a\x9c\x44\x4d\x9a\x29\xed\x63\x4b\xf4\xd1\x24\x9a\xd2\x3e\xb6\x45\x1f\x81\x6a\xcc\x4e\xbe\x7b\xe7\xe8\xa4\x87\x50\x9b\x76\xd2\xa4\x9b\x90\x56\xb4\x76\x92\xd0\xdb\x2b\x5a\xd1\x24\x9c\x01\xad\x68\xef\xa4\xef\x21\xd2\xd1\x93\x53\xdf\xa4\x9c\x3e\xad\x68\xed\x24\xe1\x18\x8d\x3a\x54\x34\x49\xa7\xac\x8f\x6d\xd1\xc7\x86\x9d\xd7\xb8\xfa\x48\x68\x8e\xf6\xb1\x61\x67\x36\xce\x3e\xb6\x79\x1f\x1b\x76\x6e\xe3\xea\x63\x4b\xf4\xb1\x61\x67\x37\xae\x3e\xbe\x2a\xfa\x68\xe7\x37\xce\x3e\xb6\x44\x1f\xed\x0c\xc7\xd5\x47\xc2\x18\x59\x1f\xed\x1c\xc7\xd5\xc7\xf5\xa2\x8f\x76\x96\xe3\xa4\xd5\xa6\xc7\xfb\x68\xe7\x39\xae\x3e\x36\x04\xad\x36\xec\x4c\xc7\xd5\xc7\x35\xd1\xc7\xa6\x9d\xe9\xb8\xfa\x48\x96\x3f\xed\x63\xd3\xb7\x2f\xc8\xdd\x5d\x37\xb1\xb6\x00\xd7\xa6\x9d\xeb\xec\xee\xda\x3b\x49\x86\x95\xac\xad\x93\xd3\xa6\x9d\xeb\xec\xee\x96\x2c\xc8\x0e\x54\xb4\x73\x9d\xdd\x5d\x47\x27\x5b\x1e\x6a\x34\xa1\xa2\x49\x3a\x65\x7d\xf4\x8b\x3e\xda\x99\x8e\xab\x8f\xad\xa2\x8f\x76\xa6\xe3\xea\x23\x4c\x24\xed\xa3\x9d\xe9\x38\xfb\x58\x17\x7d\xb4\x33\x1d\x67\x1f\x9b\x1e\xeb\x63\xcb\xce\x74\x5c\x7d\xac\x8b\x3e\xb6\xec\x4c\xc7\xd5\xc7\xa6\xe8\x63\xcb\xce\x74\x5c\x7d\x24\xac\x9c\xf6\xb1\x65\x67\x3a\xae\x3e\xbe\x12\xf3\xd8\xb2\x33\x1d\x57\x1f\xc9\xf2\x60\x7d\xb4\x33\x1d\x27\xad\xb6\x39\xad\xb6\xec\x4c\xc7\xd5\xc7\x46\xd1\xc7\x35\xfb\x82\xdc\xdb\x73\x0b\xaa\x1d\xda\x49\x3b\xd7\xd9\xdb\xb3\x77\x12\x68\x0e\x78\x40\xcb\xce\x75\xf6\xf6\x4a\xc4\x80\x36\x88\x80\x76\xae\xb3\xb7\x67\xef\x24\xe1\x1d\x0d\x18\xd6\xb6\x5d\xd4\x71\xf5\x91\xcc\x07\xed\x63\xdb\xce\x74\x5c\x7d\x6c\x8a\x3e\xb6\xed\x4c\xc7\xd9\xc7\xba\xe8\xa3\x9d\xe9\xb8\xfa\xe8\x17\x7d\xb4\x33\x1d\x57\x1f\xd7\xc5\x3c\xb6\xed\x4c\xc7\xd5\x47\xa0\x39\xda\x47\x3b\xd3\x71\xf5\x11\x44\x72\xda\x47\x3b\xd3\x71\xf6\xb1\xe9\xf1\x3e\xda\x99\x8e\xab\x8f\x2d\xd1\xc7\x8e\x9d\xe9\x38\xfb\xe8\xf3\x3e\x76\xec\x4c\xc7\xd5\xc7\x86\xe8\x63\xc7\xce\x74\x5c\x7d\x7c\x25\xe6\xb1\xd3\x34\x17\x24\x5c\xa3\xe4\x38\x9d\xe0\x30\x0a\x72\xe6\x54\x06\xee\x0a\x6a\x39\x72\xc4\x45\x1b\xa8\x06\xff\x2e\xa3\x40\xd7\xb0\xd2\x32\x3e\x2b\xe3\x93\x32\x7d\x7b\x99\x06\x2b\xd3\x20\x65\x06\xf6\x32\x4d\x56\xa6\x49\xca\x84\x86\x36\x57\x53\x55\xee\x58\x2c\x75\x17\x0c\x68\x0b\x99\xd2\x45\x36\xdd\x20\x0f\x6c\x07\xf3\x20\x0f\x44\x28\x9f\x20\x0f\xdc\xca\xb1\xf8\x6d\x94\x67\x27\x49\x1e\x8c\x05\xcc\x78\x2b\xc8\x03\xea\x41\xf2\x02\xad\x5b\xa0\x43\x9d\x0f\x78\x98\x73\xe8\xc2\xe3\x04\xca\x1b\x9d\x71\xa6\xbc\x12\x68\x9e\x16\x20\x7f\xfa\xe9\x27\xd4\x86\x8b\xb7\xfa\xd5\x7a\xbd\xb8\x6f\x2b\x4a\xfc\x0b\x35\x1b\x06\x71\xa8\x7d\xd9\x45\x1b\x08\xd4\xee\xc3\x71\x92\xa4\x35\xa9\x93\xab\x8a\xee\xdd\xd5\x39\x28\xfb\x01\x6d\x48\x4f\xfa\xc2\x11\xa8\xd7\x6a\xb5\x02\xb7\x65\xd4\x69\xd1\x7c\x69\xaf\x20\x98\x68\x6b\x89\x2a\x6c\xec\xfa\x59\x5e\x95\xe1\x5c\x28\x67\xe5\xb7\xd5\xb5\xb3\x26\x38\xa6\x9a\xd5\xc1\xcd\xd3\xcd\x1a\x5c\x62\x91\xce\xb6\xaa\x74\xf6\x83\xb5\xb3\x1f\x6e\xdb\xd9\x0f\xd6\xce\x7e\xa8\xda\x59\xb3\xb7\xb2\x13\x55\x4d\x74\x9f\x07\x9b\x82\x9c\x7a\x76\xff\x41\x30\x78\xa7\x6e\x0c\xe0\xa3\x68\xf3\xa4\x2a\xcd\x2b\x3f\xc7\x1b\x52\xd1\x79\x5b\xc8\x77\x97\x19\xc6\x3b\xbd\xdf\x16\xba\xf7\x70\x5c\x71\xa1\xb2\xeb\x7f\x81\x09\x5c\x61\xec\x9e\xda\xef\x2e\x76\xd9\x2d\x59\xad\xb6\xab\x5c\x4b\xec\x2e\x7c\x1f\x41\x69\x61\x57\xb9\x8b\xd8\x75\x5e\x42\xcc\xbf\x71\x38\x62\xb9\x81\x61\x0e\x59\x04\x9e\x10\xc6\x54\x2d\x5a\x21\x59\x39\xb8\x21\x94\xb2\x7a\x50\xb0\x82\x53\xa6\xb8\xa1\x83\xc7\xe2\xfa\xdf\xd8\x78\xe1\xf3\x67\x83\x16\x5c\xde\x95\x3c\x82\x06\xf9\x6a\xf7\x70\xa0\xbf\x04\x92\x9a\xea\xeb\xca\x43\x99\x87\xd4\x2b\x34\xe0\x93\x68\x03\x05\x68\x19\xd5\x6a\x7d\xf4\x23\xdd\x1c\x6b\xff\x97\xfc\x0c\x97\x08\x1b\xb8\x42\xcb\x28\x97\xda\x13\x01\x8b\x63\x32\x4d\x19\x5d\xa9\x34\x4e\x79\xb3\x81\x5e\xa2\x6c\x09\xaa\xf5\x35\xa3\x37\x81\x95\x76\xfe\xaf\x86\x15\x6c\xc7\xb5\x01\xfa\x11\xfd\xdf\x87\xc1\x4a\x3b\x04\xcd\xc5\xaa\x8f\x7e\x43\x03\xf4\x1b\x41\xec\xfe\x91\xd1\x04\xc0\xb9\xc8\x10\x44\x6a\x7d\xf4\xf5\x9e\x07\x47\xbe\xad\x3e\x76\xa5\x49\x9f\x9b\x78\xbf\x4a\x90\x35\xee\x27\xa6\xb9\x28\xc2\x6a\x30\xc1\x38\x9c\xc5\x1c\xa5\x6f\x1b\xd6\x8c\xad\x4b\x61\xe4\xb2\xbf\xd5\xb6\xf8\x7e\x95\x97\x37\x1d\xbe\x8a\xf8\x62\xca\x65\xbe\x9a\x91\x7f\x7f\xab\x6d\x35\x19\x70\x4e\xc2\x9c\x5c\xf5\xf7\x35\x05\xb7\x0a\xed\x30\x7f\xe2\x64\x2f\xbf\xfb\x98\x38\xea\x54\x26\x26\x62\x77\x12\x0c\xc8\x64\x28\x99\xe1\xcd\xf9\x60\xc5\xcc\x39\x29\xb2\xd9\xd3\x79\x29\xcd\xc0\xce\x22\x5b\x3b\x2c\xa0\x1a\x7f\x6b\x17\xb3\x7f\x7e\x4c\x36\xba\xd8\x5e\xb0\x38\x43\x68\x07\xe3\xb0\x1f\x0c\xbe\xb0\xb8\x9a\x93\x24\x84\x25\x45\x68\x46\xcc\x37\xbc\xec\xed\xbc\x25\x22\x90\x45\x3c\x00\x33\x27\xf8\xaa\x58\xcb\x81\x85\x0b\x6d\x65\x9f\x00\x60\xc6\x3c\x62\xd5\xf7\x76\xde\xae\x6c\xc7\x34\x56\x39\x18\x50\xed\xbc\xb5\x18\xfc\x4c\x1d\xe6\x32\xcc\xcc\xb0\xc4\x64\xc6\x2d\x9a\xb2\x10\x54\x5c\x20\xa1\x8f\xb6\x7b\x66\x29\x94\x07\x2d\x24\x87\xf2\x50\xcb\xf3\x18\xe5\xef\xf1\x75\x96\xa7\x38\x98\x6c\xc6\x21\xeb\x9d\xc5\x3a\x32\x61\x66\xb1\x02\x9c\xc7\x1a\xb0\x09\xd9\x47\x78\x82\x21\xc8\x38\x18\x63\xd2\x79\x62\xb1\x32\xc1\x7f\x3e\xc6\x57\x39\x7d\x6d\x17\xdf\xf1\xc5\x5b\x16\x33\x15\x5a\x5f\xc9\xc6\xd1\x00\xd7\x38\x0a\xe2\xa6\x5e\xe0\x62\xb3\x9f\x54\x66\x6d\x0b\xff\x53\x66\xed\x0e\xa3\x0b\x86\xc3\xa3\x28\x5b\x78\x6c\xbf\x19\xdd\x9c\x14\x1d\xea\xe3\x41\x32\x61\x5e\xf7\x84\x20\xa2\x64\x96\x55\x23\x19\xd1\xc5\x4a\xe2\x78\x49\x6f\x6a\x73\xbb\xa0\xf9\x46\x98\x07\x36\x38\xef\x5d\x14\xc1\x5a\x2e\x5e\xab\x46\xe3\x72\x38\x66\xda\x7c\xf1\x19\x32\xbb\x5e\x58\x8f\x34\xa2\x34\xda\x40\xd1\x05\x9b\xc2\xba\x63\x25\x26\x17\x18\xed\xfd\x0c\xe7\xcf\x6c\xd6\xcf\xf0\x7f\xcf\x70\x9c\x97\x9c\x9e\x01\x5f\xe1\xc0\x30\xd7\x00\x5a\xc7\x47\x9b\x10\x73\x12\xc8\x1f\xa3\x72\x4c\x07\x1a\x0a\xd6\x04\x10\x0f\xa9\x5d\x59\x5d\x45\x6c\x46\x8a\x77\xd6\x6c\xb9\xe5\x51\x63\xa8\xe9\x79\x61\x21\x08\x91\x60\x44\xa3\x70\x8e\x36\xe8\x85\x61\xc1\xc5\x89\x9d\xb7\x65\x06\xd7\x7c\xd3\x59\x24\x4e\x5d\xa7\xf9\x28\x7c\x7c\xef\xc2\x07\xfa\xcf\x69\x8a\x33\x9c\x5e\x60\x2a\x86\x24\x33\x22\xca\x4b\xe2\x07\xa8\x31\x82\x3c\xea\x8f\x19\x07\x46\x5b\x29\x7a\x9b\x46\x41\x8c\xde\x51\xf7\x4c\x34\x8c\xc6\x18\xc7\x83\x95\x01\x80\xe0\x21\x9f\x21\x02\xb6\x46\x3f\x27\x47\x50\xe4\xbf\x82\x18\xed\xa6\xb3\xfe\x35\xfa\x7d\x44\xfe\x59\xb9\xc4\xfd\xff\x3c\x9f\x04\xd1\x78\x65\x90\x4c\xec\xf2\xce\xc9\x11\x6f\xae\x44\xec\x91\x0b\x55\x96\x7e\x9e\x14\xf9\x5e\xe2\x01\x39\x28\xd0\x94\x49\x4f\x9f\x3c\x21\x83\x0e\xa4\x27\xd2\x21\x81\x92\x88\x2a\x85\x96\x60\xd6\xe9\xaf\x3f\xd1\xea\x6a\x72\x81\xd3\xe1\x38\xb9\x24\x75\x60\xe3\xf3\x79\x3a\x50\x52\xcf\xef\x2c\xfd\x48\xca\xbe\x16\x9f\x1b\xf2\xe7\x75\xfd\x6b\x93\xed\x61\xac\x31\xc0\x13\x50\x21\x60\x45\xbb\xab\xab\x88\x37\x8b\xfa\x3e\x29\x02\x28\x43\xd3\xf5\xd7\xa2\x4a\xa3\xa8\x22\xca\x3c\x01\x04\x68\x21\x5a\xaa\xa9\x96\x62\xc5\x9e\x00\x2a\xac\xdc\x0d\xfc\x97\x10\xa4\x5c\x62\x79\xb9\xdf\x94\xbe\xc3\x7f\x78\x19\x5a\x64\x79\xb9\xdf\x78\xfd\xd4\x5d\x60\x79\xb9\xef\xb3\xef\xe4\xbf\xd0\x71\xde\x28\x3c\x2c\x6f\x40\xcf\xdf\xbc\x61\xf9\x20\xe5\xd7\x0d\xaa\x02\x54\xde\x32\x84\xcc\x96\x44\xb5\xfa\x55\xdd\x67\x5a\xbf\xa2\x28\xe3\x7a\xa4\x10\x79\x79\xa3\x53\x07\x5b\x1e\xb5\x01\xfd\x57\xa5\x11\xf6\x92\xde\x20\x71\x52\x2a\x5e\x2e\x31\x82\x91\xa6\x60\x75\x15\x91\x5d\x02\x6e\x62\x50\x24\x2d\x24\xba\x78\x8c\x95\xf6\x3c\x43\x00\x2f\x43\x49\x3c\xbe\xa6\xcb\x71\xeb\x97\x83\xa3\x2d\xf4\x3b\x7a\x83\xd6\x01\x26\x6f\xd0\xb7\x61\x41\xef\xe2\xd4\xce\xb2\x6f\xbc\xbf\x7c\x2d\x29\x67\x01\xb1\xae\x56\x1c\xaf\xff\x42\x99\x73\x51\x91\xd3\x28\xae\xc9\x30\x66\xab\x8c\x27\x8a\x66\xf9\x80\x19\xa8\x97\x49\x3c\xc8\x2d\xf5\x80\xd0\x60\x6f\xa4\x5c\x06\x42\xb7\x90\x83\xd0\x7c\x59\x88\x4b\x07\x84\xb0\x4d\x9a\xa7\xac\xe8\x89\x2e\x1a\xb1\xcf\x12\xae\xaa\xea\x79\x11\xa1\x08\x39\x04\x23\x74\x3b\xe1\x08\x2d\x28\x20\x21\x55\x9e\x33\x0f\x5d\x05\xdd\xcb\x67\x2f\xb1\x34\x5e\x6b\x92\x95\x28\x2e\x09\x58\x4e\x11\x4b\x2a\xbc\x80\xa4\xd5\x7a\x94\xb4\xbe\x77\x49\xcb\x21\x5f\x39\xd4\x3b\x27\x47\xe5\x72\xce\xa2\xea\x1d\x0b\x4b\xd7\x79\xf9\x23\x13\xff\xe7\x31\xf1\xd2\xd3\xec\x03\xb0\xec\xbd\x78\x90\x62\x88\xdc\xc0\x80\x6b\x20\x99\x1c\x52\x4c\xee\x4b\x44\x8d\x69\x1c\x5f\xe0\xb6\xfc\x2b\xaa\xff\xad\x36\x87\xaa\xbb\xc2\xfc\xf3\x36\x29\xb3\xc0\x2e\xd0\x7e\xdc\x05\xfe\x16\xbb\xc0\xf6\x18\x0f\xf2\x34\x89\xa3\x01\xea\x25\x21\xee\x27\xc9\x7c\x85\xff\x76\xaf\x4c\xe1\x4f\xbf\x2e\xb4\x23\x6c\xf7\x54\x85\x3f\x79\xbe\xaf\x1d\x40\x66\xed\x2a\x03\x51\xeb\x95\x69\x31\x09\x3e\xca\x42\x7a\x28\xfc\x42\x7c\x2b\xfc\x78\xea\xa5\xde\x7c\xbd\x19\x94\x59\x60\x1d\xff\xbd\x93\x23\xff\xcf\x59\xc7\x07\xb3\x7c\x3a\xcb\xab\x5f\xda\x1d\x94\x5e\xda\x1d\x2c\x7e\x69\xa7\x4b\x75\x07\xda\x25\xde\xc1\x5f\x7b\x1d\xf4\xe0\x52\x9d\xa9\x9b\x17\x6f\xee\x57\xb2\x2b\x69\xe8\x7b\x91\xee\xfe\x49\x27\xec\x03\xed\x5a\xd3\x25\x44\x1d\x54\xb8\xb4\x38\x58\xf0\xd2\xe2\x31\x8b\xdd\xdf\x83\xf9\x6e\x7e\x3c\xde\x43\xbf\xae\xbc\x6a\x34\xb9\x81\x38\xca\x72\xb2\xbc\xcf\xaf\x0d\xee\x3b\x0d\xc2\x95\xcd\x38\x8b\x7e\x25\xa5\x45\x2e\xb8\x69\x10\xca\xec\x2f\x0c\xf2\x40\xba\x08\x75\x5d\x80\x66\xea\x0d\x28\xa9\x75\x5c\x18\xfc\x2a\x06\xc0\xaf\xd5\xa2\x7d\x3d\xad\x48\xdf\x95\x50\x04\x88\x62\x16\xe7\xa2\x67\x5a\x30\x2b\xb0\xc5\x3b\xa4\xdf\x0c\x60\xf4\xc5\x4b\x15\xb3\x7f\x69\xdf\x8d\xd6\x68\x4c\x9b\x71\x90\xd1\xc8\x59\x68\x9a\x64\x91\xea\x81\x4f\x1a\x25\xdf\x49\xfd\xc3\x84\x77\x56\xb4\xb0\xac\x61\xf4\x12\xf9\x5a\x23\x87\x41\x58\x3c\xc3\x40\x89\x6c\x23\xea\x6b\xca\x4a\xe4\xb6\x8a\x90\x5a\x6a\x23\x45\x48\x2d\xb9\xb4\x2d\xb8\x96\x6a\x99\xbd\xac\x01\xe2\x76\x88\xdc\x02\x77\x16\x5b\x88\x43\xa7\x88\x77\x38\x97\x12\xce\x2b\x53\x45\x15\xf8\x62\x34\xcb\x67\x4e\xea\x73\x4d\x45\xf3\x25\x39\xfe\xb2\xbe\x17\x17\x41\x12\x0a\x6c\x5f\x31\x3c\x24\x34\x30\x8e\xde\x3e\x7d\x72\x63\xe5\x9b\x7c\xb9\x5c\xbd\x6a\x34\x17\xe2\x9d\x77\x4b\x4c\xf6\xc8\x3b\xbf\x15\xef\xdc\x3b\x3e\x40\x10\x12\xb7\x1a\xeb\xdc\x63\x01\x74\xef\xca\x3a\xff\x72\x76\x58\x2c\x89\x39\xfc\xd0\xc2\xaa\x68\x3a\x00\x7b\x04\xba\x95\x34\x88\xc3\x64\x52\x33\x38\xe0\xd2\xd2\x8a\x26\x29\x95\xc3\x61\xa9\xc3\x4e\x0d\x2e\xd7\x68\x9d\x79\x04\xdc\x23\xa3\xd2\x19\x15\x27\xce\x85\x18\xd5\xdf\x3b\xf3\xc2\xff\x28\x46\xb5\xba\xb7\xdd\x43\xaf\xd6\x5e\xad\xbd\xf4\x11\xa3\x0d\xb4\x8f\xf3\x51\x12\xa2\x86\x8b\x5b\x41\x68\xef\xdb\x72\xab\xcd\x30\xa4\xfe\x83\xea\x82\xa8\xc0\x05\xf8\xea\x25\xb5\xe9\x1f\x5f\xb4\x4a\x03\xff\x07\xa7\x09\xe4\x0e\xcb\x47\x18\xa5\x38\x93\xf8\xa2\xd2\x11\x52\x8e\xf5\x98\x3c\x1b\x78\xdf\x8a\x17\xb0\x85\xf8\x07\xc3\x41\x5d\x8d\xce\xe6\x01\x34\x85\x67\x5f\xd8\x49\x8c\xd1\x24\x49\x31\x15\x1e\x5f\xbe\x84\xbe\xb9\x46\x91\xaf\xf7\x97\x2f\x2b\x2e\x70\x98\xcf\x45\x16\xf8\xda\xdd\xa2\x9c\x3f\x2e\xf0\x6f\x76\x8a\x43\x71\x92\x4c\xab\x89\x21\x1f\x39\x39\x3a\x57\xb6\x20\x76\xf7\x9a\x28\x8a\x94\xd1\x9c\x68\x6a\x21\xa2\xbb\x5b\xb8\xd9\x47\xa2\xfb\x56\x44\xf7\x7f\x24\xe6\x57\x4e\x72\x12\x0f\xfc\x0b\x85\xdf\xca\x07\x67\xf9\x7c\x6b\x08\xc0\xb5\x5a\xb9\x08\xbc\x84\xbe\x7e\xd5\x5f\xdd\x6a\x8b\xb1\xf7\x78\x7e\x5c\x81\xd5\x55\xf4\x89\xc0\x57\xeb\x45\x46\xa4\x00\xd0\x2c\x88\x32\x97\xa3\x68\x8c\x51\xed\x87\x5a\xe1\x6b\x5d\xc4\xe0\x06\x8f\x43\x23\xe6\xb6\x30\xe1\x34\x14\x99\x91\xd8\x92\x90\xaa\xa2\xd4\x1d\xbb\x21\x1e\x6f\x95\xdd\x4b\xa2\xa0\x85\x78\xc9\xdf\xdb\x71\xcb\x92\xa3\x8b\x26\xc9\x7a\x58\xbe\x52\x64\x42\x82\xd6\xfe\xfa\x3c\x1f\x0f\x9b\x24\xbc\x5a\x4c\x6c\x23\xe6\xb5\xf8\x72\xbc\xbb\xe9\x17\xb1\x9e\xc9\x93\xf4\xd1\x4c\x04\x6e\x73\x10\x3d\x0c\xb2\x8c\x2c\xe4\x97\x04\xb5\x10\xbd\xc7\xd7\x68\x0b\xa7\xd1\x05\xcd\x09\xb9\xc3\x07\xa5\x51\x1e\x73\xfa\xf0\xed\xfb\xad\x9d\x46\xd1\x9a\x78\xae\x98\x78\xbc\x97\xc4\xc3\xe8\x7c\xc6\x32\x51\x26\x90\x15\x32\x2b\xcb\x2f\x99\x26\x53\x9c\xe6\xd7\xe8\x4f\x7a\x2c\x06\x6f\x52\x60\xbe\x27\x23\x9a\xe3\x38\x23\x0f\x51\xcc\xd2\x05\xe4\x89\xf0\xa5\x59\x41\x5b\x78\x18\xcc\xc6\x79\x17\xb5\x50\xcd\x6f\xac\x43\x22\xe5\x25\x17\x7c\x47\x42\x73\x9c\xf2\x44\xe6\x05\x38\x32\xfe\xf3\xd0\x8c\x72\x96\x3c\x33\x03\x50\xc5\xa1\x5e\xfa\x90\x27\x68\x8a\xd3\x61\x92\x4e\x24\xe0\x0a\x64\x29\xfd\xe3\x60\x78\xde\x75\x8d\x32\xa2\x17\x5f\xc7\x10\x73\xc6\x6f\xac\xaf\x36\x1b\x5a\x08\x6e\xda\x15\x8a\xba\xf6\xa9\x40\x48\x69\xfc\x66\xa9\x2c\x21\x69\x59\x02\x79\x32\x2b\x61\x41\x5a\x7c\xbd\xcd\xcf\x22\x7a\x00\x7c\xee\x86\x74\x55\xce\x18\x4a\xc6\x6f\x60\xa3\x1b\xee\x6f\x36\x4c\x52\x38\xc5\x14\x8d\xde\x43\x62\xd0\x2f\xe1\xd0\x48\x1a\x4f\xa9\x9d\x9f\x1e\x15\x33\xac\x45\x2a\xfe\x59\x4c\xd6\x3a\x4d\x3f\x79\x67\x30\x9e\x3a\x8d\xf5\x7a\x5d\x07\x5c\x92\xbd\x7e\x30\x3c\xb7\x1b\x5e\x90\x89\xd8\x10\x3f\x39\xe1\x91\xe2\xae\x60\x18\xe6\x7a\x87\xeb\x0a\xea\x41\x57\x95\x05\xdd\x26\xdf\xec\x94\xc1\x06\x6a\xe1\x0f\x2b\x15\x2b\x67\xc1\x38\x47\x9b\xf0\xcf\xe2\x89\x68\xb9\x1b\x8d\xe4\xd7\x7e\x17\xb2\xa3\x89\xd4\xc3\xe1\x0a\x8b\x4a\x52\xe3\x9d\xf1\x00\x3f\xe7\xa4\xb2\xe2\xf2\xbc\x6a\x35\x17\xca\xed\xa2\x4e\xbd\xd5\x80\x30\xca\x1d\x49\x61\x99\x97\x3d\xf8\xee\x33\x5a\x25\xe4\x43\x79\x90\x27\x66\xc7\x6e\x96\xe8\x4e\x50\x0e\xb2\x29\x1d\x6c\x9a\x6e\xde\xd0\xe7\xd8\x42\x3d\x81\x9c\xbc\x17\x87\xf8\xca\x56\xe3\xb4\x7e\xc5\x14\x40\x96\x68\x9d\x73\x42\x74\x09\x54\x84\xb0\x2c\xde\x38\xf3\xd7\x17\xd8\xf0\x4a\xc5\x1b\x67\x25\xbe\xe5\x6d\x90\x59\x59\x61\x4f\x36\x23\x8c\x62\x6b\xa1\x45\x8b\x17\x73\x8c\x2c\xd4\x8f\x4c\x50\xd7\x3a\xc8\xe3\x22\xbd\xe1\xf8\x58\x8d\x0b\x44\x27\x59\x9e\x63\x9e\x2c\x1b\x28\xb0\x48\xe3\x5b\xf4\x5a\x9f\x33\xc4\x32\x7a\x17\xa9\x81\xcd\xef\xf3\xb3\x31\x00\x7c\x65\x88\xad\xa3\x6b\x16\x17\x59\x8c\x8a\x57\xac\xe3\x0e\x44\xf6\xc4\x18\xdb\x41\x47\x72\x34\x3b\x06\xd6\x82\x85\x62\xcb\xe1\x53\x5b\x0e\x69\xfa\x9c\xc6\x1c\x08\xf8\xb9\xd2\x04\x8c\x9e\x18\x69\xf9\xa3\x6d\xac\xab\x8c\x37\x9a\x17\x0a\xca\xd6\x59\x3e\xfa\xf2\x3b\x7b\xc0\x2a\xa9\x89\x5f\x0f\x8e\xd4\xee\x80\xeb\x94\xc5\xe3\xda\x18\xb7\xdf\xa9\x0d\xcc\xef\xdc\x06\x46\x9a\xcd\xd7\xe8\xf7\x92\xd1\x23\x7f\x45\x8d\xd3\xdf\xc1\x1c\xc6\xe8\xc8\xe9\xef\xba\x59\x0c\xff\xbb\x31\x5f\xeb\x01\xa7\xc8\x9f\xc4\x1c\x98\x6e\x1a\x1a\xb5\x4d\x89\xc6\x24\x4e\xeb\x67\xcb\xcb\xe5\x26\x45\x12\x70\xe9\xe8\xcb\xf9\x86\x25\x88\x19\xdb\xcb\x8a\x7a\x65\x06\x94\xf2\x31\xe2\x4e\x1b\x7a\x95\x60\x33\xa5\x1b\xf9\x82\x9b\xf8\x7d\x89\x96\x51\x66\x4b\xb7\x3f\x3f\x7a\x8d\x45\x34\xb8\x87\x20\x36\x54\x44\x10\x92\x21\x15\x0a\x5d\x62\xc2\x62\xd5\x3c\xe4\x90\x4d\xef\x02\xa6\x54\x36\x2d\x82\xec\x88\xa3\xa4\x4b\x80\xf1\x90\x2e\xa8\xb2\x61\x57\xc5\x62\x52\x68\x8e\xf0\x74\x53\x66\x8b\x46\xa1\xd9\x03\xf5\xe8\x29\x74\x79\x4e\xd8\x9b\x33\x6f\xed\xef\xed\x43\xbf\x40\x5a\xf7\xf9\xc9\xd1\x1f\x56\x77\xe4\x4c\xaf\xed\xca\x7a\xfd\x4f\xd0\x2e\x1d\x83\x71\x66\x8f\x1b\xef\x52\x25\x92\xfc\xb2\x4c\x8f\x24\xf0\x38\xc2\xb3\x2c\xe8\x8f\x31\x0b\x07\x26\xa1\x73\x8c\xe4\x54\x8b\x14\x8a\xfe\xe6\x1d\x52\x33\xac\x49\xdb\xc2\x11\x64\x53\x46\xcc\xd0\x96\xd9\x18\x9b\x9a\x24\x51\x1e\x62\xac\x44\x19\x0a\x10\x4d\xc0\x8c\x2e\x70\x9a\x41\xd4\xb2\x51\x90\xa3\x18\x9f\x8f\xf1\x20\xc7\x21\x61\xc3\x03\x96\x52\x35\x67\x0a\x9f\x3c\x41\xe3\x28\xcf\xc7\xf8\x25\x0d\x70\xb9\xa2\x02\xc5\x69\x9a\xa4\x28\x4c\x70\x16\x3f\xcf\x51\x30\x1c\xe2\x01\xad\x4b\x91\x7a\x9e\xa1\x0c\x0f\x66\x69\x94\x5f\x7b\xa2\x62\x7f\x96\xa3\x28\x87\x4a\xbc\x46\x94\x67\x22\xa0\x42\x34\x8e\x72\xe6\xc4\x4d\xf3\xba\x46\x84\x3f\x4f\x70\x4c\xf7\x83\xcc\xa6\x28\xa3\x03\xf2\x81\x76\x4e\xa8\xcb\xb4\xb7\xf2\xfc\xdd\x36\x69\x5b\xf9\x21\xe5\xbd\x6c\x06\xed\x3c\x60\x14\xd6\xdb\x70\x6a\xb8\x28\x3b\x2d\x44\xec\x84\x46\x76\x2f\xec\x3c\xa7\xfd\x2a\xda\x25\xbf\x2c\x89\xe3\xde\x9f\xd6\xcf\x3c\x54\x7b\x7f\xda\x3c\x63\xc1\x02\xd0\x57\xf2\xc8\xae\x02\xfc\xce\x92\x25\x89\xdc\xfb\x53\x9f\x56\xaa\xab\x95\x9a\xe5\x95\x1a\xb4\x92\xaf\x56\xaa\x97\x57\x6a\xd2\x4a\x0d\xb5\x92\x2f\x2a\xa9\x75\x6c\xd9\x91\x8c\x21\xe3\x5e\x86\xae\x41\xeb\x89\x41\xeb\xd9\x07\xcd\xc4\x47\x1a\x2e\xd6\x27\x7a\x61\x32\x1c\xf2\xb4\x83\x14\x69\x1a\x64\xb5\x5e\x27\x5f\x6c\xfd\x35\x27\xa2\xa9\x42\xf6\xad\x90\x1b\x95\x20\xd7\x9d\x03\x2f\xc1\xd0\x20\x37\x2b\x41\xf6\x5d\xb3\xe3\x49\x30\x34\xc8\x75\x0d\xf2\xfc\x89\xec\x05\x69\x7a\x8d\xfa\x7a\x3a\x55\x3a\x55\x7d\x1a\xff\xc2\xd4\x64\xe4\x74\xf2\x09\xeb\xc9\xae\xb3\x1c\x4f\xd0\x30\x99\xa5\x28\x8f\x26\xfa\xdc\x2f\x18\x94\x37\xc6\x57\xf9\x31\x59\x7d\xee\xf8\xb1\x96\x88\xb7\xfb\x49\x18\x0d\xaf\x29\x27\xa4\x74\x58\x01\x8b\x75\x37\x16\xbd\x53\xea\x38\xf0\xeb\x29\xa4\xbc\x84\x68\x2b\x46\xa6\x38\x5b\x92\xdc\x9f\x51\x86\xf3\xd9\x54\xfd\x50\xe2\xd1\x31\xff\xb0\xbf\xf7\x33\x75\xed\x28\x3b\xe1\xef\xfd\xfc\xb9\x8e\x36\xd0\xde\xcf\x66\x6a\x34\xa9\x88\x4f\x8b\xf8\xd6\x68\xc6\xf2\x92\x86\xa9\xcc\x66\xfd\x0b\x4c\x44\x05\xd7\xd1\xbf\x4e\x83\x1f\x43\xdb\x34\xfa\xf1\x57\x44\x9f\x5c\xd1\x8f\xe5\xe2\x2c\xcc\xb1\x28\x5f\x5c\x87\xda\xc3\x1c\x8b\x66\x1b\xa2\x59\x5f\x69\xd6\x9f\xd7\xac\xaf\x36\xeb\x2f\xd6\x2c\x84\xd1\x89\xea\x7c\x09\x12\x20\x51\x43\x5d\x81\xae\xaa\x4d\xa8\xda\xe0\x8b\x19\xaa\xd6\xd5\x65\xea\x98\x11\x46\xd6\x65\xac\x15\x01\xb5\xd6\xe9\xb9\x5e\x8f\xed\x4f\x3f\xfa\xf4\xa3\x6f\xfd\xd8\xa0\x1f\x1b\xd6\x8f\x4d\xfa\xb1\x69\xfd\xd8\x2a\x6b\xb3\x5d\xd6\x66\xa7\xac\xcd\x35\xd1\x66\x89\x46\xaa\x12\xe7\x41\x8b\x73\x1f\x54\x8d\x03\x21\x53\x49\x21\xfb\x11\xdd\x4b\x72\x57\xa7\xf2\x5a\x92\x3e\x2a\x71\x66\xb5\x88\xbd\x77\xee\xed\x1d\x06\xb7\xf0\x32\x03\x2e\xa4\x96\x3e\xa6\xa1\x86\x7e\x05\x22\x44\xb5\x5f\xc9\xdc\xf3\x55\x02\xcf\x62\xef\x7d\xad\x57\xf4\x69\xc5\x06\xab\xb8\xa6\x55\x6c\x3b\x2b\x36\x68\xc5\x16\xab\xe8\x6b\x15\xd7\x9c\x15\x9b\xb4\x62\xe7\x4c\xa0\xa6\x54\xf4\x8b\x8a\x77\xda\xc5\xca\xa2\xd4\x53\x44\x78\xec\xf8\x63\x96\x92\x9d\x05\x8f\x87\xc7\xdb\x44\x8f\xe7\x70\x18\x83\x13\x70\x6c\xf1\xe3\xad\xf8\x5a\x9d\xf0\x90\x94\xa3\x57\x78\xd3\x1d\x97\x7b\xd1\xc9\xd4\x2f\xec\x78\x8a\x9b\xdb\xe2\x63\x74\x41\xbf\x74\x5a\xab\xcd\x86\xae\x96\x13\xcb\x44\x10\x6c\xad\xa2\x2b\x94\xb2\x3e\x94\x2f\x92\x08\xaa\x19\xfc\x1c\x07\x17\x18\x25\xe3\xd0\xc9\x6a\x17\x90\x1f\x7a\x9f\xe9\xe4\xf6\xf4\x78\x87\x4a\x8b\xbd\x60\x3c\x98\x8d\xc9\x0a\x8b\xf1\xa5\xb3\xd9\x1e\x4b\x04\xd3\xa3\x89\x60\xea\x57\xad\xb0\x09\xff\x87\x96\xb9\x84\xa6\xe7\x6b\xe9\xb1\xbc\x30\x3d\x9a\x17\xa6\x7e\xc5\x6a\x34\x21\xa6\x7c\x8f\x0b\xa8\xf5\x25\xf4\x06\xd5\x7a\x9f\xa5\xe7\xff\x40\x3e\xea\xa2\xfa\x92\x09\xb1\xc1\x20\x36\x28\x44\x06\xb0\xc5\x20\xfa\x1a\x44\xbf\x02\xc4\x26\x83\xd8\x34\xba\x55\xa3\xed\x28\x10\x1b\x15\x20\xb6\x18\xc4\x96\xb5\xd7\x4d\x0d\x62\xb3\x02\xc4\x36\x83\xd8\xb6\xf6\xba\xa5\x41\x6c\x55\x80\xd8\x61\x10\x3b\xd6\x5e\xb7\x35\x88\xed\x0a\x10\xd7\x18\xc4\x35\x6b\xaf\x3b\x1a\xc4\xce\x5c\x88\x85\xd8\x4f\x81\x2a\xd5\xd7\xf4\xea\xba\x77\x8c\xa0\x69\xb2\xfb\x9c\xbf\xbc\xc3\x22\x22\xa5\xce\xaf\x80\x57\x47\xa4\x6b\x3d\x4b\x12\x0e\x9e\x2e\x3f\x9d\x0d\x72\x34\x8a\xce\x47\x28\x88\x43\x34\x4e\x2e\x51\x90\x9e\xcf\x20\xfc\x0b\xb8\x39\xff\xf7\x2c\x48\x8d\xc4\x3d\xd0\x40\x80\x36\x48\x2b\x5c\x8a\xb3\x28\x0f\xce\xfb\xb4\x08\xdd\x25\xac\xc7\x27\xde\x67\x05\x83\x14\x67\xb3\x71\x8e\x92\x61\x59\xf3\x23\xba\x05\xd4\xce\x03\xf4\x02\x9d\x07\xd4\x75\xc5\x5f\x5b\x42\xcb\x88\xbe\xea\xb3\x57\x6d\x78\xd5\x87\x57\x36\x24\xc7\x14\x90\xd4\x15\x7a\x24\x7c\x81\xce\xaf\x60\x86\x97\x80\x20\x78\x01\x21\x76\x4a\x05\x6c\x89\x60\x48\x87\x7e\x3d\x38\x42\x10\x4e\x52\xfe\xf8\x8e\x72\xb8\xf3\x11\xfa\x0d\x9d\x8f\xab\x32\x39\xbb\x52\xe5\x57\xc6\xe2\xde\x51\x16\x57\xab\xbd\x2b\xb6\x6f\xb2\x93\xbd\x93\xc4\x82\x25\x56\xa0\xa3\x16\xe8\x14\x05\x74\x7a\xfe\x95\x71\xc3\x77\x94\x1b\xd6\x68\x33\xc5\x7e\xfb\x8e\xf3\x3f\xd8\x6f\x97\x11\x69\xcd\x84\xd1\x60\x30\x1a\x1c\x86\xaf\x22\xe0\x1b\x18\xd6\xd5\x02\xf5\x32\x0c\x9b\x0c\x7a\x93\x43\x6f\xa8\x18\x36\x34\x0c\x7d\x0b\x86\x2d\x06\xa3\xc5\x61\x34\x55\x04\x9a\x06\x86\x0d\xb5\x40\xa3\x0c\xc3\x36\x83\xde\xe6\xd0\x5b\x2a\x86\x2d\x0d\xc3\xa6\x05\xc3\x0e\x83\xd1\xe1\x30\xda\x2a\x02\x6d\x03\xc3\x96\x5a\xa0\x55\x86\xe1\x1a\x83\xbe\x76\xa6\x90\x88\xc0\xb0\xa3\x61\xd8\x56\x30\xac\x94\xf8\x23\xe3\x49\x27\x84\xae\xb5\x42\xda\x89\x79\xd7\x5d\x14\x56\x8e\xaf\x72\xf9\xde\x49\xd6\xa4\xf2\x50\x0a\x4a\x1a\x07\x7a\x5b\x64\xde\x5f\x4d\xc7\x01\xc1\xe6\x2a\x47\x4e\x70\x2c\xce\x4c\xad\x68\xd9\x06\x51\x5c\x5c\x95\x29\x75\xd5\xe4\x1d\x72\xc9\xa5\xb2\x3b\x28\xb9\x60\x65\x63\x64\x4f\xbd\x1b\xe9\xb6\x5b\x5e\x71\x29\xd2\x6d\x77\x3c\x76\x57\xd2\xed\xf8\x37\x67\xde\xda\xdf\x3b\x12\xe1\xe3\x7d\xd5\xe3\x7d\xd5\x83\xdd\x57\x69\x4b\xbc\xb8\xcf\xd1\x6f\x72\xfe\x5e\x77\x38\xf7\x95\x15\xee\xbd\x38\x9a\xbf\x57\x8f\xe6\xef\x6f\x7b\x34\x7f\xaf\x1e\xcd\xdf\x97\x1d\xcd\xe7\x29\x98\x1f\x6f\xaa\x1e\x6f\xaa\x1e\x6f\xaa\x94\x2f\x8f\x37\x55\x8f\x37\x55\x8f\x37\x55\x45\xb3\x8f\x37\x55\xfa\xc7\xc7\x9b\x2a\xc7\xe3\xe3\x4d\xd5\xe3\x4d\xd5\xe3\x4d\x15\xfc\x3d\xde\x54\x55\x53\xe2\x3e\xde\x54\x3d\xde\x54\x3d\xde\x54\x49\x7f\x8f\x37\x55\x8f\x37\x55\x8f\x37\x55\x8f\x37\x55\xff\x93\x6f\xaa\xee\xed\x8e\xea\x76\xb7\x53\x55\xee\xa5\x2a\xdc\x48\x3d\xd4\x5d\xd4\xdf\x3b\x1f\xca\xe3\x5d\xd4\x3f\xff\x2e\x4a\xbe\x3b\xea\xb5\xe6\x3a\x3a\xc9\x37\x47\xbd\x96\x74\x6d\x04\x0f\x0f\x7f\x67\x44\xbd\x34\xc5\xad\x91\x3d\xa8\x00\xf7\xd0\x2e\xbb\x56\x02\x37\x4e\xd9\xa3\x58\x8a\x99\x6e\xea\x2b\xe2\x28\x47\x59\x3f\xb9\x32\xe1\x1c\x0b\x74\x8e\xe5\x6b\x3a\xfe\x67\x93\x26\x1b\xed\x8e\xfb\x50\xce\x0e\xdd\xd1\x7c\x35\xee\x7b\x7c\x6d\xd3\xe3\xaa\x2d\x7a\xdc\x7f\x7c\x6e\xc3\x6c\x50\xc8\x10\xf0\xa8\x12\x11\xfa\x97\x3c\x4e\x0e\xd5\x21\xab\x44\xb6\x36\x3e\xf6\xa7\x0a\x20\x33\x12\x9a\xf2\xd9\x08\x8a\x66\x3b\xfb\x93\x5e\xd4\x7e\x47\xcb\x74\x7c\x96\x79\xa3\x4b\xe8\x5f\xd0\x2b\x47\x2c\x85\xcb\x60\x6a\xc7\x19\xf6\x0d\x53\x43\x20\x4d\xc0\xb1\xdd\x31\x9e\xbc\x26\x33\x3e\x7f\x7a\x7a\x56\x15\x3f\xcb\xaa\x21\x88\xe6\x77\x96\x65\x56\x00\xba\xb3\x5a\x8e\x6b\x42\x40\x0b\x62\xe4\x5f\x27\xd3\x63\x57\x19\x2a\x2d\x0b\x27\xe7\x46\xbb\xe3\x50\x88\xd4\x9d\xca\x10\x6b\xa3\x55\x15\x23\xd2\x7a\xd2\x14\x23\xc5\xa0\x45\xda\x97\xdf\x8b\xe1\x9c\x9b\x01\x1e\x94\x83\x6a\xf5\x2f\x32\x9e\xda\x7c\x88\xd5\x14\xd3\x65\x14\x53\x95\x5a\x6c\x59\x44\x11\x68\xd0\x69\xc2\x38\x46\x95\xca\x77\x85\x84\x1d\x84\x6b\x25\xda\x12\x82\x75\x13\x6b\x41\xa8\xea\x7b\xb5\xb3\x5f\x49\xdd\x1a\x5b\x53\xa4\x0a\xc3\xeb\xac\xc8\x6b\x10\xeb\x79\x0c\xb4\xe3\xd3\x27\x88\x83\x62\xb9\xd1\x2a\x48\x3d\x32\xce\xee\x64\x2c\x94\xb9\x62\x62\x99\x82\xdd\xf7\x2a\xf7\xf6\x5a\xf7\x21\xf4\xf6\x5a\x0b\x4b\xbc\xe6\x1e\xab\x89\xbb\xbd\x96\x35\xb6\x05\xdc\xd0\x44\x38\xbc\xc5\x0e\xbf\x95\x26\x53\x65\x97\x67\x2f\x60\x10\xbe\x41\x54\xbc\x90\x34\xa7\x06\x9a\xd3\xf4\xfc\x64\xe2\x49\x29\x11\x6a\x0e\xf9\xaf\x1a\x32\x58\x3d\xd6\x1c\x41\x5d\x8a\xfa\xa5\xad\x62\x02\xaa\xab\x82\x50\x23\xc6\x55\x12\x62\x48\x1b\xbc\x60\xf9\x1d\x06\x19\xcf\x92\x0d\x5c\x18\xbe\x10\xbc\xc8\x2e\xfe\x13\x6c\xe6\x2f\x5f\x5a\xf7\xf0\x05\xd8\x3d\x9a\x93\x00\xe9\x3b\x5a\x6d\x64\x88\xee\x67\xc5\x01\xa4\xc5\x57\x1d\xa3\xf9\xf2\x95\x47\x0a\x95\x9f\x34\x7b\xad\x87\x3a\x66\xde\x2d\x5d\xdf\xb7\x3c\x5f\x3e\xd8\x29\xf0\xdb\x06\x71\x26\xac\x0a\x67\x38\xbd\xc0\x4f\x9f\xd4\x06\x4b\xa8\x51\xf7\x1b\xa8\x7f\x8d\x7a\xff\xdf\xff\x1b\xa6\xd1\x00\xed\xe3\x2c\x8e\xc6\x2b\x68\x73\x3c\x46\x69\x74\x3e\xca\x33\xc4\xca\x87\x2b\x4f\x9f\x3e\x39\xc2\x61\x94\xe5\x69\xd4\x9f\x01\xfc\x20\x0e\x21\x28\x4f\x14\xa3\x2c\x99\xa5\x03\x0c\x6f\xfa\x51\x1c\xa4\xd7\x84\x1d\x4c\x32\x8f\x45\x69\x48\xe1\xdf\x64\x96\xa3\x09\xf0\xf4\x01\x70\x56\x0f\x05\x29\x46\x53\x9c\x4e\xa2\x3c\xc7\x21\x9a\xa6\xc9\x45\x14\xe2\x90\x06\x9d\x20\xeb\x74\x98\x8c\xc7\xc9\x65\x14\x9f\xa3\x41\x12\x87\x11\x5d\xc3\xa4\xd2\x04\xe7\x5d\xb6\xe2\x5f\x22\x15\xad\x0c\x14\xc3\x14\x9f\x41\x12\x62\x34\x99\x65\x39\xd9\xa8\x83\x28\x06\xa0\x41\x3f\xb9\x20\x9f\xa6\xd7\xd0\x45\x14\x27\x79\x34\xc0\x1e\x8d\x2b\x34\x8e\x32\xd0\x2c\xcb\xed\xc5\xa1\x86\x4c\x18\x65\x83\x71\x10\x4d\x70\xba\xe2\xc2\x21\x8a\xe5\x81\xe0\x38\x4c\xd3\x24\x9c\x0d\xf0\xbd\xa3\x81\x58\xd7\xc2\x64\x30\x13\x71\x30\x48\x8d\xd5\x24\x65\x31\x32\x26\x41\x8e\xd3\x28\x18\x67\xc5\x30\xc3\xdc\x40\x35\x09\x75\x32\xcf\x27\xbb\x7b\xc7\xe8\xf8\x60\xe7\xe4\x97\xcd\xa3\x6d\xb4\x77\x8c\x0e\x8f\x0e\x7e\xde\xdb\xda\xde\x42\x6f\xff\x8d\x4e\x76\xb7\x51\xef\xe0\xf0\xdf\x47\x7b\xef\x76\x4f\xd0\xee\xc1\x87\xad\xed\xa3\x63\xb4\xf9\x71\x0b\xf5\x0e\x3e\x9e\x1c\xed\xbd\xfd\x74\x72\x70\x74\x8c\x9e\x6d\x1e\xa3\xbd\xe3\x67\xf0\x61\xf3\xe3\xbf\xd1\xf6\xaf\x87\x47\xdb\xc7\xc7\xe8\xe0\x08\xed\xed\x1f\x7e\xd8\xdb\xde\x42\xbf\x6c\x1e\x1d\x6d\x7e\x3c\xd9\xdb\x3e\xf6\xd0\xde\xc7\xde\x87\x4f\x5b\x7b\x1f\xdf\x79\xe8\xed\xa7\x13\xf4\xf1\xe0\x04\x7d\xd8\xdb\xdf\x3b\xd9\xde\x42\x27\x07\x1e\x34\x6a\x56\x43\x07\x3b\x68\x7f\xfb\xa8\xb7\xbb\xf9\xf1\x64\xf3\xed\xde\x87\xbd\x93\x7f\x43\x7b\x3b\x7b\x27\x1f\x49\x5b\x3b\x07\x47\x68\x13\x1d\x6e\x1e\x9d\xec\xf5\x3e\x7d\xd8\x3c\x42\x87\x9f\x8e\x0e\x0f\x8e\xb7\x11\xe9\xd6\xd6\xde\x71\xef\xc3\xe6\xde\xfe\xf6\xd6\x0a\xda\xfb\x88\x3e\x1e\xa0\xed\x9f\xb7\x3f\x9e\xa0\xe3\xdd\xcd\x0f\x1f\xac\xbd\x24\xb8\x2b\x7d\x7c\xbb\x8d\x3e\xec\x6d\xbe\xfd\xb0\x4d\x5b\xfa\xf8\x6f\xb4\xb5\x77\xb4\xdd\x3b\x21\xdd\x29\x7e\xf5\xf6\xb6\xb6\x3f\x9e\x6c\x7e\xf0\xd0\xf1\xe1\x76\x6f\x8f\xfc\xd8\xfe\x75\x7b\xff\xf0\xc3\xe6\xd1\xbf\x3d\x06\xf3\x78\xfb\x7f\x7f\xda\xfe\x78\xb2\xb7\xf9\x01\x6d\x6d\xee\x6f\xbe\xdb\x3e\x46\xb5\x39\x43\x72\x78\x74\xd0\xfb\x74\xb4\xbd\x4f\x70\x3e\xd8\x41\xc7\x9f\xde\x1e\x9f\xec\x9d\x7c\x3a\xd9\x46\xef\x0e\x0e\xb6\x60\xa0\x8f\xb7\x8f\x7e\xde\xeb\x6d\x1f\xbf\x46\x1f\x0e\x8e\x61\xb4\x3e\x1d\x6f\x7b\x68\x6b\xf3\x64\x13\x1a\x3e\x3c\x3a\xd8\xd9\x3b\x39\x7e\x4d\x7e\xbf\xfd\x74\xbc\x07\x83\xb6\xf7\xf1\x64\xfb\xe8\xe8\xd3\xe1\xc9\xde\xc1\xc7\x25\xb4\x7b\xf0\xcb\xf6\xcf\xdb\x47\xa8\xb7\xf9\xe9\x78\x7b\x0b\x46\xf7\xe0\x23\x74\xf5\x64\x77\xfb\xe0\xe8\xdf\x04\x28\x19\x03\x18\x7c\x0f\xfd\xb2\xbb\x7d\xb2\xbb\x7d\x44\x06\x14\x46\x6a\x93\x0c\xc1\xf1\xc9\xd1\x5e\xef\x44\x2e\x76\x70\x84\x4e\x0e\x8e\x4e\xa4\x3e\xa2\x8f\xdb\xef\x3e\xec\xbd\xdb\xfe\xd8\xdb\x26\x5f\x0f\x08\x94\x5f\xf6\x8e\xb7\x97\xd0\xe6\xd1\xde\x31\x29\xb0\x47\x9b\xfd\x65\xf3\xdf\xe8\xe0\x13\x74\x99\xcc\xd1\xa7\xe3\x6d\xfa\x53\xa2\x58\x0f\x66\x12\xed\xed\xa0\xcd\xad\x9f\xf7\x08\xda\xac\xf0\xe1\xc1\xf1\xf1\x1e\xa3\x13\x18\xb2\xde\x2e\x1b\xee\x95\xa7\x4f\x5e\xac\xaa\x3a\xaf\xfd\x20\x1f\xdd\xaf\xde\xab\x5a\xd4\x69\x1a\xf8\x58\x14\xa1\x8f\x95\xac\xb3\xe1\xc2\x2e\x88\xf3\x0c\xe5\x41\x9f\x4b\x2c\xa4\xca\xe7\x3f\xc6\xd6\x60\x9b\x85\x1c\x55\xf7\x10\xf2\x3d\x84\x1a\x1e\x42\x4d\x0f\xa1\x96\x87\x50\xdb\x43\xa8\xe3\x21\xb4\xe6\x21\xb4\xee\x21\xf4\xca\x43\x7e\xdd\x43\xbe\xef\x21\xbf\xe1\x21\xbf\xe9\x21\xbf\xe5\x21\xbf\x2d\x59\x58\xae\xd1\xba\xe4\x1b\x81\x47\xca\x13\x18\x7e\x9b\xc2\x25\xf5\xa0\xad\x57\x0c\x7e\x83\xc1\xf0\xa1\x8d\x02\x4e\x93\xb5\xd5\x62\xb8\xbc\x62\x30\xd6\x25\x3c\xd7\x18\xac\x0e\xc3\xc5\xa7\x30\x7d\x39\xd6\xb2\xcf\xea\x72\x5c\xea\x14\x06\xe0\xc1\xf1\x6c\x52\x58\x04\xbe\x2f\xf7\x5b\x86\xd3\x62\x75\xdb\x0c\xf7\x35\x06\xa3\x21\xe1\xe9\x33\x58\xeb\x0c\x17\xd6\x6f\xbf\x79\xb6\xf4\x5a\x9e\x8b\x74\xce\x5c\x70\x3c\xd6\xa4\xb1\x6a\x30\x98\x1c\xe7\x8e\x3a\x1e\xd0\xb7\xa6\xd6\xf7\x0e\xab\xd3\x2c\x60\x41\xdd\x76\x81\x33\x87\xc1\xc7\x03\xda\xf2\xb5\xbe\x43\xa1\xb6\xd4\xc1\x35\x86\x60\xa7\x18\x5c\x01\xa4\x21\x0d\x34\x45\xb6\x00\xb4\xce\xea\x48\x83\x05\x13\xd3\x2e\x06\x57\xc0\x68\x4a\x03\x4d\x91\x95\x10\x6a\xb0\x91\xad\x4b\xc0\xf8\x68\xac\x89\xd9\x13\x14\x8a\xd8\xe8\x50\x64\xd5\xd9\xc8\xe6\xad\x0c\x8a\x22\x1b\x2b\x40\x4f\x6e\x89\xd3\x56\x53\x1a\xcf\x4e\xf1\x4d\xa1\xe9\x35\x0f\x3e\xc1\x50\x71\x7a\x7d\x55\xd0\x1e\xa7\x29\xbf\x2d\x0d\xeb\x1a\x2b\xab\xcc\x87\x5f\x10\x81\x98\x8b\x57\xac\x20\x27\x9e\x75\xa9\x0c\x47\x7c\x0d\x7e\xcb\x67\x29\xb1\x96\x5b\x45\x55\xde\xbe\x58\xf3\xf2\x9a\x58\x57\x40\x16\xa0\xf8\xfa\x6c\x17\xb4\x2f\xfa\xd9\x28\x50\x10\xe3\xc4\x48\x86\xc2\x45\xda\x94\xcc\x5b\x20\x0c\x31\x65\xf0\xdb\x05\x02\xd0\xcf\xb5\x62\x21\x42\x83\x2d\x86\x48\x47\x43\xba\xa9\x0e\xbe\xe8\xb4\x5f\xc0\x11\x63\x27\x16\x34\x7c\x57\xe0\x08\x06\xe2\x4b\x83\xd4\x29\xda\x15\x0b\x8f\x2d\x60\xbf\x69\x99\x0f\xd1\x01\x0d\x71\x0e\x48\x2c\xb8\x86\xf4\x6f\x5b\xac\x62\x75\x80\xda\x96\x72\x2d\x75\x66\xc4\x4c\x16\x9d\x42\xbe\x8f\xce\x94\x2c\xd9\x9f\x47\x64\x85\x58\xe6\x03\x89\x50\xcd\x75\x0f\xd5\xaf\xda\x9b\xeb\x8d\xb5\x57\xaf\x5e\x91\xdf\x9d\xed\xad\x57\xdb\x6f\x37\x7d\xf2\x7b\x7d\xc7\x7f\xfb\xb6\xb7\xd5\x23\xbf\x37\x5f\xb5\x9b\x3b\x5b\xad\x6d\x75\xbe\x47\xa9\xb3\x81\x76\x7d\xb3\xb1\xfe\x76\xbb\x03\x0d\xf4\x5a\x5b\x5b\x7e\xa3\x05\x0d\x6c\xad\xd5\x9b\xdb\x3b\x4d\xf2\x7b\x6d\xb3\xb3\xb5\xd6\xd9\x86\x86\x39\x42\x67\x56\x7d\xc0\xd1\xde\xe1\xf6\xfe\x96\xdf\xa9\x43\xf8\xfd\x39\x3a\x24\x51\xb6\xd0\x22\x49\xaf\xe8\xae\x7c\xdb\xbb\x22\xaa\x4c\x04\x24\x1c\x41\xb0\x3b\x6b\xad\x76\xa3\x59\x87\x11\xdc\xde\xe9\x6d\x6d\xbe\x5d\x87\x0e\xbe\x5a\x7f\xbb\xb9\xd5\xdb\xd9\x26\xbf\xfd\x7a\xb3\xd1\x6e\xad\xc1\xe0\xf4\x9a\x5b\x8d\x6d\x7f\xa7\x7e\xe6\x54\x8d\x57\x55\xca\x5b\x15\xbb\x95\xbd\x94\xfc\x92\x9b\x9a\xf9\xe6\xf8\x14\x0b\xd0\xbd\x16\x66\x91\x8e\xeb\x9b\xfd\xcf\x52\x69\x7e\x79\xf0\xd9\x34\x64\x42\x65\x77\x2a\x52\x3d\xb4\x81\x6a\x66\x01\x44\x0d\x40\xa5\xc6\x0a\xc3\x07\xe9\xe5\x62\x46\xa5\x06\x40\x66\x57\xaa\x01\x34\xad\x4b\x4d\x70\x25\xaa\x31\x34\xcf\xd6\x79\x17\x89\xfb\x07\x42\x8a\xce\x2b\x47\x60\x00\x9f\x47\x63\x77\x81\x14\x0a\xa4\xce\x02\x20\x7e\x7e\xfe\xc3\x0d\x01\x64\xa2\xcf\x7f\xb8\x21\xc0\x36\xfd\x39\x73\x43\x80\x4d\xe3\x73\x96\xda\x23\x5a\xaf\xae\x92\x55\xf6\x85\x1c\x9a\x2f\x82\x34\x22\xd2\xb1\xe5\x92\x36\x18\x7b\xa8\x3f\xf6\xd0\x60\xec\xa1\x70\xec\x21\x3c\xb6\x34\x14\xa4\x1e\xea\xa7\x1e\x1a\xa4\x1e\x0a\x53\x0f\xe1\x54\x6f\x2c\x20\xa8\x04\x04\xe1\x5d\xd3\x65\xa4\x9f\x42\xd0\x71\xf8\xe8\xeb\x1f\x07\xe4\xe3\x80\x7e\x6c\xe8\x1f\x43\xf2\x31\xa4\x1f\x9b\xfa\x47\x38\x30\x60\xfa\xb1\xa5\x7f\x14\x69\xaa\x03\x35\x2f\x35\xef\x92\x7e\x2b\x68\x35\x25\x84\x7f\x97\x37\x90\x6f\x5d\xdb\x39\x59\x3e\xc1\x18\x2d\x17\x6b\x6a\xf9\x8f\xf1\x69\x74\x76\xb6\xf4\xd5\xe6\xc4\x00\x5e\x3b\x6f\xfc\xce\xd2\x9f\x4f\x9f\xa8\xac\x91\xb4\x81\x86\x7e\xad\x3f\xf6\x06\x63\x2f\x1c\x2f\xa1\x65\x34\x1a\xdb\x7d\x6f\x6e\x90\x50\xc8\x45\x6f\x9a\x0d\xaa\x6a\xb3\x40\x6b\xe8\xd0\x8c\x91\x37\xa0\xb5\xd6\x9d\xd0\x9a\x3a\x34\x63\xaa\x0c\x68\x9d\x96\x13\x5a\x4b\x87\x66\xcc\xad\x04\xed\xcf\xd5\x55\x06\x71\xbd\xee\x84\xd8\xd6\x21\x1a\x04\x81\xec\x61\xd2\xc9\x24\xe6\xd6\xe9\x22\x5f\x50\x9a\xe4\xe3\x5a\xee\x65\x64\x5a\x6d\x4e\x1b\x40\x03\xf9\x32\x1e\xdb\xa7\x1c\x56\x84\xb1\xa4\xc8\x1f\xd0\x6d\x68\xfb\x02\xe4\x0e\xed\x92\x35\xe9\x5b\xdd\x80\x60\xbd\xf4\x6d\xb5\x61\x99\x19\x37\x89\x02\xd5\x20\x45\xcb\x12\xb5\xa6\xb7\xa7\xd6\x76\xad\x9f\x7a\x83\xd4\x0b\x53\x18\xf1\xf4\x6e\xd4\xda\xd2\xa1\xdd\x95\x5a\x55\x68\x77\xa2\xd6\x86\x0e\xed\xce\xd4\xea\xeb\x10\xef\x99\x5a\x53\xb8\xb5\x2e\x21\xd7\xd4\x41\xae\xc0\x51\x53\x1b\xb9\x02\x23\xb6\x7d\x01\x16\x4d\xc9\x35\x75\x92\x2b\x6c\x00\xb6\xda\xb0\x35\x98\x16\x1a\x3a\x2b\xdf\x93\xd3\x31\x80\x0c\x09\x56\xbf\x9a\x84\x49\xfe\xb3\x81\x6a\xbb\xd4\x34\x77\x40\x38\x73\x68\xe9\xe9\x2e\x33\xe1\xdd\xa5\xe6\xb7\x21\x29\x67\x1b\x91\x5d\x66\xa6\xbb\x4b\x0d\x69\x31\x29\x17\x58\xcb\x35\x59\x39\x30\x96\x85\x1d\xa1\x6f\x2d\xd7\x62\xe5\xc0\x30\xb9\x4f\xca\x0d\xac\xe5\xc0\x80\x59\x19\x16\x5d\xac\xdd\x61\xa9\x35\xee\x60\x9e\x15\x06\x79\x20\x84\x21\xf2\x60\xd9\xf8\xe7\xa7\x61\xe4\x25\xe3\xb7\x51\x9e\x9d\x24\x39\x70\x3c\x0a\x33\xde\x0a\xf2\x80\x5a\x6d\xbd\x40\xeb\x16\xe8\x50\xe7\x03\x1e\xe6\x46\xd2\x46\x28\x6f\x74\x66\x33\x0c\xcd\x2c\xc4\x88\xe5\x5b\xa4\xc6\x4c\x05\x48\x22\x4d\xb6\xcf\xd0\xd7\x0d\x9a\x58\xb8\xb0\x91\x10\x25\xfe\x85\x9a\x0d\x9d\x5a\x0b\x48\xb5\x5a\xad\x28\xba\x8c\x08\x7f\x20\x20\x5f\x2d\x11\x50\x2d\xb2\x6e\xfd\x96\x43\x80\xe6\x55\xe9\x70\x14\xc2\xb3\xf4\xb2\xba\xf0\x6c\x00\x63\x82\xb3\x06\x6c\x9e\xe0\x6c\xeb\xa8\x9c\xa7\xa3\xc8\x87\xc9\x73\xec\x80\x71\x8c\x25\x6d\xc7\xea\x2a\x9c\x04\x11\x64\x77\xa1\x0e\x59\x56\xc3\xa9\x29\x3d\x79\x99\xd9\x5c\xca\xc9\x12\x56\xb7\x2c\xa3\x5b\x08\x67\x17\x6d\x20\x59\x7c\xbf\xdb\xf9\xad\x5d\xe9\xf8\x66\x3f\x91\xed\xc2\x51\x6c\xd7\xe2\x4c\x82\xca\xce\x60\xbb\xc2\x5d\x6f\x57\x39\x5e\xed\x2e\x7c\xae\xa2\x14\xb2\xab\x9c\xa9\x76\x9d\x87\xa9\xf9\xa6\x70\x47\xf4\x26\x9c\x4e\x2e\xcb\x60\x11\xc2\x60\xab\x45\xd9\x8d\xb9\x36\x41\x0a\x9b\x1a\x8c\x93\xb8\x9c\x41\xfd\xff\xec\xbd\xfd\x76\xdc\x36\xb2\x20\xfe\x77\xfc\x14\x98\xf9\x9d\xb1\x5b\x51\x4b\x22\xc0\x2f\xd0\xb6\xf2\xdb\x44\xb1\xaf\xb3\xb1\x63\x1f\xdb\xd9\xf8\x1e\x1f\x3b\x03\x92\xa0\x9a\x71\xab\x5b\xb7\x9b\xb2\x5a\x99\x38\x67\x5f\x63\x5f\x6f\x9f\x64\x0f\x0a\x20\x09\x92\x00\xd8\x2d\xcb\xb9\x93\x19\x6b\xce\x38\xdd\xcd\x42\x55\xa1\xbe\x50\xc4\x47\x01\xb6\x12\x08\xa8\x76\x76\x01\xbe\xda\xb7\x41\xc8\xc7\x3f\x0f\x8c\x44\xb6\x1b\xda\x9a\x62\x13\x9e\x76\xf6\x45\xc1\xc7\x0f\x72\xf5\x1f\xe9\x3b\xe2\x0a\x3c\xd9\x4c\xd1\xd5\x14\xfd\x6a\xba\xe6\x63\x32\xd9\xc0\xc9\xce\x2b\xf8\xf7\xd7\xf6\xb6\xf6\x0f\x03\x3c\xc4\x8d\x67\xb2\xd9\xbb\x3d\xb9\xda\x93\xc7\xc9\x7f\x17\x5f\x7e\xdd\xdb\xdb\xbb\x67\xc3\xe6\x8f\x62\x13\x88\x7e\x17\x18\x5b\xd6\x2c\xb8\x82\x71\x5c\xb7\x01\x03\xf0\x76\xb5\x77\x7b\xf2\x3b\x30\x67\xc7\x18\x6e\x23\x33\x21\xb4\xdf\x5a\x54\x16\x5c\x90\x4a\x6c\xa6\x0b\x23\xa6\xcd\xfd\xfb\x0b\xe0\x6a\xf3\xd5\x57\x5f\x4d\x7c\x72\xb0\xd0\x99\x92\x1f\x9c\xbb\x61\xea\xcd\x30\xf2\x1e\xb8\xed\x36\xc3\x58\x6f\xfb\x51\xfb\x5b\x60\xcf\x53\xfd\xb9\x5a\xca\xc8\x34\x44\x63\xb9\x9f\xc7\x02\x7d\xdd\x8b\x79\x94\x67\xb4\x3b\x59\xea\x09\xbc\xc9\x1d\xc5\xe2\x1d\xc3\x2e\x1c\x7b\xab\xcb\x9a\x5b\xd3\x76\x9b\xe1\xe4\x60\x6f\xab\x4d\x0d\xb0\xdd\x56\xa5\x5a\x39\x8f\x9e\x7c\x7d\xf2\x07\xa8\xc6\xd1\xfc\x1d\xbf\x82\xa6\x6b\x9e\xad\x78\x65\xb9\x3b\xc9\xa2\x50\xb8\x72\xf0\x06\x15\x2a\x2f\x32\x6c\x54\xf3\xe8\x8c\x65\xad\x7a\xf4\x2d\x56\x06\x0d\x75\x80\x87\x5a\x3a\x63\x99\x41\x53\x5f\x7c\x90\xeb\xc0\x96\xad\x51\x35\xa4\xf9\x76\xa2\x0f\x6f\xa6\x71\xfc\x79\x8b\xd3\xbf\xc2\x91\x95\x4f\xbd\x74\xdf\x2b\xac\xa6\x11\xb6\x96\x4c\x7b\xf1\xe8\xeb\x03\xbc\xc5\x4a\xc6\xf0\xae\xea\x9b\x5c\xbf\x38\x86\xd3\xa7\xed\x12\x46\xb9\x28\xab\x89\xa1\x00\x55\x77\x49\x83\x17\x59\xce\x52\x9a\x18\x6a\x33\x79\x9b\x84\xa6\x2c\xcf\x0a\xde\x59\xe3\x30\x01\x66\x7e\x4e\x38\x2e\xbc\xee\xb3\x8f\x5f\x02\xb1\x65\xe8\xe6\xe4\x7b\x38\x83\x3e\x40\xb0\xcd\xdc\xb3\x79\xba\x58\x3c\x4a\xcd\x93\xc5\x90\x30\x9a\xa7\x8a\xe1\x75\xd5\x3c\x51\x2c\x1e\xf1\x66\x9a\x78\xc0\xa9\x75\x9e\xd8\x3a\x27\x6c\x79\x5b\x80\x79\x1f\x24\x4f\x98\x5a\x6a\xc1\xfc\x24\x13\xff\x6e\x09\x8c\xee\xd9\xd3\xfa\xaf\x9e\x50\x32\x23\xaa\xcf\x39\xfc\xf4\xba\x44\x07\xc8\x7f\x83\xde\xaa\x8f\xb4\xfd\x88\x03\xed\x73\x64\xbb\x3b\x52\xb1\x34\x59\xc0\xe1\x58\xf9\x6e\x09\xaf\x0f\x3e\x36\x97\xa9\x31\xbf\x09\xc1\xd4\xd2\x84\x09\x24\x21\x20\x61\xf2\x4d\x26\x86\x03\xb2\x1c\xed\x03\x21\xdb\x44\x23\xba\x8f\x88\x67\x95\x1a\x4c\x9b\x4d\x26\x29\xba\x8d\x32\x99\xe7\x8a\x8f\x39\x60\xf6\x36\x21\x93\xab\xb0\x23\x53\x7c\xe8\x3e\x0a\xc6\x48\xa4\xe8\x2d\xca\xd0\x5b\x94\x4b\xcc\x11\xcf\x13\x9e\x32\x53\xd1\xa1\x1e\xe6\x68\x07\xe6\x25\xef\xe2\x53\xa6\x7a\x71\x80\xbc\x4d\xec\xf1\x20\xf0\x49\x60\xa7\x75\xf4\x65\x43\x8e\x7a\x7b\xe8\xcb\xa3\xad\xfb\x22\xf0\xfb\x61\x92\xfb\x9c\xf4\x67\x79\x90\x45\xa5\xc2\x5f\x72\xd3\x74\x1f\x3a\x46\x99\x69\x8a\x0f\x01\xc9\xfb\xf7\x91\xef\xa9\x5e\x82\xfa\x8d\x77\x8b\xa2\x63\x64\xe2\x83\x6d\x77\x5a\x6b\xab\xc9\x40\x35\x89\x56\x4f\xb6\xb1\xfe\x09\x6f\xd4\x99\x08\x84\x09\xc3\x41\xe5\x13\xd4\x99\x04\x84\xc9\xc2\xcc\x0c\xe3\xeb\x13\x85\xb9\x19\x26\xd0\x27\x09\x79\x1f\xe6\xf3\x04\xdf\x3f\xeb\x04\x9f\xc8\x85\x0f\x8b\xf9\x72\xb9\xd2\xe7\xdc\x8e\x60\xa0\x56\x7f\x1f\x45\x04\x6a\x21\xb4\x98\x47\xe6\xe9\x06\xd3\x74\x9f\x68\x86\x6e\xc7\x79\x20\xe3\x74\xdd\x9f\x71\x36\xe8\xf3\x14\xc2\x60\x32\x40\xa4\xcf\x3b\xcd\x1e\x40\x03\xd7\xc4\x41\x37\x21\xef\xce\x19\x88\x67\x9f\xa7\x0b\x6e\x74\xba\x00\xf4\xb1\xc5\x4c\x81\x59\x2d\xed\x24\x81\x52\x8d\xfd\xd8\x94\x00\xb0\x4f\x0b\xd0\x3f\x75\x81\x8d\xf5\x8c\x91\x30\xfa\xd4\xb5\x31\x14\x95\x7f\x9f\xe9\x83\xc1\xf4\x80\xfe\x0e\x4f\xc2\xa8\xf3\x16\xaf\x9d\xc2\xee\xcf\x0a\x10\x12\x6c\x37\x2f\x20\x00\x3b\x38\xe1\xbb\x44\xfe\x87\xce\x0d\x64\xd8\x0b\x13\x9e\x53\xf1\xca\xef\x47\x71\x96\x87\x5e\x0c\x9f\xbd\xd8\xcb\x73\x0c\x9f\x8b\xd8\xe3\x61\xe2\x9b\xe7\x0c\x8a\x22\xf3\xbc\xd4\x87\xc9\x85\x88\x86\x14\x87\x58\x7e\x0e\x8a\x84\x16\x0c\x10\xa4\xbc\x60\x41\xc1\x82\x1d\xa6\x0b\xb6\xca\x3c\xb5\xb0\xaf\x44\xa7\xb5\x74\x9c\xa2\x85\x88\xda\xa4\x33\x07\xc7\xc3\xe4\xc5\xb2\xb0\xf4\x79\x88\x1e\x19\x71\x09\x09\x76\x1d\xa4\x45\x93\x91\x61\xba\xe3\x1d\x83\x81\x9a\x10\xf3\x21\xf6\xcf\x43\xf5\x47\x0c\xd5\x42\x2b\xdb\x0d\xd6\x46\xe5\x74\x86\x6b\xa9\x20\xe7\x80\x4d\x48\xff\xa8\xb3\x76\xae\x59\x0d\x47\x77\xe3\x44\x0c\xe0\xc9\xe7\x79\xfd\xff\x9e\x81\xf9\xcf\x77\x2c\xef\x3b\x79\x89\x43\xf9\x6b\x73\x2a\x17\xad\x96\x17\x8b\x1c\x65\xdd\xf3\x7a\x5a\x0f\x1e\xf5\xaf\x4e\xf9\xbe\xbb\x0c\x50\x4f\xd4\xf2\x16\x87\x7c\x62\xca\x60\x90\xbe\xa4\x5c\xae\x9f\xad\xca\x33\x3e\x59\x18\x87\xb1\xf5\x7f\xad\xaa\x1f\xea\xf7\x7c\xf1\x65\xb2\xe8\xbf\x67\x36\x13\xc1\x52\x9d\xe8\x18\x91\x7b\xf5\xe7\xfb\xc7\x12\x43\xfd\x83\x63\x6e\xf8\x2f\x93\x05\xfa\x9b\x02\xdb\xb3\xce\x17\x2a\x1f\x2d\xd8\x7c\xcd\xc7\x77\x05\xf6\xe7\xc7\xea\xf7\xf1\xd5\x45\xf7\x0d\xd7\x20\x96\x53\x5e\x3d\x5c\x31\xf8\xcc\xe6\xdf\x94\xd5\xda\x20\xa0\x66\x09\x7f\x81\x0e\xd0\x64\x01\x95\x3d\xf7\xd0\x97\x9d\xc9\x8f\xfe\x4c\x96\x46\xab\x9e\xa5\xd6\x2b\xb3\xc3\x6f\xa0\x90\x5e\xfd\x9e\xcb\x59\x39\xe7\x68\xa2\x9e\xdd\x47\x6a\x4b\x66\x5f\x8a\xad\x36\xad\x82\x6e\x50\x50\xab\x94\x1f\xbd\x96\x40\x50\x76\x74\x20\x08\xb0\x85\xf3\xe5\xe5\x64\x31\x45\x18\x1d\x21\xb2\xb7\x45\xc5\x76\x04\x37\xa1\xec\x82\xd6\xdf\x33\x16\xcf\x96\x28\xf6\xf7\x47\xa6\x42\x17\x1d\x88\x3a\x43\x9a\xb4\x38\xaf\xbf\xc6\x26\x12\xef\xed\xb2\xe9\x61\x86\xfe\xc9\x57\xda\x1e\x1d\xae\xe7\x65\xc6\x27\xde\xde\xe7\x55\xaf\xad\x57\xbd\x06\x8f\x0a\x78\x14\x9a\x1e\x9d\xc2\xa3\xc1\x82\x11\xe4\x2c\xf0\x28\xfe\xe8\x65\xb4\xc8\x51\xeb\xfe\x8f\x5e\x46\x3b\x65\x67\x67\xcc\xdb\x34\x8b\x69\x78\x20\x94\x21\x34\x6c\x34\x9e\xd4\x2d\xef\xdf\x47\x44\x2e\x7a\xd5\xbf\x7c\xf5\xd5\x57\x28\xde\xdb\x43\xe8\xad\x19\x53\xf7\xaf\x83\x09\x07\x03\x4c\x98\xee\xed\x6d\x87\xa9\xdb\xce\x37\x86\x97\x4e\x4f\x70\xdb\x6f\xe3\x26\xf9\x2e\xb0\xd6\x6d\x2c\x99\xd5\xba\x8d\x37\x75\xbd\xe9\x2d\x99\xed\x62\xf2\x87\x98\x92\x1d\xbb\x5d\xb7\x33\xdf\x49\x80\x5a\xc3\x51\x4a\xdc\x57\x3d\x87\x22\xbf\xaa\x87\xfb\xce\x05\x53\xdb\xea\x67\x06\xa7\x1a\x27\x1c\xdd\x46\x05\x6c\x76\xfb\x5d\x7c\x3c\xb5\x5d\xe1\x72\xc6\xa0\xc2\x1c\x43\xb7\x51\x0a\xe0\x4c\xae\x0e\xbe\x45\x6a\x9d\xd0\xc4\x3f\x24\x2b\xe5\xa9\x60\xbc\x59\x6a\x55\x8b\x6d\x6a\xad\x55\x6e\xfd\x93\x4f\x70\xa2\x3d\xc1\x7e\xe7\x51\xa7\x91\x79\x6c\x6b\x88\xc1\x3d\x35\x13\x0e\x36\x2e\x2b\x27\x73\x68\x17\x29\x8c\xf2\x09\xd6\x9e\x60\xac\x3f\x8a\xe5\xce\x56\xf9\x88\x84\xe6\x11\x0f\x16\x90\x05\xa5\x19\xda\xaf\xc9\xee\x0b\xa1\xee\xcb\x8b\xde\xac\x8b\xc7\xd0\x90\xa0\xe3\x5a\x30\xfb\x42\xb4\x26\x0a\x22\x70\x9d\x1a\x10\x88\x58\xd7\xaf\xd3\x2e\xfe\x44\x78\x34\xa5\x5f\x50\x3b\x13\x6e\x4b\xc0\xa6\x65\x3e\x34\xb2\x44\xda\xaf\xb6\x8e\x46\x96\x43\x27\x95\x10\x44\x45\x4c\xb4\xfe\x5d\x96\x46\x25\x4c\xa8\x60\xa0\x64\x78\x61\x86\x89\x14\x0c\x94\x04\x3f\x35\xc3\xc4\x0a\x06\x7c\x7e\xf6\x79\x19\xf6\xf3\x32\xec\xe7\x65\xd8\x61\xb6\xf9\x79\x19\xf6\x9f\x72\x8e\x37\x8c\x76\x9e\xe3\x0d\xa3\xd1\x39\x5e\xfd\x9d\x6d\x38\xc7\x1b\x46\x9f\xe7\x78\x6f\x7c\x8e\x37\x8c\xb6\x9d\xe3\x35\x29\xa7\x3b\xc7\x0b\x0a\x72\x6f\xda\x6e\xd6\xce\xcc\x4b\xb3\xd4\xfb\x53\x2f\xcd\x6e\xa2\xe0\x0f\xb9\xb8\xa0\xa1\xf3\x79\x16\xb8\x3b\x0b\xbc\x89\x60\x4d\xf5\x70\x13\x05\xda\xef\xaf\xa2\x40\x55\xe9\x06\x88\x43\xad\x4e\xf4\x4e\x35\xdd\xb4\xfe\x3d\x7f\xf4\xf4\xe7\xa7\x0f\x1f\xbe\x78\xf0\xf2\x45\x7f\xb6\xf8\xd9\x77\x3f\x7f\xf7\xc3\xb7\x0f\x5e\x3d\x18\xde\xca\xfd\xfc\xe9\x8f\x3f\x7c\xfb\xf3\xc9\xd3\x1f\x5e\xbc\xfc\xfa\x87\xa6\xa5\x46\x4e\x4e\x2b\x9f\x6c\x37\xad\xac\xb5\x58\xcd\x96\x75\xd1\x96\xde\x9c\x74\x4d\x5a\xbc\x5d\xe3\x29\xba\xb2\x95\x2a\xaf\xe4\x94\x48\x85\xee\x23\x12\xdc\x43\x95\x61\x4a\x44\xeb\xf3\xeb\x0d\xda\x47\x21\xfa\x12\x5d\xc9\xd3\x83\x55\x7d\x48\x13\x3e\x91\x3d\x98\xa9\x44\x7f\x43\xd1\x20\x17\x81\x34\x90\x5f\xbe\x42\xc7\xe8\x0a\xfd\x0d\x85\xa6\x2c\x91\x5f\xfe\xa7\xc0\x4a\xd0\x97\x48\xd0\xf1\x05\x9d\x3d\x03\xf0\x46\x4e\xcb\xbd\xea\xfd\x7c\x25\x7f\xfe\x4f\xcb\x54\xb0\x26\xb6\xf3\x12\x95\x70\x9d\x80\x41\x68\x8d\x64\x36\x52\x32\x1b\x79\x40\x73\x63\x10\x4c\x03\x2a\xa5\x8b\xae\x24\xe8\x95\x65\x5a\xa9\x35\x90\xae\x18\xaf\xe0\x82\x9f\x61\xaf\x85\x5c\xfb\x5d\xff\x30\xda\xb7\xde\x2a\x47\xd7\x1a\x1e\x3f\x7c\xf1\x5c\xf0\xba\xf1\xb0\xc9\x18\xf4\x7b\x27\x2c\xf3\x63\x02\x0c\x48\xd4\xc6\xfa\x64\x7d\xd9\xb3\x2d\x23\xd8\xe3\x1a\xcc\x22\x42\x75\xf3\xc4\x2f\xe8\x3e\x8a\xef\xa1\x5f\x1c\x33\x73\xd0\x07\x38\x9a\x6a\xae\x8a\x52\x93\x4f\xcb\xea\xd9\x72\x0d\x75\x5c\x85\x55\xc1\x65\xb9\xbf\xec\xa1\x03\x64\xda\x4d\x5d\x23\xd7\x1b\xdd\x47\xaa\x5e\x84\x09\x58\xfc\x0d\x3a\xf8\xf6\x18\x01\x19\x0d\x8b\x85\x56\x77\x47\xb5\x4e\xf5\xab\x63\x20\x6b\xdf\x5c\x3d\xa0\xfc\x44\xa3\xdc\x41\x75\x60\x78\xef\x69\x18\xd8\x6e\x6a\x49\x33\xac\x05\xdf\x54\x60\x40\x23\x6a\xa1\xf6\x9d\xe8\x47\x47\xe8\xd9\xaa\x3c\x2b\xab\xf2\x3d\x47\xe7\xcb\xf9\xd5\x62\x79\x56\xb2\x39\x5a\xbe\xe7\x2b\xf4\x1f\x0f\x27\x64\xef\x2e\xda\xbc\xa5\x68\x1f\x6d\xde\x46\xf0\x6f\x08\xff\x06\x22\xcc\x98\x51\x2a\x8b\x96\xe4\xe5\xf9\x81\xb7\xc8\xdb\xc4\x8e\x2d\xf3\x16\xe6\x14\x86\x63\xa3\x7d\x8c\x2c\x7a\xf5\x02\xbc\x9c\xe3\x53\xc3\x4f\x5d\x60\xac\xaf\xb3\xe9\xc0\x7e\xf6\x76\x5d\x4d\x59\x83\xff\x54\xfc\xec\x7c\xb9\x62\xab\xab\xce\x4d\x74\xc2\x05\x5e\xea\x03\x91\x75\x95\xd2\x78\xeb\x8c\xd9\xfb\x5f\x1a\x7b\x36\xc6\x77\x6f\x6d\xc7\xdf\x6e\x65\xc7\xef\xac\xeb\xf8\xae\x55\x9d\x9b\xbf\x4a\x60\x79\x51\x9d\x5f\x54\x8f\xe1\xd5\xba\x03\x8b\x20\x49\xcf\xf9\xba\x5c\xf1\x5c\xbb\x68\x20\x2d\xab\x75\x5d\x10\x5a\x36\xee\xbc\x2d\xd4\x8d\x9f\x2e\xe6\xb5\x9a\xb4\x1a\xdc\x6c\xc5\xef\x22\x42\x82\x29\x22\x61\x34\x45\x3e\x0d\xa6\x28\xc4\xa4\xdf\x58\xdd\x59\x70\x57\x3c\xd3\x1f\xf5\x2f\x2d\xa8\x5f\x9a\xad\xf7\x16\xe8\xbd\xeb\x61\xbb\xc6\xfd\x05\x30\x53\x0b\x37\x21\xd6\xef\xde\xf5\xb7\xd7\x6f\x2c\xd1\x7e\x0b\x53\x13\x7f\x80\x47\x9a\xdc\x82\x5f\x36\x66\x07\x8b\x70\x63\xa5\x04\x80\x93\xe6\xb6\x5e\x18\x01\x22\xcf\x43\x07\x48\x0c\xb4\xcd\x4d\x09\xba\x24\x44\xf6\xe2\x93\x4f\xb5\xa2\x67\x98\x98\x33\x08\xcd\x38\x79\x56\x77\xe2\x31\x5b\xc0\xdc\x4f\xaf\x6b\x47\x88\x98\xe6\xd0\xd2\xf5\x72\x95\x8e\xcb\xbf\x87\xfe\x63\x2a\x09\x3e\x21\x25\xea\x2e\x8a\x09\x59\x5b\xa7\xcd\x9f\x10\xb8\x83\xbe\x0f\x2e\x62\xbd\xab\x98\x85\xf5\x0a\x6a\x41\xde\x59\x4f\x90\x74\x0a\x09\x92\xeb\x54\x10\x24\x9d\xd2\x81\xe4\xfa\x35\x03\x15\xc3\x78\x8c\x63\xdc\x65\x19\x5f\x8b\x67\xdc\x65\x1a\xef\xc2\xb5\x51\x0f\xd2\xb8\x9a\xa9\x91\x72\x51\x2d\xa5\x35\x9b\x35\x3d\x67\x30\x99\x57\xbb\xb3\x41\x14\x02\xe2\x10\xee\x9b\x7d\x7b\x0c\x72\xb1\xc1\xcc\x97\x97\x48\xc1\x8c\xaf\x46\x3c\x17\x03\xec\xda\xe2\x03\x32\x51\x06\x3f\x90\x1f\x65\xd2\x0b\x9f\xed\x2e\xf0\x72\xc6\x2b\x36\x7c\xb2\xc3\x5b\x83\x86\xec\x49\x29\x5e\x41\xe6\x17\x67\x0b\xe8\x9c\xc1\xad\x6a\x09\xd6\x69\xf6\x14\xb5\x99\xb4\x11\x78\xc7\x77\x12\x9d\x46\x47\x4b\xed\x1b\x8a\x85\x90\xf8\xab\x53\xcf\x46\x7b\x2e\xd8\xc7\x1a\xec\x7c\x79\x69\xcd\x4b\xad\xd2\x7a\x69\xcc\x73\x4c\x3d\x79\x29\xb4\xf0\xf2\xf5\xc6\xc6\xfb\xcb\x8d\xb4\xb5\x63\xe8\x81\x1d\x08\x8c\xed\x18\x58\xdf\x6e\xf7\xcd\xf5\xcc\xc0\x11\x56\xdb\x1e\x05\xd0\xa5\x89\xd0\x4b\x00\xaf\x87\xae\xc5\xf2\x97\x1b\xdc\x82\xe3\x6d\xc0\xa5\x7d\xbd\xdc\x60\x97\x1e\x15\xec\xe3\x06\x16\xf4\xe8\x34\xef\xf5\xc5\x0a\x3c\x4a\x5e\x27\x22\x4c\x7d\xdc\xca\x5f\x6e\x02\x15\x0b\xd0\x64\xa2\x78\x6b\x8e\x06\x2b\xfa\xea\x7c\xb0\xed\xf5\x06\xb0\x3d\x6e\xb0\xc9\xa8\x21\xb1\x3d\xee\x61\x7b\x32\x8e\xed\x0f\x75\xaa\x4e\x28\x74\xd8\x27\xea\x87\x44\x8b\x99\xa2\x9d\xde\xf6\x9e\xcf\x96\xe8\x59\xe9\xb0\x6c\x41\xb2\xbe\xf3\x11\xdf\xd3\xbe\xca\x54\xae\xf9\xfe\xd1\x26\xdf\x91\x5c\x83\xd6\x65\xc6\x02\x48\x5a\xd0\x58\x40\xaa\xa1\x1f\xb7\xd0\xf6\x90\x04\x83\xc5\x6c\xf9\x54\x66\x29\xc7\x9d\xf9\x30\x9d\x2f\x6b\x67\x9f\x2f\x21\xd1\x73\x84\x78\xf1\x02\xdd\x92\x18\x9d\x78\xd0\x7c\x65\x52\x77\xfa\xfe\xfd\x96\x49\x30\xed\xba\x7f\x70\x95\xa6\x4f\xd0\x81\xf6\xdc\x66\xe8\xa8\xeb\x3a\x0d\x0e\x23\xf2\x27\x3b\x22\xef\xce\x79\xb4\xdd\xdd\x6a\xc6\xa3\xdf\x65\xc5\x95\x86\x06\x66\x3b\x86\xcc\x45\xc1\xb5\x7b\xfe\x64\x84\xc6\xe3\x1d\x69\xb8\xc6\xb6\x15\x5b\xac\xcf\x97\x6b\xa7\x95\x40\xf8\x7d\x56\x3e\x96\x8e\xf1\xf2\xb5\x36\xa1\xd8\xda\xa1\x75\xcc\x93\x0d\xb7\x19\xf8\x14\xe4\xd8\xe8\x67\x8d\x1f\xe7\x25\x62\x15\x0c\x81\x10\x2f\xcd\x39\xe1\x4b\x0f\xfa\x60\x2c\xda\xda\xbc\x1c\x79\x4d\x00\x30\xc2\xbd\xf4\xea\xee\x48\x68\x9b\xcb\xbf\xf4\xea\xce\x28\x38\xcb\xb8\x75\x74\x84\x4e\x66\xae\xe0\xb7\xfd\xb0\x7e\xcd\x21\x63\x3c\x34\x22\x2d\x7c\xd5\x71\xb8\x19\x57\x46\x8c\x7b\xb7\x90\x5a\xb7\x7a\xd9\x18\xdc\xf6\x4d\x36\xb8\x69\x34\xd1\x92\x90\xbd\x6d\x06\x40\x89\x80\xf4\x10\x90\x01\x02\xa7\x14\x45\xee\xb1\x5a\x5e\x3a\x84\x38\xd7\xbc\xe1\x65\xeb\x1a\x6f\xd1\xe4\x77\xc5\xbe\xfc\xe1\x76\xcd\x0c\x7c\x75\xc5\x8f\xb9\xe6\x35\x2f\x5b\x17\xd2\x31\xc2\x0f\x2d\xc6\xf9\xf2\xf2\xe3\x27\x68\xbf\x5b\x9a\xde\x48\x06\xfa\xb6\x7a\x5a\x67\x1a\x52\x8c\x6f\xbd\xc9\x4c\x78\x3e\xfa\xd2\xd6\xc1\x62\x73\xc4\x4e\xbe\xd2\x6d\x21\x5c\xd2\xb1\xd8\xf1\xcf\xb5\x2d\xca\x30\x49\x73\xe3\xbb\xa2\x06\xf0\xcd\x8c\x8f\x68\x37\x9c\x06\xfa\x12\x26\xaf\x86\xf3\x40\xd7\xdd\x4b\x85\xaf\xb3\x95\x0a\x36\x49\x65\xbc\x9c\x77\xf7\x3b\xe1\x3d\x74\xd4\xe5\x7f\x0f\x7d\xd9\xff\x01\x88\xc3\x02\x4d\xb3\x9b\xeb\x9f\x64\x13\xd4\x47\xcf\xe1\xe9\xd3\x8c\x35\xf3\xc6\x39\x48\x74\x64\x54\xbd\x0e\x52\xcf\x02\x0e\x71\x1e\x19\x37\xd3\xbd\xf8\xaf\x0b\xce\x7f\xe5\x43\xa4\x33\xb6\x9e\xd5\xc6\xbd\xd5\x5d\xf4\x03\x2e\x3e\x66\xb2\x70\x7c\x4e\x68\xfb\x94\xde\x96\xce\xef\x3e\x87\xd8\xd2\xb3\xcf\xca\x69\xa9\xa1\x9a\x98\xd3\x13\xce\x9d\xe6\xe6\x34\x54\x6a\x7a\x4e\x47\x75\xdd\x79\xc5\x56\x14\xee\x4e\x3c\x1e\x74\xe2\xf1\x75\x3b\xf1\x78\xd0\x89\xc7\xbb\x75\xc2\xac\x2a\x69\xba\xca\xc9\xaa\x25\x5a\xf1\x6a\x55\xf2\xf7\xdc\xb0\x01\x11\xa9\xc3\xdd\x32\x1e\x9c\x5f\xac\x67\x35\x1b\x26\x11\x19\x20\x9f\x0c\x21\x3f\xbe\x3c\xb1\xe1\xf4\x50\x43\x7a\x3a\x74\x61\xeb\x79\xa2\x1b\xda\x35\x69\x8f\x5f\x6a\x0b\xa5\x21\x9c\x35\x87\x9d\xb6\x88\x10\x5b\x2e\xe6\xd4\x1f\xdb\xfd\x99\x4e\xb1\x7f\xde\xae\x79\xcd\xed\x9a\xfe\xae\x9b\x35\xfd\xb1\xad\x9a\xbe\x63\xa3\xa6\xff\x79\x9b\xe6\x4d\x6f\xd3\xf4\xb7\xdc\xa4\x69\x50\x4b\x67\x8b\xa6\xbf\xcd\x06\x4d\xdf\x7e\x0c\xbf\xd9\x78\x78\x97\x06\x1f\xde\x4c\x29\xfe\x17\xd9\xae\xd9\x2f\xb0\x13\x62\xf2\x87\xed\xe1\xac\xcb\xed\x08\x9a\x7f\xae\x72\x3b\xd7\xda\x6d\xa9\x1e\xb7\xbb\x3d\x6b\x98\x9d\x0a\xf2\x84\x98\x74\xb6\x85\x84\x98\x58\xb7\x99\xd0\x2d\x0b\xf2\x08\xc0\xce\x56\x13\xaa\xaa\x5a\x84\x98\xdc\xd8\x11\x62\xbd\xfb\xd6\x9a\x3c\x83\x4d\x0e\xde\x26\x4b\xd3\x34\xc9\xc3\x7c\xaa\x15\xec\xd9\x9b\x9a\x20\x23\x92\x30\x92\x10\xa6\x97\xf3\xd9\x33\xd4\xed\x31\x34\x4d\x70\x98\x78\x38\x64\x7a\xf5\x1f\x33\x11\x1c\x92\x82\x67\xb2\x66\x50\x5d\x1b\x68\x4b\x22\x51\xec\xfb\x24\x8a\x64\x59\x21\x55\x39\xc8\x4c\x84\xf2\x34\x08\x18\x8d\xf5\xba\x42\x5b\x12\xc9\x53\x2f\x23\xdc\xcb\xf5\x32\x44\x66\x22\x41\x9c\x86\x01\xc5\xb9\x5e\xa4\xa8\x97\x9a\xde\x74\x95\x22\x61\x4f\xd7\xac\x52\x84\xa3\xcf\x65\x8a\x6e\x28\x27\xa2\x3b\x97\x29\x12\x4d\xc6\xf2\x22\x3d\x66\x0c\x33\x23\xfa\xb9\x4c\xd1\xcd\xe7\x46\x74\xdb\x32\x45\x46\xe5\x74\xf3\x23\x3a\x5a\xa6\xc8\xa7\xee\x32\x45\x62\x18\xbf\x4b\x89\x29\x5b\x22\xff\x22\xd9\xd2\xbf\xf4\xe1\x96\x9b\x3d\xd8\xf2\x89\x8e\xac\x5c\x3f\x89\x92\x8f\x9a\xee\x2a\x44\x3f\xd7\x3b\x78\x0d\x77\xdd\x74\x37\xf9\x1e\xb2\xf3\xf3\xf9\xd5\x44\xfd\x38\x45\x6c\x75\x7a\x71\xc6\x17\xd5\xba\x7f\x27\x8f\x7e\x7c\xa6\xe5\x07\x4a\x29\xb5\x24\x7a\xe4\xbd\x4d\x40\x28\x23\x45\x02\x79\x45\x1e\x13\xca\x38\x21\x7b\xd3\x21\x5c\x8c\xfd\x38\x08\x12\x28\x33\x48\x7c\x5e\x44\x61\x96\xeb\xa9\xc1\xa0\x41\x1a\x66\x5e\x91\x66\x05\x5c\x80\x90\x05\xb9\x9f\x92\xc2\x84\x98\x27\x69\x98\xa7\x2c\x84\xdb\xb3\x31\x4d\xf2\x34\xcd\x9c\x88\xfd\x24\x8c\x32\x12\xa6\x90\xce\xf8\x01\x4d\x43\x9f\x9a\x10\x87\x49\x81\x31\x2e\x80\xe3\x34\xf2\xc2\xdc\xc3\x89\x13\x71\x42\xfc\x82\x12\x06\x57\x6e\xb3\x02\x27\x41\x91\xa4\x26\xc4\x2c\xc5\x59\xc8\x73\xe0\x38\x67\x51\x4e\x31\xa6\x4e\xc4\x39\xf5\x62\xc6\xa4\x8c\x99\xef\xf9\x1e\x09\x8c\x32\xc6\x84\xfa\x61\x2a\xef\x8c\x08\xc2\xd8\x8b\x8a\x94\x3b\x11\x93\xc0\xc7\x34\x4c\xe1\xee\x88\x80\xf3\x20\x25\x34\x33\x8a\x22\xf4\xb2\x38\xcf\xe0\x02\xf1\x3c\x2c\x8a\x34\xe0\xc4\x89\x38\x26\x29\x0f\xf3\x18\x44\x51\x90\x38\xa5\x49\x64\x54\x1e\xf5\x72\x9e\x62\x79\x79\x85\x9f\xe2\x28\x89\x52\xec\x96\x71\x9a\x67\x5e\x24\x2b\x54\x92\x30\x8b\x31\xf1\x43\x13\xe2\x0c\x27\x69\x81\x25\x03\x59\x11\x25\x24\x4a\x02\x27\x62\x1e\x24\x69\x94\x64\x20\xbb\x84\x17\x38\x60\xb9\x51\xc6\xbc\x48\x79\x10\x53\xb8\x46\xdc\xa7\x41\x41\x42\xee\x3b\x11\x7b\x45\x86\x93\x3c\x83\x06\x34\xa5\x59\x1e\xa6\x46\x8e\x49\xe0\x65\x0c\x67\x19\x5c\xd2\x1e\xb3\x2c\xc9\xa2\xd0\xad\xbc\x9c\x27\x24\x8b\xc0\x41\xc2\x84\xa4\x1e\x89\x8d\x88\x03\x16\x07\x34\x60\xf0\x8e\x10\x71\x16\xf1\x80\xba\x39\x0e\xb3\xd4\x63\x49\x0e\x9c\xa4\x79\x80\x8b\x34\x0f\x8c\x2e\x1d\x15\x09\xa5\x39\x20\xa6\x3e\xc6\xa1\x9f\xba\x39\x4e\xa8\xcf\x43\x1c\x12\x70\x69\x1e\x45\x79\xc1\xcc\x0e\x42\x7d\x9c\x45\x11\x64\xf8\x24\x4f\x03\x9f\x60\xcf\x1d\x2b\x3c\xcf\x27\x71\x46\xe5\x9d\xef\x45\x4a\xb0\x6f\x34\xb7\xb4\x08\x93\xb8\xc8\x54\x7d\x53\x5e\x78\x9c\xbb\xad\x22\x8b\xb8\xe7\xa5\x05\x18\xbe\x9f\x33\x4a\x8b\xcc\x68\x15\x79\xc8\xe2\x04\x07\x80\x38\xf1\x3d\xc6\x62\xe2\x16\x85\x17\x65\x2c\xf2\x43\x79\xbd\x8b\xe7\xf9\x94\x98\x1d\x04\x07\x24\x21\x89\x7c\xf7\xf2\x98\xc7\x23\x1e\xbb\x45\x41\xe2\x34\xf6\x18\x85\xe0\x12\x44\x39\x21\x45\x61\x74\x69\xc2\xb1\x10\x13\x88\x2c\xcc\x48\x94\x25\x24\x72\x22\x0e\x72\x92\x45\x79\x01\x56\x11\xb2\x2c\x20\x8c\xe7\xc6\x58\xe1\xfb\xd4\xcb\x31\x88\x2c\xc9\x93\x30\xf5\xf3\xc2\x89\x38\x0a\x3d\x16\xfb\x61\x20\x1d\x84\x15\x91\x9f\x73\xb3\xb9\x45\xcc\x63\x29\xc4\x6d\x3f\x8b\xe3\x94\x30\x77\xd8\xa4\x38\x23\x59\x42\x64\x74\x8b\x79\xce\x38\x8f\x4c\x88\x13\x12\x13\x92\x49\x91\xe1\x80\x12\x3f\xf4\x53\x27\x62\x46\xd2\x82\x53\x26\xe3\x6c\x56\x60\xcf\x8f\x8c\x0e\xc2\x28\x66\x51\x14\x00\xc7\x69\x16\x10\xdf\xf3\xdc\xd1\x2d\x23\x41\x4a\xd3\xd8\x83\x38\xeb\x15\x34\x89\x13\x6c\x8c\x6e\x71\x94\x85\x98\x81\x8c\xbd\x28\x0c\x52\xee\xbb\xad\x22\xc7\x09\xe1\x14\x27\x80\x38\xe2\x45\x48\xb0\x71\xcc\xcb\xa3\x24\xf1\x22\x02\xba\x08\xc3\x28\x64\xc9\x88\xe7\x15\x81\xc7\xfd\x50\xca\x2e\x8c\x63\x4c\x3c\xc2\x8c\x76\xec\x45\x8c\x79\xb2\x67\x3e\x49\xd3\x1c\xa7\x6e\xe5\xe1\x84\x05\x19\xc6\x10\x36\x53\x9a\x93\xdc\xcb\x8c\x1c\x63\xee\xc7\x51\xe6\x49\x3b\xc6\x01\x66\x69\xe8\x8e\x6e\x24\x0e\x68\x1c\x07\x60\xc7\x79\x41\x39\x4f\x93\xc4\x84\xd8\x0f\x52\x2f\xcd\x52\xe8\x19\xc7\x49\x1a\xd0\x11\x73\xf3\x13\x9c\x79\x59\x0a\x4a\xc9\xc2\x2c\x09\x59\xe4\x1b\xe3\x31\xcf\x29\x63\x01\x84\x4d\xee\x07\x98\xb2\xcc\x6d\x6e\x61\x9a\x64\x19\x0b\x0a\x39\x32\x44\x3e\xf7\x63\x23\xe2\x88\x12\x1e\x15\x32\x58\xe5\x51\x4a\x52\xca\xdc\xa2\x88\x03\x5a\x50\xc2\xc1\x41\xc2\x9c\x17\x29\x31\xc7\x8a\x98\xb2\x30\xf2\xe5\x48\x13\xf8\x38\x26\x45\xe4\xb6\x0a\x1a\x64\x34\xa6\x58\x66\x42\xb8\xf0\x58\x1a\x1b\xc3\x26\xcd\xb2\xd8\x23\x52\x79\x98\x45\x81\x9f\x70\x77\xee\x96\x78\x29\x2f\x8a\x82\xc9\x2c\x32\xf2\x31\x27\x46\xab\x60\x41\xe8\x45\x19\x07\xcf\xcb\x39\x25\x69\xce\xdd\xb9\x5b\xca\x8b\x84\xf9\x85\x1c\x19\x48\x16\xc5\x09\x36\xe7\x15\x51\x8c\x63\x5a\xc8\x21\xcc\x8f\x49\xe8\x13\xb7\xf2\x32\x46\x62\x9f\x67\x20\x63\xce\x48\x14\xe1\xc4\x28\xe3\x1c\xd3\x28\xa5\x72\x68\x22\xc2\x90\x48\x77\x12\x70\x98\x88\xb0\x9c\xc5\x79\x0e\x0e\x92\xe5\xdc\xe3\x29\x36\x86\xcd\x22\x8c\xf3\xa0\x88\x0b\x35\xe8\xf2\x1c\xc7\x6e\x3b\xf6\xa2\xc2\x8b\x62\x99\x2f\xc4\x04\xc7\x51\x91\x1a\x5d\xda\x63\x91\x1f\xe7\x19\x38\x08\x23\x19\x4d\x28\x73\x8f\x20\x18\xfb\x45\x42\xbd\x40\x4d\xdc\x25\x5e\xce\x8c\x1c\xe3\x34\xc6\x5e\xea\xcb\x78\xec\xe3\x2c\x88\xb1\x5b\xc6\x84\xe6\x69\x1c\x17\xa1\xb4\x0a\x2f\x88\x73\x6a\x8c\xc7\x3e\xc9\x18\x4b\x63\xb0\x8a\xc0\xcb\x62\x12\x24\x6e\x07\xf1\xb3\x84\xa7\xdc\x03\x51\xe0\x30\x4b\x52\x9e\x1a\x95\x17\xf8\x38\x8f\xe2\x0c\x7a\x96\x64\xd8\xf3\xf2\xc0\x6d\xc7\x41\x96\x85\x79\x20\x13\xef\x2c\xf5\x79\x40\x52\xe3\xd0\x24\xd2\x15\x92\x24\x10\xac\x8a\x2c\x0a\x63\x2e\xc2\xab\x2b\x56\x14\x59\x1a\x15\x4c\x0e\x92\x2c\x8f\x0a\xc6\x8d\x1c\x47\x59\x10\xe0\x84\x02\xe2\x80\x05\x71\x48\x71\xac\x26\x51\xdf\x38\x8e\xad\xb6\xef\x85\x3f\x5d\xf7\x84\xaa\xed\x1a\xb4\x9f\x3a\x27\x54\x7f\xbe\xde\x09\xd5\x10\x93\xed\x96\x0e\x0c\xcb\x11\x37\x5f\x7d\xf4\xba\x4b\x07\x11\xf3\x12\x5e\x4f\xb8\xfb\x69\x96\x25\x9e\x65\xe9\x20\x4d\xa3\x98\x71\x39\xfc\xd2\x20\x63\x2c\xee\xa6\x2e\x0e\x22\x7e\x16\xf1\xc2\x8f\x21\x92\x15\x3c\x09\x0a\x2a\x22\x99\x09\x92\x85\x41\x51\x84\x3e\x78\x41\x58\xe0\xdc\x8f\x8a\x6d\x67\xf5\x43\xec\xf1\x90\xc8\xe0\xc3\x72\x1e\x51\x92\x5b\x96\x0e\x92\xd4\x0b\x23\x2a\x0d\x92\xa4\x3e\x8f\x32\x5c\x6c\x49\x04\x17\xd4\xcf\x13\x69\xf3\x45\x1a\xe0\x34\x8f\x2c\x3d\x09\x53\xee\x65\xb9\x4c\x83\xb0\x1f\x73\x82\xe3\x64\x97\xa5\x83\x9b\x3e\x47\xba\x4d\x69\x58\x80\xf3\xec\x95\x5f\x1f\x61\x7b\xe9\xd7\x47\xc4\x5e\xfb\xf5\x91\x6f\x2f\xfe\xfa\x28\xb0\x57\x7f\x7d\x14\xda\xcb\xbf\x3e\x8a\xec\xf5\x5f\x1f\xc5\x96\x02\xb0\xb2\x83\x50\x1e\xd6\xb8\x0f\x5c\x3e\x9f\xcb\xe7\xc3\xc3\x1e\x52\x06\xd0\xdc\x78\x04\x4a\x3e\x9f\xcb\xe7\x96\xe6\x04\x9a\x13\x6b\x73\x32\x97\xcf\x2d\xcd\x7d\x68\xee\x5b\x9b\xfb\x73\xf9\xdc\xd2\x3c\x80\xe6\x81\xb5\x79\x30\x97\xcf\x2d\xcd\x43\x68\x1e\x5a\x9b\x87\x73\xf9\xdc\xd2\x3c\x82\xe6\x91\xb5\x79\x34\x97\xcf\x2d\xcd\x63\x68\x1e\x5b\x9b\xc7\x73\xf9\xdc\xb0\xad\x6f\xcb\xa2\xc7\xd2\x32\x4c\xc8\x99\x34\x8a\x7e\xc5\x3d\xd8\x72\x2b\x0d\xc2\xd4\x2a\x95\xb6\x60\x6a\x95\x49\x3b\x30\xb5\xca\xa4\x09\x98\x5a\xe5\x52\xfd\xa6\x56\xb9\xd4\xbc\xa9\x15\x97\x5a\x37\xb5\xe2\x52\xe1\xa6\x56\x85\x54\xb6\xa9\x55\x21\xf5\x6c\x6a\x75\x2a\x75\x6c\x6a\x75\x2a\xd5\x6b\x6a\x35\x93\xaa\x35\xb5\x9a\x49\xad\xce\x4d\x75\x07\x5d\x47\x77\xb7\xbc\x0e\xd5\x5a\x4f\xbb\xa6\xff\x53\x29\x6b\x0f\xdb\x8e\x9b\x3f\x80\x11\xbc\x5e\x3e\x1b\x82\x6c\x51\x28\x5a\x92\x11\x22\xf8\xa9\xac\x4f\x1b\xe8\x55\xa3\xd1\x97\x88\xbc\x01\x48\x73\x2d\xd7\x16\xc7\x5c\xe2\x50\xe7\x0b\xfa\x38\xe0\xd4\xfc\xb5\x2a\x50\x1f\x1d\xa1\xff\x80\x6a\xc4\x76\xe2\x75\x49\xe7\x9d\x2a\x54\x6f\x66\x4d\x9d\xe3\xcd\xd8\x59\x3c\x05\x36\xd7\x5a\xb8\xcf\xe3\x49\xa8\x59\xa7\x0a\xf6\x4c\x16\xff\xd5\x8b\x57\xcf\xa1\x44\x71\x5d\x0e\xb8\x03\x47\x07\x70\xb0\xe9\xf5\x2d\xea\x82\xc5\xae\x13\xa6\x12\x72\xde\xe1\x62\x3e\xe4\x62\x66\xe2\x62\x3e\xe4\x62\xa6\x73\xd1\x85\x8b\x87\x70\x96\x4a\xc6\xba\x4a\x2d\x35\x73\xde\x6b\xb5\xb7\x77\x29\xbe\xdd\x6a\x14\x6f\xa7\x51\xdc\x6a\x14\x6f\xa5\x51\x3c\xeb\x14\xf8\x9e\xd5\x55\xb8\xb5\xc2\xdc\x73\x55\xab\x5b\x13\x12\x56\x12\xee\x82\xc1\x3e\xe6\x44\x53\x69\x8d\x2f\x1a\x55\x29\x9e\x77\xd8\x98\x1b\xd8\x98\x99\xd8\x98\x0f\xd8\x98\x75\xd8\xe8\x22\x8c\x06\xf8\x48\xe4\xd4\xe9\x4e\xb5\xc3\x5d\xa1\x24\x6e\xd5\x1e\xbb\xd4\xfe\x53\x19\xcb\xc8\x65\x1c\x98\x7b\x90\x73\x05\xe9\x38\x13\x2e\x21\x71\xa4\x05\x12\xeb\xad\xd0\x35\xac\x64\x00\x1b\x33\x8b\x3e\xec\xbc\x86\x1d\xe5\xa1\x8d\x34\x73\x21\xb4\x32\xee\x8f\x5c\x5d\xf0\x36\x94\xcd\x24\xf8\x0c\x6a\xb6\x09\x3c\x42\x93\xde\x1e\xba\x5f\x7b\x67\xf3\xcb\xff\x8f\x30\xba\x8b\x06\xdb\xa6\x87\x7c\x88\x7f\x6b\x0d\x8e\xb3\x21\xfe\xdd\x6f\xbc\xc5\xc2\x05\xbe\x2e\x17\x20\xc5\x2d\x79\x90\xda\x19\x72\x20\x35\x31\xa0\x6f\x46\xda\x8e\x8a\x3f\x95\x36\xf5\xb6\xa3\xde\x4f\xa5\x89\x39\x7b\x4d\x7c\x55\x14\x7f\x86\x6e\xa3\x62\xa6\xca\xe2\x8b\x2f\xe6\x73\x7c\xb2\x8d\xf4\x7d\x3e\x17\x6d\xe6\xaa\x8d\xf8\x72\x3a\x77\x14\xd3\x9f\x41\x35\x7d\x81\x3a\x95\x74\xe0\x73\x26\x3f\xa7\xea\xb3\xbd\xf9\x1c\x9a\x0b\x2a\xa9\x24\x09\x9f\x33\xf9\x39\x55\x9f\xdd\x25\xf9\x67\xb2\x26\xbf\x0a\x38\x72\x5c\x61\x73\x59\x5e\x7a\x4f\x16\x3f\x60\xb3\xba\x62\xbf\x7a\xd8\xa9\xd9\x3f\xd3\x6e\x91\x60\xf5\xa8\xe3\xac\xcc\x0f\x6f\x53\x93\x06\x91\xa2\x39\xeb\xd2\x9c\x77\x68\xce\xba\x34\xe7\x3a\xcd\xd9\x36\x34\xb1\xec\x27\x57\x43\x83\x3c\x6f\xc2\xe5\xa0\x40\xeb\xb2\xff\xb3\xfa\xd2\x0a\xed\x61\xd0\x3e\x14\x34\xfd\xfa\x99\x2c\xc3\xed\xa6\x29\xfb\xa9\x80\x6b\x9a\xb3\x2e\xcd\x79\x87\xe6\xac\x4b\x73\xae\xd3\x9c\xb5\x34\x8d\x59\xe7\xf8\x3d\x04\x66\x5e\xbf\x87\xea\x4b\xdf\xdb\x0f\x53\x7d\x0f\xce\xfb\x7d\xe9\x3a\x46\xf5\x3d\x04\x83\xef\x4b\x5b\x08\x7d\x0f\x17\x25\x08\x98\xd9\xbc\x61\xd1\xe4\x94\x12\x50\x10\x9c\xb5\x7d\x91\xe1\xa2\xc2\x7a\xb8\x98\x6d\x13\xab\x5a\xb2\xe2\x5f\x21\x11\x37\xcd\x0a\x48\x65\x33\x13\xc1\xec\x5a\x14\xbf\x37\x86\x9e\x3e\xc5\xef\x4b\x13\xc5\xef\xcb\xeb\x50\x34\x07\xbb\x3e\xc5\x9f\x8c\x14\x7f\x32\x51\x34\x5b\x5b\xff\xf2\x0a\x0b\x49\x98\xbc\xa8\xdd\x1e\x00\xad\xdc\xc1\x3c\x48\x1d\x95\xf6\x65\x78\x04\x16\x89\xce\x62\x8d\x6b\x3b\x36\x7f\x3c\xcf\x59\xc5\xd1\xa5\xfb\x4d\x5f\xfc\xc1\xfb\xa6\xd1\xbe\xe1\x75\xf3\xd4\xc4\x36\x0c\x40\x85\xa9\x0d\xbc\xd8\x16\xa6\x36\xf0\x0e\xcd\x4d\x6d\xe0\x15\x9a\x9b\xda\xc0\x2b\xf9\x24\x9f\xc3\xf5\x1d\x73\xdb\xfd\x1d\xf0\x4e\x3f\xc9\x67\x00\x25\x45\xc7\x75\xc9\xe5\x03\xa1\x59\x6f\x02\x11\x98\x32\x13\x8f\x30\xa5\x90\x99\x78\x84\xd9\x8b\xd4\xd4\x06\x26\x2f\x52\x53\x1b\x98\x27\x61\xa6\x36\x30\x4d\x32\xb8\xcd\x40\xfc\xc1\xb4\xcb\x44\x9a\x7a\x45\xac\xc2\x80\x89\x9b\x89\x94\x83\xb0\xac\xfd\x76\xc4\x91\xd2\xa8\x86\xc9\xce\x8d\x5e\x56\xa2\xcd\x19\x42\x66\xf0\x08\xec\x9f\x0d\xb2\x81\x47\x4d\x31\x8a\xc9\x23\xb0\x7b\x26\x99\x7d\xe4\xe9\xdc\xb2\x21\xb3\x7d\x3c\xda\x2c\xa3\x24\x08\x22\x4a\x87\x04\x71\x4b\x10\xc4\x93\x2a\x82\x9d\x48\x90\x8e\x13\xd4\xe6\x25\x25\x41\x02\x21\x76\x48\x90\xb4\x04\xc9\xac\x1e\x97\x26\x00\xaf\x85\xd7\x71\x82\xda\x4c\xa6\x24\xe8\x0b\x82\xf9\x90\xa0\xdf\x12\xf4\x05\xad\x5c\x11\xf4\x47\xdc\xa1\x8f\x47\x9b\xfb\x94\x04\x03\x41\x90\x0f\x09\x06\x2d\xc1\x40\xd0\xe2\x8a\x60\xa0\x13\xe4\xe3\x04\xb5\xd9\x52\x49\x30\x14\x04\x8b\x21\xc1\xb0\x25\x18\x0a\x5a\x85\x22\x18\xea\x04\x8b\x71\x82\xda\xfc\xaa\x24\x18\xc1\x4b\xc5\x90\x60\xd4\x12\x84\xec\xfd\x54\x11\x8c\x3a\x2f\x11\xe3\x04\xb5\x19\x59\x49\x30\x16\x04\x67\x43\x82\x71\x4b\x10\x5e\x9b\xd4\x98\x2c\xe0\x5d\x49\xc0\x47\x9f\xbd\xf8\x7c\x29\xce\xcd\x5d\x8a\x83\x45\x72\xaf\x6e\x36\x13\xc8\xa0\x0e\x8b\xef\xdd\xf4\xb5\x38\x66\x32\xf8\x9f\xf2\x62\x9c\x93\xe5\xe2\x3d\x5f\xc9\x2a\xbf\xa8\x5a\x22\x9f\x1c\xa4\x65\x25\x12\x94\x1c\x31\xd8\x9f\x9d\xf2\x62\xb9\xe2\x6a\x3b\xf5\x40\x6b\xda\x59\x13\x6d\xed\xae\x5a\xbe\xf2\xc9\x4d\x5c\xc4\xf3\x67\xbd\x82\x47\xe7\xb3\xa9\x0f\x72\x17\x61\x8f\x04\x47\xbe\xaa\x53\xfc\xf9\x74\x93\xf5\xa8\x52\x88\xc9\xae\xa7\x9b\x44\x93\x91\xd3\x4d\x9d\x6d\x0d\x83\xd3\x4d\x21\x26\x9f\x4f\x37\xdd\xf4\xe9\x26\xa1\x95\xed\x4e\x37\x19\x95\xd3\x39\xdd\x24\x15\xe4\x3c\xdd\x24\xcf\xd1\x6e\x79\xfa\xdb\xff\x53\x9f\x67\xe2\x8b\xec\x20\x65\x6b\x1e\x05\xbd\x07\x67\x79\xd8\x07\x7d\x7f\xfe\x2e\x2f\x7a\x3f\x66\xe5\xf9\x8c\xaf\xfe\x90\x23\x51\x1a\xab\xf0\x5d\x70\x28\x1f\x48\xc6\xe0\xb3\xce\xcf\xbf\xc2\xd1\xa9\x9f\xb6\xba\x13\x08\x36\xcf\x9c\x40\xd7\x1b\x38\xed\xb7\xf1\xa3\x50\x47\x47\xe8\x19\x5f\x9d\xc1\x28\x7a\x32\x5b\x96\x19\x47\xb8\x7f\x6d\x8a\x68\xfe\xec\x04\x77\xcf\x2e\x85\xf1\x14\x05\xc9\x14\x05\x78\x8a\x7c\x7f\x8a\x48\x38\x45\x38\x9e\xa2\x64\x8a\x10\xd6\xb6\x1a\x85\x74\x8a\x42\x6f\x8a\x02\x32\x45\x7e\x30\x45\x24\x9a\x22\x4c\xa7\x08\x7b\x53\x44\x74\xb8\x64\x8a\x42\x3c\x45\x81\x3f\x45\x7e\x38\x45\x24\x9e\x22\x9c\x4c\x11\x16\xf8\x35\xb8\xc8\x9b\xa2\x90\x4c\x51\x10\x4c\x91\x1f\x4d\x51\xe4\x4f\x51\x18\x4e\x51\x10\x4f\x91\x9f\x68\x80\x3e\x9e\x22\xe2\x4f\x11\x0e\xa7\x28\x9e\x22\x14\x91\x29\x0a\x83\x29\x0a\xe0\x6a\x01\x1d\x50\x70\x42\xa6\x08\x07\x53\x14\x09\x40\x3c\x45\xa1\x3f\x45\x41\x38\x45\x7e\xac\x01\x92\x64\x8a\x08\x9e\x22\x2c\x48\x4e\x11\x22\x74\x8a\x88\x37\x45\x58\xb0\x23\xc1\xde\x38\xe4\x4a\xcc\x72\x25\x5d\xb9\x0a\x2e\x84\x1c\x45\xbf\x89\xf8\x3c\x45\x28\xd4\xb9\x55\x84\x45\xb7\x04\xb7\xc0\x90\xa7\x73\xe9\x2b\xc1\x09\xae\x04\x40\x34\x45\x7a\x77\x71\x24\xe5\x21\x04\x0c\xdc\xfb\x5d\x45\x08\x85\x0a\x01\x0b\xf9\xf9\xb1\x14\x6c\x18\xf6\xe4\x15\x78\x4a\x5b\xa1\xd4\x7e\xa0\x53\x10\xaa\x11\xa6\xe1\x0b\x95\x46\x52\xed\xa1\xae\x43\xa1\x02\x61\x0f\xc2\x2e\x84\x0e\x85\x60\xeb\xac\xa6\x73\x23\xd4\xc5\xd9\xc5\x9c\xc1\x35\x29\x22\xa9\x5c\xcf\xca\x62\x70\xc3\x13\x78\xc1\x77\x2f\x7f\x7e\xf1\xe8\xbb\x87\xf2\x4e\x29\x21\x31\x32\x45\xd0\x79\x21\x21\x2a\x2c\x52\xa9\x09\xa4\xab\x2c\x15\x2b\x75\x12\x65\xbd\x20\x10\xaa\xd3\x7f\xf1\xcd\xd3\x57\x7c\x8d\xd8\x22\x57\xb5\xd1\xcf\x41\xa5\xf2\x3e\x0d\x03\x1f\x02\xfe\xe7\x67\x5d\x7d\xf6\x52\x4a\x6f\xe3\xdd\x85\x97\x11\x4a\x3c\x6f\xda\x7f\x56\xbf\x2b\x48\x10\x03\x00\xe9\x00\x50\xcf\x23\x03\x10\x5f\x03\x19\x3e\x0d\xf4\xa7\x06\x02\x61\x97\x00\x31\x10\x88\xba\x4c\x9a\x40\xe2\x5e\x3f\x0c\x84\x68\x87\x91\x21\x8a\xa4\x4f\x65\x88\x82\xe9\x20\x26\x80\xb4\x2f\xad\x21\x48\xd6\x23\x33\x00\xc8\xfb\x5d\x19\x82\x70\x0d\x64\x48\xa1\xe8\x72\x39\x6c\x4e\x5d\xad\x31\x1d\xd5\x07\xa1\x23\x04\x7c\x3a\x62\x55\x41\x9f\x88\xc1\x2e\xa8\xdb\x6e\x22\x3a\x6a\x98\x31\x75\x19\x26\xa5\xa3\xfa\x4e\xe8\x88\xbe\x59\x9f\x09\x83\x49\xf4\xc9\x0c\x39\xc9\xe8\xa8\xc6\x73\x3a\x62\x35\x9c\xba\xad\xbb\xe8\xd3\x30\x68\xde\xaa\x2e\x15\x25\xb0\x59\x90\x44\x7b\x6a\x51\xa6\xdf\x01\x31\x52\x0f\xba\x58\x4c\x7d\x0c\x75\x10\xa3\x4d\xe8\x7c\x1a\x9e\xc7\x5d\x36\x1c\xbe\x81\x1d\xe6\x9f\xf4\x39\xb5\x06\x0a\xec\xd0\x68\xda\xed\x8c\xc1\x2a\x3a\x9d\xb1\xc6\x09\xec\xb0\x5f\xde\x03\xb1\x85\x0a\x6c\x0e\x05\x74\x54\x14\x98\x8e\x8a\x82\xd0\x51\xd5\xfb\xd4\xad\xb6\xa0\x87\xc2\x16\x2b\x5c\xe2\x8e\xa8\xcb\x84\x63\x3a\xa2\x0c\x4a\x47\x24\x99\xd0\x51\xd3\x62\xd4\xad\xd0\xb4\x2f\x6f\xc3\xe0\xd1\xa7\x32\x04\xc9\xa9\x4b\xa5\x9c\x8e\xb8\x50\xd1\xd7\xa8\x7e\x47\xd5\x74\x2c\xcb\x08\x3c\x8f\x06\x1e\xb6\x46\x10\x05\x63\x4d\x33\x1a\x05\xda\x22\x48\x4d\xc4\x33\x11\x09\xba\x44\x8c\x30\x61\x17\x8f\x91\x99\xa8\x8b\xc7\x08\x13\xb7\x30\x06\x2a\x7a\xb0\x35\x36\x4f\xfa\x24\x0c\x48\x58\xbf\x3b\xf6\x84\x43\x11\x32\x20\xc9\x3a\x82\x35\x00\xe4\x2d\x80\x35\x80\x48\x16\x0c\x8d\x8b\xbe\x56\xac\x79\x97\x53\x98\x98\x8e\xf4\x82\x50\x97\xb4\xfd\x3e\x09\x93\x6d\xd0\x9e\xde\x4d\xb6\x41\xc7\x05\x1e\xd1\x11\x43\x8d\xe9\xb8\xa1\x52\x3a\xa2\x94\x84\x3a\x94\xc2\xa8\xdb\x97\xd2\x3e\x07\xf6\x40\xe2\x74\x95\x9c\x8e\x18\x31\xef\xcb\xd4\x1e\x4f\xac\x16\xa4\xbf\x80\x18\x9e\xe2\x2d\xdc\x1e\x93\x2d\x9c\x09\xfb\x5b\x38\x3e\x0e\xb6\xb0\x67\x1c\x3a\x5d\x1f\x47\x63\x2e\x89\xe3\x91\x60\xa8\xa7\xe0\x66\x0c\xc9\x58\xb8\xc4\x6c\xcc\xef\x71\xba\x45\xb4\xc4\xd9\x58\x20\xc3\xf9\x16\xc1\x12\xf3\x2d\x42\x19\x2e\xfa\x1a\x32\x9a\xcb\x58\xa8\xc0\x78\xcc\x43\x31\xd9\xc2\x41\xb0\x3f\xe2\x65\x38\xd8\x26\xb0\x85\x5b\x84\x1d\x1c\x39\xa3\x1b\x8e\xb7\x08\x4b\x98\x6e\xe1\x8b\x38\xd9\xc2\xeb\x31\xdb\x22\x9a\xe2\x74\x2c\x82\xe1\xcc\x15\xc2\x70\x3e\x16\x16\xf8\x16\x61\x14\x17\xbd\x08\xb5\x4b\xaa\x82\xbd\xc0\x12\x8c\xcc\x2c\x93\x8e\x54\xb0\x35\x45\x91\xb8\x4d\xd8\x03\xed\xb9\x67\x78\x1e\xf6\x94\x33\x84\x88\x3a\x42\x33\xd1\x88\x3b\x10\xe3\xc3\xb1\x3d\x37\x69\xa9\xd8\x32\x93\xba\xa7\xb6\xac\xa4\xe5\x62\xc8\x67\xd6\x93\xe6\x10\x22\xef\x48\xcb\x96\x9a\x00\x06\x4b\x5a\xa2\xda\x9a\x25\xe0\xea\x1e\xa6\x63\xec\x13\x6a\x37\x14\x9f\x8e\x19\x4a\x40\xc7\x14\x1d\x52\x77\xe7\x23\xea\x36\xa5\x58\x7b\x3e\x7c\x4a\xa9\x5d\x74\x09\x75\x89\x8e\xd1\x31\xf3\x4a\xa9\xdb\x09\x32\xea\x36\x9d\x9c\x8e\x19\x06\xa7\x63\x4e\x50\xd0\x31\x13\xef\xa4\x15\x16\x23\xc0\x23\xee\x8a\xc9\x88\x85\x62\x7f\x34\x64\xe0\xc0\x69\xa9\x38\x1c\x75\x78\x1c\x8d\x46\x0d\x1c\xbb\x22\x31\x1d\xf5\x44\x9c\x8c\x86\x0c\xcc\x1c\xde\x88\xd3\x91\x70\x81\xb3\xd1\xa8\x85\xf5\x70\x60\x20\xc1\x47\x62\x2f\x2e\x46\x43\x92\x4a\x2d\x9c\xdd\xc4\x4e\xbf\xc2\x64\x3c\xb4\xf8\x8e\xc8\x81\x83\x11\xb7\xc6\xe1\x68\x6c\xc1\x91\xd3\x81\x71\x3c\x1a\xdb\x30\x1d\x09\x3e\x38\x19\xf5\x40\xcc\x46\xc2\x00\x4e\x47\x63\x20\xce\x46\x43\x01\xce\x47\xe3\x11\xe6\x8e\x60\x87\x8b\x6e\x34\xda\x25\x7f\xa0\x9e\x24\x69\x8e\x2d\x75\xf6\x89\xbd\xc0\x92\x4a\xd4\x4c\x1b\x9e\xfb\x2d\x86\xc0\x6c\x88\x81\xdd\x88\xc2\xae\x44\xcc\x39\x44\x93\x1c\x9b\xc8\xc7\x5e\x27\xfd\xb3\x8f\x9f\xf5\x8a\x8a\x39\x83\x68\x75\x6b\xce\x1f\xe4\x73\x73\xee\xd0\x8a\xcf\xb6\x82\xd2\x8a\xc7\x80\x23\xd7\xbc\xd4\x92\x39\xd4\xe6\x6d\xce\x1d\x5a\x05\x5b\xfa\xef\xd4\x2f\xa6\xf6\xee\x11\x3a\xc6\xbc\x4f\xc7\x04\x10\x50\xb7\x8a\x43\x3a\xd6\x85\x88\x5a\xed\x27\xa6\x63\xc6\x47\xa9\x4b\x7e\x49\x97\xb8\x2d\x89\x70\x58\x47\x4a\x5d\xda\xcb\xe8\x98\xf5\xe5\xd4\x6d\xbf\x9c\xba\xdd\xaf\xa0\x63\x1e\x82\xbd\x11\x17\xc1\x78\xc4\x0b\x31\x19\x75\x43\xec\xbb\x46\x0a\xa7\x85\xe3\x70\xd4\x45\x70\xe4\x8d\xe9\x09\xc7\xa3\x91\x0c\xd3\x51\x6f\xc1\xc9\x68\xb8\xc0\x6c\x34\xe0\xe1\x74\x24\x66\xe2\x6c\x34\x6e\xe0\x7c\x24\x2c\x61\xee\x88\x4b\xb8\x70\x86\x0d\x99\x3d\xb8\xfb\x80\x47\xfd\x12\x13\xbb\x63\x62\x7f\xc4\xed\x71\x30\x62\xf8\x38\x1c\xf5\x1d\x1c\x8d\x47\xb7\xd8\x11\xde\x30\x1d\x77\x9e\xc4\x19\x3f\x30\x1b\x8d\x7f\x38\x1d\x0d\xa2\x38\x73\x06\x11\x9c\x8f\x46\x29\xcc\x47\xc2\x14\x2e\xba\x71\x64\xb7\xe4\xc1\x18\x53\x6a\x7e\x6d\x2b\x24\x0d\x37\xc6\x94\xe1\xae\xb6\x5d\xc3\x98\x31\x28\x00\x98\x4f\x31\xe6\x0d\x4d\xce\x67\x78\x1e\xd5\x08\x6c\x00\x71\xcb\xa0\xe1\xa9\xae\x73\x5b\xca\xd0\xf2\x67\xc9\x19\xda\x1e\x1a\x28\xa4\x2d\x83\x66\x16\xb2\x0e\x80\x69\xe0\xb0\xfa\x1e\xd7\x95\x63\x40\x5d\x74\x84\x63\x9e\x73\x70\xb5\xc7\x74\x44\xb8\x84\x7a\x36\xc3\xf1\xa9\xdb\x70\x02\xea\x32\x9c\x90\x8e\xd8\x45\x44\x47\xa4\x16\xd3\x11\xd3\xa3\x74\x44\xb5\x09\xb5\xc9\x9d\xd1\x11\x9d\xa6\xd4\x6d\xb5\x19\x1d\xb1\x9a\x9c\x8e\x68\x8e\x53\xb7\xe1\x16\xd4\x65\xf6\xd8\x73\xba\x2d\xc6\x9e\x55\xaf\x98\x8c\xf9\x34\xf6\xc7\x7c\x12\x07\x23\x5e\x8d\xc3\x31\xa7\xc0\xd1\x58\xe4\xc0\xf1\x88\x6f\x37\xe3\x9e\x55\x8d\x38\x19\x73\x20\xcc\x46\xe2\x23\x4e\xc7\x22\x08\xce\x9c\x11\x0a\xe7\x63\x11\x06\x73\xfb\xe0\x5c\x8c\x44\x08\xc8\x0f\xdc\xba\xc2\x23\x96\x86\xc9\x88\xa7\x63\x7f\xcc\x99\x71\x30\xe6\xac\x38\x1c\x0b\x55\x91\x3d\x14\xe1\x78\x2c\x58\x60\xea\x76\x97\x64\xcc\xe1\x31\xb3\x06\x0b\x9c\x8e\xf9\x32\xce\x46\xc2\x05\xce\x9d\xc1\x12\xf3\xb1\x50\x86\x8b\x5e\xc0\xd9\x25\x2b\x50\x6c\x53\x53\x14\xa9\x71\x9a\xf2\x02\xd9\x96\x98\xfb\xec\xb7\xcf\x89\x09\x77\xd0\x4a\xc4\x88\x3f\xd4\xfb\x63\xca\x0a\x9a\xa7\x43\xdc\x71\xc7\xa0\xad\xa3\xa2\x31\x1b\xd0\x98\x1a\x22\x66\x35\x59\x23\xcb\xa9\x32\x50\x53\x06\xa0\xc9\x6a\xf8\x3c\xd7\xd0\x0e\x9f\xf2\xa6\xaf\xc3\x67\x45\x47\xca\xa6\x9e\x3a\x95\x84\xa9\x5b\x49\x84\x5a\x7a\xe4\x53\x97\x76\x02\xea\xea\x4f\x48\xdd\x56\x17\x51\xb7\x65\xc4\xd4\x2e\x0f\x4a\x5d\x76\x91\x50\xbb\x3d\x33\xea\x56\x7d\x4a\xdd\x3a\xcc\xa8\xc5\xa6\x72\xea\x56\x11\xa7\x2e\x9b\x2a\xa8\xdb\x94\xb1\x37\xe2\x47\x18\x8f\x18\x1f\x26\x23\x9e\x8a\x7d\x87\x01\xe2\xc0\xe9\xa7\x38\x1c\x71\x45\x1c\x79\x23\x31\x28\x76\xfa\x5c\x93\xc1\x5a\x78\x4f\xac\x51\x9b\xd9\xbc\x15\xa7\x23\xa1\x0d\x67\x8e\xb8\x88\xf3\x91\x18\x82\xf9\x88\xcf\xe2\xc2\x19\xdc\xc4\x88\x6e\x61\x1c\x3b\x4d\x09\x13\xa7\xd3\x62\x7f\xc4\x2f\x71\x30\xe2\x98\x38\x74\x78\x26\x8e\x46\x62\x0d\x8e\x47\x83\xd5\x88\x27\xe1\x64\xc4\x47\x31\x73\x04\x00\x9c\x3a\xa3\x16\xce\x9c\xa1\x05\xe7\x36\xff\xc7\x7c\xcc\x85\x8b\x6e\xe8\xd9\x7d\xe8\x36\xd8\x48\xcd\x6a\xe0\x61\xc3\xd0\xad\x52\x0d\xc3\xa0\xad\x90\x9a\x9a\x05\x4d\x92\x63\x7a\x1a\x5a\xba\x1f\x49\x94\x86\x31\xba\x4d\x99\x86\x4f\xa9\xd6\x01\xd3\x30\xdd\xf4\x7d\xd8\x94\x69\x46\x3e\x7c\x9a\x6a\x9d\x30\xbd\xaa\x6b\x79\x9c\x61\x98\x96\x72\x1b\x62\xe5\xad\xdc\x4c\x2f\xe9\x5a\xe6\x3b\xec\xa9\x4b\x0c\x98\x9a\x85\x4a\xa8\x4b\xbf\x3e\x75\xf5\x31\xa0\x0e\xc3\x09\xa9\x4b\x78\x11\x75\xf5\x24\xa6\x36\xf1\x50\xea\x30\xab\x84\xba\x54\xcd\xa8\x4b\x23\x29\x75\x18\x42\x46\x6d\x66\x9e\x53\x97\x25\x73\x6a\xb6\xd8\x82\x3a\x94\x8c\x3d\xa7\x96\x31\x76\xba\x2b\x71\xfa\x2b\xf6\x9d\xbe\x82\x03\x97\x3b\xe0\xd0\xe9\x4a\x38\x72\x3a\x04\x8e\x5d\x11\x41\x8d\x37\xc6\x47\x89\x33\x5a\x60\xe6\xf2\x18\x9c\x5a\x82\x06\xce\x6c\x41\x36\x77\x7a\x2e\xe6\xce\xa0\x80\x0b\x6b\x44\xc4\x9e\x53\xeb\xd8\xe9\x88\x98\xb8\xbd\xdb\xb7\x58\x1a\x0e\x9c\x8e\x86\x43\x97\x0b\xe3\xc8\xea\x87\x38\x76\x46\x06\x4c\x9d\xde\x8f\x13\xa7\x2f\x62\x66\x09\x56\x38\x75\xba\x1b\xce\x5c\xd1\x01\xe7\x56\x2f\xc6\xdc\x19\x39\x70\xa1\x05\x87\x5d\xc6\x54\x2a\x06\x78\x62\x40\xd8\x08\x67\x18\x8f\xef\xb6\x8b\x1b\xc3\x70\x2c\xdb\x0d\x03\xb1\xc2\x67\x78\x14\x4a\x7c\xc4\xc8\x47\xd4\x3c\x34\x05\x61\xc5\x89\x79\x9c\xa1\x9e\x99\xff\xa4\xe9\xb7\x29\x04\x4b\x3e\x4d\x8f\xd2\x06\xa9\x81\xcf\xec\xae\x3c\xec\x31\x0c\xbf\x66\x3b\xe1\x8d\x10\x0d\x6d\x0a\xc5\x84\xe1\x51\xbd\xa8\x64\xed\xb9\x7c\x8c\x5d\x32\x55\x30\xc4\xa5\x7f\x05\xe3\xbb\x74\xad\x7e\x0f\x5c\xc2\x56\x30\xa1\x5d\xac\x0a\x22\x1a\xed\x73\x6c\x31\x2d\xf5\x98\xba\x24\xaa\x60\x12\x9b\x96\xd4\x73\x66\xb7\x52\x05\x91\xba\xec\x51\xc1\x64\x66\x95\xab\xa7\xb9\xcb\x8c\x14\x0c\x77\x99\xa8\x82\x29\xec\x1e\x5a\x67\xc4\x46\xc7\xc6\xae\x1e\x60\x62\x11\x32\xf6\x6d\x16\x87\x03\x17\xb3\x38\x74\xa9\x05\x47\x2e\x61\xe0\xd8\xd1\x45\x5b\xfc\x4d\xec\x2a\xc4\xcc\x65\xa9\x38\x75\xc6\xc3\xcc\xe5\x51\x38\xb7\xdb\x37\xe6\x36\xa3\xc3\xc5\xb8\x77\xb5\x2f\x37\x56\x08\xec\x8e\x05\x98\x8c\x1b\x1c\xf6\xc7\xbc\x0f\x07\x4e\xef\xc3\xe1\x78\x10\xa8\x95\xed\xec\x6e\x3c\x1e\x94\x30\x1d\x0f\x6e\x38\x19\x8f\x06\xb5\x39\xb8\xbc\x4c\x1a\x85\xf5\x69\x36\x16\xd6\xa4\x61\x38\xf8\xe4\x63\x11\xa7\x36\x12\xa0\xa2\x8d\xec\xf2\xa3\x5e\xd7\xe0\x09\x5b\xbf\x5b\xa3\x6a\xc6\x2a\xb4\xe6\x73\x9e\x55\x50\x8f\xe8\xc5\x37\x4f\x5f\xa1\x72\x71\x5e\x5f\x13\xd1\x54\x34\x78\xf2\xf5\x8b\xde\xc5\xc5\xed\xc1\xc4\x29\x6a\x37\xfe\xc3\x05\x8a\xea\x0b\x7c\x56\x5f\xa6\x7a\x43\x4f\xfd\x2a\x01\xe4\x97\xfa\xb3\xf8\x32\xd5\xfa\xd3\xe7\x5c\xab\xaa\xf4\xed\x83\x17\xb2\x30\x16\x92\x85\x5f\xdc\x77\x54\x09\xe8\xe6\x82\x2a\xf9\x45\xab\x92\x72\xdd\x2b\xaa\xdc\xa5\xf5\xde\xf1\xab\xa6\x04\xd8\x3b\x7e\x65\x28\x7d\xf7\x8e\x5f\xd5\x75\xf5\xde\xf1\x2b\x73\x59\x3d\x41\x43\xaa\x28\x8c\x50\x5a\x56\x6b\xc4\xb2\x6c\xb9\xca\xcb\xc5\x29\xaa\x96\xe8\xd9\x09\x36\xe2\xfd\xa6\x84\x52\x40\xaf\xfb\x35\x90\x4d\x77\x87\x84\x91\xfd\xee\x90\x16\xdd\xb3\xa5\x40\xf8\xec\x04\xbf\x2e\xdf\xa0\x03\x84\x0d\x35\x4a\x15\x5d\x59\x9e\x7f\x52\xf7\xee\x75\xdb\x5e\x95\xe3\x13\xff\x99\xf8\x18\x1d\x68\xa8\xa1\x0e\xdf\x1e\xba\x3d\x40\x6c\x28\x58\xfa\xf5\x7a\xcd\xcf\xd2\x39\x47\x38\x42\xeb\x8b\xf4\x1d\xbf\x32\x88\x7f\x7d\x91\x7e\xcf\xaf\xd6\x8d\x0a\xda\xef\x76\xa1\x2c\x5e\x00\x90\x14\x4d\xfd\xe5\x3e\xc2\x51\xf3\xcd\x7e\xc5\xca\x09\x54\x9c\x52\xfc\x98\x05\xb9\xae\xb1\x2b\x5e\x5e\x2b\xa4\x6f\x14\x53\x46\xbc\xee\xab\x5b\xd2\xb2\x7a\x01\x55\x51\x8e\xb5\x22\x28\x0d\x5e\x1b\x4a\x69\x50\x01\x35\x1a\x14\x19\xb6\x31\x59\x0d\x09\xec\x56\xd3\xa5\x53\xac\x96\x67\x10\x60\xe6\xbc\xa8\x10\xa1\xe0\x19\x82\xb2\xb9\xa1\x14\xce\xeb\x49\x89\x8e\xe4\xdd\x10\x1e\x14\x70\xac\x8d\x6b\x32\x79\x76\x42\x94\x0d\xee\xa1\xfd\x46\x02\x7b\xe8\x6f\x88\xd0\x37\x50\xe3\x11\x6c\xab\x44\x7f\x83\x3b\x2e\xb6\x66\x6f\x55\x9e\xce\xb6\xe7\x2f\x80\xf2\x9d\x2d\x93\x7b\x1d\x2e\x09\x85\xc7\x92\x57\xb4\x8f\x48\x60\x61\x78\xcf\xc0\xf1\x80\xac\xa9\xb2\xbf\xe8\x40\xb9\xc8\x38\xe2\x2c\x9b\x29\xb3\x43\xe5\x1a\xb1\xf3\xf3\x79\xc9\x73\xa1\x4b\xb6\x40\x7c\x73\xce\x16\x39\xcf\xeb\xba\x8c\x10\xde\xa7\x46\x6c\x42\x04\x0a\x4d\xc6\x16\x28\xe5\x28\x5d\x2d\xdf\xf1\x05\x2a\x17\xd5\x12\x51\x59\x14\x78\x8d\xd6\x19\x9b\x4b\xf4\x12\xe5\xda\x8c\xed\x72\x56\x66\x33\xc4\xe6\xf3\xe5\xe5\x1a\x50\x0b\xbc\xd5\x52\xa0\xbd\x58\xf3\x1c\x5d\x96\xd5\x6c\x79\x51\x49\x06\xd7\xe5\x72\x31\xc4\xa2\x04\x0d\xe5\x35\x27\xed\x97\xfb\xf7\xd5\xb5\x32\xed\x4f\x22\xa0\xf8\xd8\x24\xb9\x8e\xe5\x62\x69\xb9\xb1\xdb\x70\x15\x5a\x08\x62\xed\x67\x88\x59\x93\x52\x2a\xf1\x4b\x24\xb4\xef\x9b\x55\x65\xeb\x47\xac\xf7\x23\x7e\xa3\x0a\x7b\xfe\xa6\xff\x04\x97\x02\x0c\xae\xda\x31\x44\xc0\x13\x59\xf8\x12\x95\x8b\xf7\x7c\xb5\xe6\xf6\x28\x58\x2e\xde\xbf\xe8\x05\xc2\xce\x4f\x5b\x0d\x10\xd8\x31\x40\xb4\xd8\x74\x89\xad\x5f\xe3\x50\x18\x74\x1f\xfb\x87\xce\x84\x43\xfb\x85\x2f\xb2\xd5\xd5\x79\xb5\xc3\x55\x80\xaa\x62\xed\xf2\xa4\x69\xd7\x02\x4f\xbb\x21\xdf\x5a\x42\x37\xe7\x9f\x82\x6a\x2b\x11\x57\xed\xde\x13\x37\xe5\x69\x2d\x48\x53\xd2\xf1\x1f\xbc\xd2\xf3\xb4\x2e\x73\x73\x40\xaa\x5d\x8d\xd5\xd7\x81\x04\x5b\xf5\xc1\xe0\xe6\x2c\x43\xf6\xf1\xdd\xa2\xac\x4a\x36\xd7\x4b\x5f\x75\x61\xf8\x26\x9b\xb1\xc5\x29\x7f\xfc\xbc\x2d\x8b\x2a\x2b\x8f\x79\x1b\xaf\x90\xff\xeb\x9b\xb4\xb9\x8d\xbc\x9f\x1a\xde\x58\x8b\xc2\xda\xe6\xf9\x63\xbd\x0d\x01\x3a\xbe\xfa\xdb\xae\x0d\x95\xbc\x79\x45\x21\xfe\xbf\x25\x6f\xd0\x26\x54\x7f\xc6\xca\xb4\xae\xab\xda\x64\xf9\x30\xf0\x28\xf9\x51\x7a\x15\x7c\x1e\xbf\xb6\xcd\x30\x12\x19\xf3\x09\x40\x67\xbb\xf6\xa2\x31\x0c\xdd\x4e\x2c\xb0\xab\x2e\xec\x4a\xc1\x1a\x99\x7c\xc8\xcb\x75\xc5\xe7\x8d\x15\x9b\x31\x16\xd0\xf9\xed\x52\x0b\xea\x0e\xd0\x85\x18\x68\x65\xa9\xb5\xd7\xe5\x9b\xd7\x93\x89\xe2\xf6\x6d\x1b\xae\x45\x22\xd9\xbc\xba\xc0\x77\x28\xab\x6d\x12\x8d\x21\x60\xf7\x1c\x69\x65\x93\x54\xcf\x93\xe6\x35\x1b\xc5\x78\x00\xff\x71\x91\x2f\xd1\xfa\x92\x9d\xcb\xf4\x63\xce\xd6\x95\x34\x86\x61\x08\xaf\xdc\x2a\xeb\x31\xdb\x55\x98\xcb\xf1\x2b\x83\x0d\x43\x45\xf1\x5d\x5d\x7d\xe0\x1a\x37\xe6\x82\xd7\x71\xf5\xeb\x84\x94\x91\xd0\x65\x78\x23\xab\xd0\xf2\xa2\x1a\x44\xe0\x26\xe4\xba\x55\xd6\x09\xb9\x76\x9d\x75\x86\x8c\x77\xfc\x4a\x96\x80\x8e\x82\x23\x9f\xe8\x4f\xca\xf7\x96\x07\x5a\xdd\xe8\xc8\x58\x35\xfa\x08\xbd\x10\x16\xa8\x5e\x02\x56\xcb\xf5\xba\x4d\xd3\xa1\xe6\x21\x24\xc4\xf0\x5a\x2a\x5b\x34\x03\x55\x2b\xb8\x49\x3d\x5e\x9d\xb1\xf5\xbb\x8e\xcb\xd6\xb6\x3b\x99\x74\x4c\x54\x38\x62\x3d\xba\xbe\xed\x74\x5d\x38\xad\xc0\xa2\x89\xa0\x63\xb2\x6f\xc1\x66\xbf\x30\x1a\xbe\x78\x26\x32\x2a\x89\x59\x41\xd5\x7e\x37\x60\xfb\xf9\xe3\xed\xd9\x5e\xd9\xd9\x9e\xbb\xd9\x9e\x3b\xd8\x5e\x6d\xc1\xb6\xb3\x88\xf4\xba\xae\x22\x2d\xa7\x3f\xb6\xab\x23\x3d\x56\x84\x59\xe2\xaa\xf8\xa6\xd2\x4b\x31\x7f\xfb\xe0\xc5\xa1\x4a\xd0\x3a\xb5\x98\xa7\x28\x2b\x4e\x0d\xc5\xb5\xcf\xe7\x4c\x30\xb1\xa9\x50\x1f\x8b\x4a\xb8\x26\x2d\x1d\x13\xa2\xa6\xb2\xf3\x70\xa2\xa6\x5b\x74\xfb\xdb\x07\x2f\x8c\x15\xb7\x5f\xae\xca\xf3\x39\x3f\xd8\x6d\x8a\x48\x36\xea\x4c\x14\xe9\x3f\xfd\x79\xa6\x8b\xd4\x44\x84\x60\xbb\x84\x0a\xa5\x59\xff\x7a\x20\x95\xc5\xf2\x35\x46\xc7\x02\xee\x50\x4a\xf5\x81\xd4\xf1\x72\x35\x69\xef\x59\x57\x17\xc7\xd7\xa4\x0f\xd7\xf3\x32\xe3\x13\x6f\x8a\xc8\xde\xe0\x2e\x8c\x06\x2d\xb9\x26\x5a\x32\x45\x81\x03\xad\x7f\x4d\xb4\xc1\x14\x45\x7b\xf6\x8b\x34\xae\xfd\xee\xc1\xd7\xf8\x50\x6f\xac\xb5\xb0\x4a\xe6\x50\x7f\xe7\xd8\xa2\x81\xbf\x05\x85\x9b\x79\xa7\x11\xb4\x76\x64\x8e\xec\xda\x7d\xbc\x05\x05\xf3\xa8\x87\x13\x72\x63\xc3\xde\x3f\x49\x58\x6d\xa2\xcb\x0d\x04\xd7\x16\xd7\x8e\x21\xd6\x16\xe2\xba\x81\xb6\x81\x72\xd6\xcf\x6f\xa0\x7a\x25\xf4\xb5\xc2\xec\x77\x43\x32\xed\x55\xd5\xd7\x8a\xbb\xdf\x0d\x83\x69\x5b\xd5\xfd\x6e\x18\x4d\x55\xb1\xf7\xbb\x11\xfe\xf0\x66\x4a\x83\x8f\x2a\xb8\xff\x47\x56\xda\xff\x64\xf5\xf0\xff\x7b\x2a\xdb\xc3\x4d\x05\xe5\x82\xe7\x37\x5b\xe2\xfe\x1b\xb6\xe6\x6d\xd5\x7a\xb6\xe6\xda\xb3\x57\x3e\x71\x56\xc0\x1f\xfa\xf2\x26\x0a\xd0\x82\x9d\xf1\xf5\xb9\xee\xa5\x47\x3a\x1b\x02\x44\xb0\x21\xff\xfb\x8f\x0f\x26\x34\x5f\xa3\x28\x68\xae\xb0\x31\xa1\x79\x15\x05\x82\x0f\x60\x6a\x13\x05\x87\xea\x8b\xe0\xdf\x90\x19\xb4\xa8\x25\x7a\x35\x9d\x52\xfe\xca\xd7\x88\xa1\x05\xbf\x9c\x5f\x21\xe9\x6b\xb9\x89\xb0\x1e\x50\x50\xe7\x36\x8f\xc5\xc5\x59\xca\x57\x1f\x10\xdc\x2a\x05\xb7\xaa\x88\x0f\x3e\x81\x74\xfe\xd0\xd9\x64\xbe\xbc\x84\x16\xe2\xbf\xa6\x06\xdd\xc6\xdd\xe8\x36\x04\xa8\xe5\xb2\x69\xe5\x52\x47\x84\x5a\x3c\xf5\xc0\x2c\x57\xff\x3c\xe2\xf9\xf0\x56\x16\x78\xa1\x17\x79\xdd\xf9\xce\x5a\xd2\x10\xe2\x17\x65\x27\xa3\x12\x3d\x9c\x0a\xae\xcd\x63\x98\xba\x5f\xcb\x70\xab\x27\x3c\x16\xbd\x3d\x46\xdd\xdb\xb7\xf5\x37\xf3\xbe\xa6\xbe\x29\xab\xcb\x72\xcd\xd1\x0f\x4f\x5f\xae\x01\xc3\x98\x62\xea\x8b\x52\x94\x81\x7c\x40\x5f\x0b\xfd\x0a\xb9\x1c\x80\x60\xd4\x48\xc2\x8a\x8a\xaf\xd0\x82\x9f\xb2\xaa\x5c\x9c\xde\x80\xe0\x01\x15\x17\x82\x57\x2a\x38\x5c\x2c\xab\x89\x55\xaa\x47\x47\x68\xb1\x1c\xcd\x54\xe1\x4e\x16\x29\xd0\xdf\x1b\xe9\xde\x33\x82\x49\xc1\xfe\x5e\x0b\xd9\x90\x92\x2a\xc9\x28\xc1\xd4\xd6\xd0\xaa\xf3\x5e\x87\xbb\x4e\x06\x60\xd3\xca\xd7\x3f\x7c\xab\x69\x05\x96\x13\x60\xdc\x3e\x67\x6b\x58\x5e\xd8\xca\x87\x1a\x4d\x01\x0e\xe1\x12\x8d\xb2\xaa\xa5\x20\x51\xe3\xbd\x61\xe5\x7f\xfd\xc3\xb7\x37\xa3\x7a\xb9\xb6\xd3\x2a\x9e\x2d\xf2\x09\x5b\x2c\xab\x19\x5f\x29\x46\x5c\x66\xc0\x16\xb9\x6e\x06\xa2\x87\x23\xa6\xd0\xfa\xd9\x6d\x29\x90\x31\xab\x68\x3c\x4f\xc1\xff\x61\xf6\xf1\xf4\xf9\xa7\x36\x8f\xa7\xcf\x3f\x91\x75\x3c\x7d\x7e\x33\xc6\xb1\x5c\x75\x6c\x63\xb9\xda\xc1\x34\x96\xab\x6b\x5b\xc6\x6f\x3b\x5a\xc6\x6f\x7f\xb0\x65\xbc\xfa\xf4\xa6\xf1\xea\x93\xd9\xc6\xab\x9b\x32\x8e\x4d\xcf\x3a\x36\x3b\x99\xc7\xe6\x23\xec\xe3\xed\x8e\xf6\xf1\xf6\x0f\xb2\x0f\x58\x94\xd7\x2d\x63\x21\x67\x46\xd5\x0b\xe1\x9c\x17\xd5\xf6\x59\xd9\x02\x6c\x42\x7e\x43\xcb\xa2\xc1\x04\x57\xd8\xdc\x94\x31\x00\xb2\x9b\x31\x07\x40\xd5\x31\x08\xf8\xe5\xf1\x84\x84\x2e\x3b\x90\x40\xba\x29\x2c\x4c\x76\x20\x5e\x81\x16\xe8\x3e\xf2\x89\x6d\xa5\x4b\xb3\x94\x49\x6b\x2a\xf7\xef\xa3\x05\x2c\x91\x37\xc6\x20\xb7\x0e\x11\x74\x80\x16\xc6\xcb\xea\xcd\x26\x24\xf0\x0c\x6d\xed\x03\xaa\x5f\x9e\xdc\x0c\xe9\x68\x26\x0b\x74\x60\xb8\x31\x74\x40\xba\xbf\xd4\x25\xc8\xfd\x77\x5a\x2f\x4c\xe5\xff\xdb\x99\xef\xf3\x89\xfd\xe5\xa2\xb6\xde\xe7\x37\x64\xbd\x52\xef\x5d\x4b\xd5\x8c\xb7\xb6\xe7\x2d\x8c\x77\x10\x31\x01\xd5\x35\xec\x57\xf3\x82\x06\xcf\xb8\x01\x2b\xf2\x7f\xb8\x05\x3f\x5f\x56\xac\xe2\x9f\x3a\x00\xaf\x80\xca\x4d\x99\x30\x60\xbb\x19\x13\x96\x8c\xe9\x26\xbc\x5a\x8e\xc6\x5f\x01\x32\x6a\xbf\xaa\x47\x60\x07\x2a\xaa\x2f\xf6\x44\x3a\xd8\xfe\xf2\x7c\x12\x05\x03\xb3\xfc\x58\x85\xdd\x50\xcc\xf9\x73\x69\x6c\x24\xe4\x08\x88\xdd\x15\xf6\x7c\xa0\xb0\xc7\xd7\x51\xd8\xd7\x79\xfe\xa9\x33\x5f\x96\xe7\x9f\x28\xf3\x95\x57\x7e\xdf\xc4\x3b\x73\xde\x7b\x67\xce\x77\x7a\x67\xce\xb7\x7e\x67\xee\x8f\x08\xfb\x4d\x22\x0b\x1b\x46\xcd\xc9\x6f\xc6\x56\xab\x2b\xd1\xac\x1e\x43\xe4\xc5\xf0\x9d\x61\xa5\xbd\x1e\xde\x8c\x63\x98\x48\xed\xb7\x39\x37\xda\x97\x34\x14\x0f\x1f\x1b\xd1\xe5\x37\xf3\xea\xca\xd7\x0b\x75\x05\xf8\xb2\xd0\xe7\x36\xd7\xa6\x1b\x8e\x57\xcb\x73\xbe\xaa\xae\xd0\x3f\xd4\x15\xc3\x00\x08\xe6\xd5\xa0\x18\x4c\x2b\x2a\x03\x59\x1f\x9a\xf0\xd4\x61\xa5\xb9\x13\xbd\x1b\x5d\xd6\xe5\xe9\xa2\x2c\xca\x8c\x2d\x2a\x94\xc2\xf3\x72\xa1\xf9\x06\x10\x75\xcc\xfe\xb6\xf3\xd2\x35\x33\xf5\x2f\x37\x30\x0f\x3c\xe4\xc0\xee\x8e\x1d\x71\x4d\x9e\x9e\x0b\xb3\x64\xf3\xbd\x8e\xec\x47\x05\x87\x8c\x01\xb9\x91\x9c\x86\x76\x2b\x21\xf2\xae\x9a\x3f\xc2\x57\x2f\x75\x51\xf7\x7b\xd1\x59\xf3\xed\xfa\xec\x47\x22\x7b\x3d\x68\x2f\xfe\x76\x9d\xd6\x9e\xee\x8a\x05\x53\x9c\x60\x86\x53\x38\x53\x93\xe1\x1c\x73\x5c\xec\x0d\x90\xbc\xf9\x37\xea\xea\x14\x61\x6f\xeb\xe5\x01\x30\xba\x69\x63\xb6\x83\xb0\x7c\xa9\x36\x4f\x40\x58\xac\xbf\xc8\xff\xfe\xf6\x9b\xe1\x00\x86\xc8\xfb\x1b\x1f\xf8\xcb\x31\x1a\xae\x82\xe9\x7f\x72\x6c\xae\xc1\x8f\x1b\x36\xfa\x7b\x01\xad\x49\x7b\x1f\x81\xf4\xa1\x39\x5f\x9c\x56\x33\xf4\x25\xa2\x5b\x6e\xa5\xee\x07\x9a\x93\xe5\xe2\x3d\x5f\xd5\xaf\x86\x5a\x18\x56\xf1\x41\x0c\xda\xf5\xe9\x80\xad\x02\x4f\x3d\x6a\x37\xda\xed\xac\xcc\x7d\x40\x2f\xbb\x41\xf4\xce\x1a\xe5\xac\x62\x88\xad\x77\xa4\xb3\xf5\x4c\x56\x77\xa5\x70\xa3\x05\xe8\xc3\x6a\xf9\xca\x27\xf6\xa5\x10\x78\xfc\x11\x7b\x76\x14\xad\xae\x51\x19\x76\xee\xd4\x70\x8f\xa5\x32\x1b\x26\x6b\xf5\x9a\x76\xf1\x48\xb5\x19\x70\xc9\xee\x6e\xbd\x79\xbf\x4b\xdb\x7d\xd2\xab\x5d\xc2\xab\x5b\xbd\x1e\x6c\xe1\x17\x7f\x35\x0f\x87\xe7\x17\xeb\xd9\xa4\x4e\xa4\x44\x8e\x60\x7a\xaf\x34\x43\xf7\x72\x09\x64\xd8\x27\x5b\xa7\x22\x9a\x82\xeb\x08\x52\xe3\x9c\x76\xdd\xc6\xba\x91\x64\xe0\x15\x80\x46\x98\x64\xb6\x3c\x87\x41\xd2\x32\xf6\xa3\xd1\xb4\xb5\x31\x7b\x8e\xb2\xf9\x72\xe1\x7a\x53\xd9\xd6\xa4\x01\x4f\xdf\x96\xe1\x47\xbb\x2d\xc3\x63\xa7\x2d\xeb\x98\x21\x4b\x91\xec\x36\x3b\x5f\x4d\x3b\x5d\x4f\x00\xfe\xaf\x60\xd8\x7f\x95\x92\x19\x22\xad\x63\xa9\xc4\x37\x0c\xb3\xf5\xae\x31\x3b\x01\x38\xc3\x54\x2f\xac\xcb\xe4\xc4\x42\xa6\x71\xa1\xcb\x8e\xff\x8c\xba\xc1\xe5\x36\x3e\x70\xa9\x4c\xbe\x46\xff\xba\x7c\x63\x12\xbb\xdd\x54\x01\xb8\xb3\xbe\xdc\xa4\xc7\xd6\x7d\x33\xbd\xdd\x32\x6a\x6b\xcc\x87\x37\x53\x1a\x6e\xb3\xdf\xe5\xe8\xcb\xbf\xa0\x59\x55\x9d\xaf\xef\x1e\x1d\x9d\x55\xb3\xf5\x61\xca\x8f\x2e\xaa\x82\xfe\xb2\x46\xef\xc9\x21\x3e\x24\x28\xbd\x42\xff\xe3\x8c\x55\xb3\x92\xad\x85\xc5\xb4\x1b\x64\x60\x57\x88\xdc\xec\x71\x74\x84\xbe\xe5\x95\x3c\x0e\xc7\xb9\x10\x77\xc9\xd2\x39\x5f\xa3\xbf\x2b\x4a\x7f\xbf\xf5\x05\x6c\xe3\x5f\x71\xfe\xa0\xd9\xff\x32\xd8\x49\x83\xee\x48\xe5\xdd\x41\xb7\x6f\xd7\x3f\xdf\xb3\xa3\x47\x7f\x97\xdd\xd1\x90\x3f\x81\x1f\x5a\xdc\x67\xea\x7b\x17\xb5\xfa\xf5\xf6\x6d\xc3\xfe\x9c\xe3\x0e\x93\x0d\xb0\x93\x8d\x53\xd8\x39\xf3\xf7\xa9\xdc\x8d\xff\xc3\x32\xe7\x87\xbf\xac\xd1\x72\x85\xbe\x91\x5b\x69\xca\xa2\xe4\x39\xca\x96\x39\x9f\x02\x16\xb6\xc8\xd1\xc5\x9a\xa3\xb2\x12\xe3\xda\xdf\x85\x1c\xb5\x3e\xa8\x7d\x38\x4d\x1f\x4e\xd5\xf7\x6e\x1f\xe4\xaf\xf7\xe4\x9e\xa4\xb6\xd9\x61\x03\x7d\xac\x23\xfb\xed\x37\xed\xdb\xe1\x65\xb9\xc8\xc5\xdb\x65\x07\x46\x6e\x1d\x12\xbc\x20\xfd\x67\xd8\xec\x73\xeb\x8b\xa3\x2f\x0f\x6e\xec\xef\xcb\xa3\x5b\xb2\xb7\xeb\x6a\x55\x2e\x4e\x1f\xae\x96\x67\x27\x33\xb6\x3a\x59\xe6\x42\x73\x2f\xe0\xc7\xc3\x42\xfb\x55\x09\xff\x25\x7b\xc7\x17\x52\xc6\x7d\x93\x3d\xbf\x58\x5c\x09\xf9\xde\xfa\xa2\x89\x60\x17\xd9\x9a\xe4\x5c\xfc\x38\x91\x74\x64\x07\x61\x69\x13\x36\xdf\xd7\x43\x20\xfc\x94\x2d\x2f\x16\x15\x5f\xa9\x99\x4b\xf8\x69\x5e\xc7\x0a\xd9\xbc\x0d\x16\xf0\x14\xce\x33\xd6\x5f\xf8\xa6\x5a\x31\xf1\xe5\x72\x56\xce\x39\x9a\xd4\xd8\xee\x2b\x24\x92\xf4\x17\xd0\xa6\x45\x98\xa9\xee\x7d\x5d\xd5\x0d\xf6\xf7\x85\xab\x7f\x01\x3a\x95\xc0\x5f\x1d\x23\x6f\xf3\x2d\xf5\x3c\xa1\x73\xf9\xd3\x7d\xf8\xe9\x9b\x87\x0f\xc5\x4f\x16\x4a\x42\x5c\xf0\xba\xbe\xbe\x58\xad\x96\xa7\xac\xe2\x53\xb0\xba\x6a\xc6\x57\x1c\xce\x79\xa2\x05\xdf\x54\x48\xb0\xc0\xb2\x8a\xaf\xa0\x11\x74\x63\x1b\xfe\x80\xc1\x89\x04\xbf\x8d\xbc\xcd\xc3\x13\xcf\xdb\x13\x16\xea\x6d\xbe\x85\x8f\xff\x10\xc1\x79\xbe\xbc\x6c\xe9\x43\xb3\x2f\xa4\xe4\xe5\x50\x3e\x51\x5d\x14\x08\xfc\x87\x0f\xf7\xe0\x68\xa6\xb7\x87\xf6\x91\x86\x19\x1e\xec\xd7\x15\x87\x14\xf5\x36\x0b\x56\x5d\xbd\x58\x9c\xb1\x2a\x9b\xf1\xbc\xa5\x77\x0f\x2d\x17\xf3\x2b\xc4\xce\xcf\x39\xf4\xbb\x5c\x83\x03\xa2\x8b\x45\x59\x4d\xc5\x8b\x66\xc6\xd6\x1c\xde\x36\x85\x20\x1a\x4c\x0d\x8c\x10\x52\x55\xef\x8b\x6a\xb0\x8a\xa1\x9e\x69\x5f\xcf\x59\xb9\x1a\xf6\x0c\xfa\xa5\x78\xfd\x42\x89\xee\xe0\x40\xf1\x7e\xab\xdf\x01\x4b\x4b\x01\x28\xfe\xaf\xe2\xbd\x84\xaa\xbd\xf1\x3a\xce\xc0\x17\xe0\x0c\x30\x0a\xb7\xbe\xd0\x58\xb9\xcc\x5b\xba\x46\x5e\x2e\x72\xbe\x41\xc7\xe8\x00\x1b\xcd\xbe\xf1\xa3\x3b\x77\x34\xe3\xdf\xdf\x97\xcd\x2c\xc6\x0f\x74\x5e\x03\xc8\x9b\xbe\xb1\x0b\x53\x7a\x28\x34\x2e\x25\x23\x7f\x3d\x38\xae\xd5\x7f\x4f\x93\x17\xda\x3f\x36\xc4\x8f\x1a\xd1\x57\x5f\x21\xec\xd5\x06\x84\x7e\x53\x3e\xa4\x54\x52\x73\x22\x8d\x15\xfd\x86\x3a\x76\xd8\x08\x7f\x0b\x42\x80\xd0\xa6\xa4\x46\xf8\xd9\x8c\x67\xef\x5e\x64\x6c\xce\x56\xff\x4b\xb4\x9a\x08\x3d\x3c\x5b\x96\x0b\xb9\x9b\x1a\x04\xd0\xfc\xd4\xf5\xf8\xf6\x67\xe9\xf5\xad\x70\xaa\xd9\x6a\x79\x89\x1e\xac\x56\xcb\xd5\x04\x7a\x75\xe7\xb1\x48\x85\x5a\xd3\xfc\x71\xff\x0e\xda\x6f\x11\x1c\x56\x4b\x19\x59\x27\x38\xda\x3b\xac\x96\x3f\x9e\x9f\xf3\xd5\x09\x5b\xf3\xc9\x1e\xda\x97\x08\x84\xc9\x2f\x96\x95\x30\x70\x60\x56\xca\xe5\x8e\x78\x58\x77\xf4\xc3\x27\x18\x09\x5a\x39\x41\x56\x2d\x32\xf1\x56\x1c\x53\xb9\xcc\xa6\x06\x27\x29\x65\x83\x36\x26\xba\x00\xbf\xaa\xdb\x48\x8d\xc2\x54\xe5\x86\x7a\x7b\x7d\xbd\x48\x87\x38\xa9\x1b\x9a\xd4\xa2\xa1\xbd\xad\x8c\xf3\xe1\x43\xaa\x62\x9d\x0a\x73\xf8\x20\xbd\xaa\x38\x5a\xf3\xff\xba\xe0\x8b\x0c\x02\x9d\x9d\xd1\x96\x46\x6d\x3a\x30\x10\x5e\x9d\xa5\xcb\x79\xe3\x48\x36\xca\xd4\xeb\x52\x26\x43\xca\x0d\xa6\x71\x21\x45\x52\x40\x58\x09\xe8\xc4\x6b\x58\x6a\x36\x1e\x1b\x98\x80\x30\xac\x33\xe1\x0f\x99\x70\x18\xfc\xbd\x1d\x99\xc4\x44\x72\xe9\x29\x2e\x1f\x78\x1d\x14\xfb\xc7\x16\xab\x89\xb6\xe8\xcc\x03\x6f\xd0\x99\xe0\xa3\x24\x8a\xa9\x62\x36\x96\xcc\x3e\xdc\x92\x59\x4c\x76\xed\x54\x0b\x69\xe2\xaa\xdb\xd1\xae\x07\x34\xb6\x09\x18\xfa\x2e\x21\x52\x7f\x35\x4e\xf4\x93\xa6\x06\xa9\x48\xdd\x87\xc9\xd5\x20\x6b\x6a\xe1\x47\x07\x95\x06\xb4\xfe\x41\x28\x41\x46\xab\x2d\x07\x97\xb6\xc7\x3a\x61\x7d\x94\xd1\x50\xee\x1f\x3b\x5c\xbf\x17\xd1\xdb\x66\x9f\x2a\x11\x6e\x64\xbf\xe2\x2c\x3f\x59\x2e\xaa\x72\x71\x01\x87\x67\x41\xfb\x6d\x28\x12\x9c\x7c\x07\x7d\xff\xea\x18\xd8\x3a\x11\x89\x85\x61\x34\xb8\xf3\xdd\xe2\x3d\x9b\x97\x39\x00\x49\x69\xdf\x51\xdd\x6a\xe4\xdd\xa5\x82\x24\x42\x98\x28\x78\xdd\xd0\x79\xa3\xdc\x44\x34\x6d\x7e\xdc\xdf\x17\xc9\x78\x1d\xa1\x7a\x68\x6e\xcb\x30\x22\x13\x41\x11\x25\xff\xa1\x05\x43\x23\xb4\xff\xb0\x61\xec\xe8\x08\x7d\x57\xa0\x4b\x8e\x44\xbe\x76\x71\x8e\x44\xa6\x3a\x45\x65\xf5\x7f\xff\xf7\xff\xa9\x87\x25\x1d\x05\x70\x7c\xcb\xd2\xf3\x01\xe0\x9d\x41\xf0\x97\xd6\xfb\x02\xbc\x60\xd2\x5a\xb9\x00\xc6\xba\x19\x12\xfd\x8b\xaf\x7f\x09\x0c\xe6\x3b\xd4\xd5\x47\xa8\xaa\x8b\xe9\x78\xa8\x75\x25\xd9\x82\xcd\xe1\xf0\x43\x23\xc7\xe7\x9c\xe5\xa8\x28\x57\xeb\xaa\x96\x12\x74\x6b\x77\x35\x0f\x47\x37\x34\x59\x2c\x87\xe2\x5d\xef\xd5\x36\x21\x09\xdd\x56\xfa\x57\x91\x55\xe3\xb5\x91\x6f\xcd\xeb\x70\x0c\xeb\xe1\x79\x50\x1b\xd4\x49\x8d\x0a\xd4\x82\x8e\x2d\x0e\x73\xaf\x1f\x0f\x74\x64\x58\xbe\x66\x40\xcd\x9d\x46\xbb\xa6\x04\xac\xb1\xde\xd6\x7c\xb5\x18\xd5\x4d\xe0\x77\x30\xc1\x3a\xad\x97\x7d\xf7\xfb\xb2\x3d\x63\x57\xa8\x5c\x64\xf3\x0b\x78\x09\x11\x2f\x17\xfa\x2b\x8d\x49\xca\x0f\x6b\xe9\x3c\xd8\x41\x3a\x60\xca\xd7\x13\xa0\xa7\xde\xd3\x08\xec\x4d\x92\xb4\x74\x81\xfa\x36\x81\x7a\x90\xbc\x48\x81\x8d\xe5\x07\x9f\x52\xe6\xc3\x11\xbe\x2f\x51\xaa\x24\xfa\xf0\x66\x25\x0a\x21\xe3\x9a\x42\x8f\x41\xe8\xde\xa6\x2f\x76\x6f\xe3\x9d\xec\xa1\xdf\x40\x22\x13\xc9\x83\xfc\xb5\xd1\x47\x60\xd5\x07\xbc\x51\x19\xde\x31\xb0\xa7\xbf\x82\x99\x35\x51\xcb\xd3\xa8\x85\x1f\x5f\x3e\x3c\xa0\x28\x87\x99\x32\x9e\x37\x91\xb7\x0e\x9b\xea\x04\x56\xf3\x1d\x02\x9a\xf6\x1d\xe2\xcf\xbd\x5e\x4e\xa2\x72\x8d\x76\x34\x96\xfc\x35\xf8\xba\x29\x89\x06\x56\x47\x35\xa0\xa2\x07\x40\x2d\x29\xd1\x62\x6c\x3b\xfb\xd3\x49\x77\xda\x79\xa2\xea\xec\x5c\xcb\x46\x26\xd5\xd9\x39\x3a\xee\x8d\x25\x7b\xe8\x2f\xc7\xc7\x32\x28\xf7\xb3\x13\xb5\x88\x51\x9d\x9d\xf7\xf3\x0c\xed\x05\xbd\x85\xde\xfb\x94\x93\x6f\x42\xac\xe8\x18\x18\xbc\xf3\x9e\xaf\xd6\xe5\x72\x71\xe7\x2e\xba\x03\x93\xbe\x77\xa6\xe2\x57\xc9\xcf\x9d\xbb\x5a\x56\x08\xbf\xcb\xee\xaa\xdf\xe5\x97\x5b\x5f\x7c\x50\x93\x74\x2f\x96\x67\x1c\x7d\xfd\xe4\x5b\x94\x5e\x94\xf3\x1c\x2d\xcf\xab\xf2\xac\xfc\x95\xaf\xd6\x53\x34\x2f\xdf\x71\xb4\x3a\xfc\x65\x3d\x95\xaf\xc4\x30\xd3\xbe\x3e\xe7\x59\x59\x94\x99\x70\xde\xbc\x04\x85\x9f\xb3\xaa\xe2\xab\xc5\x1a\xf0\x41\xa3\x6a\xc6\x51\xb1\x9c\xcf\x97\x97\xe5\xe2\xf4\xae\x9c\xf3\x14\xe6\xd7\x3b\x17\x89\xee\xd4\x46\x73\x47\x4e\xee\x76\x00\x0e\xd9\x59\xde\x9b\x45\x6d\x8e\x48\x8a\x67\xb7\xbe\x90\xea\x52\x87\x26\x9b\x69\xee\xee\x00\x26\xfa\x0c\xba\x03\xe5\xb4\x6f\x17\xbd\x59\xe3\xbf\x68\xdf\x0f\x17\xcb\x9c\xbf\xbc\x3a\xe7\x6d\x32\xd7\xce\x55\xab\x17\x8f\x72\xa1\xcf\x1b\x3f\x2f\x17\xa7\xcb\xff\xf9\x02\xbd\xf7\x0e\xe9\xa1\x07\xaf\xe7\x6d\x0b\xed\x2c\x69\xc3\x8c\x0a\x8d\x35\x26\xb6\xba\x9c\xb1\x79\x0f\x53\x7c\xe8\x1d\xc8\x89\x98\x55\xbd\x37\x4a\x9e\x62\x54\xbf\xcd\xd8\xfa\xe9\xe5\xe2\x59\xbd\x05\xe6\x58\x01\x1d\x76\x7f\x07\xf0\x66\x89\x04\xaa\xc6\x49\xa1\xd4\x11\xa3\x0b\x2e\xd7\x87\xc4\x73\x38\x48\xbc\x27\x64\xa3\xcb\xea\xf5\x3b\x59\xc0\x50\x40\xc0\xe7\xce\xe4\x57\xaf\x5f\xcf\x67\xe5\x62\x29\x7a\xc5\xd0\x25\x4f\x91\x3a\xa8\xaa\x66\xad\x0f\x95\x41\x2b\x99\x7c\xb8\xa5\x8e\xa8\xc2\xb2\xc9\x87\xe9\x3f\x3e\xbc\x99\xd2\x68\x9b\x25\x91\xc1\x89\xdd\x57\x4f\x1e\x3f\xaa\xaa\xf3\xe7\x62\xc8\x58\x57\x0d\xb6\xbf\xa6\xe5\xa9\xdc\xcc\x72\xf8\xcb\xfa\xaf\xdb\x60\xbe\x73\xb1\xe6\xf0\xc2\x96\x55\x77\xee\xdd\x1a\x12\xfa\xa6\x3c\xfd\x01\x10\xde\x13\x1d\xfe\x65\x3d\x13\x41\xb9\x3c\x5d\x2c\x57\xfc\xee\xbc\x5c\xf0\x5b\x0d\xe9\x4b\x9e\xfa\x5b\x91\x14\x4a\xfa\x89\xa7\x72\x6c\x92\xc7\x8c\xef\x1c\x1e\xcd\xcb\xf4\x48\xa0\x10\xc1\xf9\xd6\xd1\x11\xca\x97\x8b\x3b\x15\x5a\xbe\xe7\xab\x55\x99\xf3\x7a\xc5\xa1\x5e\xe0\xb8\xa5\x9d\x41\x56\x4b\x07\x22\xc2\xdd\x69\x76\x34\xc0\x82\x44\x07\xe0\x50\xd2\xec\x42\x09\x0b\x81\x75\x32\x1d\x04\xb8\xbb\x77\xeb\x83\x41\x1c\xf2\x89\x5a\xd9\xaa\x59\xfe\xeb\x5d\x42\x3e\xbc\x11\x62\x98\xbe\x96\x62\x78\xb3\x77\xeb\xe8\xe8\xff\x43\xeb\xe5\xc5\x2a\xe3\x4f\xd8\xf9\x79\xb9\x38\xfd\xf1\xf9\xe3\x63\xf1\xf0\x60\x0e\xbb\x48\x7f\x59\x1f\x9e\xb1\xf3\x5b\xff\x2f\x00\x00\xff\xff\x92\x53\x12\xbf\x48\x24\x06\x00") func web3JsBytes() ([]byte, error) { return bindataRead( @@ -106,7 +106,7 @@ func web3Js() (*asset, error) { } info := bindataFileInfo{name: "web3.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1, 0x32, 0x6b, 0xa6, 0x48, 0xcf, 0x60, 0x55, 0x11, 0x99, 0x68, 0x24, 0xdb, 0x89, 0x36, 0xbe, 0xaa, 0xc, 0x77, 0xc9, 0x80, 0xa8, 0xd2, 0x72, 0x9d, 0x71, 0xa8, 0xc3, 0x9a, 0x61, 0x8f, 0xaa}} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2d, 0xc8, 0xbb, 0x7, 0x25, 0x6e, 0x8, 0xc6, 0x1d, 0x9e, 0x30, 0x6b, 0xb7, 0xfb, 0x9c, 0x9, 0x3, 0x64, 0xd, 0xdb, 0x17, 0x8e, 0xfc, 0xac, 0xa, 0xe4, 0xbe, 0x81, 0xb0, 0x6a, 0xb5, 0x62}} return a, nil } diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js index 950576f4d9b7..2fe65672619b 100644 --- a/internal/jsre/deps/web3.js +++ b/internal/jsre/deps/web3.js @@ -3949,10 +3949,18 @@ var outputSyncingFormatter = function(result) { result.startingBlock = utils.toDecimal(result.startingBlock); result.currentBlock = utils.toDecimal(result.currentBlock); result.highestBlock = utils.toDecimal(result.highestBlock); - if (result.knownStates) { - result.knownStates = utils.toDecimal(result.knownStates); - result.pulledStates = utils.toDecimal(result.pulledStates); - } + result.syncedAccounts = utils.toDecimal(result.syncedAccounts); + result.syncedAccountBytes = utils.toDecimal(result.syncedAccountBytes); + result.syncedBytecodes = utils.toDecimal(result.syncedBytecodes); + result.syncedBytecodeBytes = utils.toDecimal(result.syncedBytecodeBytes); + result.syncedStorage = utils.toDecimal(result.syncedStorage); + result.syncedStorageBytes = utils.toDecimal(result.syncedStorageBytes); + result.healedTrienodes = utils.toDecimal(result.healedTrienodes); + result.healedTrienodeBytes = utils.toDecimal(result.healedTrienodeBytes); + result.healedBytecodes = utils.toDecimal(result.healedBytecodes); + result.healedBytecodeBytes = utils.toDecimal(result.healedBytecodeBytes); + result.healingTrienodes = utils.toDecimal(result.healingTrienodes); + result.healingBytecode = utils.toDecimal(result.healingBytecode); return result; }; diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index fe9292f5f861..23460de3a2e9 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -152,6 +152,11 @@ web3._extend({ call: 'admin_importChain', params: 1 }), + new web3._extend.Method({ + name: 'newHead', + call: 'admin_newHead', + params: 1 + }), new web3._extend.Method({ name: 'sleepBlocks', call: 'admin_sleepBlocks', diff --git a/les/downloader/downloader.go b/les/downloader/downloader.go index e7dfc4158e0e..722077a5f029 100644 --- a/les/downloader/downloader.go +++ b/les/downloader/downloader.go @@ -231,9 +231,9 @@ func New(checkpoint uint64, stateDb ethdb.Database, stateBloom *trie.SyncBloom, stateCh: make(chan dataPack), SnapSyncer: snap.NewSyncer(stateDb), stateSyncStart: make(chan *stateSync), - syncStatsState: stateSyncStats{ - processed: rawdb.ReadFastTrieProgress(stateDb), - }, + //syncStatsState: stateSyncStats{ + // processed: rawdb.ReadFastTrieProgress(stateDb), + //}, trackStateReq: make(chan *stateReq), } go dl.stateFetcher() @@ -268,8 +268,8 @@ func (d *Downloader) Progress() ethereum.SyncProgress { StartingBlock: d.syncStatsChainOrigin, CurrentBlock: current, HighestBlock: d.syncStatsChainHeight, - PulledStates: d.syncStatsState.processed, - KnownStates: d.syncStatsState.processed + d.syncStatsState.pending, + //PulledStates: d.syncStatsState.processed, + //KnownStates: d.syncStatsState.processed + d.syncStatsState.pending, } } diff --git a/les/downloader/downloader_test.go b/les/downloader/downloader_test.go index 17cd3630c98e..8736de39d14f 100644 --- a/les/downloader/downloader_test.go +++ b/les/downloader/downloader_test.go @@ -1207,8 +1207,8 @@ func checkProgress(t *testing.T, d *Downloader, stage string, want ethereum.Sync t.Helper() p := d.Progress() - p.KnownStates, p.PulledStates = 0, 0 - want.KnownStates, want.PulledStates = 0, 0 + //p.KnownStates, p.PulledStates = 0, 0 + //want.KnownStates, want.PulledStates = 0, 0 if p != want { t.Fatalf("%s progress mismatch:\nhave %+v\nwant %+v", stage, p, want) } diff --git a/les/downloader/statesync.go b/les/downloader/statesync.go index 6c53e5577a87..bc396fffb686 100644 --- a/les/downloader/statesync.go +++ b/les/downloader/statesync.go @@ -22,7 +22,6 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" @@ -610,6 +609,6 @@ func (s *stateSync) updateStats(written, duplicate, unexpected int, duration tim log.Info("Imported new state entries", "count", written, "elapsed", common.PrettyDuration(duration), "processed", s.d.syncStatsState.processed, "pending", s.d.syncStatsState.pending, "trieretry", len(s.trieTasks), "coderetry", len(s.codeTasks), "duplicate", s.d.syncStatsState.duplicate, "unexpected", s.d.syncStatsState.unexpected) } if written > 0 { - rawdb.WriteFastTrieProgress(s.d.stateDB, s.d.syncStatsState.processed) + //rawdb.WriteFastTrieProgress(s.d.stateDB, s.d.syncStatsState.processed) } } diff --git a/mobile/ethereum.go b/mobile/ethereum.go index 97c46ddca71f..d5058e4e20ab 100644 --- a/mobile/ethereum.go +++ b/mobile/ethereum.go @@ -78,11 +78,21 @@ type SyncProgress struct { progress ethereum.SyncProgress } -func (p *SyncProgress) GetStartingBlock() int64 { return int64(p.progress.StartingBlock) } -func (p *SyncProgress) GetCurrentBlock() int64 { return int64(p.progress.CurrentBlock) } -func (p *SyncProgress) GetHighestBlock() int64 { return int64(p.progress.HighestBlock) } -func (p *SyncProgress) GetPulledStates() int64 { return int64(p.progress.PulledStates) } -func (p *SyncProgress) GetKnownStates() int64 { return int64(p.progress.KnownStates) } +func (p *SyncProgress) GetStartingBlock() int64 { return int64(p.progress.StartingBlock) } +func (p *SyncProgress) GetCurrentBlock() int64 { return int64(p.progress.CurrentBlock) } +func (p *SyncProgress) GetHighestBlock() int64 { return int64(p.progress.HighestBlock) } +func (p *SyncProgress) GetSyncedAccounts() int64 { return int64(p.progress.SyncedAccounts) } +func (p *SyncProgress) GetSyncedAccountBytes() int64 { return int64(p.progress.SyncedAccountBytes) } +func (p *SyncProgress) GetSyncedBytecodes() int64 { return int64(p.progress.SyncedBytecodes) } +func (p *SyncProgress) GetSyncedBytecodeBytes() int64 { return int64(p.progress.SyncedBytecodeBytes) } +func (p *SyncProgress) GetSyncedStorage() int64 { return int64(p.progress.SyncedStorage) } +func (p *SyncProgress) GetSyncedStorageBytes() int64 { return int64(p.progress.SyncedStorageBytes) } +func (p *SyncProgress) GetHealedTrienodes() int64 { return int64(p.progress.HealedTrienodes) } +func (p *SyncProgress) GetHealedTrienodeBytes() int64 { return int64(p.progress.HealedTrienodeBytes) } +func (p *SyncProgress) GetHealedBytecodes() int64 { return int64(p.progress.HealedBytecodes) } +func (p *SyncProgress) GetHealedBytecodeBytes() int64 { return int64(p.progress.HealedBytecodeBytes) } +func (p *SyncProgress) GetHealingTrienodes() int64 { return int64(p.progress.HealingTrienodes) } +func (p *SyncProgress) GetHealingBytecode() int64 { return int64(p.progress.HealingBytecode) } // Topics is a set of topic lists to filter events with. type Topics struct{ topics [][]common.Hash } From a094be595fdb419380ac5a7318e2a232ceabecbb Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 4 Oct 2021 13:35:17 +0200 Subject: [PATCH 34/43] miner: add totalDifficultyOverride flag --- cmd/geth/config.go | 3 +++ cmd/geth/main.go | 1 + cmd/utils/flags.go | 4 ++++ eth/catalyst/api.go | 1 + miner/worker.go | 13 +++++++++++++ 5 files changed, 22 insertions(+) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 5e48c0ea6df1..e81bc302f347 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -158,6 +158,9 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { if ctx.GlobalIsSet(utils.OverrideLondonFlag.Name) { cfg.Eth.OverrideLondon = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideLondonFlag.Name)) } + if ctx.GlobalIsSet(utils.OverrideTotalTerminalDifficulty.Name) { + cfg.Eth.Genesis.Config.TerminalTotalDifficulty = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideTotalTerminalDifficulty.Name)) + } backend, _ := utils.RegisterEthService(stack, &cfg.Eth, ctx.GlobalBool(utils.CatalystFlag.Name)) // Configure GraphQL if requested diff --git a/cmd/geth/main.go b/cmd/geth/main.go index d003d590ed28..1032df3eb61c 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -67,6 +67,7 @@ var ( utils.USBFlag, utils.SmartCardDaemonPathFlag, utils.OverrideLondonFlag, + utils.OverrideTotalTerminalDifficulty, utils.EthashCacheDirFlag, utils.EthashCachesInMemoryFlag, utils.EthashCachesOnDiskFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 1a79907cf05a..7af80a88c6f5 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -240,6 +240,10 @@ var ( Name: "override.london", Usage: "Manually specify London fork-block, overriding the bundled setting", } + OverrideTotalTerminalDifficulty = cli.Uint64Flag{ + Name: "override.totalterminaldifficulty", + Usage: "Manually specify TotalTerminalDifficulty, overriding the bundled setting", + } // Light server and client settings LightServeFlag = cli.IntFlag{ Name: "light.serve", diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 64a9bf308439..8710d337df6d 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -489,6 +489,7 @@ func (api *ConsensusAPI) setHead(newHead common.Hash) error { if !merger.LeftPoW() { merger.LeavePoW() } + log.Info("Setting head", "head", newHead) if api.light { headHeader := api.les.BlockChain().CurrentHeader() if headHeader.Hash() == newHead { diff --git a/miner/worker.go b/miner/worker.go index 6dd317cecc77..8794139c1132 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -19,6 +19,7 @@ package miner import ( "bytes" "errors" + "fmt" "math/big" "sync" "sync/atomic" @@ -1024,6 +1025,18 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st if err != nil { return err } + + // TODO (MariusVanDerWijden) remove this after eth2 interop + // --- + parent := w.chain.GetBlockByHash(block.ParentHash()) + if parent == nil { + return fmt.Errorf("parent unavailable: %v", block.ParentHash()) + } + td := w.chain.GetTdByHash(parent.Hash()) + if td != nil && td.Cmp(w.chain.Config().TerminalTotalDifficulty) > 0 { + log.Warn("Total terminal difficulty reached, keeping on mining to test eth2 clients") + } + // --- if w.isRunning() && !w.merger.LeftPoW() { if interval != nil { interval() From b1ff642f3adf19449c75f1b98908e48ef7f87724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 4 Oct 2021 15:01:13 +0300 Subject: [PATCH 35/43] eth: minor sync polishes --- eth/backend.go | 4 +++ eth/catalyst/api.go | 25 ++++++--------- eth/catalyst/sync.go | 74 -------------------------------------------- 3 files changed, 14 insertions(+), 89 deletions(-) delete mode 100644 eth/catalyst/sync.go diff --git a/eth/backend.go b/eth/backend.go index d56be0528f7e..735ae45027c6 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -529,6 +529,10 @@ func (s *Ethereum) SetSynced() { atomic.StoreUint32(&s.h func (s *Ethereum) ArchiveMode() bool { return s.config.NoPruning } func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer } func (s *Ethereum) Merger() *core.Merger { return s.merger } +func (s *Ethereum) SyncMode() downloader.SyncMode { + mode, _ := s.handler.chainSync.modeAndLocalHead() + return mode +} // Protocols returns all the currently configured // network protocols to start. diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 64a9bf308439..ff0d66c32fad 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -84,7 +84,6 @@ type ConsensusAPI struct { eth *eth.Ethereum les *les.LightEthereum engine consensus.Engine // engine is the post-merge consensus engine, only for block creation - syncer *syncer // syncer is responsible for triggering chain sync preparedBlocks map[int]*ExecutableData } @@ -114,7 +113,6 @@ func NewConsensusAPI(eth *eth.Ethereum, les *les.LightEthereum) *ConsensusAPI { eth: eth, les: les, engine: engine, - syncer: newSyncer(), preparedBlocks: make(map[int]*ExecutableData), } } @@ -217,11 +215,11 @@ func (api *ConsensusAPI) ConsensusValidated(params ConsensusValidatedParams) err func (api *ConsensusAPI) ForkchoiceUpdated(params ForkChoiceParams) error { var emptyHash = common.Hash{} - if !bytes.Equal(params.FinalizedBlockHash[:], emptyHash[:]) { - if err := api.checkTerminalTotalDifficulty(params.FinalizedBlockHash); err != nil { + if !bytes.Equal(params.HeadBlockHash[:], emptyHash[:]) { + if err := api.checkTerminalTotalDifficulty(params.HeadBlockHash); err != nil { return err } - return api.setHead(params.FinalizedBlockHash) + return api.setHead(params.HeadBlockHash) } return nil } @@ -249,22 +247,19 @@ func (api *ConsensusAPI) ExecutePayload(params ExecutableData) (GenericStringRes td := api.eth.BlockChain().GetTdByHash(parent.Hash()) ttd := api.eth.BlockChain().Config().TerminalTotalDifficulty - if !api.eth.Synced() { - if td.Cmp(ttd) > 0 { - // first pos block - api.eth.SetSynced() - } else { - // TODO (MariusVanDerWijden) if the node is not synced and we received a finalized block - // we should trigger the reverse header sync here. - return SYNCING, errors.New("node is not synced yet") - } - } else if td.Cmp(ttd) < 0 { + if td.Cmp(ttd) < 0 { return INVALID, fmt.Errorf("can not execute payload on top of block with low td got: %v threshold %v", td, ttd) } block, err := ExecutableDataToBlock(api.eth.BlockChain().Config(), parent.Header(), params) if err != nil { return INVALID, err } + if !api.eth.BlockChain().HasBlock(block.ParentHash(), block.NumberU64()-1) { + if err := api.eth.Downloader().BeaconSync(api.eth.SyncMode(), block.Header()); err != nil { + return SYNCING, err + } + return SYNCING, nil + } if err := api.eth.BlockChain().InsertBlock(block); err != nil { return INVALID, err } diff --git a/eth/catalyst/sync.go b/eth/catalyst/sync.go deleted file mode 100644 index 61fd1ef9c166..000000000000 --- a/eth/catalyst/sync.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package catalyst - -import ( - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/log" -) - -type syncer struct { - running bool - newBlocks map[common.Hash]*types.Block - lock sync.Mutex -} - -func newSyncer() *syncer { - return &syncer{ - newBlocks: make(map[common.Hash]*types.Block), - } -} - -// onNewBlock is the action for receiving new block event -func (s *syncer) onNewBlock(block *types.Block) { - s.lock.Lock() - defer s.lock.Unlock() - - if s.running { - return - } - s.newBlocks[block.Hash()] = block -} - -func (s *syncer) hasBlock(hash common.Hash) bool { - s.lock.Lock() - defer s.lock.Unlock() - - _, present := s.newBlocks[hash] - return present -} - -// onNewHead is the action for receiving new head event -func (s *syncer) onNewHead(head common.Hash) { - s.lock.Lock() - defer s.lock.Unlock() - - if s.running { - return - } - _, present := s.newBlocks[head] - if !present { - log.Error("Chain head is set with an unknown header") - return - } - s.running = true - - // todo call the SetHead function exposed by the downloader -} From 7834589732101f94c2ab948a3d093e6c0ffe7c84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 4 Oct 2021 17:15:49 +0300 Subject: [PATCH 36/43] core, eth: various tiny fixups integrating sync --- core/blockchain.go | 5 +---- core/blockchain_test.go | 8 -------- core/forkchoice.go | 40 ++++++++++++++----------------------- core/headerchain_test.go | 2 +- eth/catalyst/api.go | 25 +++++++++-------------- eth/catalyst/api_test.go | 4 ++-- light/lightchain.go | 5 +---- miner/stress/beacon/main.go | 10 ++-------- 8 files changed, 31 insertions(+), 68 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index e234b5a5ba53..4f12690f4baa 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -244,10 +244,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par engine: engine, vmConfig: vmConfig, } - bc.forker = NewForkChoice(bc, merger.LeftPoW(), shouldPreserve) - merger.SubscribeLeavePoW(func() { - bc.forker.MarkTransitioned() - }) + bc.forker = NewForkChoice(bc, shouldPreserve) bc.validator = NewBlockValidator(chainConfig, bc, engine) bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine) bc.processor = NewStateProcessor(chainConfig, bc, engine) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index f1e130b19ff1..99db78fcf707 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -230,9 +230,6 @@ func testInsertAfterMerge(t *testing.T, blockchain *BlockChain, i, n int, full b t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1) } - // Trigger the transition explicitly - blockchain2.forker.MarkTransitioned() - // Extend the newly created chain if full { blockChainB := makeBlockChain(blockchain2.CurrentBlock(), n, ethash.NewFaker(), db, forkSeed) @@ -1963,7 +1960,6 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon if mergePoint == 0 { genEngine.MarkTransitioned() runEngine.MarkTransitioned() - chain.forker.MarkTransitioned() } blocks, _ := GenerateChain(params.TestChainConfig, genesis, genEngine, db, 2*TriesInMemory, func(i int, gen *BlockGen) { tx, err := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("deadbeef"), big.NewInt(100), 21000, big.NewInt(int64(i+1)*params.GWei), nil), signer, key) @@ -1994,7 +1990,6 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon if mergePoint == 1 { genEngine.MarkTransitioned() runEngine.MarkTransitioned() - chain.forker.MarkTransitioned() } // Generate the sidechain @@ -2218,9 +2213,6 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i if engine != nil { engine.MarkTransitioned() } - if forker != nil { - forker.MarkTransitioned() - } } // Apply merging since genesis diff --git a/core/forkchoice.go b/core/forkchoice.go index 9bc1f79facef..e3b8e591201f 100644 --- a/core/forkchoice.go +++ b/core/forkchoice.go @@ -27,12 +27,16 @@ import ( "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" ) // ChainReader defines a small collection of methods needed to access the local // blockchain during header verification. It's implemented by both blockchain // and lightchain. type ChainReader interface { + // Config retrieves the header chain's chain configuration. + Config() *params.ChainConfig + // GetTd returns the total difficulty of a local block. GetTd(common.Hash, uint64) *big.Int } @@ -45,12 +49,7 @@ type ChainReader interface { type ForkChoice struct { chain ChainReader rand *mrand.Rand - - // transitioned is the flag whether the chain has started(or finished) - // the transition. It's triggered by receiving the first "NewHead" message - // from the external consensus engine. - transitioned bool - lock sync.RWMutex + lock sync.RWMutex // preserve is a helper function used in td fork choice. // Miners will prefer to choose the local mined block if the @@ -59,17 +58,16 @@ type ForkChoice struct { preserve func(header *types.Header) bool } -func NewForkChoice(chainReader ChainReader, transitioned bool, preserve func(header *types.Header) bool) *ForkChoice { +func NewForkChoice(chainReader ChainReader, preserve func(header *types.Header) bool) *ForkChoice { // Seed a fast but crypto originating random generator seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) if err != nil { log.Crit("Failed to initialize random seed", "err", err) } return &ForkChoice{ - chain: chainReader, - rand: mrand.New(mrand.NewSource(seed.Int64())), - transitioned: transitioned, - preserve: preserve, + chain: chainReader, + rand: mrand.New(mrand.NewSource(seed.Int64())), + preserve: preserve, } } @@ -82,12 +80,6 @@ func (f *ForkChoice) Reorg(current *types.Header, header *types.Header) (bool, e f.lock.RLock() defer f.lock.RUnlock() - // Accept the new header as the chain head if the transition - // is already triggered. We assume all the headers after the - // transition come from the trusted consensus layer. - if f.transitioned { - return true, nil - } var ( localTD = f.chain.GetTd(current.Hash(), current.Number.Uint64()) externTd = f.chain.GetTd(header.Hash(), header.Number.Uint64()) @@ -95,6 +87,12 @@ func (f *ForkChoice) Reorg(current *types.Header, header *types.Header) (bool, e if localTD == nil || externTd == nil { return false, errors.New("missing td") } + // Accept the new header as the chain head if the transition + // is already triggered. We assume all the headers after the + // transition come from the trusted consensus layer. + if ttd := f.chain.Config().TerminalTotalDifficulty; ttd != nil && ttd.Cmp(externTd) <= 0 { + return true, nil + } // If the total difficulty is higher than our known, add it to the canonical chain // Second clause in the if statement reduces the vulnerability to selfish mining. // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf @@ -113,11 +111,3 @@ func (f *ForkChoice) Reorg(current *types.Header, header *types.Header) (bool, e } return reorg, nil } - -// MarkTransitioned marks the transition has started. -func (f *ForkChoice) MarkTransitioned() { - f.lock.Lock() - defer f.lock.Unlock() - - f.transitioned = true -} diff --git a/core/headerchain_test.go b/core/headerchain_test.go index 5371ff98642d..ed0522671fb8 100644 --- a/core/headerchain_test.go +++ b/core/headerchain_test.go @@ -84,7 +84,7 @@ func TestHeaderInsertion(t *testing.T) { chainB := makeHeaderChain(chainA[0], 128, ethash.NewFaker(), db, 10) log.Root().SetHandler(log.StdoutHandler) - forker := NewForkChoice(hc, false, nil) + forker := NewForkChoice(hc, nil) // Inserting 64 headers on an empty chain, expecting // 1 callbacks, 1 canon-status, 0 sidestatus, testInsert(t, hc, chainA[:64], CanonStatTy, nil, forker) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index ff0d66c32fad..2437eb2ab27a 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -231,7 +231,7 @@ func (api *ConsensusAPI) ExecutePayload(params ExecutableData) (GenericStringRes if parent == nil { return INVALID, fmt.Errorf("could not find parent %x", params.ParentHash) } - block, err := ExecutableDataToBlock(api.les.BlockChain().Config(), parent, params) + block, err := ExecutableDataToBlock(params) if err != nil { return INVALID, err } @@ -240,17 +240,7 @@ func (api *ConsensusAPI) ExecutePayload(params ExecutableData) (GenericStringRes } return VALID, nil } - parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash) - if parent == nil { - return INVALID, fmt.Errorf("could not find parent %x", params.ParentHash) - } - - td := api.eth.BlockChain().GetTdByHash(parent.Hash()) - ttd := api.eth.BlockChain().Config().TerminalTotalDifficulty - if td.Cmp(ttd) < 0 { - return INVALID, fmt.Errorf("can not execute payload on top of block with low td got: %v threshold %v", td, ttd) - } - block, err := ExecutableDataToBlock(api.eth.BlockChain().Config(), parent.Header(), params) + block, err := ExecutableDataToBlock(params) if err != nil { return INVALID, err } @@ -260,6 +250,12 @@ func (api *ConsensusAPI) ExecutePayload(params ExecutableData) (GenericStringRes } return SYNCING, nil } + parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash) + td := api.eth.BlockChain().GetTdByHash(parent.Hash()) + ttd := api.eth.BlockChain().Config().TerminalTotalDifficulty + if td.Cmp(ttd) < 0 { + return INVALID, fmt.Errorf("can not execute payload on top of block with low td got: %v threshold %v", td, ttd) + } if err := api.eth.BlockChain().InsertBlock(block); err != nil { return INVALID, err } @@ -392,7 +388,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { return txs, nil } -func ExecutableDataToBlock(config *chainParams.ChainConfig, parent *types.Header, params ExecutableData) (*types.Block, error) { +func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) { txs, err := decodeTransactions(params.Transactions) if err != nil { return nil, err @@ -419,9 +415,6 @@ func ExecutableDataToBlock(config *chainParams.ChainConfig, parent *types.Header Extra: params.ExtraData, // TODO (MariusVanDerWijden) add params.Random to header once required } - if config.IsLondon(number) { - header.BaseFee = misc.CalcBaseFee(config, parent) - } block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) if block.Hash() != params.BlockHash { return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash()) diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 720a678ceac6..50db1a82b940 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -255,7 +255,7 @@ func TestEth2NewBlock(t *testing.T) { if err != nil { t.Fatalf("Failed to create the executable data %v", err) } - block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) + block, err := ExecutableDataToBlock(*execData) if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } @@ -292,7 +292,7 @@ func TestEth2NewBlock(t *testing.T) { if err != nil { t.Fatalf("Failed to create the executable data %v", err) } - block, err := ExecutableDataToBlock(ethservice.BlockChain().Config(), parent.Header(), *execData) + block, err := ExecutableDataToBlock(*execData) if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } diff --git a/light/lightchain.go b/light/lightchain.go index 93d474a967e2..6bb621ff2994 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -93,10 +93,7 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus. blockCache: blockCache, engine: engine, } - bc.forker = core.NewForkChoice(bc, merger.LeftPoW(), nil) - merger.SubscribeLeavePoW(func() { - bc.forker.MarkTransitioned() - }) + bc.forker = core.NewForkChoice(bc, nil) var err error bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.engine, bc.getProcInterrupt) if err != nil { diff --git a/miner/stress/beacon/main.go b/miner/stress/beacon/main.go index 46bf6d7e1476..dc5c229410cf 100644 --- a/miner/stress/beacon/main.go +++ b/miner/stress/beacon/main.go @@ -172,13 +172,7 @@ func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed catalyst.Execut if err := n.insertBlock(ed); err != nil { return err } - var config *params.ChainConfig - if n.typ == eth2LightClient { - config = n.lesBackend.BlockChain().Config() - } else { - config = n.ethBackend.BlockChain().Config() - } - block, err := catalyst.ExecutableDataToBlock(config, parent, ed) + block, err := catalyst.ExecutableDataToBlock(ed) if err != nil { return err } @@ -319,7 +313,7 @@ func (mgr *nodeManager) run() { log.Error("Failed to assemble the block", "err", err) continue } - block, _ := catalyst.ExecutableDataToBlock(chain.Config(), parentBlock.Header(), *ed) + block, _ := catalyst.ExecutableDataToBlock(*ed) nodes := mgr.getNodes(eth2MiningNode) nodes = append(nodes, mgr.getNodes(eth2NormalNode)...) From a0fe68b0f9f9e97d8e0041fe8a9481fbdcd76dd3 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 4 Oct 2021 21:57:49 +0200 Subject: [PATCH 37/43] eth/catalyst: fixed some tiny issues/nitpicks in api --- eth/catalyst/api.go | 19 +++++++------------ eth/catalyst/api_test.go | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 77144cb78998..824622d5b624 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -226,24 +226,20 @@ func (api *ConsensusAPI) ForkchoiceUpdated(params ForkChoiceParams) error { // ExecutePayload creates an Eth1 block, inserts it in the chain, and returns the status of the chain. func (api *ConsensusAPI) ExecutePayload(params ExecutableData) (GenericStringResponse, error) { + block, err := ExecutableDataToBlock(params) + if err != nil { + return INVALID, err + } if api.light { parent := api.les.BlockChain().GetHeaderByHash(params.ParentHash) if parent == nil { return INVALID, fmt.Errorf("could not find parent %x", params.ParentHash) } - block, err := ExecutableDataToBlock(params) - if err != nil { - return INVALID, err - } if err = api.les.BlockChain().InsertHeader(block.Header()); err != nil { return INVALID, err } return VALID, nil } - block, err := ExecutableDataToBlock(params) - if err != nil { - return INVALID, err - } if !api.eth.BlockChain().HasBlock(block.ParentHash(), block.NumberU64()-1) { if err := api.eth.Downloader().BeaconSync(api.eth.SyncMode(), block.Header()); err != nil { return SYNCING, err @@ -277,13 +273,12 @@ func (api *ConsensusAPI) assembleBlock(params AssembleBlockParams) (*ExecutableD return nil, fmt.Errorf("cannot assemble block with unknown parent %s", params.ParentHash) } - if parent.Time() >= params.Timestamp { + if params.Timestamp < parent.Time() { return nil, fmt.Errorf("child timestamp lower than parent's: %d >= %d", parent.Time(), params.Timestamp) } if now := uint64(time.Now().Unix()); params.Timestamp > now+1 { - wait := time.Duration(params.Timestamp-now) * time.Second - log.Info("Producing block too far in the future", "wait", common.PrettyDuration(wait)) - time.Sleep(wait) + diff := time.Duration(params.Timestamp-now) * time.Second + log.Warn("Producing block too far in the future", "diff", common.PrettyDuration(diff)) } pending, err := api.eth.TxPool().Pending(true) if err != nil { diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 50db1a82b940..1edb0952ba72 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -175,7 +175,7 @@ func TestSetHeadBeforeTotalDifficulty(t *testing.T) { t.Errorf("consensus validated before total terminal difficulty should fail") } - if err := api.ForkchoiceUpdated(ForkChoiceParams{FinalizedBlockHash: blocks[5].Hash()}); err == nil { + if err := api.ForkchoiceUpdated(ForkChoiceParams{HeadBlockHash: blocks[5].Hash()}); err == nil { t.Errorf("fork choice updated before total terminal difficulty should fail") } } From deaa55529ed059e3c660b5a29dade7bb527d8a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 5 Oct 2021 14:29:31 +0300 Subject: [PATCH 38/43] eth: disable legacy syncing on the merge interop pr for now --- eth/sync.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eth/sync.go b/eth/sync.go index 6138524d995a..104449c90807 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -113,9 +113,9 @@ func (cs *chainSyncer) loop() { defer cs.force.Stop() for { - if op := cs.nextSyncOp(); op != nil { - cs.startSync(op) - } + //if op := cs.nextSyncOp(); op != nil { + // cs.startSync(op) + //} select { case <-cs.peerEventCh: // Peer information changed, recheck. From 9562d106438e5bbdc8ae1a530fa4a90e77604370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 5 Oct 2021 16:27:48 +0300 Subject: [PATCH 39/43] core: overwrite head header and fast block too on block sethead --- core/blockchain.go | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 4f12690f4baa..c505638a99bb 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -717,32 +717,24 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { // // Note, this function assumes that the `mu` mutex is held! func (bc *BlockChain) writeHeadBlock(block *types.Block) { - // If the block is on a side chain or an unknown one, force other heads onto it too - updateHeads := rawdb.ReadCanonicalHash(bc.db, block.NumberU64()) != block.Hash() - // Add the block to the canonical chain number scheme and mark as the head batch := bc.db.NewBatch() + rawdb.WriteHeadHeaderHash(batch, block.Hash()) + rawdb.WriteHeadFastBlockHash(batch, block.Hash()) rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64()) rawdb.WriteTxLookupEntriesByBlock(batch, block) rawdb.WriteHeadBlockHash(batch, block.Hash()) - // If the block is better than our head or is on a different chain, force update heads - // TODO(rjl493456442) What if the (header head/fast block head) is lower than the chain - // head? Is it possible in practise? - if updateHeads { - rawdb.WriteHeadHeaderHash(batch, block.Hash()) - rawdb.WriteHeadFastBlockHash(batch, block.Hash()) - } // Flush the whole batch into the disk, exit the node if failed if err := batch.Write(); err != nil { log.Crit("Failed to update chain indexes and markers", "err", err) } // Update all in-memory chain markers in the last step - if updateHeads { - bc.hc.SetCurrentHeader(block.Header()) - bc.currentFastBlock.Store(block) - headFastBlockGauge.Update(int64(block.NumberU64())) - } + bc.hc.SetCurrentHeader(block.Header()) + + bc.currentFastBlock.Store(block) + headFastBlockGauge.Update(int64(block.NumberU64())) + bc.currentBlock.Store(block) headBlockGauge.Update(int64(block.NumberU64())) } From 0b0427d27d59381a0eb1c0b250f3f2e0eadac588 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 5 Oct 2021 19:44:54 +0200 Subject: [PATCH 40/43] miner: remove superflous log message --- miner/worker.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/miner/worker.go b/miner/worker.go index 8794139c1132..fef0d2e851ca 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -19,7 +19,6 @@ package miner import ( "bytes" "errors" - "fmt" "math/big" "sync" "sync/atomic" @@ -1026,17 +1025,6 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st return err } - // TODO (MariusVanDerWijden) remove this after eth2 interop - // --- - parent := w.chain.GetBlockByHash(block.ParentHash()) - if parent == nil { - return fmt.Errorf("parent unavailable: %v", block.ParentHash()) - } - td := w.chain.GetTdByHash(parent.Hash()) - if td != nil && td.Cmp(w.chain.Config().TerminalTotalDifficulty) > 0 { - log.Warn("Total terminal difficulty reached, keeping on mining to test eth2 clients") - } - // --- if w.isRunning() && !w.merger.LeftPoW() { if interval != nil { interval() From e90ede65be386892ac76968932f504bb6a028321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 7 Oct 2021 10:14:04 +0300 Subject: [PATCH 41/43] eth/downloader: restart the downloader after completion on new head --- eth/downloader/beaconsync.go | 6 ++++++ eth/downloader/downloader.go | 2 -- eth/downloader/skeleton.go | 7 +++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/eth/downloader/beaconsync.go b/eth/downloader/beaconsync.go index 1f6fad26ee0f..6c0b76016053 100644 --- a/eth/downloader/beaconsync.go +++ b/eth/downloader/beaconsync.go @@ -54,6 +54,12 @@ func (b *beaconBackfiller) suspend() { // resume starts the downloader threads for backfilling state and chain data. func (b *beaconBackfiller) resume() { b.lock.Lock() + if b.filling { + // If a previous filling cycle is still running, just ignore this start + // request. // TODO(karalabe): We should make this channel driven + b.lock.Unlock() + return + } b.filling = true mode := b.syncMode b.lock.Unlock() diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 0463025eaaa2..c0c483678a54 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -1296,8 +1296,6 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int, beaconMode bool) } d.pivotLock.RUnlock() - fmt.Println(pivot) - frequency := fsHeaderCheckFrequency if chunk[len(chunk)-1].Number.Uint64()+uint64(fsHeaderForceVerify) > pivot { frequency = 1 diff --git a/eth/downloader/skeleton.go b/eth/downloader/skeleton.go index 698acfc9c1dd..7c604cbffac7 100644 --- a/eth/downloader/skeleton.go +++ b/eth/downloader/skeleton.go @@ -294,6 +294,7 @@ func (s *skeleton) Terminate() error { // This method does not block, rather it just waits until the syncer receives the // fed header. What the syncer does with it is the syncer's problem. func (s *skeleton) Sync(head *types.Header) error { + log.Trace("New skeleton head announced", "number", head.Number, "hash", head.Hash()) select { case s.headEvents <- head: return nil @@ -393,6 +394,12 @@ func (s *skeleton) sync(head *types.Header) (*types.Header, error) { if reorged := s.processNewHead(head); reorged { return head, errSyncReorged } + // New head was integrated into the skeleton chain. If the backfiller + // is still running, it will pick it up. If it already terminated, + // a new cycle needs to be spun up. + if s.scratchHead == 0 { + s.filler.resume() + } case req := <-requestFails: s.revertRequest(req) From 4b9ea81aa4c49afce1c5c47ace82678503ed0cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Fri, 8 Oct 2021 12:35:35 +0300 Subject: [PATCH 42/43] eth: only sync with beers below the ttd --- eth/peerset.go | 4 ++-- eth/sync.go | 8 ++++---- eth/sync_test.go | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/eth/peerset.go b/eth/peerset.go index 1e864a8e46f2..0bcf3f39b22c 100644 --- a/eth/peerset.go +++ b/eth/peerset.go @@ -231,7 +231,7 @@ func (ps *peerSet) snapLen() int { // peerWithHighestTD retrieves the known peer with the currently highest total // difficulty. -func (ps *peerSet) peerWithHighestTD() *eth.Peer { +func (ps *peerSet) peerWithHighestTD(ttd *big.Int) *eth.Peer { ps.lock.RLock() defer ps.lock.RUnlock() @@ -240,7 +240,7 @@ func (ps *peerSet) peerWithHighestTD() *eth.Peer { bestTd *big.Int ) for _, p := range ps.peers { - if _, td := p.Head(); bestPeer == nil || td.Cmp(bestTd) > 0 { + if _, td := p.Head(); td.Cmp(ttd) < 0 && (bestPeer == nil || td.Cmp(bestTd) > 0) { bestPeer, bestTd = p.Peer, td } } diff --git a/eth/sync.go b/eth/sync.go index 104449c90807..e8eee63caecb 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -113,9 +113,9 @@ func (cs *chainSyncer) loop() { defer cs.force.Stop() for { - //if op := cs.nextSyncOp(); op != nil { - // cs.startSync(op) - //} + if op := cs.nextSyncOp(); op != nil { + cs.startSync(op) + } select { case <-cs.peerEventCh: // Peer information changed, recheck. @@ -160,7 +160,7 @@ func (cs *chainSyncer) nextSyncOp() *chainSyncOp { return nil } // We have enough peers, check TD - peer := cs.handler.peers.peerWithHighestTD() + peer := cs.handler.peers.peerWithHighestTD(cs.handler.chain.Config().TerminalTotalDifficulty) if peer == nil { return nil } diff --git a/eth/sync_test.go b/eth/sync_test.go index ee51c3019f18..088a1fb1f12e 100644 --- a/eth/sync_test.go +++ b/eth/sync_test.go @@ -17,6 +17,7 @@ package eth import ( + "math/big" "sync/atomic" "testing" "time" @@ -69,7 +70,7 @@ func testFastSyncDisabling(t *testing.T, protocol uint) { time.Sleep(250 * time.Millisecond) // Check that fast sync was disabled - op := peerToSyncOp(downloader.SnapSync, empty.handler.peers.peerWithHighestTD()) + op := peerToSyncOp(downloader.SnapSync, empty.handler.peers.peerWithHighestTD(new(big.Int))) if err := empty.handler.doSync(op); err != nil { t.Fatal("sync failed:", err) } From ac736f93f769d34eb3c63931fed08101926a9ca0 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Sat, 9 Oct 2021 12:04:37 +0200 Subject: [PATCH 43/43] eth/catalyst: added GetHead endpoint for fuzzing --- core/blockchain.go | 3 --- eth/catalyst/api.go | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index c505638a99bb..64f5dfe4031f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -205,9 +205,6 @@ type BlockChain struct { processor Processor // Block transaction processor interface forker *ForkChoice vmConfig vm.Config - - shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block. - terminateInsert func(common.Hash, uint64) bool // Testing hook used to terminate ancient receipt chain insertion. } // NewBlockChain returns a fully initialised block chain using information diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 824622d5b624..9718ce2b377a 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -565,3 +565,7 @@ func (api *ConsensusAPI) ExportExecutableData(path string) error { } return nil } + +func (api *ConsensusAPI) GetHead() (common.Hash, error) { + return api.eth.BlockChain().CurrentBlock().Hash(), nil +}